aboutsummaryrefslogtreecommitdiffstats
path: root/subsurface-core
diff options
context:
space:
mode:
Diffstat (limited to 'subsurface-core')
-rw-r--r--subsurface-core/CMakeLists.txt98
-rw-r--r--subsurface-core/android.cpp199
-rw-r--r--subsurface-core/checkcloudconnection.cpp106
-rw-r--r--subsurface-core/checkcloudconnection.h22
-rw-r--r--subsurface-core/cloudstorage.cpp109
-rw-r--r--subsurface-core/cloudstorage.h27
-rw-r--r--subsurface-core/cochran.c809
-rw-r--r--subsurface-core/cochran.h44
-rw-r--r--subsurface-core/color.cpp88
-rw-r--r--subsurface-core/color.h152
-rw-r--r--subsurface-core/compressibility.r115
-rw-r--r--subsurface-core/configuredivecomputer.cpp681
-rw-r--r--subsurface-core/configuredivecomputer.h68
-rw-r--r--subsurface-core/configuredivecomputerthreads.cpp1778
-rw-r--r--subsurface-core/configuredivecomputerthreads.h60
-rw-r--r--subsurface-core/datatrak.c698
-rw-r--r--subsurface-core/datatrak.h41
-rw-r--r--subsurface-core/deco.c601
-rw-r--r--subsurface-core/deco.h20
-rw-r--r--subsurface-core/device.c184
-rw-r--r--subsurface-core/device.h18
-rw-r--r--subsurface-core/devicedetails.cpp70
-rw-r--r--subsurface-core/devicedetails.h104
-rw-r--r--subsurface-core/display.h63
-rw-r--r--subsurface-core/dive.c3561
-rw-r--r--subsurface-core/dive.h913
-rw-r--r--subsurface-core/divecomputer.cpp228
-rw-r--r--subsurface-core/divecomputer.h38
-rw-r--r--subsurface-core/divelist.c1207
-rw-r--r--subsurface-core/divelist.h62
-rw-r--r--subsurface-core/divelogexportlogic.cpp161
-rw-r--r--subsurface-core/divelogexportlogic.h20
-rw-r--r--subsurface-core/divesite.c337
-rw-r--r--subsurface-core/divesite.cpp31
-rw-r--r--subsurface-core/divesite.h80
-rw-r--r--subsurface-core/divesitehelpers.cpp208
-rw-r--r--subsurface-core/divesitehelpers.h18
-rw-r--r--subsurface-core/equipment.c238
-rw-r--r--subsurface-core/exif.cpp587
-rw-r--r--subsurface-core/exif.h147
-rw-r--r--subsurface-core/file.c1115
-rw-r--r--subsurface-core/file.h24
-rw-r--r--subsurface-core/gas-model.c64
-rw-r--r--subsurface-core/gaspressures.c430
-rw-r--r--subsurface-core/gaspressures.h35
-rw-r--r--subsurface-core/gettext.h10
-rw-r--r--subsurface-core/gettextfromc.cpp27
-rw-r--r--subsurface-core/gettextfromc.h18
-rw-r--r--subsurface-core/git-access.c929
-rw-r--r--subsurface-core/git-access.h36
-rw-r--r--subsurface-core/gpslocation.cpp606
-rw-r--r--subsurface-core/gpslocation.h66
-rw-r--r--subsurface-core/helpers.h56
-rw-r--r--subsurface-core/imagedownloader.cpp113
-rw-r--r--subsurface-core/imagedownloader.h34
-rw-r--r--subsurface-core/isocialnetworkintegration.cpp6
-rw-r--r--subsurface-core/isocialnetworkintegration.h73
-rw-r--r--subsurface-core/libdivecomputer.c1081
-rw-r--r--subsurface-core/libdivecomputer.h72
-rw-r--r--subsurface-core/linux.c232
-rw-r--r--subsurface-core/liquivision.c420
-rw-r--r--subsurface-core/load-git.c1709
-rw-r--r--subsurface-core/macos.c218
-rw-r--r--subsurface-core/membuffer.c288
-rw-r--r--subsurface-core/membuffer.h74
-rw-r--r--subsurface-core/metrics.cpp65
-rw-r--r--subsurface-core/metrics.h36
-rw-r--r--subsurface-core/ostctools.c193
-rw-r--r--subsurface-core/parse-xml.c3751
-rw-r--r--subsurface-core/planner.c1471
-rw-r--r--subsurface-core/planner.h32
-rw-r--r--subsurface-core/pluginmanager.cpp53
-rw-r--r--subsurface-core/pluginmanager.h19
-rw-r--r--subsurface-core/pref.h172
-rw-r--r--subsurface-core/prefs-macros.h68
-rw-r--r--subsurface-core/profile.c1544
-rw-r--r--subsurface-core/profile.h111
-rw-r--r--subsurface-core/qt-gui.h15
-rw-r--r--subsurface-core/qt-init.cpp48
-rw-r--r--subsurface-core/qthelper.cpp1615
-rw-r--r--subsurface-core/qthelper.h48
-rw-r--r--subsurface-core/qthelperfromc.h22
-rw-r--r--subsurface-core/qtserialbluetooth.cpp416
-rw-r--r--subsurface-core/save-git.c1249
-rw-r--r--subsurface-core/save-html.c559
-rw-r--r--subsurface-core/save-html.h31
-rw-r--r--subsurface-core/save-xml.c749
-rw-r--r--subsurface-core/serial_ftdi.c665
-rw-r--r--subsurface-core/sha1.c300
-rw-r--r--subsurface-core/sha1.h38
-rw-r--r--subsurface-core/statistics.c404
-rw-r--r--subsurface-core/statistics.h59
-rw-r--r--subsurface-core/strndup.h21
-rw-r--r--subsurface-core/strtod.c128
-rw-r--r--subsurface-core/subsurface-qt/DiveObjectHelper.cpp338
-rw-r--r--subsurface-core/subsurface-qt/DiveObjectHelper.h89
-rw-r--r--subsurface-core/subsurface-qt/SettingsObjectWrapper.cpp1617
-rw-r--r--subsurface-core/subsurface-qt/SettingsObjectWrapper.h642
-rw-r--r--subsurface-core/subsurfacestartup.c310
-rw-r--r--subsurface-core/subsurfacestartup.h26
-rw-r--r--subsurface-core/subsurfacesysinfo.cpp620
-rw-r--r--subsurface-core/subsurfacesysinfo.h65
-rw-r--r--subsurface-core/taxonomy.c48
-rw-r--r--subsurface-core/taxonomy.h41
-rw-r--r--subsurface-core/time.c98
-rw-r--r--subsurface-core/uemis-downloader.c1403
-rw-r--r--subsurface-core/uemis.c392
-rw-r--r--subsurface-core/uemis.h54
-rw-r--r--subsurface-core/units.h280
-rw-r--r--subsurface-core/version.c18
-rw-r--r--subsurface-core/version.h19
-rw-r--r--subsurface-core/webservice.h24
-rw-r--r--subsurface-core/windows.c454
-rw-r--r--subsurface-core/windowtitleupdate.cpp32
-rw-r--r--subsurface-core/windowtitleupdate.h20
-rw-r--r--subsurface-core/worldmap-options.h7
-rw-r--r--subsurface-core/worldmap-save.c117
-rw-r--r--subsurface-core/worldmap-save.h15
118 files changed, 0 insertions, 42338 deletions
diff --git a/subsurface-core/CMakeLists.txt b/subsurface-core/CMakeLists.txt
deleted file mode 100644
index d9b1d3421..000000000
--- a/subsurface-core/CMakeLists.txt
+++ /dev/null
@@ -1,98 +0,0 @@
-set(PLATFORM_SRC unknown_platform.c)
-message(STATUS "system name ${CMAKE_SYSTEM_NAME}")
-if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
- if(ANDROID)
- set(PLATFORM_SRC android.cpp)
- else()
- set(PLATFORM_SRC linux.c)
- endif()
-elseif(CMAKE_SYSTEM_NAME STREQUAL "Android")
- set(PLATFORM_SRC android.cpp)
-elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
- set(PLATFORM_SRC macos.c)
-elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
- set(PLATFORM_SRC windows.c)
-endif()
-
-if(FTDISUPPORT)
- set(SERIAL_FTDI serial_ftdi.c)
-endif()
-
-if(BTSUPPORT)
- add_definitions(-DBT_SUPPORT)
- set(BT_SRC_FILES desktop-widgets/btdeviceselectiondialog.cpp)
- set(BT_CORE_SRC_FILES qtserialbluetooth.cpp)
-endif()
-
-# compile the core library, in C.
-set(SUBSURFACE_CORE_LIB_SRCS
- cochran.c
- datatrak.c
- deco.c
- device.c
- dive.c
- divesite.c
- divesite.cpp
- divelist.c
- equipment.c
- file.c
- gas-model.c
- git-access.c
- libdivecomputer.c
- liquivision.c
- load-git.c
- membuffer.c
- ostctools.c
- parse-xml.c
- planner.c
- profile.c
- gaspressures.c
- worldmap-save.c
- save-git.c
- save-xml.c
- save-html.c
- sha1.c
- statistics.c
- strtod.c
- subsurfacestartup.c
- time.c
- uemis.c
- uemis-downloader.c
- version.c
- # gettextfrommoc should be added because we are using it on the c-code.
- gettextfromc.cpp
- # dirk ported some core functionality to c++.
- qthelper.cpp
- divecomputer.cpp
- exif.cpp
- subsurfacesysinfo.cpp
- devicedetails.cpp
- configuredivecomputer.cpp
- configuredivecomputerthreads.cpp
- divesitehelpers.cpp
- taxonomy.c
- checkcloudconnection.cpp
- windowtitleupdate.cpp
- divelogexportlogic.cpp
- qt-init.cpp
- qtserialbluetooth.cpp
- metrics.cpp
- color.cpp
- pluginmanager.cpp
- imagedownloader.cpp
- isocialnetworkintegration.cpp
- gpslocation.cpp
- cloudstorage.cpp
-
- #Subsurface Qt have the Subsurface structs QObjectified for easy access via QML.
- subsurface-qt/DiveObjectHelper.cpp
- subsurface-qt/SettingsObjectWrapper.cpp
- ${SERIAL_FTDI}
- ${PLATFORM_SRC}
- ${BT_CORE_SRC_FILES}
-)
-source_group("Subsurface Core" FILES ${SUBSURFACE_CORE_LIB_SRCS})
-
-add_library(subsurface_corelib STATIC ${SUBSURFACE_CORE_LIB_SRCS} )
-target_link_libraries(subsurface_corelib ${QT_LIBRARIES})
-
diff --git a/subsurface-core/android.cpp b/subsurface-core/android.cpp
deleted file mode 100644
index 3631b07a1..000000000
--- a/subsurface-core/android.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-/* implements Android specific functions */
-#include "dive.h"
-#include "display.h"
-#include <string.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <libusb.h>
-#include <errno.h>
-
-#include <QtAndroidExtras/QtAndroidExtras>
-#include <QtAndroidExtras/QAndroidJniObject>
-#include <QtAndroid>
-
-#define FTDI_VID 0x0403
-#define USB_SERVICE "usb"
-
-extern "C" {
-
-const char android_system_divelist_default_font[] = "Roboto";
-const char *system_divelist_default_font = android_system_divelist_default_font;
-double system_divelist_default_font_size = -1;
-
-int get_usb_fd(uint16_t idVendor, uint16_t idProduct);
-void subsurface_OS_pref_setup(void)
-{
- // Abusing this function to get a decent place where we can wire in
- // our open callback into libusb
-#ifdef libusb_android_open_callback_func
- libusb_set_android_open_callback(get_usb_fd);
-#elif __ANDROID__
-#error we need libusb_android_open_callback
-#endif
-}
-
-bool subsurface_ignore_font(const char *font)
-{
- // there are no old default fonts that we would want to ignore
- return false;
-}
-
-void subsurface_user_info(struct user_info *user)
-{ /* Encourage use of at least libgit2-0.20 */ }
-
-static const char *system_default_path_append(const char *append)
-{
- // Qt appears to find a working path for us - let's just go with that
- QString path = QStandardPaths::standardLocations(QStandardPaths::DataLocation).first();
-
- if (append)
- path += QString("/%1").arg(append);
-
- return strdup(path.toUtf8().data());
-}
-
-const char *system_default_directory(void)
-{
- static const char *path = NULL;
- if (!path)
- path = system_default_path_append(NULL);
- return path;
-}
-
-const char *system_default_filename(void)
-{
- static const char *filename = "subsurface.xml";
- static const char *path = NULL;
- if (!path)
- path = system_default_path_append(filename);
- return path;
-}
-
-int enumerate_devices(device_callback_t callback, void *userdata, int dc_type)
-{
- /* FIXME: we need to enumerate in some other way on android */
- /* qtserialport maybee? */
- return -1;
-}
-
-/**
- * Get the file descriptor of first available matching device attached to usb in android.
- *
- * returns a fd to the device, or -1 and errno is set.
- */
-int get_usb_fd(uint16_t idVendor, uint16_t idProduct)
-{
- int i;
- jint fd, vendorid, productid;
- QAndroidJniObject usbName, usbDevice;
-
- // Get the current main activity of the application.
- QAndroidJniObject activity = QtAndroid::androidActivity();
-
- QAndroidJniObject usb_service = QAndroidJniObject::fromString(USB_SERVICE);
-
- // Get UsbManager from activity
- QAndroidJniObject usbManager = activity.callObjectMethod("getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;", usb_service.object());
-
- // Get a HashMap<Name, UsbDevice> of all USB devices attached to Android
- QAndroidJniObject deviceMap = usbManager.callObjectMethod("getDeviceList", "()Ljava/util/HashMap;");
- jint num_devices = deviceMap.callMethod<jint>("size", "()I");
- if (num_devices == 0) {
- // No USB device is attached.
- return -1;
- }
-
- // Iterate over all the devices and find the first available FTDI device.
- QAndroidJniObject keySet = deviceMap.callObjectMethod("keySet", "()Ljava/util/Set;");
- QAndroidJniObject iterator = keySet.callObjectMethod("iterator", "()Ljava/util/Iterator;");
-
- for (i = 0; i < num_devices; i++) {
- usbName = iterator.callObjectMethod("next", "()Ljava/lang/Object;");
- usbDevice = deviceMap.callObjectMethod ("get", "(Ljava/lang/Object;)Ljava/lang/Object;", usbName.object());
- vendorid = usbDevice.callMethod<jint>("getVendorId", "()I");
- productid = usbDevice.callMethod<jint>("getProductId", "()I");
- if(vendorid == idVendor && productid == idProduct) // Found the requested device
- break;
- }
- if (i == num_devices) {
- // No device found.
- errno = ENOENT;
- return -1;
- }
-
- jboolean hasPermission = usbManager.callMethod<jboolean>("hasPermission", "(Landroid/hardware/usb/UsbDevice;)Z", usbDevice.object());
- if (!hasPermission) {
- // You do not have permission to use the usbDevice.
- // Please remove and reinsert the USB device.
- // Could also give an dialogbox asking for permission.
- errno = EPERM;
- return -1;
- }
-
- // An device is present and we also have permission to use the device.
- // Open the device and get its file descriptor.
- QAndroidJniObject usbDeviceConnection = usbManager.callObjectMethod("openDevice", "(Landroid/hardware/usb/UsbDevice;)Landroid/hardware/usb/UsbDeviceConnection;", usbDevice.object());
- if (usbDeviceConnection.object() == NULL) {
- // Some error occurred while opening the device. Exit.
- errno = EINVAL;
- return -1;
- }
-
- // Finally get the required file descriptor.
- fd = usbDeviceConnection.callMethod<jint>("getFileDescriptor", "()I");
- if (fd == -1) {
- // The device is not opened. Some error.
- errno = ENODEV;
- return -1;
- }
- return fd;
-}
-
-/* NOP wrappers to comform with windows.c */
-int subsurface_rename(const char *path, const char *newpath)
-{
- return rename(path, newpath);
-}
-
-int subsurface_open(const char *path, int oflags, mode_t mode)
-{
- return open(path, oflags, mode);
-}
-
-FILE *subsurface_fopen(const char *path, const char *mode)
-{
- return fopen(path, mode);
-}
-
-void *subsurface_opendir(const char *path)
-{
- return (void *)opendir(path);
-}
-
-int subsurface_access(const char *path, int mode)
-{
- return access(path, mode);
-}
-
-struct zip *subsurface_zip_open_readonly(const char *path, int flags, int *errorp)
-{
- return zip_open(path, flags, errorp);
-}
-
-int subsurface_zip_close(struct zip *zip)
-{
- return zip_close(zip);
-}
-
-/* win32 console */
-void subsurface_console_init(bool dedicated)
-{
- /* NOP */
-}
-
-void subsurface_console_exit(void)
-{
- /* NOP */
-}
-}
diff --git a/subsurface-core/checkcloudconnection.cpp b/subsurface-core/checkcloudconnection.cpp
deleted file mode 100644
index f29d971ba..000000000
--- a/subsurface-core/checkcloudconnection.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-#include <QObject>
-#include <QTimer>
-#include <QNetworkAccessManager>
-#include <QNetworkReply>
-#include <QEventLoop>
-
-#include "pref.h"
-#include "helpers.h"
-#include "git-access.h"
-
-#include "checkcloudconnection.h"
-
-
-CheckCloudConnection::CheckCloudConnection(QObject *parent) :
- QObject(parent),
- reply(0)
-{
-
-}
-
-#define TEAPOT "/make-latte?number-of-shots=3"
-#define HTTP_I_AM_A_TEAPOT 418
-#define MILK "Linus does not like non-fat milk"
-bool CheckCloudConnection::checkServer()
-{
- if (verbose)
- fprintf(stderr, "Checking cloud connection...\n");
-
- QTimer timer;
- timer.setSingleShot(true);
- QEventLoop loop;
- QNetworkRequest request;
- request.setRawHeader("Accept", "text/plain");
- request.setRawHeader("User-Agent", getUserAgent().toUtf8());
- request.setRawHeader("Client-Id", getUUID().toUtf8());
- request.setUrl(QString(prefs.cloud_base_url) + TEAPOT);
- QNetworkAccessManager *mgr = new QNetworkAccessManager();
- reply = mgr->get(request);
- connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
- connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
- connect(reply, &QNetworkReply::sslErrors, this, &CheckCloudConnection::sslErrors);
- for (int seconds = 1; seconds <= 5; seconds++) {
- timer.start(1000); // wait five seconds
- loop.exec();
- if (timer.isActive()) {
- // didn't time out, did we get the right response?
- timer.stop();
- if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == HTTP_I_AM_A_TEAPOT &&
- reply->readAll() == QByteArray(MILK)) {
- reply->deleteLater();
- mgr->deleteLater();
- if (verbose > 1)
- qWarning() << "Cloud storage: successfully checked connection to cloud server";
- git_storage_update_progress(last_git_storage_update_val + 1, "successfully checked cloud connection");
- return true;
- }
- } else if (seconds < 5) {
- git_storage_update_progress(last_git_storage_update_val + 1, "waited 1 sec for cloud connection");
- } else {
- disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
- reply->abort();
- }
- }
- git_storage_update_progress(last_git_storage_update_val + 1, "cloud connection failed");
- if (verbose)
- qDebug() << "connection test to cloud server failed" <<
- reply->error() << reply->errorString() <<
- reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() <<
- reply->readAll();
- reply->deleteLater();
- mgr->deleteLater();
- if (verbose)
- qWarning() << "Cloud storage: unable to connect to cloud server";
- return false;
-}
-
-void CheckCloudConnection::sslErrors(QList<QSslError> errorList)
-{
- if (verbose) {
- qDebug() << "Received error response trying to set up https connection with cloud storage backend:";
- Q_FOREACH (QSslError err, errorList) {
- qDebug() << err.errorString();
- }
- }
- QSslConfiguration conf = reply->sslConfiguration();
- QSslCertificate cert = conf.peerCertificate();
- QByteArray hexDigest = cert.digest().toHex();
- if (reply->url().toString().contains(prefs.cloud_base_url) &&
- hexDigest == "13ff44c62996cfa5cd69d6810675490e") {
- if (verbose)
- qDebug() << "Overriding SSL check as I recognize the certificate digest" << hexDigest;
- reply->ignoreSslErrors();
- } else {
- if (verbose)
- qDebug() << "got invalid SSL certificate with hex digest" << hexDigest;
- }
-}
-
-// helper to be used from C code
-extern "C" bool canReachCloudServer()
-{
- if (verbose)
- qWarning() << "Cloud storage: checking connection to cloud server";
- CheckCloudConnection *checker = new CheckCloudConnection;
- return checker->checkServer();
-}
diff --git a/subsurface-core/checkcloudconnection.h b/subsurface-core/checkcloudconnection.h
deleted file mode 100644
index 58a412797..000000000
--- a/subsurface-core/checkcloudconnection.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef CHECKCLOUDCONNECTION_H
-#define CHECKCLOUDCONNECTION_H
-
-#include <QObject>
-#include <QNetworkReply>
-#include <QSsl>
-
-#include "checkcloudconnection.h"
-
-class CheckCloudConnection : public QObject {
- Q_OBJECT
-public:
- CheckCloudConnection(QObject *parent = 0);
- bool checkServer();
-private:
- QNetworkReply *reply;
-private
-slots:
- void sslErrors(QList<QSslError> errorList);
-};
-
-#endif // CHECKCLOUDCONNECTION_H
diff --git a/subsurface-core/cloudstorage.cpp b/subsurface-core/cloudstorage.cpp
deleted file mode 100644
index 575191891..000000000
--- a/subsurface-core/cloudstorage.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-#include "cloudstorage.h"
-#include "pref.h"
-#include "dive.h"
-#include "helpers.h"
-
-#include <QApplication>
-
-CloudStorageAuthenticate::CloudStorageAuthenticate(QObject *parent) :
- QObject(parent),
- reply(NULL)
-{
- userAgent = getUserAgent();
-}
-
-#define CLOUDURL QString(prefs.cloud_base_url)
-#define CLOUDBACKENDSTORAGE CLOUDURL + "/storage"
-#define CLOUDBACKENDVERIFY CLOUDURL + "/verify"
-#define CLOUDBACKENDUPDATE CLOUDURL + "/update"
-
-QNetworkReply* CloudStorageAuthenticate::backend(const QString& email,const QString& password,const QString& pin,const QString& newpasswd)
-{
- QString payload(email + QChar(' ') + password);
- QUrl requestUrl;
- if (pin.isEmpty() && newpasswd.isEmpty()) {
- requestUrl = QUrl(CLOUDBACKENDSTORAGE);
- } else if (!newpasswd.isEmpty()) {
- requestUrl = QUrl(CLOUDBACKENDUPDATE);
- payload += QChar(' ') + newpasswd;
- } else {
- requestUrl = QUrl(CLOUDBACKENDVERIFY);
- payload += QChar(' ') + pin;
- }
- QNetworkRequest *request = new QNetworkRequest(requestUrl);
- request->setRawHeader("Accept", "text/xml, text/plain");
- request->setRawHeader("User-Agent", userAgent.toUtf8());
- request->setHeader(QNetworkRequest::ContentTypeHeader, "text/plain");
- reply = manager()->post(*request, qPrintable(payload));
- connect(reply, SIGNAL(finished()), this, SLOT(uploadFinished()));
- connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrors(QList<QSslError>)));
- connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
- SLOT(uploadError(QNetworkReply::NetworkError)));
- return reply;
-}
-
-void CloudStorageAuthenticate::uploadFinished()
-{
- static QString myLastError;
-
- QString cloudAuthReply(reply->readAll());
- qDebug() << "Completed connection with cloud storage backend, response" << cloudAuthReply;
- if (cloudAuthReply == QLatin1String("[VERIFIED]") || cloudAuthReply == QLatin1String("[OK]")) {
- prefs.cloud_verification_status = CS_VERIFIED;
- /* TODO: Move this to a correct place
- NotificationWidget *nw = MainWindow::instance()->getNotificationWidget();
- if (nw->getNotificationText() == myLastError)
- nw->hideNotification();
- */
- myLastError.clear();
- } else if (cloudAuthReply == QLatin1String("[VERIFY]")) {
- prefs.cloud_verification_status = CS_NEED_TO_VERIFY;
- } else if (cloudAuthReply == QLatin1String("[PASSWDCHANGED]")) {
- free(prefs.cloud_storage_password);
- prefs.cloud_storage_password = prefs.cloud_storage_newpassword;
- prefs.cloud_storage_newpassword = NULL;
- emit passwordChangeSuccessful();
- return;
- } else {
- prefs.cloud_verification_status = CS_INCORRECT_USER_PASSWD;
- myLastError = cloudAuthReply;
- report_error("%s", qPrintable(cloudAuthReply));
- /* TODO: Emit a signal with the error
- MainWindow::instance()->getNotificationWidget()->showNotification(get_error_string(), KMessageWidget::Error);
- */
- }
- emit finishedAuthenticate();
-}
-
-void CloudStorageAuthenticate::uploadError(QNetworkReply::NetworkError)
-{
- qDebug() << "Received error response from cloud storage backend:" << reply->errorString();
-}
-
-void CloudStorageAuthenticate::sslErrors(QList<QSslError> errorList)
-{
- if (verbose) {
- qDebug() << "Received error response trying to set up https connection with cloud storage backend:";
- Q_FOREACH (QSslError err, errorList) {
- qDebug() << err.errorString();
- }
- }
- QSslConfiguration conf = reply->sslConfiguration();
- QSslCertificate cert = conf.peerCertificate();
- QByteArray hexDigest = cert.digest().toHex();
- if (reply->url().toString().contains(prefs.cloud_base_url) &&
- hexDigest == "13ff44c62996cfa5cd69d6810675490e") {
- if (verbose)
- qDebug() << "Overriding SSL check as I recognize the certificate digest" << hexDigest;
- reply->ignoreSslErrors();
- } else {
- if (verbose)
- qDebug() << "got invalid SSL certificate with hex digest" << hexDigest;
- }
-}
-
-QNetworkAccessManager *manager()
-{
- static QNetworkAccessManager *manager = new QNetworkAccessManager(qApp);
- return manager;
-}
diff --git a/subsurface-core/cloudstorage.h b/subsurface-core/cloudstorage.h
deleted file mode 100644
index 6addb739d..000000000
--- a/subsurface-core/cloudstorage.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef CLOUD_STORAGE_H
-#define CLOUD_STORAGE_H
-
-#include <QObject>
-#include <QNetworkReply>
-
-class CloudStorageAuthenticate : public QObject {
- Q_OBJECT
-public:
- QNetworkReply* backend(const QString& email,const QString& password,const QString& pin = QString(),const QString& newpasswd = QString());
- explicit CloudStorageAuthenticate(QObject *parent);
-signals:
- void finishedAuthenticate();
- void passwordChangeSuccessful();
-private
-slots:
- void uploadError(QNetworkReply::NetworkError error);
- void sslErrors(QList<QSslError> errorList);
- void uploadFinished();
-private:
- QNetworkReply *reply;
- QString userAgent;
- bool verbose;
-};
-
-QNetworkAccessManager *manager();
-#endif \ No newline at end of file
diff --git a/subsurface-core/cochran.c b/subsurface-core/cochran.c
deleted file mode 100644
index b42ed8233..000000000
--- a/subsurface-core/cochran.c
+++ /dev/null
@@ -1,809 +0,0 @@
-// Clang has a bug on zero-initialization of C structs.
-#pragma clang diagnostic ignored "-Wmissing-field-initializers"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include "dive.h"
-#include "file.h"
-#include "units.h"
-#include "gettext.h"
-#include "cochran.h"
-#include "divelist.h"
-
-#include <libdivecomputer/parser.h>
-
-#define POUND 0.45359237
-#define FEET 0.3048
-#define INCH 0.0254
-#define GRAVITY 9.80665
-#define ATM 101325.0
-#define BAR 100000.0
-#define FSW (ATM / 33.0)
-#define MSW (BAR / 10.0)
-#define PSI ((POUND * GRAVITY) / (INCH * INCH))
-
-// Some say 0x4a14 and 0x4b14 are the right number for this offset
-// This works with CAN files from Analyst 4.01v and computers
-// such as Commander, Gemini, EMC-16, and EMC-20H
-#define LOG_ENTRY_OFFSET 0x4914
-
-enum cochran_type {
- TYPE_GEMINI,
- TYPE_COMMANDER,
- TYPE_EMC
-};
-
-struct config {
- enum cochran_type type;
- unsigned int logbook_size;
- unsigned int sample_size;
-} config;
-
-
-// Convert 4 bytes into an INT
-#define array_uint16_le(p) ((unsigned int) (p)[0] \
- + ((p)[1]<<8) )
-#define array_uint32_le(p) ((unsigned int) (p)[0] \
- + ((p)[1]<<8) + ((p)[2]<<16) \
- + ((p)[3]<<24))
-
-/*
- * The Cochran file format is designed to be annoying to read. It's roughly:
- *
- * 0x00000: room for 65534 4-byte words, giving the starting offsets
- * of the dives themselves.
- *
- * 0x3fff8: the size of the file + 1
- * 0x3ffff: 0 (high 32 bits of filesize? Bogus: the offsets into the file
- * are 32-bit, so it can't be a large file anyway)
- *
- * 0x40000: byte 0x46
- * 0x40001: "block 0": 256 byte encryption key
- * 0x40101: the random modulus, or length of the key to use
- * 0x40102: block 1: Version and date of Analyst and a feature string identifying
- * the computer features and the features of the file
- * 0x40138: Computer configuration page 1, 512 bytes
- * 0x40338: Computer configuration page 2, 512 bytes
- * 0x40538: Misc data (tissues) 1500 bytes
- * 0x40b14: Ownership data 512 bytes ???
- *
- * 0x4171c: Ownership data 512 bytes ??? <copy>
- *
- * 0x45415: Time stamp 17 bytes
- * 0x45426: Computer configuration page 1, 512 bytes <copy>
- * 0x45626: Computer configuration page 2, 512 bytes <copy>
- *
- */
-static unsigned int partial_decode(unsigned int start, unsigned int end,
- const unsigned char *decode, unsigned offset, unsigned mod,
- const unsigned char *buf, unsigned int size, unsigned char *dst)
-{
- unsigned i, sum = 0;
-
- for (i = start; i < end; i++) {
- unsigned char d = decode[offset++];
- if (i >= size)
- break;
- if (offset == mod)
- offset = 0;
- d += buf[i];
- if (dst)
- dst[i] = d;
- sum += d;
- }
- return sum;
-}
-
-#ifdef COCHRAN_DEBUG
-
-#define hexchar(n) ("0123456789abcdef"[(n) & 15])
-
-static int show_line(unsigned offset, const unsigned char *data,
- unsigned size, int show_empty)
-{
- unsigned char bits;
- int i, off;
- char buffer[120];
-
- if (size > 16)
- size = 16;
-
- bits = 0;
- memset(buffer, ' ', sizeof(buffer));
- off = sprintf(buffer, "%06x ", offset);
- for (i = 0; i < size; i++) {
- char *hex = buffer + off + 3 * i;
- char *asc = buffer + off + 50 + i;
- unsigned char byte = data[i];
-
- hex[0] = hexchar(byte >> 4);
- hex[1] = hexchar(byte);
- bits |= byte;
- if (byte < 32 || byte > 126)
- byte = '.';
- asc[0] = byte;
- asc[1] = 0;
- }
-
- if (bits) {
- puts(buffer);
- return 1;
- }
- if (show_empty)
- puts("...");
- return 0;
-}
-
-static void cochran_debug_write(const unsigned char *data, unsigned size)
-{
- return;
-
- int show = 1, i;
- for (i = 0; i < size; i += 16)
- show = show_line(i, data + i, size - i, show);
-}
-
-static void cochran_debug_sample(const char *s, unsigned int seconds)
-{
- switch (config.type) {
- case TYPE_GEMINI:
- switch (seconds % 4) {
- case 0:
- printf("Hex: %02x %02x ", s[0], s[1]);
- break;
- case 1:
- printf("Hex: %02x %02x ", s[0], s[1]);
- break;
- case 2:
- printf("Hex: %02x %02x ", s[0], s[1]);
- break;
- case 3:
- printf("Hex: %02x %02x ", s[0], s[1]);
- break;
- }
- break;
- case TYPE_COMMANDER:
- switch (seconds % 2) {
- case 0:
- printf("Hex: %02x %02x ", s[0], s[1]);
- break;
- case 1:
- printf("Hex: %02x %02x ", s[0], s[1]);
- break;
- }
- break;
- case TYPE_EMC:
- switch (seconds % 2) {
- case 0:
- printf("Hex: %02x %02x %02x ", s[0], s[1], s[2]);
- break;
- case 1:
- printf("Hex: %02x %02x %02x ", s[0], s[1], s[2]);
- break;
- }
- break;
- }
-
- printf ("%02dh %02dm %02ds: Depth: %-5.2f, ", seconds / 3660,
- (seconds % 3660) / 60, seconds % 60, depth);
-}
-
-#endif // COCHRAN_DEBUG
-
-static void cochran_parse_header(const unsigned char *decode, unsigned mod,
- const unsigned char *in, unsigned size)
-{
- unsigned char *buf = malloc(size);
-
- /* Do the "null decode" using a one-byte decode array of '\0' */
- /* Copies in plaintext, will be overwritten later */
- partial_decode(0, 0x0102, (const unsigned char *)"", 0, 1, in, size, buf);
-
- /*
- * The header scrambling is different form the dive
- * scrambling. Oh yay!
- */
- partial_decode(0x0102, 0x010e, decode, 0, mod, in, size, buf);
- partial_decode(0x010e, 0x0b14, decode, 0, mod, in, size, buf);
- partial_decode(0x0b14, 0x1b14, decode, 0, mod, in, size, buf);
- partial_decode(0x1b14, 0x2b14, decode, 0, mod, in, size, buf);
- partial_decode(0x2b14, 0x3b14, decode, 0, mod, in, size, buf);
- partial_decode(0x3b14, 0x5414, decode, 0, mod, in, size, buf);
- partial_decode(0x5414, size, decode, 0, mod, in, size, buf);
-
- // Detect log type
- switch (buf[0x133]) {
- case '2': // Cochran Commander, version II log format
- config.logbook_size = 256;
- if (buf[0x132] == 0x10) {
- config.type = TYPE_GEMINI;
- config.sample_size = 2; // Gemini with tank PSI samples
- } else {
- config.type = TYPE_COMMANDER;
- config.sample_size = 2; // Commander
- }
- break;
- case '3': // Cochran EMC, version III log format
- config.type = TYPE_EMC;
- config.logbook_size = 512;
- config.sample_size = 3;
- break;
- default:
- printf ("Unknown log format v%c\n", buf[0x137]);
- free(buf);
- exit(1);
- break;
- }
-
-#ifdef COCHRAN_DEBUG
- puts("Header\n======\n\n");
- cochran_debug_write(buf, size);
-#endif
-
- free(buf);
-}
-
-/*
-* Bytes expected after a pre-dive event code
-*/
-static int cochran_predive_event_bytes(unsigned char code)
-{
- int x = 0;
- int gem_event_bytes[15][2] = {{0x00, 10}, {0x02, 17}, {0x08, 18},
- {0x09, 18}, {0x0c, 18}, {0x0d, 18},
- {0x0e, 18},
- {-1, 0}};
- int cmdr_event_bytes[15][2] = {{0x00, 16}, {0x01, 20}, {0x02, 17},
- {0x03, 16}, {0x06, 18}, {0x07, 18},
- {0x08, 18}, {0x09, 18}, {0x0a, 18},
- {0x0b, 20}, {0x0c, 18}, {0x0d, 18},
- {0x0e, 18}, {0x10, 20},
- {-1, 0}};
- int emc_event_bytes[15][2] = {{0x00, 18}, {0x01, 22}, {0x02, 19},
- {0x03, 18}, {0x06, 20}, {0x07, 20},
- {0x0a, 20}, {0x0b, 20}, {0x0f, 18},
- {0x10, 20},
- {-1, 0}};
-
- switch (config.type) {
- case TYPE_GEMINI:
- while (gem_event_bytes[x][0] != code && gem_event_bytes[x][0] != -1)
- x++;
- return gem_event_bytes[x][1];
- break;
- case TYPE_COMMANDER:
- while (cmdr_event_bytes[x][0] != code && cmdr_event_bytes[x][0] != -1)
- x++;
- return cmdr_event_bytes[x][1];
- break;
- case TYPE_EMC:
- while (emc_event_bytes[x][0] != code && emc_event_bytes[x][0] != -1)
- x++;
- return emc_event_bytes[x][1];
- break;
- }
-
- return 0;
-}
-
-int cochran_dive_event_bytes(unsigned char event)
-{
- return (event == 0xAD || event == 0xAB) ? 4 : 0;
-}
-
-static void cochran_dive_event(struct divecomputer *dc, const unsigned char *s,
- unsigned int seconds, unsigned int *in_deco,
- unsigned int *deco_ceiling, unsigned int *deco_time)
-{
- switch (s[0]) {
- case 0xC5: // Deco obligation begins
- *in_deco = 1;
- add_event(dc, seconds, SAMPLE_EVENT_DECOSTOP,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "deco stop"));
- break;
- case 0xDB: // Deco obligation ends
- *in_deco = 0;
- add_event(dc, seconds, SAMPLE_EVENT_DECOSTOP,
- SAMPLE_FLAGS_END, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "deco stop"));
- break;
- case 0xAD: // Raise deco ceiling 10 ft
- *deco_ceiling -= 10; // ft
- *deco_time = (array_uint16_le(s + 3) + 1) * 60;
- break;
- case 0xAB: // Lower deco ceiling 10 ft
- *deco_ceiling += 10; // ft
- *deco_time = (array_uint16_le(s + 3) + 1) * 60;
- break;
- case 0xA8: // Entered Post Dive interval mode (surfaced)
- break;
- case 0xA9: // Exited PDI mode (re-submierged)
- break;
- case 0xBD: // Switched to normal PO2 setting
- break;
- case 0xC0: // Switched to FO2 21% mode (generally upon surface)
- break;
- case 0xC1: // "Ascent rate alarm
- add_event(dc, seconds, SAMPLE_EVENT_ASCENT,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "ascent"));
- break;
- case 0xC2: // Low battery warning
-#ifdef SAMPLE_EVENT_BATTERY
- add_event(dc, seconds, SAMPLE_EVENT_BATTERY,
- SAMPLE_FLAGS_NONE, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "battery"));
-#endif
- break;
- case 0xC3: // CNS warning
- add_event(dc, seconds, SAMPLE_EVENT_OLF,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "OLF"));
- break;
- case 0xC4: // Depth alarm begin
- add_event(dc, seconds, SAMPLE_EVENT_MAXDEPTH,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "maxdepth"));
- break;
- case 0xC8: // PPO2 alarm begin
- add_event(dc, seconds, SAMPLE_EVENT_PO2,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "pOâ‚‚"));
- break;
- case 0xCC: // Low cylinder 1 pressure";
- break;
- case 0xCD: // Switch to deco blend setting
- add_event(dc, seconds, SAMPLE_EVENT_GASCHANGE,
- SAMPLE_FLAGS_NONE, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "gaschange"));
- break;
- case 0xCE: // NDL alarm begin
- add_event(dc, seconds, SAMPLE_EVENT_RBT,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "rbt"));
- break;
- case 0xD0: // Breathing rate alarm begin
- break;
- case 0xD3: // Low gas 1 flow rate alarm begin";
- break;
- case 0xD6: // Ceiling alarm begin
- add_event(dc, seconds, SAMPLE_EVENT_CEILING,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "ceiling"));
- break;
- case 0xD8: // End decompression mode
- *in_deco = 0;
- add_event(dc, seconds, SAMPLE_EVENT_DECOSTOP,
- SAMPLE_FLAGS_END, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "deco stop"));
- break;
- case 0xE1: // Ascent alarm end
- add_event(dc, seconds, SAMPLE_EVENT_ASCENT,
- SAMPLE_FLAGS_END, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "ascent"));
- break;
- case 0xE2: // Low transmitter battery alarm
- add_event(dc, seconds, SAMPLE_EVENT_TRANSMITTER,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "transmitter"));
- break;
- case 0xE3: // Switch to FO2 mode
- break;
- case 0xE5: // Switched to PO2 mode
- break;
- case 0xE8: // PO2 too low alarm
- add_event(dc, seconds, SAMPLE_EVENT_PO2,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "pOâ‚‚"));
- break;
- case 0xEE: // NDL alarm end
- add_event(dc, seconds, SAMPLE_EVENT_RBT,
- SAMPLE_FLAGS_END, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "rbt"));
- break;
- case 0xEF: // Switch to blend 2
- add_event(dc, seconds, SAMPLE_EVENT_GASCHANGE,
- SAMPLE_FLAGS_NONE, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "gaschange"));
- break;
- case 0xF0: // Breathing rate alarm end
- break;
- case 0xF3: // Switch to blend 1 (often at dive start)
- add_event(dc, seconds, SAMPLE_EVENT_GASCHANGE,
- SAMPLE_FLAGS_NONE, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "gaschange"));
- break;
- case 0xF6: // Ceiling alarm end
- add_event(dc, seconds, SAMPLE_EVENT_CEILING,
- SAMPLE_FLAGS_END, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "ceiling"));
- break;
- default:
- break;
- }
-}
-
-/*
-* Parse sample data, extract events and build a dive
-*/
-static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
- const unsigned char *samples, unsigned int size,
- unsigned int *duration, double *max_depth,
- double *avg_depth, double *min_temp)
-{
- const unsigned char *s;
- unsigned int offset = 0, seconds = 0;
- double depth = 0, temp = 0, depth_sample = 0, psi = 0, sgc_rate = 0;
- int ascent_rate = 0;
- unsigned int ndl = 0;
- unsigned int in_deco = 0, deco_ceiling = 0, deco_time = 0;
-
- struct divecomputer *dc = &dive->dc;
- struct sample *sample;
-
- // Initialize stat variables
- *max_depth = 0, *avg_depth = 0, *min_temp = 0xFF;
-
- // Get starting depth and temp (tank PSI???)
- switch (config.type) {
- case TYPE_GEMINI:
- depth = (float) (log[CMD_START_DEPTH]
- + log[CMD_START_DEPTH + 1] * 256) / 4;
- temp = log[CMD_START_TEMP];
- psi = log[CMD_START_PSI] + log[CMD_START_PSI + 1] * 256;
- sgc_rate = (float)(log[CMD_START_SGC]
- + log[CMD_START_SGC + 1] * 256) / 2;
- break;
- case TYPE_COMMANDER:
- depth = (float) (log[CMD_START_DEPTH]
- + log[CMD_START_DEPTH + 1] * 256) / 4;
- temp = log[CMD_START_TEMP];
- break;
-
- case TYPE_EMC:
- depth = (float) log [EMC_START_DEPTH] / 256
- + log[EMC_START_DEPTH + 1];
- temp = log[EMC_START_TEMP];
- break;
- }
-
- // Skip past pre-dive events
- unsigned int x = 0;
- if (samples[x] != 0x40) {
- unsigned int c;
- while ((samples[x] & 0x80) == 0 && samples[x] != 0x40 && x < size) {
- c = cochran_predive_event_bytes(samples[x]) + 1;
-#ifdef COCHRAN_DEBUG
- printf("Predive event: ", samples[x]);
- for (int y = 0; y < c; y++) printf("%02x ", samples[x + y]);
- putchar('\n');
-#endif
- x += c;
- }
- }
-
- // Now process samples
- offset = x;
- while (offset < size) {
- s = samples + offset;
-
- // Start with an empty sample
- sample = prepare_sample(dc);
- sample->time.seconds = seconds;
-
- // Check for event
- if (s[0] & 0x80) {
- cochran_dive_event(dc, s, seconds, &in_deco, &deco_ceiling, &deco_time);
- offset += cochran_dive_event_bytes(s[0]) + 1;
- continue;
- }
-
- // Depth is in every sample
- depth_sample = (float)(s[0] & 0x3F) / 4 * (s[0] & 0x40 ? -1 : 1);
- depth += depth_sample;
-
-#ifdef COCHRAN_DEBUG
- cochran_debug_sample(s, seconds);
-#endif
-
- switch (config.type) {
- case TYPE_COMMANDER:
- switch (seconds % 2) {
- case 0: // Ascent rate
- ascent_rate = (s[1] & 0x7f) * (s[1] & 0x80 ? 1: -1);
- break;
- case 1: // Temperature
- temp = s[1] / 2 + 20;
- break;
- }
- break;
- case TYPE_GEMINI:
- // Gemini with tank pressure and SAC rate.
- switch (seconds % 4) {
- case 0: // Ascent rate
- ascent_rate = (s[1] & 0x7f) * (s[1] & 0x80 ? 1 : -1);
- break;
- case 2: // PSI change
- psi -= (float)(s[1] & 0x7f) * (s[1] & 0x80 ? 1 : -1) / 4;
- break;
- case 1: // SGC rate
- sgc_rate -= (float)(s[1] & 0x7f) * (s[1] & 0x80 ? 1 : -1) / 2;
- break;
- case 3: // Temperature
- temp = (float)s[1] / 2 + 20;
- break;
- }
- break;
- case TYPE_EMC:
- switch (seconds % 2) {
- case 0: // Ascent rate
- ascent_rate = (s[1] & 0x7f) * (s[1] & 0x80 ? 1: -1);
- break;
- case 1: // Temperature
- temp = (float)s[1] / 2 + 20;
- break;
- }
- // Get NDL and deco information
- switch (seconds % 24) {
- case 20:
- if (in_deco) {
- // Fist stop time
- //first_deco_time = (s[2] + s[5] * 256 + 1) * 60; // seconds
- ndl = 0;
- } else {
- // NDL
- ndl = (s[2] + s[5] * 256 + 1) * 60; // seconds
- deco_time = 0;
- }
- break;
- case 22:
- if (in_deco) {
- // Total stop time
- deco_time = (s[2] + s[5] * 256 + 1) * 60; // seconds
- ndl = 0;
- }
- break;
- }
- }
-
- // Track dive stats
- if (depth > *max_depth) *max_depth = depth;
- if (temp < *min_temp) *min_temp = temp;
- *avg_depth = (*avg_depth * seconds + depth) / (seconds + 1);
-
- sample->depth.mm = depth * FEET * 1000;
- sample->ndl.seconds = ndl;
- sample->in_deco = in_deco;
- sample->stoptime.seconds = deco_time;
- sample->stopdepth.mm = deco_ceiling * FEET * 1000;
- sample->temperature.mkelvin = C_to_mkelvin((temp - 32) / 1.8);
- sample->sensor = 0;
- sample->cylinderpressure.mbar = psi * PSI / 100;
-
- finish_sample(dc);
-
- offset += config.sample_size;
- seconds++;
- }
- (void)ascent_rate; // mark the variable as unused
-
- if (seconds > 0)
- *duration = seconds - 1;
-}
-
-static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
- const unsigned char *in, unsigned size)
-{
- unsigned char *buf = malloc(size);
- struct dive *dive;
- struct divecomputer *dc;
- struct tm tm = {0};
- uint32_t csum[5];
-
- double max_depth, avg_depth, min_temp;
- unsigned int duration = 0, corrupt_dive = 0;
-
- /*
- * The scrambling has odd boundaries. I think the boundaries
- * match some data structure size, but I don't know. They were
- * discovered the same way we dynamically discover the decode
- * size: automatically looking for least random output.
- *
- * The boundaries are also this confused "off-by-one" thing,
- * the same way the file size is off by one. It's as if the
- * cochran software forgot to write one byte at the beginning.
- */
- partial_decode(0, 0x0fff, decode, 1, mod, in, size, buf);
- partial_decode(0x0fff, 0x1fff, decode, 0, mod, in, size, buf);
- partial_decode(0x1fff, 0x2fff, decode, 0, mod, in, size, buf);
- partial_decode(0x2fff, 0x48ff, decode, 0, mod, in, size, buf);
-
- /*
- * This is not all the descrambling you need - the above are just
- * what appears to be the fixed-size blocks. The rest is also
- * scrambled, but there seems to be size differences in the data,
- * so this just descrambles part of it:
- */
- // Decode log entry (512 bytes + random prefix)
- partial_decode(0x48ff, 0x4914 + config.logbook_size, decode,
- 0, mod, in, size, buf);
-
- unsigned int sample_size = size - 0x4914 - config.logbook_size;
- int g;
-
- // Decode sample data
- partial_decode(0x4914 + config.logbook_size, size, decode,
- 0, mod, in, size, buf);
-
-#ifdef COCHRAN_DEBUG
- // Display pre-logbook data
- puts("\nPre Logbook Data\n");
- cochran_debug_write(buf, 0x4914);
-
- // Display log book
- puts("\nLogbook Data\n");
- cochran_debug_write(buf + 0x4914, config.logbook_size + 0x400);
-
- // Display sample data
- puts("\nSample Data\n");
-#endif
-
- dive = alloc_dive();
- dc = &dive->dc;
-
- unsigned char *log = (buf + 0x4914);
-
- switch (config.type) {
- case TYPE_GEMINI:
- case TYPE_COMMANDER:
- if (config.type == TYPE_GEMINI) {
- dc->model = "Gemini";
- dc->deviceid = buf[0x18c] * 256 + buf[0x18d]; // serial no
- fill_default_cylinder(&dive->cylinder[0]);
- dive->cylinder[0].gasmix.o2.permille = (log[CMD_O2_PERCENT] / 256
- + log[CMD_O2_PERCENT + 1]) * 10;
- dive->cylinder[0].gasmix.he.permille = 0;
- } else {
- dc->model = "Commander";
- dc->deviceid = array_uint32_le(buf + 0x31e); // serial no
- for (g = 0; g < 2; g++) {
- fill_default_cylinder(&dive->cylinder[g]);
- dive->cylinder[g].gasmix.o2.permille = (log[CMD_O2_PERCENT + g * 2] / 256
- + log[CMD_O2_PERCENT + g * 2 + 1]) * 10;
- dive->cylinder[g].gasmix.he.permille = 0;
- }
- }
-
- tm.tm_year = log[CMD_YEAR];
- tm.tm_mon = log[CMD_MON] - 1;
- tm.tm_mday = log[CMD_DAY];
- tm.tm_hour = log[CMD_HOUR];
- tm.tm_min = log[CMD_MIN];
- tm.tm_sec = log[CMD_SEC];
- tm.tm_isdst = -1;
-
- dive->when = dc->when = utc_mktime(&tm);
- dive->number = log[CMD_NUMBER] + log[CMD_NUMBER + 1] * 256 + 1;
- dc->duration.seconds = (log[CMD_BT] + log[CMD_BT + 1] * 256) * 60;
- dc->surfacetime.seconds = (log[CMD_SIT] + log[CMD_SIT + 1] * 256) * 60;
- dc->maxdepth.mm = (log[CMD_MAX_DEPTH] +
- log[CMD_MAX_DEPTH + 1] * 256) / 4 * FEET * 1000;
- dc->meandepth.mm = (log[CMD_AVG_DEPTH] +
- log[CMD_AVG_DEPTH + 1] * 256) / 4 * FEET * 1000;
- dc->watertemp.mkelvin = C_to_mkelvin((log[CMD_MIN_TEMP] / 32) - 1.8);
- dc->surface_pressure.mbar = ATM / BAR * pow(1 - 0.0000225577
- * (double) log[CMD_ALTITUDE] * 250 * FEET, 5.25588) * 1000;
- dc->salinity = 10000 + 150 * log[CMD_WATER_CONDUCTIVITY];
-
- SHA1(log + CMD_NUMBER, 2, (unsigned char *)csum);
- dc->diveid = csum[0];
-
- if (log[CMD_MAX_DEPTH] == 0xff && log[CMD_MAX_DEPTH + 1] == 0xff)
- corrupt_dive = 1;
-
- break;
- case TYPE_EMC:
- dc->model = "EMC";
- dc->deviceid = array_uint32_le(buf + 0x31e); // serial no
- for (g = 0; g < 4; g++) {
- fill_default_cylinder(&dive->cylinder[g]);
- dive->cylinder[g].gasmix.o2.permille =
- (log[EMC_O2_PERCENT + g * 2] / 256
- + log[EMC_O2_PERCENT + g * 2 + 1]) * 10;
- dive->cylinder[g].gasmix.he.permille =
- (log[EMC_HE_PERCENT + g * 2] / 256
- + log[EMC_HE_PERCENT + g * 2 + 1]) * 10;
- }
-
- tm.tm_year = log[EMC_YEAR];
- tm.tm_mon = log[EMC_MON] - 1;
- tm.tm_mday = log[EMC_DAY];
- tm.tm_hour = log[EMC_HOUR];
- tm.tm_min = log[EMC_MIN];
- tm.tm_sec = log[EMC_SEC];
- tm.tm_isdst = -1;
-
- dive->when = dc->when = utc_mktime(&tm);
- dive->number = log[EMC_NUMBER] + log[EMC_NUMBER + 1] * 256 + 1;
- dc->duration.seconds = (log[EMC_BT] + log[EMC_BT + 1] * 256) * 60;
- dc->surfacetime.seconds = (log[EMC_SIT] + log[EMC_SIT + 1] * 256) * 60;
- dc->maxdepth.mm = (log[EMC_MAX_DEPTH] +
- log[EMC_MAX_DEPTH + 1] * 256) / 4 * FEET * 1000;
- dc->meandepth.mm = (log[EMC_AVG_DEPTH] +
- log[EMC_AVG_DEPTH + 1] * 256) / 4 * FEET * 1000;
- dc->watertemp.mkelvin = C_to_mkelvin((log[EMC_MIN_TEMP] - 32) / 1.8);
- dc->surface_pressure.mbar = ATM / BAR * pow(1 - 0.0000225577
- * (double) log[EMC_ALTITUDE] * 250 * FEET, 5.25588) * 1000;
- dc->salinity = 10000 + 150 * (log[EMC_WATER_CONDUCTIVITY] & 0x3);
-
- SHA1(log + EMC_NUMBER, 2, (unsigned char *)csum);
- dc->diveid = csum[0];
-
- if (log[EMC_MAX_DEPTH] == 0xff && log[EMC_MAX_DEPTH + 1] == 0xff)
- corrupt_dive = 1;
-
- break;
- }
-
- cochran_parse_samples(dive, buf + 0x4914, buf + 0x4914
- + config.logbook_size, sample_size,
- &duration, &max_depth, &avg_depth, &min_temp);
-
- // Check for corrupt dive
- if (corrupt_dive) {
- dc->maxdepth.mm = max_depth * FEET * 1000;
- dc->meandepth.mm = avg_depth * FEET * 1000;
- dc->watertemp.mkelvin = C_to_mkelvin((min_temp - 32) / 1.8);
- dc->duration.seconds = duration;
- }
-
- dive->downloaded = true;
- record_dive(dive);
- mark_divelist_changed(true);
-
- free(buf);
-}
-
-int try_to_open_cochran(const char *filename, struct memblock *mem)
-{
- (void) filename;
- unsigned int i;
- unsigned int mod;
- unsigned int *offsets, dive1, dive2;
- unsigned char *decode = mem->buffer + 0x40001;
-
- if (mem->size < 0x40000)
- return 0;
-
- offsets = (unsigned int *) mem->buffer;
- dive1 = offsets[0];
- dive2 = offsets[1];
-
- if (dive1 < 0x40000 || dive2 < dive1 || dive2 > mem->size)
- return 0;
-
- mod = decode[0x100] + 1;
- cochran_parse_header(decode, mod, mem->buffer + 0x40000, dive1 - 0x40000);
-
- // Decode each dive
- for (i = 0; i < 65534; i++) {
- dive1 = offsets[i];
- dive2 = offsets[i + 1];
- if (dive2 < dive1)
- break;
- if (dive2 > mem->size)
- break;
-
- cochran_parse_dive(decode, mod, mem->buffer + dive1,
- dive2 - dive1);
- }
-
- return 1; // no further processing needed
-}
diff --git a/subsurface-core/cochran.h b/subsurface-core/cochran.h
deleted file mode 100644
index 97d4361c8..000000000
--- a/subsurface-core/cochran.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Commander log fields
-#define CMD_SEC 1
-#define CMD_MIN 0
-#define CMD_HOUR 3
-#define CMD_DAY 2
-#define CMD_MON 5
-#define CMD_YEAR 4
-#define CME_START_OFFSET 6 // 4 bytes
-#define CMD_WATER_CONDUCTIVITY 25 // 1 byte, 0=low, 2=high
-#define CMD_START_SGC 42 // 2 bytes
-#define CMD_START_TEMP 45 // 1 byte, F
-#define CMD_START_DEPTH 56 // 2 bytes, /4=ft
-#define CMD_START_PSI 62
-#define CMD_SIT 68 // 2 bytes, minutes
-#define CMD_NUMBER 70 // 2 bytes
-#define CMD_ALTITUDE 73 // 1 byte, /4=Kilofeet
-#define CMD_END_OFFSET 128 // 4 bytes
-#define CMD_MIN_TEMP 153 // 1 byte, F
-#define CMD_BT 166 // 2 bytes, minutes
-#define CMD_MAX_DEPTH 168 // 2 bytes, /4=ft
-#define CMD_AVG_DEPTH 170 // 2 bytes, /4=ft
-#define CMD_O2_PERCENT 210 // 8 bytes, 4 x 2 byte, /256=%
-
-// EMC log fields
-#define EMC_SEC 0
-#define EMC_MIN 1
-#define EMC_HOUR 2
-#define EMC_DAY 3
-#define EMC_MON 4
-#define EMC_YEAR 5
-#define EMC_START_OFFSET 6 // 4 bytes
-#define EMC_WATER_CONDUCTIVITY 24 // 1 byte bits 0:1, 0=low, 2=high
-#define EMC_START_DEPTH 42 // 2 byte, /256=ft
-#define EMC_START_TEMP 55 // 1 byte, F
-#define EMC_SIT 84 // 2 bytes, minutes, LE
-#define EMC_NUMBER 86 // 2 bytes
-#define EMC_ALTITUDE 89 // 1 byte, /4=Kilofeet
-#define EMC_O2_PERCENT 144 // 20 bytes, 10 x 2 bytes, /256=%
-#define EMC_HE_PERCENT 164 // 20 bytes, 10 x 2 bytes, /256=%
-#define EMC_END_OFFSET 256 // 4 bytes
-#define EMC_MIN_TEMP 293 // 1 byte, F
-#define EMC_BT 304 // 2 bytes, minutes
-#define EMC_MAX_DEPTH 306 // 2 bytes, /4=ft
-#define EMC_AVG_DEPTH 310 // 2 bytes, /4=ft
diff --git a/subsurface-core/color.cpp b/subsurface-core/color.cpp
deleted file mode 100644
index cf6f43916..000000000
--- a/subsurface-core/color.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-#include "color.h"
-
-QMap<color_indice_t, QVector<QColor> > profile_color;
-
-void fill_profile_color()
-{
-#define COLOR(x, y, z) QVector<QColor>() << x << y << z;
- profile_color[SAC_1] = COLOR(FUNGREEN1, BLACK1_LOW_TRANS, FUNGREEN1);
- profile_color[SAC_2] = COLOR(APPLE1, BLACK1_LOW_TRANS, APPLE1);
- profile_color[SAC_3] = COLOR(ATLANTIS1, BLACK1_LOW_TRANS, ATLANTIS1);
- profile_color[SAC_4] = COLOR(ATLANTIS2, BLACK1_LOW_TRANS, ATLANTIS2);
- profile_color[SAC_5] = COLOR(EARLSGREEN1, BLACK1_LOW_TRANS, EARLSGREEN1);
- profile_color[SAC_6] = COLOR(HOKEYPOKEY1, BLACK1_LOW_TRANS, HOKEYPOKEY1);
- profile_color[SAC_7] = COLOR(TUSCANY1, BLACK1_LOW_TRANS, TUSCANY1);
- profile_color[SAC_8] = COLOR(CINNABAR1, BLACK1_LOW_TRANS, CINNABAR1);
- profile_color[SAC_9] = COLOR(REDORANGE1, BLACK1_LOW_TRANS, REDORANGE1);
-
- profile_color[VELO_STABLE] = COLOR(CAMARONE1, BLACK1_LOW_TRANS, CAMARONE1);
- profile_color[VELO_SLOW] = COLOR(LIMENADE1, BLACK1_LOW_TRANS, LIMENADE1);
- profile_color[VELO_MODERATE] = COLOR(RIOGRANDE1, BLACK1_LOW_TRANS, RIOGRANDE1);
- profile_color[VELO_FAST] = COLOR(PIRATEGOLD1, BLACK1_LOW_TRANS, PIRATEGOLD1);
- profile_color[VELO_CRAZY] = COLOR(RED1, BLACK1_LOW_TRANS, RED1);
-
- profile_color[PO2] = COLOR(APPLE1, BLACK1_LOW_TRANS, APPLE1);
- profile_color[PO2_ALERT] = COLOR(RED1, BLACK1_LOW_TRANS, RED1);
- profile_color[PN2] = COLOR(BLACK1_LOW_TRANS, BLACK1_LOW_TRANS, BLACK1_LOW_TRANS);
- profile_color[PN2_ALERT] = COLOR(RED1, BLACK1_LOW_TRANS, RED1);
- profile_color[PHE] = COLOR(PEANUT, BLACK1_LOW_TRANS, PEANUT);
- profile_color[PHE_ALERT] = COLOR(RED1, BLACK1_LOW_TRANS, RED1);
- profile_color[O2SETPOINT] = COLOR(RED1, BLACK1_LOW_TRANS, RED1);
- profile_color[CCRSENSOR1] = COLOR(TUNDORA1_MED_TRANS, BLACK1_LOW_TRANS, TUNDORA1_MED_TRANS);
- profile_color[CCRSENSOR2] = COLOR(ROYALBLUE2_LOW_TRANS, BLACK1_LOW_TRANS, ROYALBLUE2_LOW_TRANS);
- profile_color[CCRSENSOR3] = COLOR(PEANUT, BLACK1_LOW_TRANS, PEANUT);
- profile_color[PP_LINES] = COLOR(BLACK1_HIGH_TRANS, BLACK1_LOW_TRANS, BLACK1_HIGH_TRANS);
-
- profile_color[TEXT_BACKGROUND] = COLOR(CONCRETE1_LOWER_TRANS, WHITE1, CONCRETE1_LOWER_TRANS);
- profile_color[ALERT_BG] = COLOR(BROOM1_LOWER_TRANS, BLACK1_LOW_TRANS, BROOM1_LOWER_TRANS);
- profile_color[ALERT_FG] = COLOR(BLACK1_LOW_TRANS, WHITE1, BLACK1_LOW_TRANS);
- profile_color[EVENTS] = COLOR(REDORANGE1, BLACK1_LOW_TRANS, REDORANGE1);
- profile_color[SAMPLE_DEEP] = COLOR(QColor(Qt::red).darker(), BLACK1, PERSIANRED1);
- profile_color[SAMPLE_SHALLOW] = COLOR(QColor(Qt::red).lighter(), BLACK1_LOW_TRANS, PERSIANRED1);
- profile_color[SMOOTHED] = COLOR(REDORANGE1_HIGH_TRANS, BLACK1_LOW_TRANS, REDORANGE1_HIGH_TRANS);
- profile_color[MINUTE] = COLOR(MEDIUMREDVIOLET1_HIGHER_TRANS, BLACK1_LOW_TRANS, MEDIUMREDVIOLET1_HIGHER_TRANS);
- profile_color[TIME_GRID] = COLOR(WHITE1, BLACK1_HIGH_TRANS, TUNDORA1_MED_TRANS);
- profile_color[TIME_TEXT] = COLOR(FORESTGREEN1, BLACK1, FORESTGREEN1);
- profile_color[DEPTH_GRID] = COLOR(WHITE1, BLACK1_HIGH_TRANS, TUNDORA1_MED_TRANS);
- profile_color[MEAN_DEPTH] = COLOR(REDORANGE1_MED_TRANS, BLACK1_LOW_TRANS, REDORANGE1_MED_TRANS);
- profile_color[HR_PLOT] = COLOR(REDORANGE1_MED_TRANS, BLACK1_LOW_TRANS, REDORANGE1_MED_TRANS);
- profile_color[HR_TEXT] = COLOR(REDORANGE1_MED_TRANS, BLACK1_LOW_TRANS, REDORANGE1_MED_TRANS);
- profile_color[HR_AXIS] = COLOR(MED_GRAY_HIGH_TRANS, MED_GRAY_HIGH_TRANS, MED_GRAY_HIGH_TRANS);
- profile_color[DEPTH_BOTTOM] = COLOR(GOVERNORBAY1_MED_TRANS, BLACK1_HIGH_TRANS, GOVERNORBAY1_MED_TRANS);
- profile_color[DEPTH_TOP] = COLOR(MERCURY1_MED_TRANS, WHITE1_MED_TRANS, MERCURY1_MED_TRANS);
- profile_color[TEMP_TEXT] = COLOR(GOVERNORBAY2, BLACK1_LOW_TRANS, GOVERNORBAY2);
- profile_color[TEMP_PLOT] = COLOR(ROYALBLUE2_LOW_TRANS, BLACK1_LOW_TRANS, ROYALBLUE2_LOW_TRANS);
- profile_color[SAC_DEFAULT] = COLOR(WHITE1, BLACK1_LOW_TRANS, FORESTGREEN1);
- profile_color[BOUNDING_BOX] = COLOR(WHITE1, BLACK1_LOW_TRANS, TUNDORA1_MED_TRANS);
- profile_color[PRESSURE_TEXT] = COLOR(KILLARNEY1, BLACK1_LOW_TRANS, KILLARNEY1);
- profile_color[BACKGROUND] = COLOR(SPRINGWOOD1, WHITE1, SPRINGWOOD1);
- profile_color[BACKGROUND_TRANS] = COLOR(SPRINGWOOD1_MED_TRANS, WHITE1_MED_TRANS, SPRINGWOOD1_MED_TRANS);
- profile_color[CEILING_SHALLOW] = COLOR(REDORANGE1_HIGH_TRANS, BLACK1_HIGH_TRANS, REDORANGE1_HIGH_TRANS);
- profile_color[CEILING_DEEP] = COLOR(RED1_MED_TRANS, BLACK1_HIGH_TRANS, RED1_MED_TRANS);
- profile_color[CALC_CEILING_SHALLOW] = COLOR(FUNGREEN1_HIGH_TRANS, BLACK1_HIGH_TRANS, FUNGREEN1_HIGH_TRANS);
- profile_color[CALC_CEILING_DEEP] = COLOR(APPLE1_HIGH_TRANS, BLACK1_HIGH_TRANS, APPLE1_HIGH_TRANS);
- profile_color[TISSUE_PERCENTAGE] = COLOR(GOVERNORBAY2, BLACK1_LOW_TRANS, GOVERNORBAY2);
- profile_color[GF_LINE] = COLOR(BLACK1, BLACK1_LOW_TRANS, BLACK1);
- profile_color[AMB_PRESSURE_LINE] = COLOR(TUNDORA1_MED_TRANS, BLACK1_LOW_TRANS, ATLANTIS1);
-#undef COLOR
-}
-
-QColor getColor(const color_indice_t i, bool isGrayscale)
-{
- if (profile_color.count() > i && i >= 0)
- return profile_color[i].at((isGrayscale) ? 1 : 0);
- return QColor(Qt::black);
-}
-
-QColor getSacColor(int sac, int avg_sac)
-{
- int sac_index = 0;
- int delta = sac - avg_sac + 7000;
-
- sac_index = delta / 2000;
- if (sac_index < 0)
- sac_index = 0;
- if (sac_index > SAC_COLORS - 1)
- sac_index = SAC_COLORS - 1;
- return getColor((color_indice_t)(SAC_COLORS_START_IDX + sac_index), false);
-}
diff --git a/subsurface-core/color.h b/subsurface-core/color.h
deleted file mode 100644
index 57ad77242..000000000
--- a/subsurface-core/color.h
+++ /dev/null
@@ -1,152 +0,0 @@
-#ifndef COLOR_H
-#define COLOR_H
-
-/* The colors are named by picking the closest match
- from http://chir.ag/projects/name-that-color */
-
-#include <QColor>
-#include <QMap>
-#include <QVector>
-
-// Greens
-#define CAMARONE1 QColor::fromRgbF(0.0, 0.4, 0.0, 1)
-#define FUNGREEN1 QColor::fromRgbF(0.0, 0.4, 0.2, 1)
-#define FUNGREEN1_HIGH_TRANS QColor::fromRgbF(0.0, 0.4, 0.2, 0.25)
-#define KILLARNEY1 QColor::fromRgbF(0.2, 0.4, 0.2, 1)
-#define APPLE1 QColor::fromRgbF(0.2, 0.6, 0.2, 1)
-#define APPLE1_MED_TRANS QColor::fromRgbF(0.2, 0.6, 0.2, 0.5)
-#define APPLE1_HIGH_TRANS QColor::fromRgbF(0.2, 0.6, 0.2, 0.25)
-#define LIMENADE1 QColor::fromRgbF(0.4, 0.8, 0.0, 1)
-#define ATLANTIS1 QColor::fromRgbF(0.4, 0.8, 0.2, 1)
-#define ATLANTIS2 QColor::fromRgbF(0.6, 0.8, 0.2, 1)
-#define RIOGRANDE1 QColor::fromRgbF(0.8, 0.8, 0.0, 1)
-#define EARLSGREEN1 QColor::fromRgbF(0.8, 0.8, 0.2, 1)
-#define FORESTGREEN1 QColor::fromRgbF(0.1, 0.5, 0.1, 1)
-#define NITROX_GREEN QColor::fromRgbF(0, 0.54, 0.375, 1)
-
-// Reds
-#define PERSIANRED1 QColor::fromRgbF(0.8, 0.2, 0.2, 1)
-#define TUSCANY1 QColor::fromRgbF(0.8, 0.4, 0.2, 1)
-#define PIRATEGOLD1 QColor::fromRgbF(0.8, 0.5, 0.0, 1)
-#define HOKEYPOKEY1 QColor::fromRgbF(0.8, 0.6, 0.2, 1)
-#define CINNABAR1 QColor::fromRgbF(0.9, 0.3, 0.2, 1)
-#define REDORANGE1 QColor::fromRgbF(1.0, 0.2, 0.2, 1)
-#define REDORANGE1_HIGH_TRANS QColor::fromRgbF(1.0, 0.2, 0.2, 0.25)
-#define REDORANGE1_MED_TRANS QColor::fromRgbF(1.0, 0.2, 0.2, 0.5)
-#define RED1_MED_TRANS QColor::fromRgbF(1.0, 0.0, 0.0, 0.5)
-#define RED1 QColor::fromRgbF(1.0, 0.0, 0.0, 1)
-
-// Monochromes
-#define BLACK1 QColor::fromRgbF(0.0, 0.0, 0.0, 1)
-#define BLACK1_LOW_TRANS QColor::fromRgbF(0.0, 0.0, 0.0, 0.75)
-#define BLACK1_HIGH_TRANS QColor::fromRgbF(0.0, 0.0, 0.0, 0.25)
-#define TUNDORA1_MED_TRANS QColor::fromRgbF(0.3, 0.3, 0.3, 0.5)
-#define MED_GRAY_HIGH_TRANS QColor::fromRgbF(0.5, 0.5, 0.5, 0.25)
-#define MERCURY1_MED_TRANS QColor::fromRgbF(0.9, 0.9, 0.9, 0.5)
-#define CONCRETE1_LOWER_TRANS QColor::fromRgbF(0.95, 0.95, 0.95, 0.9)
-#define WHITE1_MED_TRANS QColor::fromRgbF(1.0, 1.0, 1.0, 0.5)
-#define WHITE1 QColor::fromRgbF(1.0, 1.0, 1.0, 1)
-
-// Blues
-#define GOVERNORBAY2 QColor::fromRgbF(0.2, 0.2, 0.7, 1)
-#define GOVERNORBAY1_MED_TRANS QColor::fromRgbF(0.2, 0.2, 0.8, 0.5)
-#define ROYALBLUE2 QColor::fromRgbF(0.2, 0.2, 0.9, 1)
-#define ROYALBLUE2_LOW_TRANS QColor::fromRgbF(0.2, 0.2, 0.9, 0.75)
-#define AIR_BLUE QColor::fromRgbF(0.25, 0.75, 1.0, 1)
-#define AIR_BLUE_TRANS QColor::fromRgbF(0.25, 0.75, 1.0, 0.5)
-
-// Yellows / BROWNS
-#define SPRINGWOOD1 QColor::fromRgbF(0.95, 0.95, 0.9, 1)
-#define SPRINGWOOD1_MED_TRANS QColor::fromRgbF(0.95, 0.95, 0.9, 0.5)
-#define BROOM1_LOWER_TRANS QColor::fromRgbF(1.0, 1.0, 0.1, 0.9)
-#define PEANUT QColor::fromRgbF(0.5, 0.2, 0.1, 1.0)
-#define PEANUT_MED_TRANS QColor::fromRgbF(0.5, 0.2, 0.1, 0.5)
-#define NITROX_YELLOW QColor::fromRgbF(0.98, 0.89, 0.07, 1.0)
-
-// Magentas
-#define MEDIUMREDVIOLET1_HIGHER_TRANS QColor::fromRgbF(0.7, 0.2, 0.7, 0.1)
-
-#define SAC_COLORS_START_IDX SAC_1
-#define SAC_COLORS 9
-#define VELOCITY_COLORS_START_IDX VELO_STABLE
-#define VELOCITY_COLORS 5
-
-typedef enum {
- /* SAC colors. Order is important, the SAC_COLORS_START_IDX define above. */
- SAC_1,
- SAC_2,
- SAC_3,
- SAC_4,
- SAC_5,
- SAC_6,
- SAC_7,
- SAC_8,
- SAC_9,
-
- /* Velocity colors. Order is still important, ref VELOCITY_COLORS_START_IDX. */
- VELO_STABLE,
- VELO_SLOW,
- VELO_MODERATE,
- VELO_FAST,
- VELO_CRAZY,
-
- /* gas colors */
- PO2,
- PO2_ALERT,
- PN2,
- PN2_ALERT,
- PHE,
- PHE_ALERT,
- O2SETPOINT,
- CCRSENSOR1,
- CCRSENSOR2,
- CCRSENSOR3,
- PP_LINES,
-
- /* Other colors */
- TEXT_BACKGROUND,
- ALERT_BG,
- ALERT_FG,
- EVENTS,
- SAMPLE_DEEP,
- SAMPLE_SHALLOW,
- SMOOTHED,
- MINUTE,
- TIME_GRID,
- TIME_TEXT,
- DEPTH_GRID,
- MEAN_DEPTH,
- HR_TEXT,
- HR_PLOT,
- HR_AXIS,
- DEPTH_TOP,
- DEPTH_BOTTOM,
- TEMP_TEXT,
- TEMP_PLOT,
- SAC_DEFAULT,
- BOUNDING_BOX,
- PRESSURE_TEXT,
- BACKGROUND,
- BACKGROUND_TRANS,
- CEILING_SHALLOW,
- CEILING_DEEP,
- CALC_CEILING_SHALLOW,
- CALC_CEILING_DEEP,
- TISSUE_PERCENTAGE,
- GF_LINE,
- AMB_PRESSURE_LINE
-} color_indice_t;
-
-extern QMap<color_indice_t, QVector<QColor> > profile_color;
-void fill_profile_color();
-QColor getColor(const color_indice_t i, bool isGrayscale = false);
-QColor getSacColor(int sac, int diveSac);
-struct text_render_options {
- double size;
- color_indice_t color;
- double hpos, vpos;
-};
-
-typedef text_render_options text_render_options_t;
-
-#endif // COLOR_H
diff --git a/subsurface-core/compressibility.r b/subsurface-core/compressibility.r
deleted file mode 100644
index 66310f3aa..000000000
--- a/subsurface-core/compressibility.r
+++ /dev/null
@@ -1,115 +0,0 @@
-# Compressibility data gathered by Lubomir I Ivanov:
-#
-# "Data obtained by finding two books online:
-#
-# [1]
-# PERRY’S CHEMICAL ENGINEERS’ HANDBOOK SEVENTH EDITION
-# pretty serious book, from which the wiki AIR values come from!
-#
-# http://www.unhas.ac.id/rhiza/arsip/kuliah/Sistem-dan-Tekn-Kendali-Proses/PDF_Collections/REFERENSI/Perrys_Chemical_Engineering_Handbook.pdf
-# page 2-165
-#
-# [*](Computed from pressure-volume-temperature tables in Vasserman monographs)
-# ^ i have no idea idea what this means, but the values might not be exactly
-# experimental?!
-#
-# the only thing this book is missing is helium, thus [2]!
-#
-# [2]
-# VOLUMETRIC BEHAVIOR OF HELIUM-ARGON MIXTURES AT HIGH PRESSURE AND MODERATE TEMPERATURE.
-#
-# https://shareok.org/bitstream/handle/11244/2062/6614196.PDF?sequence=1
-# page 108
-#
-#
-# the book has some tables with pressure values in atmosphere units. i'm
-# converting them bars. one of the relevant tables is for 323K and one for 273K
-# (both almost equal distance from 300K).
-#
-# this again is a linear mix operation between isotherms, which is probably not
-# the most accurate solution but it works.
-#
-# all data sets contain Z values at 300k, while the pressures are in bars in
-# the 1 to 500 range
-#
-#
-
-x = c(1, 5, 10, 20, 40, 60, 80, 100, 200, 300, 400, 500)
-o2 = c(0.9994, 0.9968, 0.9941, 0.9884, 0.9771, 0.9676, 0.9597, 0.9542, 0.9560, 0.9972, 1.0689, 1.1572)
-n2 = c(0.9998, 0.9990, 0.9983, 0.9971, 0.9964, 0.9973, 1.0000, 1.0052, 1.0559, 1.1422, 1.2480, 1.3629)
-he = c(1.0005, 1.0024, 1.0048, 1.0096, 1.0191, 1.0286, 1.0381, 1.0476, 1.0943, 1.1402, 1.1854, 1.2297)
-
-options(digits=15)
-
-#
-# Get the O2 virial coefficients
-#
-plot(x,o2)
-o2fit = nls(o2 ~ 1.0 + p1*x + p2 *x^2 + p3*x^3, start=list(p1=0,p2=0,p3=0))
-summary(o2fit)
-
-new = data.frame(x = seq(min(x),max(x),len=200))
-lines(new$x,predict(o2fit,newdata=new))
-
-#
-# Get the N2 virial coefficients
-#
-plot(x,n2)
-n2fit = nls(n2 ~ 1.0 + p1*x + p2 *x^2 + p3*x^3, start=list(p1=0,p2=0,p3=0))
-summary(n2fit)
-
-new = data.frame(x = seq(min(x),max(x),len=200))
-lines(new$x,predict(n2fit,newdata=new))
-
-#
-# Get the He virial coefficients
-#
-# NOTE! This will not confirm convergence, thus the warnOnly.
-# That may be a sign that the data is possibly artificial.
-#
-plot(x,he)
-hefit = nls(he ~ 1.0 + p1*x + p2 *x^2 + p3*x^3,
- start=list(p1=0,p2=0,p3=0),
- control=nls.control(warnOnly=TRUE))
-summary(hefit)
-
-new = data.frame(x = seq(min(x),max(x),len=200))
-lines(new$x,predict(hefit,newdata=new))
-
-#
-# Raw data from VOLUMETRIC BEHAVIOR OF HELIUM-ARGON MIXTURES [..]
-# T=323.15K (50 C)
-p323atm = c(674.837, 393.223, 237.310, 146.294, 91.4027, 57.5799, 36.4620, 23.1654, 14.7478, 9.4017, 5.9987, 3.8300,
- 540.204, 319.943, 195.008, 120.951, 75.8599, 47.9005, 30.3791, 19.3193, 12.3080, 7.8495, 5.0100, 3.1992)
-
-Hez323 = c(1.28067, 1.16782, 1.10289, 1.06407, 1.04028, 1.02548, 1.01617, 1.01029, 1.00656, 1.00418, 1.00267, 1.00171,
- 1.22738, 1.13754, 1.08493, 1.05312, 1.03349, 1.02122, 1.01349, 1.00859, 1.00548, 1.00349, 1.00223, 1.00143)
-
-
-# T=273.15 (0 C)
-p273atm = c(683.599, 391.213, 233.607, 143.091, 89.0521, 55.9640, 35.3851, 22.4593, 14.2908, 9.1072, 5.8095, 3.7083,
- 534.047, 312.144, 188.741, 116.508, 72.8529, 45.9194, 29.0883, 18.4851, 11.7702, 7.5040, 4.7881, 3.0570)
-
-Hez273 = c(1.33969, 1.19985, 1.12121, 1.07494, 1.04689, 1.02957, 1.01874, 1.01191, 1.00758, 1.00484, 1.00309, 1.00197,
- 1.26914, 1.16070, 1.09837, 1.06118, 1.03843, 1.02429, 1.01541, 1.00980, 1.00625, 1.00398, 1.00254, 1.00162)
-
-p323 = p323atm * 1.01325
-p273 = p273atm * 1.01325
-
-x2=append(p323,p273)
-he2=append(Hez323,Hez273)
-
-plot(x2,he2)
-
-hefit2 = nls(he2 ~ 1.0 + p1*x2 + p2*x2^2 + p3*x2^3,
- start=list(p1=0,p2=0,p3=0))
-summary(hefit2)
-
-he3 = function(bar)
-{
- 1.0 +0.00047961098687979363 * bar -0.00000004077670019935 * bar^2 +0.00000000000077707035 * bar^3
-}
-
-new = data.frame(x2 = seq(min(x2),max(x2),len=200))
-lines(new$x2,predict(hefit2,newdata=new))
-curve(he3, min(x2),max(x2),add=TRUE)
diff --git a/subsurface-core/configuredivecomputer.cpp b/subsurface-core/configuredivecomputer.cpp
deleted file mode 100644
index 2457ffe82..000000000
--- a/subsurface-core/configuredivecomputer.cpp
+++ /dev/null
@@ -1,681 +0,0 @@
-#include "configuredivecomputer.h"
-#include "libdivecomputer/hw.h"
-#include <QTextStream>
-#include <QFile>
-#include <libxml/parser.h>
-#include <libxml/parserInternals.h>
-#include <libxml/tree.h>
-#include <libxslt/transform.h>
-#include <QStringList>
-#include <QXmlStreamWriter>
-
-ConfigureDiveComputer::ConfigureDiveComputer() : readThread(0),
- writeThread(0),
- resetThread(0),
- firmwareThread(0)
-{
- setState(INITIAL);
-}
-
-void ConfigureDiveComputer::readSettings(device_data_t *data)
-{
- setState(READING);
-
- if (readThread)
- readThread->deleteLater();
-
- readThread = new ReadSettingsThread(this, data);
- connect(readThread, SIGNAL(finished()),
- this, SLOT(readThreadFinished()), Qt::QueuedConnection);
- connect(readThread, SIGNAL(error(QString)), this, SLOT(setError(QString)));
- connect(readThread, SIGNAL(devicedetails(DeviceDetails *)), this,
- SIGNAL(deviceDetailsChanged(DeviceDetails *)));
- connect(readThread, SIGNAL(progress(int)), this, SLOT(progressEvent(int)), Qt::QueuedConnection);
-
- readThread->start();
-}
-
-void ConfigureDiveComputer::saveDeviceDetails(DeviceDetails *details, device_data_t *data)
-{
- setState(WRITING);
-
- if (writeThread)
- writeThread->deleteLater();
-
- writeThread = new WriteSettingsThread(this, data);
- connect(writeThread, SIGNAL(finished()),
- this, SLOT(writeThreadFinished()), Qt::QueuedConnection);
- connect(writeThread, SIGNAL(error(QString)), this, SLOT(setError(QString)));
- connect(writeThread, SIGNAL(progress(int)), this, SLOT(progressEvent(int)), Qt::QueuedConnection);
-
- writeThread->setDeviceDetails(details);
- writeThread->start();
-}
-
-bool ConfigureDiveComputer::saveXMLBackup(QString fileName, DeviceDetails *details, device_data_t *data)
-{
- QString xml = "";
- QString vendor = data->vendor;
- QString product = data->product;
- QXmlStreamWriter writer(&xml);
- writer.setAutoFormatting(true);
-
- writer.writeStartDocument();
- writer.writeStartElement("DiveComputerSettingsBackup");
- writer.writeStartElement("DiveComputer");
- writer.writeTextElement("Vendor", vendor);
- writer.writeTextElement("Product", product);
- writer.writeEndElement();
- writer.writeStartElement("Settings");
- writer.writeTextElement("CustomText", details->customText);
- //Add gasses
- QString gas1 = QString("%1,%2,%3,%4")
- .arg(QString::number(details->gas1.oxygen),
- QString::number(details->gas1.helium),
- QString::number(details->gas1.type),
- QString::number(details->gas1.depth));
- QString gas2 = QString("%1,%2,%3,%4")
- .arg(QString::number(details->gas2.oxygen),
- QString::number(details->gas2.helium),
- QString::number(details->gas2.type),
- QString::number(details->gas2.depth));
- QString gas3 = QString("%1,%2,%3,%4")
- .arg(QString::number(details->gas3.oxygen),
- QString::number(details->gas3.helium),
- QString::number(details->gas3.type),
- QString::number(details->gas3.depth));
- QString gas4 = QString("%1,%2,%3,%4")
- .arg(QString::number(details->gas4.oxygen),
- QString::number(details->gas4.helium),
- QString::number(details->gas4.type),
- QString::number(details->gas4.depth));
- QString gas5 = QString("%1,%2,%3,%4")
- .arg(QString::number(details->gas5.oxygen),
- QString::number(details->gas5.helium),
- QString::number(details->gas5.type),
- QString::number(details->gas5.depth));
- writer.writeTextElement("Gas1", gas1);
- writer.writeTextElement("Gas2", gas2);
- writer.writeTextElement("Gas3", gas3);
- writer.writeTextElement("Gas4", gas4);
- writer.writeTextElement("Gas5", gas5);
- //
- //Add dil values
- QString dil1 = QString("%1,%2,%3,%4")
- .arg(QString::number(details->dil1.oxygen),
- QString::number(details->dil1.helium),
- QString::number(details->dil1.type),
- QString::number(details->dil1.depth));
- QString dil2 = QString("%1,%2,%3,%4")
- .arg(QString::number(details->dil2.oxygen),
- QString::number(details->dil2.helium),
- QString::number(details->dil2.type),
- QString::number(details->dil2.depth));
- QString dil3 = QString("%1,%2,%3,%4")
- .arg(QString::number(details->dil3.oxygen),
- QString::number(details->dil3.helium),
- QString::number(details->dil3.type),
- QString::number(details->dil3.depth));
- QString dil4 = QString("%1,%2,%3,%4")
- .arg(QString::number(details->dil4.oxygen),
- QString::number(details->dil4.helium),
- QString::number(details->dil4.type),
- QString::number(details->dil4.depth));
- QString dil5 = QString("%1,%2,%3,%4")
- .arg(QString::number(details->dil5.oxygen),
- QString::number(details->dil5.helium),
- QString::number(details->dil5.type),
- QString::number(details->dil5.depth));
- writer.writeTextElement("Dil1", dil1);
- writer.writeTextElement("Dil2", dil2);
- writer.writeTextElement("Dil3", dil3);
- writer.writeTextElement("Dil4", dil4);
- writer.writeTextElement("Dil5", dil5);
- //
- //Add set point values
- QString sp1 = QString("%1,%2")
- .arg(QString::number(details->sp1.sp),
- QString::number(details->sp1.depth));
- QString sp2 = QString("%1,%2")
- .arg(QString::number(details->sp2.sp),
- QString::number(details->sp2.depth));
- QString sp3 = QString("%1,%2")
- .arg(QString::number(details->sp3.sp),
- QString::number(details->sp3.depth));
- QString sp4 = QString("%1,%2")
- .arg(QString::number(details->sp4.sp),
- QString::number(details->sp4.depth));
- QString sp5 = QString("%1,%2")
- .arg(QString::number(details->sp5.sp),
- QString::number(details->sp5.depth));
- writer.writeTextElement("SetPoint1", sp1);
- writer.writeTextElement("SetPoint2", sp2);
- writer.writeTextElement("SetPoint3", sp3);
- writer.writeTextElement("SetPoint4", sp4);
- writer.writeTextElement("SetPoint5", sp5);
-
- //Other Settings
- writer.writeTextElement("DiveMode", QString::number(details->diveMode));
- writer.writeTextElement("Saturation", QString::number(details->saturation));
- writer.writeTextElement("Desaturation", QString::number(details->desaturation));
- writer.writeTextElement("LastDeco", QString::number(details->lastDeco));
- writer.writeTextElement("Brightness", QString::number(details->brightness));
- writer.writeTextElement("Units", QString::number(details->units));
- writer.writeTextElement("SamplingRate", QString::number(details->samplingRate));
- writer.writeTextElement("Salinity", QString::number(details->salinity));
- writer.writeTextElement("DiveModeColor", QString::number(details->diveModeColor));
- writer.writeTextElement("Language", QString::number(details->language));
- writer.writeTextElement("DateFormat", QString::number(details->dateFormat));
- writer.writeTextElement("CompassGain", QString::number(details->compassGain));
- writer.writeTextElement("SafetyStop", QString::number(details->safetyStop));
- writer.writeTextElement("GfHigh", QString::number(details->gfHigh));
- writer.writeTextElement("GfLow", QString::number(details->gfLow));
- writer.writeTextElement("PressureSensorOffset", QString::number(details->pressureSensorOffset));
- writer.writeTextElement("PpO2Min", QString::number(details->ppO2Min));
- writer.writeTextElement("PpO2Max", QString::number(details->ppO2Max));
- writer.writeTextElement("FutureTTS", QString::number(details->futureTTS));
- writer.writeTextElement("CcrMode", QString::number(details->ccrMode));
- writer.writeTextElement("DecoType", QString::number(details->decoType));
- writer.writeTextElement("AGFSelectable", QString::number(details->aGFSelectable));
- writer.writeTextElement("AGFHigh", QString::number(details->aGFHigh));
- writer.writeTextElement("AGFLow", QString::number(details->aGFLow));
- writer.writeTextElement("CalibrationGas", QString::number(details->calibrationGas));
- writer.writeTextElement("FlipScreen", QString::number(details->flipScreen));
- writer.writeTextElement("SetPointFallback", QString::number(details->setPointFallback));
- writer.writeTextElement("LeftButtonSensitivity", QString::number(details->leftButtonSensitivity));
- writer.writeTextElement("RightButtonSensitivity", QString::number(details->rightButtonSensitivity));
- writer.writeTextElement("BottomGasConsumption", QString::number(details->bottomGasConsumption));
- writer.writeTextElement("DecoGasConsumption", QString::number(details->decoGasConsumption));
- writer.writeTextElement("ModWarning", QString::number(details->modWarning));
- writer.writeTextElement("DynamicAscendRate", QString::number(details->dynamicAscendRate));
- writer.writeTextElement("GraphicalSpeedIndicator", QString::number(details->graphicalSpeedIndicator));
- writer.writeTextElement("AlwaysShowppO2", QString::number(details->alwaysShowppO2));
-
- // Suunto vyper settings.
- writer.writeTextElement("Altitude", QString::number(details->altitude));
- writer.writeTextElement("PersonalSafety", QString::number(details->personalSafety));
- writer.writeTextElement("TimeFormat", QString::number(details->timeFormat));
-
- writer.writeStartElement("Light");
- writer.writeAttribute("enabled", QString::number(details->lightEnabled));
- writer.writeCharacters(QString::number(details->light));
- writer.writeEndElement();
-
- writer.writeStartElement("AlarmTime");
- writer.writeAttribute("enabled", QString::number(details->alarmTimeEnabled));
- writer.writeCharacters(QString::number(details->alarmTime));
- writer.writeEndElement();
-
- writer.writeStartElement("AlarmDepth");
- writer.writeAttribute("enabled", QString::number(details->alarmDepthEnabled));
- writer.writeCharacters(QString::number(details->alarmDepth));
- writer.writeEndElement();
-
- writer.writeEndElement();
- writer.writeEndElement();
-
- writer.writeEndDocument();
- QFile file(fileName);
- if (!file.open(QIODevice::WriteOnly)) {
- lastError = tr("Could not save the backup file %1. Error Message: %2")
- .arg(fileName, file.errorString());
- return false;
- }
- //file open successful. write data and save.
- QTextStream out(&file);
- out << xml;
-
- file.close();
- return true;
-}
-
-bool ConfigureDiveComputer::restoreXMLBackup(QString fileName, DeviceDetails *details)
-{
- QFile file(fileName);
- if (!file.open(QIODevice::ReadOnly)) {
- lastError = tr("Could not open backup file: %1").arg(file.errorString());
- return false;
- }
-
- QString xml = file.readAll();
-
- QXmlStreamReader reader(xml);
- while (!reader.atEnd()) {
- if (reader.isStartElement()) {
- QString settingName = reader.name().toString();
- QXmlStreamAttributes attributes = reader.attributes();
- reader.readNext();
- QString keyString = reader.text().toString();
-
- if (settingName == "CustomText")
- details->customText = keyString;
-
- if (settingName == "Gas1") {
- QStringList gasData = keyString.split(",");
- gas gas1;
- gas1.oxygen = gasData.at(0).toInt();
- gas1.helium = gasData.at(1).toInt();
- gas1.type = gasData.at(2).toInt();
- gas1.depth = gasData.at(3).toInt();
- details->gas1 = gas1;
- }
-
- if (settingName == "Gas2") {
- QStringList gasData = keyString.split(",");
- gas gas2;
- gas2.oxygen = gasData.at(0).toInt();
- gas2.helium = gasData.at(1).toInt();
- gas2.type = gasData.at(2).toInt();
- gas2.depth = gasData.at(3).toInt();
- details->gas2 = gas2;
- }
-
- if (settingName == "Gas3") {
- QStringList gasData = keyString.split(",");
- gas gas3;
- gas3.oxygen = gasData.at(0).toInt();
- gas3.helium = gasData.at(1).toInt();
- gas3.type = gasData.at(2).toInt();
- gas3.depth = gasData.at(3).toInt();
- details->gas3 = gas3;
- }
-
- if (settingName == "Gas4") {
- QStringList gasData = keyString.split(",");
- gas gas4;
- gas4.oxygen = gasData.at(0).toInt();
- gas4.helium = gasData.at(1).toInt();
- gas4.type = gasData.at(2).toInt();
- gas4.depth = gasData.at(3).toInt();
- details->gas4 = gas4;
- }
-
- if (settingName == "Gas5") {
- QStringList gasData = keyString.split(",");
- gas gas5;
- gas5.oxygen = gasData.at(0).toInt();
- gas5.helium = gasData.at(1).toInt();
- gas5.type = gasData.at(2).toInt();
- gas5.depth = gasData.at(3).toInt();
- details->gas5 = gas5;
- }
-
- if (settingName == "Dil1") {
- QStringList dilData = keyString.split(",");
- gas dil1;
- dil1.oxygen = dilData.at(0).toInt();
- dil1.helium = dilData.at(1).toInt();
- dil1.type = dilData.at(2).toInt();
- dil1.depth = dilData.at(3).toInt();
- details->dil1 = dil1;
- }
-
- if (settingName == "Dil2") {
- QStringList dilData = keyString.split(",");
- gas dil2;
- dil2.oxygen = dilData.at(0).toInt();
- dil2.helium = dilData.at(1).toInt();
- dil2.type = dilData.at(2).toInt();
- dil2.depth = dilData.at(3).toInt();
- details->dil1 = dil2;
- }
-
- if (settingName == "Dil3") {
- QStringList dilData = keyString.split(",");
- gas dil3;
- dil3.oxygen = dilData.at(0).toInt();
- dil3.helium = dilData.at(1).toInt();
- dil3.type = dilData.at(2).toInt();
- dil3.depth = dilData.at(3).toInt();
- details->dil3 = dil3;
- }
-
- if (settingName == "Dil4") {
- QStringList dilData = keyString.split(",");
- gas dil4;
- dil4.oxygen = dilData.at(0).toInt();
- dil4.helium = dilData.at(1).toInt();
- dil4.type = dilData.at(2).toInt();
- dil4.depth = dilData.at(3).toInt();
- details->dil4 = dil4;
- }
-
- if (settingName == "Dil5") {
- QStringList dilData = keyString.split(",");
- gas dil5;
- dil5.oxygen = dilData.at(0).toInt();
- dil5.helium = dilData.at(1).toInt();
- dil5.type = dilData.at(2).toInt();
- dil5.depth = dilData.at(3).toInt();
- details->dil5 = dil5;
- }
-
- if (settingName == "SetPoint1") {
- QStringList spData = keyString.split(",");
- setpoint sp1;
- sp1.sp = spData.at(0).toInt();
- sp1.depth = spData.at(1).toInt();
- details->sp1 = sp1;
- }
-
- if (settingName == "SetPoint2") {
- QStringList spData = keyString.split(",");
- setpoint sp2;
- sp2.sp = spData.at(0).toInt();
- sp2.depth = spData.at(1).toInt();
- details->sp2 = sp2;
- }
-
- if (settingName == "SetPoint3") {
- QStringList spData = keyString.split(",");
- setpoint sp3;
- sp3.sp = spData.at(0).toInt();
- sp3.depth = spData.at(1).toInt();
- details->sp3 = sp3;
- }
-
- if (settingName == "SetPoint4") {
- QStringList spData = keyString.split(",");
- setpoint sp4;
- sp4.sp = spData.at(0).toInt();
- sp4.depth = spData.at(1).toInt();
- details->sp4 = sp4;
- }
-
- if (settingName == "SetPoint5") {
- QStringList spData = keyString.split(",");
- setpoint sp5;
- sp5.sp = spData.at(0).toInt();
- sp5.depth = spData.at(1).toInt();
- details->sp5 = sp5;
- }
-
- if (settingName == "Saturation")
- details->saturation = keyString.toInt();
-
- if (settingName == "Desaturation")
- details->desaturation = keyString.toInt();
-
- if (settingName == "DiveMode")
- details->diveMode = keyString.toInt();
-
- if (settingName == "LastDeco")
- details->lastDeco = keyString.toInt();
-
- if (settingName == "Brightness")
- details->brightness = keyString.toInt();
-
- if (settingName == "Units")
- details->units = keyString.toInt();
-
- if (settingName == "SamplingRate")
- details->samplingRate = keyString.toInt();
-
- if (settingName == "Salinity")
- details->salinity = keyString.toInt();
-
- if (settingName == "DiveModeColour")
- details->diveModeColor = keyString.toInt();
-
- if (settingName == "Language")
- details->language = keyString.toInt();
-
- if (settingName == "DateFormat")
- details->dateFormat = keyString.toInt();
-
- if (settingName == "CompassGain")
- details->compassGain = keyString.toInt();
-
- if (settingName == "SafetyStop")
- details->safetyStop = keyString.toInt();
-
- if (settingName == "GfHigh")
- details->gfHigh = keyString.toInt();
-
- if (settingName == "GfLow")
- details->gfLow = keyString.toInt();
-
- if (settingName == "PressureSensorOffset")
- details->pressureSensorOffset = keyString.toInt();
-
- if (settingName == "PpO2Min")
- details->ppO2Min = keyString.toInt();
-
- if (settingName == "PpO2Max")
- details->ppO2Max = keyString.toInt();
-
- if (settingName == "FutureTTS")
- details->futureTTS = keyString.toInt();
-
- if (settingName == "CcrMode")
- details->ccrMode = keyString.toInt();
-
- if (settingName == "DecoType")
- details->decoType = keyString.toInt();
-
- if (settingName == "AGFSelectable")
- details->aGFSelectable = keyString.toInt();
-
- if (settingName == "AGFHigh")
- details->aGFHigh = keyString.toInt();
-
- if (settingName == "AGFLow")
- details->aGFLow = keyString.toInt();
-
- if (settingName == "CalibrationGas")
- details->calibrationGas = keyString.toInt();
-
- if (settingName == "FlipScreen")
- details->flipScreen = keyString.toInt();
-
- if (settingName == "SetPointFallback")
- details->setPointFallback = keyString.toInt();
-
- if (settingName == "LeftButtonSensitivity")
- details->leftButtonSensitivity = keyString.toInt();
-
- if (settingName == "RightButtonSensitivity")
- details->rightButtonSensitivity = keyString.toInt();
-
- if (settingName == "BottomGasConsumption")
- details->bottomGasConsumption = keyString.toInt();
-
- if (settingName == "DecoGasConsumption")
- details->decoGasConsumption = keyString.toInt();
-
- if (settingName == "ModWarning")
- details->modWarning = keyString.toInt();
-
- if (settingName == "DynamicAscendRate")
- details->dynamicAscendRate = keyString.toInt();
-
- if (settingName == "GraphicalSpeedIndicator")
- details->graphicalSpeedIndicator = keyString.toInt();
-
- if (settingName == "AlwaysShowppO2")
- details->alwaysShowppO2 = keyString.toInt();
-
- if (settingName == "Altitude")
- details->altitude = keyString.toInt();
-
- if (settingName == "PersonalSafety")
- details->personalSafety = keyString.toInt();
-
- if (settingName == "TimeFormat")
- details->timeFormat = keyString.toInt();
-
- if (settingName == "Light") {
- if (attributes.hasAttribute("enabled"))
- details->lightEnabled = attributes.value("enabled").toString().toInt();
- details->light = keyString.toInt();
- }
-
- if (settingName == "AlarmDepth") {
- if (attributes.hasAttribute("enabled"))
- details->alarmDepthEnabled = attributes.value("enabled").toString().toInt();
- details->alarmDepth = keyString.toInt();
- }
-
- if (settingName == "AlarmTime") {
- if (attributes.hasAttribute("enabled"))
- details->alarmTimeEnabled = attributes.value("enabled").toString().toInt();
- details->alarmTime = keyString.toInt();
- }
- }
- reader.readNext();
- }
-
- return true;
-}
-
-void ConfigureDiveComputer::startFirmwareUpdate(QString fileName, device_data_t *data)
-{
- setState(FWUPDATE);
- if (firmwareThread)
- firmwareThread->deleteLater();
-
- firmwareThread = new FirmwareUpdateThread(this, data, fileName);
- connect(firmwareThread, SIGNAL(finished()),
- this, SLOT(firmwareThreadFinished()), Qt::QueuedConnection);
- connect(firmwareThread, SIGNAL(error(QString)), this, SLOT(setError(QString)));
- connect(firmwareThread, SIGNAL(progress(int)), this, SLOT(progressEvent(int)), Qt::QueuedConnection);
-
- firmwareThread->start();
-}
-
-void ConfigureDiveComputer::resetSettings(device_data_t *data)
-{
- setState(RESETTING);
-
- if (resetThread)
- resetThread->deleteLater();
-
- resetThread = new ResetSettingsThread(this, data);
- connect(resetThread, SIGNAL(finished()),
- this, SLOT(resetThreadFinished()), Qt::QueuedConnection);
- connect(resetThread, SIGNAL(error(QString)), this, SLOT(setError(QString)));
- connect(resetThread, SIGNAL(progress(int)), this, SLOT(progressEvent(int)), Qt::QueuedConnection);
-
- resetThread->start();
-}
-
-void ConfigureDiveComputer::progressEvent(int percent)
-{
- emit progress(percent);
-}
-
-void ConfigureDiveComputer::setState(ConfigureDiveComputer::states newState)
-{
- currentState = newState;
- emit stateChanged(currentState);
-}
-
-void ConfigureDiveComputer::setError(QString err)
-{
- lastError = err;
- emit error(err);
-}
-
-void ConfigureDiveComputer::readThreadFinished()
-{
- setState(DONE);
- if (lastError.isEmpty()) {
- //No error
- emit message(tr("Dive computer details read successfully"));
- }
-}
-
-void ConfigureDiveComputer::writeThreadFinished()
-{
- setState(DONE);
- if (lastError.isEmpty()) {
- //No error
- emit message(tr("Setting successfully written to device"));
- }
-}
-
-void ConfigureDiveComputer::firmwareThreadFinished()
-{
- setState(DONE);
- if (lastError.isEmpty()) {
- //No error
- emit message(tr("Device firmware successfully updated"));
- }
-}
-
-void ConfigureDiveComputer::resetThreadFinished()
-{
- setState(DONE);
- if (lastError.isEmpty()) {
- //No error
- emit message(tr("Device settings successfully reset"));
- }
-}
-
-QString ConfigureDiveComputer::dc_open(device_data_t *data)
-{
- FILE *fp = NULL;
- dc_status_t rc;
-
- if (data->libdc_log)
- fp = subsurface_fopen(logfile_name, "w");
-
- data->libdc_logfile = fp;
-
- rc = dc_context_new(&data->context);
- if (rc != DC_STATUS_SUCCESS) {
- return tr("Unable to create libdivecomputer context");
- }
-
- if (fp) {
- dc_context_set_loglevel(data->context, DC_LOGLEVEL_ALL);
- dc_context_set_logfunc(data->context, logfunc, fp);
- }
-
-#if defined(SSRF_CUSTOM_SERIAL)
- dc_serial_t *serial_device = NULL;
-
- if (data->bluetooth_mode) {
-#ifdef BT_SUPPORT
- rc = dc_serial_qt_open(&serial_device, data->context, data->devname);
-#endif
-#ifdef SERIAL_FTDI
- } else if (!strcmp(data->devname, "ftdi")) {
- rc = dc_serial_ftdi_open(&serial_device, data->context);
-#endif
- }
-
- if (rc != DC_STATUS_SUCCESS) {
- return errmsg(rc);
- } else if (serial_device) {
- rc = dc_device_custom_open(&data->device, data->context, data->descriptor, serial_device);
- } else {
-#else
- {
-#endif
- rc = dc_device_open(&data->device, data->context, data->descriptor, data->devname);
- }
-
- if (rc != DC_STATUS_SUCCESS) {
- return tr("Could not a establish connection to the dive computer.");
- }
-
- setState(OPEN);
-
- return NULL;
-}
-
-void ConfigureDiveComputer::dc_close(device_data_t *data)
-{
- if (data->device)
- dc_device_close(data->device);
- data->device = NULL;
- if (data->context)
- dc_context_free(data->context);
- data->context = NULL;
-
- if (data->libdc_logfile)
- fclose(data->libdc_logfile);
-
- setState(INITIAL);
-}
diff --git a/subsurface-core/configuredivecomputer.h b/subsurface-core/configuredivecomputer.h
deleted file mode 100644
index f14eeeca3..000000000
--- a/subsurface-core/configuredivecomputer.h
+++ /dev/null
@@ -1,68 +0,0 @@
-#ifndef CONFIGUREDIVECOMPUTER_H
-#define CONFIGUREDIVECOMPUTER_H
-
-#include <QObject>
-#include <QThread>
-#include <QVariant>
-#include "libdivecomputer.h"
-#include "configuredivecomputerthreads.h"
-#include <QDateTime>
-
-#include "libxml/xmlreader.h"
-
-class ConfigureDiveComputer : public QObject {
- Q_OBJECT
-public:
- explicit ConfigureDiveComputer();
- void readSettings(device_data_t *data);
-
- enum states {
- OPEN,
- INITIAL,
- READING,
- WRITING,
- RESETTING,
- FWUPDATE,
- CANCELLING,
- CANCELLED,
- ERROR,
- DONE,
- };
-
- QString lastError;
- states currentState;
- void saveDeviceDetails(DeviceDetails *details, device_data_t *data);
- void fetchDeviceDetails();
- bool saveXMLBackup(QString fileName, DeviceDetails *details, device_data_t *data);
- bool restoreXMLBackup(QString fileName, DeviceDetails *details);
- void startFirmwareUpdate(QString fileName, device_data_t *data);
- void resetSettings(device_data_t *data);
-
- QString dc_open(device_data_t *data);
-public
-slots:
- void dc_close(device_data_t *data);
-signals:
- void progress(int percent);
- void message(QString msg);
- void error(QString err);
- void stateChanged(states newState);
- void deviceDetailsChanged(DeviceDetails *newDetails);
-
-private:
- ReadSettingsThread *readThread;
- WriteSettingsThread *writeThread;
- ResetSettingsThread *resetThread;
- FirmwareUpdateThread *firmwareThread;
- void setState(states newState);
-private
-slots:
- void progressEvent(int percent);
- void readThreadFinished();
- void writeThreadFinished();
- void resetThreadFinished();
- void firmwareThreadFinished();
- void setError(QString err);
-};
-
-#endif // CONFIGUREDIVECOMPUTER_H
diff --git a/subsurface-core/configuredivecomputerthreads.cpp b/subsurface-core/configuredivecomputerthreads.cpp
deleted file mode 100644
index b229fc808..000000000
--- a/subsurface-core/configuredivecomputerthreads.cpp
+++ /dev/null
@@ -1,1778 +0,0 @@
-#include "configuredivecomputerthreads.h"
-#include "libdivecomputer/hw.h"
-#include "libdivecomputer.h"
-#include <libdivecomputer/version.h>
-
-#define OSTC3_GAS1 0x10
-#define OSTC3_GAS2 0x11
-#define OSTC3_GAS3 0x12
-#define OSTC3_GAS4 0x13
-#define OSTC3_GAS5 0x14
-#define OSTC3_DIL1 0x15
-#define OSTC3_DIL2 0x16
-#define OSTC3_DIL3 0x17
-#define OSTC3_DIL4 0x18
-#define OSTC3_DIL5 0x19
-#define OSTC3_SP1 0x1A
-#define OSTC3_SP2 0x1B
-#define OSTC3_SP3 0x1C
-#define OSTC3_SP4 0x1D
-#define OSTC3_SP5 0x1E
-#define OSTC3_CCR_MODE 0x1F
-#define OSTC3_DIVE_MODE 0x20
-#define OSTC3_DECO_TYPE 0x21
-#define OSTC3_PPO2_MAX 0x22
-#define OSTC3_PPO2_MIN 0x23
-#define OSTC3_FUTURE_TTS 0x24
-#define OSTC3_GF_LOW 0x25
-#define OSTC3_GF_HIGH 0x26
-#define OSTC3_AGF_LOW 0x27
-#define OSTC3_AGF_HIGH 0x28
-#define OSTC3_AGF_SELECTABLE 0x29
-#define OSTC3_SATURATION 0x2A
-#define OSTC3_DESATURATION 0x2B
-#define OSTC3_LAST_DECO 0x2C
-#define OSTC3_BRIGHTNESS 0x2D
-#define OSTC3_UNITS 0x2E
-#define OSTC3_SAMPLING_RATE 0x2F
-#define OSTC3_SALINITY 0x30
-#define OSTC3_DIVEMODE_COLOR 0x31
-#define OSTC3_LANGUAGE 0x32
-#define OSTC3_DATE_FORMAT 0x33
-#define OSTC3_COMPASS_GAIN 0x34
-#define OSTC3_PRESSURE_SENSOR_OFFSET 0x35
-#define OSTC3_SAFETY_STOP 0x36
-#define OSTC3_CALIBRATION_GAS_O2 0x37
-#define OSTC3_SETPOINT_FALLBACK 0x38
-#define OSTC3_FLIP_SCREEN 0x39
-#define OSTC3_LEFT_BUTTON_SENSIVITY 0x3A
-#define OSTC3_RIGHT_BUTTON_SENSIVITY 0x3A
-#define OSTC3_BOTTOM_GAS_CONSUMPTION 0x3C
-#define OSTC3_DECO_GAS_CONSUMPTION 0x3D
-#define OSTC3_MOD_WARNING 0x3E
-#define OSTC3_DYNAMIC_ASCEND_RATE 0x3F
-#define OSTC3_GRAPHICAL_SPEED_INDICATOR 0x40
-#define OSTC3_ALWAYS_SHOW_PPO2 0x41
-#define OSTC3_TEMP_SENSOR_OFFSET 0x42
-#define OSTC3_SAFETY_STOP_LENGTH 0x43
-#define OSTC3_SAFETY_STOP_START_DEPTH 0x44
-#define OSTC3_SAFETY_STOP_END_DEPTH 0x45
-#define OSTC3_SAFETY_STOP_RESET_DEPTH 0x46
-
-#define OSTC3_HW_OSTC_3 0x0A
-#define OSTC3_HW_OSTC_3P 0x1A
-#define OSTC3_HW_OSTC_CR 0x05
-#define OSTC3_HW_OSTC_SPORT 0x12
-#define OSTC3_HW_OSTC_2 0x11
-
-
-#define SUUNTO_VYPER_MAXDEPTH 0x1e
-#define SUUNTO_VYPER_TOTAL_TIME 0x20
-#define SUUNTO_VYPER_NUMBEROFDIVES 0x22
-#define SUUNTO_VYPER_COMPUTER_TYPE 0x24
-#define SUUNTO_VYPER_FIRMWARE 0x25
-#define SUUNTO_VYPER_SERIALNUMBER 0x26
-#define SUUNTO_VYPER_CUSTOM_TEXT 0x2c
-#define SUUNTO_VYPER_SAMPLING_RATE 0x53
-#define SUUNTO_VYPER_ALTITUDE_SAFETY 0x54
-#define SUUNTO_VYPER_TIMEFORMAT 0x60
-#define SUUNTO_VYPER_UNITS 0x62
-#define SUUNTO_VYPER_MODEL 0x63
-#define SUUNTO_VYPER_LIGHT 0x64
-#define SUUNTO_VYPER_ALARM_DEPTH_TIME 0x65
-#define SUUNTO_VYPER_ALARM_TIME 0x66
-#define SUUNTO_VYPER_ALARM_DEPTH 0x68
-#define SUUNTO_VYPER_CUSTOM_TEXT_LENGHT 30
-
-#ifdef DEBUG_OSTC
-// Fake io to ostc memory banks
-#define hw_ostc_device_eeprom_read local_hw_ostc_device_eeprom_read
-#define hw_ostc_device_eeprom_write local_hw_ostc_device_eeprom_write
-#define hw_ostc_device_clock local_hw_ostc_device_clock
-#define OSTC_FILE "../OSTC-data-dump.bin"
-
-// Fake the open function.
-static dc_status_t local_dc_device_open(dc_device_t **out, dc_context_t *context, dc_descriptor_t *descriptor, const char *name)
-{
- if (strcmp(dc_descriptor_get_vendor(descriptor), "Heinrichs Weikamp") == 0 &&strcmp(dc_descriptor_get_product(descriptor), "OSTC 2N") == 0)
- return DC_STATUS_SUCCESS;
- else
- return dc_device_open(out, context, descriptor, name);
-}
-#define dc_device_open local_dc_device_open
-
-// Fake the custom open function
-static dc_status_t local_dc_device_custom_open(dc_device_t **out, dc_context_t *context, dc_descriptor_t *descriptor, dc_serial_t *serial)
-{
- if (strcmp(dc_descriptor_get_vendor(descriptor), "Heinrichs Weikamp") == 0 &&strcmp(dc_descriptor_get_product(descriptor), "OSTC 2N") == 0)
- return DC_STATUS_SUCCESS;
- else
- return dc_device_custom_open(out, context, descriptor, serial);
-}
-#define dc_device_custom_open local_dc_device_custom_open
-
-static dc_status_t local_hw_ostc_device_eeprom_read(void *ignored, unsigned char bank, unsigned char data[], unsigned int data_size)
-{
- FILE *f;
- if ((f = fopen(OSTC_FILE, "r")) == NULL)
- return DC_STATUS_NODEVICE;
- fseek(f, bank * 256, SEEK_SET);
- if (fread(data, sizeof(unsigned char), data_size, f) != data_size) {
- fclose(f);
- return DC_STATUS_IO;
- }
- fclose(f);
-
- return DC_STATUS_SUCCESS;
-}
-
-static dc_status_t local_hw_ostc_device_eeprom_write(void *ignored, unsigned char bank, unsigned char data[], unsigned int data_size)
-{
- FILE *f;
- if ((f = fopen(OSTC_FILE, "r+")) == NULL)
- f = fopen(OSTC_FILE, "w");
- fseek(f, bank * 256, SEEK_SET);
- fwrite(data, sizeof(unsigned char), data_size, f);
- fclose(f);
-
- return DC_STATUS_SUCCESS;
-}
-
-static dc_status_t local_hw_ostc_device_clock(void *ignored, dc_datetime_t *time)
-{
- return DC_STATUS_SUCCESS;
-}
-#endif
-
-static int read_ostc_cf(unsigned char data[], unsigned char cf)
-{
- return data[128 + (cf % 32) * 4 + 3] << 8 ^ data[128 + (cf % 32) * 4 + 2];
-}
-
-static void write_ostc_cf(unsigned char data[], unsigned char cf, unsigned char max_CF, unsigned int value)
-{
- // Only write settings supported by this firmware.
- if (cf > max_CF)
- return;
-
- data[128 + (cf % 32) * 4 + 3] = (value & 0xff00) >> 8;
- data[128 + (cf % 32) * 4 + 2] = (value & 0x00ff);
-}
-
-#define EMIT_PROGRESS() do { \
- progress.current++; \
- progress_cb(device, DC_EVENT_PROGRESS, &progress, userdata); \
- } while (0)
-
-static dc_status_t read_suunto_vyper_settings(dc_device_t *device, DeviceDetails *m_deviceDetails, dc_event_callback_t progress_cb, void *userdata)
-{
- unsigned char data[SUUNTO_VYPER_CUSTOM_TEXT_LENGHT + 1];
- dc_status_t rc;
- dc_event_progress_t progress;
- progress.current = 0;
- progress.maximum = 16;
-
- rc = dc_device_read(device, SUUNTO_VYPER_COMPUTER_TYPE, data, 1);
- if (rc == DC_STATUS_SUCCESS) {
- const char *model;
- // FIXME: grab this info from libdivecomputer descriptor
- // instead of hard coded here
- switch (data[0]) {
- case 0x03:
- model = "Stinger";
- break;
- case 0x04:
- model = "Mosquito";
- break;
- case 0x05:
- model = "D3";
- break;
- case 0x0A:
- model = "Vyper";
- break;
- case 0x0B:
- model = "Vytec";
- break;
- case 0x0C:
- model = "Cobra";
- break;
- case 0x0D:
- model = "Gekko";
- break;
- case 0x16:
- model = "Zoop";
- break;
- case 20:
- case 30:
- case 60:
- // Suunto Spyder have there sample interval at this position
- // Fallthrough
- default:
- return DC_STATUS_UNSUPPORTED;
- }
- // We found a supported device
- // we can safely proceed with reading/writing to this device.
- m_deviceDetails->model = model;
- }
- EMIT_PROGRESS();
-
- rc = dc_device_read(device, SUUNTO_VYPER_MAXDEPTH, data, 2);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- // in ft * 128.0
- int depth = feet_to_mm(data[0] << 8 ^ data[1]) / 128;
- m_deviceDetails->maxDepth = depth;
- EMIT_PROGRESS();
-
- rc = dc_device_read(device, SUUNTO_VYPER_TOTAL_TIME, data, 2);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- int total_time = data[0] << 8 ^ data[1];
- m_deviceDetails->totalTime = total_time;
- EMIT_PROGRESS();
-
- rc = dc_device_read(device, SUUNTO_VYPER_NUMBEROFDIVES, data, 2);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- int number_of_dives = data[0] << 8 ^ data[1];
- m_deviceDetails->numberOfDives = number_of_dives;
- EMIT_PROGRESS();
-
- rc = dc_device_read(device, SUUNTO_VYPER_FIRMWARE, data, 1);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- m_deviceDetails->firmwareVersion = QString::number(data[0]) + ".0.0";
- EMIT_PROGRESS();
-
- rc = dc_device_read(device, SUUNTO_VYPER_SERIALNUMBER, data, 4);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- int serial_number = data[0] * 1000000 + data[1] * 10000 + data[2] * 100 + data[3];
- m_deviceDetails->serialNo = QString::number(serial_number);
- EMIT_PROGRESS();
-
- rc = dc_device_read(device, SUUNTO_VYPER_CUSTOM_TEXT, data, SUUNTO_VYPER_CUSTOM_TEXT_LENGHT);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- data[SUUNTO_VYPER_CUSTOM_TEXT_LENGHT] = 0;
- m_deviceDetails->customText = (const char *)data;
- EMIT_PROGRESS();
-
- rc = dc_device_read(device, SUUNTO_VYPER_SAMPLING_RATE, data, 1);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- m_deviceDetails->samplingRate = (int)data[0];
- EMIT_PROGRESS();
-
- rc = dc_device_read(device, SUUNTO_VYPER_ALTITUDE_SAFETY, data, 1);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- m_deviceDetails->altitude = data[0] & 0x03;
- m_deviceDetails->personalSafety = data[0] >> 2 & 0x03;
- EMIT_PROGRESS();
-
- rc = dc_device_read(device, SUUNTO_VYPER_TIMEFORMAT, data, 1);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- m_deviceDetails->timeFormat = data[0] & 0x01;
- EMIT_PROGRESS();
-
- rc = dc_device_read(device, SUUNTO_VYPER_UNITS, data, 1);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- m_deviceDetails->units = data[0] & 0x01;
- EMIT_PROGRESS();
-
- rc = dc_device_read(device, SUUNTO_VYPER_MODEL, data, 1);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- m_deviceDetails->diveMode = data[0] & 0x03;
- EMIT_PROGRESS();
-
- rc = dc_device_read(device, SUUNTO_VYPER_LIGHT, data, 1);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- m_deviceDetails->lightEnabled = data[0] >> 7;
- m_deviceDetails->light = data[0] & 0x7F;
- EMIT_PROGRESS();
-
- rc = dc_device_read(device, SUUNTO_VYPER_ALARM_DEPTH_TIME, data, 1);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- m_deviceDetails->alarmTimeEnabled = data[0] & 0x01;
- m_deviceDetails->alarmDepthEnabled = data[0] >> 1 & 0x01;
- EMIT_PROGRESS();
-
- rc = dc_device_read(device, SUUNTO_VYPER_ALARM_TIME, data, 2);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- int time = data[0] << 8 ^ data[1];
- // The stinger stores alarm time in seconds instead of minutes.
- if (m_deviceDetails->model == "Stinger")
- time /= 60;
- m_deviceDetails->alarmTime = time;
- EMIT_PROGRESS();
-
- rc = dc_device_read(device, SUUNTO_VYPER_ALARM_DEPTH, data, 2);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- depth = feet_to_mm(data[0] << 8 ^ data[1]) / 128;
- m_deviceDetails->alarmDepth = depth;
- EMIT_PROGRESS();
-
- return DC_STATUS_SUCCESS;
-}
-
-static dc_status_t write_suunto_vyper_settings(dc_device_t *device, DeviceDetails *m_deviceDetails, dc_event_callback_t progress_cb, void *userdata)
-{
- dc_status_t rc;
- dc_event_progress_t progress;
- progress.current = 0;
- progress.maximum = 10;
- unsigned char data;
- unsigned char data2[2];
- int time;
-
- // Maybee we should read the model from the device to sanity check it here too..
- // For now we just check that we actually read a device before writing to one.
- if (m_deviceDetails->model == "")
- return DC_STATUS_UNSUPPORTED;
-
- rc = dc_device_write(device, SUUNTO_VYPER_CUSTOM_TEXT,
- // Convert the customText to a 30 char wide padded with " "
- (const unsigned char *)QString("%1").arg(m_deviceDetails->customText, -30, QChar(' ')).toUtf8().data(),
- SUUNTO_VYPER_CUSTOM_TEXT_LENGHT);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- data = m_deviceDetails->samplingRate;
- rc = dc_device_write(device, SUUNTO_VYPER_SAMPLING_RATE, &data, 1);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- data = m_deviceDetails->personalSafety << 2 ^ m_deviceDetails->altitude;
- rc = dc_device_write(device, SUUNTO_VYPER_ALTITUDE_SAFETY, &data, 1);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- data = m_deviceDetails->timeFormat;
- rc = dc_device_write(device, SUUNTO_VYPER_TIMEFORMAT, &data, 1);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- data = m_deviceDetails->units;
- rc = dc_device_write(device, SUUNTO_VYPER_UNITS, &data, 1);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- data = m_deviceDetails->diveMode;
- rc = dc_device_write(device, SUUNTO_VYPER_MODEL, &data, 1);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- data = m_deviceDetails->lightEnabled << 7 ^ (m_deviceDetails->light & 0x7F);
- rc = dc_device_write(device, SUUNTO_VYPER_LIGHT, &data, 1);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- data = m_deviceDetails->alarmDepthEnabled << 1 ^ m_deviceDetails->alarmTimeEnabled;
- rc = dc_device_write(device, SUUNTO_VYPER_ALARM_DEPTH_TIME, &data, 1);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- // The stinger stores alarm time in seconds instead of minutes.
- time = m_deviceDetails->alarmTime;
- if (m_deviceDetails->model == "Stinger")
- time *= 60;
- data2[0] = time >> 8;
- data2[1] = time & 0xFF;
- rc = dc_device_write(device, SUUNTO_VYPER_ALARM_TIME, data2, 2);
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- data2[0] = (int)(mm_to_feet(m_deviceDetails->alarmDepth) * 128) >> 8;
- data2[1] = (int)(mm_to_feet(m_deviceDetails->alarmDepth) * 128) & 0x0FF;
- rc = dc_device_write(device, SUUNTO_VYPER_ALARM_DEPTH, data2, 2);
- EMIT_PROGRESS();
- return rc;
-}
-
-#if DC_VERSION_CHECK(0, 5, 0)
-static dc_status_t read_ostc3_settings(dc_device_t *device, DeviceDetails *m_deviceDetails, dc_event_callback_t progress_cb, void *userdata)
-{
- dc_status_t rc;
- dc_event_progress_t progress;
- progress.current = 0;
- progress.maximum = 57;
- unsigned char hardware[1];
-
- //Read hardware type
- rc = hw_ostc3_device_hardware (device, hardware, sizeof (hardware));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- // FIXME: can we grab this info from libdivecomputer descriptor
- // instead of hard coded here?
- switch(hardware[0]) {
- case OSTC3_HW_OSTC_3:
- m_deviceDetails->model = "3";
- break;
- case OSTC3_HW_OSTC_3P:
- m_deviceDetails->model = "3+";
- break;
- case OSTC3_HW_OSTC_CR:
- m_deviceDetails->model = "CR";
- break;
- case OSTC3_HW_OSTC_SPORT:
- m_deviceDetails->model = "Sport";
- break;
- case OSTC3_HW_OSTC_2:
- m_deviceDetails->model = "2";
- break;
- }
-
- //Read gas mixes
- gas gas1;
- gas gas2;
- gas gas3;
- gas gas4;
- gas gas5;
- unsigned char gasData[4] = { 0, 0, 0, 0 };
-
- rc = hw_ostc3_device_config_read(device, OSTC3_GAS1, gasData, sizeof(gasData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- gas1.oxygen = gasData[0];
- gas1.helium = gasData[1];
- gas1.type = gasData[2];
- gas1.depth = gasData[3];
- EMIT_PROGRESS();
-
- rc = hw_ostc3_device_config_read(device, OSTC3_GAS2, gasData, sizeof(gasData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- gas2.oxygen = gasData[0];
- gas2.helium = gasData[1];
- gas2.type = gasData[2];
- gas2.depth = gasData[3];
- EMIT_PROGRESS();
-
- rc = hw_ostc3_device_config_read(device, OSTC3_GAS3, gasData, sizeof(gasData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- gas3.oxygen = gasData[0];
- gas3.helium = gasData[1];
- gas3.type = gasData[2];
- gas3.depth = gasData[3];
- EMIT_PROGRESS();
-
- rc = hw_ostc3_device_config_read(device, OSTC3_GAS4, gasData, sizeof(gasData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- gas4.oxygen = gasData[0];
- gas4.helium = gasData[1];
- gas4.type = gasData[2];
- gas4.depth = gasData[3];
- EMIT_PROGRESS();
-
- rc = hw_ostc3_device_config_read(device, OSTC3_GAS5, gasData, sizeof(gasData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- gas5.oxygen = gasData[0];
- gas5.helium = gasData[1];
- gas5.type = gasData[2];
- gas5.depth = gasData[3];
- EMIT_PROGRESS();
-
- m_deviceDetails->gas1 = gas1;
- m_deviceDetails->gas2 = gas2;
- m_deviceDetails->gas3 = gas3;
- m_deviceDetails->gas4 = gas4;
- m_deviceDetails->gas5 = gas5;
- EMIT_PROGRESS();
-
- //Read Dil Values
- gas dil1;
- gas dil2;
- gas dil3;
- gas dil4;
- gas dil5;
- unsigned char dilData[4] = { 0, 0, 0, 0 };
-
- rc = hw_ostc3_device_config_read(device, OSTC3_DIL1, dilData, sizeof(dilData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- dil1.oxygen = dilData[0];
- dil1.helium = dilData[1];
- dil1.type = dilData[2];
- dil1.depth = dilData[3];
- EMIT_PROGRESS();
-
- rc = hw_ostc3_device_config_read(device, OSTC3_DIL2, dilData, sizeof(dilData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- dil2.oxygen = dilData[0];
- dil2.helium = dilData[1];
- dil2.type = dilData[2];
- dil2.depth = dilData[3];
- EMIT_PROGRESS();
-
- rc = hw_ostc3_device_config_read(device, OSTC3_DIL3, dilData, sizeof(dilData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- dil3.oxygen = dilData[0];
- dil3.helium = dilData[1];
- dil3.type = dilData[2];
- dil3.depth = dilData[3];
- EMIT_PROGRESS();
-
- rc = hw_ostc3_device_config_read(device, OSTC3_DIL4, dilData, sizeof(dilData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- dil4.oxygen = dilData[0];
- dil4.helium = dilData[1];
- dil4.type = dilData[2];
- dil4.depth = dilData[3];
- EMIT_PROGRESS();
-
- rc = hw_ostc3_device_config_read(device, OSTC3_DIL5, dilData, sizeof(dilData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- dil5.oxygen = dilData[0];
- dil5.helium = dilData[1];
- dil5.type = dilData[2];
- dil5.depth = dilData[3];
- EMIT_PROGRESS();
-
- m_deviceDetails->dil1 = dil1;
- m_deviceDetails->dil2 = dil2;
- m_deviceDetails->dil3 = dil3;
- m_deviceDetails->dil4 = dil4;
- m_deviceDetails->dil5 = dil5;
-
- //Read set point Values
- setpoint sp1;
- setpoint sp2;
- setpoint sp3;
- setpoint sp4;
- setpoint sp5;
- unsigned char spData[2] = { 0, 0 };
-
- rc = hw_ostc3_device_config_read(device, OSTC3_SP1, spData, sizeof(spData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- sp1.sp = spData[0];
- sp1.depth = spData[1];
- EMIT_PROGRESS();
-
- rc = hw_ostc3_device_config_read(device, OSTC3_SP2, spData, sizeof(spData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- sp2.sp = spData[0];
- sp2.depth = spData[1];
- EMIT_PROGRESS();
-
- rc = hw_ostc3_device_config_read(device, OSTC3_SP3, spData, sizeof(spData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- sp3.sp = spData[0];
- sp3.depth = spData[1];
- EMIT_PROGRESS();
-
- rc = hw_ostc3_device_config_read(device, OSTC3_SP4, spData, sizeof(spData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- sp4.sp = spData[0];
- sp4.depth = spData[1];
- EMIT_PROGRESS();
-
- rc = hw_ostc3_device_config_read(device, OSTC3_SP5, spData, sizeof(spData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- sp5.sp = spData[0];
- sp5.depth = spData[1];
- EMIT_PROGRESS();
-
- m_deviceDetails->sp1 = sp1;
- m_deviceDetails->sp2 = sp2;
- m_deviceDetails->sp3 = sp3;
- m_deviceDetails->sp4 = sp4;
- m_deviceDetails->sp5 = sp5;
-
- //Read other settings
- unsigned char uData[1] = { 0 };
-
-#define READ_SETTING(_OSTC3_SETTING, _DEVICE_DETAIL) \
- do { \
- rc = hw_ostc3_device_config_read(device, _OSTC3_SETTING, uData, sizeof(uData)); \
- if (rc != DC_STATUS_SUCCESS) \
- return rc; \
- m_deviceDetails->_DEVICE_DETAIL = uData[0]; \
- EMIT_PROGRESS(); \
- } while (0)
-
- READ_SETTING(OSTC3_DIVE_MODE, diveMode);
- READ_SETTING(OSTC3_SATURATION, saturation);
- READ_SETTING(OSTC3_DESATURATION, desaturation);
- READ_SETTING(OSTC3_LAST_DECO, lastDeco);
- READ_SETTING(OSTC3_BRIGHTNESS, brightness);
- READ_SETTING(OSTC3_UNITS, units);
- READ_SETTING(OSTC3_SAMPLING_RATE, samplingRate);
- READ_SETTING(OSTC3_SALINITY, salinity);
- READ_SETTING(OSTC3_DIVEMODE_COLOR, diveModeColor);
- READ_SETTING(OSTC3_LANGUAGE, language);
- READ_SETTING(OSTC3_DATE_FORMAT, dateFormat);
- READ_SETTING(OSTC3_COMPASS_GAIN, compassGain);
- READ_SETTING(OSTC3_SAFETY_STOP, safetyStop);
- READ_SETTING(OSTC3_GF_HIGH, gfHigh);
- READ_SETTING(OSTC3_GF_LOW, gfLow);
- READ_SETTING(OSTC3_PPO2_MIN, ppO2Min);
- READ_SETTING(OSTC3_PPO2_MAX, ppO2Max);
- READ_SETTING(OSTC3_FUTURE_TTS, futureTTS);
- READ_SETTING(OSTC3_CCR_MODE, ccrMode);
- READ_SETTING(OSTC3_DECO_TYPE, decoType);
- READ_SETTING(OSTC3_AGF_SELECTABLE, aGFSelectable);
- READ_SETTING(OSTC3_AGF_HIGH, aGFHigh);
- READ_SETTING(OSTC3_AGF_LOW, aGFLow);
- READ_SETTING(OSTC3_CALIBRATION_GAS_O2, calibrationGas);
- READ_SETTING(OSTC3_FLIP_SCREEN, flipScreen);
- READ_SETTING(OSTC3_SETPOINT_FALLBACK, setPointFallback);
- READ_SETTING(OSTC3_LEFT_BUTTON_SENSIVITY, leftButtonSensitivity);
- READ_SETTING(OSTC3_RIGHT_BUTTON_SENSIVITY, rightButtonSensitivity);
- READ_SETTING(OSTC3_BOTTOM_GAS_CONSUMPTION, bottomGasConsumption);
- READ_SETTING(OSTC3_DECO_GAS_CONSUMPTION, decoGasConsumption);
- READ_SETTING(OSTC3_MOD_WARNING, modWarning);
- READ_SETTING(OSTC3_DYNAMIC_ASCEND_RATE, dynamicAscendRate);
- READ_SETTING(OSTC3_GRAPHICAL_SPEED_INDICATOR, graphicalSpeedIndicator);
- READ_SETTING(OSTC3_ALWAYS_SHOW_PPO2, alwaysShowppO2);
- READ_SETTING(OSTC3_SAFETY_STOP_LENGTH, safetyStopLength);
- READ_SETTING(OSTC3_SAFETY_STOP_START_DEPTH, safetyStopStartDepth);
- READ_SETTING(OSTC3_SAFETY_STOP_END_DEPTH, safetyStopEndDepth);
- READ_SETTING(OSTC3_SAFETY_STOP_RESET_DEPTH, safetyStopResetDepth);
-
-#undef READ_SETTING
-
- rc = hw_ostc3_device_config_read(device, OSTC3_PRESSURE_SENSOR_OFFSET, uData, sizeof(uData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- // OSTC3 stores the pressureSensorOffset in two-complement
- m_deviceDetails->pressureSensorOffset = (signed char)uData[0];
- EMIT_PROGRESS();
-
- rc = hw_ostc3_device_config_read(device, OSTC3_TEMP_SENSOR_OFFSET, uData, sizeof(uData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- // OSTC3 stores the tempSensorOffset in two-complement
- m_deviceDetails->tempSensorOffset = (signed char)uData[0];
- EMIT_PROGRESS();
-
- //read firmware settings
- unsigned char fData[64] = { 0 };
- rc = hw_ostc3_device_version(device, fData, sizeof(fData));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- int serial = fData[0] + (fData[1] << 8);
- m_deviceDetails->serialNo = QString::number(serial);
- m_deviceDetails->firmwareVersion = QString::number(fData[2]) + "." + QString::number(fData[3]);
- QByteArray ar((char *)fData + 4, 60);
- m_deviceDetails->customText = ar.trimmed();
- EMIT_PROGRESS();
-
- return rc;
-}
-
-static dc_status_t write_ostc3_settings(dc_device_t *device, DeviceDetails *m_deviceDetails, dc_event_callback_t progress_cb, void *userdata)
-{
- dc_status_t rc;
- dc_event_progress_t progress;
- progress.current = 0;
- progress.maximum = 56;
-
- //write gas values
- unsigned char gas1Data[4] = {
- m_deviceDetails->gas1.oxygen,
- m_deviceDetails->gas1.helium,
- m_deviceDetails->gas1.type,
- m_deviceDetails->gas1.depth
- };
-
- unsigned char gas2Data[4] = {
- m_deviceDetails->gas2.oxygen,
- m_deviceDetails->gas2.helium,
- m_deviceDetails->gas2.type,
- m_deviceDetails->gas2.depth
- };
-
- unsigned char gas3Data[4] = {
- m_deviceDetails->gas3.oxygen,
- m_deviceDetails->gas3.helium,
- m_deviceDetails->gas3.type,
- m_deviceDetails->gas3.depth
- };
-
- unsigned char gas4Data[4] = {
- m_deviceDetails->gas4.oxygen,
- m_deviceDetails->gas4.helium,
- m_deviceDetails->gas4.type,
- m_deviceDetails->gas4.depth
- };
-
- unsigned char gas5Data[4] = {
- m_deviceDetails->gas5.oxygen,
- m_deviceDetails->gas5.helium,
- m_deviceDetails->gas5.type,
- m_deviceDetails->gas5.depth
- };
- //gas 1
- rc = hw_ostc3_device_config_write(device, OSTC3_GAS1, gas1Data, sizeof(gas1Data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- //gas 2
- rc = hw_ostc3_device_config_write(device, OSTC3_GAS2, gas2Data, sizeof(gas2Data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- //gas 3
- rc = hw_ostc3_device_config_write(device, OSTC3_GAS3, gas3Data, sizeof(gas3Data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- //gas 4
- rc = hw_ostc3_device_config_write(device, OSTC3_GAS4, gas4Data, sizeof(gas4Data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- //gas 5
- rc = hw_ostc3_device_config_write(device, OSTC3_GAS5, gas5Data, sizeof(gas5Data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- //write set point values
- unsigned char sp1Data[2] = {
- m_deviceDetails->sp1.sp,
- m_deviceDetails->sp1.depth
- };
-
- unsigned char sp2Data[2] = {
- m_deviceDetails->sp2.sp,
- m_deviceDetails->sp2.depth
- };
-
- unsigned char sp3Data[2] = {
- m_deviceDetails->sp3.sp,
- m_deviceDetails->sp3.depth
- };
-
- unsigned char sp4Data[2] = {
- m_deviceDetails->sp4.sp,
- m_deviceDetails->sp4.depth
- };
-
- unsigned char sp5Data[2] = {
- m_deviceDetails->sp5.sp,
- m_deviceDetails->sp5.depth
- };
-
- //sp 1
- rc = hw_ostc3_device_config_write(device, OSTC3_SP1, sp1Data, sizeof(sp1Data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- //sp 2
- rc = hw_ostc3_device_config_write(device, OSTC3_SP2, sp2Data, sizeof(sp2Data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- //sp 3
- rc = hw_ostc3_device_config_write(device, OSTC3_SP3, sp3Data, sizeof(sp3Data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- //sp 4
- rc = hw_ostc3_device_config_write(device, OSTC3_SP4, sp4Data, sizeof(sp4Data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- //sp 5
- rc = hw_ostc3_device_config_write(device, OSTC3_SP5, sp5Data, sizeof(sp5Data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- //write dil values
- unsigned char dil1Data[4] = {
- m_deviceDetails->dil1.oxygen,
- m_deviceDetails->dil1.helium,
- m_deviceDetails->dil1.type,
- m_deviceDetails->dil1.depth
- };
-
- unsigned char dil2Data[4] = {
- m_deviceDetails->dil2.oxygen,
- m_deviceDetails->dil2.helium,
- m_deviceDetails->dil2.type,
- m_deviceDetails->dil2.depth
- };
-
- unsigned char dil3Data[4] = {
- m_deviceDetails->dil3.oxygen,
- m_deviceDetails->dil3.helium,
- m_deviceDetails->dil3.type,
- m_deviceDetails->dil3.depth
- };
-
- unsigned char dil4Data[4] = {
- m_deviceDetails->dil4.oxygen,
- m_deviceDetails->dil4.helium,
- m_deviceDetails->dil4.type,
- m_deviceDetails->dil4.depth
- };
-
- unsigned char dil5Data[4] = {
- m_deviceDetails->dil5.oxygen,
- m_deviceDetails->dil5.helium,
- m_deviceDetails->dil5.type,
- m_deviceDetails->dil5.depth
- };
- //dil 1
- rc = hw_ostc3_device_config_write(device, OSTC3_DIL1, dil1Data, sizeof(gas1Data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- //dil 2
- rc = hw_ostc3_device_config_write(device, OSTC3_DIL2, dil2Data, sizeof(dil2Data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- //dil 3
- rc = hw_ostc3_device_config_write(device, OSTC3_DIL3, dil3Data, sizeof(dil3Data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- //dil 4
- rc = hw_ostc3_device_config_write(device, OSTC3_DIL4, dil4Data, sizeof(dil4Data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- //dil 5
- rc = hw_ostc3_device_config_write(device, OSTC3_DIL5, dil5Data, sizeof(dil5Data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- //write general settings
- //custom text
- rc = hw_ostc3_device_customtext(device, m_deviceDetails->customText.toUtf8().data());
- if (rc != DC_STATUS_SUCCESS)
- return rc;
-
- unsigned char data[1] = { 0 };
-#define WRITE_SETTING(_OSTC3_SETTING, _DEVICE_DETAIL) \
- do { \
- data[0] = m_deviceDetails->_DEVICE_DETAIL; \
- rc = hw_ostc3_device_config_write(device, _OSTC3_SETTING, data, sizeof(data)); \
- if (rc != DC_STATUS_SUCCESS) \
- return rc; \
- EMIT_PROGRESS(); \
- } while (0)
-
- WRITE_SETTING(OSTC3_DIVE_MODE, diveMode);
- WRITE_SETTING(OSTC3_SATURATION, saturation);
- WRITE_SETTING(OSTC3_DESATURATION, desaturation);
- WRITE_SETTING(OSTC3_LAST_DECO, lastDeco);
- WRITE_SETTING(OSTC3_BRIGHTNESS, brightness);
- WRITE_SETTING(OSTC3_UNITS, units);
- WRITE_SETTING(OSTC3_SAMPLING_RATE, samplingRate);
- WRITE_SETTING(OSTC3_SALINITY, salinity);
- WRITE_SETTING(OSTC3_DIVEMODE_COLOR, diveModeColor);
- WRITE_SETTING(OSTC3_LANGUAGE, language);
- WRITE_SETTING(OSTC3_DATE_FORMAT, dateFormat);
- WRITE_SETTING(OSTC3_COMPASS_GAIN, compassGain);
- WRITE_SETTING(OSTC3_SAFETY_STOP, safetyStop);
- WRITE_SETTING(OSTC3_GF_HIGH, gfHigh);
- WRITE_SETTING(OSTC3_GF_LOW, gfLow);
- WRITE_SETTING(OSTC3_PPO2_MIN, ppO2Min);
- WRITE_SETTING(OSTC3_PPO2_MAX, ppO2Max);
- WRITE_SETTING(OSTC3_FUTURE_TTS, futureTTS);
- WRITE_SETTING(OSTC3_CCR_MODE, ccrMode);
- WRITE_SETTING(OSTC3_DECO_TYPE, decoType);
- WRITE_SETTING(OSTC3_AGF_SELECTABLE, aGFSelectable);
- WRITE_SETTING(OSTC3_AGF_HIGH, aGFHigh);
- WRITE_SETTING(OSTC3_AGF_LOW, aGFLow);
- WRITE_SETTING(OSTC3_CALIBRATION_GAS_O2, calibrationGas);
- WRITE_SETTING(OSTC3_FLIP_SCREEN, flipScreen);
- WRITE_SETTING(OSTC3_SETPOINT_FALLBACK, setPointFallback);
- WRITE_SETTING(OSTC3_LEFT_BUTTON_SENSIVITY, leftButtonSensitivity);
- WRITE_SETTING(OSTC3_RIGHT_BUTTON_SENSIVITY, rightButtonSensitivity);
- WRITE_SETTING(OSTC3_BOTTOM_GAS_CONSUMPTION, bottomGasConsumption);
- WRITE_SETTING(OSTC3_DECO_GAS_CONSUMPTION, decoGasConsumption);
- WRITE_SETTING(OSTC3_MOD_WARNING, modWarning);
- WRITE_SETTING(OSTC3_DYNAMIC_ASCEND_RATE, dynamicAscendRate);
- WRITE_SETTING(OSTC3_GRAPHICAL_SPEED_INDICATOR, graphicalSpeedIndicator);
- WRITE_SETTING(OSTC3_ALWAYS_SHOW_PPO2, alwaysShowppO2);
- WRITE_SETTING(OSTC3_TEMP_SENSOR_OFFSET, tempSensorOffset);
- WRITE_SETTING(OSTC3_SAFETY_STOP_LENGTH, safetyStopLength);
- WRITE_SETTING(OSTC3_SAFETY_STOP_START_DEPTH, safetyStopStartDepth);
- WRITE_SETTING(OSTC3_SAFETY_STOP_END_DEPTH, safetyStopEndDepth);
- WRITE_SETTING(OSTC3_SAFETY_STOP_RESET_DEPTH, safetyStopResetDepth);
-
-#undef WRITE_SETTING
-
- // OSTC3 stores the pressureSensorOffset in two-complement
- data[0] = (unsigned char)m_deviceDetails->pressureSensorOffset;
- rc = hw_ostc3_device_config_write(device, OSTC3_PRESSURE_SENSOR_OFFSET, data, sizeof(data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- // OSTC3 stores the tempSensorOffset in two-complement
- data[0] = (unsigned char)m_deviceDetails->pressureSensorOffset;
- rc = hw_ostc3_device_config_write(device, OSTC3_TEMP_SENSOR_OFFSET, data, sizeof(data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- //sync date and time
- if (m_deviceDetails->syncTime) {
- dc_datetime_t now;
- dc_datetime_localtime(&now, dc_datetime_now());
-
- rc = hw_ostc3_device_clock(device, &now);
- }
- EMIT_PROGRESS();
-
- return rc;
-}
-#endif /* DC_VERSION_CHECK(0, 5, 0) */
-
-static dc_status_t read_ostc_settings(dc_device_t *device, DeviceDetails *m_deviceDetails, dc_event_callback_t progress_cb, void *userdata)
-{
- dc_status_t rc;
- dc_event_progress_t progress;
- progress.current = 0;
- progress.maximum = 3;
-
- unsigned char data[256] = {};
-#ifdef DEBUG_OSTC_CF
- // FIXME: how should we report settings not supported back?
- unsigned char max_CF = 0;
-#endif
- rc = hw_ostc_device_eeprom_read(device, 0, data, sizeof(data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- m_deviceDetails->serialNo = QString::number(data[1] << 8 ^ data[0]);
- m_deviceDetails->numberOfDives = data[3] << 8 ^ data[2];
- //Byte5-6:
- //Gas 1 default (%O2=21, %He=0)
- gas gas1;
- gas1.oxygen = data[6];
- gas1.helium = data[7];
- //Byte9-10:
- //Gas 2 default (%O2=21, %He=0)
- gas gas2;
- gas2.oxygen = data[10];
- gas2.helium = data[11];
- //Byte13-14:
- //Gas 3 default (%O2=21, %He=0)
- gas gas3;
- gas3.oxygen = data[14];
- gas3.helium = data[15];
- //Byte17-18:
- //Gas 4 default (%O2=21, %He=0)
- gas gas4;
- gas4.oxygen = data[18];
- gas4.helium = data[19];
- //Byte21-22:
- //Gas 5 default (%O2=21, %He=0)
- gas gas5;
- gas5.oxygen = data[22];
- gas5.helium = data[23];
- //Byte25-26:
- //Gas 6 current (%O2, %He)
- m_deviceDetails->salinity = data[26];
- // Active Gas Flag Register
- gas1.type = data[27] & 0x01;
- gas2.type = (data[27] & 0x02) >> 1;
- gas3.type = (data[27] & 0x04) >> 2;
- gas4.type = (data[27] & 0x08) >> 3;
- gas5.type = (data[27] & 0x10) >> 4;
-
- // Gas switch depths
- gas1.depth = data[28];
- gas2.depth = data[29];
- gas3.depth = data[30];
- gas4.depth = data[31];
- gas5.depth = data[32];
- // 33 which gas is Fist gas
- switch (data[33]) {
- case 1:
- gas1.type = 2;
- break;
- case 2:
- gas2.type = 2;
- break;
- case 3:
- gas3.type = 2;
- break;
- case 4:
- gas4.type = 2;
- break;
- case 5:
- gas5.type = 2;
- break;
- default:
- //Error?
- break;
- }
- // Data filled up, set the gases.
- m_deviceDetails->gas1 = gas1;
- m_deviceDetails->gas2 = gas2;
- m_deviceDetails->gas3 = gas3;
- m_deviceDetails->gas4 = gas4;
- m_deviceDetails->gas5 = gas5;
- m_deviceDetails->decoType = data[34];
- //Byte36:
- //Use O2 Sensor Module in CC Modes (0= OFF, 1= ON) (Only available in old OSTC1 - unused for OSTC Mk.2/2N)
- //m_deviceDetails->ccrMode = data[35];
- setpoint sp1;
- sp1.sp = data[36];
- sp1.depth = 0;
- setpoint sp2;
- sp2.sp = data[37];
- sp2.depth = 0;
- setpoint sp3;
- sp3.sp = data[38];
- sp3.depth = 0;
- m_deviceDetails->sp1 = sp1;
- m_deviceDetails->sp2 = sp2;
- m_deviceDetails->sp3 = sp3;
- // Byte41-42:
- // Lowest Battery voltage seen (in mV)
- // Byte43:
- // Lowest Battery voltage seen at (Month)
- // Byte44:
- // Lowest Battery voltage seen at (Day)
- // Byte45:
- // Lowest Battery voltage seen at (Year)
- // Byte46-47:
- // Lowest Battery voltage seen at (Temperature in 0.1 °C)
- // Byte48:
- // Last complete charge at (Month)
- // Byte49:
- // Last complete charge at (Day)
- // Byte50:
- // Last complete charge at (Year)
- // Byte51-52:
- // Total charge cycles
- // Byte53-54:
- // Total complete charge cycles
- // Byte55-56:
- // Temperature Extrema minimum (Temperature in 0.1 °C)
- // Byte57:
- // Temperature Extrema minimum at (Month)
- // Byte58:
- // Temperature Extrema minimum at (Day)
- // Byte59:
- // Temperature Extrema minimum at (Year)
- // Byte60-61:
- // Temperature Extrema maximum (Temperature in 0.1 °C)
- // Byte62:
- // Temperature Extrema maximum at (Month)
- // Byte63:
- // Temperature Extrema maximum at (Day)
- // Byte64:
- // Temperature Extrema maximum at (Year)
- // Byte65:
- // Custom Text active (=1), Custom Text Disabled (<>1)
- // Byte66-90:
- // TO FIX EDITOR SYNTAX/INDENT {
- // (25Bytes): Custom Text for Surfacemode (Real text must end with "}")
- // Example: OSTC Dive Computer} (19 Characters incl. "}") Bytes 85-90 will be ignored.
- if (data[64] == 1) {
- // Make shure the data is null-terminated
- data[89] = 0;
- // Find the internal termination and replace it with 0
- char *term = strchr((char *)data + 65, (int)'}');
- if (term)
- *term = 0;
- m_deviceDetails->customText = (const char *)data + 65;
- }
- // Byte91:
- // Dim OLED in Divemode (>0), Normal mode (=0)
- // Byte92:
- // Date format for all outputs:
- // =0: MM/DD/YY
- // =1: DD/MM/YY
- // =2: YY/MM/DD
- m_deviceDetails->dateFormat = data[91];
-// Byte93:
-// Total number of CF used in installed firmware
-#ifdef DEBUG_OSTC_CF
- max_CF = data[92];
-#endif
- // Byte94:
- // Last selected view for customview area in surface mode
- // Byte95:
- // Last selected view for customview area in dive mode
- // Byte96-97:
- // Diluent 1 Default (%O2,%He)
- // Byte98-99:
- // Diluent 1 Current (%O2,%He)
- gas dil1(data[97], data[98]);
- // Byte100-101:
- // Gasuent 2 Default (%O2,%He)
- // Byte102-103:
- // Gasuent 2 Current (%O2,%He)
- gas dil2(data[101], data[102]);
- // Byte104-105:
- // Gasuent 3 Default (%O2,%He)
- // Byte106-107:
- // Gasuent 3 Current (%O2,%He)
- gas dil3(data[105], data[106]);
- // Byte108-109:
- // Gasuent 4 Default (%O2,%He)
- // Byte110-111:
- // Gasuent 4 Current (%O2,%He)
- gas dil4(data[109], data[110]);
- // Byte112-113:
- // Gasuent 5 Default (%O2,%He)
- // Byte114-115:
- // Gasuent 5 Current (%O2,%He)
- gas dil5(data[113], data[114]);
- // Byte116:
- // First Diluent (1-5)
- switch (data[115]) {
- case 1:
- dil1.type = 2;
- break;
- case 2:
- dil2.type = 2;
- break;
- case 3:
- dil3.type = 2;
- break;
- case 4:
- dil4.type = 2;
- break;
- case 5:
- dil5.type = 2;
- break;
- default:
- //Error?
- break;
- }
- m_deviceDetails->dil1 = dil1;
- m_deviceDetails->dil2 = dil2;
- m_deviceDetails->dil3 = dil3;
- m_deviceDetails->dil4 = dil4;
- m_deviceDetails->dil5 = dil5;
- // Byte117-128:
- // not used/reserved
- // Byte129-256:
- // 32 custom Functions (CF0-CF31)
-
- // Decode the relevant ones
- // CF11: Factor for saturation processes
- m_deviceDetails->saturation = read_ostc_cf(data, 11);
- // CF12: Factor for desaturation processes
- m_deviceDetails->desaturation = read_ostc_cf(data, 12);
- // CF17: Lower threshold for ppO2 warning
- m_deviceDetails->ppO2Min = read_ostc_cf(data, 17);
- // CF18: Upper threshold for ppO2 warning
- m_deviceDetails->ppO2Max = read_ostc_cf(data, 18);
- // CF20: Depth sampling rate for Profile storage
- m_deviceDetails->samplingRate = read_ostc_cf(data, 20);
- // CF29: Depth of last decompression stop
- m_deviceDetails->lastDeco = read_ostc_cf(data, 29);
-
-#ifdef DEBUG_OSTC_CF
- for (int cf = 0; cf <= 31 && cf <= max_CF; cf++)
- printf("CF %d: %d\n", cf, read_ostc_cf(data, cf));
-#endif
-
- rc = hw_ostc_device_eeprom_read(device, 1, data, sizeof(data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- // Byte1:
- // Logbook version indicator (Not writable!)
- // Byte2-3:
- // Last Firmware installed, 1st Byte.2nd Byte (e.g. „1.90“) (Not writable!)
- m_deviceDetails->firmwareVersion = QString::number(data[1]) + "." + QString::number(data[2]);
- // Byte4:
- // OLED brightness (=0: Eco, =1 High) (Not writable!)
- // Byte5-11:
- // Time/Date vault during firmware updates
- // Byte12-128
- // not used/reserved
- // Byte129-256:
- // 32 custom Functions (CF 32-63)
-
- // Decode the relevant ones
- // CF32: Gradient Factor low
- m_deviceDetails->gfLow = read_ostc_cf(data, 32);
- // CF33: Gradient Factor high
- m_deviceDetails->gfHigh = read_ostc_cf(data, 33);
- // CF56: Bottom gas consumption
- m_deviceDetails->bottomGasConsumption = read_ostc_cf(data, 56);
- // CF57: Ascent gas consumption
- m_deviceDetails->decoGasConsumption = read_ostc_cf(data, 57);
- // CF58: Future time to surface setFutureTTS
- m_deviceDetails->futureTTS = read_ostc_cf(data, 58);
-
-#ifdef DEBUG_OSTC_CF
- for (int cf = 32; cf <= 63 && cf <= max_CF; cf++)
- printf("CF %d: %d\n", cf, read_ostc_cf(data, cf));
-#endif
-
- rc = hw_ostc_device_eeprom_read(device, 2, data, sizeof(data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- // Byte1-4:
- // not used/reserved (Not writable!)
- // Byte5-128:
- // not used/reserved
- // Byte129-256:
- // 32 custom Functions (CF 64-95)
-
- // Decode the relevant ones
- // CF60: Graphic velocity
- m_deviceDetails->graphicalSpeedIndicator = read_ostc_cf(data, 60);
- // CF65: Show safety stop
- m_deviceDetails->safetyStop = read_ostc_cf(data, 65);
- // CF67: Alternaitve Gradient Factor low
- m_deviceDetails->aGFLow = read_ostc_cf(data, 67);
- // CF68: Alternative Gradient Factor high
- m_deviceDetails->aGFHigh = read_ostc_cf(data, 68);
- // CF69: Allow Gradient Factor change
- m_deviceDetails->aGFSelectable = read_ostc_cf(data, 69);
- // CF70: Safety Stop Duration [s]
- m_deviceDetails->safetyStopLength = read_ostc_cf(data, 70);
- // CF71: Safety Stop Start Depth [m]
- m_deviceDetails->safetyStopStartDepth = read_ostc_cf(data, 71);
- // CF72: Safety Stop End Depth [m]
- m_deviceDetails->safetyStopEndDepth = read_ostc_cf(data, 72);
- // CF73: Safety Stop Reset Depth [m]
- m_deviceDetails->safetyStopResetDepth = read_ostc_cf(data, 73);
- // CF74: Battery Timeout [min]
-
-#ifdef DEBUG_OSTC_CF
- for (int cf = 64; cf <= 95 && cf <= max_CF; cf++)
- printf("CF %d: %d\n", cf, read_ostc_cf(data, cf));
-#endif
-
- return rc;
-}
-
-static dc_status_t write_ostc_settings(dc_device_t *device, DeviceDetails *m_deviceDetails, dc_event_callback_t progress_cb, void *userdata)
-{
- dc_status_t rc;
- dc_event_progress_t progress;
- progress.current = 0;
- progress.maximum = 7;
- unsigned char data[256] = {};
- unsigned char max_CF = 0;
-
- // Because we write whole memory blocks, we read all the current
- // values out and then change then ones we should change.
- rc = hw_ostc_device_eeprom_read(device, 0, data, sizeof(data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- //Byte5-6:
- //Gas 1 default (%O2=21, %He=0)
- gas gas1 = m_deviceDetails->gas1;
- data[6] = gas1.oxygen;
- data[7] = gas1.helium;
- //Byte9-10:
- //Gas 2 default (%O2=21, %He=0)
- gas gas2 = m_deviceDetails->gas2;
- data[10] = gas2.oxygen;
- data[11] = gas2.helium;
- //Byte13-14:
- //Gas 3 default (%O2=21, %He=0)
- gas gas3 = m_deviceDetails->gas3;
- data[14] = gas3.oxygen;
- data[15] = gas3.helium;
- //Byte17-18:
- //Gas 4 default (%O2=21, %He=0)
- gas gas4 = m_deviceDetails->gas4;
- data[18] = gas4.oxygen;
- data[19] = gas4.helium;
- //Byte21-22:
- //Gas 5 default (%O2=21, %He=0)
- gas gas5 = m_deviceDetails->gas5;
- data[22] = gas5.oxygen;
- data[23] = gas5.helium;
- //Byte25-26:
- //Gas 6 current (%O2, %He)
- data[26] = m_deviceDetails->salinity;
- // Gas types, 0=Disabled, 1=Active, 2=Fist
- // Active Gas Flag Register
- data[27] = 0;
- if (gas1.type)
- data[27] ^= 0x01;
- if (gas2.type)
- data[27] ^= 0x02;
- if (gas3.type)
- data[27] ^= 0x04;
- if (gas4.type)
- data[27] ^= 0x08;
- if (gas5.type)
- data[27] ^= 0x10;
-
- // Gas switch depths
- data[28] = gas1.depth;
- data[29] = gas2.depth;
- data[30] = gas3.depth;
- data[31] = gas4.depth;
- data[32] = gas5.depth;
- // 33 which gas is Fist gas
- if (gas1.type == 2)
- data[33] = 1;
- else if (gas2.type == 2)
- data[33] = 2;
- else if (gas3.type == 2)
- data[33] = 3;
- else if (gas4.type == 2)
- data[33] = 4;
- else if (gas5.type == 2)
- data[33] = 5;
- else
- // FIXME: No gas was First?
- // Set gas 1 to first
- data[33] = 1;
-
- data[34] = m_deviceDetails->decoType;
- //Byte36:
- //Use O2 Sensor Module in CC Modes (0= OFF, 1= ON) (Only available in old OSTC1 - unused for OSTC Mk.2/2N)
- //m_deviceDetails->ccrMode = data[35];
- data[36] = m_deviceDetails->sp1.sp;
- data[37] = m_deviceDetails->sp2.sp;
- data[38] = m_deviceDetails->sp3.sp;
- // Byte41-42:
- // Lowest Battery voltage seen (in mV)
- // Byte43:
- // Lowest Battery voltage seen at (Month)
- // Byte44:
- // Lowest Battery voltage seen at (Day)
- // Byte45:
- // Lowest Battery voltage seen at (Year)
- // Byte46-47:
- // Lowest Battery voltage seen at (Temperature in 0.1 °C)
- // Byte48:
- // Last complete charge at (Month)
- // Byte49:
- // Last complete charge at (Day)
- // Byte50:
- // Last complete charge at (Year)
- // Byte51-52:
- // Total charge cycles
- // Byte53-54:
- // Total complete charge cycles
- // Byte55-56:
- // Temperature Extrema minimum (Temperature in 0.1 °C)
- // Byte57:
- // Temperature Extrema minimum at (Month)
- // Byte58:
- // Temperature Extrema minimum at (Day)
- // Byte59:
- // Temperature Extrema minimum at (Year)
- // Byte60-61:
- // Temperature Extrema maximum (Temperature in 0.1 °C)
- // Byte62:
- // Temperature Extrema maximum at (Month)
- // Byte63:
- // Temperature Extrema maximum at (Day)
- // Byte64:
- // Temperature Extrema maximum at (Year)
- // Byte65:
- // Custom Text active (=1), Custom Text Disabled (<>1)
- // Byte66-90:
- // (25Bytes): Custom Text for Surfacemode (Real text must end with "}")
- // Example: "OSTC Dive Computer}" (19 Characters incl. "}") Bytes 85-90 will be ignored.
- if (m_deviceDetails->customText == "") {
- data[64] = 0;
- } else {
- data[64] = 1;
- // Copy the string to the right place in the memory, padded with 0x20 (" ")
- strncpy((char *)data + 65, QString("%1").arg(m_deviceDetails->customText, -23, QChar(' ')).toUtf8().data(), 23);
- // And terminate the string.
- if (m_deviceDetails->customText.length() <= 23)
- data[65 + m_deviceDetails->customText.length()] = '}';
- else
- data[90] = '}';
- }
- // Byte91:
- // Dim OLED in Divemode (>0), Normal mode (=0)
- // Byte92:
- // Date format for all outputs:
- // =0: MM/DD/YY
- // =1: DD/MM/YY
- // =2: YY/MM/DD
- data[91] = m_deviceDetails->dateFormat;
- // Byte93:
- // Total number of CF used in installed firmware
- max_CF = data[92];
- // Byte94:
- // Last selected view for customview area in surface mode
- // Byte95:
- // Last selected view for customview area in dive mode
- // Byte96-97:
- // Diluent 1 Default (%O2,%He)
- // Byte98-99:
- // Diluent 1 Current (%O2,%He)
- gas dil1 = m_deviceDetails->dil1;
- data[97] = dil1.oxygen;
- data[98] = dil1.helium;
- // Byte100-101:
- // Gasuent 2 Default (%O2,%He)
- // Byte102-103:
- // Gasuent 2 Current (%O2,%He)
- gas dil2 = m_deviceDetails->dil2;
- data[101] = dil2.oxygen;
- data[102] = dil2.helium;
- // Byte104-105:
- // Gasuent 3 Default (%O2,%He)
- // Byte106-107:
- // Gasuent 3 Current (%O2,%He)
- gas dil3 = m_deviceDetails->dil3;
- data[105] = dil3.oxygen;
- data[106] = dil3.helium;
- // Byte108-109:
- // Gasuent 4 Default (%O2,%He)
- // Byte110-111:
- // Gasuent 4 Current (%O2,%He)
- gas dil4 = m_deviceDetails->dil4;
- data[109] = dil4.oxygen;
- data[110] = dil4.helium;
- // Byte112-113:
- // Gasuent 5 Default (%O2,%He)
- // Byte114-115:
- // Gasuent 5 Current (%O2,%He)
- gas dil5 = m_deviceDetails->dil5;
- data[113] = dil5.oxygen;
- data[114] = dil5.helium;
- // Byte116:
- // First Diluent (1-5)
- if (dil1.type == 2)
- data[115] = 1;
- else if (dil2.type == 2)
- data[115] = 2;
- else if (dil3.type == 2)
- data[115] = 3;
- else if (dil4.type == 2)
- data[115] = 4;
- else if (dil5.type == 2)
- data[115] = 5;
- else
- // FIXME: No first diluent?
- // Set gas 1 to fist
- data[115] = 1;
-
- // Byte117-128:
- // not used/reserved
- // Byte129-256:
- // 32 custom Functions (CF0-CF31)
-
- // Write the relevant ones
- // CF11: Factor for saturation processes
- write_ostc_cf(data, 11, max_CF, m_deviceDetails->saturation);
- // CF12: Factor for desaturation processes
- write_ostc_cf(data, 12, max_CF, m_deviceDetails->desaturation);
- // CF17: Lower threshold for ppO2 warning
- write_ostc_cf(data, 17, max_CF, m_deviceDetails->ppO2Min);
- // CF18: Upper threshold for ppO2 warning
- write_ostc_cf(data, 18, max_CF, m_deviceDetails->ppO2Max);
- // CF20: Depth sampling rate for Profile storage
- write_ostc_cf(data, 20, max_CF, m_deviceDetails->samplingRate);
- // CF29: Depth of last decompression stop
- write_ostc_cf(data, 29, max_CF, m_deviceDetails->lastDeco);
-
-#ifdef DEBUG_OSTC_CF
- for (int cf = 0; cf <= 31 && cf <= max_CF; cf++)
- printf("CF %d: %d\n", cf, read_ostc_cf(data, cf));
-#endif
- rc = hw_ostc_device_eeprom_write(device, 0, data, sizeof(data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- rc = hw_ostc_device_eeprom_read(device, 1, data, sizeof(data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- // Byte1:
- // Logbook version indicator (Not writable!)
- // Byte2-3:
- // Last Firmware installed, 1st Byte.2nd Byte (e.g. „1.90“) (Not writable!)
- // Byte4:
- // OLED brightness (=0: Eco, =1 High) (Not writable!)
- // Byte5-11:
- // Time/Date vault during firmware updates
- // Byte12-128
- // not used/reserved
- // Byte129-256:
- // 32 custom Functions (CF 32-63)
-
- // Decode the relevant ones
- // CF32: Gradient Factor low
- write_ostc_cf(data, 32, max_CF, m_deviceDetails->gfLow);
- // CF33: Gradient Factor high
- write_ostc_cf(data, 33, max_CF, m_deviceDetails->gfHigh);
- // CF56: Bottom gas consumption
- write_ostc_cf(data, 56, max_CF, m_deviceDetails->bottomGasConsumption);
- // CF57: Ascent gas consumption
- write_ostc_cf(data, 57, max_CF, m_deviceDetails->decoGasConsumption);
- // CF58: Future time to surface setFutureTTS
- write_ostc_cf(data, 58, max_CF, m_deviceDetails->futureTTS);
-#ifdef DEBUG_OSTC_CF
- for (int cf = 32; cf <= 63 && cf <= max_CF; cf++)
- printf("CF %d: %d\n", cf, read_ostc_cf(data, cf));
-#endif
- rc = hw_ostc_device_eeprom_write(device, 1, data, sizeof(data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- rc = hw_ostc_device_eeprom_read(device, 2, data, sizeof(data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
- // Byte1-4:
- // not used/reserved (Not writable!)
- // Byte5-128:
- // not used/reserved
- // Byte129-256:
- // 32 custom Functions (CF 64-95)
-
- // Decode the relevant ones
- // CF60: Graphic velocity
- write_ostc_cf(data, 60, max_CF, m_deviceDetails->graphicalSpeedIndicator);
- // CF65: Show safety stop
- write_ostc_cf(data, 65, max_CF, m_deviceDetails->safetyStop);
- // CF67: Alternaitve Gradient Factor low
- write_ostc_cf(data, 67, max_CF, m_deviceDetails->aGFLow);
- // CF68: Alternative Gradient Factor high
- write_ostc_cf(data, 68, max_CF, m_deviceDetails->aGFHigh);
- // CF69: Allow Gradient Factor change
- write_ostc_cf(data, 69, max_CF, m_deviceDetails->aGFSelectable);
- // CF70: Safety Stop Duration [s]
- write_ostc_cf(data, 70, max_CF, m_deviceDetails->safetyStopLength);
- // CF71: Safety Stop Start Depth [m]
- write_ostc_cf(data, 71, max_CF, m_deviceDetails->safetyStopStartDepth);
- // CF72: Safety Stop End Depth [m]
- write_ostc_cf(data, 72, max_CF, m_deviceDetails->safetyStopEndDepth);
- // CF73: Safety Stop Reset Depth [m]
- write_ostc_cf(data, 73, max_CF, m_deviceDetails->safetyStopResetDepth);
- // CF74: Battery Timeout [min]
-
-#ifdef DEBUG_OSTC_CF
- for (int cf = 64; cf <= 95 && cf <= max_CF; cf++)
- printf("CF %d: %d\n", cf, read_ostc_cf(data, cf));
-#endif
- rc = hw_ostc_device_eeprom_write(device, 2, data, sizeof(data));
- if (rc != DC_STATUS_SUCCESS)
- return rc;
- EMIT_PROGRESS();
-
- //sync date and time
- if (m_deviceDetails->syncTime) {
- QDateTime timeToSet = QDateTime::currentDateTime();
- dc_datetime_t time;
- time.year = timeToSet.date().year();
- time.month = timeToSet.date().month();
- time.day = timeToSet.date().day();
- time.hour = timeToSet.time().hour();
- time.minute = timeToSet.time().minute();
- time.second = timeToSet.time().second();
- rc = hw_ostc_device_clock(device, &time);
- }
- EMIT_PROGRESS();
- return rc;
-}
-
-#undef EMIT_PROGRESS
-
-DeviceThread::DeviceThread(QObject *parent, device_data_t *data) : QThread(parent), m_data(data)
-{
-}
-
-void DeviceThread::progressCB(int percent)
-{
- emit progress(percent);
-}
-
-void DeviceThread::event_cb(dc_device_t *device, dc_event_type_t event, const void *data, void *userdata)
-{
- Q_UNUSED(device);
-
- const dc_event_progress_t *progress = (dc_event_progress_t *) data;
- DeviceThread *dt = static_cast<DeviceThread*>(userdata);
-
- switch (event) {
- case DC_EVENT_PROGRESS:
- dt->progressCB(100.0 * (double)progress->current / (double)progress->maximum);
- break;
- default:
- emit dt->error("Unexpected event recived");
- break;
- }
-}
-
-ReadSettingsThread::ReadSettingsThread(QObject *parent, device_data_t *data) : DeviceThread(parent, data)
-{
-}
-
-void ReadSettingsThread::run()
-{
- dc_status_t rc;
-
- DeviceDetails *m_deviceDetails = new DeviceDetails(0);
- switch (dc_device_get_type(m_data->device)) {
- case DC_FAMILY_SUUNTO_VYPER:
- rc = read_suunto_vyper_settings(m_data->device, m_deviceDetails, DeviceThread::event_cb, this);
- if (rc == DC_STATUS_SUCCESS) {
- emit devicedetails(m_deviceDetails);
- } else if (rc == DC_STATUS_UNSUPPORTED) {
- emit error(tr("This feature is not yet available for the selected dive computer."));
- } else {
- emit error("Failed!");
- }
- break;
-#if DC_VERSION_CHECK(0, 5, 0)
- case DC_FAMILY_HW_OSTC3:
- rc = read_ostc3_settings(m_data->device, m_deviceDetails, DeviceThread::event_cb, this);
- if (rc == DC_STATUS_SUCCESS)
- emit devicedetails(m_deviceDetails);
- else
- emit error("Failed!");
- break;
-#endif // divecomputer 0.5.0
-#ifdef DEBUG_OSTC
- case DC_FAMILY_NULL:
-#endif
- case DC_FAMILY_HW_OSTC:
- rc = read_ostc_settings(m_data->device, m_deviceDetails, DeviceThread::event_cb, this);
- if (rc == DC_STATUS_SUCCESS)
- emit devicedetails(m_deviceDetails);
- else
- emit error("Failed!");
- break;
- default:
- emit error(tr("This feature is not yet available for the selected dive computer."));
- break;
- }
-}
-
-WriteSettingsThread::WriteSettingsThread(QObject *parent, device_data_t *data) :
- DeviceThread(parent, data),
- m_deviceDetails(NULL)
-{
-}
-
-void WriteSettingsThread::setDeviceDetails(DeviceDetails *details)
-{
- m_deviceDetails = details;
-}
-
-void WriteSettingsThread::run()
-{
- dc_status_t rc;
-
- switch (dc_device_get_type(m_data->device)) {
- case DC_FAMILY_SUUNTO_VYPER:
- rc = write_suunto_vyper_settings(m_data->device, m_deviceDetails, DeviceThread::event_cb, this);
- if (rc == DC_STATUS_UNSUPPORTED) {
- emit error(tr("This feature is not yet available for the selected dive computer."));
- } else if (rc != DC_STATUS_SUCCESS) {
- emit error(tr("Failed!"));
- }
- break;
-#if DC_VERSION_CHECK(0, 5, 0)
- case DC_FAMILY_HW_OSTC3:
- rc = write_ostc3_settings(m_data->device, m_deviceDetails, DeviceThread::event_cb, this);
- if (rc != DC_STATUS_SUCCESS)
- emit error(tr("Failed!"));
- break;
-#endif // divecomputer 0.5.0
-#ifdef DEBUG_OSTC
- case DC_FAMILY_NULL:
-#endif
- case DC_FAMILY_HW_OSTC:
- rc = write_ostc_settings(m_data->device, m_deviceDetails, DeviceThread::event_cb, this);
- if (rc != DC_STATUS_SUCCESS)
- emit error(tr("Failed!"));
- break;
- default:
- emit error(tr("This feature is not yet available for the selected dive computer."));
- break;
- }
-}
-
-
-FirmwareUpdateThread::FirmwareUpdateThread(QObject *parent, device_data_t *data, QString fileName) : DeviceThread(parent, data), m_fileName(fileName)
-{
-}
-
-void FirmwareUpdateThread::run()
-{
- dc_status_t rc;
-
- rc = dc_device_set_events(m_data->device, DC_EVENT_PROGRESS, DeviceThread::event_cb, this);
- if (rc != DC_STATUS_SUCCESS) {
- emit error("Error registering the event handler.");
- return;
- }
- switch (dc_device_get_type(m_data->device)) {
-#if DC_VERSION_CHECK(0, 5, 0)
- case DC_FAMILY_HW_OSTC3:
- rc = hw_ostc3_device_fwupdate(m_data->device, m_fileName.toUtf8().data());
- break;
- case DC_FAMILY_HW_OSTC:
- rc = hw_ostc_device_fwupdate(m_data->device, m_fileName.toUtf8().data());
- break;
-#endif // divecomputer 0.5.0
- default:
- emit error(tr("This feature is not yet available for the selected dive computer."));
- return;
- }
-
- if (rc != DC_STATUS_SUCCESS) {
- emit error(tr("Firmware update failed!"));
- }
-}
-
-
-ResetSettingsThread::ResetSettingsThread(QObject *parent, device_data_t *data) : DeviceThread(parent, data)
-{
-}
-
-void ResetSettingsThread::run()
-{
- dc_status_t rc = DC_STATUS_SUCCESS;
-
-#if DC_VERSION_CHECK(0, 5, 0)
- if (dc_device_get_type(m_data->device) == DC_FAMILY_HW_OSTC3) {
- rc = hw_ostc3_device_config_reset(m_data->device);
- emit progress(100);
- }
-#endif // divecomputer 0.5.0
- if (rc != DC_STATUS_SUCCESS) {
- emit error(tr("Reset settings failed!"));
- }
-}
diff --git a/subsurface-core/configuredivecomputerthreads.h b/subsurface-core/configuredivecomputerthreads.h
deleted file mode 100644
index 8817d848a..000000000
--- a/subsurface-core/configuredivecomputerthreads.h
+++ /dev/null
@@ -1,60 +0,0 @@
-#ifndef CONFIGUREDIVECOMPUTERTHREADS_H
-#define CONFIGUREDIVECOMPUTERTHREADS_H
-
-#include <QObject>
-#include <QThread>
-#include "libdivecomputer.h"
-#include "devicedetails.h"
-
-class DeviceThread : public QThread {
- Q_OBJECT
-public:
- DeviceThread(QObject *parent, device_data_t *data);
- virtual void run() = 0;
-signals:
- void error(QString err);
- void progress(int value);
-protected:
- device_data_t *m_data;
- void progressCB(int value);
- static void event_cb(dc_device_t *device, dc_event_type_t event, const void *data, void *userdata);
-};
-
-class ReadSettingsThread : public DeviceThread {
- Q_OBJECT
-public:
- ReadSettingsThread(QObject *parent, device_data_t *data);
- void run();
-signals:
- void devicedetails(DeviceDetails *newDeviceDetails);
-};
-
-class WriteSettingsThread : public DeviceThread {
- Q_OBJECT
-public:
- WriteSettingsThread(QObject *parent, device_data_t *data);
- void setDeviceDetails(DeviceDetails *details);
- void run();
-
-private:
- DeviceDetails *m_deviceDetails;
-};
-
-class FirmwareUpdateThread : public DeviceThread {
- Q_OBJECT
-public:
- FirmwareUpdateThread(QObject *parent, device_data_t *data, QString fileName);
- void run();
-
-private:
- QString m_fileName;
-};
-
-class ResetSettingsThread : public DeviceThread {
- Q_OBJECT
-public:
- ResetSettingsThread(QObject *parent, device_data_t *data);
- void run();
-};
-
-#endif // CONFIGUREDIVECOMPUTERTHREADS_H
diff --git a/subsurface-core/datatrak.c b/subsurface-core/datatrak.c
deleted file mode 100644
index 204ebd9b3..000000000
--- a/subsurface-core/datatrak.c
+++ /dev/null
@@ -1,698 +0,0 @@
-// Clang has a bug on zero-initialization of C structs.
-#pragma clang diagnostic ignored "-Wmissing-field-initializers"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-
-#include "datatrak.h"
-#include "dive.h"
-#include "units.h"
-#include "device.h"
-#include "gettext.h"
-
-extern struct sample *add_sample(struct sample *sample, int time, struct divecomputer *dc);
-
-unsigned char lector_bytes[2], lector_word[4], tmp_1byte, *byte;
-unsigned int tmp_2bytes;
-char is_nitrox, is_O2, is_SCR;
-unsigned long tmp_4bytes;
-
-static unsigned int two_bytes_to_int(unsigned char x, unsigned char y)
-{
- return (x << 8) + y;
-}
-
-static unsigned long four_bytes_to_long(unsigned char x, unsigned char y, unsigned char z, unsigned char t)
-{
- return ((long)x << 24) + ((long)y << 16) + ((long)z << 8) + (long)t;
-}
-
-static unsigned char *byte_to_bits(unsigned char byte)
-{
- unsigned char i, *bits = (unsigned char *)malloc(8);
-
- for (i = 0; i < 8; i++)
- bits[i] = byte & (1 << i);
- return bits;
-}
-
-/*
- * Datatrak stores the date in days since 01-01-1600, while Subsurface uses
- * time_t (seconds since 00:00 01-01-1970). Function subtracts
- * (1970 - 1600) * 365,2425 = 135139,725 to our date variable, getting the
- * days since Epoch.
- */
-static time_t date_time_to_ssrfc(unsigned long date, int time)
-{
- time_t tmp;
- tmp = (date - 135140) * 86400 + time * 60;
- return tmp;
-}
-
-static unsigned char to_8859(unsigned char char_cp850)
-{
- static const unsigned char char_8859[46] = { 0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7,
- 0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xc4, 0xc5,
- 0xc9, 0xe6, 0xc6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9,
- 0xff, 0xd6, 0xdc, 0xf8, 0xa3, 0xd8, 0xd7, 0x66,
- 0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xd1, 0xaa, 0xba,
- 0xbf, 0xae, 0xac, 0xbd, 0xbc, 0xa1 };
- return char_8859[char_cp850 - 0x80];
-}
-
-static char *to_utf8(unsigned char *in_string)
-{
- int outlen, inlen, i = 0, j = 0;
- inlen = strlen((char *)in_string);
- outlen = inlen * 2 + 1;
-
- char *out_string = calloc(outlen, 1);
- for (i = 0; i < inlen; i++) {
- if (in_string[i] < 127)
- out_string[j] = in_string[i];
- else {
- if (in_string[i] > 127 && in_string[i] <= 173)
- in_string[i] = to_8859(in_string[i]);
- out_string[j] = (in_string[i] >> 6) | 0xC0;
- j++;
- out_string[j] = (in_string[i] & 0x3F) | 0x80;
- }
- j++;
- }
- out_string[j + 1] = '\0';
- return out_string;
-}
-
-/*
- * Subsurface sample structure doesn't support the flags and alarms in the dt .log
- * so will treat them as dc events.
- */
-static struct sample *dtrak_profile(struct dive *dt_dive, FILE *archivo)
-{
- int i, j = 1, interval, o2percent = dt_dive->cylinder[0].gasmix.o2.permille / 10;
- struct sample *sample = dt_dive->dc.sample;
- struct divecomputer *dc = &dt_dive->dc;
-
- for (i = 1; i <= dt_dive->dc.alloc_samples; i++) {
- if (fread(&lector_bytes, 1, 2, archivo) != 2)
- return sample;
- interval= 20 * (i + 1);
- sample = add_sample(sample, interval, dc);
- sample->depth.mm = (two_bytes_to_int(lector_bytes[0], lector_bytes[1]) & 0xFFC0) * 1000 / 410;
- byte = byte_to_bits(two_bytes_to_int(lector_bytes[0], lector_bytes[1]) & 0x003F);
- if (byte[0] != 0)
- sample->in_deco = true;
- else
- sample->in_deco = false;
- if (byte[1] != 0)
- add_event(dc, sample->time.seconds, 0, 0, 0, "rbt");
- if (byte[2] != 0)
- add_event(dc, sample->time.seconds, 0, 0, 0, "ascent");
- if (byte[3] != 0)
- add_event(dc, sample->time.seconds, 0, 0, 0, "ceiling");
- if (byte[4] != 0)
- add_event(dc, sample->time.seconds, 0, 0, 0, "workload");
- if (byte[5] != 0)
- add_event(dc, sample->time.seconds, 0, 0, 0, "transmitter");
- if (j == 3) {
- read_bytes(1);
- if (is_O2) {
- read_bytes(1);
- o2percent = tmp_1byte;
- }
- j = 0;
- }
- free(byte);
-
- // In commit 5f44fdd setpoint replaced po2, so although this is not necessarily CCR dive ...
- if (is_O2)
- sample->setpoint.mbar = calculate_depth_to_mbar(sample->depth.mm, dt_dive->surface_pressure, 0) * o2percent / 100;
- j++;
- }
-bail:
- return sample;
-}
-
-/*
- * Reads the header of a file and returns the header struct
- * If it's not a DATATRAK file returns header zero initalized
- */
-static dtrakheader read_file_header(FILE *archivo)
-{
- dtrakheader fileheader = { 0 };
- const short headerbytes = 12;
- unsigned char *lector = (unsigned char *)malloc(headerbytes);
-
- if (fread(lector, 1, headerbytes, archivo) != headerbytes) {
- free(lector);
- return fileheader;
- }
- if (two_bytes_to_int(lector[0], lector[1]) != 0xA100) {
- report_error(translate("gettextFromC", "Error: the file does not appear to be a DATATRAK divelog"));
- free(lector);
- return fileheader;
- }
- fileheader.header = (lector[0] << 8) + lector[1];
- fileheader.dc_serial_1 = two_bytes_to_int(lector[2], lector[3]);
- fileheader.dc_serial_2 = two_bytes_to_int(lector[4], lector[5]);
- fileheader.divesNum = two_bytes_to_int(lector[7], lector[6]);
- free(lector);
- return fileheader;
-}
-
-#define CHECK(_func, _val) if ((_func) != (_val)) goto bail
-
-/*
- * Parses the dive extracting its data and filling a subsurface's dive structure
- */
-bool dt_dive_parser(FILE *archivo, struct dive *dt_dive)
-{
- unsigned char n;
- int profile_length;
- char *tmp_notes_str = NULL;
- unsigned char *tmp_string1 = NULL,
- *locality = NULL,
- *dive_point = NULL;
- char buffer[1024];
- struct divecomputer *dc = &dt_dive->dc;
-
- is_nitrox = is_O2 = is_SCR = 0;
-
- /*
- * Parse byte to byte till next dive entry
- */
- n = 0;
- CHECK(fread(&lector_bytes[n], 1, 1, archivo), 1);
- while (lector_bytes[n] != 0xA0)
- CHECK(fread(&lector_bytes[n], 1, 1, archivo), 1);
-
- /*
- * Found dive header 0xA000, verify second byte
- */
- CHECK(fread(&lector_bytes[n+1], 1, 1, archivo), 1);
- if (two_bytes_to_int(lector_bytes[0], lector_bytes[1]) != 0xA000) {
- printf("Error: byte = %4x\n", two_bytes_to_int(lector_bytes[0], lector_bytes[1]));
- return false;
- }
-
- /*
- * Begin parsing
- * First, Date of dive, 4 bytes
- */
- read_bytes(4);
-
-
- /*
- * Next, Time in minutes since 00:00
- */
- read_bytes(2);
-
- dt_dive->dc.when = dt_dive->when = (timestamp_t)date_time_to_ssrfc(tmp_4bytes, tmp_2bytes);
-
- /*
- * Now, Locality, 1st byte is long of string, rest is string
- */
- read_bytes(1);
- read_string(locality);
-
- /*
- * Next, Dive point, defined as Locality
- */
- read_bytes(1);
- read_string(dive_point);
-
- /*
- * Subsurface only have a location variable, so we have to merge DTrak's
- * Locality and Dive points.
- */
- snprintf(buffer, sizeof(buffer), "%s, %s", locality, dive_point);
- dt_dive->dive_site_uuid = get_dive_site_uuid_by_name(buffer, NULL);
- if (dt_dive->dive_site_uuid == 0)
- dt_dive->dive_site_uuid = create_dive_site(buffer, dt_dive->when);
- free(locality);
- free(dive_point);
-
- /*
- * Altitude. Don't exist in Subsurface, the equivalent would be
- * surface air pressure which can, be calculated from altitude.
- * As dtrak registers altitude intervals, we, arbitrarily, choose
- * the lower altitude/pressure equivalence for each segment. So
- *
- * Datatrak table * Conversion formula:
- * *
- * byte = 1 0 - 700 m * P = P0 * exp(-(g * M * h ) / (R * T0))
- * byte = 2 700 - 1700m * P0 = sealevel pressure = 101325 Pa
- * byte = 3 1700 - 2700 m * g = grav. acceleration = 9,80665 m/s²
- * byte = 4 2700 - * m * M = molar mass (dry air) = 0,0289644 Kg/mol
- * * h = altitude over sea level (m)
- * * R = Universal gas constant = 8,31447 J/(mol*K)
- * * T0 = sea level standard temperature = 288,15 K
- */
- read_bytes(1);
- switch (tmp_1byte) {
- case 1:
- dt_dive->dc.surface_pressure.mbar = 1013;
- break;
- case 2:
- dt_dive->dc.surface_pressure.mbar = 932;
- break;
- case 3:
- dt_dive->dc.surface_pressure.mbar = 828;
- break;
- case 4:
- dt_dive->dc.surface_pressure.mbar = 735;
- break;
- default:
- dt_dive->dc.surface_pressure.mbar = 1013;
- }
-
- /*
- * Interval (minutes)
- */
- read_bytes(2);
- if (tmp_2bytes != 0x7FFF)
- dt_dive->dc.surfacetime.seconds = (uint32_t) tmp_2bytes * 60;
-
- /*
- * Weather, values table, 0 to 6
- * Subsurface don't have this record but we can use tags
- */
- dt_dive->tag_list = NULL;
- read_bytes(1);
- switch (tmp_1byte) {
- case 1:
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "clear")));
- break;
- case 2:
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "misty")));
- break;
- case 3:
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "fog")));
- break;
- case 4:
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "rain")));
- break;
- case 5:
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "storm")));
- break;
- case 6:
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "snow")));
- break;
- default:
- // unknown, do nothing
- break;
- }
-
- /*
- * Air Temperature
- */
- read_bytes(2);
- if (tmp_2bytes != 0x7FFF)
- dt_dive->dc.airtemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100));
-
- /*
- * Dive suit, values table, 0 to 6
- */
- read_bytes(1);
- switch (tmp_1byte) {
- case 1:
- dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "No suit"));
- break;
- case 2:
- dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Shorty"));
- break;
- case 3:
- dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Combi"));
- break;
- case 4:
- dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Wet suit"));
- break;
- case 5:
- dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Semidry suit"));
- break;
- case 6:
- dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Dry suit"));
- break;
- default:
- // unknown, do nothing
- break;
- }
-
- /*
- * Tank, volume size in liter*100. And initialize gasmix to air (default).
- * Dtrak don't record init and end pressures, but consumed bar, so let's
- * init a default pressure of 200 bar.
- */
- read_bytes(2);
- if (tmp_2bytes != 0x7FFF) {
- dt_dive->cylinder[0].type.size.mliter = tmp_2bytes * 10;
- dt_dive->cylinder[0].type.description = strdup("");
- dt_dive->cylinder[0].start.mbar = 200000;
- dt_dive->cylinder[0].gasmix.he.permille = 0;
- dt_dive->cylinder[0].gasmix.o2.permille = 210;
- dt_dive->cylinder[0].manually_added = true;
- }
-
- /*
- * Maximum depth, in cm.
- */
- read_bytes(2);
- if (tmp_2bytes != 0x7FFF)
- dt_dive->maxdepth.mm = dt_dive->dc.maxdepth.mm = (int32_t)tmp_2bytes * 10;
-
- /*
- * Dive time in minutes.
- */
- read_bytes(2);
- if (tmp_2bytes != 0x7FFF)
- dt_dive->duration.seconds = dt_dive->dc.duration.seconds = (uint32_t)tmp_2bytes * 60;
-
- /*
- * Minimum water temperature in C*100. If unknown, set it to 0K which
- * is subsurface's value for "unknown"
- */
- read_bytes(2);
- if (tmp_2bytes != 0x7fff)
- dt_dive->watertemp.mkelvin = dt_dive->dc.watertemp.mkelvin = C_to_mkelvin((double)(tmp_2bytes / 100));
- else
- dt_dive->watertemp.mkelvin = 0;
-
- /*
- * Air used in bar*100.
- */
- read_bytes(2);
- if (tmp_2bytes != 0x7FFF && dt_dive->cylinder[0].type.size.mliter)
- dt_dive->cylinder[0].gas_used.mliter = dt_dive->cylinder[0].type.size.mliter * (tmp_2bytes / 100.0);
-
- /*
- * Dive Type 1 - Bit table. Subsurface don't have this record, but
- * will use tags. Bits 0 and 1 are not used. Reuse coincident tags.
- */
- read_bytes(1);
- byte = byte_to_bits(tmp_1byte);
- if (byte[2] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "no stop")));
- if (byte[3] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "deco")));
- if (byte[4] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "single ascent")));
- if (byte[5] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "multiple ascent")));
- if (byte[6] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "fresh")));
- if (byte[7] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "salt water")));
- free(byte);
-
- /*
- * Dive Type 2 - Bit table, use tags again
- */
- read_bytes(1);
- byte = byte_to_bits(tmp_1byte);
- if (byte[0] != 0) {
- taglist_add_tag(&dt_dive->tag_list, strdup("nitrox"));
- is_nitrox = 1;
- }
- if (byte[1] != 0) {
- taglist_add_tag(&dt_dive->tag_list, strdup("rebreather"));
- is_SCR = 1;
- dt_dive->dc.divemode = PSCR;
- }
- free(byte);
-
- /*
- * Dive Activity 1 - Bit table, use tags again
- */
- read_bytes(1);
- byte = byte_to_bits(tmp_1byte);
- if (byte[0] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "sight seeing")));
- if (byte[1] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "club dive")));
- if (byte[2] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "instructor")));
- if (byte[3] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "instruction")));
- if (byte[4] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "night")));
- if (byte[5] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "cave")));
- if (byte[6] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "ice")));
- if (byte[7] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "search")));
- free(byte);
-
-
- /*
- * Dive Activity 2 - Bit table, use tags again
- */
- read_bytes(1);
- byte = byte_to_bits(tmp_1byte);
- if (byte[0] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "wreck")));
- if (byte[1] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "river")));
- if (byte[2] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "drift")));
- if (byte[3] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "photo")));
- if (byte[4] != 0)
- taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "other")));
- free(byte);
-
- /*
- * Other activities - String 1st byte = long
- * Will put this in dive notes before the true notes
- */
- read_bytes(1);
- if (tmp_1byte != 0) {
- read_string(tmp_string1);
- snprintf(buffer, sizeof(buffer), "%s: %s\n",
- QT_TRANSLATE_NOOP("gettextFromC", "Other activities"),
- tmp_string1);
- tmp_notes_str = strdup(buffer);
- free(tmp_string1);
- }
-
- /*
- * Dive buddies
- */
- read_bytes(1);
- if (tmp_1byte != 0) {
- read_string(tmp_string1);
- dt_dive->buddy = strdup((char *)tmp_string1);
- free(tmp_string1);
- }
-
- /*
- * Dive notes
- */
- read_bytes(1);
- if (tmp_1byte != 0) {
- read_string(tmp_string1);
- int len = snprintf(buffer, sizeof(buffer), "%s%s:\n%s",
- tmp_notes_str ? tmp_notes_str : "",
- QT_TRANSLATE_NOOP("gettextFromC", "Datatrak/Wlog notes"),
- tmp_string1);
- dt_dive->notes = calloc((len +1), 1);
- dt_dive->notes = memcpy(dt_dive->notes, buffer, len);
- free(tmp_string1);
- if (tmp_notes_str != NULL)
- free(tmp_notes_str);
- }
-
- /*
- * Alarms 1 - Bit table - Not in Subsurface, we use the profile
- */
- read_bytes(1);
-
- /*
- * Alarms 2 - Bit table - Not in Subsurface, we use the profile
- */
- read_bytes(1);
-
- /*
- * Dive number (in datatrak, after import user has to renumber)
- */
- read_bytes(2);
- dt_dive->number = tmp_2bytes;
-
- /*
- * Computer timestamp - Useless for Subsurface
- */
- read_bytes(4);
-
- /*
- * Model - table - Not included 0x14, 0x24, 0x41, and 0x73
- * known to exist, but not its model name - To add in the future.
- * Strangely 0x00 serves for manually added dives and a dc too, at
- * least in EXAMPLE.LOG file, shipped with the software.
- */
- read_bytes(1);
- switch (tmp_1byte) {
- case 0x00:
- dt_dive->dc.model = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Manually entered dive"));
- break;
- case 0x1C:
- dt_dive->dc.model = strdup("Aladin Air");
- break;
- case 0x1D:
- dt_dive->dc.model = strdup("Spiro Monitor 2 plus");
- break;
- case 0x1E:
- dt_dive->dc.model = strdup("Aladin Sport");
- break;
- case 0x1F:
- dt_dive->dc.model = strdup("Aladin Pro");
- break;
- case 0x34:
- dt_dive->dc.model = strdup("Aladin Air X");
- break;
- case 0x3D:
- dt_dive->dc.model = strdup("Spiro Monitor 2 plus");
- break;
- case 0x3F:
- dt_dive->dc.model = strdup("Mares Genius");
- break;
- case 0x44:
- dt_dive->dc.model = strdup("Aladin Air X");
- break;
- case 0x48:
- dt_dive->dc.model = strdup("Spiro Monitor 3 Air");
- break;
- case 0xA4:
- dt_dive->dc.model = strdup("Aladin Air X O2");
- break;
- case 0xB1:
- dt_dive->dc.model = strdup("Citizen Hyper Aqualand");
- break;
- case 0xB2:
- dt_dive->dc.model = strdup("Citizen ProMaster");
- break;
- case 0xB3:
- dt_dive->dc.model = strdup("Mares Guardian");
- break;
- case 0xBC:
- dt_dive->dc.model = strdup("Aladin Air X Nitrox");
- break;
- case 0xF4:
- dt_dive->dc.model = strdup("Aladin Air X Nitrox");
- break;
- case 0xFF:
- dt_dive->dc.model = strdup("Aladin Pro Nitrox");
- break;
- default:
- dt_dive->dc.model = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Unknown"));
- break;
- }
- if ((tmp_1byte & 0xF0) == 0xF0)
- is_nitrox = 1;
- if ((tmp_1byte & 0xF0) == 0xA0)
- is_O2 = 1;
-
- /*
- * Air usage, unknown use. Probably allows or deny manually entering gas
- * comsumption based on dc model - Useless for Subsurface
- */
- read_bytes(1);
- if (fseek(archivo, 6, 1) != 0) // jump over 6 bytes whitout known use
- goto bail;
- /*
- * Profile data length
- */
- read_bytes(2);
- profile_length = tmp_2bytes;
- if (profile_length != 0) {
- /*
- * 8 x 2 bytes for the tissues saturation useless for subsurface
- * and other 6 bytes without known use
- */
- if (fseek(archivo, 22, 1) != 0)
- goto bail;
- if (is_nitrox || is_O2) {
-
- /*
- * CNS % (unsure) values table (only nitrox computers)
- */
- read_bytes(1);
-
- /*
- * % O2 in nitrox mix - (only nitrox and O2 computers but differents)
- */
- read_bytes(1);
- if (is_nitrox) {
- dt_dive->cylinder[0].gasmix.o2.permille =
- (tmp_1byte & 0x0F ? 20.0 + 2 * (tmp_1byte & 0x0F) : 21.0) * 10;
- } else {
- dt_dive->cylinder[0].gasmix.o2.permille = tmp_1byte * 10;
- read_bytes(1) // Jump over one byte, unknown use
- }
- }
- /*
- * profileLength = Nº bytes, need to know how many samples are there.
- * 2bytes per sample plus another one each three samples. Also includes the
- * bytes jumped over (22) and the nitrox (2) or O2 (3).
- */
- int samplenum = is_O2 ? (profile_length - 25) * 3 / 8 : (profile_length - 24) * 3 / 7;
-
- dc->events = calloc(samplenum, sizeof(struct event));
- dc->alloc_samples = samplenum;
- dc->samples = 0;
- dc->sample = calloc(samplenum, sizeof(struct sample));
-
- dtrak_profile(dt_dive, archivo);
- }
- /*
- * Initialize some dive data not supported by Datatrak/WLog
- */
- if (!strcmp(dt_dive->dc.model, "Manually entered dive"))
- dt_dive->dc.deviceid = 0;
- else
- dt_dive->dc.deviceid = 0xffffffff;
- create_device_node(dt_dive->dc.model, dt_dive->dc.deviceid, "", "", dt_dive->dc.model);
- dt_dive->dc.next = NULL;
- if (!is_SCR && dt_dive->cylinder[0].type.size.mliter) {
- dt_dive->cylinder[0].end.mbar = dt_dive->cylinder[0].start.mbar -
- ((dt_dive->cylinder[0].gas_used.mliter / dt_dive->cylinder[0].type.size.mliter) * 1000);
- }
- return true;
-
-bail:
- return false;
-}
-
-void datatrak_import(const char *file, struct dive_table *table)
-{
- FILE *archivo;
- dtrakheader *fileheader = (dtrakheader *)malloc(sizeof(dtrakheader));
- int i = 0;
-
- if ((archivo = subsurface_fopen(file, "rb")) == NULL) {
- report_error(translate("gettextFromC", "Error: couldn't open the file %s"), file);
- free(fileheader);
- return;
- }
-
- /*
- * Verify fileheader, get number of dives in datatrak divelog
- */
- *fileheader = read_file_header(archivo);
- while (i < fileheader->divesNum) {
- struct dive *ptdive = alloc_dive();
-
- if (!dt_dive_parser(archivo, ptdive)) {
- report_error(translate("gettextFromC", "Error: no dive"));
- free(ptdive);
- } else {
- record_dive(ptdive);
- }
- i++;
- }
- taglist_cleanup(&g_tag_list);
- fclose(archivo);
- sort_table(table);
- free(fileheader);
-}
diff --git a/subsurface-core/datatrak.h b/subsurface-core/datatrak.h
deleted file mode 100644
index 3a37e0465..000000000
--- a/subsurface-core/datatrak.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef DATATRAK_HEADER_H
-#define DATATRAK_HEADER_H
-
-#include <string.h>
-
-typedef struct dtrakheader_ {
- int header; //Must be 0xA100;
- int divesNum;
- int dc_serial_1;
- int dc_serial_2;
-} dtrakheader;
-
-#define read_bytes(_n) \
- switch (_n) { \
- case 1: \
- if (fread (&lector_bytes, sizeof(char), _n, archivo) != _n) \
- goto bail; \
- tmp_1byte = lector_bytes[0]; \
- break; \
- case 2: \
- if (fread (&lector_bytes, sizeof(char), _n, archivo) != _n) \
- goto bail; \
- tmp_2bytes = two_bytes_to_int (lector_bytes[1], lector_bytes[0]); \
- break; \
- default: \
- if (fread (&lector_word, sizeof(char), _n, archivo) != _n) \
- goto bail; \
- tmp_4bytes = four_bytes_to_long(lector_word[3], lector_word[2], lector_word[1], lector_word[0]); \
- break; \
- }
-
-#define read_string(_property) \
- unsigned char *_property##tmp = (unsigned char *)calloc(tmp_1byte + 1, 1); \
- if (fread((char *)_property##tmp, 1, tmp_1byte, archivo) != tmp_1byte) { \
- free(_property##tmp); \
- goto bail; \
- } \
- _property = (unsigned char *)strcat(to_utf8(_property##tmp), ""); \
- free(_property##tmp);
-
-#endif // DATATRAK_HEADER_H
diff --git a/subsurface-core/deco.c b/subsurface-core/deco.c
deleted file mode 100644
index 3cd8c4a16..000000000
--- a/subsurface-core/deco.c
+++ /dev/null
@@ -1,601 +0,0 @@
-/* calculate deco values
- * based on Bühlmann ZHL-16b
- * based on an implemention by heinrichs weikamp for the DR5
- * the original file was given to Subsurface under the GPLv2
- * by Matthias Heinrichs
- *
- * The implementation below is a fairly complete rewrite since then
- * (C) Robert C. Helling 2013 and released under the GPLv2
- *
- * add_segment() - add <seconds> at the given pressure, breathing gasmix
- * deco_allowed_depth() - ceiling based on lead tissue, surface pressure, 3m increments or smooth
- * set_gf() - set Buehlmann gradient factors
- * clear_deco()
- * cache_deco_state()
- * restore_deco_state()
- * dump_tissues()
- */
-#include <math.h>
-#include <string.h>
-#include "dive.h"
-#include <assert.h>
-#include <planner.h>
-
-#define cube(x) (x * x * x)
-
-// Subsurface appears to produce marginally less conservative plans than our benchmarks
-// Introduce 1.2% additional conservatism
-#define subsurface_conservatism_factor 1.012
-
-
-extern bool in_planner();
-
-extern pressure_t first_ceiling_pressure;
-
-//! Option structure for Buehlmann decompression.
-struct buehlmann_config {
- double satmult; //! safety at inert gas accumulation as percentage of effect (more than 100).
- double desatmult; //! safety at inert gas depletion as percentage of effect (less than 100).
- int last_deco_stop_in_mtr; //! depth of last_deco_stop.
- double gf_high; //! gradient factor high (at surface).
- double gf_low; //! gradient factor low (at bottom/start of deco calculation).
- double gf_low_position_min; //! gf_low_position below surface_min_shallow.
- bool gf_low_at_maxdepth; //! if true, gf_low applies at max depth instead of at deepest ceiling.
-};
-
-struct buehlmann_config buehlmann_config = {
- .satmult = 1.0,
- .desatmult = 1.01,
- .last_deco_stop_in_mtr = 0,
- .gf_high = 0.75,
- .gf_low = 0.35,
- .gf_low_position_min = 1.0,
- .gf_low_at_maxdepth = false
-};
-
-//! Option structure for VPM-B decompression.
-struct vpmb_config {
- double crit_radius_N2; //! Critical radius of N2 nucleon (microns).
- double crit_radius_He; //! Critical radius of He nucleon (microns).
- double crit_volume_lambda; //! Constant corresponding to critical gas volume (bar * min).
- double gradient_of_imperm; //! Gradient after which bubbles become impermeable (bar).
- double surface_tension_gamma; //! Nucleons surface tension constant (N / bar = m2).
- double skin_compression_gammaC; //! Skin compression gammaC (N / bar = m2).
- double regeneration_time; //! Time needed for the bubble to regenerate to the start radius (min).
- double other_gases_pressure; //! Always present pressure of other gasses in tissues (bar).
-};
-
-struct vpmb_config vpmb_config = {
- .crit_radius_N2 = 0.55,
- .crit_radius_He = 0.45,
- .crit_volume_lambda = 199.58,
- .gradient_of_imperm = 8.30865, // = 8.2 atm
- .surface_tension_gamma = 0.18137175, // = 0.0179 N/msw
- .skin_compression_gammaC = 2.6040525, // = 0.257 N/msw
- .regeneration_time = 20160.0,
- .other_gases_pressure = 0.1359888
-};
-
-const double buehlmann_N2_a[] = { 1.1696, 1.0, 0.8618, 0.7562,
- 0.62, 0.5043, 0.441, 0.4,
- 0.375, 0.35, 0.3295, 0.3065,
- 0.2835, 0.261, 0.248, 0.2327 };
-
-const double buehlmann_N2_b[] = { 0.5578, 0.6514, 0.7222, 0.7825,
- 0.8126, 0.8434, 0.8693, 0.8910,
- 0.9092, 0.9222, 0.9319, 0.9403,
- 0.9477, 0.9544, 0.9602, 0.9653 };
-
-const double buehlmann_N2_t_halflife[] = { 5.0, 8.0, 12.5, 18.5,
- 27.0, 38.3, 54.3, 77.0,
- 109.0, 146.0, 187.0, 239.0,
- 305.0, 390.0, 498.0, 635.0 };
-
-const double buehlmann_N2_factor_expositon_one_second[] = {
- 2.30782347297664E-003, 1.44301447809736E-003, 9.23769302935806E-004, 6.24261986779007E-004,
- 4.27777107246730E-004, 3.01585140931371E-004, 2.12729727268379E-004, 1.50020603047807E-004,
- 1.05980191127841E-004, 7.91232600646508E-005, 6.17759153688224E-005, 4.83354552742732E-005,
- 3.78761777920511E-005, 2.96212356654113E-005, 2.31974277413727E-005, 1.81926738960225E-005
-};
-
-const double buehlmann_He_a[] = { 1.6189, 1.383, 1.1919, 1.0458,
- 0.922, 0.8205, 0.7305, 0.6502,
- 0.595, 0.5545, 0.5333, 0.5189,
- 0.5181, 0.5176, 0.5172, 0.5119 };
-
-const double buehlmann_He_b[] = { 0.4770, 0.5747, 0.6527, 0.7223,
- 0.7582, 0.7957, 0.8279, 0.8553,
- 0.8757, 0.8903, 0.8997, 0.9073,
- 0.9122, 0.9171, 0.9217, 0.9267 };
-
-const double buehlmann_He_t_halflife[] = { 1.88, 3.02, 4.72, 6.99,
- 10.21, 14.48, 20.53, 29.11,
- 41.20, 55.19, 70.69, 90.34,
- 115.29, 147.42, 188.24, 240.03 };
-
-const double buehlmann_He_factor_expositon_one_second[] = {
- 6.12608039419837E-003, 3.81800836683133E-003, 2.44456078654209E-003, 1.65134647076792E-003,
- 1.13084424730725E-003, 7.97503165599123E-004, 5.62552521860549E-004, 3.96776399429366E-004,
- 2.80360036664540E-004, 2.09299583354805E-004, 1.63410794820518E-004, 1.27869320250551E-004,
- 1.00198406028040E-004, 7.83611475491108E-005, 6.13689891868496E-005, 4.81280465299827E-005
-};
-
-const double conservatism_lvls[] = { 1.0, 1.05, 1.12, 1.22, 1.35 };
-
-/* Inspired gas loading equations depend on the partial pressure of inert gas in the alveolar.
- * P_alv = (P_amb - P_H2O + (1 - Rq) / Rq * P_CO2) * f
- * where:
- * P_alv alveolar partial pressure of inert gas
- * P_amb ambient pressure
- * P_H2O water vapour partial pressure = ~0.0627 bar
- * P_CO2 carbon dioxide partial pressure = ~0.0534 bar
- * Rq respiratory quotient (O2 consumption / CO2 production)
- * f fraction of inert gas
- *
- * In our calculations, we simplify this to use an effective water vapour pressure
- * WV = P_H20 - (1 - Rq) / Rq * P_CO2
- *
- * Buhlmann ignored the contribution of CO2 (i.e. Rq = 1.0), whereas Schreiner adopted Rq = 0.8.
- * WV_Buhlmann = PP_H2O = 0.0627 bar
- * WV_Schreiner = 0.0627 - (1 - 0.8) / Rq * 0.0534 = 0.0493 bar
-
- * Buhlmann calculations use the Buhlmann value, VPM-B calculations use the Schreiner value.
-*/
-#define WV_PRESSURE 0.0627 // water vapor pressure in bar, based on respiratory quotient Rq = 1.0 (Buhlmann value)
-#define WV_PRESSURE_SCHREINER 0.0493 // water vapor pressure in bar, based on respiratory quotient Rq = 0.8 (Schreiner value)
-
-#define DECO_STOPS_MULTIPLIER_MM 3000.0
-#define NITROGEN_FRACTION 0.79
-
-double tissue_n2_sat[16];
-double tissue_he_sat[16];
-int ci_pointing_to_guiding_tissue;
-double gf_low_pressure_this_dive;
-#define TISSUE_ARRAY_SZ sizeof(tissue_n2_sat)
-
-double tolerated_by_tissue[16];
-double tissue_inertgas_saturation[16];
-double buehlmann_inertgas_a[16], buehlmann_inertgas_b[16];
-
-double max_n2_crushing_pressure[16];
-double max_he_crushing_pressure[16];
-
-double crushing_onset_tension[16]; // total inert gas tension in the t* moment
-double n2_regen_radius[16]; // rs
-double he_regen_radius[16];
-double max_ambient_pressure; // last moment we were descending
-
-double bottom_n2_gradient[16];
-double bottom_he_gradient[16];
-
-double initial_n2_gradient[16];
-double initial_he_gradient[16];
-
-double get_crit_radius_He()
-{
- if (prefs.conservatism_level <= 4)
- return vpmb_config.crit_radius_He * conservatism_lvls[prefs.conservatism_level] * subsurface_conservatism_factor;
- return vpmb_config.crit_radius_He;
-}
-
-double get_crit_radius_N2()
-{
- if (prefs.conservatism_level <= 4)
- return vpmb_config.crit_radius_N2 * conservatism_lvls[prefs.conservatism_level] * subsurface_conservatism_factor;
- return vpmb_config.crit_radius_N2;
-}
-
-// Solve another cubic equation, this time
-// x^3 - B x - C == 0
-// Use trigonometric formula for negative discriminants (see Wikipedia for details)
-
-double solve_cubic2(double B, double C)
-{
- double discriminant = 27 * C * C - 4 * cube(B);
- if (discriminant < 0.0) {
- return 2.0 * sqrt(B / 3.0) * cos(acos(3.0 * C * sqrt(3.0 / B) / (2.0 * B)) / 3.0);
- }
-
- double denominator = pow(9 * C + sqrt(3 * discriminant), 1 / 3.0);
-
- return pow(2.0 / 3.0, 1.0 / 3.0) * B / denominator + denominator / pow(18.0, 1.0 / 3.0);
-}
-
-// This is a simplified formula avoiding radii. It uses the fact that Boyle's law says
-// pV = (G + P_amb) / G^3 is constant to solve for the new gradient G.
-
-double update_gradient(double next_stop_pressure, double first_gradient)
-{
- double B = cube(first_gradient) / (first_ceiling_pressure.mbar / 1000.0 + first_gradient);
- double C = next_stop_pressure * B;
-
- double new_gradient = solve_cubic2(B, C);
-
- if (new_gradient < 0.0)
- report_error("Negative gradient encountered!");
- return new_gradient;
-}
-
-double vpmb_tolerated_ambient_pressure(double reference_pressure, int ci)
-{
- double n2_gradient, he_gradient, total_gradient;
-
- if (reference_pressure >= first_ceiling_pressure.mbar / 1000.0 || !first_ceiling_pressure.mbar) {
- n2_gradient = bottom_n2_gradient[ci];
- he_gradient = bottom_he_gradient[ci];
- } else {
- n2_gradient = update_gradient(reference_pressure, bottom_n2_gradient[ci]);
- he_gradient = update_gradient(reference_pressure, bottom_he_gradient[ci]);
- }
-
- total_gradient = ((n2_gradient * tissue_n2_sat[ci]) + (he_gradient * tissue_he_sat[ci])) / (tissue_n2_sat[ci] + tissue_he_sat[ci]);
-
- return tissue_n2_sat[ci] + tissue_he_sat[ci] + vpmb_config.other_gases_pressure - total_gradient;
-}
-
-
-double tissue_tolerance_calc(const struct dive *dive, double pressure)
-{
- int ci = -1;
- double ret_tolerance_limit_ambient_pressure = 0.0;
- double gf_high = buehlmann_config.gf_high;
- double gf_low = buehlmann_config.gf_low;
- double surface = get_surface_pressure_in_mbar(dive, true) / 1000.0;
- double lowest_ceiling = 0.0;
- double tissue_lowest_ceiling[16];
-
- if (prefs.deco_mode != VPMB) {
- for (ci = 0; ci < 16; ci++) {
- tissue_inertgas_saturation[ci] = tissue_n2_sat[ci] + tissue_he_sat[ci];
- buehlmann_inertgas_a[ci] = ((buehlmann_N2_a[ci] * tissue_n2_sat[ci]) + (buehlmann_He_a[ci] * tissue_he_sat[ci])) / tissue_inertgas_saturation[ci];
- buehlmann_inertgas_b[ci] = ((buehlmann_N2_b[ci] * tissue_n2_sat[ci]) + (buehlmann_He_b[ci] * tissue_he_sat[ci])) / tissue_inertgas_saturation[ci];
-
-
- /* tolerated = (tissue_inertgas_saturation - buehlmann_inertgas_a) * buehlmann_inertgas_b; */
-
- tissue_lowest_ceiling[ci] = (buehlmann_inertgas_b[ci] * tissue_inertgas_saturation[ci] - gf_low * buehlmann_inertgas_a[ci] * buehlmann_inertgas_b[ci]) /
- ((1.0 - buehlmann_inertgas_b[ci]) * gf_low + buehlmann_inertgas_b[ci]);
- if (tissue_lowest_ceiling[ci] > lowest_ceiling)
- lowest_ceiling = tissue_lowest_ceiling[ci];
- if (!buehlmann_config.gf_low_at_maxdepth) {
- if (lowest_ceiling > gf_low_pressure_this_dive)
- gf_low_pressure_this_dive = lowest_ceiling;
- }
- }
- for (ci = 0; ci < 16; ci++) {
- double tolerated;
-
- if ((surface / buehlmann_inertgas_b[ci] + buehlmann_inertgas_a[ci] - surface) * gf_high + surface <
- (gf_low_pressure_this_dive / buehlmann_inertgas_b[ci] + buehlmann_inertgas_a[ci] - gf_low_pressure_this_dive) * gf_low + gf_low_pressure_this_dive)
- tolerated = (-buehlmann_inertgas_a[ci] * buehlmann_inertgas_b[ci] * (gf_high * gf_low_pressure_this_dive - gf_low * surface) -
- (1.0 - buehlmann_inertgas_b[ci]) * (gf_high - gf_low) * gf_low_pressure_this_dive * surface +
- buehlmann_inertgas_b[ci] * (gf_low_pressure_this_dive - surface) * tissue_inertgas_saturation[ci]) /
- (-buehlmann_inertgas_a[ci] * buehlmann_inertgas_b[ci] * (gf_high - gf_low) +
- (1.0 - buehlmann_inertgas_b[ci]) * (gf_low * gf_low_pressure_this_dive - gf_high * surface) +
- buehlmann_inertgas_b[ci] * (gf_low_pressure_this_dive - surface));
- else
- tolerated = ret_tolerance_limit_ambient_pressure;
-
-
- tolerated_by_tissue[ci] = tolerated;
-
- if (tolerated >= ret_tolerance_limit_ambient_pressure) {
- ci_pointing_to_guiding_tissue = ci;
- ret_tolerance_limit_ambient_pressure = tolerated;
- }
- }
- } else {
- // VPM-B ceiling
- double reference_pressure;
-
- ret_tolerance_limit_ambient_pressure = pressure;
- // The Boyle compensated gradient depends on ambient pressure. For the ceiling, this should set the ambient pressure.
- do {
- reference_pressure = ret_tolerance_limit_ambient_pressure;
- ret_tolerance_limit_ambient_pressure = 0.0;
- for (ci = 0; ci < 16; ci++) {
- double tolerated = vpmb_tolerated_ambient_pressure(reference_pressure, ci);
- if (tolerated >= ret_tolerance_limit_ambient_pressure) {
- ci_pointing_to_guiding_tissue = ci;
- ret_tolerance_limit_ambient_pressure = tolerated;
- }
- tolerated_by_tissue[ci] = tolerated;
- }
- // We are doing ok if the gradient was computed within ten centimeters of the ceiling.
- } while (fabs(ret_tolerance_limit_ambient_pressure - reference_pressure) > 0.01);
- }
- return ret_tolerance_limit_ambient_pressure;
-}
-
-/*
- * Return buelman factor for a particular period and tissue index.
- *
- * We cache the last factor, since we commonly call this with the
- * same values... We have a special "fixed cache" for the one second
- * case, although I wonder if that's even worth it considering the
- * more general-purpose cache.
- */
-struct factor_cache {
- int last_period;
- double last_factor;
-};
-
-double n2_factor(int period_in_seconds, int ci)
-{
- static struct factor_cache cache[16];
-
- if (period_in_seconds == 1)
- return buehlmann_N2_factor_expositon_one_second[ci];
-
- if (period_in_seconds != cache[ci].last_period) {
- cache[ci].last_period = period_in_seconds;
- cache[ci].last_factor = 1 - pow(2.0, -period_in_seconds / (buehlmann_N2_t_halflife[ci] * 60));
- }
-
- return cache[ci].last_factor;
-}
-
-double he_factor(int period_in_seconds, int ci)
-{
- static struct factor_cache cache[16];
-
- if (period_in_seconds == 1)
- return buehlmann_He_factor_expositon_one_second[ci];
-
- if (period_in_seconds != cache[ci].last_period) {
- cache[ci].last_period = period_in_seconds;
- cache[ci].last_factor = 1 - pow(2.0, -period_in_seconds / (buehlmann_He_t_halflife[ci] * 60));
- }
-
- return cache[ci].last_factor;
-}
-
-double calc_surface_phase(double surface_pressure, double he_pressure, double n2_pressure, double he_time_constant, double n2_time_constant)
-{
- double inspired_n2 = (surface_pressure - ((in_planner() && (prefs.deco_mode == VPMB)) ? WV_PRESSURE_SCHREINER : WV_PRESSURE)) * NITROGEN_FRACTION;
-
- if (n2_pressure > inspired_n2)
- return (he_pressure / he_time_constant + (n2_pressure - inspired_n2) / n2_time_constant) / (he_pressure + n2_pressure - inspired_n2);
-
- if (he_pressure + n2_pressure >= inspired_n2){
- double gradient_decay_time = 1.0 / (n2_time_constant - he_time_constant) * log ((inspired_n2 - n2_pressure) / he_pressure);
- double gradients_integral = he_pressure / he_time_constant * (1.0 - exp(-he_time_constant * gradient_decay_time)) + (n2_pressure - inspired_n2) / n2_time_constant * (1.0 - exp(-n2_time_constant * gradient_decay_time));
- return gradients_integral / (he_pressure + n2_pressure - inspired_n2);
- }
-
- return 0;
-}
-
-void vpmb_start_gradient()
-{
- int ci;
-
- for (ci = 0; ci < 16; ++ci) {
- initial_n2_gradient[ci] = bottom_n2_gradient[ci] = 2.0 * (vpmb_config.surface_tension_gamma / vpmb_config.skin_compression_gammaC) * ((vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma) / n2_regen_radius[ci]);
- initial_he_gradient[ci] = bottom_he_gradient[ci] = 2.0 * (vpmb_config.surface_tension_gamma / vpmb_config.skin_compression_gammaC) * ((vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma) / he_regen_radius[ci]);
- }
-}
-
-void vpmb_next_gradient(double deco_time, double surface_pressure)
-{
- int ci;
- double n2_b, n2_c;
- double he_b, he_c;
- double desat_time;
- deco_time /= 60.0;
-
- for (ci = 0; ci < 16; ++ci) {
- desat_time = deco_time + calc_surface_phase(surface_pressure, tissue_he_sat[ci], tissue_n2_sat[ci], log(2.0) / buehlmann_He_t_halflife[ci], log(2.0) / buehlmann_N2_t_halflife[ci]);
-
- n2_b = initial_n2_gradient[ci] + (vpmb_config.crit_volume_lambda * vpmb_config.surface_tension_gamma) / (vpmb_config.skin_compression_gammaC * desat_time);
- he_b = initial_he_gradient[ci] + (vpmb_config.crit_volume_lambda * vpmb_config.surface_tension_gamma) / (vpmb_config.skin_compression_gammaC * desat_time);
-
- n2_c = vpmb_config.surface_tension_gamma * vpmb_config.surface_tension_gamma * vpmb_config.crit_volume_lambda * max_n2_crushing_pressure[ci];
- n2_c = n2_c / (vpmb_config.skin_compression_gammaC * vpmb_config.skin_compression_gammaC * desat_time);
- he_c = vpmb_config.surface_tension_gamma * vpmb_config.surface_tension_gamma * vpmb_config.crit_volume_lambda * max_he_crushing_pressure[ci];
- he_c = he_c / (vpmb_config.skin_compression_gammaC * vpmb_config.skin_compression_gammaC * desat_time);
-
- bottom_n2_gradient[ci] = 0.5 * ( n2_b + sqrt(n2_b * n2_b - 4.0 * n2_c));
- bottom_he_gradient[ci] = 0.5 * ( he_b + sqrt(he_b * he_b - 4.0 * he_c));
- }
-}
-
-// A*r^3 - B*r^2 - C == 0
-// Solved with the help of mathematica
-
-double solve_cubic(double A, double B, double C)
-{
- double BA = B/A;
- double CA = C/A;
-
- double discriminant = CA * (4 * cube(BA) + 27 * CA);
-
- // Let's make sure we have a real solution:
- if (discriminant < 0.0) {
- // This should better not happen
- report_error("Complex solution for inner pressure encountered!\n A=%f\tB=%f\tC=%f\n", A, B, C);
- return 0.0;
- }
- double denominator = pow(cube(BA) + 1.5 * (9 * CA + sqrt(3.0) * sqrt(discriminant)), 1/3.0);
- return (BA + BA * BA / denominator + denominator) / 3.0;
-
-}
-
-
-void nuclear_regeneration(double time)
-{
- time /= 60.0;
- int ci;
- double crushing_radius_N2, crushing_radius_He;
- for (ci = 0; ci < 16; ++ci) {
- //rm
- crushing_radius_N2 = 1.0 / (max_n2_crushing_pressure[ci] / (2.0 * (vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma)) + 1.0 / get_crit_radius_N2());
- crushing_radius_He = 1.0 / (max_he_crushing_pressure[ci] / (2.0 * (vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma)) + 1.0 / get_crit_radius_He());
- //rs
- n2_regen_radius[ci] = crushing_radius_N2 + (get_crit_radius_N2() - crushing_radius_N2) * (1.0 - exp (-time / vpmb_config.regeneration_time));
- he_regen_radius[ci] = crushing_radius_He + (get_crit_radius_He() - crushing_radius_He) * (1.0 - exp (-time / vpmb_config.regeneration_time));
- }
-}
-
-
-// Calculates the nucleons inner pressure during the impermeable period
-double calc_inner_pressure(double crit_radius, double onset_tension, double current_ambient_pressure)
-{
- double onset_radius = 1.0 / (vpmb_config.gradient_of_imperm / (2.0 * (vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma)) + 1.0 / crit_radius);
-
-
- double A = current_ambient_pressure - vpmb_config.gradient_of_imperm + (2.0 * (vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma)) / onset_radius;
- double B = 2.0 * (vpmb_config.skin_compression_gammaC - vpmb_config.surface_tension_gamma);
- double C = onset_tension * pow(onset_radius, 3);
-
- double current_radius = solve_cubic(A, B, C);
-
- return onset_tension * onset_radius * onset_radius * onset_radius / (current_radius * current_radius * current_radius);
-}
-
-// Calculates the crushing pressure in the given moment. Updates crushing_onset_tension and critical radius if needed
-void calc_crushing_pressure(double pressure)
-{
- int ci;
- double gradient;
- double gas_tension;
- double n2_crushing_pressure, he_crushing_pressure;
- double n2_inner_pressure, he_inner_pressure;
-
- for (ci = 0; ci < 16; ++ci) {
- gas_tension = tissue_n2_sat[ci] + tissue_he_sat[ci] + vpmb_config.other_gases_pressure;
- gradient = pressure - gas_tension;
-
- if (gradient <= vpmb_config.gradient_of_imperm) { // permeable situation
- n2_crushing_pressure = he_crushing_pressure = gradient;
- crushing_onset_tension[ci] = gas_tension;
- }
- else { // impermeable
- if (max_ambient_pressure >= pressure)
- return;
-
- n2_inner_pressure = calc_inner_pressure(get_crit_radius_N2(), crushing_onset_tension[ci], pressure);
- he_inner_pressure = calc_inner_pressure(get_crit_radius_He(), crushing_onset_tension[ci], pressure);
-
- n2_crushing_pressure = pressure - n2_inner_pressure;
- he_crushing_pressure = pressure - he_inner_pressure;
- }
- max_n2_crushing_pressure[ci] = MAX(max_n2_crushing_pressure[ci], n2_crushing_pressure);
- max_he_crushing_pressure[ci] = MAX(max_he_crushing_pressure[ci], he_crushing_pressure);
- }
- max_ambient_pressure = MAX(pressure, max_ambient_pressure);
-}
-
-/* add period_in_seconds at the given pressure and gas to the deco calculation */
-void add_segment(double pressure, const struct gasmix *gasmix, int period_in_seconds, int ccpo2, const struct dive *dive, int sac)
-{
- (void) sac;
- int ci;
- struct gas_pressures pressures;
-
- fill_pressures(&pressures, pressure - ((in_planner() && (prefs.deco_mode == VPMB)) ? WV_PRESSURE_SCHREINER : WV_PRESSURE),
- gasmix, (double) ccpo2 / 1000.0, dive->dc.divemode);
-
- if (buehlmann_config.gf_low_at_maxdepth && pressure > gf_low_pressure_this_dive)
- gf_low_pressure_this_dive = pressure;
-
- for (ci = 0; ci < 16; ci++) {
- double pn2_oversat = pressures.n2 - tissue_n2_sat[ci];
- double phe_oversat = pressures.he - tissue_he_sat[ci];
- double n2_f = n2_factor(period_in_seconds, ci);
- double he_f = he_factor(period_in_seconds, ci);
- double n2_satmult = pn2_oversat > 0 ? buehlmann_config.satmult : buehlmann_config.desatmult;
- double he_satmult = phe_oversat > 0 ? buehlmann_config.satmult : buehlmann_config.desatmult;
-
- tissue_n2_sat[ci] += n2_satmult * pn2_oversat * n2_f;
- tissue_he_sat[ci] += he_satmult * phe_oversat * he_f;
- }
- if(prefs.deco_mode == VPMB)
- calc_crushing_pressure(pressure);
- return;
-}
-
-void dump_tissues()
-{
- int ci;
- printf("N2 tissues:");
- for (ci = 0; ci < 16; ci++)
- printf(" %6.3e", tissue_n2_sat[ci]);
- printf("\nHe tissues:");
- for (ci = 0; ci < 16; ci++)
- printf(" %6.3e", tissue_he_sat[ci]);
- printf("\n");
-}
-
-void clear_deco(double surface_pressure)
-{
- int ci;
- for (ci = 0; ci < 16; ci++) {
- tissue_n2_sat[ci] = (surface_pressure - ((in_planner() && (prefs.deco_mode == VPMB)) ? WV_PRESSURE_SCHREINER : WV_PRESSURE)) * N2_IN_AIR / 1000;
- tissue_he_sat[ci] = 0.0;
- max_n2_crushing_pressure[ci] = 0.0;
- max_he_crushing_pressure[ci] = 0.0;
- n2_regen_radius[ci] = get_crit_radius_N2();
- he_regen_radius[ci] = get_crit_radius_He();
- }
- gf_low_pressure_this_dive = surface_pressure;
- if (!buehlmann_config.gf_low_at_maxdepth)
- gf_low_pressure_this_dive += buehlmann_config.gf_low_position_min;
- max_ambient_pressure = 0.0;
-}
-
-void cache_deco_state(char **cached_datap)
-{
- char *data = *cached_datap;
-
- if (!data) {
- data = malloc(2 * TISSUE_ARRAY_SZ + sizeof(double) + sizeof(int));
- *cached_datap = data;
- }
- memcpy(data, tissue_n2_sat, TISSUE_ARRAY_SZ);
- data += TISSUE_ARRAY_SZ;
- memcpy(data, tissue_he_sat, TISSUE_ARRAY_SZ);
- data += TISSUE_ARRAY_SZ;
- memcpy(data, &gf_low_pressure_this_dive, sizeof(double));
- data += sizeof(double);
- memcpy(data, &ci_pointing_to_guiding_tissue, sizeof(int));
-}
-
-void restore_deco_state(char *data)
-{
- memcpy(tissue_n2_sat, data, TISSUE_ARRAY_SZ);
- data += TISSUE_ARRAY_SZ;
- memcpy(tissue_he_sat, data, TISSUE_ARRAY_SZ);
- data += TISSUE_ARRAY_SZ;
- memcpy(&gf_low_pressure_this_dive, data, sizeof(double));
- data += sizeof(double);
- memcpy(&ci_pointing_to_guiding_tissue, data, sizeof(int));
-}
-
-int deco_allowed_depth(double tissues_tolerance, double surface_pressure, struct dive *dive, bool smooth)
-{
- int depth;
- double pressure_delta;
-
- /* Avoid negative depths */
- pressure_delta = tissues_tolerance > surface_pressure ? tissues_tolerance - surface_pressure : 0.0;
-
- depth = rel_mbar_to_depth(pressure_delta * 1000, dive);
-
- if (!smooth)
- depth = ceil(depth / DECO_STOPS_MULTIPLIER_MM) * DECO_STOPS_MULTIPLIER_MM;
-
- if (depth > 0 && depth < buehlmann_config.last_deco_stop_in_mtr * 1000)
- depth = buehlmann_config.last_deco_stop_in_mtr * 1000;
-
- return depth;
-}
-
-void set_gf(short gflow, short gfhigh, bool gf_low_at_maxdepth)
-{
- if (gflow != -1)
- buehlmann_config.gf_low = (double)gflow / 100.0;
- if (gfhigh != -1)
- buehlmann_config.gf_high = (double)gfhigh / 100.0;
- buehlmann_config.gf_low_at_maxdepth = gf_low_at_maxdepth;
-}
diff --git a/subsurface-core/deco.h b/subsurface-core/deco.h
deleted file mode 100644
index fd3b94a9f..000000000
--- a/subsurface-core/deco.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef DECO_H
-#define DECO_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern double tolerated_by_tissue[];
-extern double buehlmann_N2_t_halflife[];
-extern double tissue_inertgas_saturation[16];
-extern double buehlmann_inertgas_a[16], buehlmann_inertgas_b[16];
-extern double gf_low_pressure_this_dive;
-
-extern int deco_allowed_depth(double tissues_tolerance, double surface_pressure, struct dive *dive, bool smooth);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // DECO_H
diff --git a/subsurface-core/device.c b/subsurface-core/device.c
deleted file mode 100644
index 6c4452f78..000000000
--- a/subsurface-core/device.c
+++ /dev/null
@@ -1,184 +0,0 @@
-#include <string.h>
-#include "dive.h"
-#include "device.h"
-
-/*
- * Good fake dive profiles are hard.
- *
- * "depthtime" is the integral of the dive depth over
- * time ("area" of the dive profile). We want that
- * area to match the average depth (avg_d*max_t).
- *
- * To do that, we generate a 6-point profile:
- *
- * (0, 0)
- * (t1, max_d)
- * (t2, max_d)
- * (t3, d)
- * (t4, d)
- * (max_t, 0)
- *
- * with the same ascent/descent rates between the
- * different depths.
- *
- * NOTE: avg_d, max_d and max_t are given constants.
- * The rest we can/should play around with to get a
- * good-looking profile.
- *
- * That six-point profile gives a total area of:
- *
- * (max_d*max_t) - (max_d*t1) - (max_d-d)*(t4-t3)
- *
- * And the "same ascent/descent rates" requirement
- * gives us (time per depth must be same):
- *
- * t1 / max_d = (t3-t2) / (max_d-d)
- * t1 / max_d = (max_t-t4) / d
- *
- * We also obviously require:
- *
- * 0 <= t1 <= t2 <= t3 <= t4 <= max_t
- *
- * Let us call 'd_frac = d / max_d', and we get:
- *
- * Total area must match average depth-time:
- *
- * (max_d*max_t) - (max_d*t1) - (max_d-d)*(t4-t3) = avg_d*max_t
- * max_d*(max_t-t1-(1-d_frac)*(t4-t3)) = avg_d*max_t
- * max_t-t1-(1-d_frac)*(t4-t3) = avg_d*max_t/max_d
- * t1+(1-d_frac)*(t4-t3) = max_t*(1-avg_d/max_d)
- *
- * and descent slope must match ascent slopes:
- *
- * t1 / max_d = (t3-t2) / (max_d*(1-d_frac))
- * t1 = (t3-t2)/(1-d_frac)
- *
- * and
- *
- * t1 / max_d = (max_t-t4) / (max_d*d_frac)
- * t1 = (max_t-t4)/d_frac
- *
- * In general, we have more free variables than we have constraints,
- * but we can aim for certain basics, like a good ascent slope.
- */
-static int fill_samples(struct sample *s, int max_d, int avg_d, int max_t, double slope, double d_frac)
-{
- double t_frac = max_t * (1 - avg_d / (double)max_d);
- int t1 = max_d / slope;
- int t4 = max_t - t1 * d_frac;
- int t3 = t4 - (t_frac - t1) / (1 - d_frac);
- int t2 = t3 - t1 * (1 - d_frac);
-
- if (t1 < 0 || t1 > t2 || t2 > t3 || t3 > t4 || t4 > max_t)
- return 0;
-
- s[1].time.seconds = t1;
- s[1].depth.mm = max_d;
- s[2].time.seconds = t2;
- s[2].depth.mm = max_d;
- s[3].time.seconds = t3;
- s[3].depth.mm = max_d * d_frac;
- s[4].time.seconds = t4;
- s[4].depth.mm = max_d * d_frac;
-
- return 1;
-}
-
-/* we have no average depth; instead of making up a random average depth
- * we should assume either a PADI rectangular profile (for short and/or
- * shallow dives) or more reasonably a six point profile with a 3 minute
- * safety stop at 5m */
-static void fill_samples_no_avg(struct sample *s, int max_d, int max_t, double slope)
-{
- // shallow or short dives are just trapecoids based on the given slope
- if (max_d < 10000 || max_t < 600) {
- s[1].time.seconds = max_d / slope;
- s[1].depth.mm = max_d;
- s[2].time.seconds = max_t - max_d / slope;
- s[2].depth.mm = max_d;
- } else {
- s[1].time.seconds = max_d / slope;
- s[1].depth.mm = max_d;
- s[2].time.seconds = max_t - max_d / slope - 180;
- s[2].depth.mm = max_d;
- s[3].time.seconds = max_t - 5000 / slope - 180;
- s[3].depth.mm = 5000;
- s[4].time.seconds = max_t - 5000 / slope;
- s[4].depth.mm = 5000;
- }
-}
-
-struct divecomputer *fake_dc(struct divecomputer *dc, bool alloc)
-{
- static struct sample fake_samples[6];
- static struct divecomputer fakedc;
- struct sample *fake = fake_samples;
-
- fakedc = (*dc);
- if (alloc)
- fake = malloc(sizeof(fake_samples));
-
- fakedc.sample = fake;
- fakedc.samples = 6;
-
- /* The dive has no samples, so create a few fake ones */
- int max_t = dc->duration.seconds;
- int max_d = dc->maxdepth.mm;
- int avg_d = dc->meandepth.mm;
-
- memset(fake, 0, sizeof(fake_samples));
- fake[5].time.seconds = max_t;
- if (!max_t || !max_d)
- return &fakedc;
-
- /*
- * We want to fake the profile so that the average
- * depth ends up correct. However, in the absence of
- * a reasonable average, let's just make something
- * up. Note that 'avg_d == max_d' is _not_ a reasonable
- * average.
- * We explicitly treat avg_d == 0 differently */
- if (avg_d == 0) {
- /* we try for a sane slope, but bow to the insanity of
- * the user supplied data */
- fill_samples_no_avg(fake, max_d, max_t, MAX(2.0 * max_d / max_t, 5000.0 / 60));
- if (fake[3].time.seconds == 0) { // just a 4 point profile
- fakedc.samples = 4;
- fake[3].time.seconds = max_t;
- }
- return &fakedc;
- }
- if (avg_d < max_d / 10 || avg_d >= max_d) {
- avg_d = (max_d + 10000) / 3;
- if (avg_d > max_d)
- avg_d = max_d * 2 / 3;
- }
- if (!avg_d)
- avg_d = 1;
-
- /*
- * Ok, first we try a basic profile with a specific ascent
- * rate (5 meters per minute) and d_frac (1/3).
- */
- if (fill_samples(fake, max_d, avg_d, max_t, 5000.0 / 60, 0.33))
- return &fakedc;
-
- /*
- * Ok, assume that didn't work because we cannot make the
- * average come out right because it was a quick deep dive
- * followed by a much shallower region
- */
- if (fill_samples(fake, max_d, avg_d, max_t, 10000.0 / 60, 0.10))
- return &fakedc;
-
- /*
- * Uhhuh. That didn't work. We'd need to find a good combination that
- * satisfies our constraints. Currently, we don't, we just give insane
- * slopes.
- */
- if (fill_samples(fake, max_d, avg_d, max_t, 10000.0, 0.01))
- return &fakedc;
-
- /* Even that didn't work? Give up, there's something wrong */
- return &fakedc;
-}
diff --git a/subsurface-core/device.h b/subsurface-core/device.h
deleted file mode 100644
index 8a00b96d3..000000000
--- a/subsurface-core/device.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef DEVICE_H
-#define DEVICE_H
-
-#ifdef __cplusplus
-#include "dive.h"
-extern "C" {
-#endif
-
-extern struct divecomputer *fake_dc(struct divecomputer *dc, bool alloc);
-extern void create_device_node(const char *model, uint32_t deviceid, const char *serial, const char *firmware, const char *nickname);
-extern void call_for_each_dc(void *f, void (*callback)(void *, const char *, uint32_t,
- const char *, const char *, const char *), bool select_only);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // DEVICE_H
diff --git a/subsurface-core/devicedetails.cpp b/subsurface-core/devicedetails.cpp
deleted file mode 100644
index a917a4d0e..000000000
--- a/subsurface-core/devicedetails.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "devicedetails.h"
-
-gas::gas(unsigned char oxygen, unsigned char helium, unsigned char type, unsigned char depth) :
- oxygen(oxygen), helium(helium), type(type), depth(depth)
-{
-}
-
-setpoint::setpoint(unsigned char sp, unsigned char depth) :
- sp(sp), depth(depth)
-{
-}
-
-DeviceDetails::DeviceDetails(QObject *parent) :
- QObject(parent),
- data(0),
- syncTime(false),
- setPointFallback(0),
- ccrMode(0),
- calibrationGas(0),
- diveMode(0),
- decoType(0),
- ppO2Max(0),
- ppO2Min(0),
- futureTTS(0),
- gfLow(0),
- gfHigh(0),
- aGFLow(0),
- aGFHigh(0),
- aGFSelectable(0),
- saturation(0),
- desaturation(0),
- lastDeco(0),
- brightness(0),
- units(0),
- samplingRate(0),
- salinity(0),
- diveModeColor(0),
- language(0),
- dateFormat(0),
- compassGain(0),
- pressureSensorOffset(0),
- flipScreen(0),
- safetyStop(0),
- maxDepth(0),
- totalTime(0),
- numberOfDives(0),
- altitude(0),
- personalSafety(0),
- timeFormat(0),
- lightEnabled(false),
- light(0),
- alarmTimeEnabled(false),
- alarmTime(0),
- alarmDepthEnabled(false),
- alarmDepth(0),
- leftButtonSensitivity(0),
- rightButtonSensitivity(0),
- bottomGasConsumption(0),
- decoGasConsumption(0),
- modWarning(false),
- dynamicAscendRate(false),
- graphicalSpeedIndicator(false),
- alwaysShowppO2(false),
- tempSensorOffset(0),
- safetyStopLength(0),
- safetyStopStartDepth(0),
- safetyStopEndDepth(0),
- safetyStopResetDepth(0)
-{
-}
diff --git a/subsurface-core/devicedetails.h b/subsurface-core/devicedetails.h
deleted file mode 100644
index ff3009bc5..000000000
--- a/subsurface-core/devicedetails.h
+++ /dev/null
@@ -1,104 +0,0 @@
-#ifndef DEVICEDETAILS_H
-#define DEVICEDETAILS_H
-
-#include <QObject>
-#include <QDateTime>
-#include "libdivecomputer.h"
-
-struct gas {
- unsigned char oxygen;
- unsigned char helium;
- unsigned char type;
- unsigned char depth;
- gas(unsigned char oxygen = 0, unsigned char helium = 0, unsigned char type = 0, unsigned char depth = 0);
-};
-
-struct setpoint {
- unsigned char sp;
- unsigned char depth;
- setpoint(unsigned char sp = 0, unsigned char depth = 0);
-};
-
-class DeviceDetails : public QObject
-{
- Q_OBJECT
-public:
- explicit DeviceDetails(QObject *parent = 0);
-
- device_data_t *data;
- QString serialNo;
- QString firmwareVersion;
- QString customText;
- QString model;
- bool syncTime;
- gas gas1;
- gas gas2;
- gas gas3;
- gas gas4;
- gas gas5;
- gas dil1;
- gas dil2;
- gas dil3;
- gas dil4;
- gas dil5;
- setpoint sp1;
- setpoint sp2;
- setpoint sp3;
- setpoint sp4;
- setpoint sp5;
- bool setPointFallback;
- int ccrMode;
- int calibrationGas;
- int diveMode;
- int decoType;
- int ppO2Max;
- int ppO2Min;
- int futureTTS;
- int gfLow;
- int gfHigh;
- int aGFLow;
- int aGFHigh;
- int aGFSelectable;
- int saturation;
- int desaturation;
- int lastDeco;
- int brightness;
- int units;
- int samplingRate;
- int salinity;
- int diveModeColor;
- int language;
- int dateFormat;
- int compassGain;
- int pressureSensorOffset;
- bool flipScreen;
- bool safetyStop;
- int maxDepth;
- int totalTime;
- int numberOfDives;
- int altitude;
- int personalSafety;
- int timeFormat;
- bool lightEnabled;
- int light;
- bool alarmTimeEnabled;
- int alarmTime;
- bool alarmDepthEnabled;
- int alarmDepth;
- int leftButtonSensitivity;
- int rightButtonSensitivity;
- int bottomGasConsumption;
- int decoGasConsumption;
- bool modWarning;
- bool dynamicAscendRate;
- bool graphicalSpeedIndicator;
- bool alwaysShowppO2;
- int tempSensorOffset;
- unsigned safetyStopLength;
- unsigned safetyStopStartDepth;
- unsigned safetyStopEndDepth;
- unsigned safetyStopResetDepth;
-};
-
-
-#endif // DEVICEDETAILS_H
diff --git a/subsurface-core/display.h b/subsurface-core/display.h
deleted file mode 100644
index 9e3e1d159..000000000
--- a/subsurface-core/display.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef DISPLAY_H
-#define DISPLAY_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct membuffer;
-
-#define SCALE_SCREEN 1.0
-#define SCALE_PRINT (1.0 / get_screen_dpi())
-
-extern double get_screen_dpi(void);
-
-/* Plot info with smoothing, velocity indication
- * and one-, two- and three-minute minimums and maximums */
-struct plot_info {
- int nr;
- int maxtime;
- int meandepth, maxdepth;
- int minpressure, maxpressure;
- int minhr, maxhr;
- int mintemp, maxtemp;
- enum {AIR, NITROX, TRIMIX, FREEDIVING} dive_type;
- double endtempcoord;
- double maxpp;
- bool has_ndl;
- struct plot_data *entry;
-};
-
-typedef enum {
- SC_SCREEN,
- SC_PRINT
-} scale_mode_t;
-
-extern struct divecomputer *select_dc(struct dive *);
-
-extern unsigned int dc_number;
-
-extern unsigned int amount_selected;
-
-extern int is_default_dive_computer_device(const char *);
-extern int is_default_dive_computer(const char *, const char *);
-
-typedef void (*device_callback_t)(const char *name, void *userdata);
-
-#define DC_TYPE_SERIAL 1
-#define DC_TYPE_UEMIS 2
-#define DC_TYPE_OTHER 3
-
-int enumerate_devices(device_callback_t callback, void *userdata, int dc_type);
-
-extern const char *default_dive_computer_vendor;
-extern const char *default_dive_computer_product;
-extern const char *default_dive_computer_device;
-extern int default_dive_computer_download_mode;
-#define AMB_PERCENTAGE 50.0
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // DISPLAY_H
diff --git a/subsurface-core/dive.c b/subsurface-core/dive.c
deleted file mode 100644
index ce730da28..000000000
--- a/subsurface-core/dive.c
+++ /dev/null
@@ -1,3561 +0,0 @@
-/* dive.c */
-/* maintains the internal dive list structure */
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
-#include "gettext.h"
-#include "dive.h"
-#include "libdivecomputer.h"
-#include "device.h"
-#include "divelist.h"
-#include "qthelperfromc.h"
-
-/* one could argue about the best place to have this variable -
- * it's used in the UI, but it seems to make the most sense to have it
- * here */
-struct dive displayed_dive;
-struct dive_site displayed_dive_site;
-
-struct tag_entry *g_tag_list = NULL;
-
-static const char *default_tags[] = {
- QT_TRANSLATE_NOOP("gettextFromC", "boat"), QT_TRANSLATE_NOOP("gettextFromC", "shore"), QT_TRANSLATE_NOOP("gettextFromC", "drift"),
- QT_TRANSLATE_NOOP("gettextFromC", "deep"), QT_TRANSLATE_NOOP("gettextFromC", "cavern"), QT_TRANSLATE_NOOP("gettextFromC", "ice"),
- QT_TRANSLATE_NOOP("gettextFromC", "wreck"), QT_TRANSLATE_NOOP("gettextFromC", "cave"), QT_TRANSLATE_NOOP("gettextFromC", "altitude"),
- QT_TRANSLATE_NOOP("gettextFromC", "pool"), QT_TRANSLATE_NOOP("gettextFromC", "lake"), QT_TRANSLATE_NOOP("gettextFromC", "river"),
- QT_TRANSLATE_NOOP("gettextFromC", "night"), QT_TRANSLATE_NOOP("gettextFromC", "fresh"), QT_TRANSLATE_NOOP("gettextFromC", "student"),
- QT_TRANSLATE_NOOP("gettextFromC", "instructor"), QT_TRANSLATE_NOOP("gettextFromC", "photo"), QT_TRANSLATE_NOOP("gettextFromC", "video"),
- QT_TRANSLATE_NOOP("gettextFromC", "deco")
-};
-
-const char *cylinderuse_text[] = {
- QT_TRANSLATE_NOOP("gettextFromC", "OC-gas"), QT_TRANSLATE_NOOP("gettextFromC", "diluent"), QT_TRANSLATE_NOOP("gettextFromC", "oxygen")
-};
-const char *divemode_text[] = { "OC", "CCR", "PSCR", "Freedive" };
-
-int event_is_gaschange(struct event *ev)
-{
- return ev->type == SAMPLE_EVENT_GASCHANGE ||
- ev->type == SAMPLE_EVENT_GASCHANGE2;
-}
-
-/*
- * Does the gas mix data match the legacy
- * libdivecomputer event format? If so,
- * we can skip saving it, in order to maintain
- * the old save formats. We'll re-generate the
- * gas mix when loading.
- */
-int event_gasmix_redundant(struct event *ev)
-{
- int value = ev->value;
- int o2, he;
-
- o2 = (value & 0xffff) * 10;
- he = (value >> 16) * 10;
- return o2 == ev->gas.mix.o2.permille &&
- he == ev->gas.mix.he.permille;
-}
-
-struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const char *name)
-{
- int gas_index = -1;
- struct event *ev, **p;
- unsigned int size, len = strlen(name);
-
- size = sizeof(*ev) + len + 1;
- ev = malloc(size);
- if (!ev)
- return NULL;
- memset(ev, 0, size);
- memcpy(ev->name, name, len);
- ev->time.seconds = time;
- ev->type = type;
- ev->flags = flags;
- ev->value = value;
-
- /*
- * Expand the events into a sane format. Currently
- * just gas switches
- */
- switch (type) {
- case SAMPLE_EVENT_GASCHANGE2:
- /* High 16 bits are He percentage */
- ev->gas.mix.he.permille = (value >> 16) * 10;
-
- /* Extension to the GASCHANGE2 format: cylinder index in 'flags' */
- if (flags > 0 && flags <= MAX_CYLINDERS)
- gas_index = flags-1;
- /* Fallthrough */
- case SAMPLE_EVENT_GASCHANGE:
- /* Low 16 bits are O2 percentage */
- ev->gas.mix.o2.permille = (value & 0xffff) * 10;
- ev->gas.index = gas_index;
- break;
- }
-
- p = &dc->events;
-
- /* insert in the sorted list of events */
- while (*p && (*p)->time.seconds <= time)
- p = &(*p)->next;
- ev->next = *p;
- *p = ev;
- remember_event(name);
- return ev;
-}
-
-static int same_event(struct event *a, struct event *b)
-{
- if (a->time.seconds != b->time.seconds)
- return 0;
- if (a->type != b->type)
- return 0;
- if (a->flags != b->flags)
- return 0;
- if (a->value != b->value)
- return 0;
- return !strcmp(a->name, b->name);
-}
-
-void remove_event(struct event *event)
-{
- struct event **ep = &current_dc->events;
- while (ep && !same_event(*ep, event))
- ep = &(*ep)->next;
- if (ep) {
- /* we can't link directly with event->next
- * because 'event' can be a copy from another
- * dive (for instance the displayed_dive
- * that we use on the interface to show things). */
- struct event *temp = (*ep)->next;
- free(*ep);
- *ep = temp;
- }
-}
-
-/* since the name is an array as part of the structure (how silly is that?) we
- * have to actually remove the existing event and replace it with a new one.
- * WARNING, WARNING... this may end up freeing event in case that event is indeed
- * WARNING, WARNING... part of this divecomputer on this dive! */
-void update_event_name(struct dive *d, struct event *event, char *name)
-{
- if (!d || !event)
- return;
- struct divecomputer *dc = get_dive_dc(d, dc_number);
- if (!dc)
- return;
- struct event **removep = &dc->events;
- struct event *remove;
- while ((*removep)->next && !same_event(*removep, event))
- removep = &(*removep)->next;
- if (!same_event(*removep, event))
- return;
- remove = *removep;
- *removep = (*removep)->next;
- add_event(dc, event->time.seconds, event->type, event->flags, event->value, name);
- free(remove);
- invalidate_dive_cache(d);
-}
-
-void add_extra_data(struct divecomputer *dc, const char *key, const char *value)
-{
- struct extra_data **ed = &dc->extra_data;
-
- while (*ed)
- ed = &(*ed)->next;
- *ed = malloc(sizeof(struct extra_data));
- if (*ed) {
- (*ed)->key = strdup(key);
- (*ed)->value = strdup(value);
- (*ed)->next = NULL;
- }
-}
-
-/* this returns a pointer to static variable - so use it right away after calling */
-struct gasmix *get_gasmix_from_event(struct event *ev)
-{
- static struct gasmix dummy;
- if (ev && event_is_gaschange(ev))
- return &ev->gas.mix;
-
- return &dummy;
-}
-
-int get_pressure_units(int mb, const char **units)
-{
- int pressure;
- const char *unit;
- struct units *units_p = get_units();
-
- switch (units_p->pressure) {
- case PASCAL:
- pressure = mb * 100;
- unit = translate("gettextFromC", "pascal");
- break;
- case BAR:
- default:
- pressure = (mb + 500) / 1000;
- unit = translate("gettextFromC", "bar");
- break;
- case PSI:
- pressure = mbar_to_PSI(mb);
- unit = translate("gettextFromC", "psi");
- break;
- }
- if (units)
- *units = unit;
- return pressure;
-}
-
-double get_temp_units(unsigned int mk, const char **units)
-{
- double deg;
- const char *unit;
- struct units *units_p = get_units();
-
- if (units_p->temperature == FAHRENHEIT) {
- deg = mkelvin_to_F(mk);
- unit = UTF8_DEGREE "F";
- } else {
- deg = mkelvin_to_C(mk);
- unit = UTF8_DEGREE "C";
- }
- if (units)
- *units = unit;
- return deg;
-}
-
-double get_volume_units(unsigned int ml, int *frac, const char **units)
-{
- int decimals;
- double vol;
- const char *unit;
- struct units *units_p = get_units();
-
- switch (units_p->volume) {
- case LITER:
- default:
- vol = ml / 1000.0;
- unit = translate("gettextFromC", "â„“");
- decimals = 1;
- break;
- case CUFT:
- vol = ml_to_cuft(ml);
- unit = translate("gettextFromC", "cuft");
- decimals = 2;
- break;
- }
- if (frac)
- *frac = decimals;
- if (units)
- *units = unit;
- return vol;
-}
-
-int units_to_sac(double volume)
-{
- if (get_units()->volume == CUFT)
- return rint(cuft_to_l(volume) * 1000.0);
- else
- return rint(volume * 1000);
-}
-
-unsigned int units_to_depth(double depth)
-{
- if (get_units()->length == METERS)
- return rint(depth * 1000);
- return feet_to_mm(depth);
-}
-
-double get_depth_units(int mm, int *frac, const char **units)
-{
- int decimals;
- double d;
- const char *unit;
- struct units *units_p = get_units();
-
- switch (units_p->length) {
- case METERS:
- default:
- d = mm / 1000.0;
- unit = translate("gettextFromC", "m");
- decimals = d < 20;
- break;
- case FEET:
- d = mm_to_feet(mm);
- unit = translate("gettextFromC", "ft");
- decimals = 0;
- break;
- }
- if (frac)
- *frac = decimals;
- if (units)
- *units = unit;
- return d;
-}
-
-double get_vertical_speed_units(unsigned int mms, int *frac, const char **units)
-{
- double d;
- const char *unit;
- const struct units *units_p = get_units();
- const double time_factor = units_p->vertical_speed_time == MINUTES ? 60.0 : 1.0;
-
- switch (units_p->length) {
- case METERS:
- default:
- d = mms / 1000.0 * time_factor;
- if (units_p->vertical_speed_time == MINUTES)
- unit = translate("gettextFromC", "m/min");
- else
- unit = translate("gettextFromC", "m/s");
- break;
- case FEET:
- d = mm_to_feet(mms) * time_factor;
- if (units_p->vertical_speed_time == MINUTES)
- unit = translate("gettextFromC", "ft/min");
- else
- unit = translate("gettextFromC", "ft/s");
- break;
- }
- if (frac)
- *frac = d < 10;
- if (units)
- *units = unit;
- return d;
-}
-
-double get_weight_units(unsigned int grams, int *frac, const char **units)
-{
- int decimals;
- double value;
- const char *unit;
- struct units *units_p = get_units();
-
- if (units_p->weight == LBS) {
- value = grams_to_lbs(grams);
- unit = translate("gettextFromC", "lbs");
- decimals = 0;
- } else {
- value = grams / 1000.0;
- unit = translate("gettextFromC", "kg");
- decimals = 1;
- }
- if (frac)
- *frac = decimals;
- if (units)
- *units = unit;
- return value;
-}
-
-bool has_hr_data(struct divecomputer *dc)
-{
- int i;
- struct sample *sample;
-
- if (!dc)
- return false;
-
- sample = dc->sample;
- for (i = 0; i < dc->samples; i++)
- if (sample[i].heartbeat)
- return true;
- return false;
-}
-
-struct dive *alloc_dive(void)
-{
- struct dive *dive;
-
- dive = malloc(sizeof(*dive));
- if (!dive)
- exit(1);
- memset(dive, 0, sizeof(*dive));
- dive->id = dive_getUniqID(dive);
- return dive;
-}
-
-static void free_dc(struct divecomputer *dc);
-static void free_pic(struct picture *picture);
-
-/* this is very different from the copy_divecomputer later in this file;
- * this function actually makes full copies of the content */
-static void copy_dc(struct divecomputer *sdc, struct divecomputer *ddc)
-{
- *ddc = *sdc;
- ddc->model = copy_string(sdc->model);
- copy_samples(sdc, ddc);
- copy_events(sdc, ddc);
-}
-
-/* copy an element in a list of pictures */
-static void copy_pl(struct picture *sp, struct picture *dp)
-{
- *dp = *sp;
- dp->filename = copy_string(sp->filename);
- dp->hash = copy_string(sp->hash);
-}
-
-/* copy an element in a list of tags */
-static void copy_tl(struct tag_entry *st, struct tag_entry *dt)
-{
- dt->tag = malloc(sizeof(struct divetag));
- dt->tag->name = copy_string(st->tag->name);
- dt->tag->source = copy_string(st->tag->source);
-}
-
-/* Clear everything but the first element;
- * this works for taglist, picturelist, even dive computers */
-#define STRUCTURED_LIST_FREE(_type, _start, _free) \
- { \
- _type *_ptr = _start; \
- while (_ptr) { \
- _type *_next = _ptr->next; \
- _free(_ptr); \
- _ptr = _next; \
- } \
- }
-
-#define STRUCTURED_LIST_COPY(_type, _first, _dest, _cpy) \
- { \
- _type *_sptr = _first; \
- _type **_dptr = &_dest; \
- while (_sptr) { \
- *_dptr = malloc(sizeof(_type)); \
- _cpy(_sptr, *_dptr); \
- _sptr = _sptr->next; \
- _dptr = &(*_dptr)->next; \
- } \
- *_dptr = 0; \
- }
-
-/* copy_dive makes duplicates of many components of a dive;
- * in order not to leak memory, we need to free those .
- * copy_dive doesn't play with the divetrip and forward/backward pointers
- * so we can ignore those */
-void clear_dive(struct dive *d)
-{
- if (!d)
- return;
- /* free the strings */
- free(d->buddy);
- free(d->divemaster);
- free(d->notes);
- free(d->suit);
- /* free tags, additional dive computers, and pictures */
- taglist_free(d->tag_list);
- STRUCTURED_LIST_FREE(struct divecomputer, d->dc.next, free_dc);
- STRUCTURED_LIST_FREE(struct picture, d->picture_list, free_pic);
- for (int i = 0; i < MAX_CYLINDERS; i++)
- free((void *)d->cylinder[i].type.description);
- for (int i = 0; i < MAX_WEIGHTSYSTEMS; i++)
- free((void *)d->weightsystem[i].description);
- memset(d, 0, sizeof(struct dive));
-}
-
-/* make a true copy that is independent of the source dive;
- * all data structures are duplicated, so the copy can be modified without
- * any impact on the source */
-void copy_dive(struct dive *s, struct dive *d)
-{
- clear_dive(d);
- /* simply copy things over, but then make actual copies of the
- * relevant components that are referenced through pointers,
- * so all the strings and the structured lists */
- *d = *s;
- invalidate_dive_cache(d);
- d->buddy = copy_string(s->buddy);
- d->divemaster = copy_string(s->divemaster);
- d->notes = copy_string(s->notes);
- d->suit = copy_string(s->suit);
- for (int i = 0; i < MAX_CYLINDERS; i++)
- d->cylinder[i].type.description = copy_string(s->cylinder[i].type.description);
- for (int i = 0; i < MAX_WEIGHTSYSTEMS; i++)
- d->weightsystem[i].description = copy_string(s->weightsystem[i].description);
- STRUCTURED_LIST_COPY(struct picture, s->picture_list, d->picture_list, copy_pl);
- STRUCTURED_LIST_COPY(struct tag_entry, s->tag_list, d->tag_list, copy_tl);
-
- // Copy the first dc explicitly, then the list of subsequent dc's
- copy_dc(&s->dc, &d->dc);
- STRUCTURED_LIST_COPY(struct divecomputer, s->dc.next, d->dc.next, copy_dc);
-}
-
-/* make a clone of the source dive and clean out the source dive;
- * this is specifically so we can create a dive in the displayed_dive and then
- * add it to the divelist.
- * Note the difference to copy_dive() / clean_dive() */
-struct dive *clone_dive(struct dive *s)
-{
- struct dive *dive = alloc_dive();
- *dive = *s; // so all the pointers in dive point to the things s pointed to
- memset(s, 0, sizeof(struct dive)); // and now the pointers in s are gone
- return dive;
-}
-
-#define CONDITIONAL_COPY_STRING(_component) \
- if (what._component) \
- d->_component = copy_string(s->_component)
-
-// copy elements, depending on bits in what that are set
-void selective_copy_dive(struct dive *s, struct dive *d, struct dive_components what, bool clear)
-{
- if (clear)
- clear_dive(d);
- CONDITIONAL_COPY_STRING(notes);
- CONDITIONAL_COPY_STRING(divemaster);
- CONDITIONAL_COPY_STRING(buddy);
- CONDITIONAL_COPY_STRING(suit);
- if (what.rating)
- d->rating = s->rating;
- if (what.visibility)
- d->visibility = s->visibility;
- if (what.divesite)
- d->dive_site_uuid = s->dive_site_uuid;
- if (what.tags)
- STRUCTURED_LIST_COPY(struct tag_entry, s->tag_list, d->tag_list, copy_tl);
- if (what.cylinders)
- copy_cylinders(s, d, false);
- if (what.weights)
- for (int i = 0; i < MAX_WEIGHTSYSTEMS; i++) {
- free((void *)d->weightsystem[i].description);
- d->weightsystem[i] = s->weightsystem[i];
- d->weightsystem[i].description = copy_string(s->weightsystem[i].description);
- }
-}
-#undef CONDITIONAL_COPY_STRING
-
-struct event *clone_event(const struct event *src_ev)
-{
- struct event *ev;
- if (!src_ev)
- return NULL;
-
- size_t size = sizeof(*src_ev) + strlen(src_ev->name) + 1;
- ev = (struct event*) malloc(size);
- if (!ev)
- exit(1);
- memcpy(ev, src_ev, size);
- ev->next = NULL;
-
- return ev;
-}
-
-/* copies all events in this dive computer */
-void copy_events(struct divecomputer *s, struct divecomputer *d)
-{
- struct event *ev, **pev;
- if (!s || !d)
- return;
- ev = s->events;
- pev = &d->events;
- while (ev != NULL) {
- struct event *new_ev = clone_event(ev);
- *pev = new_ev;
- pev = &new_ev->next;
- ev = ev->next;
- }
- *pev = NULL;
-}
-
-int nr_cylinders(struct dive *dive)
-{
- int nr;
-
- for (nr = MAX_CYLINDERS; nr; --nr) {
- cylinder_t *cylinder = dive->cylinder + nr - 1;
- if (!cylinder_nodata(cylinder))
- break;
- }
- return nr;
-}
-
-int nr_weightsystems(struct dive *dive)
-{
- int nr;
-
- for (nr = MAX_WEIGHTSYSTEMS; nr; --nr) {
- weightsystem_t *ws = dive->weightsystem + nr - 1;
- if (!weightsystem_none(ws))
- break;
- }
- return nr;
-}
-
-/* copy the equipment data part of the cylinders */
-void copy_cylinders(struct dive *s, struct dive *d, bool used_only)
-{
- int i,j;
- if (!s || !d)
- return;
- for (i = 0; i < MAX_CYLINDERS; i++) {
- free((void *)d->cylinder[i].type.description);
- memset(&d->cylinder[i], 0, sizeof(cylinder_t));
- }
- for (i = j = 0; i < MAX_CYLINDERS; i++) {
- if (!used_only || is_cylinder_used(s, i)) {
- d->cylinder[j].type = s->cylinder[i].type;
- d->cylinder[j].type.description = copy_string(s->cylinder[i].type.description);
- d->cylinder[j].gasmix = s->cylinder[i].gasmix;
- d->cylinder[j].depth = s->cylinder[i].depth;
- d->cylinder[j].cylinder_use = s->cylinder[i].cylinder_use;
- d->cylinder[j].manually_added = true;
- j++;
- }
- }
-}
-
-int cylinderuse_from_text(const char *text)
-{
- for (enum cylinderuse i = 0; i < NUM_GAS_USE; i++) {
- if (same_string(text, cylinderuse_text[i]) || same_string(text, translate("gettextFromC", cylinderuse_text[i])))
- return i;
- }
- return -1;
-}
-
-void copy_samples(struct divecomputer *s, struct divecomputer *d)
-{
- /* instead of carefully copying them one by one and calling add_sample
- * over and over again, let's just copy the whole blob */
- if (!s || !d)
- return;
- int nr = s->samples;
- d->samples = nr;
- d->alloc_samples = nr;
- // We expect to be able to read the memory in the other end of the pointer
- // if its a valid pointer, so don't expect malloc() to return NULL for
- // zero-sized malloc, do it ourselves.
- d->sample = NULL;
-
- if(!nr)
- return;
-
- d->sample = malloc(nr * sizeof(struct sample));
- if (d->sample)
- memcpy(d->sample, s->sample, nr * sizeof(struct sample));
-}
-
-struct sample *prepare_sample(struct divecomputer *dc)
-{
- if (dc) {
- int nr = dc->samples;
- int alloc_samples = dc->alloc_samples;
- struct sample *sample;
- if (nr >= alloc_samples) {
- struct sample *newsamples;
-
- alloc_samples = (alloc_samples * 3) / 2 + 10;
- newsamples = realloc(dc->sample, alloc_samples * sizeof(struct sample));
- if (!newsamples)
- return NULL;
- dc->alloc_samples = alloc_samples;
- dc->sample = newsamples;
- }
- sample = dc->sample + nr;
- memset(sample, 0, sizeof(*sample));
- return sample;
- }
- return NULL;
-}
-
-void finish_sample(struct divecomputer *dc)
-{
- dc->samples++;
-}
-
-/*
- * So when we re-calculate maxdepth and meandepth, we will
- * not override the old numbers if they are close to the
- * new ones.
- *
- * Why? Because a dive computer may well actually track the
- * max depth and mean depth at finer granularity than the
- * samples it stores. So it's possible that the max and mean
- * have been reported more correctly originally.
- *
- * Only if the values calculated from the samples are clearly
- * different do we override the normal depth values.
- *
- * This considers 1m to be "clearly different". That's
- * a totally random number.
- */
-static void update_depth(depth_t *depth, int new)
-{
- if (new) {
- int old = depth->mm;
-
- if (abs(old - new) > 1000)
- depth->mm = new;
- }
-}
-
-static void update_temperature(temperature_t *temperature, int new)
-{
- if (new) {
- int old = temperature->mkelvin;
-
- if (abs(old - new) > 1000)
- temperature->mkelvin = new;
- }
-}
-
-/*
- * Calculate how long we were actually under water, and the average
- * depth while under water.
- *
- * This ignores any surface time in the middle of the dive.
- */
-void fixup_dc_duration(struct divecomputer *dc)
-{
- int duration, i;
- int lasttime, lastdepth, depthtime;
-
- duration = 0;
- lasttime = 0;
- lastdepth = 0;
- depthtime = 0;
- for (i = 0; i < dc->samples; i++) {
- struct sample *sample = dc->sample + i;
- int time = sample->time.seconds;
- int depth = sample->depth.mm;
-
- /* We ignore segments at the surface */
- if (depth > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD) {
- duration += time - lasttime;
- depthtime += (time - lasttime) * (depth + lastdepth) / 2;
- }
- lastdepth = depth;
- lasttime = time;
- }
- if (duration) {
- dc->duration.seconds = duration;
- dc->meandepth.mm = (depthtime + duration / 2) / duration;
- }
-}
-
-void per_cylinder_mean_depth(struct dive *dive, struct divecomputer *dc, int *mean, int *duration)
-{
- int i;
- int depthtime[MAX_CYLINDERS] = { 0, };
- uint32_t lasttime = 0;
- int lastdepth = 0;
- int idx = 0;
-
- for (i = 0; i < MAX_CYLINDERS; i++)
- mean[i] = duration[i] = 0;
- if (!dc)
- return;
- struct event *ev = get_next_event(dc->events, "gaschange");
- if (!ev || (dc && dc->sample && ev->time.seconds == dc->sample[0].time.seconds && get_next_event(ev->next, "gaschange") == NULL)) {
- // we have either no gas change or only one gas change and that's setting an explicit first cylinder
- mean[explicit_first_cylinder(dive, dc)] = dc->meandepth.mm;
- duration[explicit_first_cylinder(dive, dc)] = dc->duration.seconds;
-
- if (dc->divemode == CCR) {
- // Do the same for the O2 cylinder
- int o2_cyl = get_cylinder_idx_by_use(dive, OXYGEN);
- if (o2_cyl < 0)
- return;
- mean[o2_cyl] = dc->meandepth.mm;
- duration[o2_cyl] = dc->duration.seconds;
- }
- return;
- }
- if (!dc->samples)
- dc = fake_dc(dc, false);
- for (i = 0; i < dc->samples; i++) {
- struct sample *sample = dc->sample + i;
- uint32_t time = sample->time.seconds;
- int depth = sample->depth.mm;
-
- /* Make sure to move the event past 'lasttime' */
- while (ev && lasttime >= ev->time.seconds) {
- idx = get_cylinder_index(dive, ev);
- ev = get_next_event(ev->next, "gaschange");
- }
-
- /* Do we need to fake a midway sample at an event? */
- if (ev && time > ev->time.seconds) {
- int newtime = ev->time.seconds;
- int newdepth = interpolate(lastdepth, depth, newtime - lasttime, time - lasttime);
-
- time = newtime;
- depth = newdepth;
- i--;
- }
- /* We ignore segments at the surface */
- if (depth > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD) {
- duration[idx] += time - lasttime;
- depthtime[idx] += (time - lasttime) * (depth + lastdepth) / 2;
- }
- lastdepth = depth;
- lasttime = time;
- }
- for (i = 0; i < MAX_CYLINDERS; i++) {
- if (duration[i])
- mean[i] = (depthtime[i] + duration[i] / 2) / duration[i];
- }
-}
-
-static void update_min_max_temperatures(struct dive *dive, temperature_t temperature)
-{
- if (temperature.mkelvin) {
- if (!dive->maxtemp.mkelvin || temperature.mkelvin > dive->maxtemp.mkelvin)
- dive->maxtemp = temperature;
- if (!dive->mintemp.mkelvin || temperature.mkelvin < dive->mintemp.mkelvin)
- dive->mintemp = temperature;
- }
-}
-
-int gas_volume(cylinder_t *cyl, pressure_t p)
-{
- double bar = p.mbar / 1000.0;
- double z_factor = gas_compressibility_factor(&cyl->gasmix, bar);
- return cyl->type.size.mliter * bar_to_atm(bar) / z_factor;
-}
-
-/*
- * If the cylinder tank pressures are within half a bar
- * (about 8 PSI) of the sample pressures, we consider it
- * to be a rounding error, and throw them away as redundant.
- */
-static int same_rounded_pressure(pressure_t a, pressure_t b)
-{
- return abs(a.mbar - b.mbar) <= 500;
-}
-
-/* Some dive computers (Cobalt) don't start the dive with cylinder 0 but explicitly
- * tell us what the first gas is with a gas change event in the first sample.
- * Sneakily we'll use a return value of 0 (or FALSE) when there is no explicit
- * first cylinder - in which case cylinder 0 is indeed the first cylinder */
-int explicit_first_cylinder(struct dive *dive, struct divecomputer *dc)
-{
- if (dc) {
- struct event *ev = get_next_event(dc->events, "gaschange");
- if (ev && dc->sample && ev->time.seconds == dc->sample[0].time.seconds)
- return get_cylinder_index(dive, ev);
- else if (dc->divemode == CCR)
- return MAX(get_cylinder_idx_by_use(dive, DILUENT), 0);
- }
- return 0;
-}
-
-/* this gets called when the dive mode has changed (so OC vs. CC)
- * there are two places we might have setpoints... events or in the samples
- */
-void update_setpoint_events(struct divecomputer *dc)
-{
- struct event *ev;
- int new_setpoint = 0;
-
- if (dc->divemode == CCR)
- new_setpoint = prefs.defaultsetpoint;
-
- if (dc->divemode == OC &&
- (same_string(dc->model, "Shearwater Predator") ||
- same_string(dc->model, "Shearwater Petrel") ||
- same_string(dc->model, "Shearwater Nerd"))) {
- // make sure there's no setpoint in the samples
- // this is an irreversible change - so switching a dive to OC
- // by mistake when it's actually CCR is _bad_
- // So we make sure, this comes from a Predator or Petrel and we only remove
- // pO2 values we would have computed anyway.
- struct event *ev = get_next_event(dc->events, "gaschange");
- struct gasmix *gasmix = get_gasmix_from_event(ev);
- struct event *next = get_next_event(ev, "gaschange");
-
- for (int i = 0; i < dc->samples; i++) {
- struct gas_pressures pressures;
- if (next && dc->sample[i].time.seconds >= next->time.seconds) {
- ev = next;
- gasmix = get_gasmix_from_event(ev);
- next = get_next_event(ev, "gaschange");
- }
- fill_pressures(&pressures, calculate_depth_to_mbar(dc->sample[i].depth.mm, dc->surface_pressure, 0), gasmix ,0, OC);
- if (abs(dc->sample[i].setpoint.mbar - (int)(1000 * pressures.o2)) <= 50)
- dc->sample[i].setpoint.mbar = 0;
- }
- }
-
- // an "SP change" event at t=0 is currently our marker for OC vs CCR
- // this will need to change to a saner setup, but for now we can just
- // check if such an event is there and adjust it, or add that event
- ev = get_next_event(dc->events, "SP change");
- if (ev && ev->time.seconds == 0) {
- ev->value = new_setpoint;
- } else {
- if (!add_event(dc, 0, SAMPLE_EVENT_PO2, 0, new_setpoint, "SP change"))
- fprintf(stderr, "Could not add setpoint change event\n");
- }
-}
-
-void sanitize_gasmix(struct gasmix *mix)
-{
- unsigned int o2, he;
-
- o2 = mix->o2.permille;
- he = mix->he.permille;
-
- /* Regular air: leave empty */
- if (!he) {
- if (!o2)
- return;
- /* 20.8% to 21% O2 is just air */
- if (gasmix_is_air(mix)) {
- mix->o2.permille = 0;
- return;
- }
- }
-
- /* Sane mix? */
- if (o2 <= 1000 && he <= 1000 && o2 + he <= 1000)
- return;
- fprintf(stderr, "Odd gasmix: %u O2 %u He\n", o2, he);
- memset(mix, 0, sizeof(*mix));
-}
-
-/*
- * See if the size/workingpressure looks like some standard cylinder
- * size, eg "AL80".
- *
- * NOTE! We don't take compressibility into account when naming
- * cylinders. That makes a certain amount of sense, since the
- * cylinder name is independent from the gasmix, and different
- * gasmixes have different compressibility.
- */
-static void match_standard_cylinder(cylinder_type_t *type)
-{
- double cuft, bar;
- int psi, len;
- const char *fmt;
- char buffer[40], *p;
-
- /* Do we already have a cylinder description? */
- if (type->description)
- return;
-
- bar = type->workingpressure.mbar / 1000.0;
- cuft = ml_to_cuft(type->size.mliter);
- cuft *= bar_to_atm(bar);
- psi = to_PSI(type->workingpressure);
-
- switch (psi) {
- case 2300 ... 2500: /* 2400 psi: LP tank */
- fmt = "LP%d";
- break;
- case 2600 ... 2700: /* 2640 psi: LP+10% */
- fmt = "LP%d";
- break;
- case 2900 ... 3100: /* 3000 psi: ALx tank */
- fmt = "AL%d";
- break;
- case 3400 ... 3500: /* 3442 psi: HP tank */
- fmt = "HP%d";
- break;
- case 3700 ... 3850: /* HP+10% */
- fmt = "HP%d+";
- break;
- default:
- return;
- }
- len = snprintf(buffer, sizeof(buffer), fmt, (int)rint(cuft));
- p = malloc(len + 1);
- if (!p)
- return;
- memcpy(p, buffer, len + 1);
- type->description = p;
-}
-
-
-/*
- * There are two ways to give cylinder size information:
- * - total amount of gas in cuft (depends on working pressure and physical size)
- * - physical size
- *
- * where "physical size" is the one that actually matters and is sane.
- *
- * We internally use physical size only. But we save the workingpressure
- * so that we can do the conversion if required.
- */
-static void sanitize_cylinder_type(cylinder_type_t *type)
-{
- double volume_of_air, volume;
-
- /* If we have no working pressure, it had *better* be just a physical size! */
- if (!type->workingpressure.mbar)
- return;
-
- /* No size either? Nothing to go on */
- if (!type->size.mliter)
- return;
-
- if (xml_parsing_units.volume == CUFT) {
- double bar = type->workingpressure.mbar / 1000.0;
- /* confusing - we don't really start from ml but millicuft !*/
- volume_of_air = cuft_to_l(type->size.mliter);
- /* milliliters at 1 atm: not corrected for compressibility! */
- volume = volume_of_air / bar_to_atm(bar);
- type->size.mliter = rint(volume);
- }
-
- /* Ok, we have both size and pressure: try to match a description */
- match_standard_cylinder(type);
-}
-
-static void sanitize_cylinder_info(struct dive *dive)
-{
- int i;
-
- for (i = 0; i < MAX_CYLINDERS; i++) {
- sanitize_gasmix(&dive->cylinder[i].gasmix);
- sanitize_cylinder_type(&dive->cylinder[i].type);
- }
-}
-
-/* some events should never be thrown away */
-static bool is_potentially_redundant(struct event *event)
-{
- if (!strcmp(event->name, "gaschange"))
- return false;
- if (!strcmp(event->name, "bookmark"))
- return false;
- if (!strcmp(event->name, "heading"))
- return false;
- return true;
-}
-
-/* match just by name - we compare the details in the code that uses this helper */
-static struct event *find_previous_event(struct divecomputer *dc, struct event *event)
-{
- struct event *ev = dc->events;
- struct event *previous = NULL;
-
- if (same_string(event->name, ""))
- return NULL;
- while (ev && ev != event) {
- if (same_string(ev->name, event->name))
- previous = ev;
- ev = ev->next;
- }
- return previous;
-}
-
-static void fixup_surface_pressure(struct dive *dive)
-{
- struct divecomputer *dc;
- int sum = 0, nr = 0;
-
- for_each_dc (dive, dc) {
- if (dc->surface_pressure.mbar) {
- sum += dc->surface_pressure.mbar;
- nr++;
- }
- }
- if (nr)
- dive->surface_pressure.mbar = (sum + nr / 2) / nr;
-}
-
-static void fixup_water_salinity(struct dive *dive)
-{
- struct divecomputer *dc;
- int sum = 0, nr = 0;
-
- for_each_dc (dive, dc) {
- if (dc->salinity) {
- if (dc->salinity < 500)
- dc->salinity += FRESHWATER_SALINITY;
- sum += dc->salinity;
- nr++;
- }
- }
- if (nr)
- dive->salinity = (sum + nr / 2) / nr;
-}
-
-static void fixup_meandepth(struct dive *dive)
-{
- struct divecomputer *dc;
- int sum = 0, nr = 0;
-
- for_each_dc (dive, dc) {
- if (dc->meandepth.mm) {
- sum += dc->meandepth.mm;
- nr++;
- }
- }
- if (nr)
- dive->meandepth.mm = (sum + nr / 2) / nr;
-}
-
-static void fixup_duration(struct dive *dive)
-{
- struct divecomputer *dc;
- unsigned int duration = 0;
-
- for_each_dc (dive, dc)
- duration = MAX(duration, dc->duration.seconds);
-
- dive->duration.seconds = duration;
-}
-
-/*
- * What do the dive computers say the water temperature is?
- * (not in the samples, but as dc property for dcs that support that)
- */
-unsigned int dc_watertemp(struct divecomputer *dc)
-{
- int sum = 0, nr = 0;
-
- do {
- if (dc->watertemp.mkelvin) {
- sum += dc->watertemp.mkelvin;
- nr++;
- }
- } while ((dc = dc->next) != NULL);
- if (!nr)
- return 0;
- return (sum + nr / 2) / nr;
-}
-
-static void fixup_watertemp(struct dive *dive)
-{
- if (!dive->watertemp.mkelvin)
- dive->watertemp.mkelvin = dc_watertemp(&dive->dc);
-}
-
-/*
- * What do the dive computers say the air temperature is?
- */
-unsigned int dc_airtemp(struct divecomputer *dc)
-{
- int sum = 0, nr = 0;
-
- do {
- if (dc->airtemp.mkelvin) {
- sum += dc->airtemp.mkelvin;
- nr++;
- }
- } while ((dc = dc->next) != NULL);
- if (!nr)
- return 0;
- return (sum + nr / 2) / nr;
-}
-
-static void fixup_cylinder_use(struct dive *dive) // for CCR dives, store the indices
-{ // of the oxygen and diluent cylinders
- dive->oxygen_cylinder_index = get_cylinder_idx_by_use(dive, OXYGEN);
- dive->diluent_cylinder_index = get_cylinder_idx_by_use(dive, DILUENT);
-}
-
-static void fixup_airtemp(struct dive *dive)
-{
- if (!dive->airtemp.mkelvin)
- dive->airtemp.mkelvin = dc_airtemp(&dive->dc);
-}
-
-/* zero out the airtemp in the dive structure if it was just created by
- * running fixup on the dive. keep it if it had been edited by hand */
-static void un_fixup_airtemp(struct dive *a)
-{
- if (a->airtemp.mkelvin && a->airtemp.mkelvin == dc_airtemp(&a->dc))
- a->airtemp.mkelvin = 0;
-}
-
-/*
- * events are stored as a linked list, so the concept of
- * "consecutive, identical events" is somewhat hard to
- * implement correctly (especially given that on some dive
- * computers events are asynchronous, so they can come in
- * between what would be the non-constant sample rate).
- *
- * So what we do is that we throw away clearly redundant
- * events that are fewer than 61 seconds apart (assuming there
- * is no dive computer with a sample rate of more than 60
- * seconds... that would be pretty pointless to plot the
- * profile with)
- *
- * We first only mark the events for deletion so that we
- * still know when the previous event happened.
- */
-static void fixup_dc_events(struct divecomputer *dc)
-{
- struct event *event;
-
- event = dc->events;
- while (event) {
- struct event *prev;
- if (is_potentially_redundant(event)) {
- prev = find_previous_event(dc, event);
- if (prev && prev->value == event->value &&
- prev->flags == event->flags &&
- event->time.seconds - prev->time.seconds < 61)
- event->deleted = true;
- }
- event = event->next;
- }
- event = dc->events;
- while (event) {
- if (event->next && event->next->deleted) {
- struct event *nextnext = event->next->next;
- free(event->next);
- event->next = nextnext;
- } else {
- event = event->next;
- }
- }
-}
-
-static int interpolate_depth(struct divecomputer *dc, int idx, int lastdepth, int lasttime, int now)
-{
- int i;
- int nextdepth = lastdepth;
- int nexttime = now;
-
- for (i = idx+1; i < dc->samples; i++) {
- struct sample *sample = dc->sample + i;
- if (sample->depth.mm < 0)
- continue;
- nextdepth = sample->depth.mm;
- nexttime = sample->time.seconds;
- break;
- }
- return interpolate(lastdepth, nextdepth, now-lasttime, nexttime-lasttime);
-}
-
-static void fixup_dc_depths(struct dive *dive, struct divecomputer *dc)
-{
- int i;
- int maxdepth = dc->maxdepth.mm;
- int lasttime = 0, lastdepth = 0;
-
- for (i = 0; i < dc->samples; i++) {
- struct sample *sample = dc->sample + i;
- int time = sample->time.seconds;
- int depth = sample->depth.mm;
-
- if (depth < 0) {
- depth = interpolate_depth(dc, i, lastdepth, lasttime, time);
- sample->depth.mm = depth;
- }
-
- if (depth > SURFACE_THRESHOLD) {
- if (depth > maxdepth)
- maxdepth = depth;
- }
-
- lastdepth = depth;
- lasttime = time;
- if (sample->cns > dive->maxcns)
- dive->maxcns = sample->cns;
- }
-
- update_depth(&dc->maxdepth, maxdepth);
- if (maxdepth > dive->maxdepth.mm)
- dive->maxdepth.mm = maxdepth;
-}
-
-static void fixup_dc_temp(struct dive *dive, struct divecomputer *dc)
-{
- int i;
- int mintemp = 0, lasttemp = 0;
-
- for (i = 0; i < dc->samples; i++) {
- struct sample *sample = dc->sample + i;
- int temp = sample->temperature.mkelvin;
-
- if (temp) {
- /*
- * If we have consecutive identical
- * temperature readings, throw away
- * the redundant ones.
- */
- if (lasttemp == temp)
- sample->temperature.mkelvin = 0;
- else
- lasttemp = temp;
-
- if (!mintemp || temp < mintemp)
- mintemp = temp;
- }
-
- update_min_max_temperatures(dive, sample->temperature);
- }
- update_temperature(&dc->watertemp, mintemp);
- update_min_max_temperatures(dive, dc->watertemp);
-}
-
-/*
- * Fix up cylinder sensor information in the samples if we have
- * an explicit first cylinder
- */
-static void fixup_dc_cylinder_index(struct dive *dive, struct divecomputer *dc)
-{
- int i;
- int first_cylinder = explicit_first_cylinder(dive, dc);
-
- if (!first_cylinder)
- return;
-
- for (i = 0; i < dc->samples; i++) {
- struct sample *sample = dc->sample + i;
-
- if (sample->sensor == 0)
- sample->sensor = first_cylinder;
- }
-}
-
-/*
- * Simplify dc pressure information:
- * (a) Remove redundant pressure information
- * (b) Remove linearly interpolated pressure data
- */
-static void simplify_dc_pressures(struct dive *dive, struct divecomputer *dc)
-{
- int i, j;
- int lastindex = -1;
- int lastpressure = 0, lasto2pressure = 0;
- int pressure_delta[MAX_CYLINDERS] = { INT_MAX, };
-
- for (i = 0; i < dc->samples; i++) {
- struct sample *sample = dc->sample + i;
- int pressure = sample->cylinderpressure.mbar;
- int o2_pressure = sample->o2cylinderpressure.mbar;
- int index;
-
- index = sample->sensor;
-
- if (index == lastindex) {
- /* Remove duplicate redundant pressure information */
- if (pressure == lastpressure)
- sample->cylinderpressure.mbar = 0;
- if (o2_pressure == lasto2pressure)
- sample->o2cylinderpressure.mbar = 0;
- /* check for simply linear data in the samples
- +INT_MAX means uninitialized, -INT_MAX means not linear */
- if (pressure_delta[index] != -INT_MAX && lastpressure) {
- if (pressure_delta[index] == INT_MAX) {
- pressure_delta[index] = abs(pressure - lastpressure);
- } else {
- int cur_delta = abs(pressure - lastpressure);
- if (cur_delta && abs(cur_delta - pressure_delta[index]) > 150) {
- /* ok the samples aren't just a linearisation
- * between start and end */
- pressure_delta[index] = -INT_MAX;
- }
- }
- }
- }
- lastindex = index;
- lastpressure = pressure;
- lasto2pressure = o2_pressure;
- }
-
- /* if all the samples for a cylinder have pressure data that
- * is basically equidistant throw out the sample cylinder pressure
- * information but make sure we still have a valid start and end
- * pressure
- * this happens when DivingLog decides to linearalize the
- * pressure between beginning and end and for strange reasons
- * decides to put that in the sample data as if it came from
- * the dive computer; we don't want that (we'll visualize with
- * constant SAC rate instead)
- * WARNING WARNING - I have only seen this in single tank dives
- * --- maybe I should try to create a multi tank dive and see what
- * --- divinglog does there - but the code right now is only tested
- * --- for the single tank case */
- for (j = 0; j < MAX_CYLINDERS; j++) {
- if (abs(pressure_delta[j]) != INT_MAX) {
- cylinder_t *cyl = dive->cylinder + j;
- for (i = 0; i < dc->samples; i++)
- if (dc->sample[i].sensor == j)
- dc->sample[i].cylinderpressure.mbar = 0;
- if (!cyl->start.mbar)
- cyl->start.mbar = cyl->sample_start.mbar;
- if (!cyl->end.mbar)
- cyl->end.mbar = cyl->sample_end.mbar;
- cyl->sample_start.mbar = 0;
- cyl->sample_end.mbar = 0;
- }
- }
-}
-
-/* FIXME! sensor -> cylinder mapping? */
-static void fixup_start_pressure(struct dive *dive, int idx, pressure_t p)
-{
- if (idx >= 0 && idx < MAX_CYLINDERS) {
- cylinder_t *cyl = dive->cylinder + idx;
- if (p.mbar && !cyl->sample_start.mbar)
- cyl->sample_start = p;
- }
-}
-
-static void fixup_end_pressure(struct dive *dive, int idx, pressure_t p)
-{
- if (idx >= 0 && idx < MAX_CYLINDERS) {
- cylinder_t *cyl = dive->cylinder + idx;
- if (p.mbar && !cyl->sample_end.mbar)
- cyl->sample_end = p;
- }
-}
-
-/*
- * Check the cylinder pressure sample information and fill in the
- * overall cylinder pressures from those.
- *
- * We ignore surface samples for tank pressure information.
- *
- * At the beginning of the dive, let the cylinder cool down
- * if the diver starts off at the surface. And at the end
- * of the dive, there may be surface pressures where the
- * diver has already turned off the air supply (especially
- * for computers like the Uemis Zurich that end up saving
- * quite a bit of samples after the dive has ended).
- */
-static void fixup_dive_pressures(struct dive *dive, struct divecomputer *dc)
-{
- int i, o2index = -1;
-
- if (dive->dc.divemode == CCR)
- o2index = get_cylinder_idx_by_use(dive, OXYGEN);
-
- /* Walk the samples from the beginning to find starting pressures.. */
- for (i = 0; i < dc->samples; i++) {
- struct sample *sample = dc->sample + i;
-
- if (sample->depth.mm < SURFACE_THRESHOLD)
- continue;
-
- fixup_start_pressure(dive, sample->sensor, sample->cylinderpressure);
- fixup_start_pressure(dive, o2index, sample->o2cylinderpressure);
- }
-
- /* ..and from the end for ending pressures */
- for (i = dc->samples; --i >= 0; ) {
- struct sample *sample = dc->sample + i;
-
- if (sample->depth.mm < SURFACE_THRESHOLD)
- continue;
-
- fixup_end_pressure(dive, sample->sensor, sample->cylinderpressure);
- fixup_end_pressure(dive, o2index, sample->o2cylinderpressure);
- }
-
- simplify_dc_pressures(dive, dc);
-}
-
-static void fixup_dive_dc(struct dive *dive, struct divecomputer *dc)
-{
- int i;
-
- /* Add device information to table */
- if (dc->deviceid && (dc->serial || dc->fw_version))
- create_device_node(dc->model, dc->deviceid, dc->serial, dc->fw_version, "");
-
- /* Fixup duration and mean depth */
- fixup_dc_duration(dc);
-
- /* Fix up sample depth data */
- fixup_dc_depths(dive, dc);
-
- /* Fix up dive temperatures based on dive computer samples */
- fixup_dc_temp(dive, dc);
-
- /* Fix up cylinder sensor data */
- fixup_dc_cylinder_index(dive, dc);
-
- /* Fix up cylinder pressures based on DC info */
- fixup_dive_pressures(dive, dc);
-
- fixup_dc_events(dc);
-}
-
-struct dive *fixup_dive(struct dive *dive)
-{
- int i;
- struct divecomputer *dc;
-
- sanitize_cylinder_info(dive);
- dive->maxcns = dive->cns;
-
- /*
- * Use the dive's temperatures for minimum and maximum in case
- * we do not have temperatures recorded by DC.
- */
-
- update_min_max_temperatures(dive, dive->watertemp);
-
- for_each_dc (dive, dc)
- fixup_dive_dc(dive, dc);
-
- fixup_water_salinity(dive);
- fixup_surface_pressure(dive);
- fixup_meandepth(dive);
- fixup_duration(dive);
- fixup_watertemp(dive);
- fixup_airtemp(dive);
- fixup_cylinder_use(dive); // store indices for CCR oxygen and diluent cylinders
- for (i = 0; i < MAX_CYLINDERS; i++) {
- cylinder_t *cyl = dive->cylinder + i;
- add_cylinder_description(&cyl->type);
- if (same_rounded_pressure(cyl->sample_start, cyl->start))
- cyl->start.mbar = 0;
- if (same_rounded_pressure(cyl->sample_end, cyl->end))
- cyl->end.mbar = 0;
- }
- update_cylinder_related_info(dive);
- for (i = 0; i < MAX_WEIGHTSYSTEMS; i++) {
- weightsystem_t *ws = dive->weightsystem + i;
- add_weightsystem_description(ws);
- }
- /* we should always have a uniq ID as that gets assigned during alloc_dive(),
- * but we want to make sure... */
- if (!dive->id)
- dive->id = dive_getUniqID(dive);
-
- return dive;
-}
-
-/* Don't pick a zero for MERGE_MIN() */
-#define MERGE_MAX(res, a, b, n) res->n = MAX(a->n, b->n)
-#define MERGE_MIN(res, a, b, n) res->n = (a->n) ? (b->n) ? MIN(a->n, b->n) : (a->n) : (b->n)
-#define MERGE_TXT(res, a, b, n) res->n = merge_text(a->n, b->n)
-#define MERGE_NONZERO(res, a, b, n) res->n = a->n ? a->n : b->n
-
-struct sample *add_sample(struct sample *sample, int time, struct divecomputer *dc)
-{
- struct sample *p = prepare_sample(dc);
-
- if (p) {
- *p = *sample;
- p->time.seconds = time;
- finish_sample(dc);
- }
- return p;
-}
-
-/*
- * This is like add_sample(), but if the distance from the last sample
- * is excessive, we add two surface samples in between.
- *
- * This is so that if you merge two non-overlapping dives, we make sure
- * that the time in between the dives is at the surface, not some "last
- * sample that happened to be at a depth of 1.2m".
- */
-static void merge_one_sample(struct sample *sample, int time, struct divecomputer *dc)
-{
- int last = dc->samples - 1;
- if (last >= 0) {
- static struct sample surface;
- struct sample *prev = dc->sample + last;
- int last_time = prev->time.seconds;
- int last_depth = prev->depth.mm;
-
- /*
- * Only do surface events if the samples are more than
- * a minute apart, and shallower than 5m
- */
- if (time > last_time + 60 && last_depth < 5000) {
- add_sample(&surface, last_time + 20, dc);
- add_sample(&surface, time - 20, dc);
- }
- }
- add_sample(sample, time, dc);
-}
-
-
-/*
- * Merge samples. Dive 'a' is "offset" seconds before Dive 'b'
- */
-static void merge_samples(struct divecomputer *res, struct divecomputer *a, struct divecomputer *b, int offset)
-{
- int asamples = a->samples;
- int bsamples = b->samples;
- struct sample *as = a->sample;
- struct sample *bs = b->sample;
-
- /*
- * We want a positive sample offset, so that sample
- * times are always positive. So if the samples for
- * 'b' are before the samples for 'a' (so the offset
- * is negative), we switch a and b around, and use
- * the reverse offset.
- */
- if (offset < 0) {
- offset = -offset;
- asamples = bsamples;
- bsamples = a->samples;
- as = bs;
- bs = a->sample;
- }
-
- for (;;) {
- int at, bt;
- struct sample sample;
-
- if (!res)
- return;
-
- at = asamples ? as->time.seconds : -1;
- bt = bsamples ? bs->time.seconds + offset : -1;
-
- /* No samples? All done! */
- if (at < 0 && bt < 0)
- return;
-
- /* Only samples from a? */
- if (bt < 0) {
- add_sample_a:
- merge_one_sample(as, at, res);
- as++;
- asamples--;
- continue;
- }
-
- /* Only samples from b? */
- if (at < 0) {
- add_sample_b:
- merge_one_sample(bs, bt, res);
- bs++;
- bsamples--;
- continue;
- }
-
- if (at < bt)
- goto add_sample_a;
- if (at > bt)
- goto add_sample_b;
-
- /* same-time sample: add a merged sample. Take the non-zero ones */
- sample = *bs;
- if (as->depth.mm)
- sample.depth = as->depth;
- if (as->temperature.mkelvin)
- sample.temperature = as->temperature;
- if (as->cylinderpressure.mbar)
- sample.cylinderpressure = as->cylinderpressure;
- if (as->sensor)
- sample.sensor = as->sensor;
- if (as->cns)
- sample.cns = as->cns;
- if (as->setpoint.mbar)
- sample.setpoint = as->setpoint;
- if (as->ndl.seconds)
- sample.ndl = as->ndl;
- if (as->stoptime.seconds)
- sample.stoptime = as->stoptime;
- if (as->stopdepth.mm)
- sample.stopdepth = as->stopdepth;
- if (as->in_deco)
- sample.in_deco = true;
-
- merge_one_sample(&sample, at, res);
-
- as++;
- bs++;
- asamples--;
- bsamples--;
- }
-}
-
-static char *merge_text(const char *a, const char *b)
-{
- char *res;
- if (!a && !b)
- return NULL;
- if (!a || !*a)
- return copy_string(b);
- if (!b || !*b)
- return strdup(a);
- if (!strcmp(a, b))
- return copy_string(a);
- res = malloc(strlen(a) + strlen(b) + 32);
- if (!res)
- return (char *)a;
- sprintf(res, translate("gettextFromC", "(%s) or (%s)"), a, b);
- return res;
-}
-
-#define SORT(a, b, field) \
- if (a->field != b->field) \
- return a->field < b->field ? -1 : 1
-
-static int sort_event(struct event *a, struct event *b)
-{
- SORT(a, b, time.seconds);
- SORT(a, b, type);
- SORT(a, b, flags);
- SORT(a, b, value);
- return strcmp(a->name, b->name);
-}
-
-static void merge_events(struct divecomputer *res, struct divecomputer *src1, struct divecomputer *src2, int offset)
-{
- struct event *a, *b;
- struct event **p = &res->events;
-
- /* Always use positive offsets */
- if (offset < 0) {
- struct divecomputer *tmp;
-
- offset = -offset;
- tmp = src1;
- src1 = src2;
- src2 = tmp;
- }
-
- a = src1->events;
- b = src2->events;
- while (b) {
- b->time.seconds += offset;
- b = b->next;
- }
- b = src2->events;
-
- while (a || b) {
- int s;
- if (!b) {
- *p = a;
- break;
- }
- if (!a) {
- *p = b;
- break;
- }
- s = sort_event(a, b);
- /* Pick b */
- if (s > 0) {
- *p = b;
- p = &b->next;
- b = b->next;
- continue;
- }
- /* Pick 'a' or neither */
- if (s < 0) {
- *p = a;
- p = &a->next;
- }
- a = a->next;
- continue;
- }
-}
-
-/* Pick whichever has any info (if either). Prefer 'a' */
-static void merge_cylinder_type(cylinder_type_t *src, cylinder_type_t *dst)
-{
- if (!dst->size.mliter)
- dst->size.mliter = src->size.mliter;
- if (!dst->workingpressure.mbar)
- dst->workingpressure.mbar = src->workingpressure.mbar;
- if (!dst->description) {
- dst->description = src->description;
- src->description = NULL;
- }
-}
-
-static void merge_cylinder_mix(struct gasmix *src, struct gasmix *dst)
-{
- if (!dst->o2.permille)
- *dst = *src;
-}
-
-static void merge_cylinder_info(cylinder_t *src, cylinder_t *dst)
-{
- merge_cylinder_type(&src->type, &dst->type);
- merge_cylinder_mix(&src->gasmix, &dst->gasmix);
- MERGE_MAX(dst, dst, src, start.mbar);
- MERGE_MIN(dst, dst, src, end.mbar);
-}
-
-static void merge_weightsystem_info(weightsystem_t *res, weightsystem_t *a, weightsystem_t *b)
-{
- if (!a->weight.grams)
- a = b;
- *res = *a;
-}
-
-/* get_cylinder_idx_by_use(): Find the index of the first cylinder with a particular CCR use type.
- * The index returned corresponds to that of the first cylinder with a cylinder_use that
- * equals the appropriate enum value [oxygen, diluent, bailout] given by cylinder_use_type.
- * A negative number returned indicates that a match could not be found.
- * Call parameters: dive = the dive being processed
- * cylinder_use_type = an enum, one of {oxygen, diluent, bailout} */
-extern int get_cylinder_idx_by_use(struct dive *dive, enum cylinderuse cylinder_use_type)
-{
- int cylinder_index;
- for (cylinder_index = 0; cylinder_index < MAX_CYLINDERS; cylinder_index++) {
- if (dive->cylinder[cylinder_index].cylinder_use == cylinder_use_type)
- return cylinder_index; // return the index of the cylinder with that cylinder use type
- }
- return -1; // negative number means cylinder_use_type not found in list of cylinders
-}
-
-int gasmix_distance(const struct gasmix *a, const struct gasmix *b)
-{
- int a_o2 = get_o2(a), b_o2 = get_o2(b);
- int a_he = get_he(a), b_he = get_he(b);
- int delta_o2 = a_o2 - b_o2, delta_he = a_he - b_he;
-
- delta_he = delta_he * delta_he;
- delta_o2 = delta_o2 * delta_o2;
- return delta_he + delta_o2;
-}
-
-/* fill_pressures(): Compute partial gas pressures in bar from gasmix and ambient pressures, possibly for OC or CCR, to be
- * extended to PSCT. This function does the calculations of gas pressures applicable to a single point on the dive profile.
- * The structure "pressures" is used to return calculated gas pressures to the calling software.
- * Call parameters: po2 = po2 value applicable to the record in calling function
- * amb_pressure = ambient pressure applicable to the record in calling function
- * *pressures = structure for communicating o2 sensor values from and gas pressures to the calling function.
- * *mix = structure containing cylinder gas mixture information.
- * This function called by: calculate_gas_information_new() in profile.c; add_segment() in deco.c.
- */
-extern void fill_pressures(struct gas_pressures *pressures, const double amb_pressure, const struct gasmix *mix, double po2, enum dive_comp_type divemode)
-{
- if (po2) { // This is probably a CCR dive where pressures->o2 is defined
- if (po2 >= amb_pressure) {
- pressures->o2 = amb_pressure;
- pressures->n2 = pressures->he = 0.0;
- } else {
- pressures->o2 = po2;
- if (get_o2(mix) == 1000) {
- pressures->he = pressures->n2 = 0;
- } else {
- pressures->he = (amb_pressure - pressures->o2) * (double)get_he(mix) / (1000 - get_o2(mix));
- pressures->n2 = amb_pressure - pressures->o2 - pressures->he;
- }
- }
- } else {
- if (divemode == PSCR) { /* The steady state approximation should be good enough */
- pressures->o2 = get_o2(mix) / 1000.0 * amb_pressure - (1.0 - get_o2(mix) / 1000.0) * prefs.o2consumption / (prefs.bottomsac * prefs.pscr_ratio / 1000.0);
- if (pressures->o2 < 0) // He's dead, Jim.
- pressures->o2 = 0;
- if (get_o2(mix) != 1000) {
- pressures->he = (amb_pressure - pressures->o2) * get_he(mix) / (1000.0 - get_o2(mix));
- pressures->n2 = (amb_pressure - pressures->o2) * (1000 - get_o2(mix) - get_he(mix)) / (1000.0 - get_o2(mix));
- } else {
- pressures->he = pressures->n2 = 0;
- }
- } else {
- // Open circuit dives: no gas pressure values available, they need to be calculated
- pressures->o2 = get_o2(mix) / 1000.0 * amb_pressure; // These calculations are also used if the CCR calculation above..
- pressures->he = get_he(mix) / 1000.0 * amb_pressure; // ..returned a po2 of zero (i.e. o2 sensor data not resolvable)
- pressures->n2 = (1000 - get_o2(mix) - get_he(mix)) / 1000.0 * amb_pressure;
- }
- }
-}
-
-static int find_cylinder_match(cylinder_t *cyl, cylinder_t array[], unsigned int used)
-{
- int i;
- int best = -1, score = INT_MAX;
-
- if (cylinder_nodata(cyl))
- return -1;
- for (i = 0; i < MAX_CYLINDERS; i++) {
- const cylinder_t *match;
- int distance;
-
- if (used & (1 << i))
- continue;
- match = array + i;
- distance = gasmix_distance(&cyl->gasmix, &match->gasmix);
- if (distance >= score)
- continue;
- best = i;
- score = distance;
- }
- return best;
-}
-
-/* Force an initial gaschange event to the (old) gas #0 */
-static void add_initial_gaschange(struct dive *dive, struct divecomputer *dc)
-{
- struct event *ev = get_next_event(dc->events, "gaschange");
-
- if (ev && ev->time.seconds < 30)
- return;
-
- /* Old starting gas mix */
- add_gas_switch_event(dive, dc, 0, 0);
-}
-
-void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, int mapping[])
-{
- int i;
- struct event *ev;
-
- /* Did the first gas get remapped? Add gas switch event */
- if (mapping[0] > 0)
- add_initial_gaschange(dive, dc);
-
- /* Remap the sensor indexes */
- for (i = 0; i < dc->samples; i++) {
- struct sample *s = dc->sample + i;
- int sensor;
-
- if (!s->cylinderpressure.mbar)
- continue;
- sensor = mapping[s->sensor];
- if (sensor >= 0)
- s->sensor = sensor;
- }
-
- /* Remap the gas change indexes */
- for (ev = dc->events; ev; ev = ev->next) {
- if (!event_is_gaschange(ev))
- continue;
- if (ev->gas.index < 0)
- continue;
- ev->gas.index = mapping[ev->gas.index];
- }
-}
-
-/*
- * If the cylinder indexes change (due to merging dives or deleting
- * cylinders in the middle), we need to change the indexes in the
- * dive computer data for this dive.
- *
- * Also note that we assume that the initial cylinder is cylinder 0,
- * so if that got renamed, we need to create a fake gas change event
- */
-static void cylinder_renumber(struct dive *dive, int mapping[])
-{
- struct divecomputer *dc;
- for_each_dc (dive, dc)
- dc_cylinder_renumber(dive, dc, mapping);
-}
-
-/*
- * Merging cylinder information is non-trivial, because the two dive computers
- * may have different ideas of what the different cylinder indexing is.
- *
- * Logic: take all the cylinder information from the preferred dive ('a'), and
- * then try to match each of the cylinders in the other dive by the gasmix that
- * is the best match and hasn't been used yet.
- */
-static void merge_cylinders(struct dive *res, struct dive *a, struct dive *b)
-{
- int i, renumber = 0;
- int mapping[MAX_CYLINDERS];
- unsigned int used = 0;
-
- /* Copy the cylinder info raw from 'a' */
- memcpy(res->cylinder, a->cylinder, sizeof(res->cylinder));
- memset(a->cylinder, 0, sizeof(a->cylinder));
-
- for (i = 0; i < MAX_CYLINDERS; i++) {
- int j;
- cylinder_t *cyl = b->cylinder + i;
-
- j = find_cylinder_match(cyl, res->cylinder, used);
- mapping[i] = j;
- if (j < 0)
- continue;
- used |= 1 << j;
- merge_cylinder_info(cyl, res->cylinder + j);
-
- /* If that renumbered the cylinders, fix it up! */
- if (i != j)
- renumber = 1;
- }
- if (renumber)
- cylinder_renumber(b, mapping);
-}
-
-static void merge_equipment(struct dive *res, struct dive *a, struct dive *b)
-{
- int i;
-
- merge_cylinders(res, a, b);
- for (i = 0; i < MAX_WEIGHTSYSTEMS; i++)
- merge_weightsystem_info(res->weightsystem + i, a->weightsystem + i, b->weightsystem + i);
-}
-
-static void merge_airtemps(struct dive *res, struct dive *a, struct dive *b)
-{
- un_fixup_airtemp(a);
- un_fixup_airtemp(b);
- MERGE_NONZERO(res, a, b, airtemp.mkelvin);
-}
-
-/*
- * When merging two dives, this picks the trip from one, and removes it
- * from the other.
- *
- * The 'next' dive is not involved in the dive merging, but is the dive
- * that will be the next dive after the merged dive.
- */
-static void pick_trip(struct dive *res, struct dive *pick)
-{
- tripflag_t tripflag = pick->tripflag;
- dive_trip_t *trip = pick->divetrip;
-
- res->tripflag = tripflag;
- add_dive_to_trip(res, trip);
-}
-
-/*
- * Pick a trip for a dive
- */
-static void merge_trip(struct dive *res, struct dive *a, struct dive *b)
-{
- dive_trip_t *atrip, *btrip;
-
- /*
- * The larger tripflag is more relevant: we prefer
- * take manually assigned trips over auto-generated
- * ones.
- */
- if (a->tripflag > b->tripflag)
- goto pick_a;
-
- if (a->tripflag < b->tripflag)
- goto pick_b;
-
- /* Otherwise, look at the trip data and pick the "better" one */
- atrip = a->divetrip;
- btrip = b->divetrip;
- if (!atrip)
- goto pick_b;
- if (!btrip)
- goto pick_a;
- if (!atrip->location)
- goto pick_b;
- if (!btrip->location)
- goto pick_a;
- if (!atrip->notes)
- goto pick_b;
- if (!btrip->notes)
- goto pick_a;
-
- /*
- * Ok, so both have location and notes.
- * Pick the earlier one.
- */
- if (a->when < b->when)
- goto pick_a;
- goto pick_b;
-
-pick_a:
- b = a;
-pick_b:
- pick_trip(res, b);
-}
-
-#if CURRENTLY_NOT_USED
-/*
- * Sample 's' is between samples 'a' and 'b'. It is 'offset' seconds before 'b'.
- *
- * If 's' and 'a' are at the same time, offset is 0, and b is NULL.
- */
-static int compare_sample(struct sample *s, struct sample *a, struct sample *b, int offset)
-{
- unsigned int depth = a->depth.mm;
- int diff;
-
- if (offset) {
- unsigned int interval = b->time.seconds - a->time.seconds;
- unsigned int depth_a = a->depth.mm;
- unsigned int depth_b = b->depth.mm;
-
- if (offset > interval)
- return -1;
-
- /* pick the average depth, scaled by the offset from 'b' */
- depth = (depth_a * offset) + (depth_b * (interval - offset));
- depth /= interval;
- }
- diff = s->depth.mm - depth;
- if (diff < 0)
- diff = -diff;
- /* cut off at one meter difference */
- if (diff > 1000)
- diff = 1000;
- return diff * diff;
-}
-
-/*
- * Calculate a "difference" in samples between the two dives, given
- * the offset in seconds between them. Use this to find the best
- * match of samples between two different dive computers.
- */
-static unsigned long sample_difference(struct divecomputer *a, struct divecomputer *b, int offset)
-{
- int asamples = a->samples;
- int bsamples = b->samples;
- struct sample *as = a->sample;
- struct sample *bs = b->sample;
- unsigned long error = 0;
- int start = -1;
-
- if (!asamples || !bsamples)
- return 0;
-
- /*
- * skip the first sample - this way we know can always look at
- * as/bs[-1] to look at the samples around it in the loop.
- */
- as++;
- bs++;
- asamples--;
- bsamples--;
-
- for (;;) {
- int at, bt, diff;
-
-
- /* If we run out of samples, punt */
- if (!asamples)
- return INT_MAX;
- if (!bsamples)
- return INT_MAX;
-
- at = as->time.seconds;
- bt = bs->time.seconds + offset;
-
- /* b hasn't started yet? Ignore it */
- if (bt < 0) {
- bs++;
- bsamples--;
- continue;
- }
-
- if (at < bt) {
- diff = compare_sample(as, bs - 1, bs, bt - at);
- as++;
- asamples--;
- } else if (at > bt) {
- diff = compare_sample(bs, as - 1, as, at - bt);
- bs++;
- bsamples--;
- } else {
- diff = compare_sample(as, bs, NULL, 0);
- as++;
- bs++;
- asamples--;
- bsamples--;
- }
-
- /* Invalid comparison point? */
- if (diff < 0)
- continue;
-
- if (start < 0)
- start = at;
-
- error += diff;
-
- if (at - start > 120)
- break;
- }
- return error;
-}
-
-/*
- * Dive 'a' is 'offset' seconds before dive 'b'
- *
- * This is *not* because the dive computers clocks aren't in sync,
- * it is because the dive computers may "start" the dive at different
- * points in the dive, so the sample at time X in dive 'a' is the
- * same as the sample at time X+offset in dive 'b'.
- *
- * For example, some dive computers take longer to "wake up" when
- * they sense that you are under water (ie Uemis Zurich if it was off
- * when the dive started). And other dive computers have different
- * depths that they activate at, etc etc.
- *
- * If we cannot find a shared offset, don't try to merge.
- */
-static int find_sample_offset(struct divecomputer *a, struct divecomputer *b)
-{
- int offset, best;
- unsigned long max;
-
- /* No samples? Merge at any time (0 offset) */
- if (!a->samples)
- return 0;
- if (!b->samples)
- return 0;
-
- /*
- * Common special-case: merging a dive that came from
- * the same dive computer, so the samples are identical.
- * Check this first, without wasting time trying to find
- * some minimal offset case.
- */
- best = 0;
- max = sample_difference(a, b, 0);
- if (!max)
- return 0;
-
- /*
- * Otherwise, look if we can find anything better within
- * a thirty second window..
- */
- for (offset = -30; offset <= 30; offset++) {
- unsigned long diff;
-
- diff = sample_difference(a, b, offset);
- if (diff > max)
- continue;
- best = offset;
- max = diff;
- }
-
- return best;
-}
-#endif
-
-/*
- * Are a and b "similar" values, when given a reasonable lower end expected
- * difference?
- *
- * So for example, we'd expect different dive computers to give different
- * max depth readings. You might have them on different arms, and they
- * have different pressure sensors and possibly different ideas about
- * water salinity etc.
- *
- * So have an expected minimum difference, but also allow a larger relative
- * error value.
- */
-static int similar(unsigned long a, unsigned long b, unsigned long expected)
-{
- if (!a && !b)
- return 1;
-
- if (a && b) {
- unsigned long min, max, diff;
-
- min = a;
- max = b;
- if (a > b) {
- min = b;
- max = a;
- }
- diff = max - min;
-
- /* Smaller than expected difference? */
- if (diff < expected)
- return 1;
- /* Error less than 10% or the maximum */
- if (diff * 10 < max)
- return 1;
- }
- return 0;
-}
-
-/*
- * Match two dive computer entries against each other, and
- * tell if it's the same dive. Return 0 if "don't know",
- * positive for "same dive" and negative for "definitely
- * not the same dive"
- */
-int match_one_dc(struct divecomputer *a, struct divecomputer *b)
-{
- /* Not same model? Don't know if matching.. */
- if (!a->model || !b->model)
- return 0;
- if (strcasecmp(a->model, b->model))
- return 0;
-
- /* Different device ID's? Don't know */
- if (a->deviceid != b->deviceid)
- return 0;
-
- /* Do we have dive IDs? */
- if (!a->diveid || !b->diveid)
- return 0;
-
- /*
- * If they have different dive ID's on the same
- * dive computer, that's a definite "same or not"
- */
- return a->diveid == b->diveid ? 1 : -1;
-}
-
-/*
- * Match every dive computer against each other to see if
- * we have a matching dive.
- *
- * Return values:
- * -1 for "is definitely *NOT* the same dive"
- * 0 for "don't know"
- * 1 for "is definitely the same dive"
- */
-static int match_dc_dive(struct divecomputer *a, struct divecomputer *b)
-{
- do {
- struct divecomputer *tmp = b;
- do {
- int match = match_one_dc(a, tmp);
- if (match)
- return match;
- tmp = tmp->next;
- } while (tmp);
- a = a->next;
- } while (a);
- return 0;
-}
-
-static bool new_without_trip(struct dive *a)
-{
- return a->downloaded && !a->divetrip;
-}
-
-/*
- * Do we want to automatically try to merge two dives that
- * look like they are the same dive?
- *
- * This happens quite commonly because you download a dive
- * that you already had, or perhaps because you maintained
- * multiple dive logs and want to load them all together
- * (possibly one of them was imported from another dive log
- * application entirely).
- *
- * NOTE! We mainly look at the dive time, but it can differ
- * between two dives due to a few issues:
- *
- * - rounding the dive date to the nearest minute in other dive
- * applications
- *
- * - dive computers with "relative datestamps" (ie the dive
- * computer doesn't actually record an absolute date at all,
- * but instead at download-time synchronizes its internal
- * time with real-time on the downloading computer)
- *
- * - using multiple dive computers with different real time on
- * the same dive
- *
- * We do not merge dives that look radically different, and if
- * the dates are *too* far off the user will have to join two
- * dives together manually. But this tries to handle the sane
- * cases.
- */
-static int likely_same_dive(struct dive *a, struct dive *b)
-{
- int match, fuzz = 20 * 60;
-
- /* don't merge manually added dives with anything */
- if (same_string(a->dc.model, "manually added dive") ||
- same_string(b->dc.model, "manually added dive"))
- return 0;
-
- /* Don't try to merge dives with different trip information */
- if (a->divetrip != b->divetrip) {
- /*
- * Exception: if the dive is downloaded without any
- * explicit trip information, we do want to merge it
- * with existing old dives even if they have trips.
- */
- if (!new_without_trip(a) && !new_without_trip(b))
- return 0;
- }
-
- /*
- * Do some basic sanity testing of the values we
- * have filled in during 'fixup_dive()'
- */
- if (!similar(a->maxdepth.mm, b->maxdepth.mm, 1000) ||
- (a->meandepth.mm && b->meandepth.mm && !similar(a->meandepth.mm, b->meandepth.mm, 1000)) ||
- !similar(a->duration.seconds, b->duration.seconds, 5 * 60))
- return 0;
-
- /* See if we can get an exact match on the dive computer */
- match = match_dc_dive(&a->dc, &b->dc);
- if (match)
- return match > 0;
-
- /*
- * Allow a time difference due to dive computer time
- * setting etc. Check if they overlap.
- */
- fuzz = MAX(a->duration.seconds, b->duration.seconds) / 2;
- if (fuzz < 60)
- fuzz = 60;
-
- return ((a->when <= b->when + fuzz) && (a->when >= b->when - fuzz));
-}
-
-/*
- * This could do a lot more merging. Right now it really only
- * merges almost exact duplicates - something that happens easily
- * with overlapping dive downloads.
- */
-struct dive *try_to_merge(struct dive *a, struct dive *b, bool prefer_downloaded)
-{
- if (likely_same_dive(a, b))
- return merge_dives(a, b, 0, prefer_downloaded);
- return NULL;
-}
-
-void free_events(struct event *ev)
-{
- while (ev) {
- struct event *next = ev->next;
- free(ev);
- ev = next;
- }
-}
-
-static void free_dc(struct divecomputer *dc)
-{
- free(dc->sample);
- free((void *)dc->model);
- free_events(dc->events);
- free(dc);
-}
-
-static void free_pic(struct picture *picture)
-{
- if (picture) {
- free(picture->filename);
- free(picture);
- }
-}
-
-static int same_sample(struct sample *a, struct sample *b)
-{
- if (a->time.seconds != b->time.seconds)
- return 0;
- if (a->depth.mm != b->depth.mm)
- return 0;
- if (a->temperature.mkelvin != b->temperature.mkelvin)
- return 0;
- if (a->cylinderpressure.mbar != b->cylinderpressure.mbar)
- return 0;
- return a->sensor == b->sensor;
-}
-
-static int same_dc(struct divecomputer *a, struct divecomputer *b)
-{
- int i;
- struct event *eva, *evb;
-
- i = match_one_dc(a, b);
- if (i)
- return i > 0;
-
- if (a->when && b->when && a->when != b->when)
- return 0;
- if (a->samples != b->samples)
- return 0;
- for (i = 0; i < a->samples; i++)
- if (!same_sample(a->sample + i, b->sample + i))
- return 0;
- eva = a->events;
- evb = b->events;
- while (eva && evb) {
- if (!same_event(eva, evb))
- return 0;
- eva = eva->next;
- evb = evb->next;
- }
- return eva == evb;
-}
-
-static int might_be_same_device(struct divecomputer *a, struct divecomputer *b)
-{
- /* No dive computer model? That matches anything */
- if (!a->model || !b->model)
- return 1;
-
- /* Otherwise at least the model names have to match */
- if (strcasecmp(a->model, b->model))
- return 0;
-
- /* No device ID? Match */
- if (!a->deviceid || !b->deviceid)
- return 1;
-
- return a->deviceid == b->deviceid;
-}
-
-static void remove_redundant_dc(struct divecomputer *dc, int prefer_downloaded)
-{
- do {
- struct divecomputer **p = &dc->next;
-
- /* Check this dc against all the following ones.. */
- while (*p) {
- struct divecomputer *check = *p;
- if (same_dc(dc, check) || (prefer_downloaded && might_be_same_device(dc, check))) {
- *p = check->next;
- check->next = NULL;
- free_dc(check);
- continue;
- }
- p = &check->next;
- }
-
- /* .. and then continue down the chain, but we */
- prefer_downloaded = 0;
- dc = dc->next;
- } while (dc);
-}
-
-static void clear_dc(struct divecomputer *dc)
-{
- memset(dc, 0, sizeof(*dc));
-}
-
-static struct divecomputer *find_matching_computer(struct divecomputer *match, struct divecomputer *list)
-{
- struct divecomputer *p;
-
- while ((p = list) != NULL) {
- list = list->next;
-
- if (might_be_same_device(match, p))
- break;
- }
- return p;
-}
-
-
-static void copy_dive_computer(struct divecomputer *res, struct divecomputer *a)
-{
- *res = *a;
- res->model = copy_string(a->model);
- res->samples = res->alloc_samples = 0;
- res->sample = NULL;
- res->events = NULL;
- res->next = NULL;
-}
-
-/*
- * Join dive computers with a specific time offset between
- * them.
- *
- * Use the dive computer ID's (or names, if ID's are missing)
- * to match them up. If we find a matching dive computer, we
- * merge them. If not, we just take the data from 'a'.
- */
-static void interleave_dive_computers(struct divecomputer *res,
- struct divecomputer *a, struct divecomputer *b, int offset)
-{
- do {
- struct divecomputer *match;
-
- copy_dive_computer(res, a);
-
- match = find_matching_computer(a, b);
- if (match) {
- merge_events(res, a, match, offset);
- merge_samples(res, a, match, offset);
- /* Use the diveid of the later dive! */
- if (offset > 0)
- res->diveid = match->diveid;
- } else {
- res->sample = a->sample;
- res->samples = a->samples;
- res->events = a->events;
- a->sample = NULL;
- a->samples = 0;
- a->events = NULL;
- }
- a = a->next;
- if (!a)
- break;
- res->next = calloc(1, sizeof(struct divecomputer));
- res = res->next;
- } while (res);
-}
-
-
-/*
- * Join dive computer information.
- *
- * If we have old-style dive computer information (no model
- * name etc), we will prefer a new-style one and just throw
- * away the old. We're assuming it's a re-download.
- *
- * Otherwise, we'll just try to keep all the information,
- * unless the user has specified that they prefer the
- * downloaded computer, in which case we'll aggressively
- * try to throw out old information that *might* be from
- * that one.
- */
-static void join_dive_computers(struct divecomputer *res, struct divecomputer *a, struct divecomputer *b, int prefer_downloaded)
-{
- struct divecomputer *tmp;
-
- if (a->model && !b->model) {
- *res = *a;
- clear_dc(a);
- return;
- }
- if (b->model && !a->model) {
- *res = *b;
- clear_dc(b);
- return;
- }
-
- *res = *a;
- clear_dc(a);
- tmp = res;
- while (tmp->next)
- tmp = tmp->next;
-
- tmp->next = calloc(1, sizeof(*tmp));
- *tmp->next = *b;
- clear_dc(b);
-
- remove_redundant_dc(res, prefer_downloaded);
-}
-
-static bool tag_seen_before(struct tag_entry *start, struct tag_entry *before)
-{
- while (start && start != before) {
- if (same_string(start->tag->name, before->tag->name))
- return true;
- start = start->next;
- }
- return false;
-}
-
-/* remove duplicates and empty nodes */
-void taglist_cleanup(struct tag_entry **tag_list)
-{
- struct tag_entry **tl = tag_list;
- while (*tl) {
- /* skip tags that are empty or that we have seen before */
- if (same_string((*tl)->tag->name, "") || tag_seen_before(*tag_list, *tl)) {
- *tl = (*tl)->next;
- continue;
- }
- tl = &(*tl)->next;
- }
-}
-
-int taglist_get_tagstring(struct tag_entry *tag_list, char *buffer, int len)
-{
- int i = 0;
- struct tag_entry *tmp;
- tmp = tag_list;
- memset(buffer, 0, len);
- while (tmp != NULL) {
- int newlength = strlen(tmp->tag->name);
- if (i > 0)
- newlength += 2;
- if ((i + newlength) < len) {
- if (i > 0) {
- strcpy(buffer + i, ", ");
- strcpy(buffer + i + 2, tmp->tag->name);
- } else {
- strcpy(buffer, tmp->tag->name);
- }
- } else {
- return i;
- }
- i += newlength;
- tmp = tmp->next;
- }
- return i;
-}
-
-static inline void taglist_free_divetag(struct divetag *tag)
-{
- if (tag->name != NULL)
- free(tag->name);
- if (tag->source != NULL)
- free(tag->source);
- free(tag);
-}
-
-/* Add a tag to the tag_list, keep the list sorted */
-static struct divetag *taglist_add_divetag(struct tag_entry **tag_list, struct divetag *tag)
-{
- struct tag_entry *next, *entry;
-
- while ((next = *tag_list) != NULL) {
- int cmp = strcmp(next->tag->name, tag->name);
-
- /* Already have it? */
- if (!cmp)
- return next->tag;
- /* Is the entry larger? If so, insert here */
- if (cmp > 0)
- break;
- /* Continue traversing the list */
- tag_list = &next->next;
- }
-
- /* Insert in front of it */
- entry = malloc(sizeof(struct tag_entry));
- entry->next = next;
- entry->tag = tag;
- *tag_list = entry;
- return tag;
-}
-
-struct divetag *taglist_add_tag(struct tag_entry **tag_list, const char *tag)
-{
- size_t i = 0;
- int is_default_tag = 0;
- struct divetag *ret_tag, *new_tag;
- const char *translation;
- new_tag = malloc(sizeof(struct divetag));
-
- for (i = 0; i < sizeof(default_tags) / sizeof(char *); i++) {
- if (strcmp(default_tags[i], tag) == 0) {
- is_default_tag = 1;
- break;
- }
- }
- /* Only translate default tags */
- if (is_default_tag) {
- translation = translate("gettextFromC", tag);
- new_tag->name = malloc(strlen(translation) + 1);
- memcpy(new_tag->name, translation, strlen(translation) + 1);
- new_tag->source = malloc(strlen(tag) + 1);
- memcpy(new_tag->source, tag, strlen(tag) + 1);
- } else {
- new_tag->source = NULL;
- new_tag->name = malloc(strlen(tag) + 1);
- memcpy(new_tag->name, tag, strlen(tag) + 1);
- }
- /* Try to insert new_tag into g_tag_list if we are not operating on it */
- if (tag_list != &g_tag_list) {
- ret_tag = taglist_add_divetag(&g_tag_list, new_tag);
- /* g_tag_list already contains new_tag, free the duplicate */
- if (ret_tag != new_tag)
- taglist_free_divetag(new_tag);
- ret_tag = taglist_add_divetag(tag_list, ret_tag);
- } else {
- ret_tag = taglist_add_divetag(tag_list, new_tag);
- if (ret_tag != new_tag)
- taglist_free_divetag(new_tag);
- }
- return ret_tag;
-}
-
-void taglist_free(struct tag_entry *entry)
-{
- STRUCTURED_LIST_FREE(struct tag_entry, entry, free)
-}
-
-/* Merge src1 and src2, write to *dst */
-static void taglist_merge(struct tag_entry **dst, struct tag_entry *src1, struct tag_entry *src2)
-{
- struct tag_entry *entry;
-
- for (entry = src1; entry; entry = entry->next)
- taglist_add_divetag(dst, entry->tag);
- for (entry = src2; entry; entry = entry->next)
- taglist_add_divetag(dst, entry->tag);
-}
-
-void taglist_init_global()
-{
- size_t i;
-
- for (i = 0; i < sizeof(default_tags) / sizeof(char *); i++)
- taglist_add_tag(&g_tag_list, default_tags[i]);
-}
-
-bool taglist_contains(struct tag_entry *tag_list, const char *tag)
-{
- while (tag_list) {
- if (same_string(tag_list->tag->name, tag))
- return true;
- tag_list = tag_list->next;
- }
- return false;
-}
-
-// check if all tags in subtl are included in supertl (so subtl is a subset of supertl)
-static bool taglist_contains_all(struct tag_entry *subtl, struct tag_entry *supertl)
-{
- while (subtl) {
- if (!taglist_contains(supertl, subtl->tag->name))
- return false;
- subtl = subtl->next;
- }
- return true;
-}
-
-struct tag_entry *taglist_added(struct tag_entry *original_list, struct tag_entry *new_list)
-{
- struct tag_entry *added_list = NULL;
- while (new_list) {
- if (!taglist_contains(original_list, new_list->tag->name))
- taglist_add_tag(&added_list, new_list->tag->name);
- new_list = new_list->next;
- }
- return added_list;
-}
-
-void dump_taglist(const char *intro, struct tag_entry *tl)
-{
- char *comma = "";
- fprintf(stderr, "%s", intro);
- while(tl) {
- fprintf(stderr, "%s %s", comma, tl->tag->name);
- comma = ",";
- tl = tl->next;
- }
- fprintf(stderr, "\n");
-}
-
-// if tl1 is both a subset and superset of tl2 they must be the same
-bool taglist_equal(struct tag_entry *tl1, struct tag_entry *tl2)
-{
- return taglist_contains_all(tl1, tl2) && taglist_contains_all(tl2, tl1);
-}
-
-// count the dives where the tag list contains the given tag
-int count_dives_with_tag(const char *tag)
-{
- int i, counter = 0;
- struct dive *d;
-
- for_each_dive (i, d) {
- if (same_string(tag, "")) {
- // count dives with no tags
- if (d->tag_list == NULL)
- counter++;
- } else if (taglist_contains(d->tag_list, tag)) {
- counter++;
- }
- }
- return counter;
-}
-
-extern bool string_sequence_contains(const char *string_sequence, const char *text);
-
-// count the dives where the person is included in the comma separated string sequences of buddies or divemasters
-int count_dives_with_person(const char *person)
-{
- int i, counter = 0;
- struct dive *d;
-
- for_each_dive (i, d) {
- if (same_string(person, "")) {
- // solo dive
- if (same_string(d->buddy, "") && same_string(d->divemaster, ""))
- counter++;
- } else if (string_sequence_contains(d->buddy, person) || string_sequence_contains(d->divemaster, person)) {
- counter++;
- }
- }
- return counter;
-}
-
-// count the dives with exactly the location
-int count_dives_with_location(const char *location)
-{
- int i, counter = 0;
- struct dive *d;
-
- for_each_dive (i, d) {
- if (same_string(get_dive_location(d), location))
- counter++;
- }
- return counter;
-}
-
-// count the dives with exactly the suit
-int count_dives_with_suit(const char *suit)
-{
- int i, counter = 0;
- struct dive *d;
-
- for_each_dive (i, d) {
- if (same_string(d->suit, suit))
- counter++;
- }
- return counter;
-}
-
-/*
- * Merging two dives can be subtle, because there's two different ways
- * of merging:
- *
- * (a) two distinctly _different_ dives that have the same dive computer
- * are merged into one longer dive, because the user asked for it
- * in the divelist.
- *
- * Because this case is with the same dive computer, we *know* the
- * two must have a different start time, and "offset" is the relative
- * time difference between the two.
- *
- * (a) two different dive computers that we might want to merge into
- * one single dive with multiple dive computers.
- *
- * This is the "try_to_merge()" case, which will have offset == 0,
- * even if the dive times might be different.
- */
-struct dive *merge_dives(struct dive *a, struct dive *b, int offset, bool prefer_downloaded)
-{
- struct dive *res = alloc_dive();
- struct dive *dl = NULL;
-
- if (offset) {
- /*
- * If "likely_same_dive()" returns true, that means that
- * it is *not* the same dive computer, and we do not want
- * to try to turn it into a single longer dive. So we'd
- * join them as two separate dive computers at zero offset.
- */
- if (likely_same_dive(a, b))
- offset = 0;
- } else {
- /* Aim for newly downloaded dives to be 'b' (keep old dive data first) */
- if (a->downloaded && !b->downloaded) {
- struct dive *tmp = a;
- a = b;
- b = tmp;
- }
- if (prefer_downloaded && b->downloaded)
- dl = b;
- }
-
- res->when = dl ? dl->when : a->when;
- res->selected = a->selected || b->selected;
- merge_trip(res, a, b);
- MERGE_TXT(res, a, b, notes);
- MERGE_TXT(res, a, b, buddy);
- MERGE_TXT(res, a, b, divemaster);
- MERGE_MAX(res, a, b, rating);
- MERGE_TXT(res, a, b, suit);
- MERGE_MAX(res, a, b, number);
- MERGE_NONZERO(res, a, b, cns);
- MERGE_NONZERO(res, a, b, visibility);
- MERGE_NONZERO(res, a, b, picture_list);
- taglist_merge(&res->tag_list, a->tag_list, b->tag_list);
- merge_equipment(res, a, b);
- merge_airtemps(res, a, b);
- if (dl) {
- /* If we prefer downloaded, do those first, and get rid of "might be same" computers */
- join_dive_computers(&res->dc, &dl->dc, &a->dc, 1);
- } else if (offset && might_be_same_device(&a->dc, &b->dc))
- interleave_dive_computers(&res->dc, &a->dc, &b->dc, offset);
- else
- join_dive_computers(&res->dc, &a->dc, &b->dc, 0);
- res->dive_site_uuid = a->dive_site_uuid ?: b->dive_site_uuid;
- fixup_dive(res);
- return res;
-}
-
-// copy_dive(), but retaining the new ID for the copied dive
-static struct dive *create_new_copy(struct dive *from)
-{
- struct dive *to = alloc_dive();
- int id;
-
- // alloc_dive() gave us a new ID, we just need to
- // make sure it's not overwritten.
- id = to->id;
- copy_dive(from, to);
- to->id = id;
- return to;
-}
-
-static void force_fixup_dive(struct dive *d)
-{
- struct divecomputer *dc = &d->dc;
- int old_temp = dc->watertemp.mkelvin;
- int old_mintemp = d->mintemp.mkelvin;
- int old_maxtemp = d->maxtemp.mkelvin;
- duration_t old_duration = d->duration;
-
- d->maxdepth.mm = 0;
- dc->maxdepth.mm = 0;
- d->watertemp.mkelvin = 0;
- dc->watertemp.mkelvin = 0;
- d->duration.seconds = 0;
- d->maxtemp.mkelvin = 0;
- d->mintemp.mkelvin = 0;
-
- fixup_dive(d);
-
- if (!d->watertemp.mkelvin)
- d->watertemp.mkelvin = old_temp;
-
- if (!dc->watertemp.mkelvin)
- dc->watertemp.mkelvin = old_temp;
-
- if (!d->maxtemp.mkelvin)
- d->maxtemp.mkelvin = old_maxtemp;
-
- if (!d->mintemp.mkelvin)
- d->mintemp.mkelvin = old_mintemp;
-
- if (!d->duration.seconds)
- d->duration = old_duration;
-
-}
-
-/*
- * Split a dive that has a surface interval from samples 'a' to 'b'
- * into two dives.
- */
-static int split_dive_at(struct dive *dive, int a, int b)
-{
- int i, nr;
- uint32_t t;
- struct dive *d1, *d2;
- struct divecomputer *dc1, *dc2;
- struct event *event, **evp;
-
- /* if we can't find the dive in the dive list, don't bother */
- if ((nr = get_divenr(dive)) < 0)
- return 0;
-
- /* We're not trying to be efficient here.. */
- d1 = create_new_copy(dive);
- d2 = create_new_copy(dive);
-
- /* now unselect the first first segment so we don't keep all
- * dives selected by mistake. But do keep the second one selected
- * so the algorithm keeps splitting the dive further */
- d1->selected = false;
-
- dc1 = &d1->dc;
- dc2 = &d2->dc;
- /*
- * Cut off the samples of d1 at the beginning
- * of the interval.
- */
- dc1->samples = a;
-
- /* And get rid of the 'b' first samples of d2 */
- dc2->samples -= b;
- memmove(dc2->sample, dc2->sample+b, dc2->samples * sizeof(struct sample));
-
- /*
- * This is where we cut off events from d1,
- * and shift everything in d2
- */
- t = dc2->sample[0].time.seconds;
- d2->when += t;
- for (i = 0; i < dc2->samples; i++)
- dc2->sample[i].time.seconds -= t;
-
- /* Remove the events past 't' from d1 */
- evp = &dc1->events;
- while ((event = *evp) != NULL && event->time.seconds < t)
- evp = &event->next;
- *evp = NULL;
- while (event) {
- struct event *next = event->next;
- free(event);
- event = next;
- }
-
- /* Remove the events before 't' from d2, and shift the rest */
- evp = &dc2->events;
- while ((event = *evp) != NULL) {
- if (event->time.seconds < t) {
- *evp = event->next;
- free(event);
- } else {
- event->time.seconds -= t;
- }
- }
-
- force_fixup_dive(d1);
- force_fixup_dive(d2);
-
- if (dive->divetrip) {
- d1->divetrip = d2->divetrip = 0;
- add_dive_to_trip(d1, dive->divetrip);
- add_dive_to_trip(d2, dive->divetrip);
- }
-
- delete_single_dive(nr);
- add_single_dive(nr, d1);
-
- /*
- * Was the dive numbered? If it was the last dive, then we'll
- * increment the dive number for the tail part that we split off.
- * Otherwise the tail is unnumbered.
- */
- if (d2->number) {
- if (dive_table.nr == nr + 1)
- d2->number++;
- else
- d2->number = 0;
- }
- add_single_dive(nr + 1, d2);
-
- mark_divelist_changed(true);
-
- return 1;
-}
-
-/* in freedive mode we split for as little as 10 seconds on the surface,
- * otherwise we use a minute */
-static bool should_split(struct divecomputer *dc, int t1, int t2)
-{
- int threshold = dc->divemode == FREEDIVE ? 10 : 60;
-
- return t2 - t1 >= threshold;
-}
-
-/*
- * Try to split a dive into multiple dives at a surface interval point.
- *
- * NOTE! We will not split dives with multiple dive computers, and
- * only split when there is at least one surface event that has
- * non-surface events on both sides.
- *
- * In other words, this is a (simplified) reversal of the dive merging.
- */
-int split_dive(struct dive *dive)
-{
- int i;
- int at_surface, surface_start;
- struct divecomputer *dc;
-
- if (!dive || (dc = &dive->dc)->next)
- return 0;
-
- surface_start = 0;
- at_surface = 1;
- for (i = 1; i < dc->samples; i++) {
- struct sample *sample = dc->sample+i;
- int surface_sample = sample->depth.mm < SURFACE_THRESHOLD;
-
- /*
- * We care about the transition from and to depth 0,
- * not about the depth staying similar.
- */
- if (at_surface == surface_sample)
- continue;
- at_surface = surface_sample;
-
- // Did it become surface after having been non-surface? We found the start
- if (at_surface) {
- surface_start = i;
- continue;
- }
-
- // Going down again? We want at least a minute from
- // the surface start.
- if (!surface_start)
- continue;
- if (!should_split(dc, dc->sample[surface_start].time.seconds, sample[i - 1].time.seconds))
- continue;
-
- return split_dive_at(dive, surface_start, i-1);
- }
- return 0;
-}
-
-/*
- * "dc_maxtime()" is how much total time this dive computer
- * has for this dive. Note that it can differ from "duration"
- * if there are surface events in the middle.
- *
- * Still, we do ignore all but the last surface samples from the
- * end, because some divecomputers just generate lots of them.
- */
-static inline int dc_totaltime(const struct divecomputer *dc)
-{
- int time = dc->duration.seconds;
- int nr = dc->samples;
-
- while (nr--) {
- struct sample *s = dc->sample + nr;
- time = s->time.seconds;
- if (s->depth.mm >= SURFACE_THRESHOLD)
- break;
- }
- return time;
-}
-
-/*
- * The end of a dive is actually not trivial, because "duration"
- * is not the duration until the end, but the time we spend under
- * water, which can be very different if there are surface events
- * during the dive.
- *
- * So walk the dive computers, looking for the longest actual
- * time in the samples (and just default to the dive duration if
- * there are no samples).
- */
-static inline int dive_totaltime(const struct dive *dive)
-{
- int time = dive->duration.seconds;
- const struct divecomputer *dc;
-
- for_each_dc(dive, dc) {
- int dc_time = dc_totaltime(dc);
- if (dc_time > time)
- time = dc_time;
- }
- return time;
-}
-
-timestamp_t dive_endtime(const struct dive *dive)
-{
- return dive->when + dive_totaltime(dive);
-}
-
-struct dive *find_dive_including(timestamp_t when)
-{
- int i;
- struct dive *dive;
-
- /* binary search, anyone? Too lazy for now;
- * also we always use the duration from the first divecomputer
- * could this ever be a problem? */
- for_each_dive (i, dive) {
- if (dive->when <= when && when <= dive_endtime(dive))
- return dive;
- }
- return NULL;
-}
-
-bool time_during_dive_with_offset(struct dive *dive, timestamp_t when, timestamp_t offset)
-{
- timestamp_t start = dive->when;
- timestamp_t end = dive_endtime(dive);
- return start - offset <= when && when <= end + offset;
-}
-
-bool dive_within_time_range(struct dive *dive, timestamp_t when, timestamp_t offset)
-{
- timestamp_t start = dive->when;
- timestamp_t end = dive_endtime(dive);
- return when - offset <= start && end <= when + offset;
-}
-
-/* find the n-th dive that is part of a group of dives within the offset around 'when'.
- * How is that for a vague definition of what this function should do... */
-struct dive *find_dive_n_near(timestamp_t when, int n, timestamp_t offset)
-{
- int i, j = 0;
- struct dive *dive;
-
- for_each_dive (i, dive) {
- if (dive_within_time_range(dive, when, offset))
- if (++j == n)
- return dive;
- }
- return NULL;
-}
-
-void shift_times(const timestamp_t amount)
-{
- int i;
- struct dive *dive;
-
- for_each_dive (i, dive) {
- if (!dive->selected)
- continue;
- dive->when += amount;
- invalidate_dive_cache(dive);
- }
-}
-
-timestamp_t get_times()
-{
- int i;
- struct dive *dive;
-
- for_each_dive (i, dive) {
- if (dive->selected)
- break;
- }
- return dive->when;
-}
-
-void set_save_userid_local(short value)
-{
- prefs.save_userid_local = value;
-}
-
-void set_userid(char *rUserId)
-{
- if (prefs.userid)
- free(prefs.userid);
- prefs.userid = strdup(rUserId);
- if (strlen(prefs.userid) > 30)
- prefs.userid[30]='\0';
-}
-
-/* this sets a usually unused copy of the preferences with the units
- * that were active the last time the dive list was saved to git storage
- * (this isn't used in XML files); storing the unit preferences in the
- * data file is usually pointless (that's a setting of the software,
- * not a property of the data), but it's a great hint of what the user
- * might expect to see when creating a backend service that visualizes
- * the dive list without Subsurface running - so this is basically a
- * functionality for the core library that Subsurface itself doesn't
- * use but that another consumer of the library (like an HTML exporter)
- * will need */
-void set_informational_units(char *units)
-{
- if (strstr(units, "METRIC")) {
- informational_prefs.unit_system = METRIC;
- } else if (strstr(units, "IMPERIAL")) {
- informational_prefs.unit_system = IMPERIAL;
- } else if (strstr(units, "PERSONALIZE")) {
- informational_prefs.unit_system = PERSONALIZE;
- if (strstr(units, "METERS"))
- informational_prefs.units.length = METERS;
- if (strstr(units, "FEET"))
- informational_prefs.units.length = FEET;
- if (strstr(units, "LITER"))
- informational_prefs.units.volume = LITER;
- if (strstr(units, "CUFT"))
- informational_prefs.units.volume = CUFT;
- if (strstr(units, "BAR"))
- informational_prefs.units.pressure = BAR;
- if (strstr(units, "PSI"))
- informational_prefs.units.pressure = PSI;
- if (strstr(units, "PASCAL"))
- informational_prefs.units.pressure = PASCAL;
- if (strstr(units, "CELSIUS"))
- informational_prefs.units.temperature = CELSIUS;
- if (strstr(units, "FAHRENHEIT"))
- informational_prefs.units.temperature = FAHRENHEIT;
- if (strstr(units, "KG"))
- informational_prefs.units.weight = KG;
- if (strstr(units, "LBS"))
- informational_prefs.units.weight = LBS;
- if (strstr(units, "SECONDS"))
- informational_prefs.units.vertical_speed_time = SECONDS;
- if (strstr(units, "MINUTES"))
- informational_prefs.units.vertical_speed_time = MINUTES;
- }
-}
-
-void average_max_depth(struct diveplan *dive, int *avg_depth, int *max_depth)
-{
- int integral = 0;
- int last_time = 0;
- int last_depth = 0;
- struct divedatapoint *dp = dive->dp;
-
- *max_depth = 0;
-
- while (dp) {
- if (dp->time) {
- /* Ignore gas indication samples */
- integral += (dp->depth + last_depth) * (dp->time - last_time) / 2;
- last_time = dp->time;
- last_depth = dp->depth;
- if (dp->depth > *max_depth)
- *max_depth = dp->depth;
- }
- dp = dp->next;
- }
- if (last_time)
- *avg_depth = integral / last_time;
- else
- *avg_depth = *max_depth = 0;
-}
-
-struct picture *alloc_picture()
-{
- struct picture *pic = malloc(sizeof(struct picture));
- if (!pic)
- exit(1);
- memset(pic, 0, sizeof(struct picture));
- return pic;
-}
-
-static bool new_picture_for_dive(struct dive *d, char *filename)
-{
- FOR_EACH_PICTURE (d) {
- if (same_string(picture->filename, filename))
- return false;
- }
- return true;
-}
-
-// only add pictures that have timestamps between 30 minutes before the dive and
-// 30 minutes after the dive ends
-#define D30MIN (30 * 60)
-bool dive_check_picture_time(struct dive *d, int shift_time, timestamp_t timestamp)
-{
- offset_t offset;
- if (timestamp) {
- offset.seconds = timestamp - d->when + shift_time;
- if (offset.seconds > -D30MIN && offset.seconds < dive_totaltime(d) + D30MIN) {
- // this picture belongs to this dive
- return true;
- }
- }
- return false;
-}
-
-bool picture_check_valid(char *filename, int shift_time)
-{
- int i;
- struct dive *dive;
-
- timestamp_t timestamp = picture_get_timestamp(filename);
- for_each_dive (i, dive)
- if (dive->selected && dive_check_picture_time(dive, shift_time, timestamp))
- return true;
- return false;
-}
-
-void dive_create_picture(struct dive *dive, char *filename, int shift_time, bool match_all)
-{
- timestamp_t timestamp = picture_get_timestamp(filename);
- if (!new_picture_for_dive(dive, filename))
- return;
- if (!match_all && !dive_check_picture_time(dive, shift_time, timestamp))
- return;
-
- struct picture *picture = alloc_picture();
- picture->filename = strdup(filename);
- picture->offset.seconds = timestamp - dive->when + shift_time;
- picture_load_exif_data(picture);
-
- dive_add_picture(dive, picture);
- dive_set_geodata_from_picture(dive, picture);
- invalidate_dive_cache(dive);
-}
-
-void dive_add_picture(struct dive *dive, struct picture *newpic)
-{
- struct picture **pic_ptr = &dive->picture_list;
- /* let's keep the list sorted by time */
- while (*pic_ptr && (*pic_ptr)->offset.seconds <= newpic->offset.seconds)
- pic_ptr = &(*pic_ptr)->next;
- newpic->next = *pic_ptr;
- *pic_ptr = newpic;
- cache_picture(newpic);
- return;
-}
-
-unsigned int dive_get_picture_count(struct dive *dive)
-{
- unsigned int i = 0;
- FOR_EACH_PICTURE (dive)
- i++;
- return i;
-}
-
-void dive_set_geodata_from_picture(struct dive *dive, struct picture *picture)
-{
- struct dive_site *ds = get_dive_site_by_uuid(dive->dive_site_uuid);
- if (!dive_site_has_gps_location(ds) && (picture->latitude.udeg || picture->longitude.udeg)) {
- if (ds) {
- ds->latitude = picture->latitude;
- ds->longitude = picture->longitude;
- } else {
- dive->dive_site_uuid = create_dive_site_with_gps("", picture->latitude, picture->longitude, dive->when);
- invalidate_dive_cache(dive);
- }
- }
-}
-
-void picture_free(struct picture *picture)
-{
- if (!picture)
- return;
- free(picture->filename);
- free(picture->hash);
- free(picture);
-}
-
-// When handling pictures in different threads, we need to copy them so we don't
-// run into problems when the main thread frees the picture.
-
-struct picture *clone_picture(struct picture *src)
-{
- struct picture *dst;
-
- dst = alloc_picture();
- copy_pl(src, dst);
- return dst;
-}
-
-void dive_remove_picture(char *filename)
-{
- struct picture **picture = &current_dive->picture_list;
- while (picture && !same_string((*picture)->filename, filename))
- picture = &(*picture)->next;
- if (picture) {
- struct picture *temp = (*picture)->next;
- picture_free(*picture);
- *picture = temp;
- invalidate_dive_cache(current_dive);
- }
-}
-
-/* this always acts on the current divecomputer of the current dive */
-void make_first_dc()
-{
- struct divecomputer *dc = &current_dive->dc;
- struct divecomputer *newdc = malloc(sizeof(*newdc));
- struct divecomputer *cur_dc = current_dc; /* needs to be in a local variable so the macro isn't re-executed */
-
- /* skip the current DC in the linked list */
- while (dc && dc->next != cur_dc)
- dc = dc->next;
- if (!dc) {
- free(newdc);
- fprintf(stderr, "data inconsistent: can't find the current DC");
- return;
- }
- dc->next = cur_dc->next;
- *newdc = current_dive->dc;
- current_dive->dc = *cur_dc;
- current_dive->dc.next = newdc;
- free(cur_dc);
- invalidate_dive_cache(current_dive);
-}
-
-/* always acts on the current dive */
-unsigned int count_divecomputers(void)
-{
- int ret = 1;
- struct divecomputer *dc = current_dive->dc.next;
- while (dc) {
- ret++;
- dc = dc->next;
- }
- return ret;
-}
-
-/* always acts on the current dive */
-void delete_current_divecomputer(void)
-{
- struct divecomputer *dc = current_dc;
-
- if (dc == &current_dive->dc) {
- /* remove the first one, so copy the second one in place of the first and free the second one
- * be careful about freeing the no longer needed structures - since we copy things around we can't use free_dc()*/
- struct divecomputer *fdc = dc->next;
- free(dc->sample);
- free((void *)dc->model);
- free_events(dc->events);
- memcpy(dc, fdc, sizeof(struct divecomputer));
- free(fdc);
- } else {
- struct divecomputer *pdc = &current_dive->dc;
- while (pdc->next != dc && pdc->next)
- pdc = pdc->next;
- if (pdc->next == dc) {
- pdc->next = dc->next;
- free_dc(dc);
- }
- }
- if (dc_number == count_divecomputers())
- dc_number--;
- invalidate_dive_cache(current_dive);
-}
-
-/* helper function to make it easier to work with our structures
- * we don't interpolate here, just use the value from the last sample up to that time */
-int get_depth_at_time(struct divecomputer *dc, unsigned int time)
-{
- int depth = 0;
- if (dc && dc->sample)
- for (int i = 0; i < dc->samples; i++) {
- if (dc->sample[i].time.seconds > time)
- break;
- depth = dc->sample[i].depth.mm;
- }
- return depth;
-}
diff --git a/subsurface-core/dive.h b/subsurface-core/dive.h
deleted file mode 100644
index ae24b7409..000000000
--- a/subsurface-core/dive.h
+++ /dev/null
@@ -1,913 +0,0 @@
-#ifndef DIVE_H
-#define DIVE_H
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <time.h>
-#include <math.h>
-#include <zip.h>
-#include <sqlite3.h>
-#include <string.h>
-#include "divesite.h"
-
-/* Windows has no MIN/MAX macros - so let's just roll our own */
-#define MIN(x, y) ({ \
- __typeof__(x) _min1 = (x); \
- __typeof__(y) _min2 = (y); \
- (void) (&_min1 == &_min2); \
- _min1 < _min2 ? _min1 : _min2; })
-
-#define MAX(x, y) ({ \
- __typeof__(x) _max1 = (x); \
- __typeof__(y) _max2 = (y); \
- (void) (&_max1 == &_max2); \
- _max1 > _max2 ? _max1 : _max2; })
-
-#define IS_FP_SAME(_a, _b) (fabs((_a) - (_b)) <= 0.000001 * MAX(fabs(_a), fabs(_b)))
-
-static inline int same_string(const char *a, const char *b)
-{
- return !strcmp(a ?: "", b ?: "");
-}
-
-static inline char *copy_string(const char *s)
-{
- return (s && *s) ? strdup(s) : NULL;
-}
-
-#include <libxml/tree.h>
-#include <libxslt/transform.h>
-#include <libxslt/xsltutils.h>
-
-#include "sha1.h"
-#include "units.h"
-
-#ifdef __cplusplus
-extern "C" {
-#else
-#include <stdbool.h>
-#endif
-
-extern int last_xml_version;
-
-enum dive_comp_type {OC, CCR, PSCR, FREEDIVE, NUM_DC_TYPE}; // Flags (Open-circuit and Closed-circuit-rebreather) for setting dive computer type
-enum cylinderuse {OC_GAS, DILUENT, OXYGEN, NUM_GAS_USE}; // The different uses for cylinders
-
-extern const char *cylinderuse_text[];
-extern const char *divemode_text[];
-
-struct gasmix {
- fraction_t o2;
- fraction_t he;
-};
-
-typedef struct
-{
- volume_t size;
- pressure_t workingpressure;
- const char *description; /* "LP85", "AL72", "AL80", "HP100+" or whatever */
-} cylinder_type_t;
-
-typedef struct
-{
- cylinder_type_t type;
- struct gasmix gasmix;
- pressure_t start, end, sample_start, sample_end;
- depth_t depth;
- bool manually_added;
- volume_t gas_used;
- volume_t deco_gas_used;
- enum cylinderuse cylinder_use;
-} cylinder_t;
-
-typedef struct
-{
- weight_t weight;
- const char *description; /* "integrated", "belt", "ankle" */
-} weightsystem_t;
-
-/*
- * Events are currently based straight on what libdivecomputer gives us.
- * We need to wrap these into our own events at some point to remove some of the limitations.
- */
-struct event {
- struct event *next;
- duration_t time;
- int type;
- /* This is the annoying libdivecomputer format. */
- int flags, value;
- /* .. and this is our "extended" data for some event types */
- union {
- /*
- * Currently only for gas switch events.
- *
- * NOTE! The index may be -1, which means "unknown". In that
- * case, the get_cylinder_index() function will give the best
- * match with the cylinders in the dive based on gasmix.
- */
- struct {
- int index;
- struct gasmix mix;
- } gas;
- };
- bool deleted;
- char name[];
-};
-
-extern int event_is_gaschange(struct event *ev);
-extern int event_gasmix_redundant(struct event *ev);
-
-extern int get_pressure_units(int mb, const char **units);
-extern double get_depth_units(int mm, int *frac, const char **units);
-extern double get_volume_units(unsigned int ml, int *frac, const char **units);
-extern double get_temp_units(unsigned int mk, const char **units);
-extern double get_weight_units(unsigned int grams, int *frac, const char **units);
-extern double get_vertical_speed_units(unsigned int mms, int *frac, const char **units);
-
-extern unsigned int units_to_depth(double depth);
-extern int units_to_sac(double volume);
-
-/* Volume in mliter of a cylinder at pressure 'p' */
-extern int gas_volume(cylinder_t *cyl, pressure_t p);
-extern double gas_compressibility_factor(struct gasmix *gas, double bar);
-
-
-static inline int get_o2(const struct gasmix *mix)
-{
- return mix->o2.permille ?: O2_IN_AIR;
-}
-
-static inline int get_he(const struct gasmix *mix)
-{
- return mix->he.permille;
-}
-
-struct gas_pressures {
- double o2, n2, he;
-};
-
-extern void fill_pressures(struct gas_pressures *pressures, const double amb_pressure, const struct gasmix *mix, double po2, enum dive_comp_type dctype);
-
-extern void sanitize_gasmix(struct gasmix *mix);
-extern int gasmix_distance(const struct gasmix *a, const struct gasmix *b);
-extern struct gasmix *get_gasmix_from_event(struct event *ev);
-
-static inline bool gasmix_is_air(const struct gasmix *gasmix)
-{
- int o2 = gasmix->o2.permille;
- int he = gasmix->he.permille;
- return (he == 0) && (o2 == 0 || ((o2 >= O2_IN_AIR - 1) && (o2 <= O2_IN_AIR + 1)));
-}
-
-/* Linear interpolation between 'a' and 'b', when we are 'part'way into the 'whole' distance from a to b */
-static inline int interpolate(int a, int b, int part, int whole)
-{
- /* It is doubtful that we actually need floating point for this, but whatever */
- double x = (double)a * (whole - part) + (double)b * part;
- return rint(x / whole);
-}
-
-void get_gas_string(const struct gasmix *gasmix, char *text, int len);
-const char *gasname(const struct gasmix *gasmix);
-
-struct sample // BASE TYPE BYTES UNITS RANGE DESCRIPTION
-{ // --------- ----- ----- ----- -----------
- duration_t time; // uint32_t 4 seconds (0-68 yrs) elapsed dive time up to this sample
- duration_t stoptime; // uint32_t 4 seconds (0-18 h) time duration of next deco stop
- duration_t ndl; // uint32_t 4 seconds (0-18 h) time duration before no-deco limit
- duration_t tts; // uint32_t 4 seconds (0-18 h) time duration to reach the surface
- duration_t rbt; // uint32_t 4 seconds (0-18 h) remaining bottom time
- depth_t depth; // int32_t 4 mm (0-2000 km) dive depth of this sample
- depth_t stopdepth; // int32_t 4 mm (0-2000 km) depth of next deco stop
- temperature_t temperature; // int32_t 4 mdegrK (0-2 MdegK) ambient temperature
- pressure_t cylinderpressure; // int32_t 4 mbar (0-2 Mbar) main cylinder pressure
- pressure_t o2cylinderpressure; // int32_t 4 mbar (0-2 Mbar) CCR o2 cylinder pressure (rebreather)
- o2pressure_t setpoint; // uint16_t 2 mbar (0-65 bar) O2 partial pressure (will be setpoint)
- o2pressure_t o2sensor[3]; // uint16_t 6 mbar (0-65 bar) Up to 3 PO2 sensor values (rebreather)
- bearing_t bearing; // int16_t 2 degrees (-32k to 32k deg) compass bearing
- uint8_t sensor; // uint8_t 1 sensorID (0-255) ID of cylinder pressure sensor
- uint8_t cns; // uint8_t 1 % (0-255 %) cns% accumulated
- uint8_t heartbeat; // uint8_t 1 beats/m (0-255) heart rate measurement
- volume_t sac; // 4 ml/min predefined SAC
- bool in_deco; // bool 1 y/n y/n this sample is part of deco
- bool manually_entered; // bool 1 y/n y/n this sample was entered by the user,
- // not calculated when planning a dive
-}; // Total size of structure: 57 bytes, excluding padding at end
-
-struct divetag {
- /*
- * The name of the divetag. If a translation is available, name contains
- * the translated tag
- */
- char *name;
- /*
- * If a translation is available, we write the original tag to source.
- * This enables us to write a non-localized tag to the xml file.
- */
- char *source;
-};
-
-struct tag_entry {
- struct divetag *tag;
- struct tag_entry *next;
-};
-
-/*
- * divetags are only stored once, each dive only contains
- * a list of tag_entries which then point to the divetags
- * in the global g_tag_list
- */
-
-extern struct tag_entry *g_tag_list;
-
-struct divetag *taglist_add_tag(struct tag_entry **tag_list, const char *tag);
-struct tag_entry *taglist_added(struct tag_entry *original_list, struct tag_entry *new_list);
-void dump_taglist(const char *intro, struct tag_entry *tl);
-
-/*
- * Writes all divetags in tag_list to buffer, limited by the buffer's (len)gth.
- * Returns the characters written
- */
-int taglist_get_tagstring(struct tag_entry *tag_list, char *buffer, int len);
-
-/* cleans up a list: removes empty tags and duplicates */
-void taglist_cleanup(struct tag_entry **tag_list);
-
-void taglist_init_global();
-void taglist_free(struct tag_entry *tag_list);
-
-bool taglist_contains(struct tag_entry *tag_list, const char *tag);
-bool taglist_equal(struct tag_entry *tl1, struct tag_entry *tl2);
-int count_dives_with_tag(const char *tag);
-int count_dives_with_person(const char *person);
-int count_dives_with_location(const char *location);
-int count_dives_with_suit(const char *suit);
-
-struct extra_data {
- const char *key;
- const char *value;
- struct extra_data *next;
-};
-
-/*
- * NOTE! The deviceid and diveid are model-specific *hashes* of
- * whatever device identification that model may have. Different
- * dive computers will have different identifying data, it could
- * be a firmware number or a serial ID (in either string or in
- * numeric format), and we do not care.
- *
- * The only thing we care about is that subsurface will hash
- * that information the same way. So then you can check the ID
- * of a dive computer by comparing the hashes for equality.
- *
- * A deviceid or diveid of zero is assumed to be "no ID".
- */
-struct divecomputer {
- timestamp_t when;
- duration_t duration, surfacetime;
- depth_t maxdepth, meandepth;
- temperature_t airtemp, watertemp;
- pressure_t surface_pressure;
- enum dive_comp_type divemode; // dive computer type: OC(default) or CCR
- uint8_t no_o2sensors; // rebreathers: number of O2 sensors used
- int salinity; // kg per 10000 l
- const char *model, *serial, *fw_version;
- uint32_t deviceid, diveid;
- int samples, alloc_samples;
- struct sample *sample;
- struct event *events;
- struct extra_data *extra_data;
- struct divecomputer *next;
-};
-
-#define MAX_CYLINDERS (8)
-#define MAX_WEIGHTSYSTEMS (6)
-#define W_IDX_PRIMARY 0
-#define W_IDX_SECONDARY 1
-
-typedef enum {
- TF_NONE,
- NO_TRIP,
- IN_TRIP,
- ASSIGNED_TRIP,
- NUM_TRIPFLAGS
-} tripflag_t;
-
-typedef struct dive_trip
-{
- timestamp_t when;
- char *location;
- char *notes;
- struct dive *dives;
- int nrdives;
- int index;
- unsigned expanded : 1, selected : 1, autogen : 1, fixup : 1;
- struct dive_trip *next;
-} dive_trip_t;
-
-/* List of dive trips (sorted by date) */
-extern dive_trip_t *dive_trip_list;
-struct picture;
-struct dive {
- int number;
- tripflag_t tripflag;
- dive_trip_t *divetrip;
- struct dive *next, **pprev;
- bool selected;
- bool hidden_by_filter;
- bool downloaded;
- timestamp_t when;
- uint32_t dive_site_uuid;
- char *notes;
- char *divemaster, *buddy;
- int rating;
- int visibility; /* 0 - 5 star rating */
- cylinder_t cylinder[MAX_CYLINDERS];
- weightsystem_t weightsystem[MAX_WEIGHTSYSTEMS];
- char *suit;
- int sac, otu, cns, maxcns;
-
- /* Calculated based on dive computer data */
- temperature_t mintemp, maxtemp, watertemp, airtemp;
- depth_t maxdepth, meandepth;
- pressure_t surface_pressure;
- duration_t duration;
- int salinity; // kg per 10000 l
-
- struct tag_entry *tag_list;
- struct divecomputer dc;
- int id; // unique ID for this dive
- struct picture *picture_list;
- int oxygen_cylinder_index, diluent_cylinder_index; // CCR dive cylinder indices
- unsigned char git_id[20];
-};
-
-static inline void invalidate_dive_cache(struct dive *dive)
-{
- memset(dive->git_id, 0, 20);
-}
-
-static inline bool dive_cache_is_valid(const struct dive *dive)
-{
- static const unsigned char null_id[20] = { 0, };
- return !!memcmp(dive->git_id, null_id, 20);
-}
-
-extern int get_cylinder_idx_by_use(struct dive *dive, enum cylinderuse cylinder_use_type);
-extern void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, int mapping[]);
-
-/* when selectively copying dive information, which parts should be copied? */
-struct dive_components {
- unsigned int divesite : 1;
- unsigned int notes : 1;
- unsigned int divemaster : 1;
- unsigned int buddy : 1;
- unsigned int suit : 1;
- unsigned int rating : 1;
- unsigned int visibility : 1;
- unsigned int tags : 1;
- unsigned int cylinders : 1;
- unsigned int weights : 1;
-};
-
-/* picture list and methods related to dive picture handling */
-struct picture {
- char *filename;
- char *hash;
- offset_t offset;
- degrees_t latitude;
- degrees_t longitude;
- struct picture *next;
-};
-
-#define FOR_EACH_PICTURE(_dive) \
- if (_dive) \
- for (struct picture *picture = (_dive)->picture_list; picture; picture = picture->next)
-
-#define FOR_EACH_PICTURE_NON_PTR(_divestruct) \
- for (struct picture *picture = (_divestruct).picture_list; picture; picture = picture->next)
-
-extern struct picture *alloc_picture();
-extern struct picture *clone_picture(struct picture *src);
-extern bool dive_check_picture_time(struct dive *d, int shift_time, timestamp_t timestamp);
-extern void dive_create_picture(struct dive *d, char *filename, int shift_time, bool match_all);
-extern void dive_add_picture(struct dive *d, struct picture *newpic);
-extern void dive_remove_picture(char *filename);
-extern unsigned int dive_get_picture_count(struct dive *d);
-extern bool picture_check_valid(char *filename, int shift_time);
-extern void picture_load_exif_data(struct picture *p);
-extern timestamp_t picture_get_timestamp(char *filename);
-extern void dive_set_geodata_from_picture(struct dive *d, struct picture *pic);
-extern void picture_free(struct picture *picture);
-
-extern int explicit_first_cylinder(struct dive *dive, struct divecomputer *dc);
-extern int get_depth_at_time(struct divecomputer *dc, unsigned int time);
-
-static inline int get_surface_pressure_in_mbar(const struct dive *dive, bool non_null)
-{
- int mbar = dive->surface_pressure.mbar;
- if (!mbar && non_null)
- mbar = SURFACE_PRESSURE;
- return mbar;
-}
-
-/* Pa = N/m^2 - so we determine the weight (in N) of the mass of 10m
- * of water (and use standard salt water at 1.03kg per liter if we don't know salinity)
- * and add that to the surface pressure (or to 1013 if that's unknown) */
-static inline int calculate_depth_to_mbar(int depth, pressure_t surface_pressure, int salinity)
-{
- double specific_weight;
- int mbar = surface_pressure.mbar;
-
- if (!mbar)
- mbar = SURFACE_PRESSURE;
- if (!salinity)
- salinity = SEAWATER_SALINITY;
- if (salinity < 500)
- salinity += FRESHWATER_SALINITY;
- specific_weight = salinity / 10000.0 * 0.981;
- mbar += rint(depth / 10.0 * specific_weight);
- return mbar;
-}
-
-static inline int depth_to_mbar(int depth, struct dive *dive)
-{
- return calculate_depth_to_mbar(depth, dive->surface_pressure, dive->salinity);
-}
-
-static inline double depth_to_bar(int depth, struct dive *dive)
-{
- return depth_to_mbar(depth, dive) / 1000.0;
-}
-
-static inline double depth_to_atm(int depth, struct dive *dive)
-{
- return mbar_to_atm(depth_to_mbar(depth, dive));
-}
-
-/* for the inverse calculation we use just the relative pressure
- * (that's the one that some dive computers like the Uemis Zurich
- * provide - for the other models that do this libdivecomputer has to
- * take care of this, but the Uemis we support natively */
-static inline int rel_mbar_to_depth(int mbar, struct dive *dive)
-{
- int cm;
- double specific_weight = 1.03 * 0.981;
- if (dive->dc.salinity)
- specific_weight = dive->dc.salinity / 10000.0 * 0.981;
- /* whole mbar gives us cm precision */
- cm = rint(mbar / specific_weight);
- return cm * 10;
-}
-
-static inline int mbar_to_depth(int mbar, struct dive *dive)
-{
- pressure_t surface_pressure;
- if (dive->surface_pressure.mbar)
- surface_pressure = dive->surface_pressure;
- else
- surface_pressure.mbar = SURFACE_PRESSURE;
- return rel_mbar_to_depth(mbar - surface_pressure.mbar, dive);
-}
-
-/* MOD rounded to multiples of roundto mm */
-static inline depth_t gas_mod(struct gasmix *mix, pressure_t po2_limit, struct dive *dive, int roundto) {
- depth_t rounded_depth;
-
- double depth = (double) mbar_to_depth(po2_limit.mbar * 1000 / get_o2(mix), dive);
- rounded_depth.mm = rint(depth / roundto) * roundto;
- return rounded_depth;
-}
-
-#define SURFACE_THRESHOLD 750 /* somewhat arbitrary: only below 75cm is it really diving */
-
-/* this is a global spot for a temporary dive structure that we use to
- * be able to edit a dive without unintended side effects */
-extern struct dive edit_dive;
-
-extern short autogroup;
-/* random threashold: three days without diving -> new trip
- * this works very well for people who usually dive as part of a trip and don't
- * regularly dive at a local facility; this is why trips are an optional feature */
-#define TRIP_THRESHOLD 3600 * 24 * 3
-
-#define UNGROUPED_DIVE(_dive) ((_dive)->tripflag == NO_TRIP)
-#define DIVE_IN_TRIP(_dive) ((_dive)->tripflag == IN_TRIP || (_dive)->tripflag == ASSIGNED_TRIP)
-#define DIVE_NEEDS_TRIP(_dive) ((_dive)->tripflag == TF_NONE)
-
-extern void add_dive_to_trip(struct dive *, dive_trip_t *);
-
-extern void delete_single_dive(int idx);
-extern void add_single_dive(int idx, struct dive *dive);
-
-extern void insert_trip(dive_trip_t **trip);
-
-
-extern const struct units SI_units, IMPERIAL_units;
-extern struct units xml_parsing_units;
-
-extern struct units *get_units(void);
-extern int run_survey, verbose, quit, force_root;
-
-struct dive_table {
- int nr, allocated, preexisting;
- struct dive **dives;
-};
-
-extern struct dive_table dive_table, downloadTable;
-extern struct dive displayed_dive;
-extern struct dive_site displayed_dive_site;
-extern int selected_dive;
-extern unsigned int dc_number;
-#define current_dive (get_dive(selected_dive))
-#define current_dc (get_dive_dc(current_dive, dc_number))
-
-static inline struct dive *get_dive(int nr)
-{
- if (nr >= dive_table.nr || nr < 0)
- return NULL;
- return dive_table.dives[nr];
-}
-
-static inline struct dive *get_dive_from_table(int nr, struct dive_table *dt)
-{
- if (nr >= dt->nr || nr < 0)
- return NULL;
- return dt->dives[nr];
-}
-
-static inline struct dive_site *get_dive_site_for_dive(struct dive *dive)
-{
- if (dive)
- return get_dive_site_by_uuid(dive->dive_site_uuid);
- return NULL;
-}
-
-static inline char *get_dive_location(struct dive *dive)
-{
- struct dive_site *ds = get_dive_site_by_uuid(dive->dive_site_uuid);
- if (ds && ds->name)
- return ds->name;
- return NULL;
-}
-
-static inline unsigned int number_of_computers(struct dive *dive)
-{
- unsigned int total_number = 0;
- struct divecomputer *dc = &dive->dc;
-
- if (!dive)
- return 1;
-
- do {
- total_number++;
- dc = dc->next;
- } while (dc);
- return total_number;
-}
-
-static inline struct divecomputer *get_dive_dc(struct dive *dive, int nr)
-{
- struct divecomputer *dc = &dive->dc;
-
- while (nr-- > 0) {
- dc = dc->next;
- if (!dc)
- return &dive->dc;
- }
- return dc;
-}
-
-extern timestamp_t dive_endtime(const struct dive *dive);
-
-extern void make_first_dc(void);
-extern unsigned int count_divecomputers(void);
-extern void delete_current_divecomputer(void);
-
-/*
- * Iterate over each dive, with the first parameter being the index
- * iterator variable, and the second one being the dive one.
- *
- * I don't think anybody really wants the index, and we could make
- * it local to the for-loop, but that would make us requires C99.
- */
-#define for_each_dive(_i, _x) \
- for ((_i) = 0; ((_x) = get_dive(_i)) != NULL; (_i)++)
-
-#define for_each_dc(_dive, _dc) \
- for (_dc = &_dive->dc; _dc; _dc = _dc->next)
-
-#define for_each_gps_location(_i, _x) \
- for ((_i) = 0; ((_x) = get_gps_location(_i, &gps_location_table)) != NULL; (_i)++)
-
-static inline struct dive *get_dive_by_uniq_id(int id)
-{
- int i;
- struct dive *dive = NULL;
-
- for_each_dive (i, dive) {
- if (dive->id == id)
- break;
- }
-#ifdef DEBUG
- if (dive == NULL) {
- fprintf(stderr, "Invalid id %x passed to get_dive_by_diveid, try to fix the code\n", id);
- exit(1);
- }
-#endif
- return dive;
-}
-
-static inline int get_idx_by_uniq_id(int id)
-{
- int i;
- struct dive *dive = NULL;
-
- for_each_dive (i, dive) {
- if (dive->id == id)
- break;
- }
-#ifdef DEBUG
- if (dive == NULL) {
- fprintf(stderr, "Invalid id %x passed to get_dive_by_diveid, try to fix the code\n", id);
- exit(1);
- }
-#endif
- return i;
-}
-
-static inline bool dive_site_has_gps_location(struct dive_site *ds)
-{
- return ds && (ds->latitude.udeg || ds->longitude.udeg);
-}
-
-static inline int dive_has_gps_location(struct dive *dive)
-{
- if (!dive)
- return false;
- return dive_site_has_gps_location(get_dive_site_by_uuid(dive->dive_site_uuid));
-}
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern int report_error(const char *fmt, ...);
-extern void report_message(const char *msg);
-extern const char *get_error_string(void);
-
-extern struct dive *find_dive_including(timestamp_t when);
-extern bool dive_within_time_range(struct dive *dive, timestamp_t when, timestamp_t offset);
-extern bool time_during_dive_with_offset(struct dive *dive, timestamp_t when, timestamp_t offset);
-struct dive *find_dive_n_near(timestamp_t when, int n, timestamp_t offset);
-
-/* Check if two dive computer entries are the exact same dive (-1=no/0=maybe/1=yes) */
-extern int match_one_dc(struct divecomputer *a, struct divecomputer *b);
-
-extern void parse_xml_init(void);
-extern int parse_xml_buffer(const char *url, const char *buf, int size, struct dive_table *table, const char **params);
-extern void parse_xml_exit(void);
-extern void set_filename(const char *filename, bool force);
-
-extern int parse_dm4_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct dive_table *table);
-extern int parse_dm5_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct dive_table *table);
-extern int parse_shearwater_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct dive_table *table);
-extern int parse_cobalt_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct dive_table *table);
-extern int parse_divinglog_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct dive_table *table);
-extern int parse_dlf_buffer(unsigned char *buffer, size_t size);
-
-extern int check_git_sha(const char *filename);
-extern int parse_file(const char *filename);
-extern int parse_csv_file(const char *filename, char **params, int pnr, const char *csvtemplate);
-extern int parse_seabear_csv_file(const char *filename, char **params, int pnr, const char *csvtemplate);
-extern int parse_txt_file(const char *filename, const char *csv);
-extern int parse_manual_file(const char *filename, char **params, int pnr);
-extern int save_dives(const char *filename);
-extern int save_dives_logic(const char *filename, bool select_only);
-extern int save_dive(FILE *f, struct dive *dive);
-extern int export_dives_xslt(const char *filename, const bool selected, const int units, const char *export_xslt);
-
-struct membuffer;
-extern void save_one_dive_to_mb(struct membuffer *b, struct dive *dive);
-
-int cylinderuse_from_text(const char *text);
-
-
-struct user_info {
- const char *name;
- const char *email;
-};
-
-extern void subsurface_user_info(struct user_info *);
-extern int subsurface_rename(const char *path, const char *newpath);
-extern int subsurface_dir_rename(const char *path, const char *newpath);
-extern int subsurface_open(const char *path, int oflags, mode_t mode);
-extern FILE *subsurface_fopen(const char *path, const char *mode);
-extern void *subsurface_opendir(const char *path);
-extern int subsurface_access(const char *path, int mode);
-extern struct zip *subsurface_zip_open_readonly(const char *path, int flags, int *errorp);
-extern int subsurface_zip_close(struct zip *zip);
-extern void subsurface_console_init(bool dedicated);
-extern void subsurface_console_exit(void);
-extern bool subsurface_user_is_root(void);
-
-extern void shift_times(const timestamp_t amount);
-extern timestamp_t get_times();
-
-extern xsltStylesheetPtr get_stylesheet(const char *name);
-
-extern timestamp_t utc_mktime(struct tm *tm);
-extern void utc_mkdate(timestamp_t, struct tm *tm);
-
-extern struct dive *alloc_dive(void);
-extern void record_dive_to_table(struct dive *dive, struct dive_table *table);
-extern void record_dive(struct dive *dive);
-extern void clear_dive(struct dive *dive);
-extern void copy_dive(struct dive *s, struct dive *d);
-extern void selective_copy_dive(struct dive *s, struct dive *d, struct dive_components what, bool clear);
-extern struct dive *clone_dive(struct dive *s);
-
-extern void clear_table(struct dive_table *table);
-
-extern struct sample *prepare_sample(struct divecomputer *dc);
-extern void finish_sample(struct divecomputer *dc);
-
-extern bool has_hr_data(struct divecomputer *dc);
-
-extern void sort_table(struct dive_table *table);
-extern struct dive *fixup_dive(struct dive *dive);
-extern void fixup_dc_duration(struct divecomputer *dc);
-extern int dive_getUniqID(struct dive *d);
-extern unsigned int dc_airtemp(struct divecomputer *dc);
-extern unsigned int dc_watertemp(struct divecomputer *dc);
-extern int split_dive(struct dive *);
-extern struct dive *merge_dives(struct dive *a, struct dive *b, int offset, bool prefer_downloaded);
-extern struct dive *try_to_merge(struct dive *a, struct dive *b, bool prefer_downloaded);
-extern struct event *clone_event(const struct event *src_ev);
-extern void copy_events(struct divecomputer *s, struct divecomputer *d);
-extern void free_events(struct event *ev);
-extern void copy_cylinders(struct dive *s, struct dive *d, bool used_only);
-extern void copy_samples(struct divecomputer *s, struct divecomputer *d);
-extern bool is_cylinder_used(struct dive *dive, int idx);
-extern void fill_default_cylinder(cylinder_t *cyl);
-extern void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int time, int idx);
-extern struct event *add_event(struct divecomputer *dc, unsigned int time, int type, int flags, int value, const char *name);
-extern void remove_event(struct event *event);
-extern void update_event_name(struct dive *d, struct event* event, char *name);
-extern void add_extra_data(struct divecomputer *dc, const char *key, const char *value);
-extern void per_cylinder_mean_depth(struct dive *dive, struct divecomputer *dc, int *mean, int *duration);
-extern int get_cylinder_index(struct dive *dive, struct event *ev);
-extern int nr_cylinders(struct dive *dive);
-extern int nr_weightsystems(struct dive *dive);
-
-/* UI related protopypes */
-
-// extern void report_error(GError* error);
-
-extern void add_cylinder_description(cylinder_type_t *);
-extern void add_weightsystem_description(weightsystem_t *);
-extern void remember_event(const char *eventname);
-extern void invalidate_dive_cache(struct dive *dc);
-
-#if WE_DONT_USE_THIS /* this is a missing feature in Qt - selecting which events to display */
-extern int evn_foreach(void (*callback)(const char *, bool *, void *), void *data);
-#endif /* WE_DONT_USE_THIS */
-
-extern void clear_events(void);
-
-extern void set_dc_nickname(struct dive *dive);
-extern void set_autogroup(bool value);
-extern int total_weight(struct dive *);
-
-#ifdef __cplusplus
-}
-#endif
-
-#define DIVE_ERROR_PARSE 1
-#define DIVE_ERROR_PLAN 2
-
-const char *weekday(int wday);
-const char *monthname(int mon);
-
-#define UTF8_DEGREE "\xc2\xb0"
-#define UTF8_DELTA "\xce\x94"
-#define UTF8_UPWARDS_ARROW "\xE2\x86\x91"
-#define UTF8_DOWNWARDS_ARROW "\xE2\x86\x93"
-#define UTF8_AVERAGE "\xc3\xb8"
-#define UTF8_SUBSCRIPT_2 "\xe2\x82\x82"
-#define UTF8_WHITESTAR "\xe2\x98\x86"
-#define UTF8_BLACKSTAR "\xe2\x98\x85"
-
-extern const char *existing_filename;
-extern void subsurface_command_line_init(int *, char ***);
-extern void subsurface_command_line_exit(int *, char ***);
-
-#define FRACTION(n, x) ((unsigned)(n) / (x)), ((unsigned)(n) % (x))
-
-extern void add_segment(double pressure, const struct gasmix *gasmix, int period_in_seconds, int setpoint, const struct dive *dive, int sac);
-extern void clear_deco(double surface_pressure);
-extern void dump_tissues(void);
-extern void set_gf(short gflow, short gfhigh, bool gf_low_at_maxdepth);
-extern void cache_deco_state(char **datap);
-extern void restore_deco_state(char *data);
-extern void nuclear_regeneration(double time);
-extern void vpmb_start_gradient();
-extern void vpmb_next_gradient(double deco_time, double surface_pressure);
-extern double tissue_tolerance_calc(const struct dive *dive, double pressure);
-
-/* this should be converted to use our types */
-struct divedatapoint {
- int time;
- int depth;
- struct gasmix gasmix;
- int setpoint;
- bool entered;
- struct divedatapoint *next;
-};
-
-struct diveplan {
- timestamp_t when;
- int surface_pressure; /* mbar */
- int bottomsac; /* ml/min */
- int decosac; /* ml/min */
- int salinity;
- short gflow;
- short gfhigh;
- struct divedatapoint *dp;
-};
-
-struct divedatapoint *plan_add_segment(struct diveplan *diveplan, int duration, int depth, struct gasmix gasmix, int po2, bool entered);
-struct divedatapoint *create_dp(int time_incr, int depth, struct gasmix gasmix, int po2);
-#if DEBUG_PLAN
-void dump_plan(struct diveplan *diveplan);
-#endif
-bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool show_disclaimer);
-void calc_crushing_pressure(double pressure);
-void vpmb_start_gradient();
-
-void delete_single_dive(int idx);
-
-struct event *get_next_event(struct event *event, const char *name);
-
-
-/* these structs holds the information that
- * describes the cylinders / weight systems.
- * they are global variables initialized in equipment.c
- * used to fill the combobox in the add/edit cylinder
- * dialog
- */
-
-struct tank_info_t {
- const char *name;
- int cuft, ml, psi, bar;
-};
-extern struct tank_info_t tank_info[100];
-
-struct ws_info_t {
- const char *name;
- int grams;
-};
-extern struct ws_info_t ws_info[100];
-
-extern bool cylinder_nodata(cylinder_t *cyl);
-extern bool cylinder_none(void *_data);
-extern bool weightsystem_none(void *_data);
-extern bool no_weightsystems(weightsystem_t *ws);
-extern bool weightsystems_equal(weightsystem_t *ws1, weightsystem_t *ws2);
-extern void remove_cylinder(struct dive *dive, int idx);
-extern void remove_weightsystem(struct dive *dive, int idx);
-extern void reset_cylinders(struct dive *dive, bool track_gas);
-
-/*
- * String handling.
- */
-#define STRTOD_NO_SIGN 0x01
-#define STRTOD_NO_DOT 0x02
-#define STRTOD_NO_COMMA 0x04
-#define STRTOD_NO_EXPONENT 0x08
-extern double strtod_flags(const char *str, const char **ptr, unsigned int flags);
-
-#define STRTOD_ASCII (STRTOD_NO_COMMA)
-
-#define ascii_strtod(str, ptr) strtod_flags(str, ptr, STRTOD_ASCII)
-
-extern void set_save_userid_local(short value);
-extern void set_userid(char *user_id);
-extern void set_informational_units(char *units);
-
-extern const char *get_dive_date_c_string(timestamp_t when);
-extern void update_setpoint_events(struct divecomputer *dc);
-#ifdef __cplusplus
-}
-#endif
-
-extern weight_t string_to_weight(const char *str);
-extern depth_t string_to_depth(const char *str);
-extern pressure_t string_to_pressure(const char *str);
-extern volume_t string_to_volume(const char *str, pressure_t workp);
-extern fraction_t string_to_fraction(const char *str);
-extern void average_max_depth(struct diveplan *dive, int *avg_depth, int *max_depth);
-
-#include "pref.h"
-
-#endif // DIVE_H
diff --git a/subsurface-core/divecomputer.cpp b/subsurface-core/divecomputer.cpp
deleted file mode 100644
index e4081e1cd..000000000
--- a/subsurface-core/divecomputer.cpp
+++ /dev/null
@@ -1,228 +0,0 @@
-#include "divecomputer.h"
-#include "dive.h"
-
-#include <QSettings>
-
-const char *default_dive_computer_vendor;
-const char *default_dive_computer_product;
-const char *default_dive_computer_device;
-int default_dive_computer_download_mode;
-DiveComputerList dcList;
-
-DiveComputerList::DiveComputerList()
-{
-}
-
-DiveComputerList::~DiveComputerList()
-{
-}
-
-bool DiveComputerNode::operator==(const DiveComputerNode &a) const
-{
- return model == a.model &&
- deviceId == a.deviceId &&
- firmware == a.firmware &&
- serialNumber == a.serialNumber &&
- nickName == a.nickName;
-}
-
-bool DiveComputerNode::operator!=(const DiveComputerNode &a) const
-{
- return !(*this == a);
-}
-
-bool DiveComputerNode::changesValues(const DiveComputerNode &b) const
-{
- if (model != b.model || deviceId != b.deviceId) {
- qDebug("DiveComputerNodes were not for the same DC");
- return false;
- }
- return (firmware != b.firmware) ||
- (serialNumber != b.serialNumber) ||
- (nickName != b.nickName);
-}
-
-const DiveComputerNode *DiveComputerList::getExact(const QString &m, uint32_t d)
-{
- for (QMap<QString, DiveComputerNode>::iterator it = dcMap.find(m); it != dcMap.end() && it.key() == m; ++it)
- if (it->deviceId == d)
- return &*it;
- return NULL;
-}
-
-const DiveComputerNode *DiveComputerList::get(const QString &m)
-{
- QMap<QString, DiveComputerNode>::iterator it = dcMap.find(m);
- if (it != dcMap.end())
- return &*it;
- return NULL;
-}
-
-void DiveComputerNode::showchanges(const QString &n, const QString &s, const QString &f) const
-{
- if (nickName != n)
- qDebug("new nickname %s for DC model %s deviceId 0x%x", n.toUtf8().data(), model.toUtf8().data(), deviceId);
- if (serialNumber != s)
- qDebug("new serial number %s for DC model %s deviceId 0x%x", s.toUtf8().data(), model.toUtf8().data(), deviceId);
- if (firmware != f)
- qDebug("new firmware version %s for DC model %s deviceId 0x%x", f.toUtf8().data(), model.toUtf8().data(), deviceId);
-}
-
-void DiveComputerList::addDC(QString m, uint32_t d, QString n, QString s, QString f)
-{
- if (m.isEmpty() || d == 0)
- return;
- const DiveComputerNode *existNode = this->getExact(m, d);
-
- if (existNode) {
- // Update any non-existent fields from the old entry
- if (n.isEmpty())
- n = existNode->nickName;
- if (s.isEmpty())
- s = existNode->serialNumber;
- if (f.isEmpty())
- f = existNode->firmware;
-
- // Do all the old values match?
- if (n == existNode->nickName && s == existNode->serialNumber && f == existNode->firmware)
- return;
-
- // debugging: show changes
- if (verbose)
- existNode->showchanges(n, s, f);
- dcMap.remove(m, *existNode);
- }
-
- DiveComputerNode newNode(m, d, s, f, n);
- dcMap.insert(m, newNode);
-}
-
-extern "C" void create_device_node(const char *model, uint32_t deviceid, const char *serial, const char *firmware, const char *nickname)
-{
- dcList.addDC(model, deviceid, nickname, serial, firmware);
-}
-
-extern "C" bool compareDC(const DiveComputerNode &a, const DiveComputerNode &b)
-{
- return a.deviceId < b.deviceId;
-}
-
-extern "C" void call_for_each_dc (void *f, void (*callback)(void *, const char *, uint32_t,
- const char *, const char *, const char *),
- bool select_only)
-{
- QList<DiveComputerNode> values = dcList.dcMap.values();
- qSort(values.begin(), values.end(), compareDC);
- for (int i = 0; i < values.size(); i++) {
- const DiveComputerNode *node = &values.at(i);
- bool found = false;
- if (select_only) {
- int j;
- struct dive *d;
- for_each_dive (j, d) {
- struct divecomputer *dc;
- if (!d->selected)
- continue;
- for_each_dc(d, dc) {
- if (dc->deviceid == node->deviceId) {
- found = true;
- break;
- }
- }
- if (found)
- break;
- }
- } else {
- found = true;
- }
- if (found)
- callback(f, node->model.toUtf8().data(), node->deviceId, node->nickName.toUtf8().data(),
- node->serialNumber.toUtf8().data(), node->firmware.toUtf8().data());
- }
-}
-
-
-extern "C" int is_default_dive_computer(const char *vendor, const char *product)
-{
- return default_dive_computer_vendor && !strcmp(vendor, default_dive_computer_vendor) &&
- default_dive_computer_product && !strcmp(product, default_dive_computer_product);
-}
-
-extern "C" int is_default_dive_computer_device(const char *name)
-{
- return default_dive_computer_device && !strcmp(name, default_dive_computer_device);
-}
-
-void set_default_dive_computer(const char *vendor, const char *product)
-{
- QSettings s;
-
- if (!vendor || !*vendor)
- return;
- if (!product || !*product)
- return;
- if (is_default_dive_computer(vendor, product))
- return;
-
- free((void *)default_dive_computer_vendor);
- free((void *)default_dive_computer_product);
- default_dive_computer_vendor = strdup(vendor);
- default_dive_computer_product = strdup(product);
- s.beginGroup("DiveComputer");
- s.setValue("dive_computer_vendor", vendor);
- s.setValue("dive_computer_product", product);
- s.endGroup();
-}
-
-void set_default_dive_computer_device(const char *name)
-{
- QSettings s;
-
- if (!name || !*name)
- return;
- if (is_default_dive_computer_device(name))
- return;
-
- free((void *)default_dive_computer_device);
- default_dive_computer_device = strdup(name);
- s.beginGroup("DiveComputer");
- s.setValue("dive_computer_device", name);
- s.endGroup();
-}
-
-void set_default_dive_computer_download_mode(int download_mode)
-{
- QSettings s;
-
- default_dive_computer_download_mode = download_mode;
- s.beginGroup("DiveComputer");
- s.setValue("dive_computer_download_mode", download_mode);
- s.endGroup();
-}
-
-extern "C" void set_dc_nickname(struct dive *dive)
-{
- if (!dive)
- return;
-
- struct divecomputer *dc;
-
- for_each_dc (dive, dc) {
- if (dc->model && *dc->model && dc->deviceid &&
- !dcList.getExact(dc->model, dc->deviceid)) {
- // we don't have this one, yet
- const DiveComputerNode *existNode = dcList.get(dc->model);
- if (existNode) {
- // we already have this model but a different deviceid
- QString simpleNick(dc->model);
- if (dc->deviceid == 0)
- simpleNick.append(" (unknown deviceid)");
- else
- simpleNick.append(" (").append(QString::number(dc->deviceid, 16)).append(")");
- dcList.addDC(dc->model, dc->deviceid, simpleNick);
- } else {
- dcList.addDC(dc->model, dc->deviceid);
- }
- }
- }
-}
diff --git a/subsurface-core/divecomputer.h b/subsurface-core/divecomputer.h
deleted file mode 100644
index 98d12ab8b..000000000
--- a/subsurface-core/divecomputer.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef DIVECOMPUTER_H
-#define DIVECOMPUTER_H
-
-#include <QString>
-#include <QMap>
-#include <stdint.h>
-
-class DiveComputerNode {
-public:
- DiveComputerNode(QString m, uint32_t d, QString s, QString f, QString n)
- : model(m), deviceId(d), serialNumber(s), firmware(f), nickName(n) {};
- bool operator==(const DiveComputerNode &a) const;
- bool operator!=(const DiveComputerNode &a) const;
- bool changesValues(const DiveComputerNode &b) const;
- void showchanges(const QString &n, const QString &s, const QString &f) const;
- QString model;
- uint32_t deviceId;
- QString serialNumber;
- QString firmware;
- QString nickName;
-};
-
-class DiveComputerList {
-public:
- DiveComputerList();
- ~DiveComputerList();
- const DiveComputerNode *getExact(const QString &m, uint32_t d);
- const DiveComputerNode *get(const QString &m);
- void addDC(QString m, uint32_t d, QString n = QString(), QString s = QString(), QString f = QString());
- DiveComputerNode matchDC(const QString &m, uint32_t d);
- DiveComputerNode matchModel(const QString &m);
- QMultiMap<QString, DiveComputerNode> dcMap;
- QMultiMap<QString, DiveComputerNode> dcWorkingMap;
-};
-
-extern DiveComputerList dcList;
-
-#endif
diff --git a/subsurface-core/divelist.c b/subsurface-core/divelist.c
deleted file mode 100644
index 543d9e17b..000000000
--- a/subsurface-core/divelist.c
+++ /dev/null
@@ -1,1207 +0,0 @@
-/* divelist.c */
-/* core logic for the dive list -
- * accessed through the following interfaces:
- *
- * dive_trip_t *dive_trip_list;
- * unsigned int amount_selected;
- * void dump_selection(void)
- * dive_trip_t *find_trip_by_idx(int idx)
- * int trip_has_selected_dives(dive_trip_t *trip)
- * void get_dive_gas(struct dive *dive, int *o2_p, int *he_p, int *o2low_p)
- * int total_weight(struct dive *dive)
- * int get_divenr(struct dive *dive)
- * double init_decompression(struct dive *dive)
- * void update_cylinder_related_info(struct dive *dive)
- * void dump_trip_list(void)
- * dive_trip_t *find_matching_trip(timestamp_t when)
- * void insert_trip(dive_trip_t **dive_trip_p)
- * void remove_dive_from_trip(struct dive *dive)
- * void add_dive_to_trip(struct dive *dive, dive_trip_t *trip)
- * dive_trip_t *create_and_hookup_trip_from_dive(struct dive *dive)
- * void autogroup_dives(void)
- * void delete_single_dive(int idx)
- * void add_single_dive(int idx, struct dive *dive)
- * void merge_two_dives(struct dive *a, struct dive *b)
- * void select_dive(int idx)
- * void deselect_dive(int idx)
- * void mark_divelist_changed(int changed)
- * int unsaved_changes()
- * void remove_autogen_trips()
- */
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <math.h>
-#include "gettext.h"
-#include <assert.h>
-#include <zip.h>
-#include <libxslt/transform.h>
-
-#include "dive.h"
-#include "divelist.h"
-#include "display.h"
-#include "planner.h"
-#include "qthelperfromc.h"
-#include "git-access.h"
-
-static short dive_list_changed = false;
-
-short autogroup = false;
-
-dive_trip_t *dive_trip_list;
-
-unsigned int amount_selected;
-
-#if DEBUG_SELECTION_TRACKING
-void dump_selection(void)
-{
- int i;
- struct dive *dive;
-
- printf("currently selected are %u dives:", amount_selected);
- for_each_dive(i, dive) {
- if (dive->selected)
- printf(" %d", i);
- }
- printf("\n");
-}
-#endif
-
-void set_autogroup(bool value)
-{
- /* if we keep the UI paradigm, this needs to toggle
- * the checkbox on the autogroup menu item */
- autogroup = value;
-}
-
-dive_trip_t *find_trip_by_idx(int idx)
-{
- dive_trip_t *trip = dive_trip_list;
-
- if (idx >= 0)
- return NULL;
- idx = -idx;
- while (trip) {
- if (trip->index == idx)
- return trip;
- trip = trip->next;
- }
- return NULL;
-}
-
-int trip_has_selected_dives(dive_trip_t *trip)
-{
- struct dive *dive;
- for (dive = trip->dives; dive; dive = dive->next) {
- if (dive->selected)
- return 1;
- }
- return 0;
-}
-
-/*
- * Get "maximal" dive gas for a dive.
- * Rules:
- * - Trimix trumps nitrox (highest He wins, O2 breaks ties)
- * - Nitrox trumps air (even if hypoxic)
- * These are the same rules as the inter-dive sorting rules.
- */
-void get_dive_gas(struct dive *dive, int *o2_p, int *he_p, int *o2max_p)
-{
- int i;
- int maxo2 = -1, maxhe = -1, mino2 = 1000;
-
-
- for (i = 0; i < MAX_CYLINDERS; i++) {
- cylinder_t *cyl = dive->cylinder + i;
- int o2 = get_o2(&cyl->gasmix);
- int he = get_he(&cyl->gasmix);
-
- if (!is_cylinder_used(dive, i))
- continue;
- if (cylinder_none(cyl))
- continue;
- if (o2 > maxo2)
- maxo2 = o2;
- if (he > maxhe)
- goto newmax;
- if (he < maxhe)
- continue;
- if (o2 <= maxo2)
- continue;
- newmax:
- maxhe = he;
- mino2 = o2;
- }
- /* All air? Show/sort as "air"/zero */
- if ((!maxhe && maxo2 == O2_IN_AIR && mino2 == maxo2) ||
- (maxo2 == -1 && maxhe == -1 && mino2 == 1000))
- maxo2 = mino2 = 0;
- *o2_p = mino2;
- *he_p = maxhe;
- *o2max_p = maxo2;
-}
-
-int total_weight(struct dive *dive)
-{
- int i, total_grams = 0;
-
- if (dive)
- for (i = 0; i < MAX_WEIGHTSYSTEMS; i++)
- total_grams += dive->weightsystem[i].weight.grams;
- return total_grams;
-}
-
-static int active_o2(struct dive *dive, struct divecomputer *dc, duration_t time)
-{
- struct gasmix gas;
- get_gas_at_time(dive, dc, time, &gas);
- return get_o2(&gas);
-}
-
-/* calculate OTU for a dive - this only takes the first divecomputer into account */
-static int calculate_otu(struct dive *dive)
-{
- int i;
- double otu = 0.0;
- struct divecomputer *dc = &dive->dc;
-
- for (i = 1; i < dc->samples; i++) {
- int t;
- int po2;
- struct sample *sample = dc->sample + i;
- struct sample *psample = sample - 1;
- t = sample->time.seconds - psample->time.seconds;
- if (sample->setpoint.mbar) {
- po2 = sample->setpoint.mbar;
- } else {
- int o2 = active_o2(dive, dc, sample->time);
- po2 = o2 * depth_to_atm(sample->depth.mm, dive);
- }
- if (po2 >= 500)
- otu += pow((po2 - 500) / 1000.0, 0.83) * t / 30.0;
- }
- return rint(otu);
-}
-/* calculate CNS for a dive - this only takes the first divecomputer into account */
-int const cns_table[][3] = {
- /* po2, Maximum Single Exposure, Maximum 24 hour Exposure */
- { 1600, 45 * 60, 150 * 60 },
- { 1500, 120 * 60, 180 * 60 },
- { 1400, 150 * 60, 180 * 60 },
- { 1300, 180 * 60, 210 * 60 },
- { 1200, 210 * 60, 240 * 60 },
- { 1100, 240 * 60, 270 * 60 },
- { 1000, 300 * 60, 300 * 60 },
- { 900, 360 * 60, 360 * 60 },
- { 800, 450 * 60, 450 * 60 },
- { 700, 570 * 60, 570 * 60 },
- { 600, 720 * 60, 720 * 60 }
-};
-
-/* this only gets called if dive->maxcns == 0 which means we know that
- * none of the divecomputers has tracked any CNS for us
- * so we calculated it "by hand" */
-static int calculate_cns(struct dive *dive)
-{
- int i, divenr;
- size_t j;
- double cns = 0.0;
- struct divecomputer *dc = &dive->dc;
- struct dive *prev_dive;
- timestamp_t endtime;
-
- /* shortcut */
- if (dive->cns)
- return dive->cns;
- /*
- * Do we start with a cns loading from a previous dive?
- * Check if we did a dive 12 hours prior, and what cns we had from that.
- * Then apply ha 90min halftime to see whats left.
- */
- divenr = get_divenr(dive);
- if (divenr) {
- prev_dive = get_dive(divenr - 1);
- if (prev_dive) {
- endtime = prev_dive->when + prev_dive->duration.seconds;
- if (dive->when < (endtime + 3600 * 12)) {
- cns = calculate_cns(prev_dive);
- cns = cns * 1 / pow(2, (dive->when - endtime) / (90.0 * 60.0));
- }
- }
- }
- /* Caclulate the cns for each sample in this dive and sum them */
- for (i = 1; i < dc->samples; i++) {
- int t;
- int po2;
- struct sample *sample = dc->sample + i;
- struct sample *psample = sample - 1;
- t = sample->time.seconds - psample->time.seconds;
- if (sample->setpoint.mbar) {
- po2 = sample->setpoint.mbar;
- } else {
- int o2 = active_o2(dive, dc, sample->time);
- po2 = o2 * depth_to_atm(sample->depth.mm, dive);
- }
- /* CNS don't increse when below 500 matm */
- if (po2 < 500)
- continue;
- /* Find what table-row we should calculate % for */
- for (j = 1; j < sizeof(cns_table) / (sizeof(int) * 3); j++)
- if (po2 > cns_table[j][0])
- break;
- j--;
- cns += ((double)t) / ((double)cns_table[j][1]) * 100;
- }
- /* save calculated cns in dive struct */
- dive->cns = cns;
- return dive->cns;
-}
-/*
- * Return air usage (in liters).
- */
-static double calculate_airuse(struct dive *dive)
-{
- int airuse = 0;
- int i;
-
- for (i = 0; i < MAX_CYLINDERS; i++) {
- pressure_t start, end;
- cylinder_t *cyl = dive->cylinder + i;
-
- start = cyl->start.mbar ? cyl->start : cyl->sample_start;
- end = cyl->end.mbar ? cyl->end : cyl->sample_end;
- if (!end.mbar || start.mbar <= end.mbar)
- continue;
-
- airuse += gas_volume(cyl, start) - gas_volume(cyl, end);
- }
- return airuse / 1000.0;
-}
-
-/* this only uses the first divecomputer to calculate the SAC rate */
-static int calculate_sac(struct dive *dive)
-{
- struct divecomputer *dc = &dive->dc;
- double airuse, pressure, sac;
- int duration, meandepth;
-
- airuse = calculate_airuse(dive);
- if (!airuse)
- return 0;
-
- duration = dc->duration.seconds;
- if (!duration)
- return 0;
-
- meandepth = dc->meandepth.mm;
- if (!meandepth)
- return 0;
-
- /* Mean pressure in ATM (SAC calculations are in atm*l/min) */
- pressure = depth_to_atm(meandepth, dive);
- sac = airuse / pressure * 60 / duration;
-
- /* milliliters per minute.. */
- return sac * 1000;
-}
-
-/* for now we do this based on the first divecomputer */
-static void add_dive_to_deco(struct dive *dive)
-{
- struct divecomputer *dc = &dive->dc;
- int i;
-
- if (!dc)
- return;
- for (i = 1; i < dc->samples; i++) {
- struct sample *psample = dc->sample + i - 1;
- struct sample *sample = dc->sample + i;
- int t0 = psample->time.seconds;
- int t1 = sample->time.seconds;
- int j;
-
- for (j = t0; j < t1; j++) {
- int depth = interpolate(psample->depth.mm, sample->depth.mm, j - t0, t1 - t0);
- add_segment(depth_to_bar(depth, dive),
- &dive->cylinder[sample->sensor].gasmix, 1, sample->setpoint.mbar, dive, dive->sac);
- }
- }
-}
-
-int get_divenr(struct dive *dive)
-{
- int i;
- struct dive *d;
- // tempting as it may be, don't die when called with dive=NULL
- if (dive)
- for_each_dive(i, d) {
- if (d->id == dive->id) // don't compare pointers, we could be passing in a copy of the dive
- return i;
- }
- return -1;
-}
-
-int get_divesite_idx(struct dive_site *ds)
-{
- int i;
- struct dive_site *d;
- // tempting as it may be, don't die when called with dive=NULL
- if (ds)
- for_each_dive_site(i, d) {
- if (d->uuid == ds->uuid) // don't compare pointers, we could be passing in a copy of the dive
- return i;
- }
- return -1;
-}
-
-static struct gasmix air = { .o2.permille = O2_IN_AIR, .he.permille = 0 };
-
-/* take into account previous dives until there is a 48h gap between dives */
-double init_decompression(struct dive *dive)
-{
- int i, divenr = -1;
- unsigned int surface_time;
- timestamp_t when, lasttime = 0, laststart = 0;
- bool deco_init = false;
- double surface_pressure;
-
- if (!dive)
- return 0.0;
-
- surface_pressure = get_surface_pressure_in_mbar(dive, true) / 1000.0;
- divenr = get_divenr(dive);
- when = dive->when;
- i = divenr;
- if (i < 0) {
- i = dive_table.nr - 1;
- while (i >= 0 && get_dive(i)->when > when)
- --i;
- i++;
- }
- while (i--) {
- struct dive *pdive = get_dive(i);
- /* we don't want to mix dives from different trips as we keep looking
- * for how far back we need to go */
- if (dive->divetrip && pdive->divetrip != dive->divetrip)
- continue;
- if (!pdive || pdive->when >= when || pdive->when + pdive->duration.seconds + 48 * 60 * 60 < when)
- break;
- /* For simultaneous dives, only consider the first */
- if (pdive->when == laststart)
- continue;
- when = pdive->when;
- lasttime = when + pdive->duration.seconds;
- }
- while (++i < (divenr >= 0 ? divenr : dive_table.nr)) {
- struct dive *pdive = get_dive(i);
- /* again skip dives from different trips */
- if (dive->divetrip && dive->divetrip != pdive->divetrip)
- continue;
- surface_pressure = get_surface_pressure_in_mbar(pdive, true) / 1000.0;
- if (!deco_init) {
- clear_deco(surface_pressure);
- deco_init = true;
-#if DECO_CALC_DEBUG & 2
- dump_tissues();
-#endif
- }
- add_dive_to_deco(pdive);
- laststart = pdive->when;
-#if DECO_CALC_DEBUG & 2
- printf("added dive #%d\n", pdive->number);
- dump_tissues();
-#endif
- if (pdive->when > lasttime) {
- surface_time = pdive->when - lasttime;
- lasttime = pdive->when + pdive->duration.seconds;
- add_segment(surface_pressure, &air, surface_time, 0, dive, prefs.decosac);
-#if DECO_CALC_DEBUG & 2
- printf("after surface intervall of %d:%02u\n", FRACTION(surface_time, 60));
- dump_tissues();
-#endif
- }
- }
- /* add the final surface time */
- if (lasttime && dive->when > lasttime) {
- surface_time = dive->when - lasttime;
- surface_pressure = get_surface_pressure_in_mbar(dive, true) / 1000.0;
- add_segment(surface_pressure, &air, surface_time, 0, dive, prefs.decosac);
-#if DECO_CALC_DEBUG & 2
- printf("after surface intervall of %d:%02u\n", FRACTION(surface_time, 60));
- dump_tissues();
-#endif
- }
- if (!deco_init) {
- surface_pressure = get_surface_pressure_in_mbar(dive, true) / 1000.0;
- clear_deco(surface_pressure);
-#if DECO_CALC_DEBUG & 2
- printf("no previous dive\n");
- dump_tissues();
-#endif
- }
- return tissue_tolerance_calc(dive, surface_pressure);
-}
-
-void update_cylinder_related_info(struct dive *dive)
-{
- if (dive != NULL) {
- dive->sac = calculate_sac(dive);
- dive->otu = calculate_otu(dive);
- if (dive->maxcns == 0)
- dive->maxcns = calculate_cns(dive);
- }
-}
-
-#define MAX_GAS_STRING 80
-#define UTF8_ELLIPSIS "\xE2\x80\xA6"
-
-/* callers needs to free the string */
-char *get_dive_gas_string(struct dive *dive)
-{
- int o2, he, o2max;
- char *buffer = malloc(MAX_GAS_STRING);
-
- if (buffer) {
- get_dive_gas(dive, &o2, &he, &o2max);
- o2 = (o2 + 5) / 10;
- he = (he + 5) / 10;
- o2max = (o2max + 5) / 10;
-
- if (he)
- if (o2 == o2max)
- snprintf(buffer, MAX_GAS_STRING, "%d/%d", o2, he);
- else
- snprintf(buffer, MAX_GAS_STRING, "%d/%d" UTF8_ELLIPSIS "%d%%", o2, he, o2max);
- else if (o2)
- if (o2 == o2max)
- snprintf(buffer, MAX_GAS_STRING, "%d%%", o2);
- else
- snprintf(buffer, MAX_GAS_STRING, "%d" UTF8_ELLIPSIS "%d%%", o2, o2max);
- else
- strcpy(buffer, translate("gettextFromC", "air"));
- }
- return buffer;
-}
-
-/*
- * helper functions for dive_trip handling
- */
-#ifdef DEBUG_TRIP
-void dump_trip_list(void)
-{
- dive_trip_t *trip;
- int i = 0;
- timestamp_t last_time = 0;
-
- for (trip = dive_trip_list; trip; trip = trip->next) {
- struct tm tm;
- utc_mkdate(trip->when, &tm);
- if (trip->when < last_time)
- printf("\n\ndive_trip_list OUT OF ORDER!!!\n\n\n");
- printf("%s trip %d to \"%s\" on %04u-%02u-%02u %02u:%02u:%02u (%d dives - %p)\n",
- trip->autogen ? "autogen " : "",
- ++i, trip->location,
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
- trip->nrdives, trip);
- last_time = trip->when;
- }
- printf("-----\n");
-}
-#endif
-
-/* this finds the last trip that at or before the time given */
-dive_trip_t *find_matching_trip(timestamp_t when)
-{
- dive_trip_t *trip = dive_trip_list;
-
- if (!trip || trip->when > when) {
-#ifdef DEBUG_TRIP
- printf("no matching trip\n");
-#endif
- return NULL;
- }
- while (trip->next && trip->next->when <= when)
- trip = trip->next;
-#ifdef DEBUG_TRIP
- {
- struct tm tm;
- utc_mkdate(trip->when, &tm);
- printf("found trip %p @ %04d-%02d-%02d %02d:%02d:%02d\n",
- trip,
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec);
- }
-#endif
- return trip;
-}
-
-/* insert the trip into the dive_trip_list - but ensure you don't have
- * two trips for the same date; but if you have, make sure you don't
- * keep the one with less information */
-void insert_trip(dive_trip_t **dive_trip_p)
-{
- dive_trip_t *dive_trip = *dive_trip_p;
- dive_trip_t **p = &dive_trip_list;
- dive_trip_t *trip;
- struct dive *divep;
-
- /* Walk the dive trip list looking for the right location.. */
- while ((trip = *p) != NULL && trip->when < dive_trip->when)
- p = &trip->next;
-
- if (trip && trip->when == dive_trip->when) {
- if (!trip->location)
- trip->location = dive_trip->location;
- if (!trip->notes)
- trip->notes = dive_trip->notes;
- divep = dive_trip->dives;
- while (divep) {
- add_dive_to_trip(divep, trip);
- divep = divep->next;
- }
- *dive_trip_p = trip;
- } else {
- dive_trip->next = trip;
- *p = dive_trip;
- }
-#ifdef DEBUG_TRIP
- dump_trip_list();
-#endif
-}
-
-static void delete_trip(dive_trip_t *trip)
-{
- dive_trip_t **p, *tmp;
-
- assert(!trip->dives);
-
- /* Remove the trip from the list of trips */
- p = &dive_trip_list;
- while ((tmp = *p) != NULL) {
- if (tmp == trip) {
- *p = trip->next;
- break;
- }
- p = &tmp->next;
- }
-
- /* .. and free it */
- free(trip->location);
- free(trip->notes);
- free(trip);
-}
-
-void find_new_trip_start_time(dive_trip_t *trip)
-{
- struct dive *dive = trip->dives;
- timestamp_t when = dive->when;
-
- while ((dive = dive->next) != NULL) {
- if (dive->when < when)
- when = dive->when;
- }
- trip->when = when;
-}
-
-/* check if we have a trip right before / after this dive */
-bool is_trip_before_after(struct dive *dive, bool before)
-{
- int idx = get_idx_by_uniq_id(dive->id);
- if (before) {
- if (idx > 0 && get_dive(idx - 1)->divetrip)
- return true;
- } else {
- if (idx < dive_table.nr - 1 && get_dive(idx + 1)->divetrip)
- return true;
- }
- return false;
-}
-
-struct dive *first_selected_dive()
-{
- int idx;
- struct dive *d;
-
- for_each_dive (idx, d) {
- if (d->selected)
- return d;
- }
- return NULL;
-}
-
-struct dive *last_selected_dive()
-{
- int idx;
- struct dive *d, *ret = NULL;
-
- for_each_dive (idx, d) {
- if (d->selected)
- ret = d;
- }
- return ret;
-}
-
-void remove_dive_from_trip(struct dive *dive, short was_autogen)
-{
- struct dive *next, **pprev;
- dive_trip_t *trip = dive->divetrip;
-
- if (!trip)
- return;
-
- /* Remove the dive from the trip's list of dives */
- next = dive->next;
- pprev = dive->pprev;
- *pprev = next;
- if (next)
- next->pprev = pprev;
-
- dive->divetrip = NULL;
- if (was_autogen)
- dive->tripflag = TF_NONE;
- else
- dive->tripflag = NO_TRIP;
- assert(trip->nrdives > 0);
- if (!--trip->nrdives)
- delete_trip(trip);
- else if (trip->when == dive->when)
- find_new_trip_start_time(trip);
-}
-
-void add_dive_to_trip(struct dive *dive, dive_trip_t *trip)
-{
- if (dive->divetrip == trip)
- return;
- remove_dive_from_trip(dive, false);
- trip->nrdives++;
- dive->divetrip = trip;
- dive->tripflag = ASSIGNED_TRIP;
-
- /* Add it to the trip's list of dives*/
- dive->next = trip->dives;
- if (dive->next)
- dive->next->pprev = &dive->next;
- trip->dives = dive;
- dive->pprev = &trip->dives;
-
- if (dive->when && trip->when > dive->when)
- trip->when = dive->when;
-}
-
-dive_trip_t *create_and_hookup_trip_from_dive(struct dive *dive)
-{
- dive_trip_t *dive_trip = calloc(1, sizeof(dive_trip_t));
-
- dive_trip->when = dive->when;
- dive_trip->location = copy_string(get_dive_location(dive));
- insert_trip(&dive_trip);
-
- dive->tripflag = IN_TRIP;
- add_dive_to_trip(dive, dive_trip);
- return dive_trip;
-}
-
-/*
- * Walk the dives from the oldest dive, and see if we can autogroup them
- */
-void autogroup_dives(void)
-{
- int i;
- struct dive *dive, *lastdive = NULL;
-
- for_each_dive(i, dive) {
- dive_trip_t *trip;
-
- if (dive->divetrip) {
- lastdive = dive;
- continue;
- }
-
- if (!DIVE_NEEDS_TRIP(dive)) {
- lastdive = NULL;
- continue;
- }
-
- /* Do we have a trip we can combine this into? */
- if (lastdive && dive->when < lastdive->when + TRIP_THRESHOLD) {
- dive_trip_t *trip = lastdive->divetrip;
- add_dive_to_trip(dive, trip);
- if (get_dive_location(dive) && !trip->location)
- trip->location = copy_string(get_dive_location(dive));
- lastdive = dive;
- continue;
- }
-
- lastdive = dive;
- trip = create_and_hookup_trip_from_dive(dive);
- trip->autogen = 1;
- }
-
-#ifdef DEBUG_TRIP
- dump_trip_list();
-#endif
-}
-
-/* this implements the mechanics of removing the dive from the table,
- * but doesn't deal with updating dive trips, etc */
-void delete_single_dive(int idx)
-{
- int i;
- struct dive *dive = get_dive(idx);
- if (!dive)
- return; /* this should never happen */
- remove_dive_from_trip(dive, false);
- if (dive->selected)
- deselect_dive(idx);
- for (i = idx; i < dive_table.nr - 1; i++)
- dive_table.dives[i] = dive_table.dives[i + 1];
- dive_table.dives[--dive_table.nr] = NULL;
- /* free all allocations */
- free(dive->dc.sample);
- free((void *)dive->notes);
- free((void *)dive->divemaster);
- free((void *)dive->buddy);
- free((void *)dive->suit);
- taglist_free(dive->tag_list);
- free(dive);
-}
-
-struct dive **grow_dive_table(struct dive_table *table)
-{
- int nr = table->nr, allocated = table->allocated;
- struct dive **dives = table->dives;
-
- if (nr >= allocated) {
- allocated = (nr + 32) * 3 / 2;
- dives = realloc(dives, allocated * sizeof(struct dive *));
- if (!dives)
- exit(1);
- table->dives = dives;
- table->allocated = allocated;
- }
- return dives;
-}
-
-void add_single_dive(int idx, struct dive *dive)
-{
- int i;
- grow_dive_table(&dive_table);
- dive_table.nr++;
- if (dive->selected)
- amount_selected++;
-
- if (idx < 0) {
- // convert an idx of -1 so we do insert-in-chronological-order
- idx = dive_table.nr - 1;
- for (int i = 0; i < dive_table.nr - 1; i++) {
- if (dive->when <= dive_table.dives[i]->when) {
- idx = i;
- break;
- }
- }
- }
-
- for (i = idx; i < dive_table.nr; i++) {
- struct dive *tmp = dive_table.dives[i];
- dive_table.dives[i] = dive;
- dive = tmp;
- }
-}
-
-bool consecutive_selected()
-{
- struct dive *d;
- int i;
- bool consecutive = true;
- bool firstfound = false;
- bool lastfound = false;
-
- if (amount_selected == 0 || amount_selected == 1)
- return true;
-
- for_each_dive(i, d) {
- if (d->selected) {
- if (!firstfound)
- firstfound = true;
- else if (lastfound)
- consecutive = false;
- } else if (firstfound) {
- lastfound = true;
- }
- }
- return consecutive;
-}
-
-/*
- * Merge two dives. 'a' is always before 'b' in the dive list
- * (and thus in time).
- */
-struct dive *merge_two_dives(struct dive *a, struct dive *b)
-{
- struct dive *res;
- int i, j, nr, nrdiff;
- int id;
-
- if (!a || !b)
- return NULL;
-
- id = a->id;
- i = get_divenr(a);
- j = get_divenr(b);
- if (i < 0 || j < 0)
- // something is wrong with those dives. Bail
- return NULL;
- res = merge_dives(a, b, b->when - a->when, false);
- if (!res)
- return NULL;
-
- /*
- * If 'a' and 'b' were numbered, and in proper order,
- * then the resulting dive will get the first number,
- * and the subsequent dives will be renumbered by the
- * difference.
- *
- * So if you had a dive list 1 3 6 7 8, and you
- * merge 1 and 3, the resulting numbered list will
- * be 1 4 5 6, because we assume that there were
- * some missing dives (originally dives 4 and 5),
- * that now will still be missing (dives 2 and 3
- * in the renumbered world).
- *
- * Obviously the normal case is that everything is
- * consecutive, and the difference will be 1, so the
- * above example is not supposed to be normal.
- */
- nrdiff = 0;
- nr = a->number;
- if (a->number && b->number > a->number) {
- res->number = nr;
- nrdiff = b->number - nr;
- }
-
- add_single_dive(i, res);
- delete_single_dive(i + 1);
- delete_single_dive(j);
- // now make sure that we keep the id of the first dive.
- // why?
- // because this way one of the previously selected ids is still around
- res->id = id;
-
- // renumber dives from merged one in advance by difference between
- // merged dives numbers. Do not renumber if actual number is zero.
- for (; j < dive_table.nr; j++) {
- struct dive *dive = dive_table.dives[j];
- int newnr;
-
- if (!dive->number)
- continue;
- newnr = dive->number - nrdiff;
-
- /*
- * Don't renumber stuff that isn't in order!
- *
- * So if the new dive number isn't larger than the
- * previous dive number, just stop here.
- */
- if (newnr <= nr)
- break;
- dive->number = newnr;
- nr = newnr;
- }
-
- mark_divelist_changed(true);
- return res;
-}
-
-void select_dive(int idx)
-{
- struct dive *dive = get_dive(idx);
- if (dive) {
- /* never select an invalid dive that isn't displayed */
- if (!dive->selected) {
- dive->selected = 1;
- amount_selected++;
- }
- selected_dive = idx;
- }
-}
-
-void deselect_dive(int idx)
-{
- struct dive *dive = get_dive(idx);
- if (dive && dive->selected) {
- dive->selected = 0;
- if (amount_selected)
- amount_selected--;
- if (selected_dive == idx && amount_selected > 0) {
- /* pick a different dive as selected */
- while (--selected_dive >= 0) {
- dive = get_dive(selected_dive);
- if (dive && dive->selected)
- return;
- }
- selected_dive = idx;
- while (++selected_dive < dive_table.nr) {
- dive = get_dive(selected_dive);
- if (dive && dive->selected)
- return;
- }
- }
- if (amount_selected == 0)
- selected_dive = -1;
- }
-}
-
-void deselect_dives_in_trip(struct dive_trip *trip)
-{
- struct dive *dive;
- if (!trip)
- return;
- for (dive = trip->dives; dive; dive = dive->next)
- deselect_dive(get_divenr(dive));
-}
-
-void select_dives_in_trip(struct dive_trip *trip)
-{
- struct dive *dive;
- if (!trip)
- return;
- for (dive = trip->dives; dive; dive = dive->next)
- if (!dive->hidden_by_filter)
- select_dive(get_divenr(dive));
-}
-
-void filter_dive(struct dive *d, bool shown)
-{
- if (!d)
- return;
- d->hidden_by_filter = !shown;
- if (!shown && d->selected)
- deselect_dive(get_divenr(d));
-}
-
-
-/* This only gets called with non-NULL trips.
- * It does not combine notes or location, just picks the first one
- * (or the second one if the first one is empty */
-void combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b)
-{
- if (same_string(trip_a->location, "") && trip_b->location) {
- free(trip_a->location);
- trip_a->location = strdup(trip_b->location);
- }
- if (same_string(trip_a->notes, "") && trip_b->notes) {
- free(trip_a->notes);
- trip_a->notes = strdup(trip_b->notes);
- }
- /* this also removes the dives from trip_b and eventually
- * calls delete_trip(trip_b) when the last dive has been moved */
- while (trip_b->dives)
- add_dive_to_trip(trip_b->dives, trip_a);
-}
-
-void mark_divelist_changed(int changed)
-{
- dive_list_changed = changed;
- updateWindowTitle();
-}
-
-int unsaved_changes()
-{
- return dive_list_changed;
-}
-
-void remove_autogen_trips()
-{
- int i;
- struct dive *dive;
-
- for_each_dive(i, dive) {
- dive_trip_t *trip = dive->divetrip;
-
- if (trip && trip->autogen)
- remove_dive_from_trip(dive, true);
- }
-}
-
-/*
- * When adding dives to the dive table, we try to renumber
- * the new dives based on any old dives in the dive table.
- *
- * But we only do it if:
- *
- * - there are no dives in the dive table
- *
- * OR
- *
- * - the last dive in the old dive table was numbered
- *
- * - all the new dives are strictly at the end (so the
- * "last dive" is at the same location in the dive table
- * after re-sorting the dives.
- *
- * - none of the new dives have any numbers
- *
- * This catches the common case of importing new dives from
- * a dive computer, and gives them proper numbers based on
- * your old dive list. But it tries to be very conservative
- * and not give numbers if there is *any* question about
- * what the numbers should be - in which case you need to do
- * a manual re-numbering.
- */
-static void try_to_renumber(struct dive *last, int preexisting)
-{
- int i, nr;
-
- /*
- * If the new dives aren't all strictly at the end,
- * we're going to expect the user to do a manual
- * renumbering.
- */
- if (preexisting && get_dive(preexisting - 1) != last)
- return;
-
- /*
- * If any of the new dives already had a number,
- * we'll have to do a manual renumbering.
- */
- for (i = preexisting; i < dive_table.nr; i++) {
- struct dive *dive = get_dive(i);
- if (dive->number)
- return;
- }
-
- /*
- * Ok, renumber..
- */
- if (last)
- nr = last->number;
- else
- nr = 0;
- for (i = preexisting; i < dive_table.nr; i++) {
- struct dive *dive = get_dive(i);
- dive->number = ++nr;
- }
-}
-
-void process_dives(bool is_imported, bool prefer_imported)
-{
- int i;
- int preexisting = dive_table.preexisting;
- bool did_merge = false;
- struct dive *last;
-
- /* check if we need a nickname for the divecomputer for newly downloaded dives;
- * since we know they all came from the same divecomputer we just check for the
- * first one */
- if (preexisting < dive_table.nr && dive_table.dives[preexisting]->downloaded)
- set_dc_nickname(dive_table.dives[preexisting]);
- else
- /* they aren't downloaded, so record / check all new ones */
- for (i = preexisting; i < dive_table.nr; i++)
- set_dc_nickname(dive_table.dives[i]);
-
- for (i = preexisting; i < dive_table.nr; i++)
- dive_table.dives[i]->downloaded = true;
-
- /* This does the right thing for -1: NULL */
- last = get_dive(preexisting - 1);
-
- sort_table(&dive_table);
-
- for (i = 1; i < dive_table.nr; i++) {
- struct dive **pp = &dive_table.dives[i - 1];
- struct dive *prev = pp[0];
- struct dive *dive = pp[1];
- struct dive *merged;
- int id;
-
- /* only try to merge overlapping dives - or if one of the dives has
- * zero duration (that might be a gps marker from the webservice) */
- if (prev->duration.seconds && dive->duration.seconds &&
- prev->when + prev->duration.seconds < dive->when)
- continue;
-
- merged = try_to_merge(prev, dive, prefer_imported);
- if (!merged)
- continue;
-
- // remember the earlier dive's id
- id = prev->id;
-
- /* careful - we might free the dive that last points to. Oops... */
- if (last == prev || last == dive)
- last = merged;
-
- /* Redo the new 'i'th dive */
- i--;
- add_single_dive(i, merged);
- delete_single_dive(i + 1);
- delete_single_dive(i + 1);
- // keep the id or the first dive for the merged dive
- merged->id = id;
-
- /* this means the table was changed */
- did_merge = true;
- }
- /* make sure no dives are still marked as downloaded */
- for (i = 1; i < dive_table.nr; i++)
- dive_table.dives[i]->downloaded = false;
-
- if (is_imported) {
- /* If there are dives in the table, are they numbered */
- if (!last || last->number)
- try_to_renumber(last, preexisting);
-
- /* did we add dives or divecomputers to the dive table? */
- if (did_merge || preexisting < dive_table.nr) {
- mark_divelist_changed(true);
- }
- }
-}
-
-void set_dive_nr_for_current_dive()
-{
- if (dive_table.nr == 1)
- current_dive->number = 1;
- else if (selected_dive == dive_table.nr - 1 && get_dive(dive_table.nr - 2)->number)
- current_dive->number = get_dive(dive_table.nr - 2)->number + 1;
-}
-
-static int min_datafile_version;
-
-int get_min_datafile_version()
-{
- return min_datafile_version;
-}
-
-void reset_min_datafile_version()
-{
- min_datafile_version = 0;
-}
-
-void report_datafile_version(int version)
-{
- if (min_datafile_version == 0 || min_datafile_version > version)
- min_datafile_version = version;
-}
-
-void clear_dive_file_data()
-{
- while (dive_table.nr)
- delete_single_dive(0);
- while (dive_site_table.nr)
- delete_dive_site(get_dive_site(0)->uuid);
-
- clear_dive(&displayed_dive);
- clear_dive_site(&displayed_dive_site);
-
- free((void *)existing_filename);
- existing_filename = NULL;
-
- reset_min_datafile_version();
- saved_git_id = "";
-}
diff --git a/subsurface-core/divelist.h b/subsurface-core/divelist.h
deleted file mode 100644
index 5bae09cff..000000000
--- a/subsurface-core/divelist.h
+++ /dev/null
@@ -1,62 +0,0 @@
-#ifndef DIVELIST_H
-#define DIVELIST_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* this is used for both git and xml format */
-#define DATAFORMAT_VERSION 3
-
-struct dive;
-
-extern void update_cylinder_related_info(struct dive *);
-extern void mark_divelist_changed(int);
-extern int unsaved_changes(void);
-extern void remove_autogen_trips(void);
-extern double init_decompression(struct dive *dive);
-
-/* divelist core logic functions */
-extern void process_dives(bool imported, bool prefer_imported);
-extern char *get_dive_gas_string(struct dive *dive);
-
-extern dive_trip_t *find_trip_by_idx(int idx);
-
-struct dive **grow_dive_table(struct dive_table *table);
-extern int trip_has_selected_dives(dive_trip_t *trip);
-extern void get_dive_gas(struct dive *dive, int *o2_p, int *he_p, int *o2low_p);
-extern int get_divenr(struct dive *dive);
-extern int get_divesite_idx(struct dive_site *ds);
-extern dive_trip_t *find_matching_trip(timestamp_t when);
-extern void remove_dive_from_trip(struct dive *dive, short was_autogen);
-extern dive_trip_t *create_and_hookup_trip_from_dive(struct dive *dive);
-extern void autogroup_dives(void);
-extern struct dive *merge_two_dives(struct dive *a, struct dive *b);
-extern bool consecutive_selected();
-extern void select_dive(int idx);
-extern void deselect_dive(int idx);
-extern void select_dives_in_trip(struct dive_trip *trip);
-extern void deselect_dives_in_trip(struct dive_trip *trip);
-extern void filter_dive(struct dive *d, bool shown);
-extern void combine_trips(struct dive_trip *trip_a, struct dive_trip *trip_b);
-extern void find_new_trip_start_time(dive_trip_t *trip);
-extern struct dive *first_selected_dive();
-extern struct dive *last_selected_dive();
-extern bool is_trip_before_after(struct dive *dive, bool before);
-extern void set_dive_nr_for_current_dive();
-
-int get_min_datafile_version();
-void reset_min_datafile_version();
-void report_datafile_version(int version);
-void clear_dive_file_data();
-
-#ifdef DEBUG_TRIP
-extern void dump_selection(void);
-extern void dump_trip_list(void);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // DIVELIST_H
diff --git a/subsurface-core/divelogexportlogic.cpp b/subsurface-core/divelogexportlogic.cpp
deleted file mode 100644
index af5157f4a..000000000
--- a/subsurface-core/divelogexportlogic.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-#include <QString>
-#include <QFile>
-#include <QFileInfo>
-#include <QDir>
-#include <QSettings>
-#include <QTextStream>
-#include "divelogexportlogic.h"
-#include "helpers.h"
-#include "units.h"
-#include "statistics.h"
-#include "save-html.h"
-
-void file_copy_and_overwrite(const QString &fileName, const QString &newName)
-{
- QFile file(newName);
- if (file.exists())
- file.remove();
- QFile::copy(fileName, newName);
-}
-
-void exportHTMLsettings(const QString &filename, struct htmlExportSetting &hes)
-{
- QString fontSize = hes.fontSize;
- QString fontFamily = hes.fontFamily;
- QFile file(filename);
- file.open(QIODevice::WriteOnly | QIODevice::Text);
- QTextStream out(&file);
- out << "settings = {\"fontSize\":\"" << fontSize << "\",\"fontFamily\":\"" << fontFamily << "\",\"listOnly\":\""
- << hes.listOnly << "\",\"subsurfaceNumbers\":\"" << hes.subsurfaceNumbers << "\",";
- //save units preferences
- if (prefs.unit_system == METRIC) {
- out << "\"unit_system\":\"Meteric\"";
- } else if (prefs.unit_system == IMPERIAL) {
- out << "\"unit_system\":\"Imperial\"";
- } else {
- QVariant v;
- QString length, pressure, volume, temperature, weight;
- length = prefs.units.length == units::METERS ? "METER" : "FEET";
- pressure = prefs.units.pressure == units::BAR ? "BAR" : "PSI";
- volume = prefs.units.volume == units::LITER ? "LITER" : "CUFT";
- temperature = prefs.units.temperature == units::CELSIUS ? "CELSIUS" : "FAHRENHEIT";
- weight = prefs.units.weight == units::KG ? "KG" : "LBS";
- out << "\"unit_system\":\"Personalize\",";
- out << "\"units\":{\"depth\":\"" << length << "\",\"pressure\":\"" << pressure << "\",\"volume\":\"" << volume << "\",\"temperature\":\"" << temperature << "\",\"weight\":\"" << weight << "\"}";
- }
- out << "}";
- file.close();
-}
-
-static void exportHTMLstatisticsTotal(QTextStream &out, stats_t *total_stats)
-{
- out << "{";
- out << "\"YEAR\":\"Total\",";
- out << "\"DIVES\":\"" << total_stats->selection_size << "\",";
- out << "\"TOTAL_TIME\":\"" << get_time_string(total_stats->total_time.seconds, 0) << "\",";
- out << "\"AVERAGE_TIME\":\"--\",";
- out << "\"SHORTEST_TIME\":\"--\",";
- out << "\"LONGEST_TIME\":\"--\",";
- out << "\"AVG_DEPTH\":\"--\",";
- out << "\"MIN_DEPTH\":\"--\",";
- out << "\"MAX_DEPTH\":\"--\",";
- out << "\"AVG_SAC\":\"--\",";
- out << "\"MIN_SAC\":\"--\",";
- out << "\"MAX_SAC\":\"--\",";
- out << "\"AVG_TEMP\":\"--\",";
- out << "\"MIN_TEMP\":\"--\",";
- out << "\"MAX_TEMP\":\"--\",";
- out << "},";
-}
-
-
-static void exportHTMLstatistics(const QString filename, struct htmlExportSetting &hes)
-{
- QFile file(filename);
- file.open(QIODevice::WriteOnly | QIODevice::Text);
- QTextStream out(&file);
-
- stats_t total_stats;
-
- total_stats.selection_size = 0;
- total_stats.total_time.seconds = 0;
-
- int i = 0;
- out << "divestat=[";
- if (hes.yearlyStatistics) {
- while (stats_yearly != NULL && stats_yearly[i].period) {
- out << "{";
- out << "\"YEAR\":\"" << stats_yearly[i].period << "\",";
- out << "\"DIVES\":\"" << stats_yearly[i].selection_size << "\",";
- out << "\"TOTAL_TIME\":\"" << get_time_string(stats_yearly[i].total_time.seconds, 0) << "\",";
- out << "\"AVERAGE_TIME\":\"" << get_minutes(stats_yearly[i].total_time.seconds / stats_yearly[i].selection_size) << "\",";
- out << "\"SHORTEST_TIME\":\"" << get_minutes(stats_yearly[i].shortest_time.seconds) << "\",";
- out << "\"LONGEST_TIME\":\"" << get_minutes(stats_yearly[i].longest_time.seconds) << "\",";
- out << "\"AVG_DEPTH\":\"" << get_depth_string(stats_yearly[i].avg_depth) << "\",";
- out << "\"MIN_DEPTH\":\"" << get_depth_string(stats_yearly[i].min_depth) << "\",";
- out << "\"MAX_DEPTH\":\"" << get_depth_string(stats_yearly[i].max_depth) << "\",";
- out << "\"AVG_SAC\":\"" << get_volume_string(stats_yearly[i].avg_sac) << "\",";
- out << "\"MIN_SAC\":\"" << get_volume_string(stats_yearly[i].min_sac) << "\",";
- out << "\"MAX_SAC\":\"" << get_volume_string(stats_yearly[i].max_sac) << "\",";
- if ( stats_yearly[i].combined_count )
- out << "\"AVG_TEMP\":\"" << QString::number(stats_yearly[i].combined_temp / stats_yearly[i].combined_count, 'f', 1) << "\",";
- else
- out << "\"AVG_TEMP\":\"0.0\",";
- out << "\"MIN_TEMP\":\"" << ( stats_yearly[i].min_temp == 0 ? 0 : get_temp_units(stats_yearly[i].min_temp, NULL)) << "\",";
- out << "\"MAX_TEMP\":\"" << ( stats_yearly[i].max_temp == 0 ? 0 : get_temp_units(stats_yearly[i].max_temp, NULL)) << "\",";
- out << "},";
- total_stats.selection_size += stats_yearly[i].selection_size;
- total_stats.total_time.seconds += stats_yearly[i].total_time.seconds;
- i++;
- }
- exportHTMLstatisticsTotal(out, &total_stats);
- }
- out << "]";
- file.close();
-
-}
-
-void exportHtmlInitLogic(const QString &filename, struct htmlExportSetting &hes)
-{
- QString photosDirectory;
- QFile file(filename);
- QFileInfo info(file);
- QDir mainDir = info.absoluteDir();
- mainDir.mkdir(file.fileName() + "_files");
- QString exportFiles = file.fileName() + "_files";
-
- QString json_dive_data = exportFiles + QDir::separator() + "file.js";
- QString json_settings = exportFiles + QDir::separator() + "settings.js";
- QString translation = exportFiles + QDir::separator() + "translation.js";
- QString stat_file = exportFiles + QDir::separator() + "stat.js";
- exportFiles += "/";
-
- if (hes.exportPhotos) {
- photosDirectory = exportFiles + QDir::separator() + "photos" + QDir::separator();
- mainDir.mkdir(photosDirectory);
- }
-
-
- exportHTMLsettings(json_settings, hes);
- exportHTMLstatistics(stat_file, hes);
- export_translation(translation.toUtf8().data());
-
- export_HTML(qPrintable(json_dive_data), qPrintable(photosDirectory), hes.selectedOnly, hes.listOnly);
-
- QString searchPath = getSubsurfaceDataPath("theme");
- if (searchPath.isEmpty())
- return;
-
- searchPath += QDir::separator();
-
- file_copy_and_overwrite(searchPath + "dive_export.html", filename);
- file_copy_and_overwrite(searchPath + "list_lib.js", exportFiles + "list_lib.js");
- file_copy_and_overwrite(searchPath + "poster.png", exportFiles + "poster.png");
- file_copy_and_overwrite(searchPath + "jqplot.highlighter.min.js", exportFiles + "jqplot.highlighter.min.js");
- file_copy_and_overwrite(searchPath + "jquery.jqplot.min.js", exportFiles + "jquery.jqplot.min.js");
- file_copy_and_overwrite(searchPath + "jqplot.canvasAxisTickRenderer.min.js", exportFiles + "jqplot.canvasAxisTickRenderer.min.js");
- file_copy_and_overwrite(searchPath + "jqplot.canvasTextRenderer.min.js", exportFiles + "jqplot.canvasTextRenderer.min.js");
- file_copy_and_overwrite(searchPath + "jquery.min.js", exportFiles + "jquery.min.js");
- file_copy_and_overwrite(searchPath + "jquery.jqplot.css", exportFiles + "jquery.jqplot.css");
- file_copy_and_overwrite(searchPath + hes.themeFile, exportFiles + "theme.css");
-}
diff --git a/subsurface-core/divelogexportlogic.h b/subsurface-core/divelogexportlogic.h
deleted file mode 100644
index 84f09c362..000000000
--- a/subsurface-core/divelogexportlogic.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef DIVELOGEXPORTLOGIC_H
-#define DIVELOGEXPORTLOGIC_H
-
-struct htmlExportSetting {
- bool exportPhotos;
- bool selectedOnly;
- bool listOnly;
- QString fontFamily;
- QString fontSize;
- int themeSelection;
- bool subsurfaceNumbers;
- bool yearlyStatistics;
- QString themeFile;
-};
-
-void file_copy_and_overwrite(const QString &fileName, const QString &newName);
-void exportHtmlInitLogic(const QString &filename, struct htmlExportSetting &hes);
-
-#endif // DIVELOGEXPORTLOGIC_H
-
diff --git a/subsurface-core/divesite.c b/subsurface-core/divesite.c
deleted file mode 100644
index e9eed2a07..000000000
--- a/subsurface-core/divesite.c
+++ /dev/null
@@ -1,337 +0,0 @@
-/* divesite.c */
-#include "divesite.h"
-#include "dive.h"
-#include "divelist.h"
-
-#include <math.h>
-
-struct dive_site_table dive_site_table;
-
-/* there could be multiple sites of the same name - return the first one */
-uint32_t get_dive_site_uuid_by_name(const char *name, struct dive_site **dsp)
-{
- int i;
- struct dive_site *ds;
- for_each_dive_site (i, ds) {
- if (same_string(ds->name, name)) {
- if (dsp)
- *dsp = ds;
- return ds->uuid;
- }
- }
- return 0;
-}
-
-/* there could be multiple sites at the same GPS fix - return the first one */
-uint32_t get_dive_site_uuid_by_gps(degrees_t latitude, degrees_t longitude, struct dive_site **dsp)
-{
- int i;
- struct dive_site *ds;
- for_each_dive_site (i, ds) {
- if (ds->latitude.udeg == latitude.udeg && ds->longitude.udeg == longitude.udeg) {
- if (dsp)
- *dsp = ds;
- return ds->uuid;
- }
- }
- return 0;
-}
-
-
-/* to avoid a bug where we have two dive sites with different name and the same GPS coordinates
- * and first get the gps coordinates (reading a V2 file) and happen to get back "the other" name,
- * this function allows us to verify if a very specific name/GPS combination already exists */
-uint32_t get_dive_site_uuid_by_gps_and_name(char *name, degrees_t latitude, degrees_t longitude)
-{
- int i;
- struct dive_site *ds;
- for_each_dive_site (i, ds) {
- if (ds->latitude.udeg == latitude.udeg && ds->longitude.udeg == longitude.udeg && same_string(ds->name, name))
- return ds->uuid;
- }
- return 0;
-}
-
-// Calculate the distance in meters between two coordinates.
-unsigned int get_distance(degrees_t lat1, degrees_t lon1, degrees_t lat2, degrees_t lon2)
-{
- double lat2_r = udeg_to_radians(lat2.udeg);
- double lat_d_r = udeg_to_radians(lat2.udeg-lat1.udeg);
- double lon_d_r = udeg_to_radians(lon2.udeg-lon1.udeg);
-
- double a = sin(lat_d_r/2) * sin(lat_d_r/2) +
- cos(lat2_r) * cos(lat2_r) * sin(lon_d_r/2) * sin(lon_d_r/2);
- double c = 2 * atan2(sqrt(a), sqrt(1.0 - a));
-
- // Earth radious in metres
- return 6371000 * c;
-}
-
-/* find the closest one, no more than distance meters away - if more than one at same distance, pick the first */
-uint32_t get_dive_site_uuid_by_gps_proximity(degrees_t latitude, degrees_t longitude, int distance, struct dive_site **dsp)
-{
- int i;
- int uuid = 0;
- struct dive_site *ds;
- unsigned int cur_distance, min_distance = distance;
- for_each_dive_site (i, ds) {
- if (dive_site_has_gps_location(ds) &&
- (cur_distance = get_distance(ds->latitude, ds->longitude, latitude, longitude)) < min_distance) {
- min_distance = cur_distance;
- uuid = ds->uuid;
- if (dsp)
- *dsp = ds;
- }
- }
- return uuid;
-}
-
-/* try to create a uniqe ID - fingers crossed */
-static uint32_t dive_site_getUniqId()
-{
- uint32_t id = 0;
-
- while (id == 0 || get_dive_site_by_uuid(id)) {
- id = rand() & 0xff;
- id |= (rand() & 0xff) << 8;
- id |= (rand() & 0xff) << 16;
- id |= (rand() & 0xff) << 24;
- }
-
- return id;
-}
-
-/* we never allow a second dive site with the same uuid */
-struct dive_site *alloc_or_get_dive_site(uint32_t uuid)
-{
- struct dive_site *ds;
- if (uuid) {
- if ((ds = get_dive_site_by_uuid(uuid)) != NULL) {
- fprintf(stderr, "PROBLEM: refusing to create dive site with the same uuid %08x\n", uuid);
- return ds;
- }
- }
- int nr = dive_site_table.nr;
- int allocated = dive_site_table.allocated;
- struct dive_site **sites = dive_site_table.dive_sites;
-
- if (nr >= allocated) {
- allocated = (nr + 32) * 3 / 2;
- sites = realloc(sites, allocated * sizeof(struct dive_site *));
- if (!sites)
- exit(1);
- dive_site_table.dive_sites = sites;
- dive_site_table.allocated = allocated;
- }
- ds = calloc(1, sizeof(*ds));
- if (!ds)
- exit(1);
- sites[nr] = ds;
- dive_site_table.nr = nr + 1;
- // we should always be called with a valid uuid except in the special
- // case where we want to copy a dive site into the memory we allocated
- // here - then we need to pass in 0 and create a temporary uuid here
- // (just so things are always consistent)
- if (uuid)
- ds->uuid = uuid;
- else
- ds->uuid = dive_site_getUniqId();
- return ds;
-}
-
-int nr_of_dives_at_dive_site(uint32_t uuid, bool select_only)
-{
- int j;
- int nr = 0;
- struct dive *d;
- for_each_dive(j, d) {
- if (d->dive_site_uuid == uuid && (!select_only || d->selected)) {
- nr++;
- }
- }
- return nr;
-}
-
-bool is_dive_site_used(uint32_t uuid, bool select_only)
-{
- int j;
- bool found = false;
- struct dive *d;
- for_each_dive(j, d) {
- if (d->dive_site_uuid == uuid && (!select_only || d->selected)) {
- found = true;
- break;
- }
- }
- return found;
-}
-
-void delete_dive_site(uint32_t id)
-{
- int nr = dive_site_table.nr;
- for (int i = 0; i < nr; i++) {
- struct dive_site *ds = get_dive_site(i);
- if (ds->uuid == id) {
- free(ds->name);
- free(ds->notes);
- free(ds);
- if (nr - 1 > i)
- memmove(&dive_site_table.dive_sites[i],
- &dive_site_table.dive_sites[i+1],
- (nr - 1 - i) * sizeof(dive_site_table.dive_sites[0]));
- dive_site_table.nr = nr - 1;
- break;
- }
- }
-}
-
-uint32_t create_divesite_uuid(const char *name, timestamp_t divetime)
-{
- if (name == NULL)
- name ="";
- unsigned char hash[20];
- SHA_CTX ctx;
- SHA1_Init(&ctx);
- SHA1_Update(&ctx, &divetime, sizeof(timestamp_t));
- SHA1_Update(&ctx, name, strlen(name));
- SHA1_Final(hash, &ctx);
- // now return the first 32 of the 160 bit hash
- return *(uint32_t *)hash;
-}
-
-/* allocate a new site and add it to the table */
-uint32_t create_dive_site(const char *name, timestamp_t divetime)
-{
- uint32_t uuid = create_divesite_uuid(name, divetime);
- struct dive_site *ds = alloc_or_get_dive_site(uuid);
- ds->name = copy_string(name);
-
- return uuid;
-}
-
-/* same as before, but with current time if no current_dive is present */
-uint32_t create_dive_site_from_current_dive(const char *name)
-{
- if (current_dive != NULL) {
- return create_dive_site(name, current_dive->when);
- } else {
- timestamp_t when;
- time_t now = time(0);
- when = utc_mktime(localtime(&now));
- return create_dive_site(name, when);
- }
-}
-
-/* same as before, but with GPS data */
-uint32_t create_dive_site_with_gps(const char *name, degrees_t latitude, degrees_t longitude, timestamp_t divetime)
-{
- uint32_t uuid = create_divesite_uuid(name, divetime);
- struct dive_site *ds = alloc_or_get_dive_site(uuid);
- ds->name = copy_string(name);
- ds->latitude = latitude;
- ds->longitude = longitude;
-
- return ds->uuid;
-}
-
-/* a uuid is always present - but if all the other fields are empty, the dive site is pointless */
-bool dive_site_is_empty(struct dive_site *ds)
-{
- return same_string(ds->name, "") &&
- same_string(ds->description, "") &&
- same_string(ds->notes, "") &&
- ds->latitude.udeg == 0 &&
- ds->longitude.udeg == 0;
-}
-
-void copy_dive_site(struct dive_site *orig, struct dive_site *copy)
-{
- free(copy->name);
- free(copy->notes);
- free(copy->description);
-
- copy->latitude = orig->latitude;
- copy->longitude = orig->longitude;
- copy->name = copy_string(orig->name);
- copy->notes = copy_string(orig->notes);
- copy->description = copy_string(orig->description);
- copy->uuid = orig->uuid;
- if (orig->taxonomy.category == NULL) {
- free_taxonomy(&copy->taxonomy);
- } else {
- if (copy->taxonomy.category == NULL)
- copy->taxonomy.category = alloc_taxonomy();
- for (int i = 0; i < TC_NR_CATEGORIES; i++) {
- if (i < copy->taxonomy.nr)
- free((void *)copy->taxonomy.category[i].value);
- if (i < orig->taxonomy.nr) {
- copy->taxonomy.category[i] = orig->taxonomy.category[i];
- copy->taxonomy.category[i].value = copy_string(orig->taxonomy.category[i].value);
- }
- }
- copy->taxonomy.nr = orig->taxonomy.nr;
- }
-}
-
-void clear_dive_site(struct dive_site *ds)
-{
- free(ds->name);
- free(ds->notes);
- free(ds->description);
- ds->name = 0;
- ds->notes = 0;
- ds->description = 0;
- ds->latitude.udeg = 0;
- ds->longitude.udeg = 0;
- ds->uuid = 0;
- ds->taxonomy.nr = 0;
- free_taxonomy(&ds->taxonomy);
-}
-
-void merge_dive_sites(uint32_t ref, uint32_t* uuids, int count)
-{
- int curr_dive, i;
- struct dive *d;
- for(i = 0; i < count; i++){
- if (uuids[i] == ref)
- continue;
-
- for_each_dive(curr_dive, d) {
- if (d->dive_site_uuid != uuids[i] )
- continue;
- d->dive_site_uuid = ref;
- }
- }
-
- for(i = 0; i < count; i++) {
- if (uuids[i] == ref)
- continue;
- delete_dive_site(uuids[i]);
- }
- mark_divelist_changed(true);
-}
-
-uint32_t find_or_create_dive_site_with_name(const char *name, timestamp_t divetime)
-{
- int i;
- struct dive_site *ds;
- for_each_dive_site(i,ds) {
- if (same_string(name, ds->name))
- break;
- }
- if (ds)
- return ds->uuid;
- return create_dive_site(name, divetime);
-}
-
-static int compare_sites(const void *_a, const void *_b)
-{
- const struct dive_site *a = (const struct dive_site *)*(void **)_a;
- const struct dive_site *b = (const struct dive_site *)*(void **)_b;
- return a->uuid > b->uuid ? 1 : a->uuid == b->uuid ? 0 : -1;
-}
-
-void dive_site_table_sort()
-{
- qsort(dive_site_table.dive_sites, dive_site_table.nr, sizeof(struct dive_site *), compare_sites);
-}
diff --git a/subsurface-core/divesite.cpp b/subsurface-core/divesite.cpp
deleted file mode 100644
index ae102a14b..000000000
--- a/subsurface-core/divesite.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#include "divesite.h"
-#include "pref.h"
-
-QString constructLocationTags(uint32_t ds_uuid)
-{
- QString locationTag;
- struct dive_site *ds = get_dive_site_by_uuid(ds_uuid);
-
- if (!ds || !ds->taxonomy.nr)
- return locationTag;
-
- locationTag = "<small><small>(tags: ";
- QString connector;
- for (int i = 0; i < 3; i++) {
- if (prefs.geocoding.category[i] == TC_NONE)
- continue;
- for (int j = 0; j < TC_NR_CATEGORIES; j++) {
- if (ds->taxonomy.category[j].category == prefs.geocoding.category[i]) {
- QString tag = ds->taxonomy.category[j].value;
- if (!tag.isEmpty()) {
- locationTag += connector + tag;
- connector = " / ";
- }
- break;
- }
- }
- }
-
- locationTag += ")</small></small>";
- return locationTag;
-}
diff --git a/subsurface-core/divesite.h b/subsurface-core/divesite.h
deleted file mode 100644
index f18b2e8e8..000000000
--- a/subsurface-core/divesite.h
+++ /dev/null
@@ -1,80 +0,0 @@
-#ifndef DIVESITE_H
-#define DIVESITE_H
-
-#include "units.h"
-#include "taxonomy.h"
-#include <stdlib.h>
-
-#ifdef __cplusplus
-#include <QString>
-extern "C" {
-#else
-#include <stdbool.h>
-#endif
-
-struct dive_site
-{
- uint32_t uuid;
- char *name;
- degrees_t latitude, longitude;
- char *description;
- char *notes;
- struct taxonomy_data taxonomy;
-};
-
-struct dive_site_table {
- int nr, allocated;
- struct dive_site **dive_sites;
-};
-
-extern struct dive_site_table dive_site_table;
-
-static inline struct dive_site *get_dive_site(int nr)
-{
- if (nr >= dive_site_table.nr || nr < 0)
- return NULL;
- return dive_site_table.dive_sites[nr];
-}
-
-/* iterate over each dive site */
-#define for_each_dive_site(_i, _x) \
- for ((_i) = 0; ((_x) = get_dive_site(_i)) != NULL; (_i)++)
-
-static inline struct dive_site *get_dive_site_by_uuid(uint32_t uuid)
-{
- int i;
- struct dive_site *ds;
- for_each_dive_site (i, ds)
- if (ds->uuid == uuid)
- return get_dive_site(i);
- return NULL;
-}
-
-void dive_site_table_sort();
-struct dive_site *alloc_or_get_dive_site(uint32_t uuid);
-int nr_of_dives_at_dive_site(uint32_t uuid, bool select_only);
-bool is_dive_site_used(uint32_t uuid, bool select_only);
-void delete_dive_site(uint32_t id);
-uint32_t create_dive_site(const char *name, timestamp_t divetime);
-uint32_t create_dive_site_from_current_dive(const char *name);
-uint32_t create_dive_site_with_gps(const char *name, degrees_t latitude, degrees_t longitude, timestamp_t divetime);
-uint32_t get_dive_site_uuid_by_name(const char *name, struct dive_site **dsp);
-uint32_t get_dive_site_uuid_by_gps(degrees_t latitude, degrees_t longitude, struct dive_site **dsp);
-uint32_t get_dive_site_uuid_by_gps_and_name(char *name, degrees_t latitude, degrees_t longitude);
-uint32_t get_dive_site_uuid_by_gps_proximity(degrees_t latitude, degrees_t longitude, int distance, struct dive_site **dsp);
-bool dive_site_is_empty(struct dive_site *ds);
-void copy_dive_site(struct dive_site *orig, struct dive_site *copy);
-void clear_dive_site(struct dive_site *ds);
-unsigned int get_distance(degrees_t lat1, degrees_t lon1, degrees_t lat2, degrees_t lon2);
-uint32_t find_or_create_dive_site_with_name(const char *name, timestamp_t divetime);
-void merge_dive_sites(uint32_t ref, uint32_t *uuids, int count);
-
-#define INVALID_DIVE_SITE_NAME "development use only - not a valid dive site name"
-
-#ifdef __cplusplus
-}
-QString constructLocationTags(uint32_t ds_uuid);
-
-#endif
-
-#endif // DIVESITE_H
diff --git a/subsurface-core/divesitehelpers.cpp b/subsurface-core/divesitehelpers.cpp
deleted file mode 100644
index 3542f96fa..000000000
--- a/subsurface-core/divesitehelpers.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-//
-// infrastructure to deal with dive sites
-//
-
-#include "divesitehelpers.h"
-
-#include "divesite.h"
-#include "helpers.h"
-#include "membuffer.h"
-#include <QJsonDocument>
-#include <QJsonArray>
-#include <QJsonObject>
-#include <QNetworkReply>
-#include <QNetworkRequest>
-#include <QNetworkAccessManager>
-#include <QUrlQuery>
-#include <QEventLoop>
-#include <QTimer>
-
-struct GeoLookupInfo {
- degrees_t lat;
- degrees_t lon;
- uint32_t uuid;
-};
-
-QVector<GeoLookupInfo> geo_lookup_data;
-
-ReverseGeoLookupThread* ReverseGeoLookupThread::instance() {
- static ReverseGeoLookupThread* self = new ReverseGeoLookupThread();
- return self;
-}
-
-ReverseGeoLookupThread::ReverseGeoLookupThread(QObject *obj) : QThread(obj)
-{
-}
-
-void ReverseGeoLookupThread::run() {
- if (geo_lookup_data.isEmpty())
- return;
-
- QNetworkRequest request;
- QNetworkAccessManager *rgl = new QNetworkAccessManager();
- QEventLoop loop;
- QString mapquestURL("http://open.mapquestapi.com/nominatim/v1/reverse.php?format=json&accept-language=%1&lat=%2&lon=%3");
- QString geonamesURL("http://api.geonames.org/findNearbyPlaceNameJSON?language=%1&lat=%2&lng=%3&radius=50&username=dirkhh");
- QString geonamesOceanURL("http://api.geonames.org/oceanJSON?language=%1&lat=%2&lng=%3&radius=50&username=dirkhh");
- QString divelogsURL("https://www.divelogs.de/mapsearch_divespotnames.php?lat=%1&lng=%2&radius=50");
- QTimer timer;
-
- request.setRawHeader("Accept", "text/json");
- request.setRawHeader("User-Agent", getUserAgent().toUtf8());
- connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
-
- Q_FOREACH (const GeoLookupInfo& info, geo_lookup_data ) {
- struct dive_site *ds = info.uuid ? get_dive_site_by_uuid(info.uuid) : &displayed_dive_site;
-
- // first check the findNearbyPlaces API from geonames - that should give us country, state, city
- request.setUrl(geonamesURL.arg(uiLanguage(NULL)).arg(info.lat.udeg / 1000000.0).arg(info.lon.udeg / 1000000.0));
-
- QNetworkReply *reply = rgl->get(request);
- timer.setSingleShot(true);
- connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
- timer.start(5000); // 5 secs. timeout
- loop.exec();
-
- if(timer.isActive()) {
- timer.stop();
- if(reply->error() > 0) {
- report_error("got error accessing geonames.org: %s", qPrintable(reply->errorString()));
- goto clear_reply;
- }
- int v = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
- if (v < 200 || v >= 300)
- goto clear_reply;
- QByteArray fullReply = reply->readAll();
- QJsonParseError errorObject;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(fullReply, &errorObject);
- if (errorObject.error != QJsonParseError::NoError) {
- report_error("error parsing geonames.org response: %s", qPrintable(errorObject.errorString()));
- goto clear_reply;
- }
- QJsonObject obj = jsonDoc.object();
- QVariant geoNamesObject = obj.value("geonames").toVariant();
- QVariantList geoNames = geoNamesObject.toList();
- if (geoNames.count() > 0) {
- QVariantMap firstData = geoNames.at(0).toMap();
- int ri = 0, l3 = -1, lt = -1;
- if (ds->taxonomy.category == NULL) {
- ds->taxonomy.category = alloc_taxonomy();
- } else {
- // clear out the data (except for the ocean data)
- int ocean;
- if ((ocean = taxonomy_index_for_category(&ds->taxonomy, TC_OCEAN)) > 0) {
- ds->taxonomy.category[0] = ds->taxonomy.category[ocean];
- ds->taxonomy.nr = 1;
- } else {
- // ocean is -1 if there is no such entry, and we didn't copy above
- // if ocean is 0, so the following gets us the correct count
- ds->taxonomy.nr = ocean + 1;
- }
- }
- // get all the data - OCEAN is special, so start at COUNTRY
- for (int j = TC_COUNTRY; j < TC_NR_CATEGORIES; j++) {
- if (firstData[taxonomy_api_names[j]].isValid()) {
- ds->taxonomy.category[ri].category = j;
- ds->taxonomy.category[ri].origin = taxonomy::GEOCODED;
- free((void*)ds->taxonomy.category[ri].value);
- ds->taxonomy.category[ri].value = copy_string(qPrintable(firstData[taxonomy_api_names[j]].toString()));
- ri++;
- }
- }
- ds->taxonomy.nr = ri;
- l3 = taxonomy_index_for_category(&ds->taxonomy, TC_ADMIN_L3);
- lt = taxonomy_index_for_category(&ds->taxonomy, TC_LOCALNAME);
- if (l3 == -1 && lt != -1) {
- // basically this means we did get a local name (what we call town), but just like most places
- // we didn't get an adminName_3 - which in some regions is the actual city that town belongs to,
- // then we copy the town into the city
- ds->taxonomy.category[ri].value = copy_string(ds->taxonomy.category[lt].value);
- ds->taxonomy.category[ri].origin = taxonomy::COPIED;
- ds->taxonomy.category[ri].category = TC_ADMIN_L3;
- ds->taxonomy.nr++;
- }
- mark_divelist_changed(true);
- } else {
- report_error("geonames.org did not provide reverse lookup information");
- qDebug() << "no reverse geo lookup; geonames returned\n" << fullReply;
- }
- } else {
- report_error("timeout accessing geonames.org");
- disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
- reply->abort();
- }
- // next check the oceans API to figure out the body of water
- request.setUrl(geonamesOceanURL.arg(uiLanguage(NULL)).arg(info.lat.udeg / 1000000.0).arg(info.lon.udeg / 1000000.0));
- reply = rgl->get(request);
- connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
- timer.start(5000); // 5 secs. timeout
- loop.exec();
- if(timer.isActive()) {
- timer.stop();
- if(reply->error() > 0) {
- report_error("got error accessing oceans API of geonames.org: %s", qPrintable(reply->errorString()));
- goto clear_reply;
- }
- int v = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
- if (v < 200 || v >= 300)
- goto clear_reply;
- QByteArray fullReply = reply->readAll();
- QJsonParseError errorObject;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(fullReply, &errorObject);
- if (errorObject.error != QJsonParseError::NoError) {
- report_error("error parsing geonames.org response: %s", qPrintable(errorObject.errorString()));
- goto clear_reply;
- }
- QJsonObject obj = jsonDoc.object();
- QVariant oceanObject = obj.value("ocean").toVariant();
- QVariantMap oceanName = oceanObject.toMap();
- if (oceanName["name"].isValid()) {
- int idx;
- if (ds->taxonomy.category == NULL)
- ds->taxonomy.category = alloc_taxonomy();
- idx = taxonomy_index_for_category(&ds->taxonomy, TC_OCEAN);
- if (idx == -1)
- idx = ds->taxonomy.nr;
- if (idx < TC_NR_CATEGORIES) {
- ds->taxonomy.category[idx].category = TC_OCEAN;
- ds->taxonomy.category[idx].origin = taxonomy::GEOCODED;
- ds->taxonomy.category[idx].value = copy_string(qPrintable(oceanName["name"].toString()));
- if (idx == ds->taxonomy.nr)
- ds->taxonomy.nr++;
- }
- mark_divelist_changed(true);
- }
- } else {
- report_error("timeout accessing geonames.org");
- disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
- reply->abort();
- }
-
-clear_reply:
- reply->deleteLater();
- }
- rgl->deleteLater();
-}
-
-void ReverseGeoLookupThread::lookup(dive_site *ds)
-{
- if (!ds)
- return;
- GeoLookupInfo info;
- info.lat = ds->latitude;
- info.lon = ds->longitude;
- info.uuid = ds->uuid;
-
- geo_lookup_data.clear();
- geo_lookup_data.append(info);
- run();
-}
-
-extern "C" void add_geo_information_for_lookup(degrees_t latitude, degrees_t longitude, uint32_t uuid) {
- GeoLookupInfo info;
- info.lat = latitude;
- info.lon = longitude;
- info.uuid = uuid;
-
- geo_lookup_data.append(info);
-}
diff --git a/subsurface-core/divesitehelpers.h b/subsurface-core/divesitehelpers.h
deleted file mode 100644
index a08069bc0..000000000
--- a/subsurface-core/divesitehelpers.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef DIVESITEHELPERS_H
-#define DIVESITEHELPERS_H
-
-#include "units.h"
-#include <QThread>
-
-class ReverseGeoLookupThread : public QThread {
-Q_OBJECT
-public:
- static ReverseGeoLookupThread *instance();
- void lookup(struct dive_site *ds);
- void run() Q_DECL_OVERRIDE;
-
-private:
- ReverseGeoLookupThread(QObject *parent = 0);
-};
-
-#endif // DIVESITEHELPERS_H
diff --git a/subsurface-core/equipment.c b/subsurface-core/equipment.c
deleted file mode 100644
index 9f3e49039..000000000
--- a/subsurface-core/equipment.c
+++ /dev/null
@@ -1,238 +0,0 @@
-// Clang has a bug on zero-initialization of C structs.
-#pragma clang diagnostic ignored "-Wmissing-field-initializers"
-
-/* equipment.c */
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <time.h>
-#include "gettext.h"
-#include "dive.h"
-#include "display.h"
-#include "divelist.h"
-
-/* placeholders for a few functions that we need to redesign for the Qt UI */
-void add_cylinder_description(cylinder_type_t *type)
-{
- const char *desc;
- int i;
-
- desc = type->description;
- if (!desc)
- return;
- for (i = 0; i < 100 && tank_info[i].name != NULL; i++) {
- if (strcmp(tank_info[i].name, desc) == 0)
- return;
- }
- if (i < 100) {
- // FIXME: leaked on exit
- tank_info[i].name = strdup(desc);
- tank_info[i].ml = type->size.mliter;
- tank_info[i].bar = type->workingpressure.mbar / 1000;
- }
-}
-void add_weightsystem_description(weightsystem_t *weightsystem)
-{
- const char *desc;
- int i;
-
- desc = weightsystem->description;
- if (!desc)
- return;
- for (i = 0; i < 100 && ws_info[i].name != NULL; i++) {
- if (strcmp(ws_info[i].name, desc) == 0) {
- ws_info[i].grams = weightsystem->weight.grams;
- return;
- }
- }
- if (i < 100) {
- // FIXME: leaked on exit
- ws_info[i].name = strdup(desc);
- ws_info[i].grams = weightsystem->weight.grams;
- }
-}
-
-bool cylinder_nodata(cylinder_t *cyl)
-{
- return !cyl->type.size.mliter &&
- !cyl->type.workingpressure.mbar &&
- !cyl->type.description &&
- !cyl->gasmix.o2.permille &&
- !cyl->gasmix.he.permille &&
- !cyl->start.mbar &&
- !cyl->end.mbar &&
- !cyl->gas_used.mliter &&
- !cyl->deco_gas_used.mliter;
-}
-
-static bool cylinder_nosamples(cylinder_t *cyl)
-{
- return !cyl->sample_start.mbar &&
- !cyl->sample_end.mbar;
-}
-
-bool cylinder_none(void *_data)
-{
- cylinder_t *cyl = _data;
- return cylinder_nodata(cyl) && cylinder_nosamples(cyl);
-}
-
-void get_gas_string(const struct gasmix *gasmix, char *text, int len)
-{
- if (gasmix_is_air(gasmix))
- snprintf(text, len, "%s", translate("gettextFromC", "air"));
- else if (get_he(gasmix) == 0 && get_o2(gasmix) < 1000)
- snprintf(text, len, translate("gettextFromC", "EAN%d"), (get_o2(gasmix) + 5) / 10);
- else if (get_he(gasmix) == 0 && get_o2(gasmix) == 1000)
- snprintf(text, len, "%s", translate("gettextFromC", "oxygen"));
- else
- snprintf(text, len, "(%d/%d)", (get_o2(gasmix) + 5) / 10, (get_he(gasmix) + 5) / 10);
-}
-
-/* Returns a static char buffer - only good for immediate use by printf etc */
-const char *gasname(const struct gasmix *gasmix)
-{
- static char gas[64];
- get_gas_string(gasmix, gas, sizeof(gas));
- return gas;
-}
-
-bool weightsystem_none(void *_data)
-{
- weightsystem_t *ws = _data;
- return !ws->weight.grams && !ws->description;
-}
-
-bool no_weightsystems(weightsystem_t *ws)
-{
- int i;
-
- for (i = 0; i < MAX_WEIGHTSYSTEMS; i++)
- if (!weightsystem_none(ws + i))
- return false;
- return true;
-}
-
-static bool one_weightsystem_equal(weightsystem_t *ws1, weightsystem_t *ws2)
-{
- return ws1->weight.grams == ws2->weight.grams &&
- same_string(ws1->description, ws2->description);
-}
-
-bool weightsystems_equal(weightsystem_t *ws1, weightsystem_t *ws2)
-{
- int i;
-
- for (i = 0; i < MAX_WEIGHTSYSTEMS; i++)
- if (!one_weightsystem_equal(ws1 + i, ws2 + i))
- return false;
- return true;
-}
-
-/*
- * We hardcode the most common standard cylinders,
- * we should pick up any other names from the dive
- * logs directly.
- */
-struct tank_info_t tank_info[100] = {
- /* Need an empty entry for the no-cylinder case */
- { "", },
-
- /* Size-only metric cylinders */
- { "10.0â„“", .ml = 10000 },
- { "11.1â„“", .ml = 11100 },
-
- /* Most common AL cylinders */
- { "AL40", .cuft = 40, .psi = 3000 },
- { "AL50", .cuft = 50, .psi = 3000 },
- { "AL63", .cuft = 63, .psi = 3000 },
- { "AL72", .cuft = 72, .psi = 3000 },
- { "AL80", .cuft = 80, .psi = 3000 },
- { "AL100", .cuft = 100, .psi = 3300 },
-
- /* Metric AL cylinders */
- { "ALU7", .ml = 7000, .bar = 200 },
-
- /* Somewhat common LP steel cylinders */
- { "LP85", .cuft = 85, .psi = 2640 },
- { "LP95", .cuft = 95, .psi = 2640 },
- { "LP108", .cuft = 108, .psi = 2640 },
- { "LP121", .cuft = 121, .psi = 2640 },
-
- /* Somewhat common HP steel cylinders */
- { "HP65", .cuft = 65, .psi = 3442 },
- { "HP80", .cuft = 80, .psi = 3442 },
- { "HP100", .cuft = 100, .psi = 3442 },
- { "HP119", .cuft = 119, .psi = 3442 },
- { "HP130", .cuft = 130, .psi = 3442 },
-
- /* Common European steel cylinders */
- { "3â„“ 232 bar", .ml = 3000, .bar = 232 },
- { "3â„“ 300 bar", .ml = 3000, .bar = 300 },
- { "10â„“ 300 bar", .ml = 10000, .bar = 300 },
- { "12â„“ 200 bar", .ml = 12000, .bar = 200 },
- { "12â„“ 232 bar", .ml = 12000, .bar = 232 },
- { "12â„“ 300 bar", .ml = 12000, .bar = 300 },
- { "15â„“ 200 bar", .ml = 15000, .bar = 200 },
- { "15â„“ 232 bar", .ml = 15000, .bar = 232 },
- { "D7 300 bar", .ml = 14000, .bar = 300 },
- { "D8.5 232 bar", .ml = 17000, .bar = 232 },
- { "D12 232 bar", .ml = 24000, .bar = 232 },
- { "D13 232 bar", .ml = 26000, .bar = 232 },
- { "D15 232 bar", .ml = 30000, .bar = 232 },
- { "D16 232 bar", .ml = 32000, .bar = 232 },
- { "D18 232 bar", .ml = 36000, .bar = 232 },
- { "D20 232 bar", .ml = 40000, .bar = 232 },
-
- /* We'll fill in more from the dive log dynamically */
- { NULL, }
-};
-
-/*
- * We hardcode the most common weight system types
- * This is a bit odd as the weight system types don't usually encode weight
- */
-struct ws_info_t ws_info[100] = {
- { QT_TRANSLATE_NOOP("gettextFromC", "integrated"), 0 },
- { QT_TRANSLATE_NOOP("gettextFromC", "belt"), 0 },
- { QT_TRANSLATE_NOOP("gettextFromC", "ankle"), 0 },
- { QT_TRANSLATE_NOOP("gettextFromC", "backplate weight"), 0 },
- { QT_TRANSLATE_NOOP("gettextFromC", "clip-on"), 0 },
-};
-
-void remove_cylinder(struct dive *dive, int idx)
-{
- cylinder_t *cyl = dive->cylinder + idx;
- int nr = MAX_CYLINDERS - idx - 1;
- memmove(cyl, cyl + 1, nr * sizeof(*cyl));
- memset(cyl + nr, 0, sizeof(*cyl));
-}
-
-void remove_weightsystem(struct dive *dive, int idx)
-{
- weightsystem_t *ws = dive->weightsystem + idx;
- int nr = MAX_WEIGHTSYSTEMS - idx - 1;
- memmove(ws, ws + 1, nr * sizeof(*ws));
- memset(ws + nr, 0, sizeof(*ws));
-}
-
-/* when planning a dive we need to make sure that all cylinders have a sane depth assigned
- * and if we are tracking gas consumption the pressures need to be reset to start = end = workingpressure */
-void reset_cylinders(struct dive *dive, bool track_gas)
-{
- int i;
- pressure_t decopo2 = {.mbar = prefs.decopo2};
-
- for (i = 0; i < MAX_CYLINDERS; i++) {
- cylinder_t *cyl = &dive->cylinder[i];
- if (cylinder_none(cyl))
- continue;
- if (cyl->depth.mm == 0) /* if the gas doesn't give a mod, calculate based on prefs */
- cyl->depth = gas_mod(&cyl->gasmix, decopo2, dive, M_OR_FT(3,10));
- if (track_gas)
- cyl->start.mbar = cyl->end.mbar = cyl->type.workingpressure.mbar;
- cyl->gas_used.mliter = 0;
- cyl->deco_gas_used.mliter = 0;
- }
-}
diff --git a/subsurface-core/exif.cpp b/subsurface-core/exif.cpp
deleted file mode 100644
index 0b1cda2bc..000000000
--- a/subsurface-core/exif.cpp
+++ /dev/null
@@ -1,587 +0,0 @@
-#include <stdio.h>
-/**************************************************************************
- exif.cpp -- A simple ISO C++ library to parse basic EXIF
- information from a JPEG file.
-
- Copyright (c) 2010-2013 Mayank Lahiri
- mlahiri@gmail.com
- All rights reserved (BSD License).
-
- See exif.h for version history.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- -- Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- -- Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS
- OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
- NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
- EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-#include <algorithm>
-#include "dive.h"
-#include "exif.h"
-
-using std::string;
-
-namespace {
- // IF Entry
- struct IFEntry {
- // Raw fields
- unsigned short tag;
- unsigned short format;
- unsigned data;
- unsigned length;
-
- // Parsed fields
- string val_string;
- unsigned short val_16;
- unsigned val_32;
- double val_rational;
- unsigned char val_byte;
- };
-
- // Helper functions
- unsigned int parse32(const unsigned char *buf, bool intel)
- {
- if (intel)
- return ((unsigned)buf[3] << 24) |
- ((unsigned)buf[2] << 16) |
- ((unsigned)buf[1] << 8) |
- buf[0];
-
- return ((unsigned)buf[0] << 24) |
- ((unsigned)buf[1] << 16) |
- ((unsigned)buf[2] << 8) |
- buf[3];
- }
-
- unsigned short parse16(const unsigned char *buf, bool intel)
- {
- if (intel)
- return ((unsigned)buf[1] << 8) | buf[0];
- return ((unsigned)buf[0] << 8) | buf[1];
- }
-
- string parseEXIFString(const unsigned char *buf,
- const unsigned num_components,
- const unsigned data,
- const unsigned base,
- const unsigned len)
- {
- string value;
- if (num_components <= 4)
- value.assign((const char *)&data, num_components);
- else {
- if (base + data + num_components <= len)
- value.assign((const char *)(buf + base + data), num_components);
- }
- return value;
- }
-
- double parseEXIFRational(const unsigned char *buf, bool intel)
- {
- double numerator = 0;
- double denominator = 1;
-
- numerator = (double)parse32(buf, intel);
- denominator = (double)parse32(buf + 4, intel);
- if (denominator < 1e-20)
- return 0;
- return numerator / denominator;
- }
-
- IFEntry parseIFEntry(const unsigned char *buf,
- const unsigned offs,
- const bool alignIntel,
- const unsigned base,
- const unsigned len)
- {
- IFEntry result;
-
- // Each directory entry is composed of:
- // 2 bytes: tag number (data field)
- // 2 bytes: data format
- // 4 bytes: number of components
- // 4 bytes: data value or offset to data value
- result.tag = parse16(buf + offs, alignIntel);
- result.format = parse16(buf + offs + 2, alignIntel);
- result.length = parse32(buf + offs + 4, alignIntel);
- result.data = parse32(buf + offs + 8, alignIntel);
-
- // Parse value in specified format
- switch (result.format) {
- case 1:
- result.val_byte = (unsigned char)*(buf + offs + 8);
- break;
- case 2:
- result.val_string = parseEXIFString(buf, result.length, result.data, base, len);
- break;
- case 3:
- result.val_16 = parse16((const unsigned char *)buf + offs + 8, alignIntel);
- break;
- case 4:
- result.val_32 = result.data;
- break;
- case 5:
- if (base + result.data + 8 <= len)
- result.val_rational = parseEXIFRational(buf + base + result.data, alignIntel);
- break;
- case 7:
- case 9:
- case 10:
- break;
- default:
- result.tag = 0xFF;
- }
- return result;
- }
-}
-
-//
-// Locates the EXIF segment and parses it using parseFromEXIFSegment
-//
-int EXIFInfo::parseFrom(const unsigned char *buf, unsigned len)
-{
- // Sanity check: all JPEG files start with 0xFFD8 and end with 0xFFD9
- // This check also ensures that the user has supplied a correct value for len.
- if (!buf || len < 4)
- return PARSE_EXIF_ERROR_NO_EXIF;
- if (buf[0] != 0xFF || buf[1] != 0xD8)
- return PARSE_EXIF_ERROR_NO_JPEG;
- if (buf[len - 2] != 0xFF || buf[len - 1] != 0xD9)
- return PARSE_EXIF_ERROR_NO_JPEG;
- clear();
-
- // Scan for EXIF header (bytes 0xFF 0xE1) and do a sanity check by
- // looking for bytes "Exif\0\0". The marker length data is in Motorola
- // byte order, which results in the 'false' parameter to parse16().
- // The marker has to contain at least the TIFF header, otherwise the
- // EXIF data is corrupt. So the minimum length specified here has to be:
- // 2 bytes: section size
- // 6 bytes: "Exif\0\0" string
- // 2 bytes: TIFF header (either "II" or "MM" string)
- // 2 bytes: TIFF magic (short 0x2a00 in Motorola byte order)
- // 4 bytes: Offset to first IFD
- // =========
- // 16 bytes
- unsigned offs = 0; // current offset into buffer
- for (offs = 0; offs < len - 1; offs++)
- if (buf[offs] == 0xFF && buf[offs + 1] == 0xE1)
- break;
- if (offs + 4 > len)
- return PARSE_EXIF_ERROR_NO_EXIF;
- offs += 2;
- unsigned short section_length = parse16(buf + offs, false);
- if (offs + section_length > len || section_length < 16)
- return PARSE_EXIF_ERROR_CORRUPT;
- offs += 2;
-
- return parseFromEXIFSegment(buf + offs, len - offs);
-}
-
-int EXIFInfo::parseFrom(const string &data)
-{
- return parseFrom((const unsigned char *)data.data(), data.length());
-}
-
-//
-// Main parsing function for an EXIF segment.
-//
-// PARAM: 'buf' start of the EXIF TIFF, which must be the bytes "Exif\0\0".
-// PARAM: 'len' length of buffer
-//
-int EXIFInfo::parseFromEXIFSegment(const unsigned char *buf, unsigned len)
-{
- bool alignIntel = true; // byte alignment (defined in EXIF header)
- unsigned offs = 0; // current offset into buffer
- if (!buf || len < 6)
- return PARSE_EXIF_ERROR_NO_EXIF;
-
- if (!std::equal(buf, buf + 6, "Exif\0\0"))
- return PARSE_EXIF_ERROR_NO_EXIF;
- offs += 6;
-
- // Now parsing the TIFF header. The first two bytes are either "II" or
- // "MM" for Intel or Motorola byte alignment. Sanity check by parsing
- // the unsigned short that follows, making sure it equals 0x2a. The
- // last 4 bytes are an offset into the first IFD, which are added to
- // the global offset counter. For this block, we expect the following
- // minimum size:
- // 2 bytes: 'II' or 'MM'
- // 2 bytes: 0x002a
- // 4 bytes: offset to first IDF
- // -----------------------------
- // 8 bytes
- if (offs + 8 > len)
- return PARSE_EXIF_ERROR_CORRUPT;
- unsigned tiff_header_start = offs;
- if (buf[offs] == 'I' && buf[offs + 1] == 'I')
- alignIntel = true;
- else {
- if (buf[offs] == 'M' && buf[offs + 1] == 'M')
- alignIntel = false;
- else
- return PARSE_EXIF_ERROR_UNKNOWN_BYTEALIGN;
- }
- this->ByteAlign = alignIntel;
- offs += 2;
- if (0x2a != parse16(buf + offs, alignIntel))
- return PARSE_EXIF_ERROR_CORRUPT;
- offs += 2;
- unsigned first_ifd_offset = parse32(buf + offs, alignIntel);
- offs += first_ifd_offset - 4;
- if (offs >= len)
- return PARSE_EXIF_ERROR_CORRUPT;
-
- // Now parsing the first Image File Directory (IFD0, for the main image).
- // An IFD consists of a variable number of 12-byte directory entries. The
- // first two bytes of the IFD section contain the number of directory
- // entries in the section. The last 4 bytes of the IFD contain an offset
- // to the next IFD, which means this IFD must contain exactly 6 + 12 * num
- // bytes of data.
- if (offs + 2 > len)
- return PARSE_EXIF_ERROR_CORRUPT;
- int num_entries = parse16(buf + offs, alignIntel);
- if (offs + 6 + 12 * num_entries > len)
- return PARSE_EXIF_ERROR_CORRUPT;
- offs += 2;
- unsigned exif_sub_ifd_offset = len;
- unsigned gps_sub_ifd_offset = len;
- while (--num_entries >= 0) {
- IFEntry result = parseIFEntry(buf, offs, alignIntel, tiff_header_start, len);
- offs += 12;
- switch (result.tag) {
- case 0x102:
- // Bits per sample
- if (result.format == 3)
- this->BitsPerSample = result.val_16;
- break;
-
- case 0x10E:
- // Image description
- if (result.format == 2)
- this->ImageDescription = result.val_string;
- break;
-
- case 0x10F:
- // Digicam make
- if (result.format == 2)
- this->Make = result.val_string;
- break;
-
- case 0x110:
- // Digicam model
- if (result.format == 2)
- this->Model = result.val_string;
- break;
-
- case 0x112:
- // Orientation of image
- if (result.format == 3)
- this->Orientation = result.val_16;
- break;
-
- case 0x131:
- // Software used for image
- if (result.format == 2)
- this->Software = result.val_string;
- break;
-
- case 0x132:
- // EXIF/TIFF date/time of image modification
- if (result.format == 2)
- this->DateTime = result.val_string;
- break;
-
- case 0x8298:
- // Copyright information
- if (result.format == 2)
- this->Copyright = result.val_string;
- break;
-
- case 0x8825:
- // GPS IFS offset
- gps_sub_ifd_offset = tiff_header_start + result.data;
- break;
-
- case 0x8769:
- // EXIF SubIFD offset
- exif_sub_ifd_offset = tiff_header_start + result.data;
- break;
- }
- }
-
- // Jump to the EXIF SubIFD if it exists and parse all the information
- // there. Note that it's possible that the EXIF SubIFD doesn't exist.
- // The EXIF SubIFD contains most of the interesting information that a
- // typical user might want.
- if (exif_sub_ifd_offset + 4 <= len) {
- offs = exif_sub_ifd_offset;
- int num_entries = parse16(buf + offs, alignIntel);
- if (offs + 6 + 12 * num_entries > len)
- return PARSE_EXIF_ERROR_CORRUPT;
- offs += 2;
- while (--num_entries >= 0) {
- IFEntry result = parseIFEntry(buf, offs, alignIntel, tiff_header_start, len);
- switch (result.tag) {
- case 0x829a:
- // Exposure time in seconds
- if (result.format == 5)
- this->ExposureTime = result.val_rational;
- break;
-
- case 0x829d:
- // FNumber
- if (result.format == 5)
- this->FNumber = result.val_rational;
- break;
-
- case 0x8827:
- // ISO Speed Rating
- if (result.format == 3)
- this->ISOSpeedRatings = result.val_16;
- break;
-
- case 0x9003:
- // Original date and time
- if (result.format == 2)
- this->DateTimeOriginal = result.val_string;
- break;
-
- case 0x9004:
- // Digitization date and time
- if (result.format == 2)
- this->DateTimeDigitized = result.val_string;
- break;
-
- case 0x9201:
- // Shutter speed value
- if (result.format == 5)
- this->ShutterSpeedValue = result.val_rational;
- break;
-
- case 0x9204:
- // Exposure bias value
- if (result.format == 5)
- this->ExposureBiasValue = result.val_rational;
- break;
-
- case 0x9206:
- // Subject distance
- if (result.format == 5)
- this->SubjectDistance = result.val_rational;
- break;
-
- case 0x9209:
- // Flash used
- if (result.format == 3)
- this->Flash = result.data ? 1 : 0;
- break;
-
- case 0x920a:
- // Focal length
- if (result.format == 5)
- this->FocalLength = result.val_rational;
- break;
-
- case 0x9207:
- // Metering mode
- if (result.format == 3)
- this->MeteringMode = result.val_16;
- break;
-
- case 0x9291:
- // Subsecond original time
- if (result.format == 2)
- this->SubSecTimeOriginal = result.val_string;
- break;
-
- case 0xa002:
- // EXIF Image width
- if (result.format == 4)
- this->ImageWidth = result.val_32;
- if (result.format == 3)
- this->ImageWidth = result.val_16;
- break;
-
- case 0xa003:
- // EXIF Image height
- if (result.format == 4)
- this->ImageHeight = result.val_32;
- if (result.format == 3)
- this->ImageHeight = result.val_16;
- break;
-
- case 0xa405:
- // Focal length in 35mm film
- if (result.format == 3)
- this->FocalLengthIn35mm = result.val_16;
- break;
- }
- offs += 12;
- }
- }
-
- // Jump to the GPS SubIFD if it exists and parse all the information
- // there. Note that it's possible that the GPS SubIFD doesn't exist.
- if (gps_sub_ifd_offset + 4 <= len) {
- offs = gps_sub_ifd_offset;
- int num_entries = parse16(buf + offs, alignIntel);
- if (offs + 6 + 12 * num_entries > len)
- return PARSE_EXIF_ERROR_CORRUPT;
- offs += 2;
- while (--num_entries >= 0) {
- unsigned short tag = parse16(buf + offs, alignIntel);
- unsigned short format = parse16(buf + offs + 2, alignIntel);
- unsigned length = parse32(buf + offs + 4, alignIntel);
- unsigned data = parse32(buf + offs + 8, alignIntel);
- switch (tag) {
- case 1:
- // GPS north or south
- this->GeoLocation.LatComponents.direction = *(buf + offs + 8);
- if ('S' == this->GeoLocation.LatComponents.direction)
- this->GeoLocation.Latitude = -this->GeoLocation.Latitude;
- break;
-
- case 2:
- // GPS latitude
- if (format == 5 && length == 3) {
- this->GeoLocation.LatComponents.degrees =
- parseEXIFRational(buf + data + tiff_header_start, alignIntel);
- this->GeoLocation.LatComponents.minutes =
- parseEXIFRational(buf + data + tiff_header_start + 8, alignIntel);
- this->GeoLocation.LatComponents.seconds =
- parseEXIFRational(buf + data + tiff_header_start + 16, alignIntel);
- this->GeoLocation.Latitude =
- this->GeoLocation.LatComponents.degrees +
- this->GeoLocation.LatComponents.minutes / 60 +
- this->GeoLocation.LatComponents.seconds / 3600;
- if ('S' == this->GeoLocation.LatComponents.direction)
- this->GeoLocation.Latitude = -this->GeoLocation.Latitude;
- }
- break;
-
- case 3:
- // GPS east or west
- this->GeoLocation.LonComponents.direction = *(buf + offs + 8);
- if ('W' == this->GeoLocation.LonComponents.direction)
- this->GeoLocation.Longitude = -this->GeoLocation.Longitude;
- break;
-
- case 4:
- // GPS longitude
- if (format == 5 && length == 3) {
- this->GeoLocation.LonComponents.degrees =
- parseEXIFRational(buf + data + tiff_header_start, alignIntel);
- this->GeoLocation.LonComponents.minutes =
- parseEXIFRational(buf + data + tiff_header_start + 8, alignIntel);
- this->GeoLocation.LonComponents.seconds =
- parseEXIFRational(buf + data + tiff_header_start + 16, alignIntel);
- this->GeoLocation.Longitude =
- this->GeoLocation.LonComponents.degrees +
- this->GeoLocation.LonComponents.minutes / 60 +
- this->GeoLocation.LonComponents.seconds / 3600;
- if ('W' == this->GeoLocation.LonComponents.direction)
- this->GeoLocation.Longitude = -this->GeoLocation.Longitude;
- }
- break;
-
- case 5:
- // GPS altitude reference (below or above sea level)
- this->GeoLocation.AltitudeRef = *(buf + offs + 8);
- if (1 == this->GeoLocation.AltitudeRef)
- this->GeoLocation.Altitude = -this->GeoLocation.Altitude;
- break;
-
- case 6:
- // GPS altitude reference
- if (format == 5) {
- this->GeoLocation.Altitude =
- parseEXIFRational(buf + data + tiff_header_start, alignIntel);
- if (1 == this->GeoLocation.AltitudeRef)
- this->GeoLocation.Altitude = -this->GeoLocation.Altitude;
- }
- break;
- }
- offs += 12;
- }
- }
-
- return PARSE_EXIF_SUCCESS;
-}
-
-void EXIFInfo::clear()
-{
- // Strings
- ImageDescription.clear();
- Make.clear();
- Model.clear();
- Software.clear();
- DateTime.clear();
- DateTimeOriginal.clear();
- DateTimeDigitized.clear();
- SubSecTimeOriginal.clear();
- Copyright.clear();
-
- // Shorts / unsigned / double
- ByteAlign = 0;
- Orientation = 0;
-
- BitsPerSample = 0;
- ExposureTime = 0;
- FNumber = 0;
- ISOSpeedRatings = 0;
- ShutterSpeedValue = 0;
- ExposureBiasValue = 0;
- SubjectDistance = 0;
- FocalLength = 0;
- FocalLengthIn35mm = 0;
- Flash = 0;
- MeteringMode = 0;
- ImageWidth = 0;
- ImageHeight = 0;
-
- // Geolocation
- GeoLocation.Latitude = 0;
- GeoLocation.Longitude = 0;
- GeoLocation.Altitude = 0;
- GeoLocation.AltitudeRef = 0;
- GeoLocation.LatComponents.degrees = 0;
- GeoLocation.LatComponents.minutes = 0;
- GeoLocation.LatComponents.seconds = 0;
- GeoLocation.LatComponents.direction = 0;
- GeoLocation.LonComponents.degrees = 0;
- GeoLocation.LonComponents.minutes = 0;
- GeoLocation.LonComponents.seconds = 0;
- GeoLocation.LonComponents.direction = 0;
-}
-
-time_t EXIFInfo::epoch()
-{
- struct tm tm;
- int year, month, day, hour, min, sec;
-
- if (DateTimeOriginal.size())
- sscanf(DateTimeOriginal.c_str(), "%d:%d:%d %d:%d:%d", &year, &month, &day, &hour, &min, &sec);
- else
- sscanf(DateTime.c_str(), "%d:%d:%d %d:%d:%d", &year, &month, &day, &hour, &min, &sec);
- tm.tm_year = year;
- tm.tm_mon = month - 1;
- tm.tm_mday = day;
- tm.tm_hour = hour;
- tm.tm_min = min;
- tm.tm_sec = sec;
- return (utc_mktime(&tm));
-}
diff --git a/subsurface-core/exif.h b/subsurface-core/exif.h
deleted file mode 100644
index 0fb3a7d4a..000000000
--- a/subsurface-core/exif.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/**************************************************************************
- exif.h -- A simple ISO C++ library to parse basic EXIF
- information from a JPEG file.
-
- Based on the description of the EXIF file format at:
- -- http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
- -- http://www.media.mit.edu/pia/Research/deepview/exif.html
- -- http://www.exif.org/Exif2-2.PDF
-
- Copyright (c) 2010-2013 Mayank Lahiri
- mlahiri@gmail.com
- All rights reserved.
-
- VERSION HISTORY:
- ================
-
- 2.1: Released July 2013
- -- fixed a bug where JPEGs without an EXIF SubIFD would not be parsed
- -- fixed a bug in parsing GPS coordinate seconds
- -- fixed makefile bug
- -- added two pathological test images from Matt Galloway
- http://www.galloway.me.uk/2012/01/uiimageorientation-exif-orientation-sample-images/
- -- split main parsing routine for easier integration into Firefox
-
- 2.0: Released February 2013
- -- complete rewrite
- -- no new/delete
- -- added GPS support
-
- 1.0: Released 2010
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- -- Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- -- Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS
- OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
- NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
- EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-#ifndef EXIF_H
-#define EXIF_H
-
-#include <string>
-
-//
-// Class responsible for storing and parsing EXIF information from a JPEG blob
-//
-class EXIFInfo {
-public:
- // Parsing function for an entire JPEG image buffer.
- //
- // PARAM 'data': A pointer to a JPEG image.
- // PARAM 'length': The length of the JPEG image.
- // RETURN: PARSE_EXIF_SUCCESS (0) on succes with 'result' filled out
- // error code otherwise, as defined by the PARSE_EXIF_ERROR_* macros
- int parseFrom(const unsigned char *data, unsigned length);
- int parseFrom(const std::string &data);
-
- // Parsing function for an EXIF segment. This is used internally by parseFrom()
- // but can be called for special cases where only the EXIF section is
- // available (i.e., a blob starting with the bytes "Exif\0\0").
- int parseFromEXIFSegment(const unsigned char *buf, unsigned len);
-
- // Set all data members to default values.
- void clear();
-
- // Data fields filled out by parseFrom()
- char ByteAlign; // 0 = Motorola byte alignment, 1 = Intel
- std::string ImageDescription; // Image description
- std::string Make; // Camera manufacturer's name
- std::string Model; // Camera model
- unsigned short Orientation; // Image orientation, start of data corresponds to
- // 0: unspecified in EXIF data
- // 1: upper left of image
- // 3: lower right of image
- // 6: upper right of image
- // 8: lower left of image
- // 9: undefined
- unsigned short BitsPerSample; // Number of bits per component
- std::string Software; // Software used
- std::string DateTime; // File change date and time
- std::string DateTimeOriginal; // Original file date and time (may not exist)
- std::string DateTimeDigitized; // Digitization date and time (may not exist)
- std::string SubSecTimeOriginal; // Sub-second time that original picture was taken
- std::string Copyright; // File copyright information
- double ExposureTime; // Exposure time in seconds
- double FNumber; // F/stop
- unsigned short ISOSpeedRatings; // ISO speed
- double ShutterSpeedValue; // Shutter speed (reciprocal of exposure time)
- double ExposureBiasValue; // Exposure bias value in EV
- double SubjectDistance; // Distance to focus point in meters
- double FocalLength; // Focal length of lens in millimeters
- unsigned short FocalLengthIn35mm; // Focal length in 35mm film
- char Flash; // 0 = no flash, 1 = flash used
- unsigned short MeteringMode; // Metering mode
- // 1: average
- // 2: center weighted average
- // 3: spot
- // 4: multi-spot
- // 5: multi-segment
- unsigned ImageWidth; // Image width reported in EXIF data
- unsigned ImageHeight; // Image height reported in EXIF data
- struct Geolocation_t
- { // GPS information embedded in file
- double Latitude; // Image latitude expressed as decimal
- double Longitude; // Image longitude expressed as decimal
- double Altitude; // Altitude in meters, relative to sea level
- char AltitudeRef; // 0 = above sea level, -1 = below sea level
- struct Coord_t {
- double degrees;
- double minutes;
- double seconds;
- char direction;
- } LatComponents, LonComponents; // Latitude, Longitude expressed in deg/min/sec
- } GeoLocation;
- EXIFInfo()
- {
- clear();
- }
-
- time_t epoch();
-};
-
-// Parse was successful
-#define PARSE_EXIF_SUCCESS 0
-// No JPEG markers found in buffer, possibly invalid JPEG file
-#define PARSE_EXIF_ERROR_NO_JPEG 1982
-// No EXIF header found in JPEG file.
-#define PARSE_EXIF_ERROR_NO_EXIF 1983
-// Byte alignment specified in EXIF file was unknown (not Motorola or Intel).
-#define PARSE_EXIF_ERROR_UNKNOWN_BYTEALIGN 1984
-// EXIF header was found, but data was corrupted.
-#define PARSE_EXIF_ERROR_CORRUPT 1985
-
-#endif // EXIF_H
diff --git a/subsurface-core/file.c b/subsurface-core/file.c
deleted file mode 100644
index 1337da3a2..000000000
--- a/subsurface-core/file.c
+++ /dev/null
@@ -1,1115 +0,0 @@
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include "gettext.h"
-#include <zip.h>
-#include <time.h>
-
-#include "dive.h"
-#include "divelist.h"
-#include "file.h"
-#include "git-access.h"
-#include "qthelperfromc.h"
-
-/* For SAMPLE_* */
-#include <libdivecomputer/parser.h>
-
-/* to check XSLT version number */
-#include <libxslt/xsltconfig.h>
-
-/* Crazy windows sh*t */
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-int readfile(const char *filename, struct memblock *mem)
-{
- int ret, fd;
- struct stat st;
- char *buf;
-
- mem->buffer = NULL;
- mem->size = 0;
-
- fd = subsurface_open(filename, O_RDONLY | O_BINARY, 0);
- if (fd < 0)
- return fd;
- ret = fstat(fd, &st);
- if (ret < 0)
- goto out;
- ret = -EINVAL;
- if (!S_ISREG(st.st_mode))
- goto out;
- ret = 0;
- if (!st.st_size)
- goto out;
- buf = malloc(st.st_size + 1);
- ret = -1;
- errno = ENOMEM;
- if (!buf)
- goto out;
- mem->buffer = buf;
- mem->size = st.st_size;
- ret = read(fd, buf, mem->size);
- if (ret < 0)
- goto free;
- buf[ret] = 0;
- if (ret == (int)mem->size) // converting to int loses a bit but size will never be that big
- goto out;
- errno = EIO;
- ret = -1;
-free:
- free(mem->buffer);
- mem->buffer = NULL;
- mem->size = 0;
-out:
- close(fd);
- return ret;
-}
-
-
-static void zip_read(struct zip_file *file, const char *filename)
-{
- int size = 1024, n, read = 0;
- char *mem = malloc(size);
-
- while ((n = zip_fread(file, mem + read, size - read)) > 0) {
- read += n;
- size = read * 3 / 2;
- mem = realloc(mem, size);
- }
- mem[read] = 0;
- (void) parse_xml_buffer(filename, mem, read, &dive_table, NULL);
- free(mem);
-}
-
-int try_to_open_zip(const char *filename)
-{
- int success = 0;
- /* Grr. libzip needs to re-open the file, it can't take a buffer */
- struct zip *zip = subsurface_zip_open_readonly(filename, ZIP_CHECKCONS, NULL);
-
- if (zip) {
- int index;
- for (index = 0;; index++) {
- struct zip_file *file = zip_fopen_index(zip, index, 0);
- if (!file)
- break;
- /* skip parsing the divelogs.de pictures */
- if (strstr(zip_get_name(zip, index, 0), "pictures/"))
- continue;
- zip_read(file, filename);
- zip_fclose(file);
- success++;
- }
- subsurface_zip_close(zip);
-
- if (!success)
- return report_error(translate("gettextFromC", "No dives in the input file '%s'"), filename);
- }
- return success;
-}
-
-static int try_to_xslt_open_csv(const char *filename, struct memblock *mem, const char *tag)
-{
- char *buf;
-
- if (mem->size == 0 && readfile(filename, mem) < 0)
- return report_error(translate("gettextFromC", "Failed to read '%s'"), filename);
-
- /* Surround the CSV file content with XML tags to enable XSLT
- * parsing
- *
- * Tag markers take: strlen("<></>") = 5
- */
- buf = realloc(mem->buffer, mem->size + 7 + strlen(tag) * 2);
- if (buf != NULL) {
- char *starttag = NULL;
- char *endtag = NULL;
-
- starttag = malloc(3 + strlen(tag));
- endtag = malloc(5 + strlen(tag));
-
- if (starttag == NULL || endtag == NULL) {
- /* this is fairly silly - so the malloc fails, but we strdup the error?
- * let's complete the silliness by freeing the two pointers in case one malloc succeeded
- * and the other one failed - this will make static analysis tools happy */
- free(starttag);
- free(endtag);
- free(buf);
- return report_error("Memory allocation failed in %s", __func__);
- }
-
- sprintf(starttag, "<%s>", tag);
- sprintf(endtag, "\n</%s>", tag);
-
- memmove(buf + 2 + strlen(tag), buf, mem->size);
- memcpy(buf, starttag, 2 + strlen(tag));
- memcpy(buf + mem->size + 2 + strlen(tag), endtag, 5 + strlen(tag));
- mem->size += (6 + 2 * strlen(tag));
- mem->buffer = buf;
-
- free(starttag);
- free(endtag);
- } else {
- free(mem->buffer);
- return report_error("realloc failed in %s", __func__);
- }
-
- return 0;
-}
-
-int db_test_func(void *param, int columns, char **data, char **column)
-{
- (void) param;
- (void) columns;
- (void) column;
- return *data[0] == '0';
-}
-
-
-static int try_to_open_db(const char *filename, struct memblock *mem)
-{
- sqlite3 *handle;
- char dm4_test[] = "select count(*) from sqlite_master where type='table' and name='Dive' and sql like '%ProfileBlob%'";
- char dm5_test[] = "select count(*) from sqlite_master where type='table' and name='Dive' and sql like '%SampleBlob%'";
- char shearwater_test[] = "select count(*) from sqlite_master where type='table' and name='system' and sql like '%dbVersion%'";
- char cobalt_test[] = "select count(*) from sqlite_master where type='table' and name='TrackPoints' and sql like '%DepthPressure%'";
- char divinglog_test[] = "select count(*) from sqlite_master where type='table' and name='DBInfo' and sql like '%PrgName%'";
- int retval;
-
- retval = sqlite3_open(filename, &handle);
-
- if (retval) {
- fprintf(stderr, "Database connection failed '%s'.\n", filename);
- return 1;
- }
-
- /* Testing if DB schema resembles Suunto DM5 database format */
- retval = sqlite3_exec(handle, dm5_test, &db_test_func, 0, NULL);
- if (!retval) {
- retval = parse_dm5_buffer(handle, filename, mem->buffer, mem->size, &dive_table);
- sqlite3_close(handle);
- return retval;
- }
-
- /* Testing if DB schema resembles Suunto DM4 database format */
- retval = sqlite3_exec(handle, dm4_test, &db_test_func, 0, NULL);
- if (!retval) {
- retval = parse_dm4_buffer(handle, filename, mem->buffer, mem->size, &dive_table);
- sqlite3_close(handle);
- return retval;
- }
-
- /* Testing if DB schema resembles Shearwater database format */
- retval = sqlite3_exec(handle, shearwater_test, &db_test_func, 0, NULL);
- if (!retval) {
- retval = parse_shearwater_buffer(handle, filename, mem->buffer, mem->size, &dive_table);
- sqlite3_close(handle);
- return retval;
- }
-
- /* Testing if DB schema resembles Atomic Cobalt database format */
- retval = sqlite3_exec(handle, cobalt_test, &db_test_func, 0, NULL);
- if (!retval) {
- retval = parse_cobalt_buffer(handle, filename, mem->buffer, mem->size, &dive_table);
- sqlite3_close(handle);
- return retval;
- }
-
- /* Testing if DB schema resembles Divinglog database format */
- retval = sqlite3_exec(handle, divinglog_test, &db_test_func, 0, NULL);
- if (!retval) {
- retval = parse_divinglog_buffer(handle, filename, mem->buffer, mem->size, &dive_table);
- sqlite3_close(handle);
- return retval;
- }
-
- sqlite3_close(handle);
-
- return retval;
-}
-
-timestamp_t parse_date(const char *date)
-{
- int hour, min, sec;
- struct tm tm;
- char *p;
-
- memset(&tm, 0, sizeof(tm));
- tm.tm_mday = strtol(date, &p, 10);
- if (tm.tm_mday < 1 || tm.tm_mday > 31)
- return 0;
- for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++) {
- if (!memcmp(p, monthname(tm.tm_mon), 3))
- break;
- }
- if (tm.tm_mon > 11)
- return 0;
- date = p + 3;
- tm.tm_year = strtol(date, &p, 10);
- if (date == p)
- return 0;
- if (tm.tm_year < 70)
- tm.tm_year += 2000;
- if (tm.tm_year < 100)
- tm.tm_year += 1900;
- if (sscanf(p, "%d:%d:%d", &hour, &min, &sec) != 3)
- return 0;
- tm.tm_hour = hour;
- tm.tm_min = min;
- tm.tm_sec = sec;
- return utc_mktime(&tm);
-}
-
-enum csv_format {
- CSV_DEPTH,
- CSV_TEMP,
- CSV_PRESSURE,
- POSEIDON_DEPTH,
- POSEIDON_TEMP,
- POSEIDON_SETPOINT,
- POSEIDON_SENSOR1,
- POSEIDON_SENSOR2,
- POSEIDON_PRESSURE,
- POSEIDON_O2CYLINDER,
- POSEIDON_NDL,
- POSEIDON_CEILING
-};
-
-static void add_sample_data(struct sample *sample, enum csv_format type, double val)
-{
- switch (type) {
- case CSV_DEPTH:
- sample->depth.mm = feet_to_mm(val);
- break;
- case CSV_TEMP:
- sample->temperature.mkelvin = F_to_mkelvin(val);
- break;
- case CSV_PRESSURE:
- sample->cylinderpressure.mbar = psi_to_mbar(val * 4);
- break;
- case POSEIDON_DEPTH:
- sample->depth.mm = val * 0.5 *1000;
- break;
- case POSEIDON_TEMP:
- sample->temperature.mkelvin = C_to_mkelvin(val * 0.2);
- break;
- case POSEIDON_SETPOINT:
- sample->setpoint.mbar = val * 10;
- break;
- case POSEIDON_SENSOR1:
- sample->o2sensor[0].mbar = val * 10;
- break;
- case POSEIDON_SENSOR2:
- sample->o2sensor[1].mbar = val * 10;
- break;
- case POSEIDON_PRESSURE:
- sample->cylinderpressure.mbar = val * 1000;
- break;
- case POSEIDON_O2CYLINDER:
- sample->o2cylinderpressure.mbar = val * 1000;
- break;
- case POSEIDON_NDL:
- sample->ndl.seconds = val * 60;
- break;
- case POSEIDON_CEILING:
- sample->stopdepth.mm = val * 1000;
- break;
- }
-}
-
-/*
- * Cochran comma-separated values: depth in feet, temperature in F, pressure in psi.
- *
- * They start with eight comma-separated fields like:
- *
- * filename: {C:\Analyst4\can\T036785.can},{C:\Analyst4\can\K031892.can}
- * divenr: %d
- * datetime: {03Sep11 16:37:22},{15Dec11 18:27:02}
- * ??: 1
- * serialnr??: {CCI134},{CCI207}
- * computer??: {GeminiII},{CommanderIII}
- * computer??: {GeminiII},{CommanderIII}
- * ??: 1
- *
- * Followed by the data values (all comma-separated, all one long line).
- */
-static int try_to_open_csv(struct memblock *mem, enum csv_format type)
-{
- char *p = mem->buffer;
- char *header[8];
- int i, time;
- timestamp_t date;
- struct dive *dive;
- struct divecomputer *dc;
-
- for (i = 0; i < 8; i++) {
- header[i] = p;
- p = strchr(p, ',');
- if (!p)
- return 0;
- p++;
- }
-
- date = parse_date(header[2]);
- if (!date)
- return 0;
-
- dive = alloc_dive();
- dive->when = date;
- dive->number = atoi(header[1]);
- dc = &dive->dc;
-
- time = 0;
- for (;;) {
- char *end;
- double val;
- struct sample *sample;
-
- errno = 0;
- val = strtod(p, &end); // FIXME == localization issue
- if (end == p)
- break;
- if (errno)
- break;
-
- sample = prepare_sample(dc);
- sample->time.seconds = time;
- add_sample_data(sample, type, val);
- finish_sample(dc);
-
- time++;
- dc->duration.seconds = time;
- if (*end != ',')
- break;
- p = end + 1;
- }
- record_dive(dive);
- return 1;
-}
-
-static int open_by_filename(const char *filename, const char *fmt, struct memblock *mem)
-{
- // hack to be able to provide a comment for the translated string
- static char *csv_warning = QT_TRANSLATE_NOOP3("gettextFromC",
- "Cannot open CSV file %s; please use Import log file dialog",
- "'Import log file' should be the same text as corresponding label in Import menu");
-
- /* Suunto Dive Manager files: SDE, ZIP; divelogs.de files: DLD */
- if (!strcasecmp(fmt, "SDE") || !strcasecmp(fmt, "ZIP") || !strcasecmp(fmt, "DLD"))
- return try_to_open_zip(filename);
-
- /* CSV files */
- if (!strcasecmp(fmt, "CSV"))
- return report_error(translate("gettextFromC", csv_warning), filename);
- /* Truly nasty intentionally obfuscated Cochran Anal software */
- if (!strcasecmp(fmt, "CAN"))
- return try_to_open_cochran(filename, mem);
- /* Cochran export comma-separated-value files */
- if (!strcasecmp(fmt, "DPT"))
- return try_to_open_csv(mem, CSV_DEPTH);
- if (!strcasecmp(fmt, "LVD"))
- return try_to_open_liquivision(filename, mem);
- if (!strcasecmp(fmt, "TMP"))
- return try_to_open_csv(mem, CSV_TEMP);
- if (!strcasecmp(fmt, "HP1"))
- return try_to_open_csv(mem, CSV_PRESSURE);
-
- return 0;
-}
-
-static int parse_file_buffer(const char *filename, struct memblock *mem)
-{
- int ret;
- char *fmt = strrchr(filename, '.');
- if (fmt && (ret = open_by_filename(filename, fmt + 1, mem)) != 0)
- return ret;
-
- if (!mem->size || !mem->buffer)
- return report_error("Out of memory parsing file %s\n", filename);
-
- return parse_xml_buffer(filename, mem->buffer, mem->size, &dive_table, NULL);
-}
-
-int check_git_sha(const char *filename)
-{
- struct git_repository *git;
- const char *branch = NULL;
-
- git = is_git_repository(filename, &branch, NULL, false);
- if (prefs.cloud_git_url &&
- strstr(filename, prefs.cloud_git_url)
- && git == dummy_git_repository)
- /* opening the cloud storage repository failed for some reason,
- * so we don't know if there is additional data in the remote */
- return 1;
-
- /* if this is a git repository, do we already have this exact state loaded ?
- * get the SHA and compare with what we currently have */
- if (git && git != dummy_git_repository) {
- const char *sha = get_sha(git, branch);
- if (!same_string(sha, "") &&
- same_string(sha, saved_git_id)) {
- fprintf(stderr, "already have loaded SHA %s - don't load again\n", sha);
- return 0;
- }
- }
- return 1;
-}
-
-int parse_file(const char *filename)
-{
- struct git_repository *git;
- const char *branch = NULL;
- char *current_sha = copy_string(saved_git_id);
- struct memblock mem;
- char *fmt;
- int ret;
-
- git = is_git_repository(filename, &branch, NULL, false);
- if (prefs.cloud_git_url &&
- strstr(filename, prefs.cloud_git_url)
- && git == dummy_git_repository) {
- /* opening the cloud storage repository failed for some reason
- * give up here and don't send errors about git repositories */
- free(current_sha);
- return 0;
- }
- /* if this is a git repository, do we already have this exact state loaded ?
- * get the SHA and compare with what we currently have */
- if (git && git != dummy_git_repository) {
- const char *sha = get_sha(git, branch);
- if (!same_string(sha, "") &&
- same_string(sha, current_sha) &&
- !unsaved_changes()) {
- fprintf(stderr, "already have loaded SHA %s - don't load again\n", sha);
- free(current_sha);
- return 0;
- }
- }
- free(current_sha);
- if (git)
- return git_load_dives(git, branch);
-
- if ((ret = readfile(filename, &mem)) < 0) {
- /* we don't want to display an error if this was the default file or the cloud storage */
- if ((prefs.default_filename && !strcmp(filename, prefs.default_filename)) ||
- isCloudUrl(filename))
- return 0;
-
- return report_error(translate("gettextFromC", "Failed to read '%s'"), filename);
- } else if (ret == 0) {
- return report_error(translate("gettextFromC", "Empty file '%s'"), filename);
- }
-
- fmt = strrchr(filename, '.');
- if (fmt && (!strcasecmp(fmt + 1, "DB") || !strcasecmp(fmt + 1, "BAK") || !strcasecmp(fmt + 1, "SQL"))) {
- if (!try_to_open_db(filename, &mem)) {
- free(mem.buffer);
- return 0;
- }
- }
-
- /* Divesoft Freedom */
- if (fmt && (!strcasecmp(fmt + 1, "DLF"))) {
- if (!parse_dlf_buffer(mem.buffer, mem.size)) {
- free(mem.buffer);
- return 0;
- }
- return -1;
- }
-
- /* DataTrak/Wlog */
- if (fmt && !strcasecmp(fmt + 1, "LOG")) {
- datatrak_import(filename, &dive_table);
- return 0;
- }
-
- /* OSTCtools */
- if (fmt && (!strcasecmp(fmt + 1, "DIVE"))) {
- ostctools_import(filename, &dive_table);
- return 0;
- }
-
- ret = parse_file_buffer(filename, &mem);
- free(mem.buffer);
- return ret;
-}
-
-#define MATCH(buffer, pattern) \
- memcmp(buffer, pattern, strlen(pattern))
-
-char *parse_mkvi_value(const char *haystack, const char *needle)
-{
- char *lineptr, *valueptr, *endptr, *ret = NULL;
-
- if ((lineptr = strstr(haystack, needle)) != NULL) {
- if ((valueptr = strstr(lineptr, ": ")) != NULL) {
- valueptr += 2;
- }
- if ((endptr = strstr(lineptr, "\n")) != NULL) {
- char terminator = '\n';
- if (*(endptr - 1) == '\r') {
- --endptr;
- terminator = '\r';
- }
- *endptr = 0;
- ret = copy_string(valueptr);
- *endptr = terminator;
-
- }
- }
- return ret;
-}
-
-char *next_mkvi_key(const char *haystack)
-{
- char *valueptr, *endptr, *ret = NULL;
-
- if ((valueptr = strstr(haystack, "\n")) != NULL) {
- valueptr += 1;
- if ((endptr = strstr(valueptr, ": ")) != NULL) {
- *endptr = 0;
- ret = strdup(valueptr);
- *endptr = ':';
- }
- }
- return ret;
-}
-
-int parse_txt_file(const char *filename, const char *csv)
-{
- struct memblock memtxt, memcsv;
-
- if (readfile(filename, &memtxt) < 0) {
- return report_error(translate("gettextFromC", "Failed to read '%s'"), filename);
- }
-
- /*
- * MkVI stores some information in .txt file but the whole profile and events are stored in .csv file. First
- * make sure the input .txt looks like proper MkVI file, then start parsing the .csv.
- */
- if (MATCH(memtxt.buffer, "MkVI_Config") == 0) {
- int d, m, y, he;
- int hh = 0, mm = 0, ss = 0;
- int prev_depth = 0, cur_sampletime = 0, prev_setpoint = -1, prev_ndl = -1;
- bool has_depth = false, has_setpoint = false, has_ndl = false;
- char *lineptr, *key, *value;
- int o2cylinder_pressure = 0, cylinder_pressure = 0, cur_cylinder_index = 0;
- unsigned int prev_time = 0;
-
- struct dive *dive;
- struct divecomputer *dc;
- struct tm cur_tm;
-
- value = parse_mkvi_value(memtxt.buffer, "Dive started at");
- if (sscanf(value, "%d-%d-%d %d:%d:%d", &y, &m, &d, &hh, &mm, &ss) != 6) {
- free(value);
- return -1;
- }
- free(value);
- cur_tm.tm_year = y;
- cur_tm.tm_mon = m - 1;
- cur_tm.tm_mday = d;
- cur_tm.tm_hour = hh;
- cur_tm.tm_min = mm;
- cur_tm.tm_sec = ss;
-
- dive = alloc_dive();
- dive->when = utc_mktime(&cur_tm);;
- dive->dc.model = strdup("Poseidon MkVI Discovery");
- value = parse_mkvi_value(memtxt.buffer, "Rig Serial number");
- dive->dc.deviceid = atoi(value);
- free(value);
- dive->dc.divemode = CCR;
- dive->dc.no_o2sensors = 2;
-
- dive->cylinder[cur_cylinder_index].cylinder_use = OXYGEN;
- dive->cylinder[cur_cylinder_index].type.size.mliter = 3000;
- dive->cylinder[cur_cylinder_index].type.workingpressure.mbar = 200000;
- dive->cylinder[cur_cylinder_index].type.description = strdup("3l Mk6");
- dive->cylinder[cur_cylinder_index].gasmix.o2.permille = 1000;
- cur_cylinder_index++;
-
- dive->cylinder[cur_cylinder_index].cylinder_use = DILUENT;
- dive->cylinder[cur_cylinder_index].type.size.mliter = 3000;
- dive->cylinder[cur_cylinder_index].type.workingpressure.mbar = 200000;
- dive->cylinder[cur_cylinder_index].type.description = strdup("3l Mk6");
- value = parse_mkvi_value(memtxt.buffer, "Helium percentage");
- he = atoi(value);
- free(value);
- value = parse_mkvi_value(memtxt.buffer, "Nitrogen percentage");
- dive->cylinder[cur_cylinder_index].gasmix.o2.permille = (100 - atoi(value) - he) * 10;
- free(value);
- dive->cylinder[cur_cylinder_index].gasmix.he.permille = he * 10;
- cur_cylinder_index++;
-
- lineptr = strstr(memtxt.buffer, "Dive started at");
- while (lineptr && *lineptr && (lineptr = strchr(lineptr, '\n')) && ++lineptr) {
- key = next_mkvi_key(lineptr);
- if (!key)
- break;
- value = parse_mkvi_value(lineptr, key);
- if (!value) {
- free(key);
- break;
- }
- add_extra_data(&dive->dc, key, value);
- free(key);
- free(value);
- }
- dc = &dive->dc;
-
- /*
- * Read samples from the CSV file. A sample contains all the lines with same timestamp. The CSV file has
- * the following format:
- *
- * timestamp, type, value
- *
- * And following fields are of interest to us:
- *
- * 6 sensor1
- * 7 sensor2
- * 8 depth
- * 13 o2 tank pressure
- * 14 diluent tank pressure
- * 20 o2 setpoint
- * 39 water temp
- */
-
- if (readfile(csv, &memcsv) < 0) {
- free(dive);
- return report_error(translate("gettextFromC", "Poseidon import failed: unable to read '%s'"), csv);
- }
- lineptr = memcsv.buffer;
- for (;;) {
- struct sample *sample;
- int type;
- int value;
- int sampletime;
- int gaschange = 0;
-
- /* Collect all the information for one sample */
- sscanf(lineptr, "%d,%d,%d", &cur_sampletime, &type, &value);
-
- has_depth = false;
- has_setpoint = false;
- has_ndl = false;
- sample = prepare_sample(dc);
-
- /*
- * There was a bug in MKVI download tool that resulted in erroneous sample
- * times. This fix should work similarly as the vendor's own.
- */
-
- sample->time.seconds = cur_sampletime < 0xFFFF * 3 / 4 ? cur_sampletime : prev_time;
- prev_time = sample->time.seconds;
-
- do {
- int i = sscanf(lineptr, "%d,%d,%d", &sampletime, &type, &value);
- switch (i) {
- case 3:
- switch (type) {
- case 0:
- //Mouth piece position event: 0=OC, 1=CC, 2=UN, 3=NC
- switch (value) {
- case 0:
- add_event(dc, cur_sampletime, 0, 0, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position OC"));
- break;
- case 1:
- add_event(dc, cur_sampletime, 0, 0, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position CC"));
- break;
- case 2:
- add_event(dc, cur_sampletime, 0, 0, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position unknown"));
- break;
- case 3:
- add_event(dc, cur_sampletime, 0, 0, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "Mouth piece position not connected"));
- break;
- }
- break;
- case 3:
- //Power Off event
- add_event(dc, cur_sampletime, 0, 0, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "Power off"));
- break;
- case 4:
- //Battery State of Charge in %
-#ifdef SAMPLE_EVENT_BATTERY
- add_event(dc, cur_sampletime, SAMPLE_EVENT_BATTERY, 0,
- value, QT_TRANSLATE_NOOP("gettextFromC", "battery"));
-#endif
- break;
- case 6:
- //PO2 Cell 1 Average
- add_sample_data(sample, POSEIDON_SENSOR1, value);
- break;
- case 7:
- //PO2 Cell 2 Average
- add_sample_data(sample, POSEIDON_SENSOR2, value);
- break;
- case 8:
- //Depth * 2
- has_depth = true;
- prev_depth = value;
- add_sample_data(sample, POSEIDON_DEPTH, value);
- break;
- //9 Max Depth * 2
- //10 Ascent/Descent Rate * 2
- case 11:
- //Ascent Rate Alert >10 m/s
- add_event(dc, cur_sampletime, SAMPLE_EVENT_ASCENT, 0, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "ascent"));
- break;
- case 13:
- //O2 Tank Pressure
- add_sample_data(sample, POSEIDON_O2CYLINDER, value);
- if (!o2cylinder_pressure) {
- dive->cylinder[0].sample_start.mbar = value * 1000;
- o2cylinder_pressure = value;
- } else
- o2cylinder_pressure = value;
- break;
- case 14:
- //Diluent Tank Pressure
- add_sample_data(sample, POSEIDON_PRESSURE, value);
- if (!cylinder_pressure) {
- dive->cylinder[1].sample_start.mbar = value * 1000;
- cylinder_pressure = value;
- } else
- cylinder_pressure = value;
- break;
- //16 Remaining dive time #1?
- //17 related to O2 injection
- case 20:
- //PO2 Setpoint
- has_setpoint = true;
- prev_setpoint = value;
- add_sample_data(sample, POSEIDON_SETPOINT, value);
- break;
- case 22:
- //End of O2 calibration Event: 0 = OK, 2 = Failed, rest of dive setpoint 1.0
- if (value == 2)
- add_event(dc, cur_sampletime, 0, SAMPLE_FLAGS_END, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "Oâ‚‚ calibration failed"));
- add_event(dc, cur_sampletime, 0, SAMPLE_FLAGS_END, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "Oâ‚‚ calibration"));
- break;
- case 25:
- //25 Max Ascent depth
- add_sample_data(sample, POSEIDON_CEILING, value);
- break;
- case 31:
- //Start of O2 calibration Event
- add_event(dc, cur_sampletime, 0, SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "Oâ‚‚ calibration"));
- break;
- case 37:
- //Remaining dive time #2?
- has_ndl = true;
- prev_ndl = value;
- add_sample_data(sample, POSEIDON_NDL, value);
- break;
- case 39:
- // Water Temperature in Celcius
- add_sample_data(sample, POSEIDON_TEMP, value);
- break;
- case 85:
- //He diluent part in %
- gaschange += value << 16;
- break;
- case 86:
- //O2 diluent part in %
- gaschange += value;
- break;
- //239 Unknown, maybe PO2 at sensor validation?
- //240 Unknown, maybe PO2 at sensor validation?
- //247 Unknown, maybe PO2 Cell 1 during pressure test
- //248 Unknown, maybe PO2 Cell 2 during pressure test
- //250 PO2 Cell 1
- //251 PO2 Cell 2
- default:
- break;
- } /* sample types */
- break;
- case EOF:
- break;
- default:
- printf("Unable to parse input: %s\n", lineptr);
- break;
- }
-
- lineptr = strchr(lineptr, '\n');
- if (!lineptr || !*lineptr)
- break;
- lineptr++;
-
- /* Grabbing next sample time */
- sscanf(lineptr, "%d,%d,%d", &cur_sampletime, &type, &value);
- } while (sampletime == cur_sampletime);
-
- if (gaschange)
- add_event(dc, cur_sampletime, SAMPLE_EVENT_GASCHANGE2, 0, gaschange,
- QT_TRANSLATE_NOOP("gettextFromC", "gaschange"));
- if (!has_depth)
- add_sample_data(sample, POSEIDON_DEPTH, prev_depth);
- if (!has_setpoint && prev_setpoint >= 0)
- add_sample_data(sample, POSEIDON_SETPOINT, prev_setpoint);
- if (!has_ndl && prev_ndl >= 0)
- add_sample_data(sample, POSEIDON_NDL, prev_ndl);
- if (cylinder_pressure)
- dive->cylinder[1].sample_end.mbar = cylinder_pressure * 1000;
- if (o2cylinder_pressure)
- dive->cylinder[0].sample_end.mbar = o2cylinder_pressure * 1000;
- finish_sample(dc);
-
- if (!lineptr || !*lineptr)
- break;
- }
- record_dive(dive);
- return 1;
- } else {
- return report_error(translate("gettextFromC", "No matching DC found for file '%s'"), csv);
- }
-
- return 0;
-}
-
-#define MAXCOLDIGITS 10
-#define DATESTR 9
-#define TIMESTR 6
-
-int parse_csv_file(const char *filename, char **params, int pnr, const char *csvtemplate)
-{
- int ret, i;
- struct memblock mem;
- time_t now;
- struct tm *timep = NULL;
- char tmpbuf[MAXCOLDIGITS];
-
- /* Increase the limits for recursion and variables on XSLT
- * parsing */
- xsltMaxDepth = 30000;
-#if LIBXSLT_VERSION > 10126
- xsltMaxVars = 150000;
-#endif
-
- if (filename == NULL)
- return report_error("No CSV filename");
-
- time(&now);
- timep = localtime(&now);
-
- strftime(tmpbuf, MAXCOLDIGITS, "%Y%m%d", timep);
- params[pnr++] = "date";
- params[pnr++] = strdup(tmpbuf);
-
- /* As the parameter is numeric, we need to ensure that the leading zero
- * is not discarded during the transform, thus prepend time with 1 */
-
- strftime(tmpbuf, MAXCOLDIGITS, "1%H%M", timep);
- params[pnr++] = "time";
- params[pnr++] = strdup(tmpbuf);
- params[pnr++] = NULL;
-
- mem.size = 0;
- if (try_to_xslt_open_csv(filename, &mem, csvtemplate))
- return -1;
-
- /*
- * Lets print command line for manual testing with xsltproc if
- * verbosity level is high enough. The printed line needs the
- * input file added as last parameter.
- */
-
-#ifndef SUBSURFACE_MOBILE
- if (verbose >= 2) {
- fprintf(stderr, "(echo '<csv>'; cat %s;echo '</csv>') | xsltproc ", filename);
- for (i=0; params[i]; i+=2)
- fprintf(stderr, "--stringparam %s %s ", params[i], params[i+1]);
- fprintf(stderr, "%s/xslt/csv2xml.xslt -\n", SUBSURFACE_SOURCE);
- }
-#endif
- ret = parse_xml_buffer(filename, mem.buffer, mem.size, &dive_table, (const char **)params);
-
- free(mem.buffer);
- for (i = 0; params[i]; i += 2)
- free(params[i + 1]);
-
- return ret;
-}
-
-#define SBPARAMS 40
-int parse_seabear_csv_file(const char *filename, char **params, int pnr, const char *csvtemplate)
-{
- int ret, i;
- struct memblock mem;
- time_t now;
- struct tm *timep = NULL;
- char *ptr, *ptr_old = NULL;
- char *NL = NULL;
- char tmpbuf[MAXCOLDIGITS];
-
- /* Increase the limits for recursion and variables on XSLT
- * parsing */
- xsltMaxDepth = 30000;
-#if LIBXSLT_VERSION > 10126
- xsltMaxVars = 150000;
-#endif
-
- time(&now);
- timep = localtime(&now);
-
- strftime(tmpbuf, MAXCOLDIGITS, "%Y%m%d", timep);
- params[pnr++] = "date";
- params[pnr++] = strdup(tmpbuf);
-
- /* As the parameter is numeric, we need to ensure that the leading zero
- * is not discarded during the transform, thus prepend time with 1 */
- strftime(tmpbuf, MAXCOLDIGITS, "1%H%M", timep);
- params[pnr++] = "time";
- params[pnr++] = strdup(tmpbuf);
-
-
- if (filename == NULL)
- return report_error("No CSV filename");
-
- if (readfile(filename, &mem) < 0)
- return report_error(translate("gettextFromC", "Failed to read '%s'"), filename);
-
- /* Determine NL (new line) character and the start of CSV data */
- ptr = mem.buffer;
- while ((ptr = strstr(ptr, "\r\n\r\n")) != NULL) {
- ptr_old = ptr;
- ptr += 1;
- NL = "\r\n";
- }
-
- if (!ptr_old) {
- ptr = mem.buffer;
- while ((ptr = strstr(ptr, "\n\n")) != NULL) {
- ptr_old = ptr;
- ptr += 1;
- NL = "\n";
- }
- ptr_old += 2;
- } else
- ptr_old += 4;
-
- /*
- * If file does not contain empty lines, it is not a valid
- * Seabear CSV file.
- */
- if (NL == NULL)
- return -1;
-
- /*
- * On my current sample of Seabear DC log file, the date is
- * without any identifier. Thus we must search for the previous
- * line and step through from there. That is the line after
- * Serial number.
- */
- ptr = strstr(mem.buffer, "Serial number:");
- if (ptr)
- ptr = strstr(ptr, NL);
-
- /*
- * Write date and time values to params array, if available in
- * the CSV header
- */
-
- if (ptr) {
- ptr += strlen(NL) + 2;
- /*
- * pnr is the index of NULL on the params as filled by
- * the init function. The two last entries should be
- * date and time. Here we overwrite them with the data
- * from the CSV header.
- */
-
- memcpy(params[pnr - 3], ptr, 4);
- memcpy(params[pnr - 3] + 4, ptr + 5, 2);
- memcpy(params[pnr - 3] + 6, ptr + 8, 2);
- params[pnr - 3][8] = 0;
-
- memcpy(params[pnr - 1] + 1, ptr + 11, 2);
- memcpy(params[pnr - 1] + 3, ptr + 14, 2);
- params[pnr - 1][5] = 0;
- }
-
- params[pnr++] = NULL;
-
- /* Move the CSV data to the start of mem buffer */
- memmove(mem.buffer, ptr_old, mem.size - (ptr_old - (char*)mem.buffer));
- mem.size = (int)mem.size - (ptr_old - (char*)mem.buffer);
-
- if (try_to_xslt_open_csv(filename, &mem, csvtemplate))
- return -1;
-
- /*
- * Lets print command line for manual testing with xsltproc if
- * verbosity level is high enough. The printed line needs the
- * input file added as last parameter.
- */
-
- if (verbose >= 2) {
- fprintf(stderr, "xsltproc ");
- for (i=0; params[i]; i+=2)
- fprintf(stderr, "--stringparam %s %s ", params[i], params[i+1]);
- fprintf(stderr, "xslt/csv2xml.xslt\n");
- }
-
- ret = parse_xml_buffer(filename, mem.buffer, mem.size, &dive_table, (const char **)params);
- free(mem.buffer);
- for (i = 0; params[i]; i += 2)
- free(params[i + 1]);
-
- return ret;
-}
-
-int parse_manual_file(const char *filename, char **params, int pnr)
-{
- struct memblock mem;
- time_t now;
- struct tm *timep;
- char curdate[9];
- char curtime[6];
- int ret, i;
-
-
- time(&now);
- timep = localtime(&now);
- strftime(curdate, DATESTR, "%Y%m%d", timep);
-
- /* As the parameter is numeric, we need to ensure that the leading zero
- * is not discarded during the transform, thus prepend time with 1 */
- strftime(curtime, TIMESTR, "1%H%M", timep);
-
-
- params[pnr++] = strdup("date");
- params[pnr++] = strdup(curdate);
- params[pnr++] = strdup("time");
- params[pnr++] = strdup(curtime);
- params[pnr++] = NULL;
-
- if (filename == NULL)
- return report_error("No manual CSV filename");
-
- mem.size = 0;
- if (try_to_xslt_open_csv(filename, &mem, "manualCSV"))
- return -1;
-
- ret = parse_xml_buffer(filename, mem.buffer, mem.size, &dive_table, (const char **)params);
-
- free(mem.buffer);
- for (i = 0; i < pnr - 2; ++i)
- free(params[i]);
- return ret;
-}
diff --git a/subsurface-core/file.h b/subsurface-core/file.h
deleted file mode 100644
index 1c1dfc116..000000000
--- a/subsurface-core/file.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef FILE_H
-#define FILE_H
-
-struct memblock {
- void *buffer;
- size_t size;
-};
-
-extern int try_to_open_cochran(const char *filename, struct memblock *mem);
-extern int try_to_open_liquivision(const char *filename, struct memblock *mem);
-extern void datatrak_import(const char *file, struct dive_table *table);
-extern void ostctools_import(const char *file, struct dive_table *table);
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-extern int readfile(const char *filename, struct memblock *mem);
-extern timestamp_t parse_date(const char *date);
-extern int try_to_open_zip(const char *filename);
-#ifdef __cplusplus
-}
-#endif
-
-#endif // FILE_H
diff --git a/subsurface-core/gas-model.c b/subsurface-core/gas-model.c
deleted file mode 100644
index ad1160f3b..000000000
--- a/subsurface-core/gas-model.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/* gas-model.c */
-/* gas compressibility model */
-#include <stdio.h>
-#include <stdlib.h>
-#include "dive.h"
-
-/* "Virial minus one" - the virial cubic form without the initial 1.0 */
-#define virial_m1(C, x1, x2, x3) (C[0]*x1+C[1]*x2+C[2]*x3)
-
-/*
- * Cubic virial least-square coefficients for O2/N2/He based on data from
- *
- * PERRY’S CHEMICAL ENGINEERS’ HANDBOOK SEVENTH EDITION
- *
- * with the lookup and curve fitting by Lubomir.
- *
- * The "virial" form of the compression factor polynomial is
- *
- * Z = 1.0 + C[0]*P + C[1]*P^2 + C[2]*P^3 ...
- *
- * and these tables do not contain the initial 1.0 term.
- *
- * NOTE! Helium coefficients are a linear mix operation between the
- * 323K and one for 273K isotherms, to make everything be at 300K.
- */
-double gas_compressibility_factor(struct gasmix *gas, double bar)
-{
- static const double o2_coefficients[3] = {
- -7.18092073703e-04,
- +2.81852572808e-06,
- -1.50290620492e-09
- };
- static const double n2_coefficients[3] = {
- -2.19260353292e-04,
- +2.92844845532e-06,
- -2.07613482075e-09
- };
- static const double he_coefficients[3] = {
- +4.87320026468e-04,
- -8.83632921053e-08,
- +5.33304543646e-11
- };
- int o2, he;
- double x1, x2, x3;
- double Z;
-
- o2 = get_o2(gas);
- he = get_he(gas);
-
- x1 = bar; x2 = x1*x1; x3 = x2*x1;
-
- Z = virial_m1(o2_coefficients, x1, x2, x3) * o2 +
- virial_m1(he_coefficients, x1, x2, x3) * he +
- virial_m1(n2_coefficients, x1, x2, x3) * (1000 - o2 - he);
-
- /*
- * We add the 1.0 at the very end - the linear mixing of the
- * three 1.0 terms is still 1.0 regardless of the gas mix.
- *
- * The * 0.001 is because we did the linear mixing using the
- * raw permille gas values.
- */
- return Z * 0.001 + 1.0;
-}
diff --git a/subsurface-core/gaspressures.c b/subsurface-core/gaspressures.c
deleted file mode 100644
index 5d3fc9791..000000000
--- a/subsurface-core/gaspressures.c
+++ /dev/null
@@ -1,430 +0,0 @@
-/* gaspressures.c
- * ---------------
- * This file contains the routines to calculate the gas pressures in the cylinders.
- * The functions below support the code in profile.c.
- * The high-level function is populate_pressure_information(), called by function
- * create_plot_info_new() in profile.c. The other functions below are, in turn,
- * called by populate_pressure_information(). The calling sequence is as follows:
- *
- * populate_pressure_information() -> calc_pressure_time()
- * -> fill_missing_tank_pressures() -> fill_missing_segment_pressures()
- * -> get_pr_interpolate_data()
- *
- * The pr_track_t related functions below implement a linked list that is used by
- * the majority of the functions below. The linked list covers a part of the dive profile
- * for which there are no cylinder pressure data. Each element in the linked list
- * represents a segment between two consecutive points on the dive profile.
- * pr_track_t is defined in gaspressures.h
- */
-
-#include "dive.h"
-#include "display.h"
-#include "profile.h"
-#include "gaspressures.h"
-
-static pr_track_t *pr_track_alloc(int start, int t_start)
-{
- pr_track_t *pt = malloc(sizeof(pr_track_t));
- pt->start = start;
- pt->end = 0;
- pt->t_start = pt->t_end = t_start;
- pt->pressure_time = 0;
- pt->next = NULL;
- return pt;
-}
-
-/* poor man's linked list */
-static pr_track_t *list_last(pr_track_t *list)
-{
- pr_track_t *tail = list;
- if (!tail)
- return NULL;
- while (tail->next) {
- tail = tail->next;
- }
- return tail;
-}
-
-static pr_track_t *list_add(pr_track_t *list, pr_track_t *element)
-{
- pr_track_t *tail = list_last(list);
- if (!tail)
- return element;
- tail->next = element;
- return list;
-}
-
-static void list_free(pr_track_t *list)
-{
- if (!list)
- return;
- list_free(list->next);
- free(list);
-}
-
-#ifdef DEBUG_PR_TRACK
-static void dump_pr_track(pr_track_t **track_pr)
-{
- int cyl;
- pr_track_t *list;
-
- for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) {
- list = track_pr[cyl];
- while (list) {
- printf("cyl%d: start %d end %d t_start %d t_end %d pt %d\n", cyl,
- list->start, list->end, list->t_start, list->t_end, list->pressure_time);
- list = list->next;
- }
- }
-}
-#endif
-
-/*
- * This looks at the pressures for one cylinder, and
- * calculates any missing beginning/end pressures for
- * each segment by taking the over-all SAC-rate into
- * account for that cylinder.
- *
- * NOTE! Many segments have full pressure information
- * (both beginning and ending pressure). But if we have
- * switched away from a cylinder, we will have the
- * beginning pressure for the first segment with a
- * missing end pressure. We may then have one or more
- * segments without beginning or end pressures, until
- * we finally have a segment with an end pressure.
- *
- * We want to spread out the pressure over these missing
- * segments according to how big of a time_pressure area
- * they have.
- */
-static void fill_missing_segment_pressures(pr_track_t *list, enum interpolation_strategy strategy)
-{
- double magic;
-
- while (list) {
- int start = list->start, end;
- pr_track_t *tmp = list;
- int pt_sum = 0, pt = 0;
-
- for (;;) {
- pt_sum += tmp->pressure_time;
- end = tmp->end;
- if (end)
- break;
- end = start;
- if (!tmp->next)
- break;
- tmp = tmp->next;
- }
-
- if (!start)
- start = end;
-
- /*
- * Now 'start' and 'end' contain the pressure values
- * for the set of segments described by 'list'..'tmp'.
- * pt_sum is the sum of all the pressure-times of the
- * segments.
- *
- * Now dole out the pressures relative to pressure-time.
- */
- list->start = start;
- tmp->end = end;
- switch (strategy) {
- case SAC:
- for (;;) {
- int pressure;
- pt += list->pressure_time;
- pressure = start;
- if (pt_sum)
- pressure -= (start - end) * (double)pt / pt_sum;
- list->end = pressure;
- if (list == tmp)
- break;
- list = list->next;
- list->start = pressure;
- }
- break;
- case TIME:
- if (list->t_end && (tmp->t_start - tmp->t_end)) {
- magic = (list->t_start - tmp->t_end) / (tmp->t_start - tmp->t_end);
- list->end = rint(start - (start - end) * magic);
- } else {
- list->end = start;
- }
- break;
- case CONSTANT:
- list->end = start;
- }
-
- /* Ok, we've done that set of segments */
- list = list->next;
- }
-}
-
-#ifdef DEBUG_PR_INTERPOLATE
-void dump_pr_interpolate(int i, pr_interpolate_t interpolate_pr)
-{
- printf("Interpolate for entry %d: start %d - end %d - pt %d - acc_pt %d\n", i,
- interpolate_pr.start, interpolate_pr.end, interpolate_pr.pressure_time, interpolate_pr.acc_pressure_time);
-}
-#endif
-
-
-static struct pr_interpolate_struct get_pr_interpolate_data(pr_track_t *segment, struct plot_info *pi, int cur)
-{ // cur = index to pi->entry corresponding to t_end of segment;
- struct pr_interpolate_struct interpolate;
- int i;
- struct plot_data *entry;
-
- interpolate.start = segment->start;
- interpolate.end = segment->end;
- interpolate.acc_pressure_time = 0;
- interpolate.pressure_time = 0;
-
- for (i = 0; i < pi->nr; i++) {
- entry = pi->entry + i;
-
- if (entry->sec < segment->t_start)
- continue;
- interpolate.pressure_time += entry->pressure_time;
- if (entry->sec >= segment->t_end)
- break;
- if (i <= cur)
- interpolate.acc_pressure_time += entry->pressure_time;
- }
- return interpolate;
-}
-
-static void fill_missing_tank_pressures(struct dive *dive, struct plot_info *pi, pr_track_t **track_pr, bool o2_flag)
-{
- int cyl, i;
- struct plot_data *entry;
- pr_interpolate_t interpolate = { 0, 0, 0, 0 };
- pr_track_t *last_segment = NULL;
- int cur_pr[MAX_CYLINDERS]; // cur_pr[MAX_CYLINDERS] is the CCR diluent cylinder
-
- for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) {
- enum interpolation_strategy strategy;
- if (!track_pr[cyl]) {
- /* no segment where this cylinder is used */
- cur_pr[cyl] = -1;
- continue;
- }
- if (dive->cylinder[cyl].cylinder_use == OC_GAS)
- strategy = SAC;
- else
- strategy = TIME;
- fill_missing_segment_pressures(track_pr[cyl], strategy); // Interpolate the missing tank pressure values ..
- cur_pr[cyl] = track_pr[cyl]->start; // in the pr_track_t lists of structures
- } // and keep the starting pressure for each cylinder.
-
-#ifdef DEBUG_PR_TRACK
- /* another great debugging tool */
- dump_pr_track(track_pr);
-#endif
-
- /* Transfer interpolated cylinder pressures from pr_track strucktures to plotdata
- * Go down the list of tank pressures in plot_info. Align them with the start &
- * end times of each profile segment represented by a pr_track_t structure. Get
- * the accumulated pressure_depths from the pr_track_t structures and then
- * interpolate the pressure where these do not exist in the plot_info pressure
- * variables. Pressure values are transferred from the pr_track_t structures
- * to the plot_info structure, allowing us to plot the tank pressure.
- *
- * The first two pi structures are "fillers", but in case we don't have a sample
- * at time 0 we need to process the second of them here, therefore i=1 */
- for (i = 1; i < pi->nr; i++) { // For each point on the profile:
- double magic;
- pr_track_t *segment;
- int pressure;
- int *save_pressure, *save_interpolated;
-
- entry = pi->entry + i;
-
- if (o2_flag) {
- // Find the cylinder index (cyl) and pressure
- cyl = dive->oxygen_cylinder_index;
- if (cyl < 0)
- return; // Can we do this?!?
- pressure = O2CYLINDER_PRESSURE(entry);
- save_pressure = &(entry->o2cylinderpressure[SENSOR_PR]);
- save_interpolated = &(entry->o2cylinderpressure[INTERPOLATED_PR]);
- } else {
- pressure = SENSOR_PRESSURE(entry);
- save_pressure = &(entry->pressure[SENSOR_PR]);
- save_interpolated = &(entry->pressure[INTERPOLATED_PR]);
- cyl = entry->cylinderindex;
- }
-
- if (pressure) { // If there is a valid pressure value,
- last_segment = NULL; // get rid of interpolation data,
- cur_pr[cyl] = pressure; // set current pressure
- continue; // and skip to next point.
- }
- // If there is NO valid pressure value..
- // Find the pressure segment corresponding to this entry..
- segment = track_pr[cyl];
- while (segment && segment->t_end < entry->sec) // Find the track_pr with end time..
- segment = segment->next; // ..that matches the plot_info time (entry->sec)
-
- if (!segment || !segment->pressure_time) { // No (or empty) segment?
- *save_pressure = cur_pr[cyl]; // Just use our current pressure
- continue; // and skip to next point.
- }
-
- // If there is a valid segment but no tank pressure ..
- if (segment == last_segment) {
- interpolate.acc_pressure_time += entry->pressure_time;
- } else {
- // Set up an interpolation structure
- interpolate = get_pr_interpolate_data(segment, pi, i);
- last_segment = segment;
- }
-
- if(dive->cylinder[cyl].cylinder_use == OC_GAS) {
-
- /* if this segment has pressure_time, then calculate a new interpolated pressure */
- if (interpolate.pressure_time) {
- /* Overall pressure change over total pressure-time for this segment*/
- magic = (interpolate.end - interpolate.start) / (double)interpolate.pressure_time;
-
- /* Use that overall pressure change to update the current pressure */
- cur_pr[cyl] = rint(interpolate.start + magic * interpolate.acc_pressure_time);
- }
- } else {
- magic = (interpolate.end - interpolate.start) / (segment->t_end - segment->t_start);
- cur_pr[cyl] = rint(segment->start + magic * (entry->sec - segment->t_start));
- }
- *save_interpolated = cur_pr[cyl]; // and store the interpolated data in plot_info
- }
-}
-
-
-/*
- * What's the pressure-time between two plot data entries?
- * We're calculating the integral of pressure over time by
- * adding these up.
- *
- * The units won't matter as long as everybody agrees about
- * them, since they'll cancel out - we use this to calculate
- * a constant SAC-rate-equivalent, but we only use it to
- * scale pressures, so it ends up being a unitless scaling
- * factor.
- */
-static inline int calc_pressure_time(struct dive *dive, struct plot_data *a, struct plot_data *b)
-{
- int time = b->sec - a->sec;
- int depth = (a->depth + b->depth) / 2;
-
- if (depth <= SURFACE_THRESHOLD)
- return 0;
-
- return depth_to_mbar(depth, dive) * time;
-}
-
-#ifdef PRINT_PRESSURES_DEBUG
-// A CCR debugging tool that prints the gas pressures in cylinder 0 and in the diluent cylinder, used in populate_pressure_information():
-static void debug_print_pressures(struct plot_info *pi)
-{
- int i;
- for (i = 0; i < pi->nr; i++) {
- struct plot_data *entry = pi->entry + i;
- printf("%5d |%9d | %9d || %9d | %9d |\n", i, SENSOR_PRESSURE(entry), INTERPOLATED_PRESSURE(entry), DILUENT_PRESSURE(entry), INTERPOLATED_DILUENT_PRESSURE(entry));
- }
-}
-#endif
-
-/* This function goes through the list of tank pressures, either SENSOR_PRESSURE(entry) or O2CYLINDER_PRESSURE(entry),
- * of structure plot_info for the dive profile where each item in the list corresponds to one point (node) of the
- * profile. It finds values for which there are no tank pressures (pressure==0). For each missing item (node) of
- * tank pressure it creates a pr_track_alloc structure that represents a segment on the dive profile and that
- * contains tank pressures. There is a linked list of pr_track_alloc structures for each cylinder. These pr_track_alloc
- * structures ultimately allow for filling the missing tank pressure values on the dive profile using the depth_pressure
- * of the dive. To do this, it calculates the summed pressure-time value for the duration of the dive and stores these
- * in the pr_track_alloc structures. If diluent_flag = 1, then DILUENT_PRESSURE(entry) is used instead of SENSOR_PRESSURE.
- * This function is called by create_plot_info_new() in profile.c
- */
-void populate_pressure_information(struct dive *dive, struct divecomputer *dc, struct plot_info *pi, int o2_flag)
-{
- (void) dc;
- int i, cylinderid, cylinderindex = -1;
- pr_track_t *track_pr[MAX_CYLINDERS] = { NULL, };
- pr_track_t *current = NULL;
- bool missing_pr = false;
- bool found_any_pr_data = false;
-
- /* if we have no pressure data whatsoever, this is pointless, so let's just return */
- for (i = 0; i < MAX_CYLINDERS; i++) {
- if (dive->cylinder[i].start.mbar || dive->cylinder[i].sample_start.mbar ||
- dive->cylinder[i].end.mbar || dive->cylinder[i].sample_end.mbar) {
- found_any_pr_data = true;
- break;
- }
- }
- if (!found_any_pr_data)
- return;
-
- for (i = 0; i < pi->nr; i++) {
- struct plot_data *entry = pi->entry + i;
- unsigned pressure;
- if (o2_flag) { // if this is a diluent cylinder:
- pressure = O2CYLINDER_PRESSURE(entry);
- cylinderid = dive->oxygen_cylinder_index;
- if (cylinderid < 0)
- goto GIVE_UP;
- } else {
- pressure = SENSOR_PRESSURE(entry);
- cylinderid = entry->cylinderindex;
- }
- /* If track_pr structure already exists, then update it: */
- /* discrete integration of pressure over time to get the SAC rate equivalent */
- if (current) {
- entry->pressure_time = calc_pressure_time(dive, entry - 1, entry);
- current->pressure_time += entry->pressure_time;
- current->t_end = entry->sec;
- }
-
- /* If 1st record or different cylinder: Create a new track_pr structure: */
- /* track the segments per cylinder and their pressure/time integral */
- if (cylinderid != cylinderindex) {
- if (o2_flag) // For CCR dives:
- cylinderindex = dive->oxygen_cylinder_index; // indicate o2 cylinder
- else
- cylinderindex = entry->cylinderindex;
- current = pr_track_alloc(pressure, entry->sec);
- track_pr[cylinderindex] = list_add(track_pr[cylinderindex], current);
- continue;
- }
-
- if (!pressure) {
- missing_pr = 1;
- continue;
- }
- if (current)
- current->end = pressure;
-
- /* Was it continuous? */
- if ((o2_flag) && (O2CYLINDER_PRESSURE(entry - 1))) // in the case of CCR o2 pressure
- continue;
- else if (SENSOR_PRESSURE(entry - 1)) // for all other cylinders
- continue;
-
- /* transmitter stopped transmitting cylinder pressure data */
- current = pr_track_alloc(pressure, entry->sec);
- if (cylinderindex >= 0)
- track_pr[cylinderindex] = list_add(track_pr[cylinderindex], current);
- }
-
- if (missing_pr) {
- fill_missing_tank_pressures(dive, pi, track_pr, o2_flag);
- }
-
-#ifdef PRINT_PRESSURES_DEBUG
- debug_print_pressures(pi);
-#endif
-
-GIVE_UP:
- for (i = 0; i < MAX_CYLINDERS; i++)
- list_free(track_pr[i]);
-}
diff --git a/subsurface-core/gaspressures.h b/subsurface-core/gaspressures.h
deleted file mode 100644
index 420c117a2..000000000
--- a/subsurface-core/gaspressures.h
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef GASPRESSURES_H
-#define GASPRESSURES_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * simple structure to track the beginning and end tank pressure as
- * well as the integral of depth over time spent while we have no
- * pressure reading from the tank */
-typedef struct pr_track_struct pr_track_t;
-struct pr_track_struct {
- int start;
- int end;
- int t_start;
- int t_end;
- int pressure_time;
- pr_track_t *next;
-};
-
-typedef struct pr_interpolate_struct pr_interpolate_t;
-struct pr_interpolate_struct {
- int start;
- int end;
- int pressure_time;
- int acc_pressure_time;
-};
-
-enum interpolation_strategy {SAC, TIME, CONSTANT};
-
-#ifdef __cplusplus
-}
-#endif
-#endif // GASPRESSURES_H
diff --git a/subsurface-core/gettext.h b/subsurface-core/gettext.h
deleted file mode 100644
index 43ff023c7..000000000
--- a/subsurface-core/gettext.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef MYGETTEXT_H
-#define MYGETTEXT_H
-
-/* this is for the Qt based translations */
-extern const char *trGettext(const char *);
-#define translate(_context, arg) trGettext(arg)
-#define QT_TRANSLATE_NOOP(_context, arg) arg
-#define QT_TRANSLATE_NOOP3(_context, arg, _comment) arg
-
-#endif // MYGETTEXT_H
diff --git a/subsurface-core/gettextfromc.cpp b/subsurface-core/gettextfromc.cpp
deleted file mode 100644
index c579e3c3c..000000000
--- a/subsurface-core/gettextfromc.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#include <QCoreApplication>
-#include <QString>
-#include <gettextfromc.h>
-
-const char *gettextFromC::trGettext(const char *text)
-{
- QByteArray &result = translationCache[QByteArray(text)];
- if (result.isEmpty())
- result = translationCache[QByteArray(text)] = trUtf8(text).toUtf8();
- return result.constData();
-}
-
-void gettextFromC::reset(void)
-{
- translationCache.clear();
-}
-
-gettextFromC *gettextFromC::instance()
-{
- static QScopedPointer<gettextFromC> self(new gettextFromC());
- return self.data();
-}
-
-extern "C" const char *trGettext(const char *text)
-{
- return gettextFromC::instance()->trGettext(text);
-}
diff --git a/subsurface-core/gettextfromc.h b/subsurface-core/gettextfromc.h
deleted file mode 100644
index 53df18365..000000000
--- a/subsurface-core/gettextfromc.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef GETTEXTFROMC_H
-#define GETTEXTFROMC_H
-
-#include <QHash>
-#include <QCoreApplication>
-
-extern "C" const char *trGettext(const char *text);
-
-class gettextFromC {
- Q_DECLARE_TR_FUNCTIONS(gettextFromC)
-public:
- static gettextFromC *instance();
- const char *trGettext(const char *text);
- void reset(void);
- QHash<QByteArray, QByteArray> translationCache;
-};
-
-#endif // GETTEXTFROMC_H
diff --git a/subsurface-core/git-access.c b/subsurface-core/git-access.c
deleted file mode 100644
index d10139d3d..000000000
--- a/subsurface-core/git-access.c
+++ /dev/null
@@ -1,929 +0,0 @@
-// Clang has a bug on zero-initialization of C structs.
-#pragma clang diagnostic ignored "-Wmissing-field-initializers"
-
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <time.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <git2.h>
-
-#include "dive.h"
-#include "membuffer.h"
-#include "strndup.h"
-#include "qthelperfromc.h"
-#include "git-access.h"
-#include "gettext.h"
-
-bool is_subsurface_cloud = false;
-
-int (*update_progress_cb)(int, const char *) = NULL;
-
-void set_git_update_cb(int(*cb)(int, const char *))
-{
- update_progress_cb = cb;
-}
-
-// total overkill, but this allows us to get good timing in various scenarios;
-// the various parts of interacting with the local and remote git repositories send
-// us updates which indicate progress (and no, this is not smooth and definitely not
-// proportional - some parts are based on compute performance, some on network speed)
-// they also provide information where in the process we are so we can analyze the log
-// to understand which parts of the process take how much time.
-
-// last_git_storage_update_val is used to detect when we suddenly go back to smaller
-// "percentage" value because we are back to executing earlier code a second (or third
-// time) in that case a negative percentage value is sent to the callback function as a
-// special case to mark that situation. Overall this ensures monotonous percentage values
-int last_git_storage_update_val;
-
-int git_storage_update_progress(int percent, const char *text)
-{
- static int delta = 0;
-
- if (percent == 0) {
- delta = 0;
- } else if (percent > 0 && percent < last_git_storage_update_val) {
- delta = last_git_storage_update_val + delta;
- if (update_progress_cb)
- (*update_progress_cb)(-delta, "DELTA");
- if (verbose)
- fprintf(stderr, "set git storage percentage delta to %d\n", delta);
- }
-
- last_git_storage_update_val = percent;
-
- percent += delta;
-
- int ret = 0;
- if (update_progress_cb)
- ret = (*update_progress_cb)(percent, text);
- return ret;
-}
-
-// the checkout_progress_cb doesn't allow canceling of the operation
-// map the git progress to 70..90% of overall progress
-static void progress_cb(const char *path, size_t completed_steps, size_t total_steps, void *payload)
-{
- (void) path;
- (void) payload;
-
- int percent = 0;
- if (total_steps)
- percent = 70 + 20 * completed_steps / total_steps;
- (void)git_storage_update_progress(percent, "checkout_progress_cb");
-}
-
-// this randomly assumes that 80% of the time is spent on the objects and 20% on the deltas
-// map the git progress to 70..90% of overall progress
-// if the user cancels the dialog this is passed back to libgit2
-static int transfer_progress_cb(const git_transfer_progress *stats, void *payload)
-{
- (void) payload;
-
- int percent = 0;
- if (stats->total_objects)
- percent = 70 + 16 * stats->received_objects / stats->total_objects;
- if (stats->total_deltas)
- percent += 4 * stats->indexed_deltas / stats->total_deltas;
- /* for debugging this is useful
- char buf[100];
- snprintf(buf, 100, "transfer cb rec_obj %d tot_obj %d idx_delta %d total_delta %d local obj %d", stats->received_objects, stats->total_objects, stats->indexed_deltas, stats->total_deltas, stats->local_objects);
- return git_storage_update_progress(percent, buf);
- */
- return git_storage_update_progress(percent, "transfer cb");
-}
-
-// the initial push to sync the repos is mapped to 10..15% of overall progress
-static int push_transfer_progress_cb(unsigned int current, unsigned int total, size_t bytes, void *payload)
-{
- (void) bytes;
- (void) payload;
-
- int percent = 0;
- if (total != 0)
- percent = 12 + 5 * current / total;
- return git_storage_update_progress(percent, "push trasfer cb");
-}
-
-char *get_local_dir(const char *remote, const char *branch)
-{
- SHA_CTX ctx;
- unsigned char hash[20];
-
- // That zero-byte update is so that we don't get hash
- // collisions for "repo1 branch" vs "repo 1branch".
- SHA1_Init(&ctx);
- SHA1_Update(&ctx, remote, strlen(remote));
- SHA1_Update(&ctx, "", 1);
- SHA1_Update(&ctx, branch, strlen(branch));
- SHA1_Final(hash, &ctx);
-
- return format_string("%s/cloudstorage/%02x%02x%02x%02x%02x%02x%02x%02x",
- system_default_directory(),
- hash[0], hash[1], hash[2], hash[3],
- hash[4], hash[5], hash[6], hash[7]);
-}
-
-static char *move_local_cache(const char *remote, const char *branch)
-{
- char *old_path = get_local_dir(remote, branch);
- return move_away(old_path);
-}
-
-static int check_clean(const char *path, unsigned int status, void *payload)
-{
- (void) payload;
- status &= ~GIT_STATUS_CURRENT | GIT_STATUS_IGNORED;
- if (!status)
- return 0;
- if (is_subsurface_cloud)
- report_error(translate("gettextFromC", "Local cache directory %s corrupted - can't sync with Subsurface cloud storage"), path);
- else
- report_error("WARNING: Git cache directory modified (path %s) status %0x", path, status);
- return 1;
-}
-
-/*
- * The remote is strictly newer than the local branch.
- */
-static int reset_to_remote(git_repository *repo, git_reference *local, const git_oid *new_id)
-{
- git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
- opts.progress_cb = &progress_cb;
- git_object *target;
-
- if (verbose)
- fprintf(stderr, "git storage: reset to remote\n");
-
- // If it's not checked out (bare or not HEAD), just update the reference */
- if (git_repository_is_bare(repo) || git_branch_is_head(local) != 1) {
- git_reference *out;
-
- if (git_reference_set_target(&out, local, new_id, "Update to remote"))
- return report_error(translate("gettextFromC", "Could not update local cache to newer remote data"));
-
- git_reference_free(out);
-
-#ifdef DEBUG
- // Not really an error, just informational
- report_error("Updated local branch from remote");
-#endif
- return 0;
- }
-
- if (git_object_lookup(&target, repo, new_id, GIT_OBJ_COMMIT)) {
- if (is_subsurface_cloud)
- return report_error(translate("gettextFromC", "Subsurface cloud storage corrupted"));
- else
- return report_error("Could not look up remote commit");
- }
- opts.checkout_strategy = GIT_CHECKOUT_SAFE;
- if (git_reset(repo, target, GIT_RESET_HARD, &opts)) {
- if (is_subsurface_cloud)
- return report_error(translate("gettextFromC", "Could not update local cache to newer remote data"));
- else
- return report_error("Local head checkout failed after update");
- }
- // Not really an error, just informational
-#ifdef DEBUG
- report_error("Updated local information from remote");
-#endif
- return 0;
-}
-
-int credential_ssh_cb(git_cred **out,
- const char *url,
- const char *username_from_url,
- unsigned int allowed_types,
- void *payload)
-{
- (void) url;
- (void) allowed_types;
- (void) payload;
-
- const char *priv_key = format_string("%s/%s", system_default_directory(), "ssrf_remote.key");
- const char *passphrase = prefs.cloud_storage_password ? strdup(prefs.cloud_storage_password) : strdup("");
- return git_cred_ssh_key_new(out, username_from_url, NULL, priv_key, passphrase);
-}
-
-int credential_https_cb(git_cred **out,
- const char *url,
- const char *username_from_url,
- unsigned int allowed_types,
- void *payload)
-{
- (void) url;
- (void) username_from_url;
- (void) payload;
- (void) allowed_types;
- const char *username = prefs.cloud_storage_email_encoded;
- const char *password = prefs.cloud_storage_password ? strdup(prefs.cloud_storage_password) : strdup("");
- return git_cred_userpass_plaintext_new(out, username, password);
-}
-
-#define KNOWN_CERT "\xfd\xb8\xf7\x73\x76\xe2\x75\x53\x93\x37\xdc\xfe\x1e\x55\x43\x3d\xf2\x2c\x18\x2c"
-int certificate_check_cb(git_cert *cert, int valid, const char *host, void *payload)
-{
- (void) payload;
- if (same_string(host, "cloud.subsurface-divelog.org") && cert->cert_type == GIT_CERT_X509) {
- SHA_CTX ctx;
- unsigned char hash[21];
- git_cert_x509 *cert509 = (git_cert_x509 *)cert;
- SHA1_Init(&ctx);
- SHA1_Update(&ctx, cert509->data, cert509->len);
- SHA1_Final(hash, &ctx);
- hash[20] = 0;
- if (verbose > 1)
- if (same_string((char *)hash, KNOWN_CERT)) {
- fprintf(stderr, "cloud certificate considered %s, forcing it valid\n",
- valid ? "valid" : "not valid");
- return 1;
- }
- }
- return valid;
-}
-
-static int update_remote(git_repository *repo, git_remote *origin, git_reference *local, git_reference *remote, enum remote_transport rt)
-{
- (void) repo;
- (void) remote;
-
- git_push_options opts = GIT_PUSH_OPTIONS_INIT;
- git_strarray refspec;
- const char *name = git_reference_name(local);
-
- if (verbose)
- fprintf(stderr, "git storage: update remote\n");
-
- refspec.count = 1;
- refspec.strings = (char **)&name;
-
- opts.callbacks.push_transfer_progress = &push_transfer_progress_cb;
- if (rt == RT_SSH)
- opts.callbacks.credentials = credential_ssh_cb;
- else if (rt == RT_HTTPS)
- opts.callbacks.credentials = credential_https_cb;
- opts.callbacks.certificate_check = certificate_check_cb;
-
- if (git_remote_push(origin, &refspec, &opts)) {
- if (is_subsurface_cloud)
- return report_error(translate("gettextFromC", "Could not update Subsurface cloud storage, try again later"));
- else
- return report_error("Unable to update remote with current local cache state (%s)", giterr_last()->message);
- }
- return 0;
-}
-
-extern int update_git_checkout(git_repository *repo, git_object *parent, git_tree *tree);
-
-static int try_to_git_merge(git_repository *repo, git_reference **local_p, git_reference *remote, git_oid *base, const git_oid *local_id, const git_oid *remote_id)
-{
- (void) remote;
- git_tree *local_tree, *remote_tree, *base_tree;
- git_commit *local_commit, *remote_commit, *base_commit;
- git_index *merged_index;
- git_merge_options merge_options;
-
- if (verbose) {
- char outlocal[41], outremote[41];
- outlocal[40] = outremote[40] = 0;
- git_oid_fmt(outlocal, local_id);
- git_oid_fmt(outremote, remote_id);
- fprintf(stderr, "trying to merge local SHA %s remote SHA %s\n", outlocal, outremote);
- }
-
- git_merge_init_options(&merge_options, GIT_MERGE_OPTIONS_VERSION);
- merge_options.tree_flags = GIT_MERGE_TREE_FIND_RENAMES;
- merge_options.file_favor = GIT_MERGE_FILE_FAVOR_UNION;
- merge_options.rename_threshold = 100;
- if (git_commit_lookup(&local_commit, repo, local_id)) {
- fprintf(stderr, "Remote storage and local data diverged. Error: can't get commit (%s)", giterr_last()->message);
- goto diverged_error;
- }
- if (git_commit_tree(&local_tree, local_commit)) {
- fprintf(stderr, "Remote storage and local data diverged. Error: failed local tree lookup (%s)", giterr_last()->message);
- goto diverged_error;
- }
- if (git_commit_lookup(&remote_commit, repo, remote_id)) {
- fprintf(stderr, "Remote storage and local data diverged. Error: can't get commit (%s)", giterr_last()->message);
- goto diverged_error;
- }
- if (git_commit_tree(&remote_tree, remote_commit)) {
- fprintf(stderr, "Remote storage and local data diverged. Error: failed local tree lookup (%s)", giterr_last()->message);
- goto diverged_error;
- }
- if (git_commit_lookup(&base_commit, repo, base)) {
- fprintf(stderr, "Remote storage and local data diverged. Error: can't get commit (%s)", giterr_last()->message);
- goto diverged_error;
- }
- if (git_commit_tree(&base_tree, base_commit)) {
- fprintf(stderr, "Remote storage and local data diverged. Error: failed base tree lookup (%s)", giterr_last()->message);
- goto diverged_error;
- }
- if (git_merge_trees(&merged_index, repo, base_tree, local_tree, remote_tree, &merge_options)) {
- fprintf(stderr, "Remote storage and local data diverged. Error: merge failed (%s)", giterr_last()->message);
- // this is the one where I want to report more detail to the user - can't quite explain why
- return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: merge failed (%s)"), giterr_last()->message);
- }
- if (git_index_has_conflicts(merged_index)) {
- int error;
- const git_index_entry *ancestor = NULL,
- *ours = NULL,
- *theirs = NULL;
- git_index_conflict_iterator *iter = NULL;
- error = git_index_conflict_iterator_new(&iter, merged_index);
- while (git_index_conflict_next(&ancestor, &ours, &theirs, iter)
- != GIT_ITEROVER) {
- /* Mark this conflict as resolved */
- fprintf(stderr, "conflict in %s / %s / %s -- ",
- ours ? ours->path : "-",
- theirs ? theirs->path : "-",
- ancestor ? ancestor->path : "-");
- if ((!ours && theirs && ancestor) ||
- (ours && !theirs && ancestor)) {
- // the file was removed on one side or the other - just remove it
- fprintf(stderr, "looks like a delete on one side; removing the file from the index\n");
- error = git_index_remove(merged_index, ours ? ours->path : theirs->path, GIT_INDEX_STAGE_ANY);
- } else if (ancestor) {
- error = git_index_conflict_remove(merged_index, ours ? ours->path : theirs ? theirs->path : ancestor->path);
- }
- if (error) {
- fprintf(stderr, "error at conflict resplution (%s)", giterr_last()->message);
- }
- }
- git_index_conflict_cleanup(merged_index);
- git_index_conflict_iterator_free(iter);
- report_error(translate("gettextFromC", "Remote storage and local data diverged. Cannot combine local and remote changes"));
- }
- git_oid merge_oid, commit_oid;
- git_tree *merged_tree;
- git_signature *author;
- git_commit *commit;
-
- if (git_index_write_tree_to(&merge_oid, merged_index, repo))
- goto write_error;
- if (git_tree_lookup(&merged_tree, repo, &merge_oid))
- goto write_error;
- if (git_signature_default(&author, repo) < 0)
- if (git_signature_now(&author, "Subsurface", "noemail@given") < 0)
- goto write_error;
- if (git_commit_create_v(&commit_oid, repo, NULL, author, author, NULL, "automatic merge", merged_tree, 2, local_commit, remote_commit))
- goto write_error;
- if (git_commit_lookup(&commit, repo, &commit_oid))
- goto write_error;
- if (git_branch_is_head(*local_p) && !git_repository_is_bare(repo)) {
- git_object *parent;
- git_reference_peel(&parent, *local_p, GIT_OBJ_COMMIT);
- if (update_git_checkout(repo, parent, merged_tree)) {
- goto write_error;
- }
- }
- if (git_reference_set_target(local_p, *local_p, &commit_oid, "Subsurface merge event"))
- goto write_error;
- set_git_id(&commit_oid);
- git_signature_free(author);
- if (verbose)
- fprintf(stderr, "Successfully merged repositories");
- return 0;
-
-diverged_error:
- return report_error(translate("gettextFromC", "Remote storage and local data diverged"));
-
-write_error:
- return report_error(translate("gettextFromC", "Remote storage and local data diverged. Error: writing the data failed (%s)"), giterr_last()->message);
-}
-
-// if accessing the local cache of Subsurface cloud storage fails, we simplify things
-// for the user and simply move the cache away (in case they want to try and extract data)
-// and ask them to retry the operation (which will then refresh the data from the cloud server)
-static int cleanup_local_cache(const char *remote_url, const char *branch)
-{
- char *backup_path = move_local_cache(remote_url, branch);
- report_error(translate("gettextFromC", "Problems with local cache of Subsurface cloud data"));
- report_error(translate("gettextFromC", "Moved cache data to %s. Please try the operation again."), backup_path);
- free(backup_path);
- return -1;
-}
-static int try_to_update(git_repository *repo, git_remote *origin, git_reference *local, git_reference *remote,
- const char *remote_url, const char *branch, enum remote_transport rt)
-{
- git_oid base;
- const git_oid *local_id, *remote_id;
- int ret = 0;
-
- if (verbose)
- fprintf(stderr, "git storage: try to update\n");
- git_storage_update_progress(9, "try to update");
- if (!git_reference_cmp(local, remote))
- return 0;
-
- // Dirty modified state in the working tree? We're not going
- // to update either way
- if (git_status_foreach(repo, check_clean, NULL)) {
- if (is_subsurface_cloud)
- goto cloud_data_error;
- else
- return report_error("local cached copy is dirty, skipping update");
- }
- local_id = git_reference_target(local);
- remote_id = git_reference_target(remote);
-
- if (!local_id || !remote_id) {
- if (is_subsurface_cloud)
- goto cloud_data_error;
- else
- return report_error("Unable to get local or remote SHA1");
- }
- if (git_merge_base(&base, repo, local_id, remote_id)) {
- if (is_subsurface_cloud)
- goto cloud_data_error;
- else
- return report_error("Unable to find common commit of local and remote branches");
- }
- /* Is the remote strictly newer? Use it */
- if (git_oid_equal(&base, local_id))
- return reset_to_remote(repo, local, remote_id);
-
- /* Is the local repo the more recent one? See if we can update upstream */
- if (git_oid_equal(&base, remote_id)) {
- if (verbose)
- fprintf(stderr, "local is newer than remote, update remote\n");
- git_storage_update_progress(10, "git_update_remote, local was newer");
- return update_remote(repo, origin, local, remote, rt);
- }
- /* Merging a bare repository always needs user action */
- if (git_repository_is_bare(repo)) {
- if (is_subsurface_cloud)
- goto cloud_data_error;
- else
- return report_error("Local and remote have diverged, merge of bare branch needed");
- }
- /* Merging will definitely need the head branch too */
- if (git_branch_is_head(local) != 1) {
- if (is_subsurface_cloud)
- goto cloud_data_error;
- else
- return report_error("Local and remote do not match, local branch not HEAD - cannot update");
- }
- /* Ok, let's try to merge these */
- git_storage_update_progress(11, "try to merge");
- ret = try_to_git_merge(repo, &local, remote, &base, local_id, remote_id);
- if (ret == 0)
- return update_remote(repo, origin, local, remote, rt);
- else
- return ret;
-
-cloud_data_error:
- // since we are working with Subsurface cloud storage we want to make the user interaction
- // as painless as possible. So if something went wrong with the local cache, tell the user
- // about it an move it away
- return cleanup_local_cache(remote_url, branch);
-}
-
-static int check_remote_status(git_repository *repo, git_remote *origin, const char *remote, const char *branch, enum remote_transport rt)
-{
- int error = 0;
-
- git_reference *local_ref, *remote_ref;
-
- if (verbose)
- fprintf(stderr, "git storage: check remote status\n");
- git_storage_update_progress(7, "git check remote status");
-
- if (git_branch_lookup(&local_ref, repo, branch, GIT_BRANCH_LOCAL)) {
- if (is_subsurface_cloud)
- return cleanup_local_cache(remote, branch);
- else
- return report_error("Git cache branch %s no longer exists", branch);
- }
- if (git_branch_upstream(&remote_ref, local_ref)) {
- /* so there is no upstream branch for our branch; that's a problem.
- * let's push our branch */
- git_strarray refspec;
- git_reference_list(&refspec, repo);
- git_push_options opts = GIT_PUSH_OPTIONS_INIT;
- opts.callbacks.transfer_progress = &transfer_progress_cb;
- if (rt == RT_SSH)
- opts.callbacks.credentials = credential_ssh_cb;
- else if (rt == RT_HTTPS)
- opts.callbacks.credentials = credential_https_cb;
- opts.callbacks.certificate_check = certificate_check_cb;
- git_storage_update_progress(8, "git remote push (no remote existed)");
- error = git_remote_push(origin, &refspec, &opts);
- } else {
- error = try_to_update(repo, origin, local_ref, remote_ref, remote, branch, rt);
- git_reference_free(remote_ref);
- }
- git_reference_free(local_ref);
- return error;
-}
-
-int sync_with_remote(git_repository *repo, const char *remote, const char *branch, enum remote_transport rt)
-{
- int error;
- git_remote *origin;
- char *proxy_string;
- git_config *conf;
-
- if (prefs.git_local_only) {
- if (verbose)
- fprintf(stderr, "don't sync with remote - read from cache only\n");
- return 0;
- }
- if (verbose)
- fprintf(stderr, "sync with remote %s[%s]\n", remote, branch);
- git_storage_update_progress(2, "sync with remote");
- git_repository_config(&conf, repo);
- if (rt == RT_HTTPS && getProxyString(&proxy_string)) {
- if (verbose)
- fprintf(stderr, "set proxy to \"%s\"\n", proxy_string);
- git_config_set_string(conf, "http.proxy", proxy_string);
- free(proxy_string);
- } else {
- if (verbose)
- fprintf(stderr, "delete proxy setting\n");
- git_config_delete_entry(conf, "http.proxy");
- }
-
- /*
- * NOTE! Remote errors are reported, but are nonfatal:
- * we still successfully return the local repository.
- */
- error = git_remote_lookup(&origin, repo, "origin");
- if (error) {
- if (!is_subsurface_cloud)
- report_error("Repository '%s' origin lookup failed (%s)", remote, giterr_last()->message);
- return 0;
- }
-
- if (rt == RT_HTTPS && !canReachCloudServer()) {
- // this is not an error, just a warning message, so return 0
- report_error("Cannot connect to cloud server, working with local copy");
- git_storage_update_progress(18, "can't reach cloud server, working with local copy");
- return 0;
- }
- if (verbose)
- fprintf(stderr, "git storage: fetch remote\n");
- git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
- opts.callbacks.transfer_progress = &transfer_progress_cb;
- if (rt == RT_SSH)
- opts.callbacks.credentials = credential_ssh_cb;
- else if (rt == RT_HTTPS)
- opts.callbacks.credentials = credential_https_cb;
- opts.callbacks.certificate_check = certificate_check_cb;
- git_storage_update_progress(6, "git fetch remote");
- error = git_remote_fetch(origin, NULL, &opts, NULL);
- // NOTE! A fetch error is not fatal, we just report it
- if (error) {
- if (is_subsurface_cloud)
- report_error("Cannot sync with cloud server, working with offline copy");
- else
- report_error("Unable to fetch remote '%s'", remote);
- if (verbose)
- fprintf(stderr, "remote fetch failed (%s)\n", giterr_last()->message);
- error = 0;
- } else {
- error = check_remote_status(repo, origin, remote, branch, rt);
- }
- git_remote_free(origin);
- git_storage_update_progress(18, "done with sync with remote");
- return error;
-}
-
-static git_repository *update_local_repo(const char *localdir, const char *remote, const char *branch, enum remote_transport rt)
-{
- int error;
- git_repository *repo = NULL;
-
- if (verbose)
- fprintf(stderr, "git storage: update local repo\n");
-
- error = git_repository_open(&repo, localdir);
- if (error) {
- if (is_subsurface_cloud)
- (void)cleanup_local_cache(remote, branch);
- else
- report_error("Unable to open git cache repository at %s: %s", localdir, giterr_last()->message);
- return NULL;
- }
- sync_with_remote(repo, remote, branch, rt);
- return repo;
-}
-
-static int repository_create_cb(git_repository **out, const char *path, int bare, void *payload)
-{
- (void) payload;
- char *proxy_string;
- git_config *conf;
-
- int ret = git_repository_init(out, path, bare);
-
- git_repository_config(&conf, *out);
- if (getProxyString(&proxy_string)) {
- if (verbose)
- fprintf(stderr, "set proxy to \"%s\"\n", proxy_string);
- git_config_set_string(conf, "http.proxy", proxy_string);
- free(proxy_string);
- } else {
- if (verbose)
- fprintf(stderr, "delete proxy setting\n");
- git_config_delete_entry(conf, "http.proxy");
- }
- return ret;
-}
-
-/* this should correctly initialize both the local and remote
- * repository for the Subsurface cloud storage */
-static git_repository *create_and_push_remote(const char *localdir, const char *remote, const char *branch)
-{
- git_repository *repo;
- git_config *conf;
- int len;
- char *variable_name, *merge_head;
-
- if (verbose)
- fprintf(stderr, "git storage: create and push remote\n");
-
- /* first make sure the directory for the local cache exists */
- subsurface_mkdir(localdir);
-
- /* set up the origin to point to our remote */
- git_repository_init_options init_opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
- init_opts.origin_url = remote;
-
- /* now initialize the repository with */
- git_repository_init_ext(&repo, localdir, &init_opts);
-
- /* create a config so we can set the remote tracking branch */
- git_repository_config(&conf, repo);
- len = sizeof("branch..remote") + strlen(branch);
- variable_name = malloc(len);
- snprintf(variable_name, len, "branch.%s.remote", branch);
- git_config_set_string(conf, variable_name, "origin");
- /* we know this is shorter than the previous one, so we reuse the variable*/
- snprintf(variable_name, len, "branch.%s.merge", branch);
- len = sizeof("refs/heads/") + strlen(branch);
- merge_head = malloc(len);
- snprintf(merge_head, len, "refs/heads/%s", branch);
- git_config_set_string(conf, variable_name, merge_head);
-
- /* finally create an empty commit and push it to the remote */
- if (do_git_save(repo, branch, remote, false, true))
- return NULL;
- return(repo);
-}
-
-static git_repository *create_local_repo(const char *localdir, const char *remote, const char *branch, enum remote_transport rt)
-{
- int error;
- git_repository *cloned_repo = NULL;
- git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
-
- if (verbose)
- fprintf(stderr, "git storage: create_local_repo\n");
-
- opts.fetch_opts.callbacks.transfer_progress = &transfer_progress_cb;
- if (rt == RT_SSH)
- opts.fetch_opts.callbacks.credentials = credential_ssh_cb;
- else if (rt == RT_HTTPS)
- opts.fetch_opts.callbacks.credentials = credential_https_cb;
- opts.repository_cb = repository_create_cb;
- opts.fetch_opts.callbacks.certificate_check = certificate_check_cb;
-
- opts.checkout_branch = branch;
- if (rt == RT_HTTPS && !canReachCloudServer())
- return 0;
- if (verbose > 1)
- fprintf(stderr, "git storage: calling git_clone()\n");
- error = git_clone(&cloned_repo, remote, localdir, &opts);
- if (verbose > 1)
- fprintf(stderr, "git storage: returned from git_clone() with error %d\n", error);
- if (error) {
- char *msg = giterr_last()->message;
- int len = sizeof("Reference 'refs/remotes/origin/' not found") + strlen(branch);
- char *pattern = malloc(len);
- snprintf(pattern, len, "Reference 'refs/remotes/origin/%s' not found", branch);
- if (strstr(remote, prefs.cloud_git_url) && strstr(msg, pattern)) {
- /* we're trying to open the remote branch that corresponds
- * to our cloud storage and the branch doesn't exist.
- * So we need to create the branch and push it to the remote */
- cloned_repo = create_and_push_remote(localdir, remote, branch);
-#if !defined(DEBUG) && !defined(SUBSURFACE_MOBILE)
- } else if (is_subsurface_cloud) {
- report_error(translate("gettextFromC", "Error connecting to Subsurface cloud storage"));
-#endif
- } else {
- report_error(translate("gettextFromC", "git clone of %s failed (%s)"), remote, msg);
- }
- free(pattern);
- }
- return cloned_repo;
-}
-
-static struct git_repository *get_remote_repo(const char *localdir, const char *remote, const char *branch)
-{
- struct stat st;
- enum remote_transport rt;
-
- /* figure out the remote transport */
- if (strncmp(remote, "ssh://", 6) == 0)
- rt = RT_SSH;
- else if (strncmp(remote, "https://", 8) == 0)
- rt = RT_HTTPS;
- else
- rt = RT_OTHER;
-
- if (verbose > 1) {
- fprintf(stderr, "git_remote_repo: accessing %s\n", remote);
- }
- git_storage_update_progress(1, "start git interaction");
- /* Do we already have a local cache? */
- if (!stat(localdir, &st)) {
- if (!S_ISDIR(st.st_mode)) {
- if (is_subsurface_cloud)
- (void)cleanup_local_cache(remote, branch);
- else
- report_error("local git cache at '%s' is corrupt");
- return NULL;
- }
- return update_local_repo(localdir, remote, branch, rt);
- }
- if (!prefs.git_local_only)
- return create_local_repo(localdir, remote, branch, rt);
- else
- return 0;
-
-}
-
-/*
- * This turns a remote repository into a local one if possible.
- *
- * The recognized formats are
- * git://host/repo[branch]
- * ssh://host/repo[branch]
- * http://host/repo[branch]
- * https://host/repo[branch]
- * file://repo[branch]
- */
-static struct git_repository *is_remote_git_repository(char *remote, const char *branch)
-{
- char c, *localdir;
- const char *p = remote;
-
- while ((c = *p++) >= 'a' && c <= 'z')
- /* nothing */;
- if (c != ':')
- return NULL;
- if (*p++ != '/' || *p++ != '/')
- return NULL;
-
- /* Special-case "file://", since it's already local */
- if (!strncmp(remote, "file://", 7))
- remote += 7;
-
- /*
- * Ok, we found "[a-z]*://", we've simplified the
- * local repo case (because libgit2 is insanely slow
- * for that), and we think we have a real "remote
- * git" format.
- *
- * We now create the SHA1 hash of the whole thing,
- * including the branch name. That will be our unique
- * unique local repository name.
- *
- * NOTE! We will create a local repository per branch,
- * because
- *
- * (a) libgit2 remote tracking branch support seems to
- * be a bit lacking
- * (b) we'll actually check the branch out so that we
- * can do merges etc too.
- *
- * so even if you have a single remote git repo with
- * multiple branches for different people, the local
- * caches will sadly force that to split into multiple
- * individual repositories.
- */
-
- /*
- * next we need to make sure that any encoded username
- * has been extracted from an https:// based URL
- */
- if (!strncmp(remote, "https://", 8)) {
- char *at = strchr(remote, '@');
- if (at) {
- /* was this the @ that denotes an account? that means it was before the
- * first '/' after the https:// - so let's find a '/' after that and compare */
- char *slash = strchr(remote + 8, '/');
- if (slash && slash > at) {
- /* grab the part between "https://" and "@" as encoded email address
- * (that's our username) and move the rest of the URL forward, remembering
- * to copy the closing NUL as well */
- prefs.cloud_storage_email_encoded = strndup(remote + 8, at - remote - 8);
- memmove(remote + 8, at + 1, strlen(at + 1) + 1);
- }
- }
- }
- localdir = get_local_dir(remote, branch);
- if (!localdir)
- return NULL;
-
- /* remember if the current git storage we are working on is our cloud storage
- * this is used to create more user friendly error message and warnings */
- is_subsurface_cloud = strstr(remote, prefs.cloud_git_url) != NULL;
-
- return get_remote_repo(localdir, remote, branch);
-}
-
-/*
- * If it's not a git repo, return NULL. Be very conservative.
- */
-struct git_repository *is_git_repository(const char *filename, const char **branchp, const char **remote, bool dry_run)
-{
- int flen, blen, ret;
- int offset = 1;
- struct stat st;
- git_repository *repo;
- char *loc, *branch;
-
- flen = strlen(filename);
- if (!flen || filename[--flen] != ']')
- return NULL;
-
- /* Find the matching '[' */
- blen = 0;
- while (flen && filename[--flen] != '[')
- blen++;
-
- /* Ignore slashes at the end of the repo name */
- while (flen && filename[flen-1] == '/') {
- flen--;
- offset++;
- }
-
- if (!flen)
- return NULL;
-
- /*
- * This is the "point of no return": the name matches
- * the git repository name rules, and we will no longer
- * return NULL.
- *
- * We will either return "dummy_git_repository" and the
- * branch pointer will have the _whole_ filename in it,
- * or we will return a real git repository with the
- * branch pointer being filled in with just the branch
- * name.
- *
- * The actual git reading/writing routines can use this
- * to generate proper error messages.
- */
- *branchp = filename;
- loc = format_string("%.*s", flen, filename);
- if (!loc)
- return dummy_git_repository;
-
- branch = format_string("%.*s", blen, filename + flen + offset);
- if (!branch) {
- free(loc);
- return dummy_git_repository;
- }
-
- if (dry_run) {
- *branchp = branch;
- *remote = loc;
- return dummy_git_repository;
- }
- repo = is_remote_git_repository(loc, branch);
- if (repo) {
- if (remote)
- *remote = loc;
- else
- free(loc);
- *branchp = branch;
- return repo;
- }
-
- if (stat(loc, &st) < 0 || !S_ISDIR(st.st_mode)) {
- free(loc);
- free(branch);
- return dummy_git_repository;
- }
-
- ret = git_repository_open(&repo, loc);
- free(loc);
- if (ret < 0) {
- free(branch);
- return dummy_git_repository;
- }
- if (remote)
- *remote = NULL;
- *branchp = branch;
- return repo;
-}
diff --git a/subsurface-core/git-access.h b/subsurface-core/git-access.h
deleted file mode 100644
index 3a0c5160a..000000000
--- a/subsurface-core/git-access.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef GITACCESS_H
-#define GITACCESS_H
-
-#include "git2.h"
-
-#ifdef __cplusplus
-extern "C" {
-#else
-#include <stdbool.h>
-#endif
-
-enum remote_transport { RT_OTHER, RT_HTTPS, RT_SSH };
-
-struct git_oid;
-struct git_repository;
-#define dummy_git_repository ((git_repository *)3ul) /* Random bogus pointer, not NULL */
-extern struct git_repository *is_git_repository(const char *filename, const char **branchp, const char **remote, bool dry_run);
-extern int sync_with_remote(struct git_repository *repo, const char *remote, const char *branch, enum remote_transport rt);
-extern int git_save_dives(struct git_repository *, const char *, const char *remote, bool select_only);
-extern int git_load_dives(struct git_repository *, const char *);
-extern const char *get_sha(git_repository *repo, const char *branch);
-extern int do_git_save(git_repository *repo, const char *branch, const char *remote, bool select_only, bool create_empty);
-extern const char *saved_git_id;
-extern void clear_git_id(void);
-extern void set_git_id(const struct git_oid *);
-void set_git_update_cb(int (*)(int, const char *));
-int git_storage_update_progress(int percent, const char *text);
-char *get_local_dir(const char *remote, const char *branch);
-
-extern int last_git_storage_update_val;
-
-#ifdef __cplusplus
-}
-#endif
-#endif // GITACCESS_H
-
diff --git a/subsurface-core/gpslocation.cpp b/subsurface-core/gpslocation.cpp
deleted file mode 100644
index 075b1c046..000000000
--- a/subsurface-core/gpslocation.cpp
+++ /dev/null
@@ -1,606 +0,0 @@
-#include "gpslocation.h"
-#include "gpslistmodel.h"
-#include "pref.h"
-#include "dive.h"
-#include "helpers.h"
-#include <time.h>
-#include <unistd.h>
-#include <QDebug>
-#include <QVariant>
-#include <QUrlQuery>
-#include <QApplication>
-#include <QTimer>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonArray>
-
-#define GPS_FIX_ADD_URL "http://api.subsurface-divelog.org/api/dive/add/"
-#define GPS_FIX_DELETE_URL "http://api.subsurface-divelog.org/api/dive/delete/"
-#define GPS_FIX_DOWNLOAD_URL "http://api.subsurface-divelog.org/api/dive/get/"
-#define GET_WEBSERVICE_UID_URL "https://cloud.subsurface-divelog.org/webuserid/"
-
-GpsLocation *GpsLocation::m_Instance = NULL;
-
-GpsLocation::GpsLocation(void (*showMsgCB)(const char *), QObject *parent) : QObject(parent)
-{
- Q_ASSERT_X(m_Instance == NULL, "GpsLocation", "GpsLocation recreated");
- m_Instance = this;
- m_GpsSource = 0;
- waitingForPosition = false;
- showMessageCB = showMsgCB;
- // create a QSettings object that's separate from the main application settings
- geoSettings = new QSettings(QSettings::NativeFormat, QSettings::UserScope,
- QString("org.subsurfacedivelog"), QString("subsurfacelocation"), this);
-#ifdef SUBSURFACE_MOBILE
- if (hasLocationsSource())
- status(QString("Found GPS with positioning methods %1").arg(QString::number(m_GpsSource->supportedPositioningMethods(), 16)));
-#endif
- userAgent = getUserAgent();
- loadFromStorage();
-}
-
-GpsLocation *GpsLocation::instance()
-{
- Q_ASSERT(m_Instance != NULL);
-
- return m_Instance;
-}
-
-GpsLocation::~GpsLocation()
-{
- m_Instance = NULL;
-}
-
-QGeoPositionInfoSource *GpsLocation::getGpsSource()
-{
- if (!m_GpsSource) {
- m_GpsSource = QGeoPositionInfoSource::createDefaultSource(this);
- if (m_GpsSource != 0) {
-#ifndef SUBSURFACE_MOBILE
- if (verbose)
-#endif
- status(QString("Created position source %1").arg(m_GpsSource->sourceName()));
- connect(m_GpsSource, SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(newPosition(QGeoPositionInfo)));
- connect(m_GpsSource, SIGNAL(updateTimeout()), this, SLOT(updateTimeout()));
- m_GpsSource->setUpdateInterval(5 * 60 * 1000); // 5 minutes so the device doesn't drain the battery
- } else {
-#ifdef SUBSURFACE_MOBILE
- status("don't have GPS source");
-#endif
- }
- }
- return m_GpsSource;
-}
-
-bool GpsLocation::hasLocationsSource()
-{
- QGeoPositionInfoSource *gpsSource = getGpsSource();
- return gpsSource != 0 && (gpsSource->supportedPositioningMethods() & QGeoPositionInfoSource::SatellitePositioningMethods);
-}
-
-void GpsLocation::serviceEnable(bool toggle)
-{
- QGeoPositionInfoSource *gpsSource = getGpsSource();
- if (!gpsSource) {
- if (toggle)
- status("Can't start location service, no location source available");
- return;
- }
- if (toggle) {
- gpsSource->startUpdates();
- status(QString("Starting Subsurface GPS service with update interval %1").arg(gpsSource->updateInterval()));
- } else {
- gpsSource->stopUpdates();
- status("Stopping Subsurface GPS service");
- }
-}
-
-QString GpsLocation::currentPosition()
-{
- if (!hasLocationsSource())
- return tr("Unknown GPS location");
- if (m_trackers.count() && m_trackers.lastKey() + 300 >= QDateTime::currentMSecsSinceEpoch() / 1000) {
- // we can simply use the last position that we tracked
- gpsTracker gt = m_trackers.last();
- QString gpsString = printGPSCoords(gt.latitude.udeg, gt.longitude.udeg);
- qDebug() << "returning last position" << gpsString;
- return gpsString;
- }
- qDebug() << "requesting new GPS position";
- m_GpsSource->requestUpdate();
- // ok, we need to get the current position and somehow in the callback update the location in the QML UI
- // punting right now
- waitingForPosition = true;
- return QString("waiting for the next gps location");
-}
-
-void GpsLocation::newPosition(QGeoPositionInfo pos)
-{
- int64_t lastTime = 0;
- QGeoCoordinate lastCoord;
- int nr = m_trackers.count();
- if (nr) {
- gpsTracker gt = m_trackers.last();
- lastCoord.setLatitude(gt.latitude.udeg / 1000000.0);
- lastCoord.setLongitude(gt.longitude.udeg / 1000000.0);
- lastTime = gt.when;
- }
- // if we are waiting for a position update or
- // if we have no record stored or if at least the configured minimum
- // time has passed or we moved at least the configured minimum distance
- int64_t delta = (int64_t)pos.timestamp().toTime_t() + gettimezoneoffset() - lastTime;
- if (!nr || waitingForPosition || delta > prefs.time_threshold ||
- lastCoord.distanceTo(pos.coordinate()) > prefs.distance_threshold) {
- QString msg("received new position %1 after delta %2 threshold %3");
- status(qPrintable(msg.arg(pos.coordinate().toString()).arg(delta).arg(prefs.time_threshold)));
- waitingForPosition = false;
- gpsTracker gt;
- gt.when = pos.timestamp().toTime_t();
- gt.when += gettimezoneoffset(gt.when);
- gt.latitude.udeg = rint(pos.coordinate().latitude() * 1000000);
- gt.longitude.udeg = rint(pos.coordinate().longitude() * 1000000);
- addFixToStorage(gt);
- }
-}
-
-void GpsLocation::updateTimeout()
-{
- status("request to get new position timed out");
-}
-
-void GpsLocation::status(QString msg)
-{
- qDebug() << msg;
- if (showMessageCB)
- (*showMessageCB)(qPrintable(msg));
-}
-
-QString GpsLocation::getUserid(QString user, QString passwd)
-{
- qDebug() << "called getUserid";
- QEventLoop loop;
- QTimer timer;
- timer.setSingleShot(true);
-
- QNetworkAccessManager *manager = new QNetworkAccessManager(qApp);
- QUrl url(GET_WEBSERVICE_UID_URL);
- QString data;
- data = user + " " + passwd;
- QNetworkRequest request;
- request.setUrl(url);
- request.setRawHeader("User-Agent", getUserAgent().toUtf8());
- request.setRawHeader("Accept", "text/html");
- request.setRawHeader("Content-type", "application/x-www-form-urlencoded");
- reply = manager->post(request, data.toUtf8());
- connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
- connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
- connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
- this, SLOT(getUseridError(QNetworkReply::NetworkError)));
- timer.start(10000);
- loop.exec();
- if (timer.isActive()) {
- timer.stop();
- if (reply->error() == QNetworkReply::NoError) {
- QString result = reply->readAll();
- status(QString("received ") + result);
- result.remove("WebserviceID:");
- reply->deleteLater();
- return result;
- }
- } else {
- status("Getting Web service ID timed out");
- }
- reply->deleteLater();
- return QString();
-}
-
-int GpsLocation::getGpsNum() const
-{
- return m_trackers.count();
-}
-
-static void copy_gps_location(struct gpsTracker &gps, struct dive *d)
-{
- struct dive_site *ds = get_dive_site_by_uuid(d->dive_site_uuid);
- if (!ds) {
- d->dive_site_uuid = create_dive_site(qPrintable(gps.name), gps.when);
- ds = get_dive_site_by_uuid(d->dive_site_uuid);
- }
- ds->latitude = gps.latitude;
- ds->longitude = gps.longitude;
-}
-
-#define SAME_GROUP 6 * 3600 /* six hours */
-bool GpsLocation::applyLocations()
-{
- int i;
- bool changed = false;
- int last = 0;
- int cnt = m_trackers.count();
- if (cnt == 0)
- return false;
-
- // create a table with the GPS information
- QList<struct gpsTracker> gpsTable = m_trackers.values();
-
- // now walk the dive table and see if we can fill in missing gps data
- struct dive *d;
- for_each_dive(i, d) {
- if (dive_has_gps_location(d))
- continue;
- for (int j = last; j < cnt; j++) {
- if (time_during_dive_with_offset(d, gpsTable[j].when, SAME_GROUP)) {
- if (verbose)
- qDebug() << "processing gpsFix @" << get_dive_date_string(gpsTable[j].when) <<
- "which is withing six hours of dive from" <<
- get_dive_date_string(d->when) << "until" <<
- get_dive_date_string(d->when + d->duration.seconds);
- /*
- * If position is fixed during dive. This is the good one.
- * Asign and mark position, and end gps_location loop
- */
- if (time_during_dive_with_offset(d, gpsTable[j].when, 0)) {
- if (verbose)
- qDebug() << "gpsFix is during the dive, pick that one";
- copy_gps_location(gpsTable[j], d);
- changed = true;
- last = j;
- break;
- } else {
- /*
- * If it is not, check if there are more position fixes in SAME_GROUP range
- */
- if (j + 1 < cnt && time_during_dive_with_offset(d, gpsTable[j+1].when, SAME_GROUP)) {
- if (verbose)
- qDebug() << "look at the next gps fix @" << get_dive_date_string(gpsTable[j+1].when);
- /* first let's test if this one is during the dive */
- if (time_during_dive_with_offset(d, gpsTable[j+1].when, 0)) {
- if (verbose)
- qDebug() << "which is during the dive, pick that one";
- copy_gps_location(gpsTable[j+1], d);
- changed = true;
- last = j + 1;
- break;
- }
- /* we know the gps fixes are sorted; if they are both before the dive, ignore the first,
- * if theay are both after the dive, take the first,
- * if the first is before and the second is after, take the closer one */
- if (gpsTable[j+1].when < d->when) {
- if (verbose)
- qDebug() << "which is closer to the start of the dive, do continue with that";
- continue;
- } else if (gpsTable[j].when > d->when + d->duration.seconds) {
- if (verbose)
- qDebug() << "which is even later after the end of the dive, so pick the previous one";
- copy_gps_location(gpsTable[j], d);
- changed = true;
- last = j;
- break;
- } else {
- /* ok, gpsFix is before, nextgpsFix is after */
- if (d->when - gpsTable[j].when <= gpsTable[j+1].when - (d->when + d->duration.seconds)) {
- if (verbose)
- qDebug() << "pick the one before as it's closer to the start";
- copy_gps_location(gpsTable[j], d);
- changed = true;
- last = j;
- break;
- } else {
- if (verbose)
- qDebug() << "pick the one after as it's closer to the start";
- copy_gps_location(gpsTable[j+1], d);
- changed = true;
- last = j + 1;
- break;
- }
- }
- /*
- * If no more positions in range, the actual is the one. Asign, mark and end loop.
- */
- } else {
- if (verbose)
- qDebug() << "which seems to be the best one for this dive, so pick it";
- copy_gps_location(gpsTable[j], d);
- changed = true;
- last = j;
- break;
- }
- }
- } else {
- /* If position is out of SAME_GROUP range and in the future, mark position for
- * next dive iteration and end the gps_location loop
- */
- if (gpsTable[j].when >= d->when + d->duration.seconds + SAME_GROUP) {
- last = j;
- break;
- }
- }
-
- }
- }
- if (changed)
- mark_divelist_changed(true);
- return changed;
-}
-
-QMap<qint64, gpsTracker> GpsLocation::currentGPSInfo() const
-{
- return m_trackers;
-}
-
-void GpsLocation::loadFromStorage()
-{
- int nr = geoSettings->value(QString("count")).toInt();
- for (int i = 0; i < nr; i++) {
- struct gpsTracker gt;
- gt.when = geoSettings->value(QString("gpsFix%1_time").arg(i)).toLongLong();
- gt.latitude.udeg = geoSettings->value(QString("gpsFix%1_lat").arg(i)).toInt();
- gt.longitude.udeg = geoSettings->value(QString("gpsFix%1_lon").arg(i)).toInt();
- gt.name = geoSettings->value(QString("gpsFix%1_name").arg(i)).toString();
- gt.idx = i;
- m_trackers.insert(gt.when, gt);
- }
-}
-
-void GpsLocation::replaceFixToStorage(gpsTracker &gt)
-{
- if (!m_trackers.keys().contains(gt.when)) {
- addFixToStorage(gt);
- return;
- }
- gpsTracker replacedTracker = m_trackers.value(gt.when);
- geoSettings->setValue(QString("gpsFix%1_time").arg(replacedTracker.idx), gt.when);
- geoSettings->setValue(QString("gpsFix%1_lat").arg(replacedTracker.idx), gt.latitude.udeg);
- geoSettings->setValue(QString("gpsFix%1_lon").arg(replacedTracker.idx), gt.longitude.udeg);
- geoSettings->setValue(QString("gpsFix%1_name").arg(replacedTracker.idx), gt.name);
- replacedTracker.latitude = gt.latitude;
- replacedTracker.longitude = gt.longitude;
- replacedTracker.name = gt.name;
-}
-
-void GpsLocation::addFixToStorage(gpsTracker &gt)
-{
- int nr = m_trackers.count();
- geoSettings->setValue("count", nr + 1);
- geoSettings->setValue(QString("gpsFix%1_time").arg(nr), gt.when);
- geoSettings->setValue(QString("gpsFix%1_lat").arg(nr), gt.latitude.udeg);
- geoSettings->setValue(QString("gpsFix%1_lon").arg(nr), gt.longitude.udeg);
- geoSettings->setValue(QString("gpsFix%1_name").arg(nr), gt.name);
- gt.idx = nr;
- geoSettings->sync();
- m_trackers.insert(gt.when, gt);
-}
-
-void GpsLocation::deleteFixFromStorage(gpsTracker &gt)
-{
- qint64 when = gt.when;
- int cnt = m_trackers.count();
- if (cnt == 0 || !m_trackers.keys().contains(when)) {
- qDebug() << "no gps fix with timestamp" << when;
- return;
- }
- if (geoSettings->value(QString("gpsFix%1_time").arg(gt.idx)).toLongLong() != when) {
- qDebug() << "uh oh - index for tracker has gotten out of sync...";
- return;
- }
- if (cnt > 1) {
- // now we move the last tracker into that spot (so our settings stay consecutive)
- // and delete the last settings entry
- when = geoSettings->value(QString("gpsFix%1_time").arg(cnt - 1)).toLongLong();
- struct gpsTracker movedTracker = m_trackers.value(when);
- movedTracker.idx = gt.idx;
- m_trackers.remove(movedTracker.when);
- m_trackers.insert(movedTracker.when, movedTracker);
- geoSettings->setValue(QString("gpsFix%1_time").arg(movedTracker.idx), when);
- geoSettings->setValue(QString("gpsFix%1_lat").arg(movedTracker.idx), movedTracker.latitude.udeg);
- geoSettings->setValue(QString("gpsFix%1_lon").arg(movedTracker.idx), movedTracker.longitude.udeg);
- geoSettings->setValue(QString("gpsFix%1_name").arg(movedTracker.idx), movedTracker.name);
- geoSettings->remove(QString("gpsFix%1_lat").arg(cnt - 1));
- geoSettings->remove(QString("gpsFix%1_lon").arg(cnt - 1));
- geoSettings->remove(QString("gpsFix%1_time").arg(cnt - 1));
- geoSettings->remove(QString("gpsFix%1_name").arg(cnt - 1));
- }
- geoSettings->setValue("count", cnt - 1);
- geoSettings->sync();
- m_trackers.remove(gt.when);
-}
-
-void GpsLocation::deleteGpsFix(qint64 when)
-{
- struct gpsTracker defaultTracker;
- defaultTracker.when = 0;
- struct gpsTracker deletedTracker = m_trackers.value(when, defaultTracker);
- if (deletedTracker.when != when) {
- qDebug() << "can't find tracker for timestamp" << when;
- return;
- }
- deleteFixFromStorage(deletedTracker);
- m_deletedTrackers.append(deletedTracker);
-}
-
-void GpsLocation::clearGpsData()
-{
- m_trackers.clear();
- geoSettings->clear();
- geoSettings->sync();
-}
-
-void GpsLocation::postError(QNetworkReply::NetworkError error)
-{
- Q_UNUSED(error);
- status(QString("error when sending a GPS fix: %1").arg(reply->errorString()));
-}
-
-void GpsLocation::getUseridError(QNetworkReply::NetworkError error)
-{
- Q_UNUSED(error);
- status(QString("error when retrieving Subsurface webservice user id: %1").arg(reply->errorString()));
-}
-
-void GpsLocation::deleteFixesFromServer()
-{
- QEventLoop loop;
- QTimer timer;
- timer.setSingleShot(true);
-
- QNetworkAccessManager *manager = new QNetworkAccessManager(qApp);
- QUrl url(GPS_FIX_DELETE_URL);
- QList<qint64> keys = m_trackers.keys();
- while (!m_deletedTrackers.isEmpty()) {
- gpsTracker gt = m_deletedTrackers.takeFirst();
- QDateTime dt;
- QUrlQuery data;
- dt.setTime_t(gt.when - gettimezoneoffset(gt.when));
- data.addQueryItem("login", prefs.userid);
- data.addQueryItem("dive_date", dt.toString("yyyy-MM-dd"));
- data.addQueryItem("dive_time", dt.toString("hh:mm"));
- status(data.toString(QUrl::FullyEncoded).toUtf8());
- QNetworkRequest request;
- request.setUrl(url);
- request.setRawHeader("User-Agent", getUserAgent().toUtf8());
- request.setRawHeader("Accept", "text/json");
- request.setRawHeader("Content-type", "application/x-www-form-urlencoded");
- reply = manager->post(request, data.toString(QUrl::FullyEncoded).toUtf8());
- connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
- connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
- connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
- this, SLOT(postError(QNetworkReply::NetworkError)));
- timer.start(10000);
- loop.exec();
- if (timer.isActive()) {
- timer.stop();
- if (reply->error() != QNetworkReply::NoError) {
- QString response = reply->readAll();
- status(QString("Server response:") + reply->readAll());
- }
- } else {
- status("Deleting on the server timed out - try again later");
- m_deletedTrackers.prepend(gt);
- break;
- }
- reply->deleteLater();
- status(QString("completed deleting gps fix %1 - response: ").arg(gt.idx) + reply->readAll());
- }
-}
-
-void GpsLocation::uploadToServer()
-{
- // we want to do this one at a time (the server prefers that)
- QEventLoop loop;
- QTimer timer;
- timer.setSingleShot(true);
-
- QNetworkAccessManager *manager = new QNetworkAccessManager(qApp);
- QUrl url(GPS_FIX_ADD_URL);
- Q_FOREACH(qint64 key, m_trackers.keys()) {
- struct gpsTracker gt = m_trackers.value(key);
- QDateTime dt;
- QUrlQuery data;
- dt.setTime_t(gt.when - gettimezoneoffset(gt.when));
- data.addQueryItem("login", prefs.userid);
- data.addQueryItem("dive_date", dt.toString("yyyy-MM-dd"));
- data.addQueryItem("dive_time", dt.toString("hh:mm"));
- data.addQueryItem("dive_latitude", QString::number(gt.latitude.udeg / 1000000.0, 'f', 9));
- data.addQueryItem("dive_longitude", QString::number(gt.longitude.udeg / 1000000.0, 'f', 9));
- if (gt.name.isEmpty())
- gt.name = "Auto-created dive";
- data.addQueryItem("dive_name", gt.name);
- status(data.toString(QUrl::FullyEncoded).toUtf8());
- QNetworkRequest request;
- request.setUrl(url);
- request.setRawHeader("User-Agent", getUserAgent().toUtf8());
- request.setRawHeader("Accept", "text/json");
- request.setRawHeader("Content-type", "application/x-www-form-urlencoded");
- reply = manager->post(request, data.toString(QUrl::FullyEncoded).toUtf8());
- connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
- connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
- // somehoe I cannot get this to work with the new connect syntax:
- // connect(reply, &QNetworkReply::error, this, &GpsLocation::postError);
- connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
- this, SLOT(postError(QNetworkReply::NetworkError)));
- timer.start(10000);
- loop.exec();
- if (timer.isActive()) {
- timer.stop();
- if (reply->error() != QNetworkReply::NoError) {
- QString response = reply->readAll();
- if (!response.contains("Duplicate entry")) {
- status(QString("Server response:") + reply->readAll());
- break;
- }
- }
- } else {
- status("Uploading to server timed out");
- break;
- }
- reply->deleteLater();
- status(QString("completed sending gps fix %1 - response: ").arg(gt.idx) + reply->readAll());
- }
- // and now remove the ones that were locally deleted
- deleteFixesFromServer();
-}
-
-void GpsLocation::downloadFromServer()
-{
- QEventLoop loop;
- QTimer timer;
- timer.setSingleShot(true);
- QNetworkAccessManager *manager = new QNetworkAccessManager(qApp);
- QUrl url(QString(GPS_FIX_DOWNLOAD_URL "?login=%1").arg(prefs.userid));
- QNetworkRequest request;
- request.setUrl(url);
- request.setRawHeader("User-Agent", getUserAgent().toUtf8());
- request.setRawHeader("Accept", "text/json");
- request.setRawHeader("Content-type", "text/html");
- qDebug() << "downloadFromServer accessing" << url;
- reply = manager->get(request);
- connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
- connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
- connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
- this, SLOT(getUseridError(QNetworkReply::NetworkError)));
- timer.start(10000);
- loop.exec();
- if (timer.isActive()) {
- timer.stop();
- if (!reply->error()) {
- QString response = reply->readAll();
- QJsonDocument json = QJsonDocument::fromJson(response.toLocal8Bit());
- QJsonObject object = json.object();
- if (object.value("download").toString() != "ok") {
- qDebug() << "problems downloading GPS fixes";
- return;
- }
- qDebug() << "already have" << m_trackers.count() << "GPS fixes";
- QJsonArray downloadedFixes = object.value("dives").toArray();
- qDebug() << downloadedFixes.count() << "GPS fixes downloaded";
- for (int i = 0; i < downloadedFixes.count(); i++) {
- QJsonObject fix = downloadedFixes[i].toObject();
- QString date = fix.value("date").toString();
- QString time = fix.value("time").toString();
- QString name = fix.value("name").toString();
- QString latitude = fix.value("latitude").toString();
- QString longitude = fix.value("longitude").toString();
- QDateTime timestamp = QDateTime::fromString(date + " " + time, "yyyy-M-d hh:m:s");
-
- struct gpsTracker gt;
- gt.when = timestamp.toMSecsSinceEpoch() / 1000 + gettimezoneoffset(timestamp.toMSecsSinceEpoch() / 1000);
- gt.latitude.udeg = latitude.toDouble() * 1000000;
- gt.longitude.udeg = longitude.toDouble() * 1000000;
- gt.name = name;
- // add this GPS fix to the QMap and the settings (remove existing fix at the same timestamp first)
- if (m_trackers.keys().contains(gt.when)) {
- qDebug() << "already have a fix at time stamp" << gt.when;
- replaceFixToStorage(gt);
- } else {
- addFixToStorage(gt);
- }
- }
- } else {
- qDebug() << "network error" << reply->error() << reply->errorString() << reply->readAll();
- }
- } else {
- qDebug() << "download timed out";
- status("Download from server timed out");
- }
- reply->deleteLater();
-}
diff --git a/subsurface-core/gpslocation.h b/subsurface-core/gpslocation.h
deleted file mode 100644
index 82b51a291..000000000
--- a/subsurface-core/gpslocation.h
+++ /dev/null
@@ -1,66 +0,0 @@
-#ifndef GPSLOCATION_H
-#define GPSLOCATION_H
-
-#include "units.h"
-#include <QObject>
-#include <QGeoCoordinate>
-#include <QGeoPositionInfoSource>
-#include <QGeoPositionInfo>
-#include <QSettings>
-#include <QNetworkReply>
-#include <QMap>
-
-struct gpsTracker {
- degrees_t latitude;
- degrees_t longitude;
- qint64 when;
- QString name;
- int idx;
-};
-
-class GpsLocation : QObject {
- Q_OBJECT
-public:
- GpsLocation(void (*showMsgCB)(const char *msg), QObject *parent);
- ~GpsLocation();
- static GpsLocation *instance();
- bool applyLocations();
- int getGpsNum() const;
- QString getUserid(QString user, QString passwd);
- bool hasLocationsSource();
- QString currentPosition();
-
- QMap<qint64, gpsTracker> currentGPSInfo() const;
-
-private:
- QGeoPositionInfo lastPos;
- QGeoPositionInfoSource *getGpsSource();
- QGeoPositionInfoSource *m_GpsSource;
- void status(QString msg);
- QSettings *geoSettings;
- QNetworkReply *reply;
- QString userAgent;
- void (*showMessageCB)(const char *msg);
- static GpsLocation *m_Instance;
- bool waitingForPosition;
- QMap<qint64, gpsTracker> m_trackers;
- QList<gpsTracker> m_deletedTrackers;
- void loadFromStorage();
- void addFixToStorage(gpsTracker &gt);
- void replaceFixToStorage(gpsTracker &gt);
- void deleteFixFromStorage(gpsTracker &gt);
- void deleteFixesFromServer();
-
-public slots:
- void serviceEnable(bool toggle);
- void newPosition(QGeoPositionInfo pos);
- void updateTimeout();
- void uploadToServer();
- void downloadFromServer();
- void postError(QNetworkReply::NetworkError error);
- void getUseridError(QNetworkReply::NetworkError error);
- void clearGpsData();
- void deleteGpsFix(qint64 when);
-};
-
-#endif // GPSLOCATION_H
diff --git a/subsurface-core/helpers.h b/subsurface-core/helpers.h
deleted file mode 100644
index f88da015c..000000000
--- a/subsurface-core/helpers.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * helpers.h
- *
- * header file for random helper functions of Subsurface
- *
- */
-#ifndef HELPERS_H
-#define HELPERS_H
-
-#include <QString>
-#include "dive.h"
-#include "qthelper.h"
-
-QString get_depth_string(depth_t depth, bool showunit = false, bool showdecimal = true);
-QString get_depth_string(int mm, bool showunit = false, bool showdecimal = true);
-QString get_depth_unit();
-QString get_weight_string(weight_t weight, bool showunit = false);
-QString get_weight_unit();
-QString get_cylinder_used_gas_string(cylinder_t *cyl, bool showunit = false);
-QString get_temperature_string(temperature_t temp, bool showunit = false);
-QString get_temp_unit();
-QString get_volume_string(volume_t volume, bool showunit = false);
-QString get_volume_unit();
-QString get_pressure_string(pressure_t pressure, bool showunit = false);
-QString get_pressure_unit();
-void set_default_dive_computer(const char *vendor, const char *product);
-void set_default_dive_computer_device(const char *name);
-void set_default_dive_computer_download_mode(int downloadMode);
-QString getSubsurfaceDataPath(QString folderToFind);
-QString getPrintingTemplatePathUser();
-QString getPrintingTemplatePathBundle();
-void copyPath(QString src, QString dst);
-extern const QString get_dc_nickname(const char *model, uint32_t deviceid);
-int gettimezoneoffset(timestamp_t when = 0);
-int parseLengthToMm(const QString &text);
-int parseTemperatureToMkelvin(const QString &text);
-int parseWeightToGrams(const QString &text);
-int parsePressureToMbar(const QString &text);
-int parseGasMixO2(const QString &text);
-int parseGasMixHE(const QString &text);
-QString get_dive_duration_string(timestamp_t when, QString hourText, QString minutesText);
-QString get_dive_date_string(timestamp_t when);
-QString get_short_dive_date_string(timestamp_t when);
-bool is_same_day (timestamp_t trip_when, timestamp_t dive_when);
-QString get_trip_date_string(timestamp_t when, int nr, bool getday);
-QString uiLanguage(QLocale *callerLoc);
-QLocale getLocale();
-void selectedDivesGasUsed(QVector<QPair<QString, int> > &gasUsed);
-QString getUserAgent();
-
-#if defined __APPLE__
-#define TITLE_OR_TEXT(_t, _m) "", _t + "\n" + _m
-#else
-#define TITLE_OR_TEXT(_t, _m) _t, _m
-#endif
-#endif // HELPERS_H
diff --git a/subsurface-core/imagedownloader.cpp b/subsurface-core/imagedownloader.cpp
deleted file mode 100644
index daa49eadf..000000000
--- a/subsurface-core/imagedownloader.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-#include "dive.h"
-#include "metrics.h"
-#include "divelist.h"
-#include "qthelper.h"
-#include "imagedownloader.h"
-#include <unistd.h>
-
-#include <QtConcurrent>
-
-QUrl cloudImageURL(const char *hash)
-{
- return QUrl::fromUserInput(QString("https://cloud.subsurface-divelog.org/images/").append(hash));
-}
-
-ImageDownloader::ImageDownloader(struct picture *pic)
-{
- picture = pic;
-}
-
-ImageDownloader::~ImageDownloader()
-{
- picture_free(picture);
-}
-
-void ImageDownloader::load(bool fromHash){
- QUrl url;
- loadFromHash = fromHash;
- if(fromHash)
- url = cloudImageURL(picture->hash);
- else
- url = QUrl::fromUserInput(QString(picture->filename));
- if (url.isValid()) {
- QEventLoop loop;
- QNetworkRequest request(url);
- connect(&manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(saveImage(QNetworkReply *)));
- QNetworkReply *reply = manager.get(request);
- while (reply->isRunning()) {
- loop.processEvents();
- sleep(1);
- }
- }
-}
-
-void ImageDownloader::saveImage(QNetworkReply *reply)
-{
- QByteArray imageData = reply->readAll();
- QImage image = QImage();
- image.loadFromData(imageData);
- if (image.isNull()) {
- if (loadFromHash)
- load(false);
- return;
- }
- QCryptographicHash hash(QCryptographicHash::Sha1);
- hash.addData(imageData);
- QString path = QStandardPaths::standardLocations(QStandardPaths::CacheLocation).first();
- QDir dir(path);
- if (!dir.exists())
- dir.mkpath(path);
- QFile imageFile(path.append("/").append(hash.result().toHex()));
- if (imageFile.open(QIODevice::WriteOnly)) {
- QDataStream stream(&imageFile);
- stream.writeRawData(imageData.data(), imageData.length());
- imageFile.waitForBytesWritten(-1);
- imageFile.close();
- add_hash(imageFile.fileName(), hash.result());
- learnHash(picture, hash.result());
- }
- reply->manager()->deleteLater();
- reply->deleteLater();
- // This should be called to make the picture actually show.
- // Problem is DivePictureModel is not in subsurface-core.
- // Nevertheless, the image shows when the dive is selected the next time.
- // DivePictureModel::instance()->updateDivePictures();
-
-}
-
-void loadPicture(struct picture *picture, bool fromHash)
-{
- ImageDownloader download(picture);
- download.load(fromHash);
-}
-
-SHashedImage::SHashedImage(struct picture *picture) : QImage()
-{
- QUrl url = QUrl::fromUserInput(localFilePath(QString(picture->filename)));
- if(url.isLocalFile())
- load(url.toLocalFile());
- if (isNull()) {
- // This did not load anything. Let's try to get the image from other sources
- // Let's try to load it locally via its hash
- QString filename = fileFromHash(picture->hash);
- if (filename.isNull()) {
- // That didn't produce a local filename.
- // Try the cloud server
- QtConcurrent::run(loadPicture, clone_picture(picture), true);
- } else {
- // Load locally from translated file name
- load(filename);
- if (!isNull()) {
- // Make sure the hash still matches the image file
- QtConcurrent::run(updateHash, clone_picture(picture));
- } else {
- // Interpret filename as URL
- QtConcurrent::run(loadPicture, clone_picture(picture), false);
- }
- }
- } else {
- // We loaded successfully. Now, make sure hash is up to date.
- QtConcurrent::run(hashPicture, clone_picture(picture));
- }
-}
-
diff --git a/subsurface-core/imagedownloader.h b/subsurface-core/imagedownloader.h
deleted file mode 100644
index f4e3df875..000000000
--- a/subsurface-core/imagedownloader.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef IMAGEDOWNLOADER_H
-#define IMAGEDOWNLOADER_H
-
-#include <QImage>
-#include <QFuture>
-#include <QNetworkReply>
-
-typedef QPair<QString, QByteArray> SHashedFilename;
-
-extern QUrl cloudImageURL(const char *hash);
-
-
-class ImageDownloader : public QObject {
- Q_OBJECT;
-public:
- ImageDownloader(struct picture *picture);
- ~ImageDownloader();
- void load(bool fromHash);
-
-private:
- struct picture *picture;
- QNetworkAccessManager manager;
- bool loadFromHash;
-
-private slots:
- void saveImage(QNetworkReply *reply);
-};
-
-class SHashedImage : public QImage {
-public:
- SHashedImage(struct picture *picture);
-};
-
-#endif // IMAGEDOWNLOADER_H
diff --git a/subsurface-core/isocialnetworkintegration.cpp b/subsurface-core/isocialnetworkintegration.cpp
deleted file mode 100644
index eb1e82a49..000000000
--- a/subsurface-core/isocialnetworkintegration.cpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "isocialnetworkintegration.h"
-
-//Hack for moc.
-ISocialNetworkIntegration::ISocialNetworkIntegration(QObject* parent) : QObject(parent)
-{
-}
diff --git a/subsurface-core/isocialnetworkintegration.h b/subsurface-core/isocialnetworkintegration.h
deleted file mode 100644
index 70ea3d9ab..000000000
--- a/subsurface-core/isocialnetworkintegration.h
+++ /dev/null
@@ -1,73 +0,0 @@
-#ifndef ISOCIALNETWORKINTEGRATION_H
-#define ISOCIALNETWORKINTEGRATION_H
-
-#include <QtPlugin>
-
-/* This Interface represents a Plugin for Social Network integration,
- * with it you may be able to create plugins for facebook, instagram,
- * twitpic, google plus and any other thing you may imagine.
- *
- * We bundle facebook integration as an example.
- */
-
-class ISocialNetworkIntegration : public QObject {
- Q_OBJECT
-public:
- ISocialNetworkIntegration(QObject* parent = 0);
-
- /*!
- * @name socialNetworkName
- * @brief The name of this social network
- * @return The name of this social network
- *
- * The name of this social network will be used to populate the Menu to toggle states
- * between connected/disconnected, and also submit stuff to it.
- */
- virtual QString socialNetworkName() const = 0;
-
- /*!
- * @name socialNetworkIcon
- * @brief The icon of this social network
- * @return The icon of this social network
- *
- * The icon of this social network will be used to populate the menu, and can also be
- * used on a toolbar if requested.
- */
- virtual QString socialNetworkIcon() const = 0;
-
- /*!
- * @name isConnected
- * @brief returns true if connected to this social network, false otherwise
- * @return true if connected to this social network, false otherwise
- */
- virtual bool isConnected() = 0;
-
- /*!
- * @name requestLogin
- * @brief try to login on this social network.
- *
- * Try to login on this social network. All widget implementation that
- * manages login should be done inside this function.
- */
- virtual void requestLogin() = 0;
-
- /*!
- * @name requestLogoff
- * @brief tries to logoff from this social network
- *
- * Try to logoff from this social network.
- */
- virtual void requestLogoff() = 0;
-
- /*!
- * @name uploadCurrentDive
- * @brief send the current dive info to the Social Network
- *
- * Should format all the options and pixmaps from the current dive
- * to update to the social network. All widget stuff related to sendint
- * dive information should be executed inside this function.
- */
- virtual void requestUpload() = 0;
-};
-
-#endif \ No newline at end of file
diff --git a/subsurface-core/libdivecomputer.c b/subsurface-core/libdivecomputer.c
deleted file mode 100644
index 549b894ce..000000000
--- a/subsurface-core/libdivecomputer.c
+++ /dev/null
@@ -1,1081 +0,0 @@
-// Clang has a bug on zero-initialization of C structs.
-#pragma clang diagnostic ignored "-Wmissing-field-initializers"
-
-#include <stdio.h>
-#include <unistd.h>
-#include <inttypes.h>
-#include <string.h>
-#include "gettext.h"
-#include "dive.h"
-#include "device.h"
-#include "divelist.h"
-#include "display.h"
-
-#include <libdivecomputer/uwatec.h>
-#include <libdivecomputer/hw.h>
-#include <libdivecomputer/version.h>
-#include "libdivecomputer.h"
-
-/* Christ. Libdivecomputer has the worst configuration system ever. */
-#ifdef HW_FROG_H
-#define NOT_FROG , 0
-#define LIBDIVECOMPUTER_SUPPORTS_FROG
-#else
-#define NOT_FROG
-#endif
-
-char *dumpfile_name;
-char *logfile_name;
-const char *progress_bar_text = "";
-double progress_bar_fraction = 0.0;
-
-static int stoptime, stopdepth, ndl, po2, cns;
-static bool in_deco, first_temp_is_air;
-
-/*
- * Directly taken from libdivecomputer's examples/common.c to improve
- * the error messages resulting from libdc's return codes
- */
-const char *errmsg (dc_status_t rc)
-{
- switch (rc) {
- case DC_STATUS_SUCCESS:
- return "Success";
- case DC_STATUS_UNSUPPORTED:
- return "Unsupported operation";
- case DC_STATUS_INVALIDARGS:
- return "Invalid arguments";
- case DC_STATUS_NOMEMORY:
- return "Out of memory";
- case DC_STATUS_NODEVICE:
- return "No device found";
- case DC_STATUS_NOACCESS:
- return "Access denied";
- case DC_STATUS_IO:
- return "Input/output error";
- case DC_STATUS_TIMEOUT:
- return "Timeout";
- case DC_STATUS_PROTOCOL:
- return "Protocol error";
- case DC_STATUS_DATAFORMAT:
- return "Data format error";
- case DC_STATUS_CANCELLED:
- return "Cancelled";
- default:
- return "Unknown error";
- }
-}
-
-static dc_status_t create_parser(device_data_t *devdata, dc_parser_t **parser)
-{
- return dc_parser_new(parser, devdata->device);
-}
-
-static int parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_parser_t *parser, unsigned int ngases)
-{
- static bool shown_warning = false;
- unsigned int i;
- int rc;
-
-#if DC_VERSION_CHECK(0, 5, 0) && defined(DC_GASMIX_UNKNOWN)
- unsigned int ntanks = 0;
- rc = dc_parser_get_field(parser, DC_FIELD_TANK_COUNT, 0, &ntanks);
- if (rc == DC_STATUS_SUCCESS) {
- if (ntanks && ntanks != ngases) {
- shown_warning = true;
- report_error("different number of gases (%d) and tanks (%d)", ngases, ntanks);
- }
- }
- dc_tank_t tank = { 0 };
-#endif
-
- for (i = 0; i < ngases; i++) {
- dc_gasmix_t gasmix = { 0 };
- int o2, he;
- bool no_volume = true;
-
- rc = dc_parser_get_field(parser, DC_FIELD_GASMIX, i, &gasmix);
- if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED)
- return rc;
-
- if (i >= MAX_CYLINDERS)
- continue;
-
- o2 = rint(gasmix.oxygen * 1000);
- he = rint(gasmix.helium * 1000);
-
- /* Ignore bogus data - libdivecomputer does some crazy stuff */
- if (o2 + he <= O2_IN_AIR || o2 > 1000) {
- if (!shown_warning) {
- shown_warning = true;
- report_error("unlikely dive gas data from libdivecomputer: o2 = %d he = %d", o2, he);
- }
- o2 = 0;
- }
- if (he < 0 || o2 + he > 1000) {
- if (!shown_warning) {
- shown_warning = true;
- report_error("unlikely dive gas data from libdivecomputer: o2 = %d he = %d", o2, he);
- }
- he = 0;
- }
- dive->cylinder[i].gasmix.o2.permille = o2;
- dive->cylinder[i].gasmix.he.permille = he;
-
-#if DC_VERSION_CHECK(0, 5, 0) && defined(DC_GASMIX_UNKNOWN)
- tank.volume = 0.0;
- if (i < ntanks) {
- rc = dc_parser_get_field(parser, DC_FIELD_TANK, i, &tank);
- if (rc == DC_STATUS_SUCCESS) {
- if (tank.type == DC_TANKVOLUME_IMPERIAL) {
- dive->cylinder[i].type.size.mliter = rint(tank.volume * 1000);
- dive->cylinder[i].type.workingpressure.mbar = rint(tank.workpressure * 1000);
- if (same_string(devdata->model, "Suunto EON Steel")) {
- /* Suunto EON Steele gets this wrong. Badly.
- * but on the plus side it only supports a few imperial sizes,
- * so let's try and guess at least the most common ones.
- * First, the pressures are off by a constant factor. WTF?
- * Then we can round the wet sizes so we get to multiples of 10
- * for cuft sizes (as that's all that you can enter) */
- dive->cylinder[i].type.workingpressure.mbar *= 206.843 / 206.7;
- char name_buffer[9];
- int rounded_size = ml_to_cuft(gas_volume(&dive->cylinder[i],
- dive->cylinder[i].type.workingpressure));
- rounded_size = (int)((rounded_size + 5) / 10) * 10;
- switch (dive->cylinder[i].type.workingpressure.mbar) {
- case 206843:
- snprintf(name_buffer, 9, "AL%d", rounded_size);
- break;
- case 234422: /* this is wrong - HP tanks tend to be 3440, but Suunto only allows 3400 */
- snprintf(name_buffer, 9, "HP%d", rounded_size);
- break;
- case 179263:
- snprintf(name_buffer, 9, "LP+%d", rounded_size);
- break;
- case 165474:
- snprintf(name_buffer, 9, "LP%d", rounded_size);
- break;
- default:
- snprintf(name_buffer, 9, "%d cuft", rounded_size);
- break;
- }
- dive->cylinder[i].type.description = copy_string(name_buffer);
- dive->cylinder[i].type.size.mliter = cuft_to_l(rounded_size) * 1000 /
- mbar_to_atm(dive->cylinder[i].type.workingpressure.mbar);
- }
- } else if (tank.type == DC_TANKVOLUME_METRIC) {
- dive->cylinder[i].type.size.mliter = rint(tank.volume * 1000);
- }
- if (tank.gasmix != i) { // we don't handle this, yet
- shown_warning = true;
- report_error("gasmix %d for tank %d doesn't match", tank.gasmix, i);
- }
- }
- }
- if (!IS_FP_SAME(tank.volume, 0.0))
- no_volume = false;
-
- // this new API also gives us the beginning and end pressure for the tank
- if (!IS_FP_SAME(tank.beginpressure, 0.0) && !IS_FP_SAME(tank.endpressure, 0.0)) {
- dive->cylinder[i].start.mbar = tank.beginpressure * 1000;
- dive->cylinder[i].end.mbar = tank.endpressure * 1000;
- }
-#endif
- if (no_volume) {
- /* for the first tank, if there is no tanksize available from the
- * dive computer, fill in the default tank information (if set) */
- fill_default_cylinder(&dive->cylinder[i]);
- }
- /* whatever happens, make sure there is a name for the cylinder */
- if (same_string(dive->cylinder[i].type.description, ""))
- dive->cylinder[i].type.description = strdup(translate("gettextFromC", "unknown"));
- }
- return DC_STATUS_SUCCESS;
-}
-
-static void handle_event(struct divecomputer *dc, struct sample *sample, dc_sample_value_t value)
-{
- int type, time;
- /* we mark these for translation here, but we store the untranslated strings
- * and only translate them when they are displayed on screen */
- static const char *events[] = {
- QT_TRANSLATE_NOOP("gettextFromC", "none"), QT_TRANSLATE_NOOP("gettextFromC", "deco stop"), QT_TRANSLATE_NOOP("gettextFromC", "rbt"), QT_TRANSLATE_NOOP("gettextFromC", "ascent"), QT_TRANSLATE_NOOP("gettextFromC", "ceiling"), QT_TRANSLATE_NOOP("gettextFromC", "workload"),
- QT_TRANSLATE_NOOP("gettextFromC", "transmitter"), QT_TRANSLATE_NOOP("gettextFromC", "violation"), QT_TRANSLATE_NOOP("gettextFromC", "bookmark"), QT_TRANSLATE_NOOP("gettextFromC", "surface"), QT_TRANSLATE_NOOP("gettextFromC", "safety stop"),
- QT_TRANSLATE_NOOP("gettextFromC", "gaschange"), QT_TRANSLATE_NOOP("gettextFromC", "safety stop (voluntary)"), QT_TRANSLATE_NOOP("gettextFromC", "safety stop (mandatory)"),
- QT_TRANSLATE_NOOP("gettextFromC", "deepstop"), QT_TRANSLATE_NOOP("gettextFromC", "ceiling (safety stop)"), QT_TRANSLATE_NOOP3("gettextFromC", "below floor", "event showing dive is below deco floor and adding deco time"), QT_TRANSLATE_NOOP("gettextFromC", "divetime"),
- QT_TRANSLATE_NOOP("gettextFromC", "maxdepth"), QT_TRANSLATE_NOOP("gettextFromC", "OLF"), QT_TRANSLATE_NOOP("gettextFromC", "pOâ‚‚"), QT_TRANSLATE_NOOP("gettextFromC", "airtime"), QT_TRANSLATE_NOOP("gettextFromC", "rgbm"), QT_TRANSLATE_NOOP("gettextFromC", "heading"),
- QT_TRANSLATE_NOOP("gettextFromC", "tissue level warning"), QT_TRANSLATE_NOOP("gettextFromC", "gaschange"), QT_TRANSLATE_NOOP("gettextFromC", "non stop time")
- };
- const int nr_events = sizeof(events) / sizeof(const char *);
- const char *name;
- /*
- * Just ignore surface events. They are pointless. What "surface"
- * means depends on the dive computer (and possibly even settings
- * in the dive computer). It does *not* necessarily mean "depth 0",
- * so don't even turn it into that.
- */
- if (value.event.type == SAMPLE_EVENT_SURFACE)
- return;
-
- /*
- * Other evens might be more interesting, but for now we just print them out.
- */
- type = value.event.type;
- name = QT_TRANSLATE_NOOP("gettextFromC", "invalid event number");
- if (type < nr_events)
- name = events[type];
-
- time = value.event.time;
- if (sample)
- time += sample->time.seconds;
-
- add_event(dc, time, type, value.event.flags, value.event.value, name);
-}
-
-void
-sample_cb(dc_sample_type_t type, dc_sample_value_t value, void *userdata)
-{
- static unsigned int nsensor = 0;
- struct divecomputer *dc = userdata;
- struct sample *sample;
-
- /*
- * We fill in the "previous" sample - except for DC_SAMPLE_TIME,
- * which creates a new one.
- */
- sample = dc->samples ? dc->sample + dc->samples - 1 : NULL;
-
- /*
- * Ok, sanity check.
- * If first sample is not a DC_SAMPLE_TIME, Allocate a sample for us
- */
- if (sample == NULL && type != DC_SAMPLE_TIME)
- sample = prepare_sample(dc);
-
- switch (type) {
- case DC_SAMPLE_TIME:
- nsensor = 0;
-
- // The previous sample gets some sticky values
- // that may have been around from before, even
- // if there was no new data
- if (sample) {
- sample->in_deco = in_deco;
- sample->ndl.seconds = ndl;
- sample->stoptime.seconds = stoptime;
- sample->stopdepth.mm = stopdepth;
- sample->setpoint.mbar = po2;
- sample->cns = cns;
- }
- // Create a new sample.
- // Mark depth as negative
- sample = prepare_sample(dc);
- sample->time.seconds = value.time;
- sample->depth.mm = -1;
- finish_sample(dc);
- break;
- case DC_SAMPLE_DEPTH:
- sample->depth.mm = rint(value.depth * 1000);
- break;
- case DC_SAMPLE_PRESSURE:
- sample->sensor = value.pressure.tank;
- sample->cylinderpressure.mbar = rint(value.pressure.value * 1000);
- break;
- case DC_SAMPLE_TEMPERATURE:
- sample->temperature.mkelvin = C_to_mkelvin(value.temperature);
- break;
- case DC_SAMPLE_EVENT:
- handle_event(dc, sample, value);
- break;
- case DC_SAMPLE_RBT:
- sample->rbt.seconds = (!strncasecmp(dc->model, "suunto", 6)) ? value.rbt : value.rbt * 60;
- break;
- case DC_SAMPLE_HEARTBEAT:
- sample->heartbeat = value.heartbeat;
- break;
- case DC_SAMPLE_BEARING:
- sample->bearing.degrees = value.bearing;
- break;
-#ifdef DEBUG_DC_VENDOR
- case DC_SAMPLE_VENDOR:
- printf(" <vendor time='%u:%02u' type=\"%u\" size=\"%u\">", FRACTION(sample->time.seconds, 60),
- value.vendor.type, value.vendor.size);
- for (int i = 0; i < value.vendor.size; ++i)
- printf("%02X", ((unsigned char *)value.vendor.data)[i]);
- printf("</vendor>\n");
- break;
-#endif
-#if DC_VERSION_CHECK(0, 3, 0)
- case DC_SAMPLE_SETPOINT:
- /* for us a setpoint means constant pO2 from here */
- sample->setpoint.mbar = po2 = rint(value.setpoint * 1000);
- break;
- case DC_SAMPLE_PPO2:
- if (nsensor < 3)
- sample->o2sensor[nsensor].mbar = rint(value.ppo2 * 1000);
- else
- report_error("%d is more o2 sensors than we can handle", nsensor);
- nsensor++;
- // Set the amount of detected o2 sensors
- if (nsensor > dc->no_o2sensors)
- dc->no_o2sensors = nsensor;
- break;
- case DC_SAMPLE_CNS:
- sample->cns = cns = rint(value.cns * 100);
- break;
- case DC_SAMPLE_DECO:
- if (value.deco.type == DC_DECO_NDL) {
- sample->ndl.seconds = ndl = value.deco.time;
- sample->stopdepth.mm = stopdepth = rint(value.deco.depth * 1000.0);
- sample->in_deco = in_deco = false;
- } else if (value.deco.type == DC_DECO_DECOSTOP ||
- value.deco.type == DC_DECO_DEEPSTOP) {
- sample->in_deco = in_deco = true;
- sample->stopdepth.mm = stopdepth = rint(value.deco.depth * 1000.0);
- sample->stoptime.seconds = stoptime = value.deco.time;
- ndl = 0;
- } else if (value.deco.type == DC_DECO_SAFETYSTOP) {
- sample->in_deco = in_deco = false;
- sample->stopdepth.mm = stopdepth = rint(value.deco.depth * 1000.0);
- sample->stoptime.seconds = stoptime = value.deco.time;
- }
-#endif
- default:
- break;
- }
-}
-
-static void dev_info(device_data_t *devdata, const char *fmt, ...)
-{
- (void) devdata;
- static char buffer[1024];
- va_list ap;
-
- va_start(ap, fmt);
- vsnprintf(buffer, sizeof(buffer), fmt, ap);
- va_end(ap);
- progress_bar_text = buffer;
-}
-
-static int import_dive_number = 0;
-
-static int parse_samples(device_data_t *devdata, struct divecomputer *dc, dc_parser_t *parser)
-{
- (void) devdata;
- // Parse the sample data.
- return dc_parser_samples_foreach(parser, sample_cb, dc);
-}
-
-static int might_be_same_dc(struct divecomputer *a, struct divecomputer *b)
-{
- if (!a->model || !b->model)
- return 1;
- if (strcasecmp(a->model, b->model))
- return 0;
- if (!a->deviceid || !b->deviceid)
- return 1;
- return a->deviceid == b->deviceid;
-}
-
-static int match_one_dive(struct divecomputer *a, struct dive *dive)
-{
- struct divecomputer *b = &dive->dc;
-
- /*
- * Walk the existing dive computer data,
- * see if we have a match (or an anti-match:
- * the same dive computer but a different
- * dive ID).
- */
- do {
- int match = match_one_dc(a, b);
- if (match)
- return match > 0;
- b = b->next;
- } while (b);
-
- /* Ok, no exact dive computer match. Does the date match? */
- b = &dive->dc;
- do {
- if (a->when == b->when && might_be_same_dc(a, b))
- return 1;
- b = b->next;
- } while (b);
-
- return 0;
-}
-
-/*
- * Check if this dive already existed before the import
- */
-static int find_dive(struct divecomputer *match)
-{
- int i;
-
- for (i = 0; i < dive_table.preexisting; i++) {
- struct dive *old = dive_table.dives[i];
-
- if (match_one_dive(match, old))
- return 1;
- }
- return 0;
-}
-
-/*
- * Like g_strdup_printf(), but without the stupid g_malloc/g_free confusion.
- * And we limit the string to some arbitrary size.
- */
-static char *str_printf(const char *fmt, ...)
-{
- va_list args;
- char buf[1024];
-
- va_start(args, fmt);
- vsnprintf(buf, sizeof(buf) - 1, fmt, args);
- va_end(args);
- buf[sizeof(buf) - 1] = 0;
- return strdup(buf);
-}
-
-/*
- * The dive ID for libdivecomputer dives is the first word of the
- * SHA1 of the fingerprint, if it exists.
- *
- * NOTE! This is byte-order dependent, and I don't care.
- */
-static uint32_t calculate_diveid(const unsigned char *fingerprint, unsigned int fsize)
-{
- uint32_t csum[5];
-
- if (!fingerprint || !fsize)
- return 0;
-
- SHA1(fingerprint, fsize, (unsigned char *)csum);
- return csum[0];
-}
-
-#ifdef DC_FIELD_STRING
-static uint32_t calculate_string_hash(const char *str)
-{
- return calculate_diveid((const unsigned char *)str, strlen(str));
-}
-
-static void parse_string_field(struct dive *dive, dc_field_string_t *str)
-{
- // Our dive ID is the string hash of the "Dive ID" string
- if (!strcmp(str->desc, "Dive ID")) {
- if (!dive->dc.diveid)
- dive->dc.diveid = calculate_string_hash(str->value);
- return;
- }
- add_extra_data(&dive->dc, str->desc, str->value);
- if (!strcmp(str->desc, "Serial")) {
- dive->dc.serial = strdup(str->value);
- /* should we just overwrite this whenever we have the "Serial" field?
- * It's a much better deviceid then what we have so far... for now I'm leaving it as is */
- if (!dive->dc.deviceid)
- dive->dc.deviceid = calculate_string_hash(str->value);
- return;
- }
- if (!strcmp(str->desc, "FW Version")) {
- dive->dc.fw_version = strdup(str->value);
- return;
- }
-}
-#endif
-
-static dc_status_t libdc_header_parser(dc_parser_t *parser, struct device_data_t *devdata, struct dive *dive)
-{
- dc_status_t rc = 0;
- dc_datetime_t dt = { 0 };
- struct tm tm;
-
- rc = dc_parser_get_datetime(parser, &dt);
- if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
- dev_info(devdata, translate("gettextFromC", "Error parsing the datetime"));
- return rc;
- }
-
- dive->dc.deviceid = devdata->deviceid;
-
- if (rc == DC_STATUS_SUCCESS) {
- tm.tm_year = dt.year;
- tm.tm_mon = dt.month - 1;
- tm.tm_mday = dt.day;
- tm.tm_hour = dt.hour;
- tm.tm_min = dt.minute;
- tm.tm_sec = dt.second;
- dive->when = dive->dc.when = utc_mktime(&tm);
- }
-
- // Parse the divetime.
- const char *date_string = get_dive_date_c_string(dive->when);
- dev_info(devdata, translate("gettextFromC", "Dive %d: %s"), import_dive_number, date_string);
- free((void *)date_string);
-
- unsigned int divetime = 0;
- rc = dc_parser_get_field(parser, DC_FIELD_DIVETIME, 0, &divetime);
- if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
- dev_info(devdata, translate("gettextFromC", "Error parsing the divetime"));
- return rc;
- }
- if (rc == DC_STATUS_SUCCESS)
- dive->dc.duration.seconds = divetime;
-
- // Parse the maxdepth.
- double maxdepth = 0.0;
- rc = dc_parser_get_field(parser, DC_FIELD_MAXDEPTH, 0, &maxdepth);
- if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
- dev_info(devdata, translate("gettextFromC", "Error parsing the maxdepth"));
- return rc;
- }
- if (rc == DC_STATUS_SUCCESS)
- dive->dc.maxdepth.mm = rint(maxdepth * 1000);
-
-#if DC_VERSION_CHECK(0, 5, 0) && defined(DC_GASMIX_UNKNOWN)
- // if this is defined then we have a fairly late version of libdivecomputer
- // from the 0.5 development cylcle - most likely temperatures and tank sizes
- // are supported
-
- // Parse temperatures
- double temperature;
- dc_field_type_t temp_fields[] = {DC_FIELD_TEMPERATURE_SURFACE,
- DC_FIELD_TEMPERATURE_MAXIMUM,
- DC_FIELD_TEMPERATURE_MINIMUM};
- for (int i = 0; i < 3; i++) {
- rc = dc_parser_get_field(parser, temp_fields[i], 0, &temperature);
- if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
- dev_info(devdata, translate("gettextFromC", "Error parsing temperature"));
- return rc;
- }
- if (rc == DC_STATUS_SUCCESS)
- switch(i) {
- case 0:
- dive->dc.airtemp.mkelvin = C_to_mkelvin(temperature);
- break;
- case 1: // we don't distinguish min and max water temp here, so take min if given, max otherwise
- case 2:
- dive->dc.watertemp.mkelvin = C_to_mkelvin(temperature);
- break;
- }
- }
-#endif
-
- // Parse the gas mixes.
- unsigned int ngases = 0;
- rc = dc_parser_get_field(parser, DC_FIELD_GASMIX_COUNT, 0, &ngases);
- if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
- dev_info(devdata, translate("gettextFromC", "Error parsing the gas mix count"));
- return rc;
- }
-
-#if DC_VERSION_CHECK(0, 3, 0)
- // Check if the libdivecomputer version already supports salinity & atmospheric
- dc_salinity_t salinity = {
- .type = DC_WATER_SALT,
- .density = SEAWATER_SALINITY / 10.0
- };
- rc = dc_parser_get_field(parser, DC_FIELD_SALINITY, 0, &salinity);
- if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
- dev_info(devdata, translate("gettextFromC", "Error obtaining water salinity"));
- return rc;
- }
- if (rc == DC_STATUS_SUCCESS)
- dive->dc.salinity = rint(salinity.density * 10.0);
-
- double surface_pressure = 0;
- rc = dc_parser_get_field(parser, DC_FIELD_ATMOSPHERIC, 0, &surface_pressure);
- if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
- dev_info(devdata, translate("gettextFromC", "Error obtaining surface pressure"));
- return rc;
- }
- if (rc == DC_STATUS_SUCCESS)
- dive->dc.surface_pressure.mbar = rint(surface_pressure * 1000.0);
-#endif
-
-#ifdef DC_FIELD_STRING
- // The dive parsing may give us more device information
- int idx;
- for (idx = 0; idx < 100; idx++) {
- dc_field_string_t str = { NULL };
- rc = dc_parser_get_field(parser, DC_FIELD_STRING, idx, &str);
- if (rc != DC_STATUS_SUCCESS)
- break;
- if (!str.desc || !str.value)
- break;
- parse_string_field(dive, &str);
- }
-#endif
-
-#if DC_VERSION_CHECK(0, 5, 0) && defined(DC_GASMIX_UNKNOWN)
- dc_divemode_t divemode;
- rc = dc_parser_get_field(parser, DC_FIELD_DIVEMODE, 0, &divemode);
- if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
- dev_info(devdata, translate("gettextFromC", "Error obtaining divemode"));
- return rc;
- }
- if (rc == DC_STATUS_SUCCESS)
- switch(divemode) {
- case DC_DIVEMODE_FREEDIVE:
- dive->dc.divemode = FREEDIVE;
- break;
- case DC_DIVEMODE_GAUGE:
- case DC_DIVEMODE_OC: /* Open circuit */
- dive->dc.divemode = OC;
- break;
- case DC_DIVEMODE_CC: /* Closed circuit */
- dive->dc.divemode = CCR;
- break;
- }
-#endif
-
- rc = parse_gasmixes(devdata, dive, parser, ngases);
- if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
- dev_info(devdata, translate("gettextFromC", "Error parsing the gas mix"));
- return rc;
- }
-
- return DC_STATUS_SUCCESS;
-}
-
-/* returns true if we want libdivecomputer's dc_device_foreach() to continue,
- * false otherwise */
-static int dive_cb(const unsigned char *data, unsigned int size,
- const unsigned char *fingerprint, unsigned int fsize,
- void *userdata)
-{
- int rc;
- dc_parser_t *parser = NULL;
- device_data_t *devdata = userdata;
- struct dive *dive = NULL;
-
- /* reset the deco / ndl data */
- ndl = stoptime = stopdepth = 0;
- in_deco = false;
-
- rc = create_parser(devdata, &parser);
- if (rc != DC_STATUS_SUCCESS) {
- dev_info(devdata, translate("gettextFromC", "Unable to create parser for %s %s"), devdata->vendor, devdata->product);
- return false;
- }
-
- rc = dc_parser_set_data(parser, data, size);
- if (rc != DC_STATUS_SUCCESS) {
- dev_info(devdata, translate("gettextFromC", "Error registering the data"));
- goto error_exit;
- }
-
- import_dive_number++;
- dive = alloc_dive();
-
- // Parse the dive's header data
- rc = libdc_header_parser (parser, devdata, dive);
- if (rc != DC_STATUS_SUCCESS) {
- dev_info(devdata, translate("getextFromC", "Error parsing the header"));
- goto error_exit;
- }
-
- dive->dc.model = strdup(devdata->model);
- dive->dc.diveid = calculate_diveid(fingerprint, fsize);
-
- // Initialize the sample data.
- rc = parse_samples(devdata, &dive->dc, parser);
- if (rc != DC_STATUS_SUCCESS) {
- dev_info(devdata, translate("gettextFromC", "Error parsing the samples"));
- goto error_exit;
- }
-
- /* If we already saw this dive, abort. */
- if (!devdata->force_download && find_dive(&dive->dc))
- goto error_exit;
-
- dc_parser_destroy(parser);
-
- /* Various libdivecomputer interface fixups */
- if (dive->dc.airtemp.mkelvin == 0 && first_temp_is_air && dive->dc.samples) {
- dive->dc.airtemp = dive->dc.sample[0].temperature;
- dive->dc.sample[0].temperature.mkelvin = 0;
- }
-
- if (devdata->create_new_trip) {
- if (!devdata->trip)
- devdata->trip = create_and_hookup_trip_from_dive(dive);
- else
- add_dive_to_trip(dive, devdata->trip);
- }
-
- dive->downloaded = true;
- record_dive_to_table(dive, devdata->download_table);
- mark_divelist_changed(true);
- return true;
-
-error_exit:
- dc_parser_destroy(parser);
- free(dive);
- return false;
-
-}
-
-/*
- * The device ID for libdivecomputer devices is the first 32-bit word
- * of the SHA1 hash of the model/firmware/serial numbers.
- *
- * NOTE! This is byte-order-dependent. And I can't find it in myself to
- * care.
- */
-static uint32_t calculate_sha1(unsigned int model, unsigned int firmware, unsigned int serial)
-{
- SHA_CTX ctx;
- uint32_t csum[5];
-
- SHA1_Init(&ctx);
- SHA1_Update(&ctx, &model, sizeof(model));
- SHA1_Update(&ctx, &firmware, sizeof(firmware));
- SHA1_Update(&ctx, &serial, sizeof(serial));
- SHA1_Final((unsigned char *)csum, &ctx);
- return csum[0];
-}
-
-/*
- * libdivecomputer has returned two different serial numbers for the
- * same device in different versions. First it used to just do the four
- * bytes as one 32-bit number, then it turned it into a decimal number
- * with each byte giving two digits (0-99).
- *
- * The only way we can tell is by looking at the format of the number,
- * so we'll just fix it to the first format.
- */
-static unsigned int undo_libdivecomputer_suunto_nr_changes(unsigned int serial)
-{
- unsigned char b0, b1, b2, b3;
-
- /*
- * The second format will never have more than 8 decimal
- * digits, so do a cheap check first
- */
- if (serial >= 100000000)
- return serial;
-
- /* The original format seems to be four bytes of values 00-99 */
- b0 = (serial >> 0) & 0xff;
- b1 = (serial >> 8) & 0xff;
- b2 = (serial >> 16) & 0xff;
- b3 = (serial >> 24) & 0xff;
-
- /* Looks like an old-style libdivecomputer serial number */
- if ((b0 < 100) && (b1 < 100) && (b2 < 100) && (b3 < 100))
- return serial;
-
- /* Nope, it was converted. */
- b0 = serial % 100;
- serial /= 100;
- b1 = serial % 100;
- serial /= 100;
- b2 = serial % 100;
- serial /= 100;
- b3 = serial % 100;
-
- serial = b0 + (b1 << 8) + (b2 << 16) + (b3 << 24);
- return serial;
-}
-
-static unsigned int fixup_suunto_versions(device_data_t *devdata, const dc_event_devinfo_t *devinfo)
-{
- unsigned int serial = devinfo->serial;
- char serial_nr[13] = "";
- char firmware[13] = "";
-
- first_temp_is_air = 1;
-
- serial = undo_libdivecomputer_suunto_nr_changes(serial);
-
- if (serial) {
- snprintf(serial_nr, sizeof(serial_nr), "%02d%02d%02d%02d",
- (devinfo->serial >> 24) & 0xff,
- (devinfo->serial >> 16) & 0xff,
- (devinfo->serial >> 8) & 0xff,
- (devinfo->serial >> 0) & 0xff);
- }
- if (devinfo->firmware) {
- snprintf(firmware, sizeof(firmware), "%d.%d.%d",
- (devinfo->firmware >> 16) & 0xff,
- (devinfo->firmware >> 8) & 0xff,
- (devinfo->firmware >> 0) & 0xff);
- }
- create_device_node(devdata->model, devdata->deviceid, serial_nr, firmware, "");
-
- return serial;
-}
-
-static void event_cb(dc_device_t *device, dc_event_type_t event, const void *data, void *userdata)
-{
- (void) device;
- const dc_event_progress_t *progress = data;
- const dc_event_devinfo_t *devinfo = data;
- const dc_event_clock_t *clock = data;
- const dc_event_vendor_t *vendor = data;
- device_data_t *devdata = userdata;
- unsigned int serial;
-
- switch (event) {
- case DC_EVENT_WAITING:
- dev_info(devdata, translate("gettextFromC", "Event: waiting for user action"));
- break;
- case DC_EVENT_PROGRESS:
- if (!progress->maximum)
- break;
- progress_bar_fraction = (double)progress->current / (double)progress->maximum;
- break;
- case DC_EVENT_DEVINFO:
- dev_info(devdata, translate("gettextFromC", "model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)"),
- devinfo->model, devinfo->model,
- devinfo->firmware, devinfo->firmware,
- devinfo->serial, devinfo->serial);
- if (devdata->libdc_logfile) {
- fprintf(devdata->libdc_logfile, "Event: model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)\n",
- devinfo->model, devinfo->model,
- devinfo->firmware, devinfo->firmware,
- devinfo->serial, devinfo->serial);
- }
- /*
- * libdivecomputer doesn't give serial numbers in the proper string form,
- * so we have to see if we can do some vendor-specific munging.
- */
- serial = devinfo->serial;
- if (!strcmp(devdata->vendor, "Suunto"))
- serial = fixup_suunto_versions(devdata, devinfo);
- devdata->deviceid = calculate_sha1(devinfo->model, devinfo->firmware, serial);
- /* really, serial and firmware version are NOT numbers. We'll try to save them here
- * in something that might work, but this really needs to be handled with the
- * DC_FIELD_STRING interface instead */
- devdata->libdc_serial = devinfo->serial;
- devdata->libdc_firmware = devinfo->firmware;
- break;
- case DC_EVENT_CLOCK:
- dev_info(devdata, translate("gettextFromC", "Event: systime=%" PRId64 ", devtime=%u\n"),
- (uint64_t)clock->systime, clock->devtime);
- if (devdata->libdc_logfile) {
- fprintf(devdata->libdc_logfile, "Event: systime=%" PRId64 ", devtime=%u\n",
- (uint64_t)clock->systime, clock->devtime);
- }
- break;
- case DC_EVENT_VENDOR:
- if (devdata->libdc_logfile) {
- fprintf(devdata->libdc_logfile, "Event: vendor=");
- for (unsigned int i = 0; i < vendor->size; ++i)
- fprintf(devdata->libdc_logfile, "%02X", vendor->data[i]);
- fprintf(devdata->libdc_logfile, "\n");
- }
- break;
- default:
- break;
- }
-}
-
-int import_thread_cancelled;
-
-static int cancel_cb(void *userdata)
-{
- (void) userdata;
- return import_thread_cancelled;
-}
-
-static const char *do_device_import(device_data_t *data)
-{
- dc_status_t rc;
- dc_device_t *device = data->device;
-
- data->model = str_printf("%s %s", data->vendor, data->product);
-
- // Register the event handler.
- int events = DC_EVENT_WAITING | DC_EVENT_PROGRESS | DC_EVENT_DEVINFO | DC_EVENT_CLOCK | DC_EVENT_VENDOR;
- rc = dc_device_set_events(device, events, event_cb, data);
- if (rc != DC_STATUS_SUCCESS)
- return translate("gettextFromC", "Error registering the event handler.");
-
- // Register the cancellation handler.
- rc = dc_device_set_cancel(device, cancel_cb, data);
- if (rc != DC_STATUS_SUCCESS)
- return translate("gettextFromC", "Error registering the cancellation handler.");
-
- if (data->libdc_dump) {
- dc_buffer_t *buffer = dc_buffer_new(0);
-
- rc = dc_device_dump(device, buffer);
- if (rc == DC_STATUS_SUCCESS && dumpfile_name) {
- FILE *fp = subsurface_fopen(dumpfile_name, "wb");
- if (fp != NULL) {
- fwrite(dc_buffer_get_data(buffer), 1, dc_buffer_get_size(buffer), fp);
- fclose(fp);
- }
- }
-
- dc_buffer_free(buffer);
- } else {
- rc = dc_device_foreach(device, dive_cb, data);
- }
-
- if (rc != DC_STATUS_SUCCESS) {
- progress_bar_fraction = 0.0;
- return translate("gettextFromC", "Dive data import error");
- }
-
- /* All good */
- return NULL;
-}
-
-void logfunc(dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *msg, void *userdata)
-{
- (void) context;
- const char *loglevels[] = { "NONE", "ERROR", "WARNING", "INFO", "DEBUG", "ALL" };
-
- FILE *fp = (FILE *)userdata;
-
- if (loglevel == DC_LOGLEVEL_ERROR || loglevel == DC_LOGLEVEL_WARNING) {
- fprintf(fp, "%s: %s [in %s:%d (%s)]\n", loglevels[loglevel], msg, file, line, function);
- } else {
- fprintf(fp, "%s: %s\n", loglevels[loglevel], msg);
- }
-}
-
-const char *do_libdivecomputer_import(device_data_t *data)
-{
- dc_status_t rc;
- const char *err;
- FILE *fp = NULL;
-
- import_dive_number = 0;
- first_temp_is_air = 0;
- data->device = NULL;
- data->context = NULL;
-
- if (data->libdc_log && logfile_name)
- fp = subsurface_fopen(logfile_name, "w");
-
- data->libdc_logfile = fp;
-
- rc = dc_context_new(&data->context);
- if (rc != DC_STATUS_SUCCESS)
- return translate("gettextFromC", "Unable to create libdivecomputer context");
-
- if (fp) {
- dc_context_set_loglevel(data->context, DC_LOGLEVEL_ALL);
- dc_context_set_logfunc(data->context, logfunc, fp);
- }
-
- err = translate("gettextFromC", "Unable to open %s %s (%s)");
-
-#if defined(SSRF_CUSTOM_SERIAL)
- dc_serial_t *serial_device = NULL;
-
- if (data->bluetooth_mode) {
-#if defined(BT_SUPPORT) && defined(SSRF_CUSTOM_SERIAL)
- rc = dc_serial_qt_open(&serial_device, data->context, data->devname);
-#endif
-#ifdef SERIAL_FTDI
- } else if (!strcmp(data->devname, "ftdi")) {
- rc = dc_serial_ftdi_open(&serial_device, data->context);
-#endif
- }
-
- if (rc != DC_STATUS_SUCCESS) {
- report_error(errmsg(rc));
- } else if (serial_device) {
- rc = dc_device_custom_open(&data->device, data->context, data->descriptor, serial_device);
- } else {
-#else
- {
-#endif
- rc = dc_device_open(&data->device, data->context, data->descriptor, data->devname);
-
- if (rc != DC_STATUS_SUCCESS && subsurface_access(data->devname, R_OK | W_OK) != 0)
- err = translate("gettextFromC", "Insufficient privileges to open the device %s %s (%s)");
- }
-
- if (rc == DC_STATUS_SUCCESS) {
- err = do_device_import(data);
- /* TODO: Show the logfile to the user on error. */
- dc_device_close(data->device);
- data->device = NULL;
- }
-
- dc_context_free(data->context);
- data->context = NULL;
-
- if (fp) {
- fclose(fp);
- }
-
- return err;
-}
-
-/*
- * Parse data buffers instead of dc devices downloaded data.
- * Intended to be used to parse profile data from binary files during import tasks.
- * Actually included Uwatec families because of works on datatrak and smartrak logs
- * and OSTC families for OSTCTools logs import.
- * For others, simply include them in the switch (check parameters).
- * Note that dc_descriptor_t in data *must* have been filled using dc_descriptor_iterator()
- * calls.
- */
-dc_status_t libdc_buffer_parser(struct dive *dive, device_data_t *data, unsigned char *buffer, int size)
-{
- dc_status_t rc;
- dc_parser_t *parser = NULL;
-
- switch (data->descriptor->type) {
- case DC_FAMILY_UWATEC_ALADIN:
- case DC_FAMILY_UWATEC_MEMOMOUSE:
- rc = uwatec_memomouse_parser_create(&parser, data->context, 0, 0);
- break;
- case DC_FAMILY_UWATEC_SMART:
- case DC_FAMILY_UWATEC_MERIDIAN:
- rc = uwatec_smart_parser_create (&parser, data->context, data->descriptor->model, 0, 0);
- break;
- case DC_FAMILY_HW_OSTC:
-#if defined(SSRF_CUSTOM_SERIAL)
- rc = hw_ostc_parser_create (&parser, data->context, data->deviceid, 0);
-#else
- rc = hw_ostc_parser_create (&parser, data->context, data->deviceid);
-#endif
- break;
- case DC_FAMILY_HW_FROG:
- case DC_FAMILY_HW_OSTC3:
-#if defined(SSRF_CUSTOM_SERIAL)
- rc = hw_ostc_parser_create (&parser, data->context, data->deviceid, 1);
-#else
- rc = hw_ostc_parser_create (&parser, data->context, data->deviceid);
-#endif
- break;
- default:
- report_error("Device type not handled!");
- return DC_STATUS_UNSUPPORTED;
- }
- if (rc != DC_STATUS_SUCCESS) {
- report_error("Error creating parser.");
- dc_parser_destroy (parser);
- return rc;
- }
- rc = dc_parser_set_data(parser, buffer, size);
- if (rc != DC_STATUS_SUCCESS) {
- report_error("Error registering the data.");
- dc_parser_destroy (parser);
- return rc;
- }
- // Do not parse Aladin/Memomouse headers as they are fakes
- // Do not return on error, we can still parse the samples
- if (data->descriptor->type != DC_FAMILY_UWATEC_ALADIN && data->descriptor->type != DC_FAMILY_UWATEC_MEMOMOUSE) {
- rc = libdc_header_parser (parser, data, dive);
- if (rc != DC_STATUS_SUCCESS) {
- report_error("Error parsing the dive header data. Dive # %d\nStatus = %s", dive->number, errmsg(rc));
- }
- }
- rc = dc_parser_samples_foreach (parser, sample_cb, &dive->dc);
- if (rc != DC_STATUS_SUCCESS) {
- report_error("Error parsing the sample data. Dive # %d\nStatus = %s", dive->number, errmsg(rc));
- dc_parser_destroy (parser);
- return rc;
- }
- dc_parser_destroy(parser);
- return(DC_STATUS_SUCCESS);
-}
diff --git a/subsurface-core/libdivecomputer.h b/subsurface-core/libdivecomputer.h
deleted file mode 100644
index 99f1c2490..000000000
--- a/subsurface-core/libdivecomputer.h
+++ /dev/null
@@ -1,72 +0,0 @@
-#ifndef LIBDIVECOMPUTER_H
-#define LIBDIVECOMPUTER_H
-
-
-/* libdivecomputer */
-
-#ifdef DC_VERSION /* prevent a warning with wingdi.h */
-#undef DC_VERSION
-#endif
-#ifdef HAVE_LIBDIVECOMPUTER
-#include <libdivecomputer/version.h>
-#endif
-#include <libdivecomputer/device.h>
-#include <libdivecomputer/parser.h>
-
-#include "dive.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct dc_descriptor_t {
- const char *vendor;
- const char *product;
- dc_family_t type;
- unsigned int model;
-};
-
-/* don't forget to include the UI toolkit specific display-XXX.h first
- to get the definition of progressbar_t */
-typedef struct device_data_t
-{
- dc_descriptor_t *descriptor;
- const char *vendor, *product, *devname;
- const char *model;
- uint32_t libdc_firmware, libdc_serial;
- uint32_t deviceid, diveid;
- dc_device_t *device;
- dc_context_t *context;
- struct dive_trip *trip;
- int preexisting;
- bool force_download;
- bool create_new_trip;
- bool libdc_log;
- bool libdc_dump;
- bool bluetooth_mode;
- FILE *libdc_logfile;
- struct dive_table *download_table;
-} device_data_t;
-
-const char *errmsg (dc_status_t rc);
-const char *do_libdivecomputer_import(device_data_t *data);
-const char *do_uemis_import(device_data_t *data);
-dc_status_t libdc_buffer_parser(struct dive *dive, device_data_t *data, unsigned char *buffer, int size);
-void logfunc(dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *msg, void *userdata);
-
-extern int import_thread_cancelled;
-extern const char *progress_bar_text;
-extern double progress_bar_fraction;
-extern char *logfile_name;
-extern char *dumpfile_name;
-
-#if SSRF_CUSTOM_SERIAL
-extern dc_status_t dc_serial_qt_open(dc_serial_t **out, dc_context_t *context, const char *devaddr);
-extern dc_status_t dc_serial_ftdi_open(dc_serial_t **out, dc_context_t *context);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // LIBDIVECOMPUTER_H
diff --git a/subsurface-core/linux.c b/subsurface-core/linux.c
deleted file mode 100644
index b81f6bf53..000000000
--- a/subsurface-core/linux.c
+++ /dev/null
@@ -1,232 +0,0 @@
-/* linux.c */
-/* implements Linux specific functions */
-#include "dive.h"
-#include "display.h"
-#include "membuffer.h"
-#include <string.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <fnmatch.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <pwd.h>
-
-// the DE should provide us with a default font and font size...
-const char linux_system_divelist_default_font[] = "Sans";
-const char *system_divelist_default_font = linux_system_divelist_default_font;
-double system_divelist_default_font_size = -1.0;
-
-void subsurface_OS_pref_setup(void)
-{
- // nothing
-}
-
-bool subsurface_ignore_font(const char *font)
-{
- // there are no old default fonts to ignore
- (void)font;
- return false;
-}
-
-void subsurface_user_info(struct user_info *user)
-{
- struct passwd *pwd = getpwuid(getuid());
- const char *username = getenv("USER");
-
- if (pwd) {
- if (pwd->pw_gecos && *pwd->pw_gecos)
- user->name = pwd->pw_gecos;
- if (!username)
- username = pwd->pw_name;
- }
- if (username && *username) {
- char hostname[64];
- struct membuffer mb = {};
- gethostname(hostname, sizeof(hostname));
- put_format(&mb, "%s@%s", username, hostname);
- user->email = mb_cstring(&mb);
- }
-}
-
-static const char *system_default_path_append(const char *append)
-{
- const char *home = getenv("HOME");
- if (!home)
- home = "~";
- const char *path = "/.subsurface";
-
- int len = strlen(home) + strlen(path) + 1;
- if (append)
- len += strlen(append) + 1;
-
- char *buffer = (char *)malloc(len);
- memset(buffer, 0, len);
- strcat(buffer, home);
- strcat(buffer, path);
- if (append) {
- strcat(buffer, "/");
- strcat(buffer, append);
- }
-
- return buffer;
-}
-
-const char *system_default_directory(void)
-{
- static const char *path = NULL;
- if (!path)
- path = system_default_path_append(NULL);
- return path;
-}
-
-const char *system_default_filename(void)
-{
- static char *filename = NULL;
- if (!filename) {
- const char *user = getenv("LOGNAME");
- if (same_string(user, ""))
- user = "username";
- filename = calloc(strlen(user) + 5, 1);
- strcat(filename, user);
- strcat(filename, ".xml");
- }
- static const char *path = NULL;
- if (!path)
- path = system_default_path_append(filename);
- return path;
-}
-
-int enumerate_devices(device_callback_t callback, void *userdata, int dc_type)
-{
- int index = -1, entries = 0;
- DIR *dp = NULL;
- struct dirent *ep = NULL;
- size_t i;
- FILE *file;
- char *line = NULL;
- char *fname;
- size_t len;
- if (dc_type != DC_TYPE_UEMIS) {
- const char *dirname = "/dev";
- const char *patterns[] = {
- "ttyUSB*",
- "ttyS*",
- "ttyACM*",
- "rfcomm*",
- NULL
- };
-
- dp = opendir(dirname);
- if (dp == NULL) {
- return -1;
- }
-
- while ((ep = readdir(dp)) != NULL) {
- for (i = 0; patterns[i] != NULL; ++i) {
- if (fnmatch(patterns[i], ep->d_name, 0) == 0) {
- char filename[1024];
- int n = snprintf(filename, sizeof(filename), "%s/%s", dirname, ep->d_name);
- if (n >= (int)sizeof(filename)) {
- closedir(dp);
- return -1;
- }
- callback(filename, userdata);
- if (is_default_dive_computer_device(filename))
- index = entries;
- entries++;
- break;
- }
- }
- }
- closedir(dp);
- }
- if (dc_type != DC_TYPE_SERIAL) {
- int num_uemis = 0;
- file = fopen("/proc/mounts", "r");
- if (file == NULL)
- return index;
-
- while ((getline(&line, &len, file)) != -1) {
- char *ptr = strstr(line, "UEMISSDA");
- if (ptr) {
- char *end = ptr, *start = ptr;
- while (start > line && *start != ' ')
- start--;
- if (*start == ' ')
- start++;
- while (*end != ' ' && *end != '\0')
- end++;
-
- *end = '\0';
- fname = strdup(start);
-
- callback(fname, userdata);
-
- if (is_default_dive_computer_device(fname))
- index = entries;
- entries++;
- num_uemis++;
- free((void *)fname);
- }
- }
- free(line);
- fclose(file);
- if (num_uemis == 1 && entries == 1) /* if we found only one and it's a mounted Uemis, pick it */
- index = 0;
- }
- return index;
-}
-
-/* NOP wrappers to comform with windows.c */
-int subsurface_rename(const char *path, const char *newpath)
-{
- return rename(path, newpath);
-}
-
-int subsurface_open(const char *path, int oflags, mode_t mode)
-{
- return open(path, oflags, mode);
-}
-
-FILE *subsurface_fopen(const char *path, const char *mode)
-{
- return fopen(path, mode);
-}
-
-void *subsurface_opendir(const char *path)
-{
- return (void *)opendir(path);
-}
-
-int subsurface_access(const char *path, int mode)
-{
- return access(path, mode);
-}
-
-struct zip *subsurface_zip_open_readonly(const char *path, int flags, int *errorp)
-{
- return zip_open(path, flags, errorp);
-}
-
-int subsurface_zip_close(struct zip *zip)
-{
- return zip_close(zip);
-}
-
-/* win32 console */
-void subsurface_console_init(bool dedicated)
-{
- (void)dedicated;
- /* NOP */
-}
-
-void subsurface_console_exit(void)
-{
- /* NOP */
-}
-
-bool subsurface_user_is_root()
-{
- return (geteuid() == 0);
-}
diff --git a/subsurface-core/liquivision.c b/subsurface-core/liquivision.c
deleted file mode 100644
index 9347a724a..000000000
--- a/subsurface-core/liquivision.c
+++ /dev/null
@@ -1,420 +0,0 @@
-#include <string.h>
-
-#include "dive.h"
-#include "divelist.h"
-#include "file.h"
-#include "strndup.h"
-
-// Convert bytes into an INT
-#define array_uint16_le(p) ((unsigned int) (p)[0] \
- + ((p)[1]<<8) )
-#define array_uint32_le(p) ((unsigned int) (p)[0] \
- + ((p)[1]<<8) + ((p)[2]<<16) \
- + ((p)[3]<<24))
-
-struct lv_event {
- time_t time;
- struct pressure {
- int sensor;
- int mbar;
- } pressure;
-};
-
-uint16_t primary_sensor;
-
-static int handle_event_ver2(int code, const unsigned char *ps, unsigned int ps_ptr, struct lv_event *event)
-{
- (void) code;
- (void) ps;
- (void) ps_ptr;
- (void) event;
-
- // Skip 4 bytes
- return 4;
-}
-
-
-static int handle_event_ver3(int code, const unsigned char *ps, unsigned int ps_ptr, struct lv_event *event)
-{
- int skip = 4;
- uint16_t current_sensor;
-
- switch (code) {
- case 0x0002: // Unknown
- case 0x0004: // Unknown
- skip = 4;
- break;
- case 0x0005: // Unknown
- skip = 6;
- break;
- case 0x0007: // Gas
- // 4 byte time
- // 1 byte O2, 1 bye He
- skip = 6;
- break;
- case 0x0008:
- // 4 byte time
- // 2 byte gas set point 2
- skip = 6;
- break;
- case 0x000f:
- // Tank pressure
- event->time = array_uint32_le(ps + ps_ptr);
-
- /* As far as I know, Liquivision supports 2 sensors, own and buddie's. This is my
- * best guess how it is represented. */
-
- current_sensor = array_uint16_le(ps + ps_ptr + 4);
- if (primary_sensor == 0) {
- primary_sensor = current_sensor;
- }
- if (current_sensor == primary_sensor) {
- event->pressure.sensor = 0;
- event->pressure.mbar = array_uint16_le(ps + ps_ptr + 6) * 10; // cb->mb
- } else {
- /* Ignoring the buddy sensor for no as we cannot draw it on the profile.
- event->pressure.sensor = 1;
- event->pressure.mbar = array_uint16_le(ps + ps_ptr + 6) * 10; // cb->mb
- */
- }
- // 1 byte PSR
- // 1 byte ST
- skip = 10;
- break;
- case 0x0010:
- skip = 26;
- break;
- case 0x0015: // Unknown
- skip = 2;
- break;
- default:
- skip = 4;
- break;
- }
-
- return skip;
-}
-
-static void parse_dives (int log_version, const unsigned char *buf, unsigned int buf_size)
-{
- unsigned int ptr = 0;
- unsigned char model;
-
- struct dive *dive;
- struct divecomputer *dc;
- struct sample *sample;
-
- while (ptr < buf_size) {
- int i;
- bool found_divesite = false;
- dive = alloc_dive();
- primary_sensor = 0;
- dc = &dive->dc;
-
- /* Just the main cylinder until we can handle the buddy cylinder porperly */
- for (i = 0; i < 1; i++)
- fill_default_cylinder(&dive->cylinder[i]);
-
- // Model 0=Xen, 1,2=Xeo, 4=Lynx, other=Liquivision
- model = *(buf + ptr);
- switch (model) {
- case 0:
- dc->model = strdup("Xen");
- break;
- case 1:
- case 2:
- dc->model = strdup("Xeo");
- break;
- case 4:
- dc->model = strdup("Lynx");
- break;
- default:
- dc->model = strdup("Liquivision");
- break;
- }
- ptr++;
-
- // Dive location, assemble Location and Place
- unsigned int len, place_len;
- char *location;
- len = array_uint32_le(buf + ptr);
- ptr += 4;
- place_len = array_uint32_le(buf + ptr + len);
-
- if (len && place_len) {
- location = malloc(len + place_len + 4);
- memset(location, 0, len + place_len + 4);
- memcpy(location, buf + ptr, len);
- memcpy(location + len, ", ", 2);
- memcpy(location + len + 2, buf + ptr + len + 4, place_len);
- } else if (len) {
- location = strndup((char *)buf + ptr, len);
- } else if (place_len) {
- location = strndup((char *)buf + ptr + len + 4, place_len);
- }
-
- /* Store the location only if we have one */
- if (len || place_len)
- found_divesite = true;
-
- ptr += len + 4 + place_len;
-
- // Dive comment
- len = array_uint32_le(buf + ptr);
- ptr += 4;
-
- // Blank notes are better than the default text
- if (len && strncmp((char *)buf + ptr, "Comment ...", 11)) {
- dive->notes = strndup((char *)buf + ptr, len);
- }
- ptr += len;
-
- dive->id = array_uint32_le(buf + ptr);
- ptr += 4;
-
- dive->number = array_uint16_le(buf + ptr) + 1;
- ptr += 2;
-
- dive->duration.seconds = array_uint32_le(buf + ptr); // seconds
- ptr += 4;
-
- dive->maxdepth.mm = array_uint16_le(buf + ptr) * 10; // cm->mm
- ptr += 2;
-
- dive->meandepth.mm = array_uint16_le(buf + ptr) * 10; // cm->mm
- ptr += 2;
-
- dive->when = array_uint32_le(buf + ptr);
- ptr += 4;
-
- // now that we have the dive time we can store the divesite
- // (we need the dive time to create deterministic uuids)
- if (found_divesite) {
- dive->dive_site_uuid = find_or_create_dive_site_with_name(location, dive->when);
- free(location);
- }
- //unsigned int end_time = array_uint32_le(buf + ptr);
- ptr += 4;
-
- //unsigned int sit = array_uint32_le(buf + ptr);
- ptr += 4;
- //if (sit == 0xffffffff) {
- //}
-
- dive->surface_pressure.mbar = array_uint16_le(buf + ptr); // ???
- ptr += 2;
-
- //unsigned int rep_dive = array_uint16_le(buf + ptr);
- ptr += 2;
-
- dive->mintemp.mkelvin = C_to_mkelvin((float)array_uint16_le(buf + ptr)/10);// C->mK
- ptr += 2;
-
- dive->maxtemp.mkelvin = C_to_mkelvin((float)array_uint16_le(buf + ptr)/10);// C->mK
- ptr += 2;
-
- dive->salinity = *(buf + ptr); // ???
- ptr += 1;
-
- unsigned int sample_count = array_uint32_le(buf + ptr);
- ptr += 4;
-
- // Sample interval
- unsigned char sample_interval;
- sample_interval = 1;
-
- unsigned char intervals[6] = {1,2,5,10,30,60};
- if (*(buf + ptr) < 6)
- sample_interval = intervals[*(buf + ptr)];
- ptr += 1;
-
- float start_cns = 0;
- unsigned char dive_mode = 0, algorithm = 0;
- if (array_uint32_le(buf + ptr) != sample_count) {
- // Xeo, with CNS and OTU
- start_cns = *(float *) (buf + ptr);
- ptr += 4;
- dive->cns = *(float *) (buf + ptr); // end cns
- ptr += 4;
- dive->otu = *(float *) (buf + ptr);
- ptr += 4;
- dive_mode = *(buf + ptr++); // 0=Deco, 1=Gauge, 2=None
- algorithm = *(buf + ptr++); // 0=ZH-L16C+GF
- sample_count = array_uint32_le(buf + ptr);
- }
- // we aren't using the start_cns, dive_mode, and algorithm, yet
- (void)start_cns;
- (void)dive_mode;
- (void)algorithm;
-
- ptr += 4;
-
- // Parse dive samples
- const unsigned char *ds = buf + ptr;
- const unsigned char *ts = buf + ptr + sample_count * 2 + 4;
- const unsigned char *ps = buf + ptr + sample_count * 4 + 4;
- unsigned int ps_count = array_uint32_le(ps);
- ps += 4;
-
- // Bump ptr
- ptr += sample_count * 4 + 4;
-
- // Handle events
- unsigned int ps_ptr;
- ps_ptr = 0;
-
- unsigned int event_code, d = 0, e;
- struct lv_event event;
-
- // Loop through events
- for (e = 0; e < ps_count; e++) {
- // Get event
- event_code = array_uint16_le(ps + ps_ptr);
- ps_ptr += 2;
-
- if (log_version == 3) {
- ps_ptr += handle_event_ver3(event_code, ps, ps_ptr, &event);
- if (event_code != 0xf)
- continue; // ignore all by pressure sensor event
- } else { // version 2
- ps_ptr += handle_event_ver2(event_code, ps, ps_ptr, &event);
- continue; // ignore all events
- }
- int sample_time, last_time;
- int depth_mm, last_depth, temp_mk, last_temp;
-
- while (true) {
- sample = prepare_sample(dc);
-
- // Get sample times
- sample_time = d * sample_interval;
- depth_mm = array_uint16_le(ds + d * 2) * 10; // cm->mm
- temp_mk = C_to_mkelvin((float)array_uint16_le(ts + d * 2) / 10); // dC->mK
- last_time = (d ? (d - 1) * sample_interval : 0);
-
- if (d == sample_count) {
- // We still have events to record
- sample->time.seconds = event.time;
- sample->depth.mm = array_uint16_le(ds + (d - 1) * 2) * 10; // cm->mm
- sample->temperature.mkelvin = C_to_mkelvin((float) array_uint16_le(ts + (d - 1) * 2) / 10); // dC->mK
- sample->sensor = event.pressure.sensor;
- sample->cylinderpressure.mbar = event.pressure.mbar;
- finish_sample(dc);
-
- break;
- } else if (event.time > sample_time) {
- // Record sample and loop
- sample->time.seconds = sample_time;
- sample->depth.mm = depth_mm;
- sample->temperature.mkelvin = temp_mk;
- finish_sample(dc);
- d++;
-
- continue;
- } else if (event.time == sample_time) {
- sample->time.seconds = sample_time;
- sample->depth.mm = depth_mm;
- sample->temperature.mkelvin = temp_mk;
- sample->sensor = event.pressure.sensor;
- sample->cylinderpressure.mbar = event.pressure.mbar;
- finish_sample(dc);
-
- break;
- } else { // Event is prior to sample
- sample->time.seconds = event.time;
- sample->sensor = event.pressure.sensor;
- sample->cylinderpressure.mbar = event.pressure.mbar;
- if (last_time == sample_time) {
- sample->depth.mm = depth_mm;
- sample->temperature.mkelvin = temp_mk;
- } else {
- // Extrapolate
- last_depth = array_uint16_le(ds + (d - 1) * 2) * 10; // cm->mm
- last_temp = C_to_mkelvin((float) array_uint16_le(ts + (d - 1) * 2) / 10); // dC->mK
- sample->depth.mm = last_depth + (depth_mm - last_depth)
- * (event.time - last_time) / sample_interval;
- sample->temperature.mkelvin = last_temp + (temp_mk - last_temp)
- * (event.time - last_time) / sample_interval;
- }
- finish_sample(dc);
-
- break;
- }
- } // while (true);
- } // for each event sample
-
- // record trailing depth samples
- for ( ;d < sample_count; d++) {
- sample = prepare_sample(dc);
- sample->time.seconds = d * sample_interval;
-
- sample->depth.mm = array_uint16_le(ds + d * 2) * 10; // cm->mm
- sample->temperature.mkelvin =
- C_to_mkelvin((float)array_uint16_le(ts + d * 2) / 10);
- finish_sample(dc);
- }
-
- if (log_version == 3 && model == 4) {
- // Advance to begin of next dive
- switch (array_uint16_le(ps + ps_ptr)) {
- case 0x0000:
- ps_ptr += 5;
- break;
- case 0x0100:
- ps_ptr += 7;
- break;
- case 0x0200:
- ps_ptr += 9;
- break;
- case 0x0300:
- ps_ptr += 11;
- break;
- case 0x0b0b:
- ps_ptr += 27;
- break;
- }
-
- while (*(ps + ps_ptr) != 0x04)
- ps_ptr++;
- }
-
- // End dive
- dive->downloaded = true;
- record_dive(dive);
- mark_divelist_changed(true);
-
- // Advance ptr for next dive
- ptr += ps_ptr + 4;
- } // while
-
- //DEBUG save_dives("/tmp/test.xml");
-}
-
-int try_to_open_liquivision(const char *filename, struct memblock *mem)
-{
- (void) filename;
- const unsigned char *buf = mem->buffer;
- unsigned int buf_size = mem->size;
- unsigned int ptr;
- int log_version;
-
- // Get name length
- unsigned int len = array_uint32_le(buf);
- // Ignore length field and the name
- ptr = 4 + len;
-
- unsigned int dive_count = array_uint32_le(buf + ptr);
- if (dive_count == 0xffffffff) {
- // File version 3.0
- log_version = 3;
- ptr += 6;
- dive_count = array_uint32_le(buf + ptr);
- } else {
- log_version = 2;
- }
- ptr += 4;
-
- parse_dives(log_version, buf + ptr, buf_size - ptr);
-
- return 1;
-}
diff --git a/subsurface-core/load-git.c b/subsurface-core/load-git.c
deleted file mode 100644
index a1f2d2031..000000000
--- a/subsurface-core/load-git.c
+++ /dev/null
@@ -1,1709 +0,0 @@
-// Clang has a bug on zero-initialization of C structs.
-#pragma clang diagnostic ignored "-Wmissing-field-initializers"
-
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <time.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <git2.h>
-#include <libdivecomputer/parser.h>
-
-#include "gettext.h"
-
-#include "dive.h"
-#include "divelist.h"
-#include "device.h"
-#include "membuffer.h"
-#include "git-access.h"
-#include "qthelperfromc.h"
-
-const char *saved_git_id = NULL;
-
-struct picture_entry_list {
- void *data;
- int len;
- const char *hash;
- struct picture_entry_list *next;
-};
-struct picture_entry_list *pel = NULL;
-
-struct keyword_action {
- const char *keyword;
- void (*fn)(char *, struct membuffer *, void *);
-};
-#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))
-
-extern degrees_t parse_degrees(char *buf, char **end);
-git_blob *git_tree_entry_blob(git_repository *repo, const git_tree_entry *entry);
-
-static void save_picture_from_git(struct picture *picture)
-{
- struct picture_entry_list *pic_entry = pel;
-
- while (pic_entry) {
- if (same_string(pic_entry->hash, picture->hash)) {
- savePictureLocal(picture, pic_entry->data, pic_entry->len);
- return;
- }
- pic_entry = pic_entry->next;
- }
- fprintf(stderr, "didn't find picture entry for %s\n", picture->filename);
-}
-
-static char *get_utf8(struct membuffer *b)
-{
- int len = b->len;
- char *res;
-
- if (!len)
- return NULL;
- res = malloc(len+1);
- if (res) {
- memcpy(res, b->buffer, len);
- res[len] = 0;
- }
- return res;
-}
-
-static temperature_t get_temperature(const char *line)
-{
- temperature_t t;
- t.mkelvin = C_to_mkelvin(ascii_strtod(line, NULL));
- return t;
-}
-
-static depth_t get_depth(const char *line)
-{
- depth_t d;
- d.mm = rint(1000*ascii_strtod(line, NULL));
- return d;
-}
-
-static volume_t get_volume(const char *line)
-{
- volume_t v;
- v.mliter = rint(1000*ascii_strtod(line, NULL));
- return v;
-}
-
-static weight_t get_weight(const char *line)
-{
- weight_t w;
- w.grams = rint(1000*ascii_strtod(line, NULL));
- return w;
-}
-
-static pressure_t get_pressure(const char *line)
-{
- pressure_t p;
- p.mbar = rint(1000*ascii_strtod(line, NULL));
- return p;
-}
-
-static int get_salinity(const char *line)
-{
- return rint(10*ascii_strtod(line, NULL));
-}
-
-static fraction_t get_fraction(const char *line)
-{
- fraction_t f;
- f.permille = rint(10*ascii_strtod(line, NULL));
- return f;
-}
-
-static void update_date(timestamp_t *when, const char *line)
-{
- unsigned yyyy, mm, dd;
- struct tm tm;
-
- if (sscanf(line, "%04u-%02u-%02u", &yyyy, &mm, &dd) != 3)
- return;
- utc_mkdate(*when, &tm);
- tm.tm_year = yyyy - 1900;
- tm.tm_mon = mm - 1;
- tm.tm_mday = dd;
- *when = utc_mktime(&tm);
-}
-
-static void update_time(timestamp_t *when, const char *line)
-{
- unsigned h, m, s = 0;
- struct tm tm;
-
- if (sscanf(line, "%02u:%02u:%02u", &h, &m, &s) < 2)
- return;
- utc_mkdate(*when, &tm);
- tm.tm_hour = h;
- tm.tm_min = m;
- tm.tm_sec = s;
- *when = utc_mktime(&tm);
-}
-
-static duration_t get_duration(const char *line)
-{
- int m = 0, s = 0;
- duration_t d;
- sscanf(line, "%d:%d", &m, &s);
- d.seconds = m*60+s;
- return d;
-}
-
-static enum dive_comp_type get_dctype(const char *line)
-{
- for (enum dive_comp_type i = 0; i < NUM_DC_TYPE; i++) {
- if (strcmp(line, divemode_text[i]) == 0)
- return i;
- }
- return 0;
-}
-
-static int get_index(const char *line)
-{ return atoi(line); }
-
-static int get_hex(const char *line)
-{ return strtoul(line, NULL, 16); }
-
-/* this is in qthelper.cpp, so including the .h file is a pain */
-extern const char *printGPSCoords(int lat, int lon);
-
-static void parse_dive_gps(char *line, struct membuffer *str, void *_dive)
-{
- (void) str;
- uint32_t uuid;
- degrees_t latitude = parse_degrees(line, &line);
- degrees_t longitude = parse_degrees(line, &line);
- struct dive *dive = _dive;
- struct dive_site *ds = get_dive_site_for_dive(dive);
- if (!ds) {
- uuid = get_dive_site_uuid_by_gps(latitude, longitude, NULL);
- if (!uuid)
- uuid = create_dive_site_with_gps("", latitude, longitude, dive->when);
- dive->dive_site_uuid = uuid;
- } else {
- if (dive_site_has_gps_location(ds) &&
- (ds->latitude.udeg != latitude.udeg || ds->longitude.udeg != longitude.udeg)) {
- const char *coords = printGPSCoords(latitude.udeg, longitude.udeg);
- // we have a dive site that already has GPS coordinates
- ds->notes = add_to_string(ds->notes, translate("gettextFromC", "multiple GPS locations for this dive site; also %s\n"), coords);
- free((void *)coords);
- }
- ds->latitude = latitude;
- ds->longitude = longitude;
- }
-
-}
-
-static void parse_dive_location(char *line, struct membuffer *str, void *_dive)
-{
- (void) line;
- uint32_t uuid;
- char *name = get_utf8(str);
- struct dive *dive = _dive;
- struct dive_site *ds = get_dive_site_for_dive(dive);
- if (!ds) {
- uuid = get_dive_site_uuid_by_name(name, NULL);
- if (!uuid)
- uuid = create_dive_site(name, dive->when);
- dive->dive_site_uuid = uuid;
- } else {
- // we already had a dive site linked to the dive
- if (same_string(ds->name, "")) {
- ds->name = strdup(name);
- } else {
- // and that dive site had a name. that's weird - if our name is different, add it to the notes
- if (!same_string(ds->name, name))
- ds->notes = add_to_string(ds->notes, translate("gettextFromC", "additional name for site: %s\n"), name);
- }
- }
- free(name);
-}
-
-static void parse_dive_divemaster(char *line, struct membuffer *str, void *_dive)
-{ (void) line; struct dive *dive = _dive; dive->divemaster = get_utf8(str); }
-
-static void parse_dive_buddy(char *line, struct membuffer *str, void *_dive)
-{ (void) line; struct dive *dive = _dive; dive->buddy = get_utf8(str); }
-
-static void parse_dive_suit(char *line, struct membuffer *str, void *_dive)
-{ (void) line; struct dive *dive = _dive; dive->suit = get_utf8(str); }
-
-static void parse_dive_notes(char *line, struct membuffer *str, void *_dive)
-{ (void) line; struct dive *dive = _dive; dive->notes = get_utf8(str); }
-
-static void parse_dive_divesiteid(char *line, struct membuffer *str, void *_dive)
-{ (void) str; struct dive *dive = _dive; dive->dive_site_uuid = get_hex(line); }
-
-/*
- * We can have multiple tags in the membuffer. They are separated by
- * NUL bytes.
- */
-static void parse_dive_tags(char *line, struct membuffer *str, void *_dive)
-{
- (void) line;
- struct dive *dive = _dive;
- const char *tag;
- int len = str->len;
-
- if (!len)
- return;
-
- /* Make sure there is a NUL at the end too */
- tag = mb_cstring(str);
- for (;;) {
- int taglen = strlen(tag);
- if (taglen)
- taglist_add_tag(&dive->tag_list, tag);
- len -= taglen;
- if (!len)
- return;
- tag += taglen+1;
- len--;
- }
-}
-
-static void parse_dive_airtemp(char *line, struct membuffer *str, void *_dive)
-{ (void) str; struct dive *dive = _dive; dive->airtemp = get_temperature(line); }
-
-static void parse_dive_watertemp(char *line, struct membuffer *str, void *_dive)
-{ (void) str; struct dive *dive = _dive; dive->watertemp = get_temperature(line); }
-
-static void parse_dive_duration(char *line, struct membuffer *str, void *_dive)
-{ (void) str; struct dive *dive = _dive; dive->duration = get_duration(line); }
-
-static void parse_dive_rating(char *line, struct membuffer *str, void *_dive)
-{ (void) str; struct dive *dive = _dive; dive->rating = get_index(line); }
-
-static void parse_dive_visibility(char *line, struct membuffer *str, void *_dive)
-{ (void) str; struct dive *dive = _dive; dive->visibility = get_index(line); }
-
-static void parse_dive_notrip(char *line, struct membuffer *str, void *_dive)
-{
- (void) str;
- (void) line;
- struct dive *dive = _dive; dive->tripflag = NO_TRIP;
-}
-
-static void parse_site_description(char *line, struct membuffer *str, void *_ds)
-{ (void) line; struct dive_site *ds = _ds; ds->description = strdup(mb_cstring(str)); }
-
-static void parse_site_name(char *line, struct membuffer *str, void *_ds)
-{ (void) line; struct dive_site *ds = _ds; ds->name = strdup(mb_cstring(str)); }
-
-static void parse_site_notes(char *line, struct membuffer *str, void *_ds)
-{ (void) line; struct dive_site *ds = _ds; ds->notes = strdup(mb_cstring(str)); }
-
-extern degrees_t parse_degrees(char *buf, char **end);
-static void parse_site_gps(char *line, struct membuffer *str, void *_ds)
-{
- (void) str;
- struct dive_site *ds = _ds;
-
- ds->latitude = parse_degrees(line, &line);
- ds->longitude = parse_degrees(line, &line);
-}
-
-static void parse_site_geo(char *line, struct membuffer *str, void *_ds)
-{
- struct dive_site *ds = _ds;
- if (ds->taxonomy.category == NULL)
- ds->taxonomy.category = alloc_taxonomy();
- int nr = ds->taxonomy.nr;
- if (nr < TC_NR_CATEGORIES) {
- struct taxonomy *t = &ds->taxonomy.category[nr];
- t->value = strdup(mb_cstring(str));
- sscanf(line, "cat %d origin %d \"", &t->category, (int *)&t->origin);
- ds->taxonomy.nr++;
- }
-}
-
-/* Parse key=val parts of samples and cylinders etc */
-static char *parse_keyvalue_entry(void (*fn)(void *, const char *, const char *), void *fndata, char *line)
-{
- char *key = line, *val, c;
-
- while ((c = *line) != 0) {
- if (isspace(c) || c == '=')
- break;
- line++;
- }
-
- if (c == '=')
- *line++ = 0;
- val = line;
-
- while ((c = *line) != 0) {
- if (isspace(c))
- break;
- line++;
- }
- if (c)
- *line++ = 0;
-
- fn(fndata, key, val);
- return line;
-}
-
-static int cylinder_index, weightsystem_index;
-
-static void parse_cylinder_keyvalue(void *_cylinder, const char *key, const char *value)
-{
- cylinder_t *cylinder = _cylinder;
- if (!strcmp(key, "vol")) {
- cylinder->type.size = get_volume(value);
- return;
- }
- if (!strcmp(key, "workpressure")) {
- cylinder->type.workingpressure = get_pressure(value);
- return;
- }
- /* This is handled by the "get_utf8()" */
- if (!strcmp(key, "description"))
- return;
- if (!strcmp(key, "o2")) {
- cylinder->gasmix.o2 = get_fraction(value);
- return;
- }
- if (!strcmp(key, "he")) {
- cylinder->gasmix.he = get_fraction(value);
- return;
- }
- if (!strcmp(key, "start")) {
- cylinder->start = get_pressure(value);
- return;
- }
- if (!strcmp(key, "end")) {
- cylinder->end = get_pressure(value);
- return;
- }
- if (!strcmp(key, "use")) {
- cylinder->cylinder_use = cylinderuse_from_text(value);
- return;
- }
- report_error("Unknown cylinder key/value pair (%s/%s)", key, value);
-}
-
-static void parse_dive_cylinder(char *line, struct membuffer *str, void *_dive)
-{
- struct dive *dive = _dive;
- cylinder_t *cylinder = dive->cylinder + cylinder_index;
-
- cylinder_index++;
- cylinder->type.description = get_utf8(str);
- for (;;) {
- char c;
- while (isspace(c = *line))
- line++;
- if (!c)
- break;
- line = parse_keyvalue_entry(parse_cylinder_keyvalue, cylinder, line);
- }
-}
-
-static void parse_weightsystem_keyvalue(void *_ws, const char *key, const char *value)
-{
- weightsystem_t *ws = _ws;
- if (!strcmp(key, "weight")) {
- ws->weight = get_weight(value);
- return;
- }
- /* This is handled by the "get_utf8()" */
- if (!strcmp(key, "description"))
- return;
- report_error("Unknown weightsystem key/value pair (%s/%s)", key, value);
-}
-
-static void parse_dive_weightsystem(char *line, struct membuffer *str, void *_dive)
-{
- struct dive *dive = _dive;
- weightsystem_t *ws = dive->weightsystem + weightsystem_index;
-
- weightsystem_index++;
- ws->description = get_utf8(str);
- for (;;) {
- char c;
- while (isspace(c = *line))
- line++;
- if (!c)
- break;
- line = parse_keyvalue_entry(parse_weightsystem_keyvalue, ws, line);
- }
-}
-
-static int match_action(char *line, struct membuffer *str, void *data,
- struct keyword_action *action, unsigned nr_action)
-{
- char *p = line, c;
- unsigned low, high;
-
- while ((c = *p) >= 'a' && c <= 'z') // skip over 1st word
- p++; // Extract the second word from the line:
- if (p == line)
- return -1;
- switch (c) {
- case 0: // if 2nd word is C-terminated
- break;
- case ' ': // =end of 2nd word?
- *p++ = 0; // then C-terminate that word
- break;
- default:
- return -1;
- }
-
- /* Standard binary search in a table */
- low = 0;
- high = nr_action;
- while (low < high) {
- unsigned mid = (low+high)/2;
- struct keyword_action *a = action + mid;
- int cmp = strcmp(line, a->keyword);
- if (!cmp) { // attribute found:
- a->fn(p, str, data); // Execute appropriate function,
- return 0; // .. passing 2n word from above
- } // (p) as a function argument.
- if (cmp < 0)
- high = mid;
- else
- low = mid+1;
- }
-report_error("Unmatched action '%s'", line);
- return -1;
-}
-
-/* FIXME! We should do the array thing here too. */
-static void parse_sample_keyvalue(void *_sample, const char *key, const char *value)
-{
- struct sample *sample = _sample;
-
- if (!strcmp(key, "sensor")) {
- sample->sensor = atoi(value);
- return;
- }
- if (!strcmp(key, "ndl")) {
- sample->ndl = get_duration(value);
- return;
- }
- if (!strcmp(key, "tts")) {
- sample->tts = get_duration(value);
- return;
- }
- if (!strcmp(key, "in_deco")) {
- sample->in_deco = atoi(value);
- return;
- }
- if (!strcmp(key, "stoptime")) {
- sample->stoptime = get_duration(value);
- return;
- }
- if (!strcmp(key, "stopdepth")) {
- sample->stopdepth = get_depth(value);
- return;
- }
- if (!strcmp(key, "cns")) {
- sample->cns = atoi(value);
- return;
- }
-
- if (!strcmp(key, "rbt")) {
- sample->rbt = get_duration(value);
- return;
- }
-
- if (!strcmp(key, "po2")) {
- pressure_t p = get_pressure(value);
- sample->setpoint.mbar = p.mbar;
- return;
- }
- if (!strcmp(key, "sensor1")) {
- pressure_t p = get_pressure(value);
- sample->o2sensor[0].mbar = p.mbar;
- return;
- }
- if (!strcmp(key, "sensor2")) {
- pressure_t p = get_pressure(value);
- sample->o2sensor[1].mbar = p.mbar;
- return;
- }
- if (!strcmp(key, "sensor3")) {
- pressure_t p = get_pressure(value);
- sample->o2sensor[2].mbar = p.mbar;
- return;
- }
- if (!strcmp(key, "o2pressure")) {
- pressure_t p = get_pressure(value);
- sample->o2cylinderpressure.mbar = p.mbar;
- return;
- }
- if (!strcmp(key, "heartbeat")) {
- sample->heartbeat = atoi(value);
- return;
- }
- if (!strcmp(key, "bearing")) {
- sample->bearing.degrees = atoi(value);
- return;
- }
-
- report_error("Unexpected sample key/value pair (%s/%s)", key, value);
-}
-
-static char *parse_sample_unit(struct sample *sample, double val, char *unit)
-{
- char *end = unit, c;
-
- /* Skip over the unit */
- while ((c = *end) != 0) {
- if (isspace(c)) {
- *end++ = 0;
- break;
- }
- end++;
- }
-
- /* The units are "°C", "m" or "bar", so let's just look at the first character */
- switch (*unit) {
- case 'm':
- sample->depth.mm = rint(1000*val);
- break;
- case 'b':
- sample->cylinderpressure.mbar = rint(1000*val);
- break;
- default:
- sample->temperature.mkelvin = C_to_mkelvin(val);
- break;
- }
-
- return end;
-}
-
-/*
- * By default the sample data does not change unless the
- * save-file gives an explicit new value. So we copy the
- * data from the previous sample if one exists, and then
- * the parsing will update it as necessary.
- *
- * There are a few exceptions, like the sample pressure:
- * missing sample pressure doesn't mean "same as last
- * time", but "interpolate". We clear those ones
- * explicitly.
- */
-static struct sample *new_sample(struct divecomputer *dc)
-{
- struct sample *sample = prepare_sample(dc);
- if (sample != dc->sample) {
- memcpy(sample, sample-1, sizeof(struct sample));
- sample->cylinderpressure.mbar = 0;
- }
- return sample;
-}
-
-static void sample_parser(char *line, struct divecomputer *dc)
-{
- int m, s = 0;
- struct sample *sample = new_sample(dc);
-
- m = strtol(line, &line, 10);
- if (*line == ':')
- s = strtol(line+1, &line, 10);
- sample->time.seconds = m*60+s;
-
- for (;;) {
- char c;
-
- while (isspace(c = *line))
- line++;
- if (!c)
- break;
- /* Less common sample entries have a name */
- if (c >= 'a' && c <= 'z') {
- line = parse_keyvalue_entry(parse_sample_keyvalue, sample, line);
- } else {
- const char *end;
- double val = ascii_strtod(line, &end);
- if (end == line) {
- report_error("Odd sample data: %s", line);
- break;
- }
- line = (char *)end;
- line = parse_sample_unit(sample, val, line);
- }
- }
- finish_sample(dc);
-}
-
-static void parse_dc_airtemp(char *line, struct membuffer *str, void *_dc)
-{ (void) str; struct divecomputer *dc = _dc; dc->airtemp = get_temperature(line); }
-
-static void parse_dc_date(char *line, struct membuffer *str, void *_dc)
-{ (void) str; struct divecomputer *dc = _dc; update_date(&dc->when, line); }
-
-static void parse_dc_deviceid(char *line, struct membuffer *str, void *_dc)
-{ (void) str; struct divecomputer *dc = _dc; dc->deviceid = get_hex(line); }
-
-static void parse_dc_diveid(char *line, struct membuffer *str, void *_dc)
-{ (void) str; struct divecomputer *dc = _dc; dc->diveid = get_hex(line); }
-
-static void parse_dc_duration(char *line, struct membuffer *str, void *_dc)
-{ (void) str; struct divecomputer *dc = _dc; dc->duration = get_duration(line); }
-
-static void parse_dc_dctype(char *line, struct membuffer *str, void *_dc)
-{ (void) str; struct divecomputer *dc = _dc; dc->divemode = get_dctype(line); }
-
-static void parse_dc_maxdepth(char *line, struct membuffer *str, void *_dc)
-{ (void) str; struct divecomputer *dc = _dc; dc->maxdepth = get_depth(line); }
-
-static void parse_dc_meandepth(char *line, struct membuffer *str, void *_dc)
-{ (void) str; struct divecomputer *dc = _dc; dc->meandepth = get_depth(line); }
-
-static void parse_dc_model(char *line, struct membuffer *str, void *_dc)
-{ (void) line; struct divecomputer *dc = _dc; dc->model = get_utf8(str); }
-
-static void parse_dc_numberofoxygensensors(char *line, struct membuffer *str, void *_dc)
-{ (void) str; struct divecomputer *dc = _dc; dc->no_o2sensors = get_index(line); }
-
-static void parse_dc_surfacepressure(char *line, struct membuffer *str, void *_dc)
-{ (void) str; struct divecomputer *dc = _dc; dc->surface_pressure = get_pressure(line); }
-
-static void parse_dc_salinity(char *line, struct membuffer *str, void *_dc)
-{ (void) str; struct divecomputer *dc = _dc; dc->salinity = get_salinity(line); }
-
-static void parse_dc_surfacetime(char *line, struct membuffer *str, void *_dc)
-{ (void) str; struct divecomputer *dc = _dc; dc->surfacetime = get_duration(line); }
-
-static void parse_dc_time(char *line, struct membuffer *str, void *_dc)
-{ (void) str; struct divecomputer *dc = _dc; update_time(&dc->when, line); }
-
-static void parse_dc_watertemp(char *line, struct membuffer *str, void *_dc)
-{ (void) str; struct divecomputer *dc = _dc; dc->watertemp = get_temperature(line); }
-
-static void parse_event_keyvalue(void *_event, const char *key, const char *value)
-{
- struct event *event = _event;
- int val = atoi(value);
-
- if (!strcmp(key, "type")) {
- event->type = val;
- } else if (!strcmp(key, "flags")) {
- event->flags = val;
- } else if (!strcmp(key, "value")) {
- event->value = val;
- } else if (!strcmp(key, "name")) {
- /* We get the name from the string handling */
- } else if (!strcmp(key, "cylinder")) {
- /* NOTE! We add one here as a marker that "yes, we got a cylinder index" */
- event->gas.index = 1+get_index(value);
- } else if (!strcmp(key, "o2")) {
- event->gas.mix.o2 = get_fraction(value);
- } else if (!strcmp(key, "he")) {
- event->gas.mix.he = get_fraction(value);
- } else
- report_error("Unexpected event key/value pair (%s/%s)", key, value);
-}
-
-/* keyvalue "key" "value"
- * so we have two strings (possibly empty) in the membuffer, separated by a '\0' */
-static void parse_dc_keyvalue(char *line, struct membuffer *str, void *_dc)
-{
- const char *key, *value;
- struct divecomputer *dc = _dc;
-
- // Let's make sure we have two strings...
- int string_counter = 0;
- while(*line) {
- if (*line == '"')
- string_counter++;
- line++;
- }
- if (string_counter != 2)
- return;
-
- // stupidly the second string in the membuffer isn't NUL terminated;
- // asking for a cstring fixes that; interestingly enough, given that there are two
- // strings in the mb, the next command at the same time assigns a pointer to the
- // first string to 'key' and NUL terminates the second string (which then goes to 'value')
- key = mb_cstring(str);
- value = key + strlen(key) + 1;
- add_extra_data(dc, key, value);
-}
-
-static void parse_dc_event(char *line, struct membuffer *str, void *_dc)
-{
- int m, s = 0;
- const char *name;
- struct divecomputer *dc = _dc;
- struct event event = { 0 }, *ev;
-
- m = strtol(line, &line, 10);
- if (*line == ':')
- s = strtol(line+1, &line, 10);
- event.time.seconds = m*60+s;
-
- for (;;) {
- char c;
- while (isspace(c = *line))
- line++;
- if (!c)
- break;
- line = parse_keyvalue_entry(parse_event_keyvalue, &event, line);
- }
-
- name = "";
- if (str->len)
- name = mb_cstring(str);
- ev = add_event(dc, event.time.seconds, event.type, event.flags, event.value, name);
-
- /*
- * Older logs might mark the dive to be CCR by having an "SP change" event at time 0:00.
- * Better to mark them being CCR on import so no need for special treatments elsewhere on
- * the code.
- */
- if (ev && event.time.seconds == 0 && event.type == SAMPLE_EVENT_PO2 && dc->divemode==OC) {
- dc->divemode = CCR;
- }
-
- if (ev && event_is_gaschange(ev)) {
- /*
- * We subtract one here because "0" is "no index",
- * and the parsing will add one for actual cylinder
- * index data (see parse_event_keyvalue)
- */
- ev->gas.index = event.gas.index-1;
- if (event.gas.mix.o2.permille || event.gas.mix.he.permille)
- ev->gas.mix = event.gas.mix;
- }
-}
-
-static void parse_trip_date(char *line, struct membuffer *str, void *_trip)
-{ (void) str; dive_trip_t *trip = _trip; update_date(&trip->when, line); }
-
-static void parse_trip_time(char *line, struct membuffer *str, void *_trip)
-{ (void) str; dive_trip_t *trip = _trip; update_time(&trip->when, line); }
-
-static void parse_trip_location(char *line, struct membuffer *str, void *_trip)
-{ (void) line; dive_trip_t *trip = _trip; trip->location = get_utf8(str); }
-
-static void parse_trip_notes(char *line, struct membuffer *str, void *_trip)
-{ (void) line; dive_trip_t *trip = _trip; trip->notes = get_utf8(str); }
-
-static void parse_settings_autogroup(char *line, struct membuffer *str, void *_unused)
-{
- (void) line;
- (void) str;
- (void) _unused;
- set_autogroup(1);
-}
-
-static void parse_settings_units(char *line, struct membuffer *str, void *unused)
-{
- (void) str;
- (void) unused;
- if (line)
- set_informational_units(line);
-}
-
-static void parse_settings_userid(char *line, struct membuffer *str, void *_unused)
-{
- (void) str;
- (void) _unused;
- if (line) {
- set_save_userid_local(true);
- set_userid(line);
- }
-}
-
-/*
- * Our versioning is a joke right now, but this is more of an example of what we
- * *can* do some day. And if we do change the version, this warning will show if
- * you read with a version of subsurface that doesn't know about it.
- * We MUST keep this in sync with the XML version (so we can report a consistent
- * minimum datafile version)
- */
-static void parse_settings_version(char *line, struct membuffer *str, void *_unused)
-{
- (void) str;
- (void) _unused;
- int version = atoi(line);
- report_datafile_version(version);
- if (version > DATAFORMAT_VERSION)
- report_error("Git save file version %d is newer than version %d I know about", version, DATAFORMAT_VERSION);
-}
-
-/* The string in the membuffer is the version string of subsurface that saved things, just FYI */
-static void parse_settings_subsurface(char *line, struct membuffer *str, void *_unused)
-{
- (void) line;
- (void) str;
- (void) _unused;
-}
-
-struct divecomputerid {
- const char *model;
- const char *nickname;
- const char *firmware;
- const char *serial;
- const char *cstr;
- unsigned int deviceid;
-};
-
-static void parse_divecomputerid_keyvalue(void *_cid, const char *key, const char *value)
-{
- struct divecomputerid *cid = _cid;
-
- if (*value == '"') {
- value = cid->cstr;
- cid->cstr += strlen(cid->cstr)+1;
- }
- if (!strcmp(key, "deviceid")) {
- cid->deviceid = get_hex(value);
- return;
- }
- if (!strcmp(key, "serial")) {
- cid->serial = value;
- return;
- }
- if (!strcmp(key, "firmware")) {
- cid->firmware = value;
- return;
- }
- if (!strcmp(key, "nickname")) {
- cid->nickname = value;
- return;
- }
- report_error("Unknow divecomputerid key/value pair (%s/%s)", key, value);
-}
-
-/*
- * The 'divecomputerid' is a bit harder to parse than some other things, because
- * it can have multiple strings (but see the tag parsing for another example of
- * that) in addition to the non-string entries.
- *
- * We keep the "next" string in "id.cstr" and update it as we use it.
- */
-static void parse_settings_divecomputerid(char *line, struct membuffer *str, void *_unused)
-{
- (void) _unused;
- struct divecomputerid id = { mb_cstring(str) };
-
- id.cstr = id.model + strlen(id.model) + 1;
-
- /* Skip the '"' that stood for the model string */
- line++;
-
- for (;;) {
- char c;
- while (isspace(c = *line))
- line++;
- if (!c)
- break;
- line = parse_keyvalue_entry(parse_divecomputerid_keyvalue, &id, line);
- }
- create_device_node(id.model, id.deviceid, id.serial, id.firmware, id.nickname);
-}
-
-static void parse_picture_filename(char *line, struct membuffer *str, void *_pic)
-{
- (void) line;
- struct picture *pic = _pic;
- pic->filename = get_utf8(str);
-}
-
-static void parse_picture_gps(char *line, struct membuffer *str, void *_pic)
-{
- (void) str;
- struct picture *pic = _pic;
-
- pic->latitude = parse_degrees(line, &line);
- pic->longitude = parse_degrees(line, &line);
-}
-
-static void parse_picture_hash(char *line, struct membuffer *str, void *_pic)
-{
- (void) line;
- struct picture *pic = _pic;
- pic->hash = get_utf8(str);
-}
-
-/* These need to be sorted! */
-struct keyword_action dc_action[] = {
-#undef D
-#define D(x) { #x, parse_dc_ ## x }
- D(airtemp), D(date), D(dctype), D(deviceid), D(diveid), D(duration),
- D(event), D(keyvalue), D(maxdepth), D(meandepth), D(model), D(numberofoxygensensors),
- D(salinity), D(surfacepressure), D(surfacetime), D(time), D(watertemp)
-};
-
-/* Sample lines start with a space or a number */
-static void divecomputer_parser(char *line, struct membuffer *str, void *_dc)
-{
- char c = *line;
- if (c < 'a' || c > 'z')
- sample_parser(line, _dc);
- match_action(line, str, _dc, dc_action, ARRAY_SIZE(dc_action));
-}
-
-/* These need to be sorted! */
-struct keyword_action dive_action[] = {
-#undef D
-#define D(x) { #x, parse_dive_ ## x }
- D(airtemp), D(buddy), D(cylinder), D(divemaster), D(divesiteid), D(duration),
- D(gps), D(location), D(notes), D(notrip), D(rating), D(suit),
- D(tags), D(visibility), D(watertemp), D(weightsystem)
-};
-
-static void dive_parser(char *line, struct membuffer *str, void *_dive)
-{
- match_action(line, str, _dive, dive_action, ARRAY_SIZE(dive_action));
-}
-
-/* These need to be sorted! */
-struct keyword_action site_action[] = {
-#undef D
-#define D(x) { #x, parse_site_ ## x }
- D(description), D(geo), D(gps), D(name), D(notes)
-};
-
-static void site_parser(char *line, struct membuffer *str, void *_ds)
-{
- match_action(line, str, _ds, site_action, ARRAY_SIZE(site_action));
-}
-
-/* These need to be sorted! */
-struct keyword_action trip_action[] = {
-#undef D
-#define D(x) { #x, parse_trip_ ## x }
- D(date), D(location), D(notes), D(time),
-};
-
-static void trip_parser(char *line, struct membuffer *str, void *_trip)
-{
- match_action(line, str, _trip, trip_action, ARRAY_SIZE(trip_action));
-}
-
-/* These need to be sorted! */
-static struct keyword_action settings_action[] = {
-#undef D
-#define D(x) { #x, parse_settings_ ## x }
- D(autogroup), D(divecomputerid), D(subsurface), D(units), D(userid), D(version),
-};
-
-static void settings_parser(char *line, struct membuffer *str, void *_unused)
-{
- (void) _unused;
- match_action(line, str, NULL, settings_action, ARRAY_SIZE(settings_action));
-}
-
-/* These need to be sorted! */
-static struct keyword_action picture_action[] = {
-#undef D
-#define D(x) { #x, parse_picture_ ## x }
- D(filename), D(gps), D(hash)
-};
-
-static void picture_parser(char *line, struct membuffer *str, void *_pic)
-{
- match_action(line, str, _pic, picture_action, ARRAY_SIZE(picture_action));
-}
-
-/*
- * We have a very simple line-based interface, with the small
- * complication that lines can have strings in the middle, and
- * a string can be multiple lines.
- *
- * The UTF-8 string escaping is *very* simple, though:
- *
- * - a string starts and ends with double quotes (")
- *
- * - inside the string we escape:
- * (a) double quotes with '\"'
- * (b) backslash (\) with '\\'
- *
- * - additionally, for human readability, we escape
- * newlines with '\n\t', with the exception that
- * consecutive newlines are left unescaped (so an
- * empty line doesn't become a line with just a tab
- * on it).
- *
- * Also, while the UTF-8 string can have arbitrarily
- * long lines, the non-string parts of the lines are
- * never long, so we can use a small temporary buffer
- * on stack for that part.
- *
- * Also, note that if a line has one or more strings
- * in it:
- *
- * - each string will be represented as a single '"'
- * character in the output.
- *
- * - all string will exist in the same 'membuffer',
- * separated by NUL characters (that cannot exist
- * in a string, not even quoted).
- */
-static const char *parse_one_string(const char *buf, const char *end, struct membuffer *b)
-{
- const char *p = buf;
-
- /*
- * We turn multiple strings one one line (think dive tags) into one
- * membuffer that has NUL characters in between strings.
- */
- if (b->len)
- put_bytes(b, "", 1);
-
- while (p < end) {
- char replace;
-
- switch (*p++) {
- default:
- continue;
- case '\n':
- if (p < end && *p == '\t') {
- replace = '\n';
- break;
- }
- continue;
- case '\\':
- if (p < end) {
- replace = *p;
- break;
- }
- continue;
- case '"':
- replace = 0;
- break;
- }
- put_bytes(b, buf, p - buf - 1);
- if (!replace)
- break;
- put_bytes(b, &replace, 1);
- buf = ++p;
- }
- return p;
-}
-
-typedef void (line_fn_t)(char *, struct membuffer *, void *);
-#define MAXLINE 500
-static unsigned parse_one_line(const char *buf, unsigned size, line_fn_t *fn, void *fndata, struct membuffer *b)
-{
- const char *end = buf + size;
- const char *p = buf;
- char line[MAXLINE+1];
- int off = 0;
-
- while (p < end) {
- char c = *p++;
- if (c == '\n')
- break;
- line[off] = c;
- off++;
- if (off > MAXLINE)
- off = MAXLINE;
- if (c == '"')
- p = parse_one_string(p, end, b);
- }
- line[off] = 0;
- fn(line, b, fndata);
- return p - buf;
-}
-
-/*
- * We keep on re-using the membuffer that we use for
- * strings, but the callback function can "steal" it by
- * saving its value and just clear the original.
- */
-static void for_each_line(git_blob *blob, line_fn_t *fn, void *fndata)
-{
- const char *content = git_blob_rawcontent(blob);
- unsigned int size = git_blob_rawsize(blob);
- struct membuffer str = { 0 };
-
- while (size) {
- unsigned int n = parse_one_line(content, size, fn, fndata, &str);
- content += n;
- size -= n;
-
- /* Re-use the allocation, but forget the data */
- str.len = 0;
- }
- free_buffer(&str);
-}
-
-#define GIT_WALK_OK 0
-#define GIT_WALK_SKIP 1
-
-static struct divecomputer *active_dc;
-static struct dive *active_dive;
-static dive_trip_t *active_trip;
-
-static void finish_active_trip(void)
-{
- dive_trip_t *trip = active_trip;
-
- if (trip) {
- active_trip = NULL;
- insert_trip(&trip);
- }
-}
-
-static void finish_active_dive(void)
-{
- struct dive *dive = active_dive;
-
- if (dive) {
- /* check if we need to save pictures */
- FOR_EACH_PICTURE(dive) {
- if (!picture_exists(picture))
- save_picture_from_git(picture);
- }
- /* free any memory we allocated to track pictures */
- while (pel) {
- free(pel->data);
- void *lastone = pel;
- pel = pel->next;
- free(lastone);
- }
- active_dive = NULL;
- record_dive(dive);
- }
-}
-
-static struct dive *create_new_dive(timestamp_t when)
-{
- struct dive *dive = alloc_dive();
-
- /* We'll fill in more data from the dive file */
- dive->when = when;
-
- if (active_trip)
- add_dive_to_trip(dive, active_trip);
- return dive;
-}
-
-static dive_trip_t *create_new_trip(int yyyy, int mm, int dd)
-{
- dive_trip_t *trip = calloc(1, sizeof(dive_trip_t));
- struct tm tm = { 0 };
-
- /* We'll fill in the real data from the trip descriptor file */
- tm.tm_year = yyyy;
- tm.tm_mon = mm-1;
- tm.tm_mday = dd;
- trip->when = utc_mktime(&tm);
-
- return trip;
-}
-
-static bool validate_date(int yyyy, int mm, int dd)
-{
- return yyyy > 1970 && yyyy < 3000 &&
- mm > 0 && mm < 13 &&
- dd > 0 && dd < 32;
-}
-
-static bool validate_time(int h, int m, int s)
-{
- return h >= 0 && h < 24 &&
- m >= 0 && m < 60 &&
- s >=0 && s <= 60;
-}
-
-/*
- * Dive trip directory, name is 'nn-alphabetic[~hex]'
- */
-static int dive_trip_directory(const char *root, const char *name)
-{
- int yyyy = -1, mm = -1, dd = -1;
-
- if (sscanf(root, "%d/%d", &yyyy, &mm) != 2)
- return GIT_WALK_SKIP;
- dd = atoi(name);
- if (!validate_date(yyyy, mm, dd))
- return GIT_WALK_SKIP;
- finish_active_trip();
- active_trip = create_new_trip(yyyy, mm, dd);
- return GIT_WALK_OK;
-}
-
-/*
- * Dive directory, name is [[yyyy-]mm-]nn-ddd-hh:mm:ss[~hex] in older git repositories
- * but [[yyyy-]mm-]nn-ddd-hh=mm=ss[~hex] in newer repos as ':' is an illegal character for Windows files
- * and 'timeoff' points to what should be the time part of
- * the name (the first digit of the hour).
- *
- * The root path will be of the form yyyy/mm[/tripdir],
- */
-static int dive_directory(const char *root, const git_tree_entry *entry, const char *name, int timeoff)
-{
- int yyyy = -1, mm = -1, dd = -1;
- int h, m, s;
- int mday_off, month_off, year_off;
- struct tm tm;
-
- /* Skip the '-' before the time */
- mday_off = timeoff;
- if (!mday_off || name[--mday_off] != '-')
- return GIT_WALK_SKIP;
- /* Skip the day name */
- while (mday_off > 0 && name[--mday_off] != '-')
- /* nothing */;
-
- mday_off = mday_off - 2;
- month_off = mday_off - 3;
- year_off = month_off - 5;
- if (mday_off < 0)
- return GIT_WALK_SKIP;
-
- /* Get the time of day -- parse both time formats so we can read old repos when not on Windows */
- if (sscanf(name+timeoff, "%d:%d:%d", &h, &m, &s) != 3 && sscanf(name+timeoff, "%d=%d=%d", &h, &m, &s) != 3)
- return GIT_WALK_SKIP;
- if (!validate_time(h, m, s))
- return GIT_WALK_SKIP;
-
- /*
- * Using the "git_tree_walk()" interface is simple, but
- * it kind of sucks as an interface because there is
- * no sane way to pass the hierarchy to the callbacks.
- * The "payload" is a fixed one-time thing: we'd like
- * the "current trip" to be passed down to the dives
- * that get parsed under that trip, but we can't.
- *
- * So "active_trip" is not the trip that is in the hierarchy
- * _above_ us, it's just the trip that was _before_ us. But
- * if a dive is not in a trip at all, we can't tell.
- *
- * We could just do a better walker that passes the
- * return value around, but we hack around this by
- * instead looking at the one hierarchical piece of
- * data we have: the pathname to the current entry.
- *
- * This is pretty hacky. The magic '8' is the length
- * of a pathname of the form 'yyyy/mm/'.
- */
- if (strlen(root) == 8)
- finish_active_trip();
-
- /*
- * Get the date. The day of the month is in the dive directory
- * name, the year and month might be in the path leading up
- * to it.
- */
- dd = atoi(name + mday_off);
- if (year_off < 0) {
- if (sscanf(root, "%d/%d", &yyyy, &mm) != 2)
- return GIT_WALK_SKIP;
- } else
- yyyy = atoi(name + year_off);
- if (month_off >= 0)
- mm = atoi(name + month_off);
-
- if (!validate_date(yyyy, mm, dd))
- return GIT_WALK_SKIP;
-
- /* Ok, close enough. We've gotten sufficient information */
- memset(&tm, 0, sizeof(tm));
- tm.tm_hour = h;
- tm.tm_min = m;
- tm.tm_sec = s;
- tm.tm_year = yyyy - 1900;
- tm.tm_mon = mm-1;
- tm.tm_mday = dd;
-
- finish_active_dive();
- active_dive = create_new_dive(utc_mktime(&tm));
- memcpy(active_dive->git_id, git_tree_entry_id(entry)->id, 20);
- return GIT_WALK_OK;
-}
-
-static int picture_directory(const char *root, const char *name)
-{
- (void) root;
- (void) name;
- if (!active_dive)
- return GIT_WALK_SKIP;
- return GIT_WALK_OK;
-}
-
-/*
- * Return the length of the string without the unique part.
- */
-static int nonunique_length(const char *str)
-{
- int len = 0;
-
- for (;;) {
- char c = *str++;
- if (!c || c == '~')
- return len;
- len++;
- }
-}
-
-/*
- * When hitting a directory node, we have a couple of cases:
- *
- * - It's just a date entry - all numeric (either year or month):
- *
- * [yyyy|mm]
- *
- * We don't do anything with these, we just traverse into them.
- * The numeric data will show up as part of the full path when
- * we hit more interesting entries.
- *
- * - It's a trip directory. The name will be of the form
- *
- * nn-alphabetic[~hex]
- *
- * where 'nn' is the day of the month (year and month will be
- * encoded in the path leading up to this).
- *
- * - It's a dive directory. The name will be of the form
- *
- * [[yyyy-]mm-]nn-ddd-hh=mm=ss[~hex]
- *
- * (older versions had this as [[yyyy-]mm-]nn-ddd-hh:mm:ss[~hex]
- * but that faile on Windows)
- *
- * which describes the date and time of a dive (yyyy and mm
- * are optional, and may be encoded in the path leading up to
- * the dive).
- *
- * - It is a per-dive picture directory ("Pictures")
- *
- * - It's some random non-dive-data directory.
- *
- * If it doesn't match the above patterns, we'll ignore them
- * for dive loading purposes, and not even recurse into them.
- */
-static int walk_tree_directory(const char *root, const git_tree_entry *entry)
-{
- const char *name = git_tree_entry_name(entry);
- int digits = 0, len;
- char c;
-
- if (!strcmp(name, "Pictures"))
- return picture_directory(root, name);
-
- if (!strcmp(name, "01-Divesites"))
- return GIT_WALK_OK;
-
- while (isdigit(c = name[digits]))
- digits++;
-
- /* Doesn't start with two or four digits? Skip */
- if (digits != 4 && digits != 2)
- return GIT_WALK_SKIP;
-
- /* Only digits? Do nothing, but recurse into it */
- if (!c)
- return GIT_WALK_OK;
-
- /* All valid cases need to have a slash following */
- if (c != '-')
- return GIT_WALK_SKIP;
-
- /* Do a quick check for a common dive case */
- len = nonunique_length(name);
-
- /*
- * We know the len is at least 3, because we had at least
- * two digits and a dash
- */
- if (name[len-3] == ':' || name[len-3] == '=')
- return dive_directory(root, entry, name, len-8);
-
- if (digits != 2)
- return GIT_WALK_SKIP;
-
- return dive_trip_directory(root, name);
-}
-
-git_blob *git_tree_entry_blob(git_repository *repo, const git_tree_entry *entry)
-{
- const git_oid *id = git_tree_entry_id(entry);
- git_blob *blob;
-
- if (git_blob_lookup(&blob, repo, id))
- return NULL;
- return blob;
-}
-
-static struct divecomputer *create_new_dc(struct dive *dive)
-{
- struct divecomputer *dc = &dive->dc;
-
- while (dc->next)
- dc = dc->next;
- /* Did we already fill that in? */
- if (dc->samples || dc->model || dc->when) {
- struct divecomputer *newdc = calloc(1, sizeof(*newdc));
- if (!newdc)
- return NULL;
- dc->next = newdc;
- dc = newdc;
- }
- dc->when = dive->when;
- dc->duration = dive->duration;
- return dc;
-}
-
-/*
- * We should *really* try to delay the dive computer data parsing
- * until necessary, in order to reduce load-time. The parsing is
- * cheap, but the loading of the git blob into memory can be pretty
- * costly.
- */
-static int parse_divecomputer_entry(git_repository *repo, const git_tree_entry *entry, const char *suffix)
-{
- (void) suffix;
- git_blob *blob = git_tree_entry_blob(repo, entry);
-
- if (!blob)
- return report_error("Unable to read divecomputer file");
-
- active_dc = create_new_dc(active_dive);
- for_each_line(blob, divecomputer_parser, active_dc);
- git_blob_free(blob);
- active_dc = NULL;
- return 0;
-}
-
-/*
- * NOTE! The "git_id" for the dive is the hash for the whole dive directory.
- * As such, it covers not just the dive, but the divecomputers and the
- * pictures too. So if any of the dive computers change, the dive cache
- * has to be invalidated too.
- */
-static int parse_dive_entry(git_repository *repo, const git_tree_entry *entry, const char *suffix)
-{
- struct dive *dive = active_dive;
- git_blob *blob = git_tree_entry_blob(repo, entry);
- if (!blob)
- return report_error("Unable to read dive file");
- if (*suffix)
- dive->number = atoi(suffix+1);
- cylinder_index = weightsystem_index = 0;
- for_each_line(blob, dive_parser, active_dive);
- git_blob_free(blob);
- return 0;
-}
-
-static int parse_site_entry(git_repository *repo, const git_tree_entry *entry, const char *suffix)
-{
- if (*suffix == '\0')
- return report_error("Dive site without uuid");
- uint32_t uuid = strtoul(suffix, NULL, 16);
- struct dive_site *ds = alloc_or_get_dive_site(uuid);
- git_blob *blob = git_tree_entry_blob(repo, entry);
- if (!blob)
- return report_error("Unable to read dive site file");
- for_each_line(blob, site_parser, ds);
- git_blob_free(blob);
- return 0;
-}
-
-static int parse_trip_entry(git_repository *repo, const git_tree_entry *entry)
-{
- git_blob *blob = git_tree_entry_blob(repo, entry);
- if (!blob)
- return report_error("Unable to read trip file");
- for_each_line(blob, trip_parser, active_trip);
- git_blob_free(blob);
- return 0;
-}
-
-static int parse_settings_entry(git_repository *repo, const git_tree_entry *entry)
-{
- git_blob *blob = git_tree_entry_blob(repo, entry);
- if (!blob)
- return report_error("Unable to read settings file");
- set_save_userid_local(false);
- for_each_line(blob, settings_parser, NULL);
- git_blob_free(blob);
- return 0;
-}
-
-static int parse_picture_file(git_repository *repo, const git_tree_entry *entry, const char *name)
-{
- /* remember the picture data so we can handle it when all dive data has been loaded
- * the name of the git file is PIC-<hash> */
- git_blob *blob = git_tree_entry_blob(repo, entry);
- const void *rawdata = git_blob_rawcontent(blob);
- int len = git_blob_rawsize(blob);
- struct picture_entry_list *new_pel = malloc(sizeof(struct picture_entry_list));
- new_pel->next = pel;
- pel = new_pel;
- pel->data = malloc(len);
- memcpy(pel->data, rawdata, len);
- pel->len = len;
- pel->hash = strdup(name + 4);
- git_blob_free(blob);
- return 0;
-}
-
-static int parse_picture_entry(git_repository *repo, const git_tree_entry *entry, const char *name)
-{
- git_blob *blob;
- struct picture *pic;
- int hh, mm, ss, offset;
- char sign;
-
- /*
- * The format of the picture name files is just the offset within
- * the dive in form [[+-]hh=mm=ss (previously [[+-]hh:mm:ss, but
- * that didn't work on Windows), possibly followed by a hash to
- * make the filename unique (which we can just ignore).
- */
- if (sscanf(name, "%c%d:%d:%d", &sign, &hh, &mm, &ss) != 4 &&
- sscanf(name, "%c%d=%d=%d", &sign, &hh, &mm, &ss) != 4)
- return report_error("Unknown file name %s", name);
- offset = ss + 60*(mm + 60*hh);
- if (sign == '-')
- offset = -offset;
-
- blob = git_tree_entry_blob(repo, entry);
- if (!blob)
- return report_error("Unable to read picture file");
-
- pic = alloc_picture();
- pic->offset.seconds = offset;
-
- for_each_line(blob, picture_parser, pic);
- dive_add_picture(active_dive, pic);
- git_blob_free(blob);
- return 0;
-}
-
-static int walk_tree_file(const char *root, const git_tree_entry *entry, git_repository *repo)
-{
- struct dive *dive = active_dive;
- dive_trip_t *trip = active_trip;
- const char *name = git_tree_entry_name(entry);
- if (verbose > 1)
- fprintf(stderr, "git load handling file %s\n", name);
- switch (*name) {
- /* Picture file? They are saved as time offsets in the dive */
- case '-': case '+':
- if (dive)
- return parse_picture_entry(repo, entry, name);
- break;
- case 'D':
- if (dive && !strncmp(name, "Divecomputer", 12))
- return parse_divecomputer_entry(repo, entry, name+12);
- if (dive && !strncmp(name, "Dive", 4))
- return parse_dive_entry(repo, entry, name+4);
- break;
- case 'S':
- if (!strncmp(name, "Site", 4))
- return parse_site_entry(repo, entry, name + 5);
- break;
- case '0':
- if (trip && !strcmp(name, "00-Trip"))
- return parse_trip_entry(repo, entry);
- if (!strcmp(name, "00-Subsurface"))
- return parse_settings_entry(repo, entry);
- break;
- case 'P':
- if (dive && !strncmp(name, "PIC-", 4))
- return parse_picture_file(repo, entry, name);
- break;
- }
- report_error("Unknown file %s%s (%p %p)", root, name, dive, trip);
- return GIT_WALK_SKIP;
-}
-
-static int walk_tree_cb(const char *root, const git_tree_entry *entry, void *payload)
-{
- git_repository *repo = payload;
- git_filemode_t mode = git_tree_entry_filemode(entry);
-
- if (mode == GIT_FILEMODE_TREE)
- return walk_tree_directory(root, entry);
-
- walk_tree_file(root, entry, repo);
- /* Ignore failed blob loads */
- return GIT_WALK_OK;
-}
-
-static int load_dives_from_tree(git_repository *repo, git_tree *tree)
-{
- git_tree_walk(tree, GIT_TREEWALK_PRE, walk_tree_cb, repo);
- return 0;
-}
-
-void clear_git_id(void)
-{
- saved_git_id = NULL;
-}
-
-void set_git_id(const struct git_oid * id)
-{
- static char git_id_buffer[GIT_OID_HEXSZ+1];
-
- git_oid_tostr(git_id_buffer, sizeof(git_id_buffer), id);
- saved_git_id = git_id_buffer;
-}
-
-static int find_commit(git_repository *repo, const char *branch, git_commit **commit_p)
-{
- git_object *object;
-
- if (git_revparse_single(&object, repo, branch))
- return report_error("Unable to look up revision '%s'", branch);
- if (git_object_peel((git_object **)commit_p, object, GIT_OBJ_COMMIT))
- return report_error("Revision '%s' is not a valid commit", branch);
- return 0;
-}
-
-static int do_git_load(git_repository *repo, const char *branch)
-{
- int ret;
- git_commit *commit;
- git_tree *tree;
-
- ret = find_commit(repo, branch, &commit);
- if (ret)
- return ret;
- if (git_commit_tree(&tree, commit))
- return report_error("Could not look up tree of commit in branch '%s'", branch);
- ret = load_dives_from_tree(repo, tree);
- if (!ret)
- set_git_id(git_commit_id(commit));
- git_object_free((git_object *)tree);
- return ret;
-}
-
-const char *get_sha(git_repository *repo, const char *branch)
-{
- static char git_id_buffer[GIT_OID_HEXSZ+1];
- git_commit *commit;
- if (find_commit(repo, branch, &commit))
- return NULL;
- git_oid_tostr(git_id_buffer, sizeof(git_id_buffer), (const git_oid *)commit);
- return git_id_buffer;
-}
-
-/*
- * Like git_save_dives(), this silently returns a negative
- * value if it's not a git repository at all (so that you
- * can try to load it some other way.
- *
- * If it is a git repository, we return zero for success,
- * or report an error and return 1 if the load failed.
- */
-int git_load_dives(struct git_repository *repo, const char *branch)
-{
- int ret;
-
- if (repo == dummy_git_repository)
- return report_error("Unable to open git repository at '%s'", branch);
- ret = do_git_load(repo, branch);
- git_repository_free(repo);
- free((void *)branch);
- finish_active_dive();
- finish_active_trip();
- return ret;
-}
diff --git a/subsurface-core/macos.c b/subsurface-core/macos.c
deleted file mode 100644
index 500412cd8..000000000
--- a/subsurface-core/macos.c
+++ /dev/null
@@ -1,218 +0,0 @@
-/* macos.c */
-/* implements Mac OS X specific functions */
-#include <stdlib.h>
-#include <dirent.h>
-#include <fnmatch.h>
-#include "dive.h"
-#include "display.h"
-#include <CoreFoundation/CoreFoundation.h>
-#if !defined(__IPHONE_5_0)
-#include <CoreServices/CoreServices.h>
-#endif
-#include <mach-o/dyld.h>
-#include <sys/syslimits.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-void subsurface_user_info(struct user_info *info)
-{
- (void) info;
- /* Nothing, let's use libgit2-20 on MacOS */
-}
-
-/* macos defines CFSTR to create a CFString object from a constant,
- * but no similar macros if a C string variable is supposed to be
- * the argument. We add this here (hardcoding the default allocator
- * and MacRoman encoding */
-#define CFSTR_VAR(_var) CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, \
- (_var), kCFStringEncodingMacRoman, \
- kCFAllocatorNull)
-
-#define SUBSURFACE_PREFERENCES CFSTR("org.hohndel.subsurface")
-#define ICON_NAME "Subsurface.icns"
-#define UI_FONT "Arial 12"
-
-const char mac_system_divelist_default_font[] = "Arial";
-const char *system_divelist_default_font = mac_system_divelist_default_font;
-double system_divelist_default_font_size = -1.0;
-
-void subsurface_OS_pref_setup(void)
-{
- // nothing
-}
-
-bool subsurface_ignore_font(const char *font)
-{
- (void) font;
- // there are no old default fonts to ignore
- return false;
-}
-
-static const char *system_default_path_append(const char *append)
-{
- const char *home = getenv("HOME");
- const char *path = "/Library/Application Support/Subsurface";
-
- int len = strlen(home) + strlen(path) + 1;
- if (append)
- len += strlen(append) + 1;
-
- char *buffer = (char *)malloc(len);
- memset(buffer, 0, len);
- strcat(buffer, home);
- strcat(buffer, path);
- if (append) {
- strcat(buffer, "/");
- strcat(buffer, append);
- }
-
- return buffer;
-}
-
-const char *system_default_directory(void)
-{
- static const char *path = NULL;
- if (!path)
- path = system_default_path_append(NULL);
- return path;
-}
-
-const char *system_default_filename(void)
-{
- static char *filename = NULL;
- if (!filename) {
- const char *user = getenv("LOGNAME");
- if (same_string(user, ""))
- user = "username";
- filename = calloc(strlen(user) + 5, 1);
- strcat(filename, user);
- strcat(filename, ".xml");
- }
- static const char *path = NULL;
- if (!path)
- path = system_default_path_append(filename);
- return path;
-}
-
-int enumerate_devices(device_callback_t callback, void *userdata, int dc_type)
-{
- int index = -1, entries = 0;
- DIR *dp = NULL;
- struct dirent *ep = NULL;
- size_t i;
- if (dc_type != DC_TYPE_UEMIS) {
- const char *dirname = "/dev";
- const char *patterns[] = {
- "tty.*",
- "usbserial",
- NULL
- };
-
- dp = opendir(dirname);
- if (dp == NULL) {
- return -1;
- }
-
- while ((ep = readdir(dp)) != NULL) {
- for (i = 0; patterns[i] != NULL; ++i) {
- if (fnmatch(patterns[i], ep->d_name, 0) == 0) {
- char filename[1024];
- int n = snprintf(filename, sizeof(filename), "%s/%s", dirname, ep->d_name);
- if (n >= (int)sizeof(filename)) {
- closedir(dp);
- return -1;
- }
- callback(filename, userdata);
- if (is_default_dive_computer_device(filename))
- index = entries;
- entries++;
- break;
- }
- }
- }
- closedir(dp);
- }
- if (dc_type != DC_TYPE_SERIAL) {
- const char *dirname = "/Volumes";
- int num_uemis = 0;
- dp = opendir(dirname);
- if (dp == NULL) {
- return -1;
- }
-
- while ((ep = readdir(dp)) != NULL) {
- if (fnmatch("UEMISSDA", ep->d_name, 0) == 0) {
- char filename[1024];
- int n = snprintf(filename, sizeof(filename), "%s/%s", dirname, ep->d_name);
- if (n >= (int)sizeof(filename)) {
- closedir(dp);
- return -1;
- }
- callback(filename, userdata);
- if (is_default_dive_computer_device(filename))
- index = entries;
- entries++;
- num_uemis++;
- break;
- }
- }
- closedir(dp);
- if (num_uemis == 1 && entries == 1) /* if we find exactly one entry and that's a Uemis, select it */
- index = 0;
- }
- return index;
-}
-
-/* NOP wrappers to comform with windows.c */
-int subsurface_rename(const char *path, const char *newpath)
-{
- return rename(path, newpath);
-}
-
-int subsurface_open(const char *path, int oflags, mode_t mode)
-{
- return open(path, oflags, mode);
-}
-
-FILE *subsurface_fopen(const char *path, const char *mode)
-{
- return fopen(path, mode);
-}
-
-void *subsurface_opendir(const char *path)
-{
- return (void *)opendir(path);
-}
-
-int subsurface_access(const char *path, int mode)
-{
- return access(path, mode);
-}
-
-struct zip *subsurface_zip_open_readonly(const char *path, int flags, int *errorp)
-{
- return zip_open(path, flags, errorp);
-}
-
-int subsurface_zip_close(struct zip *zip)
-{
- return zip_close(zip);
-}
-
-/* win32 console */
-void subsurface_console_init(bool dedicated)
-{
- (void) dedicated;
- /* NOP */
-}
-
-void subsurface_console_exit(void)
-{
- /* NOP */
-}
-
-bool subsurface_user_is_root()
-{
- return (geteuid() == 0);
-}
diff --git a/subsurface-core/membuffer.c b/subsurface-core/membuffer.c
deleted file mode 100644
index 053edb8f0..000000000
--- a/subsurface-core/membuffer.c
+++ /dev/null
@@ -1,288 +0,0 @@
-// Clang has a bug on zero-initialization of C structs.
-#pragma clang diagnostic ignored "-Wmissing-field-initializers"
-
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-
-#include "dive.h"
-#include "membuffer.h"
-
-char *detach_buffer(struct membuffer *b)
-{
- char *result = b->buffer;
- b->buffer = NULL;
- b->len = 0;
- b->alloc = 0;
- return result;
-}
-
-void free_buffer(struct membuffer *b)
-{
- free(detach_buffer(b));
-}
-
-void flush_buffer(struct membuffer *b, FILE *f)
-{
- if (b->len) {
- fwrite(b->buffer, 1, b->len, f);
- free_buffer(b);
- }
-}
-
-void strip_mb(struct membuffer *b)
-{
- while (b->len && isspace(b->buffer[b->len - 1]))
- b->len--;
-}
-
-/*
- * Running out of memory isn't really an issue these days.
- * So rather than do insane error handling and making the
- * interface very complex, we'll just die. It won't happen
- * unless you're running on a potato.
- */
-static void oom(void)
-{
- fprintf(stderr, "Out of memory\n");
- exit(1);
-}
-
-static void make_room(struct membuffer *b, unsigned int size)
-{
- unsigned int needed = b->len + size;
- if (needed > b->alloc) {
- char *n;
- /* round it up to not reallocate all the time.. */
- needed = needed * 9 / 8 + 1024;
- n = realloc(b->buffer, needed);
- if (!n)
- oom();
- b->buffer = n;
- b->alloc = needed;
- }
-}
-
-const char *mb_cstring(struct membuffer *b)
-{
- make_room(b, 1);
- b->buffer[b->len] = 0;
- return b->buffer;
-}
-
-void put_bytes(struct membuffer *b, const char *str, int len)
-{
- make_room(b, len);
- memcpy(b->buffer + b->len, str, len);
- b->len += len;
-}
-
-void put_string(struct membuffer *b, const char *str)
-{
- put_bytes(b, str, strlen(str));
-}
-
-void put_vformat(struct membuffer *b, const char *fmt, va_list args)
-{
- int room = 128;
-
- for (;;) {
- int len;
- va_list copy;
- char *target;
-
- make_room(b, room);
- room = b->alloc - b->len;
- target = b->buffer + b->len;
-
- va_copy(copy, args);
- len = vsnprintf(target, room, fmt, copy);
- va_end(copy);
-
- if (len < room) {
- b->len += len;
- return;
- }
-
- room = len + 1;
- }
-}
-
-/* Silly helper using membuffer */
-char *vformat_string(const char *fmt, va_list args)
-{
- struct membuffer mb = { 0 };
- put_vformat(&mb, fmt, args);
- mb_cstring(&mb);
- return detach_buffer(&mb);
-}
-
-char *format_string(const char *fmt, ...)
-{
- va_list args;
- char *result;
-
- va_start(args, fmt);
- result = vformat_string(fmt, args);
- va_end(args);
- return result;
-}
-
-void put_format(struct membuffer *b, const char *fmt, ...)
-{
- va_list args;
-
- va_start(args, fmt);
- put_vformat(b, fmt, args);
- va_end(args);
-}
-
-void put_milli(struct membuffer *b, const char *pre, int value, const char *post)
-{
- int i;
- char buf[4];
- const char *sign = "";
- unsigned v;
-
- v = value;
- if (value < 0) {
- sign = "-";
- v = -value;
- }
- for (i = 2; i >= 0; i--) {
- buf[i] = (v % 10) + '0';
- v /= 10;
- }
- buf[3] = 0;
- if (buf[2] == '0') {
- buf[2] = 0;
- if (buf[1] == '0')
- buf[1] = 0;
- }
-
- put_format(b, "%s%s%u.%s%s", pre, sign, v, buf, post);
-}
-
-void put_temperature(struct membuffer *b, temperature_t temp, const char *pre, const char *post)
-{
- if (temp.mkelvin)
- put_milli(b, pre, temp.mkelvin - ZERO_C_IN_MKELVIN, post);
-}
-
-void put_depth(struct membuffer *b, depth_t depth, const char *pre, const char *post)
-{
- if (depth.mm)
- put_milli(b, pre, depth.mm, post);
-}
-
-void put_duration(struct membuffer *b, duration_t duration, const char *pre, const char *post)
-{
- if (duration.seconds)
- put_format(b, "%s%u:%02u%s", pre, FRACTION(duration.seconds, 60), post);
-}
-
-void put_pressure(struct membuffer *b, pressure_t pressure, const char *pre, const char *post)
-{
- if (pressure.mbar)
- put_milli(b, pre, pressure.mbar, post);
-}
-
-void put_salinity(struct membuffer *b, int salinity, const char *pre, const char *post)
-{
- if (salinity)
- put_format(b, "%s%d%s", pre, salinity / 10, post);
-}
-
-void put_degrees(struct membuffer *b, degrees_t value, const char *pre, const char *post)
-{
- int udeg = value.udeg;
- const char *sign = "";
-
- if (udeg < 0) {
- udeg = -udeg;
- sign = "-";
- }
- put_format(b, "%s%s%u.%06u%s", pre, sign, FRACTION(udeg, 1000000), post);
-}
-
-void put_quoted(struct membuffer *b, const char *text, int is_attribute, int is_html)
-{
- const char *p = text;
-
- for (;;) {
- const char *escape;
-
- switch (*p++) {
- default:
- continue;
- case 0:
- escape = NULL;
- break;
- case 1 ... 8:
- case 11:
- case 12:
- case 14 ... 31:
- escape = "?";
- break;
- case '<':
- escape = "&lt;";
- break;
- case '>':
- escape = "&gt;";
- break;
- case '&':
- escape = "&amp;";
- break;
- case '\'':
- if (!is_attribute)
- continue;
- escape = "&apos;";
- break;
- case '\"':
- if (!is_attribute)
- continue;
- escape = "&quot;";
- break;
- case '\n':
- if (!is_html)
- continue;
- else
- escape = "<br>";
- }
- put_bytes(b, text, (p - text - 1));
- if (!escape)
- break;
- put_string(b, escape);
- text = p;
- }
-}
-
-char *add_to_string_va(const char *old, const char *fmt, va_list args)
-{
- char *res;
- struct membuffer o = { 0 }, n = { 0 };
- put_vformat(&n, fmt, args);
- put_format(&o, "%s\n%s", old ?: "", mb_cstring(&n));
- res = strdup(mb_cstring(&o));
- free_buffer(&o);
- free_buffer(&n);
- free((void *)old);
- return res;
-}
-
-/* this is a convenience function that cleverly adds text to a string, using our membuffer
- * infrastructure.
- * WARNING - this will free(old), the intended pattern is
- * string = add_to_string(string, fmt, ...)
- */
-char *add_to_string(const char *old, const char *fmt, ...)
-{
- char *res;
- va_list args;
-
- va_start(args, fmt);
- res = add_to_string_va(old, fmt, args);
- va_end(args);
- return res;
-}
diff --git a/subsurface-core/membuffer.h b/subsurface-core/membuffer.h
deleted file mode 100644
index 434b34c71..000000000
--- a/subsurface-core/membuffer.h
+++ /dev/null
@@ -1,74 +0,0 @@
-#ifndef MEMBUFFER_H
-#define MEMBUFFER_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <ctype.h>
-
-struct membuffer {
- unsigned int len, alloc;
- char *buffer;
-};
-
-#ifdef __GNUC__
-#define __printf(x, y) __attribute__((__format__(__printf__, x, y)))
-#else
-#define __printf(x, y)
-#endif
-
-extern char *detach_buffer(struct membuffer *b);
-extern void free_buffer(struct membuffer *);
-extern void flush_buffer(struct membuffer *, FILE *);
-extern void put_bytes(struct membuffer *, const char *, int);
-extern void put_string(struct membuffer *, const char *);
-extern void put_quoted(struct membuffer *, const char *, int, int);
-extern void strip_mb(struct membuffer *);
-extern const char *mb_cstring(struct membuffer *);
-extern __printf(2, 0) void put_vformat(struct membuffer *, const char *, va_list);
-extern __printf(2, 3) void put_format(struct membuffer *, const char *fmt, ...);
-extern __printf(2, 0) char *add_to_string_va(const char *old, const char *fmt, va_list args);
-extern __printf(2, 3) char *add_to_string(const char *old, const char *fmt, ...);
-
-/* Helpers that use membuffers internally */
-extern __printf(1, 0) char *vformat_string(const char *, va_list);
-extern __printf(1, 2) char *format_string(const char *, ...);
-
-
-/* Output one of our "milli" values with type and pre/post data */
-extern void put_milli(struct membuffer *, const char *, int, const char *);
-
-/*
- * Helper functions for showing particular types. If the type
- * is empty, nothing is done, and the function returns false.
- * Otherwise, it returns true.
- *
- * The two "const char *" at the end are pre/post data.
- *
- * The reason for the pre/post data is so that you can easily
- * prepend and append a string without having to test whether the
- * type is empty. So
- *
- * put_temperature(b, temp, "Temp=", " C\n");
- *
- * writes nothing to the buffer if there is no temperature data,
- * but otherwise would a string that looks something like
- *
- * "Temp=28.1 C\n"
- *
- * to the memory buffer (typically the post/pre will be some XML
- * pattern and unit string or whatever).
- */
-extern void put_temperature(struct membuffer *, temperature_t, const char *, const char *);
-extern void put_depth(struct membuffer *, depth_t, const char *, const char *);
-extern void put_duration(struct membuffer *, duration_t, const char *, const char *);
-extern void put_pressure(struct membuffer *, pressure_t, const char *, const char *);
-extern void put_salinity(struct membuffer *, int, const char *, const char *);
-extern void put_degrees(struct membuffer *b, degrees_t value, const char *, const char *);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/subsurface-core/metrics.cpp b/subsurface-core/metrics.cpp
deleted file mode 100644
index 3c66528b8..000000000
--- a/subsurface-core/metrics.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * metrics.cpp
- *
- * methods to find/compute essential UI metrics
- * (font properties, icon sizes, etc)
- *
- */
-
-#include "metrics.h"
-
-static IconMetrics dfltIconMetrics;
-
-IconMetrics::IconMetrics() :
- sz_small(-1),
- sz_med(-1),
- sz_big(-1),
- sz_pic(-1),
- spacing(-1),
- dpr(1.0)
-{
-}
-
-QFont defaultModelFont()
-{
- QFont font;
-// font.setPointSizeF(font.pointSizeF() * 0.8);
- return font;
-}
-
-QFontMetrics defaultModelFontMetrics()
-{
- return QFontMetrics(defaultModelFont());
-}
-
-// return the default icon size, computed as the multiple of 16 closest to
-// the given height
-static int defaultIconSize(int height)
-{
- int ret = (height + 8)/16;
- ret *= 16;
- if (ret < 16)
- ret = 16;
- return ret;
-}
-
-const IconMetrics & defaultIconMetrics()
-{
- if (dfltIconMetrics.sz_small == -1) {
- int small = defaultIconSize(defaultModelFontMetrics().height());
- dfltIconMetrics.sz_small = small;
- dfltIconMetrics.sz_med = small + small/2;
- dfltIconMetrics.sz_big = 2*small;
-
- dfltIconMetrics.sz_pic = 8*small;
-
- dfltIconMetrics.spacing = small/8;
- }
-
- return dfltIconMetrics;
-}
-
-void updateDevicePixelRatio(double dpr)
-{
- dfltIconMetrics.dpr = dpr;
-}
diff --git a/subsurface-core/metrics.h b/subsurface-core/metrics.h
deleted file mode 100644
index ca281b3b1..000000000
--- a/subsurface-core/metrics.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * metrics.h
- *
- * header file for common function to find/compute essential UI metrics
- * (font properties, icon sizes, etc)
- *
- */
-#ifndef METRICS_H
-#define METRICS_H
-
-#include <QFont>
-#include <QFontMetrics>
-#include <QSize>
-
-QFont defaultModelFont();
-QFontMetrics defaultModelFontMetrics();
-
-// Collection of icon/picture sizes and other metrics, resolution independent
-struct IconMetrics {
- // icon sizes
- int sz_small; // ex 16px
- int sz_med; // ex 24px
- int sz_big; // ex 32px
- // picture size
- int sz_pic; // ex 128px
- // icon spacing
- int spacing; // ex 2px
- // devicePixelRatio
- double dpr; // 1.0 for traditional screens, HiDPI screens up to 3.0
- IconMetrics();
-};
-
-const IconMetrics & defaultIconMetrics();
-void updateDevicePixelRatio(double dpr);
-
-#endif // METRICS_H
diff --git a/subsurface-core/ostctools.c b/subsurface-core/ostctools.c
deleted file mode 100644
index 9be591b0e..000000000
--- a/subsurface-core/ostctools.c
+++ /dev/null
@@ -1,193 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "dive.h"
-#include "gettext.h"
-#include "divelist.h"
-#include "libdivecomputer.h"
-
-/*
- * Returns a dc_descriptor_t structure based on dc model's number and family.
- */
-
-static dc_descriptor_t *ostc_get_data_descriptor(int data_model, dc_family_t data_fam)
-{
- dc_descriptor_t *descriptor = NULL, *current = NULL;
- ;
- dc_iterator_t *iterator = NULL;
- dc_status_t rc;
-
- rc = dc_descriptor_iterator(&iterator);
- if (rc != DC_STATUS_SUCCESS) {
- fprintf(stderr, "Error creating the device descriptor iterator.\n");
- return current;
- }
- while ((dc_iterator_next(iterator, &descriptor)) == DC_STATUS_SUCCESS) {
- int desc_model = dc_descriptor_get_model(descriptor);
- dc_family_t desc_fam = dc_descriptor_get_type(descriptor);
- if (data_model == desc_model && data_fam == desc_fam) {
- current = descriptor;
- break;
- }
- dc_descriptor_free(descriptor);
- }
- dc_iterator_free(iterator);
- return current;
-}
-
-/*
- * Fills a device_data_t structure with known dc data and a descriptor.
- */
-static int ostc_prepare_data(int data_model, dc_family_t dc_fam, device_data_t *dev_data)
-{
- dc_descriptor_t *data_descriptor;
-
- dev_data->device = NULL;
- dev_data->context = NULL;
-
- data_descriptor = ostc_get_data_descriptor(data_model, dc_fam);
- if (data_descriptor) {
- dev_data->descriptor = data_descriptor;
- dev_data->vendor = copy_string(data_descriptor->vendor);
- dev_data->model = copy_string(data_descriptor->product);
- } else {
- return 0;
- }
- return 1;
-}
-
-/*
- * OSTCTools stores the raw dive data in heavily padded files, one dive
- * each file. So it's not necessary to iterate once and again on a parsing
- * function. Actually there's only one kind of archive for every DC model.
- */
-void ostctools_import(const char *file, struct dive_table *divetable)
-{
- FILE *archive;
- device_data_t *devdata = calloc(1, sizeof(device_data_t));
- dc_family_t dc_fam;
- unsigned char *buffer = calloc(65536, 1), *uc_tmp;
- char *tmp;
- struct dive *ostcdive = alloc_dive();
- dc_status_t rc = 0;
- int model, ret, i = 0;
- unsigned int serial;
- struct extra_data *ptr;
-
- // Open the archive
- if ((archive = subsurface_fopen(file, "rb")) == NULL) {
- report_error(translate("gettextFromC", "Failed to read '%s'"), file);
- free(ostcdive);
- goto out;
- }
-
- // Read dive number from the log
- uc_tmp = calloc(2, 1);
- fseek(archive, 258, 0);
- fread(uc_tmp, 1, 2, archive);
- ostcdive->number = uc_tmp[0] + (uc_tmp[1] << 8);
- free(uc_tmp);
-
- // Read device's serial number
- uc_tmp = calloc(2, 1);
- fseek(archive, 265, 0);
- fread(uc_tmp, 1, 2, archive);
- serial = uc_tmp[0] + (uc_tmp[1] << 8);
- free(uc_tmp);
-
- // Read dive's raw data, header + profile
- fseek(archive, 456, 0);
- while (!feof(archive)) {
- fread(buffer + i, 1, 1, archive);
- if (buffer[i] == 0xFD && buffer[i - 1] == 0xFD)
- break;
- i++;
- }
-
- // Try to determine the dc family based on the header type
- if (buffer[2] == 0x20 || buffer[2] == 0x21) {
- dc_fam = DC_FAMILY_HW_OSTC;
- } else {
- switch (buffer[8]) {
- case 0x22:
- dc_fam = DC_FAMILY_HW_FROG;
- break;
- case 0x23:
- dc_fam = DC_FAMILY_HW_OSTC3;
- break;
- default:
- report_error(translate("gettextFromC", "Unknown DC in dive %d"), ostcdive->number);
- free(ostcdive);
- fclose(archive);
- goto out;
- }
- }
-
- // Try to determine the model based on serial number
- switch (dc_fam) {
- case DC_FAMILY_HW_OSTC:
- if (serial > 7000)
- model = 3; //2C
- else if (serial > 2048)
- model = 2; //2N
- else if (serial > 300)
- model = 1; //MK2
- else
- model = 0; //OSTC
- break;
- case DC_FAMILY_HW_FROG:
- model = 0;
- break;
- default:
- if (serial > 10000)
- model = 0x12; //Sport
- else
- model = 0x0A; //OSTC3
- }
-
- // Prepare data to pass to libdivecomputer.
- ret = ostc_prepare_data(model, dc_fam, devdata);
- if (ret == 0) {
- report_error(translate("gettextFromC", "Unknown DC in dive %d"), ostcdive->number);
- free(ostcdive);
- fclose(archive);
- goto out;
- }
- tmp = calloc(strlen(devdata->vendor) + strlen(devdata->model) + 28, 1);
- sprintf(tmp, "%s %s (Imported from OSTCTools)", devdata->vendor, devdata->model);
- ostcdive->dc.model = copy_string(tmp);
- free(tmp);
-
- // Parse the dive data
- rc = libdc_buffer_parser(ostcdive, devdata, buffer, i + 1);
- if (rc != DC_STATUS_SUCCESS)
- report_error(translate("gettextFromC", "Error - %s - parsing dive %d"), errmsg(rc), ostcdive->number);
-
- // Serial number is not part of the header nor the profile, so libdc won't
- // catch it. If Serial is part of the extra_data, and set to zero, remove
- // it from the list and add again.
- tmp = calloc(12, 1);
- sprintf(tmp, "%d", serial);
- ostcdive->dc.serial = copy_string(tmp);
- free(tmp);
-
- if (ostcdive->dc.extra_data) {
- ptr = ostcdive->dc.extra_data;
- while (strcmp(ptr->key, "Serial"))
- ptr = ptr->next;
- if (!strcmp(ptr->value, "0")) {
- add_extra_data(&ostcdive->dc, "Serial", ostcdive->dc.serial);
- *ptr = *(ptr)->next;
- }
- } else {
- add_extra_data(&ostcdive->dc, "Serial", ostcdive->dc.serial);
- }
- record_dive_to_table(ostcdive, divetable);
- mark_divelist_changed(true);
- sort_table(divetable);
- fclose(archive);
-out:
- free(devdata);
- free(buffer);
-}
diff --git a/subsurface-core/parse-xml.c b/subsurface-core/parse-xml.c
deleted file mode 100644
index e8782251e..000000000
--- a/subsurface-core/parse-xml.c
+++ /dev/null
@@ -1,3751 +0,0 @@
-// Clang has a bug on zero-initialization of C structs.
-#pragma clang diagnostic ignored "-Wmissing-field-initializers"
-
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-#include <assert.h>
-#define __USE_XOPEN
-#include <time.h>
-#include <libxml/parser.h>
-#include <libxml/parserInternals.h>
-#include <libxml/tree.h>
-#include <libxslt/transform.h>
-#include <libdivecomputer/parser.h>
-
-#include "gettext.h"
-
-#include "dive.h"
-#include "divelist.h"
-#include "device.h"
-#include "membuffer.h"
-
-int verbose, quit, force_root;
-int metric = 1;
-int last_xml_version = -1;
-int diveid = -1;
-
-static xmlDoc *test_xslt_transforms(xmlDoc *doc, const char **params);
-
-/* the dive table holds the overall dive list; target table points at
- * the table we are currently filling */
-struct dive_table dive_table;
-struct dive_table *target_table = NULL;
-
-/* Trim a character string by removing leading and trailing white space characters.
- * Parameter: a pointer to a null-terminated character string (buffer);
- * Return value: length of the trimmed string, excluding the terminal 0x0 byte
- * The original pointer (buffer) remains valid after this function has been called
- * and points to the trimmed string */
-int trimspace(char *buffer) {
- int i, size, start, end;
- size = strlen(buffer);
- for(start = 0; isspace(buffer[start]); start++)
- if (start >= size) return 0; // Find 1st character following leading whitespace
- for(end = size - 1; isspace(buffer[end]); end--) // Find last character before trailing whitespace
- if (end <= 0) return 0;
- for(i = start; i <= end; i++) // Move the nonspace characters to the start of the string
- buffer[i-start] = buffer[i];
- size = end - start + 1;
- buffer[size] = 0x0; // then terminate the string
- return size; // return string length
-}
-
-/*
- * Clear a dive_table
- */
-void clear_table(struct dive_table *table)
-{
- for (int i = 0; i < table->nr; i++)
- free(table->dives[i]);
- table->nr = 0;
-}
-
-/*
- * Add a dive into the dive_table array
- */
-void record_dive_to_table(struct dive *dive, struct dive_table *table)
-{
- assert(table != NULL);
- struct dive **dives = grow_dive_table(table);
- int nr = table->nr;
-
- dives[nr] = fixup_dive(dive);
- table->nr = nr + 1;
-}
-
-void record_dive(struct dive *dive)
-{
- record_dive_to_table(dive, &dive_table);
-}
-
-static void start_match(const char *type, const char *name, char *buffer)
-{
- if (verbose > 2)
- printf("Matching %s '%s' (%s)\n",
- type, name, buffer);
-}
-
-static void nonmatch(const char *type, const char *name, char *buffer)
-{
- if (verbose > 1)
- printf("Unable to match %s '%s' (%s)\n",
- type, name, buffer);
-}
-
-typedef void (*matchfn_t)(char *buffer, void *);
-
-static int match(const char *pattern, int plen,
- const char *name,
- matchfn_t fn, char *buf, void *data)
-{
- switch (name[plen]) {
- case '\0':
- case '.':
- break;
- default:
- return 0;
- }
- if (memcmp(pattern, name, plen))
- return 0;
- fn(buf, data);
- return 1;
-}
-
-
-struct units xml_parsing_units;
-const struct units SI_units = SI_UNITS;
-const struct units IMPERIAL_units = IMPERIAL_UNITS;
-
-/*
- * Dive info as it is being built up..
- */
-#define MAX_EVENT_NAME 128
-static struct divecomputer *cur_dc;
-static struct dive *cur_dive;
-static struct dive_site *cur_dive_site;
-degrees_t cur_latitude, cur_longitude;
-static dive_trip_t *cur_trip = NULL;
-static struct sample *cur_sample;
-static struct picture *cur_picture;
-static union {
- struct event event;
- char allocation[sizeof(struct event)+MAX_EVENT_NAME];
-} event_allocation = { .event.deleted = 1 };
-#define cur_event event_allocation.event
-static struct {
- struct {
- const char *model;
- uint32_t deviceid;
- const char *nickname, *serial_nr, *firmware;
- } dc;
-} cur_settings;
-static bool in_settings = false;
-static bool in_userid = false;
-static struct tm cur_tm;
-static int cur_cylinder_index, cur_ws_index;
-static int lastndl, laststoptime, laststopdepth, lastcns, lastpo2, lastindeco;
-static int lastcylinderindex, lastsensor, next_o2_sensor;
-static struct extra_data cur_extra_data;
-
-/*
- * If we don't have an explicit dive computer,
- * we use the implicit one that every dive has..
- */
-static struct divecomputer *get_dc(void)
-{
- return cur_dc ?: &cur_dive->dc;
-}
-
-static enum import_source {
- UNKNOWN,
- LIBDIVECOMPUTER,
- DIVINGLOG,
- UDDF,
- SSRF_WS,
-} import_source;
-
-static void divedate(const char *buffer, timestamp_t *when)
-{
- int d, m, y;
- int hh, mm, ss;
-
- hh = 0;
- mm = 0;
- ss = 0;
- if (sscanf(buffer, "%d.%d.%d %d:%d:%d", &d, &m, &y, &hh, &mm, &ss) >= 3) {
- /* This is ok, and we got at least the date */
- } else if (sscanf(buffer, "%d-%d-%d %d:%d:%d", &y, &m, &d, &hh, &mm, &ss) >= 3) {
- /* This is also ok */
- } else {
- fprintf(stderr, "Unable to parse date '%s'\n", buffer);
- return;
- }
- cur_tm.tm_year = y;
- cur_tm.tm_mon = m - 1;
- cur_tm.tm_mday = d;
- cur_tm.tm_hour = hh;
- cur_tm.tm_min = mm;
- cur_tm.tm_sec = ss;
-
- *when = utc_mktime(&cur_tm);
-}
-
-static void divetime(const char *buffer, timestamp_t *when)
-{
- int h, m, s = 0;
-
- if (sscanf(buffer, "%d:%d:%d", &h, &m, &s) >= 2) {
- cur_tm.tm_hour = h;
- cur_tm.tm_min = m;
- cur_tm.tm_sec = s;
- *when = utc_mktime(&cur_tm);
- }
-}
-
-/* Libdivecomputer: "2011-03-20 10:22:38" */
-static void divedatetime(char *buffer, timestamp_t *when)
-{
- int y, m, d;
- int hr, min, sec;
-
- if (sscanf(buffer, "%d-%d-%d %d:%d:%d",
- &y, &m, &d, &hr, &min, &sec) == 6) {
- cur_tm.tm_year = y;
- cur_tm.tm_mon = m - 1;
- cur_tm.tm_mday = d;
- cur_tm.tm_hour = hr;
- cur_tm.tm_min = min;
- cur_tm.tm_sec = sec;
- *when = utc_mktime(&cur_tm);
- }
-}
-
-enum ParseState {
- FINDSTART,
- FINDEND
-};
-static void divetags(char *buffer, struct tag_entry **tags)
-{
- int i = 0, start = 0, end = 0;
- enum ParseState state = FINDEND;
- int len = buffer ? strlen(buffer) : 0;
-
- while (i < len) {
- if (buffer[i] == ',') {
- if (state == FINDSTART) {
- /* Detect empty tags */
- } else if (state == FINDEND) {
- /* Found end of tag */
- if (i > 0 && buffer[i - 1] != '\\') {
- buffer[i] = '\0';
- state = FINDSTART;
- taglist_add_tag(tags, buffer + start);
- } else {
- state = FINDSTART;
- }
- }
- } else if (buffer[i] == ' ') {
- /* Handled */
- } else {
- /* Found start of tag */
- if (state == FINDSTART) {
- state = FINDEND;
- start = i;
- } else if (state == FINDEND) {
- end = i;
- }
- }
- i++;
- }
- if (state == FINDEND) {
- if (end < start)
- end = len - 1;
- if (len > 0) {
- buffer[end + 1] = '\0';
- taglist_add_tag(tags, buffer + start);
- }
- }
-}
-
-enum number_type {
- NEITHER,
- FLOAT
-};
-
-static enum number_type parse_float(const char *buffer, double *res, const char **endp)
-{
- double val;
- static bool first_time = true;
-
- errno = 0;
- val = ascii_strtod(buffer, endp);
- if (errno || *endp == buffer)
- return NEITHER;
- if (**endp == ',') {
- if (IS_FP_SAME(val, rint(val))) {
- /* we really want to send an error if this is a Subsurface native file
- * as this is likely indication of a bug - but right now we don't have
- * that information available */
- if (first_time) {
- fprintf(stderr, "Floating point value with decimal comma (%s)?\n", buffer);
- first_time = false;
- }
- /* Try again in permissive mode*/
- val = strtod_flags(buffer, endp, 0);
- }
- }
-
- *res = val;
- return FLOAT;
-}
-
-union int_or_float {
- double fp;
-};
-
-static enum number_type integer_or_float(char *buffer, union int_or_float *res)
-{
- const char *end;
- return parse_float(buffer, &res->fp, &end);
-}
-
-static void pressure(char *buffer, pressure_t *pressure)
-{
- double mbar = 0.0;
- union int_or_float val;
-
- switch (integer_or_float(buffer, &val)) {
- case FLOAT:
- /* Just ignore zero values */
- if (!val.fp)
- break;
- switch (xml_parsing_units.pressure) {
- case PASCAL:
- mbar = val.fp / 100;
- break;
- case BAR:
- /* Assume mbar, but if it's really small, it's bar */
- mbar = val.fp;
- if (fabs(mbar) < 5000)
- mbar = mbar * 1000;
- break;
- case PSI:
- mbar = psi_to_mbar(val.fp);
- break;
- }
- if (fabs(mbar) > 5 && fabs(mbar) < 5000000) {
- pressure->mbar = rint(mbar);
- break;
- }
- /* fallthrough */
- default:
- printf("Strange pressure reading %s\n", buffer);
- }
-}
-
-static void cylinder_use(char *buffer, enum cylinderuse *cyl_use)
-{
- if (trimspace(buffer))
- *cyl_use = cylinderuse_from_text(buffer);
-}
-
-static void salinity(char *buffer, int *salinity)
-{
- union int_or_float val;
- switch (integer_or_float(buffer, &val)) {
- case FLOAT:
- *salinity = rint(val.fp * 10.0);
- break;
- default:
- printf("Strange salinity reading %s\n", buffer);
- }
-}
-
-static void depth(char *buffer, depth_t *depth)
-{
- union int_or_float val;
-
- switch (integer_or_float(buffer, &val)) {
- case FLOAT:
- switch (xml_parsing_units.length) {
- case METERS:
- depth->mm = rint(val.fp * 1000);
- break;
- case FEET:
- depth->mm = feet_to_mm(val.fp);
- break;
- }
- break;
- default:
- printf("Strange depth reading %s\n", buffer);
- }
-}
-
-static void extra_data_start(void)
-{
- memset(&cur_extra_data, 0, sizeof(struct extra_data));
-}
-
-static void extra_data_end(void)
-{
- // don't save partial structures - we must have both key and value
- if (cur_extra_data.key && cur_extra_data.value)
- add_extra_data(cur_dc, cur_extra_data.key, cur_extra_data.value);
-}
-
-static void weight(char *buffer, weight_t *weight)
-{
- union int_or_float val;
-
- switch (integer_or_float(buffer, &val)) {
- case FLOAT:
- switch (xml_parsing_units.weight) {
- case KG:
- weight->grams = rint(val.fp * 1000);
- break;
- case LBS:
- weight->grams = lbs_to_grams(val.fp);
- break;
- }
- break;
- default:
- printf("Strange weight reading %s\n", buffer);
- }
-}
-
-static void temperature(char *buffer, temperature_t *temperature)
-{
- union int_or_float val;
-
- switch (integer_or_float(buffer, &val)) {
- case FLOAT:
- switch (xml_parsing_units.temperature) {
- case KELVIN:
- temperature->mkelvin = val.fp * 1000;
- break;
- case CELSIUS:
- temperature->mkelvin = C_to_mkelvin(val.fp);
- break;
- case FAHRENHEIT:
- temperature->mkelvin = F_to_mkelvin(val.fp);
- break;
- }
- break;
- default:
- printf("Strange temperature reading %s\n", buffer);
- }
- /* temperatures outside -40C .. +70C should be ignored */
- if (temperature->mkelvin < ZERO_C_IN_MKELVIN - 40000 ||
- temperature->mkelvin > ZERO_C_IN_MKELVIN + 70000)
- temperature->mkelvin = 0;
-}
-
-static void sampletime(char *buffer, duration_t *time)
-{
- int i;
- int min, sec;
-
- i = sscanf(buffer, "%d:%d", &min, &sec);
- switch (i) {
- case 1:
- sec = min;
- min = 0;
- /* fallthrough */
- case 2:
- time->seconds = sec + min * 60;
- break;
- default:
- printf("Strange sample time reading %s\n", buffer);
- }
-}
-
-static void offsettime(char *buffer, offset_t *time)
-{
- duration_t uoffset;
- int sign = 1;
- if (*buffer == '-') {
- sign = -1;
- buffer++;
- }
- /* yes, this could indeed fail if we have an offset > 34yrs
- * - too bad */
- sampletime(buffer, &uoffset);
- time->seconds = sign * uoffset.seconds;
-}
-
-static void duration(char *buffer, duration_t *time)
-{
- /* DivingLog 5.08 (and maybe other versions) appear to sometimes
- * store the dive time as 44.00 instead of 44:00;
- * This attempts to parse this in a fairly robust way */
- if (!strchr(buffer, ':') && strchr(buffer, '.')) {
- char *mybuffer = strdup(buffer);
- char *dot = strchr(mybuffer, '.');
- *dot = ':';
- sampletime(mybuffer, time);
- free(mybuffer);
- } else {
- sampletime(buffer, time);
- }
-}
-
-static void percent(char *buffer, fraction_t *fraction)
-{
- double val;
- const char *end;
-
- switch (parse_float(buffer, &val, &end)) {
- case FLOAT:
- /* Turn fractions into percent unless explicit.. */
- if (val <= 1.0) {
- while (isspace(*end))
- end++;
- if (*end != '%')
- val *= 100;
- }
-
- /* Then turn percent into our integer permille format */
- if (val >= 0 && val <= 100.0) {
- fraction->permille = rint(val * 10);
- break;
- }
- default:
- printf(translate("gettextFromC", "Strange percentage reading %s\n"), buffer);
- break;
- }
-}
-
-static void gasmix(char *buffer, fraction_t *fraction)
-{
- /* libdivecomputer does negative percentages. */
- if (*buffer == '-')
- return;
- if (cur_cylinder_index < MAX_CYLINDERS)
- percent(buffer, fraction);
-}
-
-static void gasmix_nitrogen(char *buffer, struct gasmix *gasmix)
-{
- (void) buffer;
- (void) gasmix;
- /* Ignore n2 percentages. There's no value in them. */
-}
-
-static void cylindersize(char *buffer, volume_t *volume)
-{
- union int_or_float val;
-
- switch (integer_or_float(buffer, &val)) {
- case FLOAT:
- volume->mliter = rint(val.fp * 1000);
- break;
-
- default:
- printf("Strange volume reading %s\n", buffer);
- break;
- }
-}
-
-static void utf8_string(char *buffer, void *_res)
-{
- char **res = _res;
- int size;
- size = trimspace(buffer);
- if(size)
- *res = strdup(buffer);
-}
-
-static void event_name(char *buffer, char *name)
-{
- int size = trimspace(buffer);
- if (size >= MAX_EVENT_NAME)
- size = MAX_EVENT_NAME-1;
- memcpy(name, buffer, size);
- name[size] = 0;
-}
-
-// We don't use gauge as a mode, and pscr doesn't exist as a libdc divemode
-const char *libdc_divemode_text[] = { "oc", "cc", "pscr", "freedive", "gauge"};
-
-/* Extract the dive computer type from the xml text buffer */
-static void get_dc_type(char *buffer, enum dive_comp_type *dct)
-{
- if (trimspace(buffer)) {
- for (enum dive_comp_type i = 0; i < NUM_DC_TYPE; i++) {
- if (strcmp(buffer, divemode_text[i]) == 0)
- *dct = i;
- else if (strcmp(buffer, libdc_divemode_text[i]) == 0)
- *dct = i;
- }
- }
-}
-
-#define MATCH(pattern, fn, dest) ({ \
- /* Silly type compatibility test */ \
- if (0) (fn)("test", dest); \
- match(pattern, strlen(pattern), name, (matchfn_t) (fn), buf, dest); })
-
-static void get_index(char *buffer, int *i)
-{
- *i = atoi(buffer);
-}
-
-static void get_uint8(char *buffer, uint8_t *i)
-{
- *i = atoi(buffer);
-}
-
-static void get_bearing(char *buffer, bearing_t *bearing)
-{
- bearing->degrees = atoi(buffer);
-}
-
-static void get_rating(char *buffer, int *i)
-{
- int j = atoi(buffer);
- if (j >= 0 && j <= 5) {
- *i = j;
- }
-}
-
-static void double_to_o2pressure(char *buffer, o2pressure_t *i)
-{
- i->mbar = rint(ascii_strtod(buffer, NULL) * 1000.0);
-}
-
-static void hex_value(char *buffer, uint32_t *i)
-{
- *i = strtoul(buffer, NULL, 16);
-}
-
-static void get_tripflag(char *buffer, tripflag_t *tf)
-{
- *tf = strcmp(buffer, "NOTRIP") ? TF_NONE : NO_TRIP;
-}
-
-/*
- * Divinglog is crazy. The temperatures are in celsius. EXCEPT
- * for the sample temperatures, that are in Fahrenheit.
- * WTF?
- *
- * Oh, and I think Diving Log *internally* probably kept them
- * in celsius, because I'm seeing entries like
- *
- * <Temp>32.0</Temp>
- *
- * in there. Which is freezing, aka 0 degC. I bet the "0" is
- * what Diving Log uses for "no temperature".
- *
- * So throw away crap like that.
- *
- * It gets worse. Sometimes the sample temperatures are in
- * Celsius, which apparently happens if you are in a SI
- * locale. So we now do:
- *
- * - temperatures < 32.0 == Celsius
- * - temperature == 32.0 -> garbage, it's a missing temperature (zero converted from C to F)
- * - temperatures > 32.0 == Fahrenheit
- */
-static void fahrenheit(char *buffer, temperature_t *temperature)
-{
- union int_or_float val;
-
- switch (integer_or_float(buffer, &val)) {
- case FLOAT:
- if (IS_FP_SAME(val.fp, 32.0))
- break;
- if (val.fp < 32.0)
- temperature->mkelvin = C_to_mkelvin(val.fp);
- else
- temperature->mkelvin = F_to_mkelvin(val.fp);
- break;
- default:
- fprintf(stderr, "Crazy Diving Log temperature reading %s\n", buffer);
- }
-}
-
-/*
- * Did I mention how bat-shit crazy divinglog is? The sample
- * pressures are in PSI. But the tank working pressure is in
- * bar. WTF^2?
- *
- * Crazy stuff like this is why subsurface has everything in
- * these inconvenient typed structures, and you have to say
- * "pressure->mbar" to get the actual value. Exactly so that
- * you can never have unit confusion.
- *
- * It gets worse: sometimes apparently the pressures are in
- * bar, sometimes in psi. Dirk suspects that this may be a
- * DivingLog Uemis importer bug, and that they are always
- * supposed to be in bar, but that the importer got the
- * sample importing wrong.
- *
- * Sadly, there's no way to really tell. So I think we just
- * have to have some arbitrary cut-off point where we assume
- * that smaller values mean bar.. Not good.
- */
-static void psi_or_bar(char *buffer, pressure_t *pressure)
-{
- union int_or_float val;
-
- switch (integer_or_float(buffer, &val)) {
- case FLOAT:
- if (val.fp > 400)
- pressure->mbar = psi_to_mbar(val.fp);
- else
- pressure->mbar = rint(val.fp * 1000);
- break;
- default:
- fprintf(stderr, "Crazy Diving Log PSI reading %s\n", buffer);
- }
-}
-
-static int divinglog_fill_sample(struct sample *sample, const char *name, char *buf)
-{
- return MATCH("time.p", sampletime, &sample->time) ||
- MATCH("depth.p", depth, &sample->depth) ||
- MATCH("temp.p", fahrenheit, &sample->temperature) ||
- MATCH("press1.p", psi_or_bar, &sample->cylinderpressure) ||
- 0;
-}
-
-static void uddf_gasswitch(char *buffer, struct sample *sample)
-{
- int idx = atoi(buffer);
- int seconds = sample->time.seconds;
- struct dive *dive = cur_dive;
- struct divecomputer *dc = get_dc();
-
- add_gas_switch_event(dive, dc, seconds, idx);
-}
-
-static int uddf_fill_sample(struct sample *sample, const char *name, char *buf)
-{
- return MATCH("divetime", sampletime, &sample->time) ||
- MATCH("depth", depth, &sample->depth) ||
- MATCH("temperature", temperature, &sample->temperature) ||
- MATCH("tankpressure", pressure, &sample->cylinderpressure) ||
- MATCH("ref.switchmix", uddf_gasswitch, sample) ||
- 0;
-}
-
-static void eventtime(char *buffer, duration_t *duration)
-{
- sampletime(buffer, duration);
- if (cur_sample)
- duration->seconds += cur_sample->time.seconds;
-}
-
-static void try_to_match_autogroup(const char *name, char *buf)
-{
- int autogroupvalue;
-
- start_match("autogroup", name, buf);
- if (MATCH("state.autogroup", get_index, &autogroupvalue)) {
- set_autogroup(autogroupvalue);
- return;
- }
- nonmatch("autogroup", name, buf);
-}
-
-void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx)
-{
- /* sanity check so we don't crash */
- if (idx < 0 || idx >= MAX_CYLINDERS)
- return;
- /* The gas switch event format is insane for historical reasons */
- struct gasmix *mix = &dive->cylinder[idx].gasmix;
- int o2 = get_o2(mix);
- int he = get_he(mix);
- struct event *ev;
- int value;
-
- o2 = (o2 + 5) / 10;
- he = (he + 5) / 10;
- value = o2 + (he << 16);
-
- ev = add_event(dc, seconds, he ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE, 0, value, "gaschange");
- if (ev) {
- ev->gas.index = idx;
- ev->gas.mix = *mix;
- }
-}
-
-static void get_cylinderindex(char *buffer, uint8_t *i)
-{
- *i = atoi(buffer);
- if (lastcylinderindex != *i) {
- add_gas_switch_event(cur_dive, get_dc(), cur_sample->time.seconds, *i);
- lastcylinderindex = *i;
- }
-}
-
-static void get_sensor(char *buffer, uint8_t *i)
-{
- *i = atoi(buffer);
- lastsensor = *i;
-}
-
-static void parse_libdc_deco(char *buffer, struct sample *s)
-{
- if (strcmp(buffer, "deco") == 0) {
- s->in_deco = true;
- } else if (strcmp(buffer, "ndl") == 0) {
- s->in_deco = false;
- // The time wasn't stoptime, it was ndl
- s->ndl = s->stoptime;
- s->stoptime.seconds = 0;
- }
-}
-
-static void try_to_fill_dc_settings(const char *name, char *buf)
-{
- start_match("divecomputerid", name, buf);
- if (MATCH("model.divecomputerid", utf8_string, &cur_settings.dc.model))
- return;
- if (MATCH("deviceid.divecomputerid", hex_value, &cur_settings.dc.deviceid))
- return;
- if (MATCH("nickname.divecomputerid", utf8_string, &cur_settings.dc.nickname))
- return;
- if (MATCH("serial.divecomputerid", utf8_string, &cur_settings.dc.serial_nr))
- return;
- if (MATCH("firmware.divecomputerid", utf8_string, &cur_settings.dc.firmware))
- return;
-
- nonmatch("divecomputerid", name, buf);
-}
-
-static void try_to_fill_event(const char *name, char *buf)
-{
- start_match("event", name, buf);
- if (MATCH("event", event_name, cur_event.name))
- return;
- if (MATCH("name", event_name, cur_event.name))
- return;
- if (MATCH("time", eventtime, &cur_event.time))
- return;
- if (MATCH("type", get_index, &cur_event.type))
- return;
- if (MATCH("flags", get_index, &cur_event.flags))
- return;
- if (MATCH("value", get_index, &cur_event.value))
- return;
- if (MATCH("cylinder", get_index, &cur_event.gas.index)) {
- /* We add one to indicate that we got an actual cylinder index value */
- cur_event.gas.index++;
- return;
- }
- if (MATCH("o2", percent, &cur_event.gas.mix.o2))
- return;
- if (MATCH("he", percent, &cur_event.gas.mix.he))
- return;
- nonmatch("event", name, buf);
-}
-
-static int match_dc_data_fields(struct divecomputer *dc, const char *name, char *buf)
-{
- if (MATCH("maxdepth", depth, &dc->maxdepth))
- return 1;
- if (MATCH("meandepth", depth, &dc->meandepth))
- return 1;
- if (MATCH("max.depth", depth, &dc->maxdepth))
- return 1;
- if (MATCH("mean.depth", depth, &dc->meandepth))
- return 1;
- if (MATCH("duration", duration, &dc->duration))
- return 1;
- if (MATCH("divetime", duration, &dc->duration))
- return 1;
- if (MATCH("divetimesec", duration, &dc->duration))
- return 1;
- if (MATCH("surfacetime", duration, &dc->surfacetime))
- return 1;
- if (MATCH("airtemp", temperature, &dc->airtemp))
- return 1;
- if (MATCH("watertemp", temperature, &dc->watertemp))
- return 1;
- if (MATCH("air.temperature", temperature, &dc->airtemp))
- return 1;
- if (MATCH("water.temperature", temperature, &dc->watertemp))
- return 1;
- if (MATCH("pressure.surface", pressure, &dc->surface_pressure))
- return 1;
- if (MATCH("salinity.water", salinity, &dc->salinity))
- return 1;
- if (MATCH("key.extradata", utf8_string, &cur_extra_data.key))
- return 1;
- if (MATCH("value.extradata", utf8_string, &cur_extra_data.value))
- return 1;
- if (MATCH("divemode", get_dc_type, &dc->divemode))
- return 1;
- if (MATCH("salinity", salinity, &dc->salinity))
- return 1;
- if (MATCH("atmospheric", pressure, &dc->surface_pressure))
- return 1;
- return 0;
-}
-
-/* We're in the top-level dive xml. Try to convert whatever value to a dive value */
-static void try_to_fill_dc(struct divecomputer *dc, const char *name, char *buf)
-{
- start_match("divecomputer", name, buf);
-
- if (MATCH("date", divedate, &dc->when))
- return;
- if (MATCH("time", divetime, &dc->when))
- return;
- if (MATCH("model", utf8_string, &dc->model))
- return;
- if (MATCH("deviceid", hex_value, &dc->deviceid))
- return;
- if (MATCH("diveid", hex_value, &dc->diveid))
- return;
- if (MATCH("dctype", get_dc_type, &dc->divemode))
- return;
- if (MATCH("no_o2sensors", get_sensor, &dc->no_o2sensors))
- return;
- if (match_dc_data_fields(dc, name, buf))
- return;
-
- nonmatch("divecomputer", name, buf);
-}
-
-/* We're in samples - try to convert the random xml value to something useful */
-static void try_to_fill_sample(struct sample *sample, const char *name, char *buf)
-{
- int in_deco;
-
- start_match("sample", name, buf);
- if (MATCH("pressure.sample", pressure, &sample->cylinderpressure))
- return;
- if (MATCH("cylpress.sample", pressure, &sample->cylinderpressure))
- return;
- if (MATCH("pdiluent.sample", pressure, &sample->cylinderpressure))
- return;
- if (MATCH("o2pressure.sample", pressure, &sample->o2cylinderpressure))
- return;
- if (MATCH("cylinderindex.sample", get_cylinderindex, &sample->sensor))
- return;
- if (MATCH("sensor.sample", get_sensor, &sample->sensor))
- return;
- if (MATCH("depth.sample", depth, &sample->depth))
- return;
- if (MATCH("temp.sample", temperature, &sample->temperature))
- return;
- if (MATCH("temperature.sample", temperature, &sample->temperature))
- return;
- if (MATCH("sampletime.sample", sampletime, &sample->time))
- return;
- if (MATCH("time.sample", sampletime, &sample->time))
- return;
- if (MATCH("ndl.sample", sampletime, &sample->ndl))
- return;
- if (MATCH("tts.sample", sampletime, &sample->tts))
- return;
- if (MATCH("in_deco.sample", get_index, &in_deco)) {
- sample->in_deco = (in_deco == 1);
- return;
- }
- if (MATCH("stoptime.sample", sampletime, &sample->stoptime))
- return;
- if (MATCH("stopdepth.sample", depth, &sample->stopdepth))
- return;
- if (MATCH("cns.sample", get_uint8, &sample->cns))
- return;
- if (MATCH("rbt.sample", sampletime, &sample->rbt))
- return;
- if (MATCH("sensor1.sample", double_to_o2pressure, &sample->o2sensor[0])) // CCR O2 sensor data
- return;
- if (MATCH("sensor2.sample", double_to_o2pressure, &sample->o2sensor[1]))
- return;
- if (MATCH("sensor3.sample", double_to_o2pressure, &sample->o2sensor[2])) // up to 3 CCR sensors
- return;
- if (MATCH("po2.sample", double_to_o2pressure, &sample->setpoint))
- return;
- if (MATCH("heartbeat", get_uint8, &sample->heartbeat))
- return;
- if (MATCH("bearing", get_bearing, &sample->bearing))
- return;
- if (MATCH("setpoint.sample", double_to_o2pressure, &sample->setpoint))
- return;
- if (MATCH("ppo2.sample", double_to_o2pressure, &sample->o2sensor[next_o2_sensor])) {
- next_o2_sensor++;
- return;
- }
- if (MATCH("deco.sample", parse_libdc_deco, sample))
- return;
- if (MATCH("time.deco", sampletime, &sample->stoptime))
- return;
- if (MATCH("depth.deco", depth, &sample->stopdepth))
- return;
-
- switch (import_source) {
- case DIVINGLOG:
- if (divinglog_fill_sample(sample, name, buf))
- return;
- break;
-
- case UDDF:
- if (uddf_fill_sample(sample, name, buf))
- return;
- break;
-
- default:
- break;
- }
-
- nonmatch("sample", name, buf);
-}
-
-void try_to_fill_userid(const char *name, char *buf)
-{
- (void) name;
- if (prefs.save_userid_local)
- set_userid(buf);
-}
-
-static const char *country, *city;
-
-static void divinglog_place(char *place, uint32_t *uuid)
-{
- char buffer[1024];
-
- snprintf(buffer, sizeof(buffer),
- "%s%s%s%s%s",
- place,
- city ? ", " : "",
- city ? city : "",
- country ? ", " : "",
- country ? country : "");
- *uuid = get_dive_site_uuid_by_name(buffer, NULL);
- if (*uuid == 0)
- *uuid = create_dive_site(buffer, cur_dive->when);
-
- city = NULL;
- country = NULL;
-}
-
-static int divinglog_dive_match(struct dive *dive, const char *name, char *buf)
-{
- return MATCH("divedate", divedate, &dive->when) ||
- MATCH("entrytime", divetime, &dive->when) ||
- MATCH("divetime", duration, &dive->dc.duration) ||
- MATCH("depth", depth, &dive->dc.maxdepth) ||
- MATCH("depthavg", depth, &dive->dc.meandepth) ||
- MATCH("tanktype", utf8_string, &dive->cylinder[0].type.description) ||
- MATCH("tanksize", cylindersize, &dive->cylinder[0].type.size) ||
- MATCH("presw", pressure, &dive->cylinder[0].type.workingpressure) ||
- MATCH("press", pressure, &dive->cylinder[0].start) ||
- MATCH("prese", pressure, &dive->cylinder[0].end) ||
- MATCH("comments", utf8_string, &dive->notes) ||
- MATCH("names.buddy", utf8_string, &dive->buddy) ||
- MATCH("name.country", utf8_string, &country) ||
- MATCH("name.city", utf8_string, &city) ||
- MATCH("name.place", divinglog_place, &dive->dive_site_uuid) ||
- 0;
-}
-
-/*
- * Uddf specifies ISO 8601 time format.
- *
- * There are many variations on that. This handles the useful cases.
- */
-static void uddf_datetime(char *buffer, timestamp_t *when)
-{
- char c;
- int y, m, d, hh, mm, ss;
- struct tm tm = { 0 };
- int i;
-
- i = sscanf(buffer, "%d-%d-%d%c%d:%d:%d", &y, &m, &d, &c, &hh, &mm, &ss);
- if (i == 7)
- goto success;
- ss = 0;
- if (i == 6)
- goto success;
-
- i = sscanf(buffer, "%04d%02d%02d%c%02d%02d%02d", &y, &m, &d, &c, &hh, &mm, &ss);
- if (i == 7)
- goto success;
- ss = 0;
- if (i == 6)
- goto success;
-bad_date:
- printf("Bad date time %s\n", buffer);
- return;
-
-success:
- if (c != 'T' && c != ' ')
- goto bad_date;
- tm.tm_year = y;
- tm.tm_mon = m - 1;
- tm.tm_mday = d;
- tm.tm_hour = hh;
- tm.tm_min = mm;
- tm.tm_sec = ss;
- *when = utc_mktime(&tm);
-}
-
-#define uddf_datedata(name, offset) \
- static void uddf_##name(char *buffer, timestamp_t *when) \
- { \
- cur_tm.tm_##name = atoi(buffer) + offset; \
- *when = utc_mktime(&cur_tm); \
- }
-
-uddf_datedata(year, 0)
-uddf_datedata(mon, -1)
-uddf_datedata(mday, 0)
-uddf_datedata(hour, 0)
-uddf_datedata(min, 0)
-
-static int uddf_dive_match(struct dive *dive, const char *name, char *buf)
-{
- return MATCH("datetime", uddf_datetime, &dive->when) ||
- MATCH("diveduration", duration, &dive->dc.duration) ||
- MATCH("greatestdepth", depth, &dive->dc.maxdepth) ||
- MATCH("year.date", uddf_year, &dive->when) ||
- MATCH("month.date", uddf_mon, &dive->when) ||
- MATCH("day.date", uddf_mday, &dive->when) ||
- MATCH("hour.time", uddf_hour, &dive->when) ||
- MATCH("minute.time", uddf_min, &dive->when) ||
- 0;
-}
-
-/*
- * This parses "floating point" into micro-degrees.
- * We don't do exponentials etc, if somebody does
- * GPS locations in that format, they are insane.
- */
-degrees_t parse_degrees(char *buf, char **end)
-{
- int sign = 1, decimals = 6, value = 0;
- degrees_t ret;
-
- while (isspace(*buf))
- buf++;
- switch (*buf) {
- case '-':
- sign = -1;
- /* fallthrough */
- case '+':
- buf++;
- }
- while (isdigit(*buf)) {
- value = 10 * value + *buf - '0';
- buf++;
- }
-
- /* Get the first six decimals if they exist */
- if (*buf == '.')
- buf++;
- do {
- value *= 10;
- if (isdigit(*buf)) {
- value += *buf - '0';
- buf++;
- }
- } while (--decimals);
-
- /* Rounding */
- switch (*buf) {
- case '5' ... '9':
- value++;
- }
- while (isdigit(*buf))
- buf++;
-
- *end = buf;
- ret.udeg = value * sign;
- return ret;
-}
-
-static void gps_lat(char *buffer, struct dive *dive)
-{
- char *end;
- degrees_t latitude = parse_degrees(buffer, &end);
- struct dive_site *ds = get_dive_site_for_dive(dive);
- if (!ds) {
- dive->dive_site_uuid = create_dive_site_with_gps(NULL, latitude, (degrees_t){0}, dive->when);
- } else {
- if (ds->latitude.udeg && ds->latitude.udeg != latitude.udeg)
- fprintf(stderr, "Oops, changing the latitude of existing dive site id %8x name %s; not good\n", ds->uuid, ds->name ?: "(unknown)");
- ds->latitude = latitude;
- }
-}
-
-static void gps_long(char *buffer, struct dive *dive)
-{
- char *end;
- degrees_t longitude = parse_degrees(buffer, &end);
- struct dive_site *ds = get_dive_site_for_dive(dive);
- if (!ds) {
- dive->dive_site_uuid = create_dive_site_with_gps(NULL, (degrees_t){0}, longitude, dive->when);
- } else {
- if (ds->longitude.udeg && ds->longitude.udeg != longitude.udeg)
- fprintf(stderr, "Oops, changing the longitude of existing dive site id %8x name %s; not good\n", ds->uuid, ds->name ?: "(unknown)");
- ds->longitude = longitude;
- }
-
-}
-
-static void gps_location(char *buffer, struct dive_site *ds)
-{
- char *end;
-
- ds->latitude = parse_degrees(buffer, &end);
- ds->longitude = parse_degrees(end, &end);
-}
-
-/* this is in qthelper.cpp, so including the .h file is a pain */
-extern const char *printGPSCoords(int lat, int lon);
-
-static void gps_in_dive(char *buffer, struct dive *dive)
-{
- char *end;
- struct dive_site *ds = NULL;
- degrees_t latitude = parse_degrees(buffer, &end);
- degrees_t longitude = parse_degrees(end, &end);
- uint32_t uuid = dive->dive_site_uuid;
- if (uuid == 0) {
- // check if we have a dive site within 20 meters of that gps fix
- uuid = get_dive_site_uuid_by_gps_proximity(latitude, longitude, 20, &ds);
-
- if (ds) {
- // found a site nearby; in case it turns out this one had a different name let's
- // remember the original coordinates so we can create the correct dive site later
- cur_latitude = latitude;
- cur_longitude = longitude;
- dive->dive_site_uuid = uuid;
- } else {
- dive->dive_site_uuid = create_dive_site_with_gps("", latitude, longitude, dive->when);
- ds = get_dive_site_by_uuid(dive->dive_site_uuid);
- }
- } else {
- ds = get_dive_site_by_uuid(uuid);
- if (dive_site_has_gps_location(ds) &&
- (latitude.udeg != 0 || longitude.udeg != 0) &&
- (ds->latitude.udeg != latitude.udeg || ds->longitude.udeg != longitude.udeg)) {
- // Houston, we have a problem
- fprintf(stderr, "dive site uuid in dive, but gps location (%10.6f/%10.6f) different from dive location (%10.6f/%10.6f)\n",
- ds->latitude.udeg / 1000000.0, ds->longitude.udeg / 1000000.0,
- latitude.udeg / 1000000.0, longitude.udeg / 1000000.0);
- const char *coords = printGPSCoords(latitude.udeg, longitude.udeg);
- ds->notes = add_to_string(ds->notes, translate("gettextFromC", "multiple GPS locations for this dive site; also %s\n"), coords);
- free((void *)coords);
- } else {
- ds->latitude = latitude;
- ds->longitude = longitude;
- }
- }
-}
-
-static void add_dive_site(char *ds_name, struct dive *dive)
-{
- static int suffix = 1;
- char *buffer = ds_name;
- char *to_free = NULL;
- int size = trimspace(buffer);
- if(size) {
- uint32_t uuid = dive->dive_site_uuid;
- struct dive_site *ds = get_dive_site_by_uuid(uuid);
- if (uuid && !ds) {
- // that's strange - we have a uuid but it doesn't exist - let's just ignore it
- fprintf(stderr, "dive contains a non-existing dive site uuid %x\n", dive->dive_site_uuid);
- uuid = 0;
- }
- if (!uuid) {
- // if the dive doesn't have a uuid, check if there's already a dive site by this name
- uuid = get_dive_site_uuid_by_name(buffer, &ds);
- if (uuid && import_source == SSRF_WS) {
- // when downloading GPS fixes from the Subsurface webservice we will often
- // get a lot of dives with identical names (the autogenerated fixes).
- // So in this case modify the name to make it unique
- int name_size = strlen(buffer) + 10; // 8 digits - enough for 100 million sites
- to_free = buffer = malloc(name_size);
- do {
- suffix++;
- snprintf(buffer, name_size, "%s %8d", ds_name, suffix);
- } while (get_dive_site_uuid_by_name(buffer, NULL) != 0);
- ds = NULL;
- }
- }
- if (ds) {
- // we have a uuid, let's hope there isn't a different name
- if (same_string(ds->name, "")) {
- ds->name = copy_string(buffer);
- } else if (!same_string(ds->name, buffer)) {
- // if it's not the same name, it's not the same dive site
- // but wait, we could have gotten this one based on GPS coords and could
- // have had two different names for the same site... so let's search the other
- // way around
- uint32_t exact_match_uuid = get_dive_site_uuid_by_gps_and_name(buffer, ds->latitude, ds->longitude);
- if (exact_match_uuid) {
- dive->dive_site_uuid = exact_match_uuid;
- } else {
- dive->dive_site_uuid = create_dive_site(buffer, dive->when);
- struct dive_site *newds = get_dive_site_by_uuid(dive->dive_site_uuid);
- if (cur_latitude.udeg || cur_longitude.udeg) {
- // we started this uuid with GPS data, so lets use those
- newds->latitude = cur_latitude;
- newds->longitude = cur_longitude;
- } else {
- newds->latitude = ds->latitude;
- newds->longitude = ds->longitude;
- }
- newds->notes = add_to_string(newds->notes, translate("gettextFromC", "additional name for site: %s\n"), ds->name);
- }
- } else {
- // add the existing dive site to the current dive
- dive->dive_site_uuid = uuid;
- }
- } else {
- dive->dive_site_uuid = create_dive_site(buffer, dive->when);
- }
- }
- free(to_free);
-}
-
-static void gps_picture_location(char *buffer, struct picture *pic)
-{
- char *end;
-
- pic->latitude = parse_degrees(buffer, &end);
- pic->longitude = parse_degrees(end, &end);
-}
-
-/* We're in the top-level dive xml. Try to convert whatever value to a dive value */
-static void try_to_fill_dive(struct dive *dive, const char *name, char *buf)
-{
- start_match("dive", name, buf);
-
- switch (import_source) {
- case DIVINGLOG:
- if (divinglog_dive_match(dive, name, buf))
- return;
- break;
-
- case UDDF:
- if (uddf_dive_match(dive, name, buf))
- return;
- break;
-
- default:
- break;
- }
- if (MATCH("divesiteid", hex_value, &dive->dive_site_uuid))
- return;
- if (MATCH("number", get_index, &dive->number))
- return;
- if (MATCH("tags", divetags, &dive->tag_list))
- return;
- if (MATCH("tripflag", get_tripflag, &dive->tripflag))
- return;
- if (MATCH("date", divedate, &dive->when))
- return;
- if (MATCH("time", divetime, &dive->when))
- return;
- if (MATCH("datetime", divedatetime, &dive->when))
- return;
- /*
- * Legacy format note: per-dive depths and duration get saved
- * in the first dive computer entry
- */
- if (match_dc_data_fields(&dive->dc, name, buf))
- return;
-
- if (MATCH("filename.picture", utf8_string, &cur_picture->filename))
- return;
- if (MATCH("offset.picture", offsettime, &cur_picture->offset))
- return;
- if (MATCH("gps.picture", gps_picture_location, cur_picture))
- return;
- if (MATCH("hash.picture", utf8_string, &cur_picture->hash))
- return;
- if (MATCH("cylinderstartpressure", pressure, &dive->cylinder[0].start))
- return;
- if (MATCH("cylinderendpressure", pressure, &dive->cylinder[0].end))
- return;
- if (MATCH("gps", gps_in_dive, dive))
- return;
- if (MATCH("Place", gps_in_dive, dive))
- return;
- if (MATCH("latitude", gps_lat, dive))
- return;
- if (MATCH("sitelat", gps_lat, dive))
- return;
- if (MATCH("lat", gps_lat, dive))
- return;
- if (MATCH("longitude", gps_long, dive))
- return;
- if (MATCH("sitelon", gps_long, dive))
- return;
- if (MATCH("lon", gps_long, dive))
- return;
- if (MATCH("location", add_dive_site, dive))
- return;
- if (MATCH("name.dive", add_dive_site, dive))
- return;
- if (MATCH("suit", utf8_string, &dive->suit))
- return;
- if (MATCH("divesuit", utf8_string, &dive->suit))
- return;
- if (MATCH("notes", utf8_string, &dive->notes))
- return;
- if (MATCH("divemaster", utf8_string, &dive->divemaster))
- return;
- if (MATCH("buddy", utf8_string, &dive->buddy))
- return;
- if (MATCH("rating.dive", get_rating, &dive->rating))
- return;
- if (MATCH("visibility.dive", get_rating, &dive->visibility))
- return;
- if (cur_ws_index < MAX_WEIGHTSYSTEMS) {
- if (MATCH("description.weightsystem", utf8_string, &dive->weightsystem[cur_ws_index].description))
- return;
- if (MATCH("weight.weightsystem", weight, &dive->weightsystem[cur_ws_index].weight))
- return;
- if (MATCH("weight", weight, &dive->weightsystem[cur_ws_index].weight))
- return;
- }
- if (cur_cylinder_index < MAX_CYLINDERS) {
- if (MATCH("size.cylinder", cylindersize, &dive->cylinder[cur_cylinder_index].type.size))
- return;
- if (MATCH("workpressure.cylinder", pressure, &dive->cylinder[cur_cylinder_index].type.workingpressure))
- return;
- if (MATCH("description.cylinder", utf8_string, &dive->cylinder[cur_cylinder_index].type.description))
- return;
- if (MATCH("start.cylinder", pressure, &dive->cylinder[cur_cylinder_index].start))
- return;
- if (MATCH("end.cylinder", pressure, &dive->cylinder[cur_cylinder_index].end))
- return;
- if (MATCH("use.cylinder", cylinder_use, &dive->cylinder[cur_cylinder_index].cylinder_use))
- return;
- if (MATCH("o2", gasmix, &dive->cylinder[cur_cylinder_index].gasmix.o2))
- return;
- if (MATCH("o2percent", gasmix, &dive->cylinder[cur_cylinder_index].gasmix.o2))
- return;
- if (MATCH("n2", gasmix_nitrogen, &dive->cylinder[cur_cylinder_index].gasmix))
- return;
- if (MATCH("he", gasmix, &dive->cylinder[cur_cylinder_index].gasmix.he))
- return;
- }
- if (MATCH("air.divetemperature", temperature, &dive->airtemp))
- return;
- if (MATCH("water.divetemperature", temperature, &dive->watertemp))
- return;
-
- nonmatch("dive", name, buf);
-}
-
-/* We're in the top-level trip xml. Try to convert whatever value to a trip value */
-static void try_to_fill_trip(dive_trip_t **dive_trip_p, const char *name, char *buf)
-{
- start_match("trip", name, buf);
-
- dive_trip_t *dive_trip = *dive_trip_p;
-
- if (MATCH("date", divedate, &dive_trip->when))
- return;
- if (MATCH("time", divetime, &dive_trip->when))
- return;
- if (MATCH("location", utf8_string, &dive_trip->location))
- return;
- if (MATCH("notes", utf8_string, &dive_trip->notes))
- return;
-
- nonmatch("trip", name, buf);
-}
-
-/* We're processing a divesite entry - try to fill the components */
-static void try_to_fill_dive_site(struct dive_site **ds_p, const char *name, char *buf)
-{
- start_match("divesite", name, buf);
-
- struct dive_site *ds = *ds_p;
- if (ds->taxonomy.category == NULL)
- ds->taxonomy.category = alloc_taxonomy();
-
- if (MATCH("uuid", hex_value, &ds->uuid))
- return;
- if (MATCH("name", utf8_string, &ds->name))
- return;
- if (MATCH("description", utf8_string, &ds->description))
- return;
- if (MATCH("notes", utf8_string, &ds->notes))
- return;
- if (MATCH("gps", gps_location, ds))
- return;
- if (MATCH("cat.geo", get_index, (int *)&ds->taxonomy.category[ds->taxonomy.nr].category))
- return;
- if (MATCH("origin.geo", get_index, (int *)&ds->taxonomy.category[ds->taxonomy.nr].origin))
- return;
- if (MATCH("value.geo", utf8_string, &ds->taxonomy.category[ds->taxonomy.nr].value)) {
- if (ds->taxonomy.nr < TC_NR_CATEGORIES)
- ds->taxonomy.nr++;
- return;
- }
-
- nonmatch("divesite", name, buf);
-}
-
-/*
- * While in some formats file boundaries are dive boundaries, in many
- * others (as for example in our native format) there are
- * multiple dives per file, so there can be other events too that
- * trigger a "new dive" marker and you may get some nesting due
- * to that. Just ignore nesting levels.
- * On the flipside it is possible that we start an XML file that ends
- * up having no dives in it at all - don't create a bogus empty dive
- * for those. It's not entirely clear what is the minimum set of data
- * to make a dive valid, but if it has no location, no date and no
- * samples I'm pretty sure it's useless.
- */
-static bool is_dive(void)
-{
- return (cur_dive &&
- (cur_dive->dive_site_uuid || cur_dive->when || cur_dive->dc.samples));
-}
-
-static void reset_dc_info(struct divecomputer *dc)
-{
- /* WARN: reset dc info does't touch the dc? */
- (void) dc;
- lastcns = lastpo2 = lastndl = laststoptime = laststopdepth = lastindeco = 0;
- lastsensor = lastcylinderindex = 0;
-}
-
-static void reset_dc_settings(void)
-{
- free((void *)cur_settings.dc.model);
- free((void *)cur_settings.dc.nickname);
- free((void *)cur_settings.dc.serial_nr);
- free((void *)cur_settings.dc.firmware);
- cur_settings.dc.model = NULL;
- cur_settings.dc.nickname = NULL;
- cur_settings.dc.serial_nr = NULL;
- cur_settings.dc.firmware = NULL;
- cur_settings.dc.deviceid = 0;
-}
-
-static void settings_start(void)
-{
- in_settings = true;
-}
-
-static void settings_end(void)
-{
- in_settings = false;
-}
-
-static void dc_settings_start(void)
-{
- reset_dc_settings();
-}
-
-static void dc_settings_end(void)
-{
- create_device_node(cur_settings.dc.model, cur_settings.dc.deviceid, cur_settings.dc.serial_nr,
- cur_settings.dc.firmware, cur_settings.dc.nickname);
- reset_dc_settings();
-}
-
-static void dive_site_start(void)
-{
- if (cur_dive_site)
- return;
- cur_dive_site = calloc(1, sizeof(struct dive_site));
-}
-
-static void dive_site_end(void)
-{
- if (!cur_dive_site)
- return;
- if (cur_dive_site->uuid) {
- // we intentionally call this with '0' to ensure we get
- // a new structure and then copy things into that new
- // structure a few lines below (which sets the correct
- // uuid)
- struct dive_site *ds = alloc_or_get_dive_site(0);
- if (cur_dive_site->taxonomy.nr == 0) {
- free(cur_dive_site->taxonomy.category);
- cur_dive_site->taxonomy.category = NULL;
- }
- copy_dive_site(cur_dive_site, ds);
-
- if (verbose > 3)
- printf("completed dive site uuid %x8 name {%s}\n", ds->uuid, ds->name);
- }
- free_taxonomy(&cur_dive_site->taxonomy);
- free(cur_dive_site);
- cur_dive_site = NULL;
-}
-
-// now we need to add the code to parse the parts of the divesite enry
-
-static void dive_start(void)
-{
- if (cur_dive)
- return;
- cur_dive = alloc_dive();
- reset_dc_info(&cur_dive->dc);
- memset(&cur_tm, 0, sizeof(cur_tm));
- if (cur_trip) {
- add_dive_to_trip(cur_dive, cur_trip);
- cur_dive->tripflag = IN_TRIP;
- }
-}
-
-static void dive_end(void)
-{
- if (!cur_dive)
- return;
- if (!is_dive())
- free(cur_dive);
- else
- record_dive_to_table(cur_dive, target_table);
- cur_dive = NULL;
- cur_dc = NULL;
- cur_latitude.udeg = 0;
- cur_longitude.udeg = 0;
- cur_cylinder_index = 0;
- cur_ws_index = 0;
-}
-
-static void trip_start(void)
-{
- if (cur_trip)
- return;
- dive_end();
- cur_trip = calloc(1, sizeof(dive_trip_t));
- memset(&cur_tm, 0, sizeof(cur_tm));
-}
-
-static void trip_end(void)
-{
- if (!cur_trip)
- return;
- insert_trip(&cur_trip);
- cur_trip = NULL;
-}
-
-static void event_start(void)
-{
- memset(&cur_event, 0, sizeof(cur_event));
- cur_event.deleted = 0; /* Active */
-}
-
-static void event_end(void)
-{
- struct divecomputer *dc = get_dc();
- if (strcmp(cur_event.name, "surface") != 0) { /* 123 is a magic event that we used for a while to encode images in dives */
- if (cur_event.type == 123) {
- struct picture *pic = alloc_picture();
- pic->filename = strdup(cur_event.name);
- /* theoretically this could fail - but we didn't support multi year offsets */
- pic->offset.seconds = cur_event.time.seconds;
- dive_add_picture(cur_dive, pic);
- } else {
- struct event *ev;
- /* At some point gas change events did not have any type. Thus we need to add
- * one on import, if we encounter the type one missing.
- */
- if (cur_event.type == 0 && strcmp(cur_event.name, "gaschange") == 0)
- cur_event.type = cur_event.value >> 16 > 0 ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE;
- ev = add_event(dc, cur_event.time.seconds,
- cur_event.type, cur_event.flags,
- cur_event.value, cur_event.name);
-
- /*
- * Older logs might mark the dive to be CCR by having an "SP change" event at time 0:00. Better
- * to mark them being CCR on import so no need for special treatments elsewhere on the code.
- */
- if (ev && cur_event.time.seconds == 0 && cur_event.type == SAMPLE_EVENT_PO2 && dc->divemode==OC) {
- dc->divemode = CCR;
- }
-
- if (ev && event_is_gaschange(ev)) {
- /* See try_to_fill_event() on why the filled-in index is one too big */
- ev->gas.index = cur_event.gas.index-1;
- if (cur_event.gas.mix.o2.permille || cur_event.gas.mix.he.permille)
- ev->gas.mix = cur_event.gas.mix;
- }
- }
- }
- cur_event.deleted = 1; /* No longer active */
-}
-
-static void picture_start(void)
-{
- cur_picture = alloc_picture();
-}
-
-static void picture_end(void)
-{
- dive_add_picture(cur_dive, cur_picture);
- cur_picture = NULL;
-}
-
-static void cylinder_start(void)
-{
-}
-
-static void cylinder_end(void)
-{
- cur_cylinder_index++;
-}
-
-static void ws_start(void)
-{
-}
-
-static void ws_end(void)
-{
- cur_ws_index++;
-}
-
-static void sample_start(void)
-{
- cur_sample = prepare_sample(get_dc());
- cur_sample->ndl.seconds = lastndl;
- cur_sample->in_deco = lastindeco;
- cur_sample->stoptime.seconds = laststoptime;
- cur_sample->stopdepth.mm = laststopdepth;
- cur_sample->cns = lastcns;
- cur_sample->setpoint.mbar = lastpo2;
- cur_sample->sensor = lastsensor;
- next_o2_sensor = 0;
-}
-
-static void sample_end(void)
-{
- if (!cur_dive)
- return;
-
- finish_sample(get_dc());
- lastndl = cur_sample->ndl.seconds;
- lastindeco = cur_sample->in_deco;
- laststoptime = cur_sample->stoptime.seconds;
- laststopdepth = cur_sample->stopdepth.mm;
- lastcns = cur_sample->cns;
- lastpo2 = cur_sample->setpoint.mbar;
- cur_sample = NULL;
-}
-
-static void divecomputer_start(void)
-{
- struct divecomputer *dc;
-
- /* Start from the previous dive computer */
- dc = &cur_dive->dc;
- while (dc->next)
- dc = dc->next;
-
- /* Did we already fill that in? */
- if (dc->samples || dc->model || dc->when) {
- struct divecomputer *newdc = calloc(1, sizeof(*newdc));
- if (newdc) {
- dc->next = newdc;
- dc = newdc;
- }
- }
-
- /* .. this is the one we'll use */
- cur_dc = dc;
- reset_dc_info(dc);
-}
-
-static void divecomputer_end(void)
-{
- if (!cur_dc->when)
- cur_dc->when = cur_dive->when;
- cur_dc = NULL;
-}
-
-static void userid_start(void)
-{
- in_userid = true;
- set_save_userid_local(true); //if the xml contains userid, keep saving it.
-}
-
-static void userid_stop(void)
-{
- in_userid = false;
-}
-
-static bool entry(const char *name, char *buf)
-{
- if (!strncmp(name, "version.program", sizeof("version.program") - 1) ||
- !strncmp(name, "version.divelog", sizeof("version.divelog") - 1)) {
- last_xml_version = atoi(buf);
- report_datafile_version(last_xml_version);
- }
- if (in_userid) {
- try_to_fill_userid(name, buf);
- return true;
- }
- if (in_settings) {
- try_to_fill_dc_settings(name, buf);
- try_to_match_autogroup(name, buf);
- return true;
- }
- if (cur_dive_site) {
- try_to_fill_dive_site(&cur_dive_site, name, buf);
- return true;
- }
- if (!cur_event.deleted) {
- try_to_fill_event(name, buf);
- return true;
- }
- if (cur_sample) {
- try_to_fill_sample(cur_sample, name, buf);
- return true;
- }
- if (cur_dc) {
- try_to_fill_dc(cur_dc, name, buf);
- return true;
- }
- if (cur_dive) {
- try_to_fill_dive(cur_dive, name, buf);
- return true;
- }
- if (cur_trip) {
- try_to_fill_trip(&cur_trip, name, buf);
- return true;
- }
- return true;
-}
-
-static const char *nodename(xmlNode *node, char *buf, int len)
-{
- int levels = 2;
- char *p = buf;
-
- if (!node || (node->type != XML_CDATA_SECTION_NODE && !node->name)) {
- return "root";
- }
-
- if (node->type == XML_CDATA_SECTION_NODE || (node->parent && !strcmp((const char *)node->name, "text")))
- node = node->parent;
-
- /* Make sure it's always NUL-terminated */
- p[--len] = 0;
-
- for (;;) {
- const char *name = (const char *)node->name;
- char c;
- while ((c = *name++) != 0) {
- /* Cheaper 'tolower()' for ASCII */
- c = (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c;
- *p++ = c;
- if (!--len)
- return buf;
- }
- *p = 0;
- node = node->parent;
- if (!node || !node->name)
- return buf;
- *p++ = '.';
- if (!--len)
- return buf;
- if (!--levels)
- return buf;
- }
-}
-
-#define MAXNAME 32
-
-static bool visit_one_node(xmlNode *node)
-{
- xmlChar *content;
- static char buffer[MAXNAME];
- const char *name;
-
- content = node->content;
- if (!content || xmlIsBlankNode(node))
- return true;
-
- name = nodename(node, buffer, sizeof(buffer));
-
- return entry(name, (char *)content);
-}
-
-static bool traverse(xmlNode *root);
-
-static bool traverse_properties(xmlNode *node)
-{
- xmlAttr *p;
- bool ret = true;
-
- for (p = node->properties; p; p = p->next)
- if ((ret = traverse(p->children)) == false)
- break;
- return ret;
-}
-
-static bool visit(xmlNode *n)
-{
- return visit_one_node(n) && traverse_properties(n) && traverse(n->children);
-}
-
-static void DivingLog_importer(void)
-{
- import_source = DIVINGLOG;
-
- /*
- * Diving Log units are really strange.
- *
- * Temperatures are in C, except in samples,
- * when they are in Fahrenheit. Depths are in
- * meters, an dpressure is in PSI in the samples,
- * but in bar when it comes to working pressure.
- *
- * Crazy f*%^ morons.
- */
- xml_parsing_units = SI_units;
-}
-
-static void uddf_importer(void)
-{
- import_source = UDDF;
- xml_parsing_units = SI_units;
- xml_parsing_units.pressure = PASCAL;
- xml_parsing_units.temperature = KELVIN;
-}
-
-static void subsurface_webservice(void)
-{
- import_source = SSRF_WS;
-}
-
-/*
- * I'm sure this could be done as some fancy DTD rules.
- * It's just not worth the headache.
- */
-static struct nesting {
- const char *name;
- void (*start)(void), (*end)(void);
-} nesting[] = {
- { "divecomputerid", dc_settings_start, dc_settings_end },
- { "settings", settings_start, settings_end },
- { "site", dive_site_start, dive_site_end },
- { "dive", dive_start, dive_end },
- { "Dive", dive_start, dive_end },
- { "trip", trip_start, trip_end },
- { "sample", sample_start, sample_end },
- { "waypoint", sample_start, sample_end },
- { "SAMPLE", sample_start, sample_end },
- { "reading", sample_start, sample_end },
- { "event", event_start, event_end },
- { "mix", cylinder_start, cylinder_end },
- { "gasmix", cylinder_start, cylinder_end },
- { "cylinder", cylinder_start, cylinder_end },
- { "weightsystem", ws_start, ws_end },
- { "divecomputer", divecomputer_start, divecomputer_end },
- { "P", sample_start, sample_end },
- { "userid", userid_start, userid_stop},
- { "picture", picture_start, picture_end },
- { "extradata", extra_data_start, extra_data_end },
-
- /* Import type recognition */
- { "Divinglog", DivingLog_importer },
- { "uddf", uddf_importer },
- { "output", subsurface_webservice },
- { NULL, }
- };
-
-static bool traverse(xmlNode *root)
-{
- xmlNode *n;
- bool ret = true;
-
- for (n = root; n; n = n->next) {
- struct nesting *rule = nesting;
-
- if (!n->name) {
- if ((ret = visit(n)) == false)
- break;
- continue;
- }
-
- do {
- if (!strcmp(rule->name, (const char *)n->name))
- break;
- rule++;
- } while (rule->name);
-
- if (rule->start)
- rule->start();
- if ((ret = visit(n)) == false)
- break;
- if (rule->end)
- rule->end();
- }
- return ret;
-}
-
-/* Per-file reset */
-static void reset_all(void)
-{
- /*
- * We reset the units for each file. You'd think it was
- * a per-dive property, but I'm not going to trust people
- * to do per-dive setup. If the xml does have per-dive
- * data within one file, we might have to reset it per
- * dive for that format.
- */
- xml_parsing_units = SI_units;
- import_source = UNKNOWN;
-}
-
-/* divelog.de sends us xml files that claim to be iso-8859-1
- * but once we decode the HTML encoded characters they turn
- * into UTF-8 instead. So skip the incorrect encoding
- * declaration and decode the HTML encoded characters */
-const char *preprocess_divelog_de(const char *buffer)
-{
- char *ret = strstr(buffer, "<DIVELOGSDATA>");
-
- if (ret) {
- xmlParserCtxtPtr ctx;
- char buf[] = "";
- size_t i;
-
- for (i = 0; i < strlen(ret); ++i)
- if (!isascii(ret[i]))
- return buffer;
-
- ctx = xmlCreateMemoryParserCtxt(buf, sizeof(buf));
- ret = (char *)xmlStringLenDecodeEntities(ctx, (xmlChar *)ret, strlen(ret), XML_SUBSTITUTE_REF, 0, 0, 0);
-
- return ret;
- }
- return buffer;
-}
-
-int parse_xml_buffer(const char *url, const char *buffer, int size,
- struct dive_table *table, const char **params)
-{
- (void) size;
- xmlDoc *doc;
- const char *res = preprocess_divelog_de(buffer);
- int ret = 0;
-
- target_table = table;
- doc = xmlReadMemory(res, strlen(res), url, NULL, 0);
- if (res != buffer)
- free((char *)res);
-
- if (!doc)
- return report_error(translate("gettextFromC", "Failed to parse '%s'"), url);
-
- set_save_userid_local(false);
- reset_all();
- dive_start();
- doc = test_xslt_transforms(doc, params);
- if (!traverse(xmlDocGetRootElement(doc))) {
- // we decided to give up on parsing... why?
- ret = -1;
- }
- dive_end();
- xmlFreeDoc(doc);
- return ret;
-}
-
-void parse_mkvi_buffer(struct membuffer *txt, struct membuffer *csv, const char *starttime)
-{
- (void) csv;
- (void) txt;
- dive_start();
- divedate(starttime, &cur_dive->when);
- dive_end();
-}
-
-extern int dm4_events(void *handle, int columns, char **data, char **column)
-{
- (void) handle;
- (void) columns;
- (void) column;
-
- event_start();
- if (data[1])
- cur_event.time.seconds = atoi(data[1]);
-
- if (data[2]) {
- switch (atoi(data[2])) {
- case 1:
- /* 1 Mandatory Safety Stop */
- strcpy(cur_event.name, "safety stop (mandatory)");
- break;
- case 3:
- /* 3 Deco */
- /* What is Subsurface's term for going to
- * deco? */
- strcpy(cur_event.name, "deco");
- break;
- case 4:
- /* 4 Ascent warning */
- strcpy(cur_event.name, "ascent");
- break;
- case 5:
- /* 5 Ceiling broken */
- strcpy(cur_event.name, "violation");
- break;
- case 6:
- /* 6 Mandatory safety stop ceiling error */
- strcpy(cur_event.name, "violation");
- break;
- case 7:
- /* 7 Below deco floor */
- strcpy(cur_event.name, "below floor");
- break;
- case 8:
- /* 8 Dive time alarm */
- strcpy(cur_event.name, "divetime");
- break;
- case 9:
- /* 9 Depth alarm */
- strcpy(cur_event.name, "maxdepth");
- break;
- case 10:
- /* 10 OLF 80% */
- case 11:
- /* 11 OLF 100% */
- strcpy(cur_event.name, "OLF");
- break;
- case 12:
- /* 12 High pOâ‚‚ */
- strcpy(cur_event.name, "PO2");
- break;
- case 13:
- /* 13 Air time */
- strcpy(cur_event.name, "airtime");
- break;
- case 17:
- /* 17 Ascent warning */
- strcpy(cur_event.name, "ascent");
- break;
- case 18:
- /* 18 Ceiling error */
- strcpy(cur_event.name, "ceiling");
- break;
- case 19:
- /* 19 Surfaced */
- strcpy(cur_event.name, "surface");
- break;
- case 20:
- /* 20 Deco */
- strcpy(cur_event.name, "deco");
- break;
- case 22:
- case 32:
- /* 22 Mandatory safety stop violation */
- /* 32 Deep stop violation */
- strcpy(cur_event.name, "violation");
- break;
- case 30:
- /* Tissue level warning */
- strcpy(cur_event.name, "tissue warning");
- break;
- case 37:
- /* Tank pressure alarm */
- strcpy(cur_event.name, "tank pressure");
- break;
- case 257:
- /* 257 Dive active */
- /* This seems to be given after surface when
- * descending again. */
- strcpy(cur_event.name, "surface");
- break;
- case 258:
- /* 258 Bookmark */
- if (data[3]) {
- strcpy(cur_event.name, "heading");
- cur_event.value = atoi(data[3]);
- } else {
- strcpy(cur_event.name, "bookmark");
- }
- break;
- case 259:
- /* Deep stop */
- strcpy(cur_event.name, "Deep stop");
- break;
- case 260:
- /* Deep stop */
- strcpy(cur_event.name, "Deep stop cleared");
- break;
- case 266:
- /* Mandatory safety stop activated */
- strcpy(cur_event.name, "safety stop (mandatory)");
- break;
- case 267:
- /* Mandatory safety stop deactivated */
- /* DM5 shows this only on event list, not on the
- * profile so skipping as well for now */
- break;
- default:
- strcpy(cur_event.name, "unknown");
- cur_event.value = atoi(data[2]);
- break;
- }
- }
- event_end();
-
- return 0;
-}
-
-extern int dm5_cylinders(void *handle, int columns, char **data, char **column)
-{
- (void) handle;
- (void) columns;
- (void) column;
-
- cylinder_start();
- if (data[7] && atoi(data[7]) > 0 && atoi(data[7]) < 350000)
- cur_dive->cylinder[cur_cylinder_index].start.mbar = atoi(data[7]);
- if (data[8] && atoi(data[8]) > 0 && atoi(data[8]) < 350000)
- cur_dive->cylinder[cur_cylinder_index].end.mbar = (atoi(data[8]));
- if (data[6]) {
- /* DM5 shows tank size of 12 liters when the actual
- * value is 0 (and using metric units). So we just use
- * the same 12 liters when size is not available */
- if (atof(data[6]) == 0.0 && cur_dive->cylinder[cur_cylinder_index].start.mbar)
- cur_dive->cylinder[cur_cylinder_index].type.size.mliter = 12000;
- else
- cur_dive->cylinder[cur_cylinder_index].type.size.mliter = (atof(data[6])) * 1000;
- }
- if (data[2])
- cur_dive->cylinder[cur_cylinder_index].gasmix.o2.permille = atoi(data[2]) * 10;
- if (data[3])
- cur_dive->cylinder[cur_cylinder_index].gasmix.he.permille = atoi(data[3]) * 10;
- cylinder_end();
- return 0;
-}
-
-extern int dm5_gaschange(void *handle, int columns, char **data, char **column)
-{
- (void) handle;
- (void) columns;
- (void) column;
-
- event_start();
- if (data[0])
- cur_event.time.seconds = atoi(data[0]);
- if (data[1]) {
- strcpy(cur_event.name, "gaschange");
- cur_event.value = atof(data[1]);
- }
- event_end();
-
- return 0;
-}
-
-extern int dm4_tags(void *handle, int columns, char **data, char **column)
-{
- (void) handle;
- (void) columns;
- (void) column;
-
- if (data[0])
- taglist_add_tag(&cur_dive->tag_list, data[0]);
-
- return 0;
-}
-
-extern int dm4_dive(void *param, int columns, char **data, char **column)
-{
- (void) columns;
- (void) column;
- unsigned int i;
- int interval, retval = 0;
- sqlite3 *handle = (sqlite3 *)param;
- float *profileBlob;
- unsigned char *tempBlob;
- int *pressureBlob;
- char *err = NULL;
- char get_events_template[] = "select * from Mark where DiveId = %d";
- char get_tags_template[] = "select Text from DiveTag where DiveId = %d";
- char get_events[64];
-
- dive_start();
- cur_dive->number = atoi(data[0]);
-
- cur_dive->when = (time_t)(atol(data[1]));
- if (data[2])
- utf8_string(data[2], &cur_dive->notes);
-
- /*
- * DM4 stores Duration and DiveTime. It looks like DiveTime is
- * 10 to 60 seconds shorter than Duration. However, I have no
- * idea what is the difference and which one should be used.
- * Duration = data[3]
- * DiveTime = data[15]
- */
- if (data[3])
- cur_dive->duration.seconds = atoi(data[3]);
- if (data[15])
- cur_dive->dc.duration.seconds = atoi(data[15]);
-
- /*
- * TODO: the deviceid hash should be calculated here.
- */
- settings_start();
- dc_settings_start();
- if (data[4])
- utf8_string(data[4], &cur_settings.dc.serial_nr);
- if (data[5])
- utf8_string(data[5], &cur_settings.dc.model);
-
- cur_settings.dc.deviceid = 0xffffffff;
- dc_settings_end();
- settings_end();
-
- if (data[6])
- cur_dive->dc.maxdepth.mm = atof(data[6]) * 1000;
- if (data[8])
- cur_dive->dc.airtemp.mkelvin = C_to_mkelvin(atoi(data[8]));
- if (data[9])
- cur_dive->dc.watertemp.mkelvin = C_to_mkelvin(atoi(data[9]));
-
- /*
- * TODO: handle multiple cylinders
- */
- cylinder_start();
- if (data[22] && atoi(data[22]) > 0)
- cur_dive->cylinder[cur_cylinder_index].start.mbar = atoi(data[22]);
- else if (data[10] && atoi(data[10]) > 0)
- cur_dive->cylinder[cur_cylinder_index].start.mbar = atoi(data[10]);
- if (data[23] && atoi(data[23]) > 0)
- cur_dive->cylinder[cur_cylinder_index].end.mbar = (atoi(data[23]));
- if (data[11] && atoi(data[11]) > 0)
- cur_dive->cylinder[cur_cylinder_index].end.mbar = (atoi(data[11]));
- if (data[12])
- cur_dive->cylinder[cur_cylinder_index].type.size.mliter = (atof(data[12])) * 1000;
- if (data[13])
- cur_dive->cylinder[cur_cylinder_index].type.workingpressure.mbar = (atoi(data[13]));
- if (data[20])
- cur_dive->cylinder[cur_cylinder_index].gasmix.o2.permille = atoi(data[20]) * 10;
- if (data[21])
- cur_dive->cylinder[cur_cylinder_index].gasmix.he.permille = atoi(data[21]) * 10;
- cylinder_end();
-
- if (data[14])
- cur_dive->dc.surface_pressure.mbar = (atoi(data[14]) * 1000);
-
- interval = data[16] ? atoi(data[16]) : 0;
- profileBlob = (float *)data[17];
- tempBlob = (unsigned char *)data[18];
- pressureBlob = (int *)data[19];
- for (i = 0; interval && i * interval < cur_dive->duration.seconds; i++) {
- sample_start();
- cur_sample->time.seconds = i * interval;
- if (profileBlob)
- cur_sample->depth.mm = profileBlob[i] * 1000;
- else
- cur_sample->depth.mm = cur_dive->dc.maxdepth.mm;
-
- if (data[18] && data[18][0])
- cur_sample->temperature.mkelvin = C_to_mkelvin(tempBlob[i]);
- if (data[19] && data[19][0])
- cur_sample->cylinderpressure.mbar = pressureBlob[i];
- sample_end();
- }
-
- snprintf(get_events, sizeof(get_events) - 1, get_events_template, cur_dive->number);
- retval = sqlite3_exec(handle, get_events, &dm4_events, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query dm4_events failed.\n");
- return 1;
- }
-
- snprintf(get_events, sizeof(get_events) - 1, get_tags_template, cur_dive->number);
- retval = sqlite3_exec(handle, get_events, &dm4_tags, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query dm4_tags failed.\n");
- return 1;
- }
-
- dive_end();
-
- /*
- for (i=0; i<columns;++i) {
- fprintf(stderr, "%s\t", column[i]);
- }
- fprintf(stderr, "\n");
- for (i=0; i<columns;++i) {
- fprintf(stderr, "%s\t", data[i]);
- }
- fprintf(stderr, "\n");
- //exit(0);
- */
- return SQLITE_OK;
-}
-
-extern int dm5_dive(void *param, int columns, char **data, char **column)
-{
- (void) columns;
- (void) column;
- unsigned int i;
- int interval, retval = 0, block_size;
- sqlite3 *handle = (sqlite3 *)param;
- unsigned const char *sampleBlob;
- char *err = NULL;
- char get_events_template[] = "select * from Mark where DiveId = %d";
- char get_tags_template[] = "select Text from DiveTag where DiveId = %d";
- char get_cylinders_template[] = "select * from DiveMixture where DiveId = %d";
- char get_gaschange_template[] = "select GasChangeTime,Oxygen,Helium from DiveGasChange join DiveMixture on DiveGasChange.DiveMixtureId=DiveMixture.DiveMixtureId where DiveId = %d";
- char get_events[512];
-
- dive_start();
- cur_dive->number = atoi(data[0]);
-
- cur_dive->when = (time_t)(atol(data[1]));
- if (data[2])
- utf8_string(data[2], &cur_dive->notes);
-
- if (data[3])
- cur_dive->duration.seconds = atoi(data[3]);
- if (data[15])
- cur_dive->dc.duration.seconds = atoi(data[15]);
-
- /*
- * TODO: the deviceid hash should be calculated here.
- */
- settings_start();
- dc_settings_start();
- if (data[4]) {
- utf8_string(data[4], &cur_settings.dc.serial_nr);
- cur_settings.dc.deviceid = atoi(data[4]);
- }
- if (data[5])
- utf8_string(data[5], &cur_settings.dc.model);
-
- dc_settings_end();
- settings_end();
-
- if (data[6])
- cur_dive->dc.maxdepth.mm = atof(data[6]) * 1000;
- if (data[8])
- cur_dive->dc.airtemp.mkelvin = C_to_mkelvin(atoi(data[8]));
- if (data[9])
- cur_dive->dc.watertemp.mkelvin = C_to_mkelvin(atoi(data[9]));
-
- if (data[4]) {
- cur_dive->dc.deviceid = atoi(data[4]);
- }
- if (data[5])
- utf8_string(data[5], &cur_dive->dc.model);
-
- snprintf(get_events, sizeof(get_events) - 1, get_cylinders_template, cur_dive->number);
- retval = sqlite3_exec(handle, get_events, &dm5_cylinders, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query dm5_cylinders failed.\n");
- return 1;
- }
-
- if (data[14])
- cur_dive->dc.surface_pressure.mbar = (atoi(data[14]) / 100);
-
- interval = data[16] ? atoi(data[16]) : 0;
- sampleBlob = (unsigned const char *)data[24];
-
- if (sampleBlob) {
- switch (sampleBlob[0]) {
- case 2:
- block_size = 19;
- break;
- case 3:
- block_size = 23;
- break;
- default:
- block_size = 16;
- break;
- }
- }
-
- for (i = 0; interval && sampleBlob && i * interval < cur_dive->duration.seconds; i++) {
- float *depth = (float *)&sampleBlob[i * block_size + 3];
- int32_t temp = (sampleBlob[i * block_size + 10] << 8) + sampleBlob[i * block_size + 11];
- int32_t pressure = (sampleBlob[i * block_size + 9] << 16) + (sampleBlob[i * block_size + 8] << 8) + sampleBlob[i * block_size + 7];
-
- sample_start();
- cur_sample->time.seconds = i * interval;
- cur_sample->depth.mm = depth[0] * 1000;
- /*
- * Limit temperatures and cylinder pressures to somewhat
- * sensible values
- */
- if (temp >= -10 && temp < 50)
- cur_sample->temperature.mkelvin = C_to_mkelvin(temp);
- if (pressure >= 0 && pressure < 350000)
- cur_sample->cylinderpressure.mbar = pressure;
- sample_end();
- }
-
- /*
- * Log was converted from DM4, thus we need to parse the profile
- * from DM4 format
- */
-
- if (i == 0) {
- float *profileBlob;
- unsigned char *tempBlob;
- int *pressureBlob;
-
- profileBlob = (float *)data[17];
- tempBlob = (unsigned char *)data[18];
- pressureBlob = (int *)data[19];
- for (i = 0; interval && i * interval < cur_dive->duration.seconds; i++) {
- sample_start();
- cur_sample->time.seconds = i * interval;
- if (profileBlob)
- cur_sample->depth.mm = profileBlob[i] * 1000;
- else
- cur_sample->depth.mm = cur_dive->dc.maxdepth.mm;
-
- if (data[18] && data[18][0])
- cur_sample->temperature.mkelvin = C_to_mkelvin(tempBlob[i]);
- if (data[19] && data[19][0])
- cur_sample->cylinderpressure.mbar = pressureBlob[i];
- sample_end();
- }
- }
-
- snprintf(get_events, sizeof(get_events) - 1, get_gaschange_template, cur_dive->number);
- retval = sqlite3_exec(handle, get_events, &dm5_gaschange, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query dm5_gaschange failed.\n");
- return 1;
- }
-
- snprintf(get_events, sizeof(get_events) - 1, get_events_template, cur_dive->number);
- retval = sqlite3_exec(handle, get_events, &dm4_events, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query dm4_events failed.\n");
- return 1;
- }
-
- snprintf(get_events, sizeof(get_events) - 1, get_tags_template, cur_dive->number);
- retval = sqlite3_exec(handle, get_events, &dm4_tags, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query dm4_tags failed.\n");
- return 1;
- }
-
- dive_end();
-
- return SQLITE_OK;
-}
-
-
-int parse_dm4_buffer(sqlite3 *handle, const char *url, const char *buffer, int size,
- struct dive_table *table)
-{
- (void) buffer;
- (void) size;
-
- int retval;
- char *err = NULL;
- target_table = table;
-
- /* StartTime is converted from Suunto's nano seconds to standard
- * time. We also need epoch, not seconds since year 1. */
- char get_dives[] = "select D.DiveId,StartTime/10000000-62135596800,Note,Duration,SourceSerialNumber,Source,MaxDepth,SampleInterval,StartTemperature,BottomTemperature,D.StartPressure,D.EndPressure,Size,CylinderWorkPressure,SurfacePressure,DiveTime,SampleInterval,ProfileBlob,TemperatureBlob,PressureBlob,Oxygen,Helium,MIX.StartPressure,MIX.EndPressure FROM Dive AS D JOIN DiveMixture AS MIX ON D.DiveId=MIX.DiveId";
-
- retval = sqlite3_exec(handle, get_dives, &dm4_dive, handle, &err);
-
- if (retval != SQLITE_OK) {
- fprintf(stderr, "Database query failed '%s'.\n", url);
- return 1;
- }
-
- return 0;
-}
-
-int parse_dm5_buffer(sqlite3 *handle, const char *url, const char *buffer, int size,
- struct dive_table *table)
-{
- (void) buffer;
- (void) size;
-
- int retval;
- char *err = NULL;
- target_table = table;
-
- /* StartTime is converted from Suunto's nano seconds to standard
- * time. We also need epoch, not seconds since year 1. */
- char get_dives[] = "select DiveId,StartTime/10000000-62135596800,Note,Duration,coalesce(SourceSerialNumber,SerialNumber),Source,MaxDepth,SampleInterval,StartTemperature,BottomTemperature,StartPressure,EndPressure,'','',SurfacePressure,DiveTime,SampleInterval,ProfileBlob,TemperatureBlob,PressureBlob,'','','','',SampleBlob FROM Dive where Deleted is null";
-
- retval = sqlite3_exec(handle, get_dives, &dm5_dive, handle, &err);
-
- if (retval != SQLITE_OK) {
- fprintf(stderr, "Database query failed '%s'.\n", url);
- return 1;
- }
-
- return 0;
-}
-
-extern int shearwater_cylinders(void *handle, int columns, char **data, char **column)
-{
- (void) handle;
- (void) columns;
- (void) column;
-
- cylinder_start();
- if (data[0])
- cur_dive->cylinder[cur_cylinder_index].gasmix.o2.permille = atof(data[0]) * 1000;
- if (data[1])
- cur_dive->cylinder[cur_cylinder_index].gasmix.he.permille = atof(data[1]) * 1000;
- cylinder_end();
-
- return 0;
-}
-
-extern int shearwater_changes(void *handle, int columns, char **data, char **column)
-{
- (void) handle;
- (void) columns;
- (void) column;
-
- event_start();
- if (data[0])
- cur_event.time.seconds = atoi(data[0]);
- if (data[1]) {
- strcpy(cur_event.name, "gaschange");
- cur_event.value = atof(data[1]) * 100;
- }
- event_end();
-
- return 0;
-}
-
-
-extern int cobalt_profile_sample(void *handle, int columns, char **data, char **column)
-{
- (void) handle;
- (void) columns;
- (void) column;
-
- sample_start();
- if (data[0])
- cur_sample->time.seconds = atoi(data[0]);
- if (data[1])
- cur_sample->depth.mm = atoi(data[1]);
- if (data[2])
- cur_sample->temperature.mkelvin = metric ? C_to_mkelvin(atof(data[2])) : F_to_mkelvin(atof(data[2]));
- sample_end();
-
- return 0;
-}
-
-
-extern int shearwater_profile_sample(void *handle, int columns, char **data, char **column)
-{
- (void) handle;
- (void) columns;
- (void) column;
-
- sample_start();
- if (data[0])
- cur_sample->time.seconds = atoi(data[0]);
- if (data[1])
- cur_sample->depth.mm = metric ? atof(data[1]) * 1000 : feet_to_mm(atof(data[1]));
- if (data[2])
- cur_sample->temperature.mkelvin = metric ? C_to_mkelvin(atof(data[2])) : F_to_mkelvin(atof(data[2]));
- if (data[3]) {
- cur_sample->setpoint.mbar = atof(data[3]) * 1000;
- cur_dive->dc.divemode = CCR;
- }
- if (data[4])
- cur_sample->ndl.seconds = atoi(data[4]) * 60;
- if (data[5])
- cur_sample->cns = atoi(data[5]);
- if (data[6])
- cur_sample->stopdepth.mm = metric ? atoi(data[6]) * 1000 : feet_to_mm(atoi(data[6]));
-
- /* We don't actually have data[3], but it should appear in the
- * SQL query at some point.
- if (data[3])
- cur_sample->cylinderpressure.mbar = metric ? atoi(data[3]) * 1000 : psi_to_mbar(atoi(data[3]));
- */
- sample_end();
-
- return 0;
-}
-
-extern int shearwater_dive(void *param, int columns, char **data, char **column)
-{
- (void) columns;
- (void) column;
-
- int retval = 0;
- sqlite3 *handle = (sqlite3 *)param;
- char *err = NULL;
- char get_profile_template[] = "select currentTime,currentDepth,waterTemp,averagePPO2,currentNdl,CNSPercent,decoCeiling from dive_log_records where diveLogId = %d";
- char get_cylinder_template[] = "select fractionO2,fractionHe from dive_log_records where diveLogId = %d group by fractionO2,fractionHe";
- char get_changes_template[] = "select a.currentTime,a.fractionO2,a.fractionHe from dive_log_records as a,dive_log_records as b where a.diveLogId = %d and b.diveLogId = %d and (a.id - 1) = b.id and (a.fractionO2 != b.fractionO2 or a.fractionHe != b.fractionHe) union select min(currentTime),fractionO2,fractionHe from dive_log_records";
- char get_buffer[1024];
-
- dive_start();
- cur_dive->number = atoi(data[0]);
-
- cur_dive->when = (time_t)(atol(data[1]));
-
- if (data[2])
- add_dive_site(data[2], cur_dive);
- if (data[3])
- utf8_string(data[3], &cur_dive->buddy);
- if (data[4])
- utf8_string(data[4], &cur_dive->notes);
-
- metric = atoi(data[5]) == 1 ? 0 : 1;
-
- /* TODO: verify that metric calculation is correct */
- if (data[6])
- cur_dive->dc.maxdepth.mm = metric ? atof(data[6]) * 1000 : feet_to_mm(atof(data[6]));
-
- if (data[7])
- cur_dive->dc.duration.seconds = atoi(data[7]) * 60;
-
- if (data[8])
- cur_dive->dc.surface_pressure.mbar = atoi(data[8]);
- /*
- * TODO: the deviceid hash should be calculated here.
- */
- settings_start();
- dc_settings_start();
- if (data[9])
- utf8_string(data[9], &cur_settings.dc.serial_nr);
- if (data[10])
- utf8_string(data[10], &cur_settings.dc.model);
-
- cur_settings.dc.deviceid = 0xffffffff;
- dc_settings_end();
- settings_end();
-
- snprintf(get_buffer, sizeof(get_buffer) - 1, get_cylinder_template, cur_dive->number);
- retval = sqlite3_exec(handle, get_buffer, &shearwater_cylinders, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query shearwater_cylinders failed.\n");
- return 1;
- }
-
- snprintf(get_buffer, sizeof(get_buffer) - 1, get_changes_template, cur_dive->number, cur_dive->number);
- retval = sqlite3_exec(handle, get_buffer, &shearwater_changes, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query shearwater_changes failed.\n");
- return 1;
- }
-
- snprintf(get_buffer, sizeof(get_buffer) - 1, get_profile_template, cur_dive->number);
- retval = sqlite3_exec(handle, get_buffer, &shearwater_profile_sample, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query shearwater_profile_sample failed.\n");
- return 1;
- }
-
- dive_end();
-
- return SQLITE_OK;
-}
-
-extern int cobalt_cylinders(void *handle, int columns, char **data, char **column)
-{
- (void) handle;
- (void) columns;
- (void) column;
-
- cylinder_start();
- if (data[0])
- cur_dive->cylinder[cur_cylinder_index].gasmix.o2.permille = atoi(data[0]) * 10;
- if (data[1])
- cur_dive->cylinder[cur_cylinder_index].gasmix.he.permille = atoi(data[1]) * 10;
- if (data[2])
- cur_dive->cylinder[cur_cylinder_index].start.mbar = psi_to_mbar(atoi(data[2]));
- if (data[3])
- cur_dive->cylinder[cur_cylinder_index].end.mbar = psi_to_mbar(atoi(data[3]));
- if (data[4])
- cur_dive->cylinder[cur_cylinder_index].type.size.mliter = atoi(data[4]) * 100;
- if (data[5])
- cur_dive->cylinder[cur_cylinder_index].gas_used.mliter = atoi(data[5]) * 1000;
- cylinder_end();
-
- return 0;
-}
-
-extern int cobalt_buddies(void *handle, int columns, char **data, char **column)
-{
- (void) handle;
- (void) columns;
- (void) column;
-
- if (data[0])
- utf8_string(data[0], &cur_dive->buddy);
-
- return 0;
-}
-
-/*
- * We still need to figure out how to map free text visibility to
- * Subsurface star rating.
- */
-
-extern int cobalt_visibility(void *handle, int columns, char **data, char **column)
-{
- (void) handle;
- (void) columns;
- (void) column;
- (void) data;
- return 0;
-}
-
-extern int cobalt_location(void *handle, int columns, char **data, char **column)
-{
- (void) handle;
- (void) columns;
- (void) column;
-
- static char *location = NULL;
- if (data[0]) {
- if (location) {
- char *tmp = malloc(strlen(location) + strlen(data[0]) + 4);
- if (!tmp)
- return -1;
- sprintf(tmp, "%s / %s", location, data[0]);
- free(location);
- location = NULL;
- cur_dive->dive_site_uuid = find_or_create_dive_site_with_name(tmp, cur_dive->when);
- free(tmp);
- } else {
- location = strdup(data[0]);
- }
- }
- return 0;
-}
-
-
-extern int cobalt_dive(void *param, int columns, char **data, char **column)
-{
- (void) columns;
- (void) column;
-
- int retval = 0;
- sqlite3 *handle = (sqlite3 *)param;
- char *err = NULL;
- char get_profile_template[] = "select runtime*60,(DepthPressure*10000/SurfacePressure)-10000,p.Temperature from Dive AS d JOIN TrackPoints AS p ON d.Id=p.DiveId where d.Id=%d";
- char get_cylinder_template[] = "select FO2,FHe,StartingPressure,EndingPressure,TankSize,TankPressure,TotalConsumption from GasMixes where DiveID=%d and StartingPressure>0 group by FO2,FHe";
- char get_buddy_template[] = "select l.Data from Items AS i, List AS l ON i.Value1=l.Id where i.DiveId=%d and l.Type=4";
- char get_visibility_template[] = "select l.Data from Items AS i, List AS l ON i.Value1=l.Id where i.DiveId=%d and l.Type=3";
- char get_location_template[] = "select l.Data from Items AS i, List AS l ON i.Value1=l.Id where i.DiveId=%d and l.Type=0";
- char get_site_template[] = "select l.Data from Items AS i, List AS l ON i.Value1=l.Id where i.DiveId=%d and l.Type=1";
- char get_buffer[1024];
-
- dive_start();
- cur_dive->number = atoi(data[0]);
-
- cur_dive->when = (time_t)(atol(data[1]));
-
- if (data[4])
- utf8_string(data[4], &cur_dive->notes);
-
- /* data[5] should have information on Units used, but I cannot
- * parse it at all based on the sample log I have received. The
- * temperatures in the samples are all Imperial, so let's go by
- * that.
- */
-
- metric = 0;
-
- /* Cobalt stores the pressures, not the depth */
- if (data[6])
- cur_dive->dc.maxdepth.mm = atoi(data[6]);
-
- if (data[7])
- cur_dive->dc.duration.seconds = atoi(data[7]);
-
- if (data[8])
- cur_dive->dc.surface_pressure.mbar = atoi(data[8]);
- /*
- * TODO: the deviceid hash should be calculated here.
- */
- settings_start();
- dc_settings_start();
- if (data[9]) {
- utf8_string(data[9], &cur_settings.dc.serial_nr);
- cur_settings.dc.deviceid = atoi(data[9]);
- cur_settings.dc.model = strdup("Cobalt import");
- }
-
- dc_settings_end();
- settings_end();
-
- if (data[9]) {
- cur_dive->dc.deviceid = atoi(data[9]);
- cur_dive->dc.model = strdup("Cobalt import");
- }
-
- snprintf(get_buffer, sizeof(get_buffer) - 1, get_cylinder_template, cur_dive->number);
- retval = sqlite3_exec(handle, get_buffer, &cobalt_cylinders, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query cobalt_cylinders failed.\n");
- return 1;
- }
-
- snprintf(get_buffer, sizeof(get_buffer) - 1, get_buddy_template, cur_dive->number);
- retval = sqlite3_exec(handle, get_buffer, &cobalt_buddies, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query cobalt_buddies failed.\n");
- return 1;
- }
-
- snprintf(get_buffer, sizeof(get_buffer) - 1, get_visibility_template, cur_dive->number);
- retval = sqlite3_exec(handle, get_buffer, &cobalt_visibility, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query cobalt_visibility failed.\n");
- return 1;
- }
-
- snprintf(get_buffer, sizeof(get_buffer) - 1, get_location_template, cur_dive->number);
- retval = sqlite3_exec(handle, get_buffer, &cobalt_location, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query cobalt_location failed.\n");
- return 1;
- }
-
- snprintf(get_buffer, sizeof(get_buffer) - 1, get_site_template, cur_dive->number);
- retval = sqlite3_exec(handle, get_buffer, &cobalt_location, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query cobalt_location (site) failed.\n");
- return 1;
- }
-
- snprintf(get_buffer, sizeof(get_buffer) - 1, get_profile_template, cur_dive->number);
- retval = sqlite3_exec(handle, get_buffer, &cobalt_profile_sample, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query cobalt_profile_sample failed.\n");
- return 1;
- }
-
- dive_end();
-
- return SQLITE_OK;
-}
-
-
-int parse_shearwater_buffer(sqlite3 *handle, const char *url, const char *buffer, int size,
- struct dive_table *table)
-{
- (void) buffer;
- (void) size;
-
- int retval;
- char *err = NULL;
- target_table = table;
-
- char get_dives[] = "select i.diveId,timestamp,location||' / '||site,buddy,notes,imperialUnits,maxDepth,maxTime,startSurfacePressure,computerSerial,computerModel FROM dive_info AS i JOIN dive_logs AS l ON i.diveId=l.diveId";
-
- retval = sqlite3_exec(handle, get_dives, &shearwater_dive, handle, &err);
-
- if (retval != SQLITE_OK) {
- fprintf(stderr, "Database query failed '%s'.\n", url);
- return 1;
- }
-
- return 0;
-}
-
-int parse_cobalt_buffer(sqlite3 *handle, const char *url, const char *buffer, int size,
- struct dive_table *table)
-{
- (void) buffer;
- (void) size;
-
- int retval;
- char *err = NULL;
- target_table = table;
-
- char get_dives[] = "select Id,strftime('%s',DiveStartTime),LocationId,'buddy','notes',Units,(MaxDepthPressure*10000/SurfacePressure)-10000,DiveMinutes,SurfacePressure,SerialNumber,'model' from Dive where IsViewDeleted = 0";
-
- retval = sqlite3_exec(handle, get_dives, &cobalt_dive, handle, &err);
-
- if (retval != SQLITE_OK) {
- fprintf(stderr, "Database query failed '%s'.\n", url);
- return 1;
- }
-
- return 0;
-}
-
-extern int divinglog_cylinder(void *handle, int columns, char **data, char **column)
-{
- (void) handle;
- (void) columns;
- (void) column;
-
- short dbl = 1;
- //char get_cylinder_template[] = "select TankID,TankSize,PresS,PresE,PresW,O2,He,DblTank from Tank where LogID = %d";
-
- /*
- * Divinglog might have more cylinders than what we support. So
- * better to ignore those.
- */
-
- if (cur_cylinder_index >= MAX_CYLINDERS)
- return 0;
-
- if (data[7] && atoi(data[7]) > 0)
- dbl = 2;
-
- cylinder_start();
-
- /*
- * Assuming that we have to double the cylinder size, if double
- * is set
- */
-
- if (data[1] && atoi(data[1]) > 0)
- cur_dive->cylinder[cur_cylinder_index].type.size.mliter = atol(data[1]) * 1000 * dbl;
-
- if (data[2] && atoi(data[2]) > 0)
- cur_dive->cylinder[cur_cylinder_index].start.mbar = atol(data[2]) * 1000;
- if (data[3] && atoi(data[3]) > 0)
- cur_dive->cylinder[cur_cylinder_index].end.mbar = atol(data[3]) * 1000;
- if (data[4] && atoi(data[4]) > 0)
- cur_dive->cylinder[cur_cylinder_index].type.workingpressure.mbar = atol(data[4]) * 1000;
- if (data[5] && atoi(data[5]) > 0)
- cur_dive->cylinder[cur_cylinder_index].gasmix.o2.permille = atol(data[5]) * 10;
- if (data[6] && atoi(data[6]) > 0)
- cur_dive->cylinder[cur_cylinder_index].gasmix.he.permille = atol(data[6]) * 10;
-
- cylinder_end();
-
- return 0;
-}
-
-extern int divinglog_profile(void *handle, int columns, char **data, char **column)
-{
- (void) handle;
- (void) columns;
- (void) column;
-
- int sinterval = 0;
- unsigned long i, len, lenprofile2 = 0;
- char *ptr, temp[4], pres[5], hbeat[4], stop[4], stime[4], ndl[4], ppo2_1[4], ppo2_2[4], ppo2_3[4], cns[5], setpoint[3];
- short oldcyl = -1;
-
- /* We do not have samples */
- if (!data[1])
- return 0;
-
- if (data[0])
- sinterval = atoi(data[0]);
-
- /*
- * Profile
- *
- * DDDDDCRASWEE
- * D: Depth (in meter with two decimals)
- * C: Deco (1 = yes, 0 = no)
- * R: RBT (Remaining Bottom Time warning)
- * A: Ascent warning
- * S: Decostop ignored
- * W: Work warning
- * E: Extra info (different for every computer)
- *
- * Example: 004500010000
- * 4.5 m, no deco, no RBT warning, ascanding too fast, no decostop ignored, no work, no extra info
- *
- *
- * Profile2
- *
- * TTTFFFFIRRR
- *
- * T: Temperature (in °C with one decimal)
- * F: Tank pressure 1 (in bar with one decimal)
- * I: Tank ID (0, 1, 2 ... 9)
- * R: RBT (in min)
- *
- * Example: 25518051099
- * 25.5 °C, 180.5 bar, Tank 1, 99 min RBT
- *
- */
-
- len = strlen(data[1]);
-
- if (data[2])
- lenprofile2 = strlen(data[2]);
-
- for (i = 0, ptr = data[1]; i * 12 < len; ++i) {
- sample_start();
-
- cur_sample->time.seconds = sinterval * i;
- cur_sample->in_deco = ptr[5] - '0' ? true : false;
- ptr[5] = 0;
- cur_sample->depth.mm = atoi(ptr) * 10;
-
- if (i * 11 < lenprofile2) {
- memcpy(temp, &data[2][i * 11], 3);
- cur_sample->temperature.mkelvin = C_to_mkelvin(atoi(temp) / 10);
- }
-
- if (data[2]) {
- memcpy(pres, &data[2][i * 11 + 3], 4);
- cur_sample->cylinderpressure.mbar = atoi(pres) * 100;
- }
-
- if (data[3] && strlen(data[3])) {
- memcpy(hbeat, &data[3][i * 14 + 8], 3);
- cur_sample->heartbeat = atoi(hbeat);
- }
-
- if (data[4] && strlen(data[4])) {
- memcpy(stop, &data[4][i * 9 + 6], 3);
- cur_sample->stopdepth.mm = atoi(stop) * 1000;
-
- memcpy(stime, &data[4][i * 9 + 3], 3);
- cur_sample->stoptime.seconds = atoi(stime) * 60;
-
- /*
- * Following value is NDL when not in deco, and
- * either 0 or TTS when in deco.
- */
-
- memcpy(ndl, &data[4][i * 9 + 0], 3);
- if (cur_sample->in_deco == false)
- cur_sample->ndl.seconds = atoi(ndl) * 60;
- else if (atoi(ndl))
- cur_sample->tts.seconds = atoi(ndl) * 60;
-
- if (cur_sample->in_deco == true)
- cur_sample->ndl.seconds = 0;
- }
-
- /*
- * AAABBBCCCOOOONNNNSS
- *
- * A = ppO2 cell 1 (measured)
- * B = ppO2 cell 2 (measured)
- * C = ppO2 cell 3 (measured)
- * O = OTU
- * N = CNS
- * S = Setpoint
- *
- * Example: 1121131141548026411
- * 1.12 bar, 1.13 bar, 1.14 bar, OTU = 154.8, CNS = 26.4, Setpoint = 1.1
- */
-
- if (data[5] && strlen(data[5])) {
- memcpy(ppo2_1, &data[5][i * 19 + 0], 3);
- memcpy(ppo2_2, &data[5][i * 19 + 3], 3);
- memcpy(ppo2_3, &data[5][i * 19 + 6], 3);
- memcpy(cns, &data[5][i * 19 + 13], 4);
- memcpy(setpoint, &data[5][i * 19 + 17], 2);
-
- if (atoi(ppo2_1) > 0)
- cur_sample->o2sensor[0].mbar = atoi(ppo2_1) * 100;
- if (atoi(ppo2_2) > 0)
- cur_sample->o2sensor[1].mbar = atoi(ppo2_2) * 100;
- if (atoi(ppo2_3) > 0)
- cur_sample->o2sensor[2].mbar = atoi(ppo2_3) * 100;
- if (atoi(cns) > 0)
- cur_sample->cns = rint(atoi(cns) / 10);
- if (atoi(setpoint) > 0)
- cur_sample->setpoint.mbar = atoi(setpoint) * 100;
-
- }
-
- /*
- * My best guess is that if we have o2sensors, then it
- * is either CCR or PSCR dive. And the first time we
- * have O2 sensor readings, we can count them to get
- * the amount O2 sensors.
- */
-
- if (!cur_dive->dc.no_o2sensors) {
- cur_dive->dc.no_o2sensors = cur_sample->o2sensor[0].mbar ? 1 : 0 +
- cur_sample->o2sensor[1].mbar ? 1 : 0 +
- cur_sample->o2sensor[2].mbar ? 1 : 0;
- cur_dive->dc.divemode = CCR;
- }
-
- ptr += 12;
- sample_end();
- }
-
- for (i = 0, ptr = data[1]; i * 12 < len; ++i) {
- /* Remaining bottom time warning */
- if (ptr[6] - '0') {
- event_start();
- cur_event.time.seconds = sinterval * i;
- strcpy(cur_event.name, "rbt");
- event_end();
- }
-
- /* Ascent warning */
- if (ptr[7] - '0') {
- event_start();
- cur_event.time.seconds = sinterval * i;
- strcpy(cur_event.name, "ascent");
- event_end();
- }
-
- /* Deco stop ignored */
- if (ptr[8] - '0') {
- event_start();
- cur_event.time.seconds = sinterval * i;
- strcpy(cur_event.name, "violation");
- event_end();
- }
-
- /* Workload warning */
- if (ptr[9] - '0') {
- event_start();
- cur_event.time.seconds = sinterval * i;
- strcpy(cur_event.name, "workload");
- event_end();
- }
- ptr += 12;
- }
-
- for (i = 0; i * 11 < lenprofile2; ++i) {
- short tank = data[2][i * 11 + 7] - '0';
- if (oldcyl != tank) {
- struct gasmix *mix = &cur_dive->cylinder[tank].gasmix;
- int o2 = get_o2(mix);
- int he = get_he(mix);
-
- event_start();
- cur_event.time.seconds = sinterval * i;
- strcpy(cur_event.name, "gaschange");
-
- o2 = (o2 + 5) / 10;
- he = (he + 5) / 10;
- cur_event.value = o2 + (he << 16);
-
- event_end();
- oldcyl = tank;
- }
- }
-
- return 0;
-}
-
-
-extern int divinglog_dive(void *param, int columns, char **data, char **column)
-{
- (void) columns;
- (void) column;
-
- int retval = 0;
- sqlite3 *handle = (sqlite3 *)param;
- char *err = NULL;
- char get_profile_template[] = "select ProfileInt,Profile,Profile2,Profile3,Profile4,Profile5 from Logbook where ID = %d";
- char get_cylinder0_template[] = "select 0,TankSize,PresS,PresE,PresW,O2,He,DblTank from Logbook where ID = %d";
- char get_cylinder_template[] = "select TankID,TankSize,PresS,PresE,PresW,O2,He,DblTank from Tank where LogID = %d order by TankID";
- char get_buffer[1024];
-
- dive_start();
- diveid = atoi(data[13]);
- cur_dive->number = atoi(data[0]);
-
- cur_dive->when = (time_t)(atol(data[1]));
-
- if (data[2])
- cur_dive->dive_site_uuid = find_or_create_dive_site_with_name(data[2], cur_dive->when);
-
- if (data[3])
- utf8_string(data[3], &cur_dive->buddy);
-
- if (data[4])
- utf8_string(data[4], &cur_dive->notes);
-
- if (data[5])
- cur_dive->dc.maxdepth.mm = atof(data[5]) * 1000;
-
- if (data[6])
- cur_dive->dc.duration.seconds = atoi(data[6]) * 60;
-
- if (data[7])
- utf8_string(data[7], &cur_dive->divemaster);
-
- if (data[8])
- cur_dive->airtemp.mkelvin = C_to_mkelvin(atol(data[8]));
-
- if (data[9])
- cur_dive->watertemp.mkelvin = C_to_mkelvin(atol(data[9]));
-
- if (data[10]) {
- cur_dive->weightsystem[0].weight.grams = atol(data[10]) * 1000;
- cur_dive->weightsystem[0].description = strdup(translate("gettextFromC", "unknown"));
- }
-
- if (data[11])
- cur_dive->suit = strdup(data[11]);
-
- settings_start();
- dc_settings_start();
-
- if (data[12]) {
- cur_dive->dc.model = strdup(data[12]);
- } else {
- cur_settings.dc.model = strdup("Divinglog import");
- }
-
- snprintf(get_buffer, sizeof(get_buffer) - 1, get_cylinder0_template, diveid);
- retval = sqlite3_exec(handle, get_buffer, &divinglog_cylinder, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query divinglog_cylinder0 failed.\n");
- return 1;
- }
-
- snprintf(get_buffer, sizeof(get_buffer) - 1, get_cylinder_template, diveid);
- retval = sqlite3_exec(handle, get_buffer, &divinglog_cylinder, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query divinglog_cylinder failed.\n");
- return 1;
- }
-
-
- dc_settings_end();
- settings_end();
-
- if (data[12]) {
- cur_dive->dc.model = strdup(data[12]);
- } else {
- cur_dive->dc.model = strdup("Divinglog import");
- }
-
- snprintf(get_buffer, sizeof(get_buffer) - 1, get_profile_template, diveid);
- retval = sqlite3_exec(handle, get_buffer, &divinglog_profile, 0, &err);
- if (retval != SQLITE_OK) {
- fprintf(stderr, "%s", "Database query divinglog_profile failed.\n");
- return 1;
- }
-
- dive_end();
-
- return SQLITE_OK;
-}
-
-
-int parse_divinglog_buffer(sqlite3 *handle, const char *url, const char *buffer, int size,
- struct dive_table *table)
-{
- (void) buffer;
- (void) size;
-
- int retval;
- char *err = NULL;
- target_table = table;
-
- char get_dives[] = "select Number,strftime('%s',Divedate || ' ' || ifnull(Entrytime,'00:00')),Country || ' - ' || City || ' - ' || Place,Buddy,Comments,Depth,Divetime,Divemaster,Airtemp,Watertemp,Weight,Divesuit,Computer,ID from Logbook where UUID not in (select UUID from DeletedRecords)";
-
- retval = sqlite3_exec(handle, get_dives, &divinglog_dive, handle, &err);
-
- if (retval != SQLITE_OK) {
- fprintf(stderr, "Database query failed '%s'.\n", url);
- return 1;
- }
-
- return 0;
-}
-
-/*
- * Parse a unsigned 32-bit integer in little-endian mode,
- * that is seconds since Jan 1, 2000.
- */
-static timestamp_t parse_dlf_timestamp(unsigned char *buffer)
-{
- timestamp_t offset;
-
- offset = buffer[3];
- offset = (offset << 8) + buffer[2];
- offset = (offset << 8) + buffer[1];
- offset = (offset << 8) + buffer[0];
-
- // Jan 1, 2000 is 946684800 seconds after Jan 1, 1970, which is
- // the Unix epoch date that "timestamp_t" uses.
- return offset + 946684800;
-}
-
-int parse_dlf_buffer(unsigned char *buffer, size_t size)
-{
- unsigned char *ptr = buffer;
- unsigned char event;
- bool found;
- unsigned int time = 0;
- int i;
- char serial[6];
-
- target_table = &dive_table;
-
- // Check for the correct file magic
- if (ptr[0] != 'D' || ptr[1] != 'i' || ptr[2] != 'v' || ptr[3] != 'E')
- return -1;
-
- dive_start();
- divecomputer_start();
-
- cur_dc->model = strdup("DLF import");
- // (ptr[7] << 8) + ptr[6] Is "Serial"
- snprintf(serial, sizeof(serial), "%d", (ptr[7] << 8) + ptr[6]);
- cur_dc->serial = strdup(serial);
- cur_dc->when = parse_dlf_timestamp(ptr + 8);
- cur_dive->when = cur_dc->when;
-
- cur_dc->duration.seconds = ((ptr[14] & 0xFE) << 16) + (ptr[13] << 8) + ptr[12];
-
- // ptr[14] >> 1 is scrubber used in %
-
- // 3 bit dive type
- switch((ptr[15] & 0x30) >> 3) {
- case 0: // unknown
- case 1:
- cur_dc->divemode = OC;
- break;
- case 2:
- cur_dc->divemode = CCR;
- break;
- case 3:
- cur_dc->divemode = CCR; // mCCR
- break;
- case 4:
- cur_dc->divemode = FREEDIVE;
- break;
- case 5:
- cur_dc->divemode = OC; // Gauge
- break;
- case 6:
- cur_dc->divemode = PSCR; // ASCR
- break;
- case 7:
- cur_dc->divemode = PSCR;
- break;
- }
-
- cur_dc->maxdepth.mm = ((ptr[21] << 8) + ptr[20]) * 10;
- cur_dc->surface_pressure.mbar = ((ptr[25] << 8) + ptr[24]) / 10;
-
- /* Done with parsing what we know about the dive header */
- ptr += 32;
-
- // We're going to interpret ppO2 saved as a sensor value in these modes.
- if (cur_dc->divemode == CCR || cur_dc->divemode == PSCR)
- cur_dc->no_o2sensors = 1;
-
- while (ptr < buffer + size) {
- time = ((ptr[0] >> 4) & 0x0f) +
- ((ptr[1] << 4) & 0xff0) +
- (ptr[2] & 0x0f) * 3600; /* hours */
- event = ptr[0] & 0x0f;
- switch (event) {
- case 0:
- /* Regular sample */
- sample_start();
- cur_sample->time.seconds = time;
- cur_sample->depth.mm = ((ptr[5] << 8) + ptr[4]) * 10;
- // Crazy precision on these stored values...
- // Only store value if we're in CCR/PSCR mode,
- // because we rather calculate ppo2 our selfs.
- if (cur_dc->divemode == CCR || cur_dc->divemode == PSCR)
- cur_sample->o2sensor[0].mbar = ((ptr[7] << 8) + ptr[6]) / 10;
- // NDL in minutes, 10 bit
- cur_sample->ndl.seconds = (((ptr[9] & 0x03) << 8) + ptr[8]) * 60;
- // TTS in minutes, 10 bit
- cur_sample->tts.seconds = (((ptr[10] & 0x0F) << 6) + (ptr[9] >> 2)) * 60;
- // Temperature in 1/10 C, 10 bit signed
- cur_sample->temperature.mkelvin = ((ptr[11] & 0x20) ? -1 : 1) * (((ptr[11] & 0x1F) << 4) + (ptr[10] >> 4)) * 100 + ZERO_C_IN_MKELVIN;
- // ptr[11] & 0xF0 is unknown, and always 0xC in all checked files
- cur_sample->stopdepth.mm = ((ptr[13] << 8) + ptr[12]) * 10;
- if (cur_sample->stopdepth.mm)
- cur_sample->in_deco = true;
- //ptr[14] is helium content, always zero?
- //ptr[15] is setpoint, always zero?
- sample_end();
- break;
- case 1: /* dive event */
- case 2: /* automatic parameter change */
- case 3: /* diver error */
- case 4: /* internal error */
- case 5: /* device activity log */
- event_start();
- cur_event.time.seconds = time;
- switch (ptr[4]) {
- case 1:
- strcpy(cur_event.name, "Setpoint Manual");
- // There is a setpoint value somewhere...
- break;
- case 2:
- strcpy(cur_event.name, "Setpoint Auto");
- // There is a setpoint value somewhere...
- switch (ptr[7]) {
- case 0:
- strcat(cur_event.name, " Manual");
- break;
- case 1:
- strcat(cur_event.name, " Auto Start");
- break;
- case 2:
- strcat(cur_event.name, " Auto Hypox");
- break;
- case 3:
- strcat(cur_event.name, " Auto Timeout");
- break;
- case 4:
- strcat(cur_event.name, " Auto Ascent");
- break;
- case 5:
- strcat(cur_event.name, " Auto Stall");
- break;
- case 6:
- strcat(cur_event.name, " Auto SP Low");
- break;
- default:
- break;
- }
- break;
- case 3:
- // obsolete
- strcpy(cur_event.name, "OC");
- break;
- case 4:
- // obsolete
- strcpy(cur_event.name, "CCR");
- break;
- case 5:
- strcpy(cur_event.name, "gaschange");
- cur_event.type = SAMPLE_EVENT_GASCHANGE2;
- cur_event.value = ptr[7] << 8 ^ ptr[6];
-
- found = false;
- for (i = 0; i < cur_cylinder_index; ++i) {
- if (cur_dive->cylinder[i].gasmix.o2.permille == ptr[6] * 10 && cur_dive->cylinder[i].gasmix.he.permille == ptr[7] * 10) {
- found = true;
- break;
- }
- }
- if (!found) {
- cylinder_start();
- cur_dive->cylinder[cur_cylinder_index].gasmix.o2.permille = ptr[6] * 10;
- cur_dive->cylinder[cur_cylinder_index].gasmix.he.permille = ptr[7] * 10;
- cylinder_end();
- cur_event.gas.index = cur_cylinder_index;
- } else {
- cur_event.gas.index = i;
- }
- break;
- case 6:
- strcpy(cur_event.name, "Start");
- break;
- case 7:
- strcpy(cur_event.name, "Too Fast");
- break;
- case 8:
- strcpy(cur_event.name, "Above Ceiling");
- break;
- case 9:
- strcpy(cur_event.name, "Toxic");
- break;
- case 10:
- strcpy(cur_event.name, "Hypox");
- break;
- case 11:
- strcpy(cur_event.name, "Critical");
- break;
- case 12:
- strcpy(cur_event.name, "Sensor Disabled");
- break;
- case 13:
- strcpy(cur_event.name, "Sensor Enabled");
- break;
- case 14:
- strcpy(cur_event.name, "O2 Backup");
- break;
- case 15:
- strcpy(cur_event.name, "Peer Down");
- break;
- case 16:
- strcpy(cur_event.name, "HS Down");
- break;
- case 17:
- strcpy(cur_event.name, "Inconsistent");
- break;
- case 18:
- // key pressed - probably not
- // interesting to view on profile
- break;
- case 19:
- // obsolete
- strcpy(cur_event.name, "SCR");
- break;
- case 20:
- strcpy(cur_event.name, "Above Stop");
- break;
- case 21:
- strcpy(cur_event.name, "Safety Miss");
- break;
- case 22:
- strcpy(cur_event.name, "Fatal");
- break;
- case 23:
- strcpy(cur_event.name, "Diluent");
- break;
- case 24:
- strcpy(cur_event.name, "gaschange");
- cur_event.type = SAMPLE_EVENT_GASCHANGE2;
- cur_event.value = ptr[7] << 8 ^ ptr[6];
- event_end();
- // This is both a mode change and a gas change event
- // so we encode it as two separate events.
- event_start();
- strcpy(cur_event.name, "Change Mode");
- switch (ptr[8]) {
- case 1:
- strcat(cur_event.name, ": OC");
- break;
- case 2:
- strcat(cur_event.name, ": CCR");
- break;
- case 3:
- strcat(cur_event.name, ": mCCR");
- break;
- case 4:
- strcat(cur_event.name, ": Free");
- break;
- case 5:
- strcat(cur_event.name, ": Gauge");
- break;
- case 6:
- strcat(cur_event.name, ": ASCR");
- break;
- case 7:
- strcat(cur_event.name, ": PSCR");
- break;
- default:
- break;
- }
- event_end();
- break;
- case 25:
- strcpy(cur_event.name, "CCR O2 solenoid opened/closed");
- break;
- case 26:
- strcpy(cur_event.name, "User mark");
- break;
- case 27:
- snprintf(cur_event.name, MAX_EVENT_NAME, "%sGF Switch (%d/%d)", ptr[6] ? "Bailout, ": "", ptr[7], ptr[8]);
- break;
- case 28:
- strcpy(cur_event.name, "Peer Up");
- break;
- case 29:
- strcpy(cur_event.name, "HS Up");
- break;
- case 30:
- snprintf(cur_event.name, MAX_EVENT_NAME, "CNS %d%%", ptr[6]);
- break;
- default:
- // No values above 30 had any description
- break;
- }
- event_end();
- break;
- case 6:
- /* device configuration */
- break;
- case 7:
- /* measure record */
- /* Po2 sample? Solenoid inject? */
- //fprintf(stderr, "%02X %02X%02X %02X%02X\n", ptr[5], ptr[6], ptr[7], ptr[8], ptr[9]);
- break;
- default:
- /* Unknown... */
- break;
- }
- ptr += 16;
- }
- divecomputer_end();
- dive_end();
- return 0;
-}
-
-
-void parse_xml_init(void)
-{
- LIBXML_TEST_VERSION
-}
-
-void parse_xml_exit(void)
-{
- xmlCleanupParser();
-}
-
-static struct xslt_files {
- const char *root;
- const char *file;
- const char *attribute;
-} xslt_files[] = {
- { "SUUNTO", "SuuntoSDM.xslt", NULL },
- { "Dive", "SuuntoDM4.xslt", "xmlns" },
- { "Dive", "shearwater.xslt", "version" },
- { "JDiveLog", "jdivelog2subsurface.xslt", NULL },
- { "dives", "MacDive.xslt", NULL },
- { "DIVELOGSDATA", "divelogs.xslt", NULL },
- { "uddf", "uddf.xslt", NULL },
- { "UDDF", "uddf.xslt", NULL },
- { "profile", "udcf.xslt", NULL },
- { "Divinglog", "DivingLog.xslt", NULL },
- { "csv", "csv2xml.xslt", NULL },
- { "sensuscsv", "sensuscsv.xslt", NULL },
- { "SubsurfaceCSV", "subsurfacecsv.xslt", NULL },
- { "manualcsv", "manualcsv2xml.xslt", NULL },
- { "logbook", "DiveLog.xslt", NULL },
- { NULL, }
- };
-
-static xmlDoc *test_xslt_transforms(xmlDoc *doc, const char **params)
-{
- struct xslt_files *info = xslt_files;
- xmlDoc *transformed;
- xsltStylesheetPtr xslt = NULL;
- xmlNode *root_element = xmlDocGetRootElement(doc);
- char *attribute;
-
- while (info->root) {
- if ((strcasecmp((const char *)root_element->name, info->root) == 0)) {
- if (info->attribute == NULL)
- break;
- else if (xmlGetProp(root_element, (const xmlChar *)info->attribute) != NULL)
- break;
- }
- info++;
- }
-
- if (info->root) {
- attribute = (char *)xmlGetProp(xmlFirstElementChild(root_element), (const xmlChar *)"name");
- if (attribute) {
- if (strcasecmp(attribute, "subsurface") == 0) {
- free((void *)attribute);
- return doc;
- }
- free((void *)attribute);
- }
- xmlSubstituteEntitiesDefault(1);
- xslt = get_stylesheet(info->file);
- if (xslt == NULL) {
- report_error(translate("gettextFromC", "Can't open stylesheet %s"), info->file);
- return doc;
- }
- transformed = xsltApplyStylesheet(xslt, doc, params);
- xmlFreeDoc(doc);
- xsltFreeStylesheet(xslt);
-
- return transformed;
- }
- return doc;
-}
diff --git a/subsurface-core/planner.c b/subsurface-core/planner.c
deleted file mode 100644
index 705aad1cb..000000000
--- a/subsurface-core/planner.c
+++ /dev/null
@@ -1,1471 +0,0 @@
-/* planner.c
- *
- * code that allows us to plan future dives
- *
- * (c) Dirk Hohndel 2013
- */
-#include <assert.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <string.h>
-#include "dive.h"
-#include "deco.h"
-#include "divelist.h"
-#include "planner.h"
-#include "gettext.h"
-#include "libdivecomputer/parser.h"
-
-#define TIMESTEP 2 /* second */
-#define DECOTIMESTEP 60 /* seconds. Unit of deco stop times */
-
-int decostoplevels_metric[] = { 0, 3000, 6000, 9000, 12000, 15000, 18000, 21000, 24000, 27000,
- 30000, 33000, 36000, 39000, 42000, 45000, 48000, 51000, 54000, 57000,
- 60000, 63000, 66000, 69000, 72000, 75000, 78000, 81000, 84000, 87000,
- 90000, 100000, 110000, 120000, 130000, 140000, 150000, 160000, 170000,
- 180000, 190000, 200000, 220000, 240000, 260000, 280000, 300000,
- 320000, 340000, 360000, 380000 };
-int decostoplevels_imperial[] = { 0, 3048, 6096, 9144, 12192, 15240, 18288, 21336, 24384, 27432,
- 30480, 33528, 36576, 39624, 42672, 45720, 48768, 51816, 54864, 57912,
- 60960, 64008, 67056, 70104, 73152, 76200, 79248, 82296, 85344, 88392,
- 91440, 101600, 111760, 121920, 132080, 142240, 152400, 162560, 172720,
- 182880, 193040, 203200, 223520, 243840, 264160, 284480, 304800,
- 325120, 345440, 365760, 386080 };
-
-double plangflow, plangfhigh;
-bool plan_verbatim, plan_display_runtime, plan_display_duration, plan_display_transitions;
-
-pressure_t first_ceiling_pressure, max_bottom_ceiling_pressure = {};
-
-const char *disclaimer;
-
-#if DEBUG_PLAN
-void dump_plan(struct diveplan *diveplan)
-{
- struct divedatapoint *dp;
- struct tm tm;
-
- if (!diveplan) {
- printf("Diveplan NULL\n");
- return;
- }
- utc_mkdate(diveplan->when, &tm);
-
- printf("\nDiveplan @ %04d-%02d-%02d %02d:%02d:%02d (surfpres %dmbar):\n",
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec,
- diveplan->surface_pressure);
- dp = diveplan->dp;
- while (dp) {
- printf("\t%3u:%02u: %dmm gas: %d o2 %d h2\n", FRACTION(dp->time, 60), dp->depth, get_o2(&dp->gasmix), get_he(&dp->gasmix));
- dp = dp->next;
- }
-}
-#endif
-
-bool diveplan_empty(struct diveplan *diveplan)
-{
- struct divedatapoint *dp;
- if (!diveplan || !diveplan->dp)
- return true;
- dp = diveplan->dp;
- while (dp) {
- if (dp->time)
- return false;
- dp = dp->next;
- }
- return true;
-}
-
-/* get the gas at a certain time during the dive */
-void get_gas_at_time(struct dive *dive, struct divecomputer *dc, duration_t time, struct gasmix *gas)
-{
- // we always start with the first gas, so that's our gas
- // unless an event tells us otherwise
- struct event *event = dc->events;
- *gas = dive->cylinder[0].gasmix;
- while (event && event->time.seconds <= time.seconds) {
- if (!strcmp(event->name, "gaschange")) {
- int cylinder_idx = get_cylinder_index(dive, event);
- *gas = dive->cylinder[cylinder_idx].gasmix;
- }
- event = event->next;
- }
-}
-
-int get_gasidx(struct dive *dive, struct gasmix *mix)
-{
- int gasidx = -1;
-
- while (++gasidx < MAX_CYLINDERS)
- if (gasmix_distance(&dive->cylinder[gasidx].gasmix, mix) < 100)
- return gasidx;
- return -1;
-}
-
-void interpolate_transition(struct dive *dive, duration_t t0, duration_t t1, depth_t d0, depth_t d1, const struct gasmix *gasmix, o2pressure_t po2)
-{
- uint32_t j;
-
- for (j = t0.seconds; j < t1.seconds; j++) {
- int depth = interpolate(d0.mm, d1.mm, j - t0.seconds, t1.seconds - t0.seconds);
- add_segment(depth_to_bar(depth, dive), gasmix, 1, po2.mbar, dive, prefs.bottomsac);
- }
- if (d1.mm > d0.mm)
- calc_crushing_pressure(depth_to_bar(d1.mm, &displayed_dive));
-}
-
-/* returns the tissue tolerance at the end of this (partial) dive */
-void tissue_at_end(struct dive *dive, char **cached_datap)
-{
- struct divecomputer *dc;
- struct sample *sample, *psample;
- int i;
- depth_t lastdepth = {};
- duration_t t0 = {}, t1 = {};
- struct gasmix gas;
-
- if (!dive)
- return;
- if (*cached_datap) {
- restore_deco_state(*cached_datap);
- } else {
- init_decompression(dive);
- cache_deco_state(cached_datap);
- }
- dc = &dive->dc;
- if (!dc->samples)
- return;
- psample = sample = dc->sample;
-
- for (i = 0; i < dc->samples; i++, sample++) {
- o2pressure_t setpoint;
-
- if (i)
- setpoint = sample[-1].setpoint;
- else
- setpoint = sample[0].setpoint;
-
- t1 = sample->time;
- get_gas_at_time(dive, dc, t0, &gas);
- if (i > 0)
- lastdepth = psample->depth;
-
- /* The ceiling in the deeper portion of a multilevel dive is sometimes critical for the VPM-B
- * Boyle's law compensation. We should check the ceiling prior to ascending during the bottom
- * portion of the dive. The maximum ceiling might be reached while ascending, but testing indicates
- * that it is only marginally deeper than the ceiling at the start of ascent.
- * Do not set the first_ceiling_pressure variable (used for the Boyle's law compensation calculation)
- * at this stage, because it would interfere with calculating the ceiling at the end of the bottom
- * portion of the dive.
- * Remember the value for later.
- */
- if ((prefs.deco_mode == VPMB) && (lastdepth.mm > sample->depth.mm)) {
- pressure_t ceiling_pressure;
- nuclear_regeneration(t0.seconds);
- vpmb_start_gradient();
- ceiling_pressure.mbar = depth_to_mbar(deco_allowed_depth(tissue_tolerance_calc(dive,
- depth_to_bar(lastdepth.mm, dive)),
- dive->surface_pressure.mbar / 1000.0,
- dive,
- 1),
- dive);
- if (ceiling_pressure.mbar > max_bottom_ceiling_pressure.mbar)
- max_bottom_ceiling_pressure.mbar = ceiling_pressure.mbar;
- }
-
- interpolate_transition(dive, t0, t1, lastdepth, sample->depth, &gas, setpoint);
- psample = sample;
- t0 = t1;
- }
-}
-
-
-/* if a default cylinder is set, use that */
-void fill_default_cylinder(cylinder_t *cyl)
-{
- const char *cyl_name = prefs.default_cylinder;
- struct tank_info_t *ti = tank_info;
- pressure_t pO2 = {.mbar = 1600};
-
- if (!cyl_name)
- return;
- while (ti->name != NULL) {
- if (strcmp(ti->name, cyl_name) == 0)
- break;
- ti++;
- }
- if (ti->name == NULL)
- /* didn't find it */
- return;
- cyl->type.description = strdup(ti->name);
- if (ti->ml) {
- cyl->type.size.mliter = ti->ml;
- cyl->type.workingpressure.mbar = ti->bar * 1000;
- } else {
- cyl->type.workingpressure.mbar = psi_to_mbar(ti->psi);
- if (ti->psi)
- cyl->type.size.mliter = cuft_to_l(ti->cuft) * 1000 / bar_to_atm(psi_to_bar(ti->psi));
- }
- // MOD of air
- cyl->depth = gas_mod(&cyl->gasmix, pO2, &displayed_dive, 1);
-}
-
-/* make sure that the gas we are switching to is represented in our
- * list of cylinders */
-static int verify_gas_exists(struct gasmix mix_in)
-{
- int i;
- cylinder_t *cyl;
-
- for (i = 0; i < MAX_CYLINDERS; i++) {
- cyl = displayed_dive.cylinder + i;
- if (cylinder_nodata(cyl))
- continue;
- if (gasmix_distance(&cyl->gasmix, &mix_in) < 100)
- return i;
- }
- fprintf(stderr, "this gas %s should have been on the cylinder list\nThings will fail now\n", gasname(&mix_in));
- return -1;
-}
-
-/* calculate the new end pressure of the cylinder, based on its current end pressure and the
- * latest segment. */
-static void update_cylinder_pressure(struct dive *d, int old_depth, int new_depth, int duration, int sac, cylinder_t *cyl, bool in_deco)
-{
- volume_t gas_used;
- pressure_t delta_p;
- depth_t mean_depth;
- int factor = 1000;
-
- if (d->dc.divemode == PSCR)
- factor = prefs.pscr_ratio;
-
- if (!cyl)
- return;
- mean_depth.mm = (old_depth + new_depth) / 2;
- gas_used.mliter = depth_to_atm(mean_depth.mm, d) * sac / 60 * duration * factor / 1000;
- cyl->gas_used.mliter += gas_used.mliter;
- if (in_deco)
- cyl->deco_gas_used.mliter += gas_used.mliter;
- if (cyl->type.size.mliter) {
- delta_p.mbar = gas_used.mliter * 1000.0 / cyl->type.size.mliter;
- cyl->end.mbar -= delta_p.mbar;
- }
-}
-
-/* simply overwrite the data in the displayed_dive
- * return false if something goes wrong */
-static void create_dive_from_plan(struct diveplan *diveplan, bool track_gas)
-{
- struct divedatapoint *dp;
- struct divecomputer *dc;
- struct sample *sample;
- struct gasmix oldgasmix;
- struct event *ev;
- cylinder_t *cyl;
- int oldpo2 = 0;
- int lasttime = 0;
- int lastdepth = 0;
- enum dive_comp_type type = displayed_dive.dc.divemode;
-
- if (!diveplan || !diveplan->dp)
- return;
-#if DEBUG_PLAN & 4
- printf("in create_dive_from_plan\n");
- dump_plan(diveplan);
-#endif
- displayed_dive.salinity = diveplan->salinity;
- // reset the cylinders and clear out the samples and events of the
- // displayed dive so we can restart
- reset_cylinders(&displayed_dive, track_gas);
- dc = &displayed_dive.dc;
- dc->when = displayed_dive.when = diveplan->when;
- free(dc->sample);
- dc->sample = NULL;
- dc->samples = 0;
- dc->alloc_samples = 0;
- while ((ev = dc->events)) {
- dc->events = dc->events->next;
- free(ev);
- }
- dp = diveplan->dp;
- cyl = &displayed_dive.cylinder[0];
- oldgasmix = cyl->gasmix;
- sample = prepare_sample(dc);
- sample->setpoint.mbar = dp->setpoint;
- sample->sac.mliter = prefs.bottomsac;
- oldpo2 = dp->setpoint;
- if (track_gas && cyl->type.workingpressure.mbar)
- sample->cylinderpressure.mbar = cyl->end.mbar;
- sample->manually_entered = true;
- finish_sample(dc);
- while (dp) {
- struct gasmix gasmix = dp->gasmix;
- int po2 = dp->setpoint;
- if (dp->setpoint)
- type = CCR;
- int time = dp->time;
- int depth = dp->depth;
-
- if (time == 0) {
- /* special entries that just inform the algorithm about
- * additional gases that are available */
- if (verify_gas_exists(gasmix) < 0)
- goto gas_error_exit;
- dp = dp->next;
- continue;
- }
-
- /* Check for SetPoint change */
- if (oldpo2 != po2) {
- /* this is a bad idea - we should get a different SAMPLE_EVENT type
- * reserved for this in libdivecomputer... overloading SMAPLE_EVENT_PO2
- * with a different meaning will only cause confusion elsewhere in the code */
- add_event(dc, lasttime, SAMPLE_EVENT_PO2, 0, po2, "SP change");
- oldpo2 = po2;
- }
-
- /* Make sure we have the new gas, and create a gas change event */
- if (gasmix_distance(&gasmix, &oldgasmix) > 0) {
- int idx;
- if ((idx = verify_gas_exists(gasmix)) < 0)
- goto gas_error_exit;
- /* need to insert a first sample for the new gas */
- add_gas_switch_event(&displayed_dive, dc, lasttime + 1, idx);
- cyl = &displayed_dive.cylinder[idx];
- sample = prepare_sample(dc);
- sample[-1].setpoint.mbar = po2;
- sample->time.seconds = lasttime + 1;
- sample->depth.mm = lastdepth;
- sample->manually_entered = dp->entered;
- sample->sac.mliter = dp->entered ? prefs.bottomsac : prefs.decosac;
- if (track_gas && cyl->type.workingpressure.mbar)
- sample->cylinderpressure.mbar = cyl->sample_end.mbar;
- finish_sample(dc);
- oldgasmix = gasmix;
- }
- /* Create sample */
- sample = prepare_sample(dc);
- /* set po2 at beginning of this segment */
- /* and keep it valid for last sample - where it likely doesn't matter */
- sample[-1].setpoint.mbar = po2;
- sample->setpoint.mbar = po2;
- sample->time.seconds = lasttime = time;
- sample->depth.mm = lastdepth = depth;
- sample->manually_entered = dp->entered;
- sample->sac.mliter = dp->entered ? prefs.bottomsac : prefs.decosac;
- if (track_gas && !sample[-1].setpoint.mbar) { /* Don't track gas usage for CCR legs of dive */
- update_cylinder_pressure(&displayed_dive, sample[-1].depth.mm, depth, time - sample[-1].time.seconds,
- dp->entered ? diveplan->bottomsac : diveplan->decosac, cyl, !dp->entered);
- if (cyl->type.workingpressure.mbar)
- sample->cylinderpressure.mbar = cyl->end.mbar;
- }
- finish_sample(dc);
- dp = dp->next;
- }
- dc->divemode = type;
-#if DEBUG_PLAN & 32
- save_dive(stdout, &displayed_dive);
-#endif
- return;
-
-gas_error_exit:
- report_error(translate("gettextFromC", "Too many gas mixes"));
- return;
-}
-
-void free_dps(struct diveplan *diveplan)
-{
- if (!diveplan)
- return;
- struct divedatapoint *dp = diveplan->dp;
- while (dp) {
- struct divedatapoint *ndp = dp->next;
- free(dp);
- dp = ndp;
- }
- diveplan->dp = NULL;
-}
-
-struct divedatapoint *create_dp(int time_incr, int depth, struct gasmix gasmix, int po2)
-{
- struct divedatapoint *dp;
-
- dp = malloc(sizeof(struct divedatapoint));
- dp->time = time_incr;
- dp->depth = depth;
- dp->gasmix = gasmix;
- dp->setpoint = po2;
- dp->entered = false;
- dp->next = NULL;
- return dp;
-}
-
-void add_to_end_of_diveplan(struct diveplan *diveplan, struct divedatapoint *dp)
-{
- struct divedatapoint **lastdp = &diveplan->dp;
- struct divedatapoint *ldp = *lastdp;
- int lasttime = 0;
- while (*lastdp) {
- ldp = *lastdp;
- if (ldp->time > lasttime)
- lasttime = ldp->time;
- lastdp = &(*lastdp)->next;
- }
- *lastdp = dp;
- if (ldp && dp->time != 0)
- dp->time += lasttime;
-}
-
-struct divedatapoint *plan_add_segment(struct diveplan *diveplan, int duration, int depth, struct gasmix gasmix, int po2, bool entered)
-{
- struct divedatapoint *dp = create_dp(duration, depth, gasmix, po2);
- dp->entered = entered;
- add_to_end_of_diveplan(diveplan, dp);
- return (dp);
-}
-
-struct gaschanges {
- int depth;
- int gasidx;
-};
-
-
-static struct gaschanges *analyze_gaslist(struct diveplan *diveplan, int *gaschangenr, int depth, int *asc_cylinder)
-{
- struct gasmix gas;
- int nr = 0;
- struct gaschanges *gaschanges = NULL;
- struct divedatapoint *dp = diveplan->dp;
- int best_depth = displayed_dive.cylinder[*asc_cylinder].depth.mm;
- while (dp) {
- if (dp->time == 0) {
- gas = dp->gasmix;
- if (dp->depth <= depth) {
- int i = 0;
- nr++;
- gaschanges = realloc(gaschanges, nr * sizeof(struct gaschanges));
- while (i < nr - 1) {
- if (dp->depth < gaschanges[i].depth) {
- memmove(gaschanges + i + 1, gaschanges + i, (nr - i - 1) * sizeof(struct gaschanges));
- break;
- }
- i++;
- }
- gaschanges[i].depth = dp->depth;
- gaschanges[i].gasidx = get_gasidx(&displayed_dive, &gas);
- assert(gaschanges[i].gasidx != -1);
- } else {
- /* is there a better mix to start deco? */
- if (dp->depth < best_depth) {
- best_depth = dp->depth;
- *asc_cylinder = get_gasidx(&displayed_dive, &gas);
- }
- }
- }
- dp = dp->next;
- }
- *gaschangenr = nr;
-#if DEBUG_PLAN & 16
- for (nr = 0; nr < *gaschangenr; nr++) {
- int idx = gaschanges[nr].gasidx;
- printf("gaschange nr %d: @ %5.2lfm gasidx %d (%s)\n", nr, gaschanges[nr].depth / 1000.0,
- idx, gasname(&displayed_dive.cylinder[idx].gasmix));
- }
-#endif
- return gaschanges;
-}
-
-/* sort all the stops into one ordered list */
-static int *sort_stops(int *dstops, int dnr, struct gaschanges *gstops, int gnr)
-{
- int i, gi, di;
- int total = dnr + gnr;
- int *stoplevels = malloc(total * sizeof(int));
-
- /* no gaschanges */
- if (gnr == 0) {
- memcpy(stoplevels, dstops, dnr * sizeof(int));
- return stoplevels;
- }
- i = total - 1;
- gi = gnr - 1;
- di = dnr - 1;
- while (i >= 0) {
- if (dstops[di] > gstops[gi].depth) {
- stoplevels[i] = dstops[di];
- di--;
- } else if (dstops[di] == gstops[gi].depth) {
- stoplevels[i] = dstops[di];
- di--;
- gi--;
- } else {
- stoplevels[i] = gstops[gi].depth;
- gi--;
- }
- i--;
- if (di < 0) {
- while (gi >= 0)
- stoplevels[i--] = gstops[gi--].depth;
- break;
- }
- if (gi < 0) {
- while (di >= 0)
- stoplevels[i--] = dstops[di--];
- break;
- }
- }
- while (i >= 0)
- stoplevels[i--] = 0;
-
-#if DEBUG_PLAN & 16
- int k;
- for (k = gnr + dnr - 1; k >= 0; k--) {
- printf("stoplevel[%d]: %5.2lfm\n", k, stoplevels[k] / 1000.0);
- if (stoplevels[k] == 0)
- break;
- }
-#endif
- return stoplevels;
-}
-
-static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive, bool show_disclaimer, int error)
-{
- const unsigned int sz_buffer = 2000000;
- const unsigned int sz_temp = 100000;
- char *buffer = (char *)malloc(sz_buffer);
- char *temp = (char *)malloc(sz_temp);
- char *deco, *segmentsymbol;
- static char buf[1000];
- int len, lastdepth = 0, lasttime = 0, lastsetpoint = -1, newdepth = 0, lastprintdepth = 0, lastprintsetpoint = -1;
- struct gasmix lastprintgasmix = {{ -1 }, { -1 }};
- struct divedatapoint *dp = diveplan->dp;
- bool gaschange_after = !plan_verbatim;
- bool gaschange_before;
- bool lastentered = true;
- struct divedatapoint *nextdp = NULL;
-
- plan_verbatim = prefs.verbatim_plan;
- plan_display_runtime = prefs.display_runtime;
- plan_display_duration = prefs.display_duration;
- plan_display_transitions = prefs.display_transitions;
-
- if (prefs.deco_mode == VPMB) {
- deco = "VPM-B";
- } else {
- deco = "BUHLMANN";
- }
-
- snprintf(buf, sizeof(buf), translate("gettextFromC", "DISCLAIMER / WARNING: THIS IS A NEW IMPLEMENTATION OF THE %s "
- "ALGORITHM AND A DIVE PLANNER IMPLEMENTATION BASED ON THAT WHICH HAS "
- "RECEIVED ONLY A LIMITED AMOUNT OF TESTING. WE STRONGLY RECOMMEND NOT TO "
- "PLAN DIVES SIMPLY BASED ON THE RESULTS GIVEN HERE."), deco);
- disclaimer = buf;
-
- if (!dp) {
- free((void *)buffer);
- free((void *)temp);
- return;
- }
-
- if (error) {
- snprintf(temp, sz_temp, "%s",
- translate("gettextFromC", "Decompression calculation aborted due to excessive time"));
- snprintf(buffer, sz_buffer, "<span style='color: red;'>%s </span> %s<br>",
- translate("gettextFromC", "Warning:"), temp);
- dive->notes = strdup(buffer);
-
- free((void *)buffer);
- free((void *)temp);
- return;
- }
-
- len = show_disclaimer ? snprintf(buffer, sz_buffer, "<div><b>%s<b></div><br>", disclaimer) : 0;
- if (prefs.deco_mode == BUEHLMANN){
- snprintf(temp, sz_temp, translate("gettextFromC", "based on Bühlmann ZHL-16B with GFlow = %d and GFhigh = %d"),
- diveplan->gflow, diveplan->gfhigh);
- } else if (prefs.deco_mode == VPMB){
- if (prefs.conservatism_level == 0)
- snprintf(temp, sz_temp, "%s", translate("gettextFromC", "based on VPM-B at nominal conservatism"));
- else
- snprintf(temp, sz_temp, translate("gettextFromC", "based on VPM-B at +%d conservatism"), prefs.conservatism_level);
- } else if (prefs.deco_mode == RECREATIONAL){
- snprintf(temp, sz_temp, translate("gettextFromC", "recreational mode based on Bühlmann ZHL-16B with GFlow = %d and GFhigh = %d"),
- diveplan->gflow, diveplan->gfhigh);
- }
- len += snprintf(buffer + len, sz_buffer - len, "<div><b>%s</b><br>%s</div><br>",
- translate("gettextFromC", "Subsurface dive plan"), temp);
-
- if (!plan_verbatim) {
- len += snprintf(buffer + len, sz_buffer - len, "<div><table><thead><tr><th></th><th>%s</th>",
- translate("gettextFromC", "depth"));
- if (plan_display_duration)
- len += snprintf(buffer + len, sz_buffer - len, "<th style='padding-left: 10px;'>%s</th>",
- translate("gettextFromC", "duration"));
- if (plan_display_runtime)
- len += snprintf(buffer + len, sz_buffer - len, "<th style='padding-left: 10px;'>%s</th>",
- translate("gettextFromC", "runtime"));
- len += snprintf(buffer + len, sz_buffer - len,
- "<th style='padding-left: 10px; float: left;'>%s</th></tr></thead><tbody style='float: left;'>",
- translate("gettextFromC", "gas"));
- }
- do {
- struct gasmix gasmix, newgasmix = {};
- const char *depth_unit;
- double depthvalue;
- int decimals;
- bool isascent = (dp->depth < lastdepth);
-
- nextdp = dp->next;
- if (dp->time == 0)
- continue;
- gasmix = dp->gasmix;
- depthvalue = get_depth_units(dp->depth, &decimals, &depth_unit);
- /* analyze the dive points ahead */
- while (nextdp && nextdp->time == 0)
- nextdp = nextdp->next;
- if (nextdp)
- newgasmix = nextdp->gasmix;
- gaschange_after = (nextdp && (gasmix_distance(&gasmix, &newgasmix) || dp->setpoint != nextdp->setpoint));
- gaschange_before = (gasmix_distance(&lastprintgasmix, &gasmix) || lastprintsetpoint != dp->setpoint);
- /* do we want to skip this leg as it is devoid of anything useful? */
- if (!dp->entered &&
- nextdp &&
- dp->depth != lastdepth &&
- nextdp->depth != dp->depth &&
- !gaschange_before &&
- !gaschange_after)
- continue;
- if (dp->time - lasttime < 10 && !(gaschange_after && dp->next && dp->depth != dp->next->depth))
- continue;
-
- len = strlen(buffer);
- if (plan_verbatim) {
- /* When displaying a verbatim plan, we output a waypoint for every gas change.
- * Therefore, we do not need to test for difficult cases that mean we need to
- * print a segment just so we don't miss a gas change. This makes the logic
- * to determine whether or not to print a segment much simpler than with the
- * non-verbatim plan.
- */
- if (dp->depth != lastprintdepth) {
- if (plan_display_transitions || dp->entered || !dp->next || (gaschange_after && dp->next && dp->depth != nextdp->depth)) {
- if (dp->setpoint)
- snprintf(temp, sz_temp, translate("gettextFromC", "Transition to %.*f %s in %d:%02d min - runtime %d:%02u on %s (SP = %.1fbar)"),
- decimals, depthvalue, depth_unit,
- FRACTION(dp->time - lasttime, 60),
- FRACTION(dp->time, 60),
- gasname(&gasmix),
- (double) dp->setpoint / 1000.0);
-
- else
- snprintf(temp, sz_temp, translate("gettextFromC", "Transition to %.*f %s in %d:%02d min - runtime %d:%02u on %s"),
- decimals, depthvalue, depth_unit,
- FRACTION(dp->time - lasttime, 60),
- FRACTION(dp->time, 60),
- gasname(&gasmix));
-
- len += snprintf(buffer + len, sz_buffer - len, "%s<br>", temp);
- }
- newdepth = dp->depth;
- lasttime = dp->time;
- } else {
- if ((nextdp && dp->depth != nextdp->depth) || gaschange_after) {
- if (dp->setpoint)
- snprintf(temp, sz_temp, translate("gettextFromC", "Stay at %.*f %s for %d:%02d min - runtime %d:%02u on %s (SP = %.1fbar)"),
- decimals, depthvalue, depth_unit,
- FRACTION(dp->time - lasttime, 60),
- FRACTION(dp->time, 60),
- gasname(&gasmix),
- (double) dp->setpoint / 1000.0);
- else
- snprintf(temp, sz_temp, translate("gettextFromC", "Stay at %.*f %s for %d:%02d min - runtime %d:%02u on %s"),
- decimals, depthvalue, depth_unit,
- FRACTION(dp->time - lasttime, 60),
- FRACTION(dp->time, 60),
- gasname(&gasmix));
-
- len += snprintf(buffer + len, sz_buffer - len, "%s<br>", temp);
- newdepth = dp->depth;
- lasttime = dp->time;
- }
- }
- } else {
- /* When not displaying the verbatim dive plan, we typically ignore ascents between deco stops,
- * unless the display transitions option has been selected. We output a segment if any of the
- * following conditions are met.
- * 1) Display transitions is selected
- * 2) The segment was manually entered
- * 3) It is the last segment of the dive
- * 4) The segment is not an ascent, there was a gas change at the start of the segment and the next segment
- * is a change in depth (typical deco stop)
- * 5) There is a gas change at the end of the segment and the last segment was entered (first calculated
- * segment if it ends in a gas change)
- * 6) There is a gaschange after but no ascent. This should only occur when backgas breaks option is selected
- * 7) It is an ascent ending with a gas change, but is not followed by a stop. As case 5 already matches
- * the first calculated ascent if it ends with a gas change, this should only occur if a travel gas is
- * used for a calculated ascent, there is a subsequent gas change before the first deco stop, and zero
- * time has been allowed for a gas switch.
- */
- if (plan_display_transitions || dp->entered || !dp->next ||
- (nextdp && dp->depth != nextdp->depth) ||
- (!isascent && gaschange_before && nextdp && dp->depth != nextdp->depth) ||
- (gaschange_after && lastentered) || (gaschange_after && !isascent) ||
- (isascent && gaschange_after && nextdp && dp->depth != nextdp->depth )) {
- // Print a symbol to indicate whether segment is an ascent, descent, constant depth (user entered) or deco stop
- if (isascent)
- segmentsymbol = "&#10138;"; // up-right arrow for ascent
- else if (dp->depth > lastdepth)
- segmentsymbol = "&#10136;"; // down-right arrow for descent
- else if (dp->entered)
- segmentsymbol = "&#10137;"; // right arrow for entered entered segment at constant depth
- else
- segmentsymbol = "&#10134;"; // heavey minus sign for deco stop
-
- len += snprintf(buffer + len, sz_buffer - len, "<tr><td style='padding-left: 10px; float: right;'>%s</td>", segmentsymbol);
-
- snprintf(temp, sz_temp, translate("gettextFromC", "%3.0f%s"), depthvalue, depth_unit);
- len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; float: right;'>%s</td>", temp);
- if (plan_display_duration) {
- snprintf(temp, sz_temp, translate("gettextFromC", "%3dmin"), (dp->time - lasttime + 30) / 60);
- len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; float: right;'>%s</td>", temp);
- }
- if (plan_display_runtime) {
- snprintf(temp, sz_temp, translate("gettextFromC", "%3dmin"), (dp->time + 30) / 60);
- len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; float: right;'>%s</td>", temp);
- }
-
- /* Normally a gas change is displayed on the stopping segment, so only display a gas change at the end of
- * an ascent segment if it is not followed by a stop
- */
- if (isascent && gaschange_after && dp->next && nextdp && dp->depth != nextdp->depth) {
- if (dp->setpoint) {
- snprintf(temp, sz_temp, translate("gettextFromC", "(SP = %.1fbar)"), (double) nextdp->setpoint / 1000.0);
- len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; color: red; float: left;'><b>%s %s</b></td>", gasname(&newgasmix),
- temp);
- } else {
- len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; color: red; float: left;'><b>%s</b></td>", gasname(&newgasmix));
- }
- lastprintsetpoint = nextdp->setpoint;
- lastprintgasmix = newgasmix;
- gaschange_after = false;
- } else if (gaschange_before) {
- // If a new gas has been used for this segment, now is the time to show it
- if (dp->setpoint) {
- snprintf(temp, sz_temp, translate("gettextFromC", "(SP = %.1fbar)"), (double) dp->setpoint / 1000.0);
- len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; color: red; float: left;'><b>%s %s</b></td>", gasname(&gasmix),
- temp);
- } else {
- len += snprintf(buffer + len, sz_buffer - len, "<td style='padding-left: 10px; color: red; float: left;'><b>%s</b></td>", gasname(&gasmix));
- }
- // Set variables so subsequent iterations can test against the last gas printed
- lastprintsetpoint = dp->setpoint;
- lastprintgasmix = gasmix;
- gaschange_after = false;
- } else {
- len += snprintf(buffer + len, sz_buffer - len, "<td>&nbsp;</td>");
- }
- len += snprintf(buffer + len, sz_buffer - len, "</tr>");
- newdepth = dp->depth;
- lasttime = dp->time;
- }
- }
- if (gaschange_after) {
- // gas switch at this waypoint
- if (plan_verbatim) {
- if (lastsetpoint >= 0) {
- if (nextdp && nextdp->setpoint)
- snprintf(temp, sz_temp, translate("gettextFromC", "Switch gas to %s (SP = %.1fbar)"), gasname(&newgasmix), (double) nextdp->setpoint / 1000.0);
- else
- snprintf(temp, sz_temp, translate("gettextFromC", "Switch gas to %s"), gasname(&newgasmix));
-
- len += snprintf(buffer + len, sz_buffer - len, "%s<br>", temp);
- }
- gaschange_after = false;
- gasmix = newgasmix;
- }
- }
- lastprintdepth = newdepth;
- lastdepth = dp->depth;
- lastsetpoint = dp->setpoint;
- lastentered = dp->entered;
- } while ((dp = nextdp) != NULL);
- if (!plan_verbatim)
- len += snprintf(buffer + len, sz_buffer - len, "</tbody></table></div>");
-
- dive->cns = 0;
- dive->maxcns = 0;
- update_cylinder_related_info(dive);
- snprintf(temp, sz_temp, "%s", translate("gettextFromC", "CNS"));
- len += snprintf(buffer + len, sz_buffer - len, "<div><br>%s: %i%%", temp, dive->cns);
- snprintf(temp, sz_temp, "%s", translate("gettextFromC", "OTU"));
- len += snprintf(buffer + len, sz_buffer - len, "<br>%s: %i</div>", temp, dive->otu);
-
- if (dive->dc.divemode == CCR)
- snprintf(temp, sz_temp, "%s", translate("gettextFromC", "Gas consumption (CCR legs excluded):"));
- else
- snprintf(temp, sz_temp, "%s", translate("gettextFromC", "Gas consumption:"));
- len += snprintf(buffer + len, sz_buffer - len, "<div><br>%s<br>", temp);
- for (int gasidx = 0; gasidx < MAX_CYLINDERS; gasidx++) {
- double volume, pressure, deco_volume, deco_pressure;
- const char *unit, *pressure_unit;
- char warning[1000] = "";
- cylinder_t *cyl = &dive->cylinder[gasidx];
- if (cylinder_none(cyl))
- break;
-
- volume = get_volume_units(cyl->gas_used.mliter, NULL, &unit);
- deco_volume = get_volume_units(cyl->deco_gas_used.mliter, NULL, &unit);
- if (cyl->type.size.mliter) {
- deco_pressure = get_pressure_units(1000.0 * cyl->deco_gas_used.mliter / cyl->type.size.mliter, &pressure_unit);
- pressure = get_pressure_units(1000.0 * cyl->gas_used.mliter / cyl->type.size.mliter, &pressure_unit);
- /* Warn if the plan uses more gas than is available in a cylinder
- * This only works if we have working pressure for the cylinder
- * 10bar is a made up number - but it seemed silly to pretend you could breathe cylinder down to 0 */
- if (cyl->end.mbar < 10000)
- snprintf(warning, sizeof(warning), " &mdash; <span style='color: red;'>%s </span> %s",
- translate("gettextFromC", "Warning:"),
- translate("gettextFromC", "this is more gas than available in the specified cylinder!"));
- else
- if ((float) cyl->end.mbar * cyl->type.size.mliter / 1000.0 < (float) cyl->deco_gas_used.mliter)
- snprintf(warning, sizeof(warning), " &mdash; <span style='color: red;'>%s </span> %s",
- translate("gettextFromC", "Warning:"),
- translate("gettextFromC", "not enough reserve for gas sharing on ascent!"));
-
- snprintf(temp, sz_temp, translate("gettextFromC", "%.0f%s/%.0f%s of %s (%.0f%s/%.0f%s in planned ascent)"), volume, unit, pressure, pressure_unit, gasname(&cyl->gasmix), deco_volume, unit, deco_pressure, pressure_unit);
- } else {
- snprintf(temp, sz_temp, translate("gettextFromC", "%.0f%s (%.0f%s during planned ascent) of %s"), volume, unit, deco_volume, unit, gasname(&cyl->gasmix));
- }
- len += snprintf(buffer + len, sz_buffer - len, "%s%s<br>", temp, warning);
- }
- dp = diveplan->dp;
- if (dive->dc.divemode != CCR) {
- while (dp) {
- if (dp->time != 0) {
- struct gas_pressures pressures;
- fill_pressures(&pressures, depth_to_atm(dp->depth, dive), &dp->gasmix, 0.0, dive->dc.divemode);
-
- if (pressures.o2 > (dp->entered ? prefs.bottompo2 : prefs.decopo2) / 1000.0) {
- const char *depth_unit;
- int decimals;
- double depth_value = get_depth_units(dp->depth, &decimals, &depth_unit);
- len = strlen(buffer);
- snprintf(temp, sz_temp,
- translate("gettextFromC", "high pOâ‚‚ value %.2f at %d:%02u with gas %s at depth %.*f %s"),
- pressures.o2, FRACTION(dp->time, 60), gasname(&dp->gasmix), decimals, depth_value, depth_unit);
- len += snprintf(buffer + len, sz_buffer - len, "<span style='color: red;'>%s </span> %s<br>",
- translate("gettextFromC", "Warning:"), temp);
- } else if (pressures.o2 < 0.16) {
- const char *depth_unit;
- int decimals;
- double depth_value = get_depth_units(dp->depth, &decimals, &depth_unit);
- len = strlen(buffer);
- snprintf(temp, sz_temp,
- translate("gettextFromC", "low pOâ‚‚ value %.2f at %d:%02u with gas %s at depth %.*f %s"),
- pressures.o2, FRACTION(dp->time, 60), gasname(&dp->gasmix), decimals, depth_value, depth_unit);
- len += snprintf(buffer + len, sz_buffer - len, "<span style='color: red;'>%s </span> %s<br>",
- translate("gettextFromC", "Warning:"), temp);
-
- }
- }
- dp = dp->next;
- }
- }
- snprintf(buffer + len, sz_buffer - len, "</div>");
- dive->notes = strdup(buffer);
-
- free((void *)buffer);
- free((void *)temp);
-}
-
-int ascent_velocity(int depth, int avg_depth, int bottom_time)
-{
- (void) bottom_time;
- /* We need to make this configurable */
-
- /* As an example (and possibly reasonable default) this is the Tech 1 provedure according
- * to http://www.globalunderwaterexplorers.org/files/Standards_and_Procedures/SOP_Manual_Ver2.0.2.pdf */
-
- if (depth * 4 > avg_depth * 3) {
- return prefs.ascrate75;
- } else {
- if (depth * 2 > avg_depth) {
- return prefs.ascrate50;
- } else {
- if (depth > 6000)
- return prefs.ascratestops;
- else
- return prefs.ascratelast6m;
- }
- }
-}
-
-void track_ascent_gas(int depth, cylinder_t *cylinder, int avg_depth, int bottom_time, bool safety_stop)
-{
- while (depth > 0) {
- int deltad = ascent_velocity(depth, avg_depth, bottom_time) * TIMESTEP;
- if (deltad > depth)
- deltad = depth;
- update_cylinder_pressure(&displayed_dive, depth, depth - deltad, TIMESTEP, prefs.decosac, cylinder, true);
- if (depth <= 5000 && depth >= (5000 - deltad) && safety_stop) {
- update_cylinder_pressure(&displayed_dive, 5000, 5000, 180, prefs.decosac, cylinder, true);
- safety_stop = false;
- }
- depth -= deltad;
- }
-}
-
-// Determine whether ascending to the next stop will break the ceiling. Return true if the ascent is ok, false if it isn't.
-bool trial_ascent(int trial_depth, int stoplevel, int avg_depth, int bottom_time, struct gasmix *gasmix, int po2, double surface_pressure)
-{
-
- bool clear_to_ascend = true;
- char *trial_cache = NULL;
-
- // For consistency with other VPM-B implementations, we should not start the ascent while the ceiling is
- // deeper than the next stop (thus the offgasing during the ascent is ignored).
- // However, we still need to make sure we don't break the ceiling due to on-gassing during ascent.
- if (prefs.deco_mode == VPMB && (deco_allowed_depth(tissue_tolerance_calc(&displayed_dive,
- depth_to_bar(stoplevel, &displayed_dive)),
- surface_pressure, &displayed_dive, 1) > stoplevel))
- return false;
-
- cache_deco_state(&trial_cache);
- while (trial_depth > stoplevel) {
- int deltad = ascent_velocity(trial_depth, avg_depth, bottom_time) * TIMESTEP;
- if (deltad > trial_depth) /* don't test against depth above surface */
- deltad = trial_depth;
- add_segment(depth_to_bar(trial_depth, &displayed_dive),
- gasmix,
- TIMESTEP, po2, &displayed_dive, prefs.decosac);
- if (deco_allowed_depth(tissue_tolerance_calc(&displayed_dive, depth_to_bar(trial_depth, &displayed_dive)),
- surface_pressure, &displayed_dive, 1) > trial_depth - deltad) {
- /* We should have stopped */
- clear_to_ascend = false;
- break;
- }
- trial_depth -= deltad;
- }
- restore_deco_state(trial_cache);
- free(trial_cache);
- return clear_to_ascend;
-}
-
-/* Determine if there is enough gas for the dive. Return true if there is enough.
- * Also return true if this cannot be calculated because the cylinder doesn't have
- * size or a starting pressure.
- */
-bool enough_gas(int current_cylinder)
-{
- cylinder_t *cyl;
- cyl = &displayed_dive.cylinder[current_cylinder];
-
- if (!cyl->start.mbar)
- return true;
- if (cyl->type.size.mliter)
- return (float) (cyl->end.mbar - prefs.reserve_gas) * cyl->type.size.mliter / 1000.0 > (float) cyl->deco_gas_used.mliter;
- else
- return true;
-}
-
-// Work out the stops. Return value is if there were any mandatory stops.
-
-bool plan(struct diveplan *diveplan, char **cached_datap, bool is_planner, bool show_disclaimer)
-{
- int bottom_depth;
- int bottom_gi;
- int bottom_stopidx;
- bool is_final_plan = true;
- int deco_time;
- int previous_deco_time;
- char *bottom_cache = NULL;
- struct sample *sample;
- int po2;
- int transitiontime, gi;
- int current_cylinder;
- int stopidx;
- int depth;
- struct gaschanges *gaschanges = NULL;
- int gaschangenr;
- int *decostoplevels;
- int decostoplevelcount;
- int *stoplevels = NULL;
- bool stopping = false;
- bool pendinggaschange = false;
- int clock, previous_point_time;
- int avg_depth, max_depth, bottom_time = 0;
- int last_ascend_rate;
- int best_first_ascend_cylinder;
- struct gasmix gas, bottom_gas;
- int o2time = 0;
- int breaktime = -1;
- int breakcylinder = 0;
- int error = 0;
- bool decodive = false;
-
- set_gf(diveplan->gflow, diveplan->gfhigh, prefs.gf_low_at_maxdepth);
- if (!diveplan->surface_pressure)
- diveplan->surface_pressure = SURFACE_PRESSURE;
- displayed_dive.surface_pressure.mbar = diveplan->surface_pressure;
- clear_deco(displayed_dive.surface_pressure.mbar / 1000.0);
- max_bottom_ceiling_pressure.mbar = first_ceiling_pressure.mbar = 0;
- create_dive_from_plan(diveplan, is_planner);
-
- // Do we want deco stop array in metres or feet?
- if (prefs.units.length == METERS ) {
- decostoplevels = decostoplevels_metric;
- decostoplevelcount = sizeof(decostoplevels_metric) / sizeof(int);
- } else {
- decostoplevels = decostoplevels_imperial;
- decostoplevelcount = sizeof(decostoplevels_imperial) / sizeof(int);
- }
-
- /* If the user has selected last stop to be at 6m/20', we need to get rid of the 3m/10' stop.
- * Otherwise reinstate the last stop 3m/10' stop.
- */
- if (prefs.last_stop)
- *(decostoplevels + 1) = 0;
- else
- *(decostoplevels + 1) = M_OR_FT(3,10);
-
- /* Let's start at the last 'sample', i.e. the last manually entered waypoint. */
- sample = &displayed_dive.dc.sample[displayed_dive.dc.samples - 1];
-
- get_gas_at_time(&displayed_dive, &displayed_dive.dc, sample->time, &gas);
-
- po2 = sample->setpoint.mbar;
- if ((current_cylinder = get_gasidx(&displayed_dive, &gas)) == -1) {
- report_error(translate("gettextFromC", "Can't find gas %s"), gasname(&gas));
- current_cylinder = 0;
- }
- depth = displayed_dive.dc.sample[displayed_dive.dc.samples - 1].depth.mm;
- average_max_depth(diveplan, &avg_depth, &max_depth);
- last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time);
-
- /* if all we wanted was the dive just get us back to the surface */
- if (!is_planner) {
- transitiontime = depth / 75; /* this still needs to be made configurable */
- plan_add_segment(diveplan, transitiontime, 0, gas, po2, false);
- create_dive_from_plan(diveplan, is_planner);
- return(false);
- }
-
-#if DEBUG_PLAN & 4
- printf("gas %s\n", gasname(&gas));
- printf("depth %5.2lfm \n", depth / 1000.0);
-#endif
-
- best_first_ascend_cylinder = current_cylinder;
- /* Find the gases available for deco */
-
- if (po2) { // Don't change gas in CCR mode
- gaschanges = NULL;
- gaschangenr = 0;
- } else {
- gaschanges = analyze_gaslist(diveplan, &gaschangenr, depth, &best_first_ascend_cylinder);
- }
- /* Find the first potential decostopdepth above current depth */
- for (stopidx = 0; stopidx < decostoplevelcount; stopidx++)
- if (*(decostoplevels + stopidx) >= depth)
- break;
- if (stopidx > 0)
- stopidx--;
- /* Stoplevels are either depths of gas changes or potential deco stop depths. */
- stoplevels = sort_stops(decostoplevels, stopidx + 1, gaschanges, gaschangenr);
- stopidx += gaschangenr;
-
- /* Keep time during the ascend */
- bottom_time = clock = previous_point_time = displayed_dive.dc.sample[displayed_dive.dc.samples - 1].time.seconds;
- gi = gaschangenr - 1;
-
- /* Set tissue tolerance and initial vpmb gradient at start of ascent phase */
- tissue_at_end(&displayed_dive, cached_datap);
- nuclear_regeneration(clock);
- vpmb_start_gradient();
-
- if(prefs.deco_mode == RECREATIONAL) {
- bool safety_stop = prefs.safetystop && max_depth >= 10000;
- track_ascent_gas(depth, &displayed_dive.cylinder[current_cylinder], avg_depth, bottom_time, safety_stop);
- // How long can we stay at the current depth and still directly ascent to the surface?
- do {
- add_segment(depth_to_bar(depth, &displayed_dive),
- &displayed_dive.cylinder[current_cylinder].gasmix,
- DECOTIMESTEP, po2, &displayed_dive, prefs.bottomsac);
- update_cylinder_pressure(&displayed_dive, depth, depth, DECOTIMESTEP, prefs.bottomsac, &displayed_dive.cylinder[current_cylinder], false);
- clock += DECOTIMESTEP;
- } while (trial_ascent(depth, 0, avg_depth, bottom_time, &displayed_dive.cylinder[current_cylinder].gasmix,
- po2, diveplan->surface_pressure / 1000.0) &&
- enough_gas(current_cylinder));
-
- // We did stay one DECOTIMESTEP too many.
- // In the best of all worlds, we would roll back also the last add_segment in terms of caching deco state, but
- // let's ignore that since for the eventual ascent in recreational mode, nobody looks at the ceiling anymore,
- // so we don't really have to compute the deco state.
- update_cylinder_pressure(&displayed_dive, depth, depth, -DECOTIMESTEP, prefs.bottomsac, &displayed_dive.cylinder[current_cylinder], false);
- clock -= DECOTIMESTEP;
- plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, true);
- previous_point_time = clock;
- do {
- /* Ascend to surface */
- int deltad = ascent_velocity(depth, avg_depth, bottom_time) * TIMESTEP;
- if (ascent_velocity(depth, avg_depth, bottom_time) != last_ascend_rate) {
- plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false);
- previous_point_time = clock;
- last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time);
- }
- if (depth - deltad < 0)
- deltad = depth;
-
- clock += TIMESTEP;
- depth -= deltad;
- if (depth <= 5000 && depth >= (5000 - deltad) && safety_stop) {
- plan_add_segment(diveplan, clock - previous_point_time, 5000, gas, po2, false);
- previous_point_time = clock;
- clock += 180;
- plan_add_segment(diveplan, clock - previous_point_time, 5000, gas, po2, false);
- previous_point_time = clock;
- safety_stop = false;
- }
- } while (depth > 0);
- plan_add_segment(diveplan, clock - previous_point_time, 0, gas, po2, false);
- create_dive_from_plan(diveplan, is_planner);
- add_plan_to_notes(diveplan, &displayed_dive, show_disclaimer, error);
- fixup_dc_duration(&displayed_dive.dc);
-
- free(stoplevels);
- free(gaschanges);
- return(false);
- }
-
- if (best_first_ascend_cylinder != current_cylinder) {
- current_cylinder = best_first_ascend_cylinder;
- gas = displayed_dive.cylinder[current_cylinder].gasmix;
-
-#if DEBUG_PLAN & 16
- printf("switch to gas %d (%d/%d) @ %5.2lfm\n", best_first_ascend_cylinder,
- (get_o2(&gas) + 5) / 10, (get_he(&gas) + 5) / 10, gaschanges[best_first_ascend_cylinder].depth / 1000.0);
-#endif
- }
-
- // VPM-B or Buehlmann Deco
- tissue_at_end(&displayed_dive, cached_datap);
- previous_deco_time = 100000000;
- deco_time = 10000000;
- cache_deco_state(&bottom_cache); // Lets us make several iterations
- bottom_depth = depth;
- bottom_gi = gi;
- bottom_gas = gas;
- bottom_stopidx = stopidx;
-
- //CVA
- do {
- is_final_plan = (prefs.deco_mode == BUEHLMANN) || (previous_deco_time - deco_time < 10); // CVA time converges
- if (deco_time != 10000000)
- vpmb_next_gradient(deco_time, diveplan->surface_pressure / 1000.0);
-
- previous_deco_time = deco_time;
- restore_deco_state(bottom_cache);
-
- depth = bottom_depth;
- gi = bottom_gi;
- clock = previous_point_time = bottom_time;
- gas = bottom_gas;
- stopping = false;
- decodive = false;
- stopidx = bottom_stopidx;
- breaktime = -1;
- breakcylinder = 0;
- o2time = 0;
- first_ceiling_pressure.mbar = depth_to_mbar(deco_allowed_depth(tissue_tolerance_calc(&displayed_dive,
- depth_to_bar(depth, &displayed_dive)),
- diveplan->surface_pressure / 1000.0,
- &displayed_dive,
- 1),
- &displayed_dive);
- if (max_bottom_ceiling_pressure.mbar > first_ceiling_pressure.mbar)
- first_ceiling_pressure.mbar = max_bottom_ceiling_pressure.mbar;
-
- last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time);
- if ((current_cylinder = get_gasidx(&displayed_dive, &gas)) == -1) {
- report_error(translate("gettextFromC", "Can't find gas %s"), gasname(&gas));
- current_cylinder = 0;
- }
-
- while (1) {
- /* We will break out when we hit the surface */
- do {
- /* Ascend to next stop depth */
- int deltad = ascent_velocity(depth, avg_depth, bottom_time) * TIMESTEP;
- if (ascent_velocity(depth, avg_depth, bottom_time) != last_ascend_rate) {
- if (is_final_plan)
- plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false);
- previous_point_time = clock;
- stopping = false;
- last_ascend_rate = ascent_velocity(depth, avg_depth, bottom_time);
- }
- if (depth - deltad < stoplevels[stopidx])
- deltad = depth - stoplevels[stopidx];
-
- add_segment(depth_to_bar(depth, &displayed_dive),
- &displayed_dive.cylinder[current_cylinder].gasmix,
- TIMESTEP, po2, &displayed_dive, prefs.decosac);
- clock += TIMESTEP;
- depth -= deltad;
- } while (depth > 0 && depth > stoplevels[stopidx]);
-
- if (depth <= 0)
- break; /* We are at the surface */
-
- if (gi >= 0 && stoplevels[stopidx] <= gaschanges[gi].depth) {
- /* We have reached a gas change.
- * Record this in the dive plan */
- if (is_final_plan)
- plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false);
- previous_point_time = clock;
- stopping = true;
-
- /* Check we need to change cylinder.
- * We might not if the cylinder was chosen by the user
- * or user has selected only to switch only at required stops.
- * If current gas is hypoxic, we want to switch asap */
-
- if (current_cylinder != gaschanges[gi].gasidx) {
- if (!prefs.switch_at_req_stop ||
- !trial_ascent(depth, stoplevels[stopidx - 1], avg_depth, bottom_time,
- &displayed_dive.cylinder[current_cylinder].gasmix, po2, diveplan->surface_pressure / 1000.0) || get_o2(&displayed_dive.cylinder[current_cylinder].gasmix) < 160) {
- current_cylinder = gaschanges[gi].gasidx;
- gas = displayed_dive.cylinder[current_cylinder].gasmix;
-#if DEBUG_PLAN & 16
- printf("switch to gas %d (%d/%d) @ %5.2lfm\n", gaschanges[gi].gasidx,
- (get_o2(&gas) + 5) / 10, (get_he(&gas) + 5) / 10, gaschanges[gi].depth / 1000.0);
-#endif
- /* Stop for the minimum duration to switch gas */
- add_segment(depth_to_bar(depth, &displayed_dive),
- &displayed_dive.cylinder[current_cylinder].gasmix,
- prefs.min_switch_duration, po2, &displayed_dive, prefs.decosac);
- clock += prefs.min_switch_duration;
- if (prefs.doo2breaks && get_o2(&displayed_dive.cylinder[current_cylinder].gasmix) == 1000)
- o2time += prefs.min_switch_duration;
- } else {
- /* The user has selected the option to switch gas only at required stops.
- * Remember that we are waiting to switch gas
- */
- pendinggaschange = true;
- }
- }
- gi--;
- }
- --stopidx;
-
- /* Save the current state and try to ascend to the next stopdepth */
- while (1) {
- /* Check if ascending to next stop is clear, go back and wait if we hit the ceiling on the way */
- if (trial_ascent(depth, stoplevels[stopidx], avg_depth, bottom_time,
- &displayed_dive.cylinder[current_cylinder].gasmix, po2, diveplan->surface_pressure / 1000.0))
- break; /* We did not hit the ceiling */
-
- /* Add a minute of deco time and then try again */
- decodive = true;
- if (!stopping) {
- /* The last segment was an ascend segment.
- * Add a waypoint for start of this deco stop */
- if (is_final_plan)
- plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false);
- previous_point_time = clock;
- stopping = true;
- }
-
- /* Are we waiting to switch gas?
- * Occurs when the user has selected the option to switch only at required stops
- */
- if (pendinggaschange) {
- current_cylinder = gaschanges[gi + 1].gasidx;
- gas = displayed_dive.cylinder[current_cylinder].gasmix;
-#if DEBUG_PLAN & 16
- printf("switch to gas %d (%d/%d) @ %5.2lfm\n", gaschanges[gi + 1].gasidx,
- (get_o2(&gas) + 5) / 10, (get_he(&gas) + 5) / 10, gaschanges[gi + 1].depth / 1000.0);
-#endif
- /* Stop for the minimum duration to switch gas */
- add_segment(depth_to_bar(depth, &displayed_dive),
- &displayed_dive.cylinder[current_cylinder].gasmix,
- prefs.min_switch_duration, po2, &displayed_dive, prefs.decosac);
- clock += prefs.min_switch_duration;
- if (prefs.doo2breaks && get_o2(&displayed_dive.cylinder[current_cylinder].gasmix) == 1000)
- o2time += prefs.min_switch_duration;
- pendinggaschange = false;
- }
-
- /* Deco stop should end when runtime is at a whole minute */
- int this_decotimestep;
- this_decotimestep = DECOTIMESTEP - clock % DECOTIMESTEP;
-
- add_segment(depth_to_bar(depth, &displayed_dive),
- &displayed_dive.cylinder[current_cylinder].gasmix,
- this_decotimestep, po2, &displayed_dive, prefs.decosac);
- clock += this_decotimestep;
- /* Finish infinite deco */
- if(clock >= 48 * 3600 && depth >= 6000) {
- error = LONGDECO;
- break;
- }
- if (prefs.doo2breaks) {
- /* The backgas breaks option limits time on oxygen to 12 minutes, followed by 6 minutes on
- * backgas (first defined gas). This could be customized if there were demand.
- */
- if (get_o2(&displayed_dive.cylinder[current_cylinder].gasmix) == 1000) {
- o2time += DECOTIMESTEP;
- if (o2time >= 12 * 60) {
- breaktime = 0;
- breakcylinder = current_cylinder;
- if (is_final_plan)
- plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false);
- previous_point_time = clock;
- current_cylinder = 0;
- gas = displayed_dive.cylinder[current_cylinder].gasmix;
- }
- } else {
- if (breaktime >= 0) {
- breaktime += DECOTIMESTEP;
- if (breaktime >= 6 * 60) {
- o2time = 0;
- if (is_final_plan)
- plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false);
- previous_point_time = clock;
- current_cylinder = breakcylinder;
- gas = displayed_dive.cylinder[current_cylinder].gasmix;
- breaktime = -1;
- }
- }
- }
- }
- }
- if (stopping) {
- /* Next we will ascend again. Add a waypoint if we have spend deco time */
- if (is_final_plan)
- plan_add_segment(diveplan, clock - previous_point_time, depth, gas, po2, false);
- previous_point_time = clock;
- stopping = false;
- }
- }
-
- deco_time = clock - bottom_time;
- } while (!is_final_plan);
-
- plan_add_segment(diveplan, clock - previous_point_time, 0, gas, po2, false);
- create_dive_from_plan(diveplan, is_planner);
- add_plan_to_notes(diveplan, &displayed_dive, show_disclaimer, error);
- fixup_dc_duration(&displayed_dive.dc);
-
- free(stoplevels);
- free(gaschanges);
- free(bottom_cache);
- return decodive;
-}
-
-/*
- * Get a value in tenths (so "10.2" == 102, "9" = 90)
- *
- * Return negative for errors.
- */
-static int get_tenths(const char *begin, const char **endp)
-{
- char *end;
- int value = strtol(begin, &end, 10);
-
- if (begin == end)
- return -1;
- value *= 10;
-
- /* Fraction? We only look at the first digit */
- if (*end == '.') {
- end++;
- if (!isdigit(*end))
- return -1;
- value += *end - '0';
- do {
- end++;
- } while (isdigit(*end));
- }
- *endp = end;
- return value;
-}
-
-static int get_permille(const char *begin, const char **end)
-{
- int value = get_tenths(begin, end);
- if (value >= 0) {
- /* Allow a percentage sign */
- if (**end == '%')
- ++*end;
- }
- return value;
-}
-
-int validate_gas(const char *text, struct gasmix *gas)
-{
- int o2, he;
-
- if (!text)
- return 0;
-
- while (isspace(*text))
- text++;
-
- if (!*text)
- return 0;
-
- if (!strcasecmp(text, translate("gettextFromC", "air"))) {
- o2 = O2_IN_AIR;
- he = 0;
- text += strlen(translate("gettextFromC", "air"));
- } else if (!strcasecmp(text, translate("gettextFromC", "oxygen"))) {
- o2 = 1000;
- he = 0;
- text += strlen(translate("gettextFromC", "oxygen"));
- } else if (!strncasecmp(text, translate("gettextFromC", "ean"), 3)) {
- o2 = get_permille(text + 3, &text);
- he = 0;
- } else {
- o2 = get_permille(text, &text);
- he = 0;
- if (*text == '/')
- he = get_permille(text + 1, &text);
- }
-
- /* We don't want any extra crud */
- while (isspace(*text))
- text++;
- if (*text)
- return 0;
-
- /* Validate the gas mix */
- if (*text || o2 < 1 || o2 > 1000 || he < 0 || o2 + he > 1000)
- return 0;
-
- /* Let it rip */
- gas->o2.permille = o2;
- gas->he.permille = he;
- return 1;
-}
-
-int validate_po2(const char *text, int *mbar_po2)
-{
- int po2;
-
- if (!text)
- return 0;
-
- po2 = get_tenths(text, &text);
- if (po2 < 0)
- return 0;
-
- while (isspace(*text))
- text++;
-
- while (isspace(*text))
- text++;
- if (*text)
- return 0;
-
- *mbar_po2 = po2 * 100;
- return 1;
-}
diff --git a/subsurface-core/planner.h b/subsurface-core/planner.h
deleted file mode 100644
index a675989e0..000000000
--- a/subsurface-core/planner.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef PLANNER_H
-#define PLANNER_H
-
-#define LONGDECO 1
-#define NOT_RECREATIONAL 2
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern int validate_gas(const char *text, struct gasmix *gas);
-extern int validate_po2(const char *text, int *mbar_po2);
-extern timestamp_t current_time_notz(void);
-extern void set_last_stop(bool last_stop_6m);
-extern void set_verbatim(bool verbatim);
-extern void set_display_runtime(bool display);
-extern void set_display_duration(bool display);
-extern void set_display_transitions(bool display);
-extern void get_gas_at_time(struct dive *dive, struct divecomputer *dc, duration_t time, struct gasmix *gas);
-extern int get_gasidx(struct dive *dive, struct gasmix *mix);
-extern bool diveplan_empty(struct diveplan *diveplan);
-
-extern void free_dps(struct diveplan *diveplan);
-extern struct dive *planned_dive;
-extern char *cache_data;
-extern const char *disclaimer;
-extern double plangflow, plangfhigh;
-
-#ifdef __cplusplus
-}
-#endif
-#endif // PLANNER_H
diff --git a/subsurface-core/pluginmanager.cpp b/subsurface-core/pluginmanager.cpp
deleted file mode 100644
index 28c978280..000000000
--- a/subsurface-core/pluginmanager.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-#include "pluginmanager.h"
-
-#include <QApplication>
-#include <QDir>
-#include <QPluginLoader>
-#include <QDebug>
-
-static QList<ISocialNetworkIntegration*> _socialNetworks;
-
-PluginManager& PluginManager::instance()
-{
- static PluginManager self;
- return self;
-}
-
-PluginManager::PluginManager()
-{
-}
-
-void PluginManager::loadPlugins()
-{
- QDir pluginsDir(qApp->applicationDirPath());
-
-#if defined(Q_OS_WIN)
- if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
- pluginsDir.cdUp();
-#elif defined(Q_OS_MAC)
- if (pluginsDir.dirName() == "MacOS") {
- pluginsDir.cdUp();
- pluginsDir.cdUp();
- pluginsDir.cdUp();
- }
-#endif
- pluginsDir.cd("plugins");
-
- qDebug() << "Plugins Directory: " << pluginsDir;
- foreach (const QString& fileName, pluginsDir.entryList(QDir::Files)) {
- QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
- QObject *plugin = loader.instance();
- if(!plugin)
- continue;
-
- if (ISocialNetworkIntegration *social = qobject_cast<ISocialNetworkIntegration*>(plugin)) {
- qDebug() << "Adding the plugin: " << social->socialNetworkName();
- _socialNetworks.push_back(social);
- }
- }
-}
-
-QList<ISocialNetworkIntegration*> PluginManager::socialNetworkIntegrationPlugins() const
-{
- return _socialNetworks;
-}
diff --git a/subsurface-core/pluginmanager.h b/subsurface-core/pluginmanager.h
deleted file mode 100644
index 3f43b5db1..000000000
--- a/subsurface-core/pluginmanager.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef PLUGINMANAGER_H
-#define PLUGINMANAGER_H
-
-#include <QObject>
-
-#include "isocialnetworkintegration.h"
-
-class PluginManager {
-public:
- static PluginManager& instance();
- void loadPlugins();
- QList<ISocialNetworkIntegration*> socialNetworkIntegrationPlugins() const;
-private:
- PluginManager();
- PluginManager(const PluginManager&);
- PluginManager& operator=(const PluginManager&);
-};
-
-#endif
diff --git a/subsurface-core/pref.h b/subsurface-core/pref.h
deleted file mode 100644
index be684fd90..000000000
--- a/subsurface-core/pref.h
+++ /dev/null
@@ -1,172 +0,0 @@
-#ifndef PREF_H
-#define PREF_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "units.h"
-#include "taxonomy.h"
-
-/* can't use 'bool' for the boolean values - different size in C and C++ */
-typedef struct
-{
- short po2;
- short pn2;
- short phe;
- double po2_threshold;
- double pn2_threshold;
- double phe_threshold;
-} partial_pressure_graphs_t;
-
-typedef struct {
- char *access_token;
- char *user_id;
- char *album_id;
-} facebook_prefs_t;
-
-typedef struct {
- bool enable_geocoding;
- bool parse_dive_without_gps;
- bool tag_existing_dives;
- enum taxonomy_category category[3];
-} geocoding_prefs_t;
-
-typedef struct {
- const char *language;
- bool use_system_language;
-} locale_prefs_t;
-
-enum deco_mode {
- BUEHLMANN,
- RECREATIONAL,
- VPMB
-};
-
-struct preferences {
- const char *divelist_font;
- const char *default_filename;
- const char *default_cylinder;
- const char *cloud_base_url;
- const char *cloud_git_url;
- const char *time_format;
- const char *date_format;
- const char *date_format_short;
- bool time_format_override;
- bool date_format_override;
- double font_size;
- partial_pressure_graphs_t pp_graphs;
- short mod;
- double modpO2;
- short ead;
- short dcceiling;
- short redceiling;
- short calcceiling;
- short calcceiling3m;
- short calcalltissues;
- short calcndltts;
- short gflow;
- short gfhigh;
- int animation_speed;
- bool gf_low_at_maxdepth;
- bool show_ccr_setpoint;
- bool show_ccr_sensors;
- short display_invalid_dives;
- short unit_system;
- struct units units;
- bool coordinates_traditional;
- short show_sac;
- short display_unused_tanks;
- short show_average_depth;
- short zoomed_plot;
- short hrgraph;
- short percentagegraph;
- short rulergraph;
- short tankbar;
- short save_userid_local;
- char *userid;
- int ascrate75; // All rates in mm / sec
- int ascrate50;
- int ascratestops;
- int ascratelast6m;
- int descrate;
- int bottompo2;
- int decopo2;
- int proxy_type;
- char *proxy_host;
- int proxy_port;
- short proxy_auth;
- char *proxy_user;
- char *proxy_pass;
- bool doo2breaks;
- bool drop_stone_mode;
- bool last_stop; // At 6m?
- bool verbatim_plan;
- bool display_runtime;
- bool display_duration;
- bool display_transitions;
- bool safetystop;
- bool switch_at_req_stop;
- int reserve_gas;
- int min_switch_duration; // seconds
- int bottomsac;
- int decosac;
- int o2consumption; // ml per min
- int pscr_ratio; // dump ratio times 1000
- int defaultsetpoint; // default setpoint in mbar
- bool show_pictures_in_profile;
- bool use_default_file;
- short default_file_behavior;
- facebook_prefs_t facebook;
- char *cloud_storage_password;
- char *cloud_storage_newpassword;
- char *cloud_storage_email;
- char *cloud_storage_email_encoded;
- bool save_password_local;
- short cloud_verification_status;
- bool cloud_background_sync;
- geocoding_prefs_t geocoding;
- enum deco_mode deco_mode;
- short conservatism_level;
- int time_threshold;
- int distance_threshold;
- bool git_local_only;
- locale_prefs_t locale; //: TODO: move the rest of locale based info here.
-};
-enum unit_system_values {
- METRIC,
- IMPERIAL,
- PERSONALIZE
-};
-
-enum def_file_behavior {
- UNDEFINED_DEFAULT_FILE,
- LOCAL_DEFAULT_FILE,
- NO_DEFAULT_FILE,
- CLOUD_DEFAULT_FILE
-};
-
-enum cloud_status {
- CS_UNKNOWN,
- CS_INCORRECT_USER_PASSWD,
- CS_NEED_TO_VERIFY,
- CS_VERIFIED
-};
-
-extern struct preferences prefs, default_prefs, informational_prefs;
-
-#define PP_GRAPHS_ENABLED (prefs.pp_graphs.po2 || prefs.pp_graphs.pn2 || prefs.pp_graphs.phe)
-
-extern const char *system_divelist_default_font;
-extern double system_divelist_default_font_size;
-
-extern const char *system_default_directory(void);
-extern const char *system_default_filename();
-extern bool subsurface_ignore_font(const char *font);
-extern void subsurface_OS_pref_setup();
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // PREF_H
diff --git a/subsurface-core/prefs-macros.h b/subsurface-core/prefs-macros.h
deleted file mode 100644
index fe459d3da..000000000
--- a/subsurface-core/prefs-macros.h
+++ /dev/null
@@ -1,68 +0,0 @@
-#ifndef PREFSMACROS_H
-#define PREFSMACROS_H
-
-#define SB(V, B) s.setValue(V, (int)(B->isChecked() ? 1 : 0))
-
-#define GET_UNIT(name, field, f, t) \
- v = s.value(QString(name)); \
- if (v.isValid()) \
- prefs.units.field = (v.toInt() == (t)) ? (t) : (f); \
- else \
- prefs.units.field = default_prefs.units.field
-
-#define GET_BOOL(name, field) \
- v = s.value(QString(name)); \
- if (v.isValid()) \
- prefs.field = v.toBool(); \
- else \
- prefs.field = default_prefs.field
-
-#define GET_DOUBLE(name, field) \
- v = s.value(QString(name)); \
- if (v.isValid()) \
- prefs.field = v.toDouble(); \
- else \
- prefs.field = default_prefs.field
-
-#define GET_INT(name, field) \
- v = s.value(QString(name)); \
- if (v.isValid()) \
- prefs.field = v.toInt(); \
- else \
- prefs.field = default_prefs.field
-
-#define GET_ENUM(name, type, field) \
- v = s.value(QString(name)); \
- if (v.isValid()) \
- prefs.field = (enum type)v.toInt(); \
- else \
- prefs.field = default_prefs.field
-
-#define GET_INT_DEF(name, field, defval) \
- v = s.value(QString(name)); \
- if (v.isValid()) \
- prefs.field = v.toInt(); \
- else \
- prefs.field = defval
-
-#define GET_TXT(name, field) \
- v = s.value(QString(name)); \
- if (v.isValid()) \
- prefs.field = strdup(v.toString().toUtf8().constData()); \
- else \
- prefs.field = default_prefs.field
-
-#define SAVE_OR_REMOVE_SPECIAL(_setting, _default, _compare, _value) \
- if (_compare != _default) \
- s.setValue(_setting, _value); \
- else \
- s.remove(_setting)
-
-#define SAVE_OR_REMOVE(_setting, _default, _value) \
- if (_value != _default) \
- s.setValue(_setting, _value); \
- else \
- s.remove(_setting)
-
-#endif // PREFSMACROS_H
-
diff --git a/subsurface-core/profile.c b/subsurface-core/profile.c
deleted file mode 100644
index 6576f6453..000000000
--- a/subsurface-core/profile.c
+++ /dev/null
@@ -1,1544 +0,0 @@
-/* profile.c */
-/* creates all the necessary data for drawing the dive profile
- */
-#include "gettext.h"
-#include <limits.h>
-#include <string.h>
-#include <assert.h>
-
-#include "dive.h"
-#include "display.h"
-#include "divelist.h"
-
-#include "profile.h"
-#include "gaspressures.h"
-#include "deco.h"
-#include "libdivecomputer/parser.h"
-#include "libdivecomputer/version.h"
-#include "membuffer.h"
-
-//#define DEBUG_GAS 1
-
-#define MAX_PROFILE_DECO 7200
-
-
-int selected_dive = -1; /* careful: 0 is a valid value */
-unsigned int dc_number = 0;
-
-static struct plot_data *last_pi_entry_new = NULL;
-void populate_pressure_information(struct dive *, struct divecomputer *, struct plot_info *, int);
-
-extern bool in_planner();
-extern pressure_t first_ceiling_pressure;
-
-#ifdef DEBUG_PI
-/* debugging tool - not normally used */
-static void dump_pi(struct plot_info *pi)
-{
- int i;
-
- printf("pi:{nr:%d maxtime:%d meandepth:%d maxdepth:%d \n"
- " maxpressure:%d mintemp:%d maxtemp:%d\n",
- pi->nr, pi->maxtime, pi->meandepth, pi->maxdepth,
- pi->maxpressure, pi->mintemp, pi->maxtemp);
- for (i = 0; i < pi->nr; i++) {
- struct plot_data *entry = &pi->entry[i];
- printf(" entry[%d]:{cylinderindex:%d sec:%d pressure:{%d,%d}\n"
- " time:%d:%02d temperature:%d depth:%d stopdepth:%d stoptime:%d ndl:%d smoothed:%d po2:%lf phe:%lf pn2:%lf sum-pp %lf}\n",
- i, entry->cylinderindex, entry->sec,
- entry->pressure[0], entry->pressure[1],
- entry->sec / 60, entry->sec % 60,
- entry->temperature, entry->depth, entry->stopdepth, entry->stoptime, entry->ndl, entry->smoothed,
- entry->pressures.o2, entry->pressures.he, entry->pressures.n2,
- entry->pressures.o2 + entry->pressures.he + entry->pressures.n2);
- }
- printf(" }\n");
-}
-#endif
-
-#define ROUND_UP(x, y) ((((x) + (y) - 1) / (y)) * (y))
-#define DIV_UP(x, y) (((x) + (y) - 1) / (y))
-
-/*
- * When showing dive profiles, we scale things to the
- * current dive. However, we don't scale past less than
- * 30 minutes or 90 ft, just so that small dives show
- * up as such unless zoom is enabled.
- * We also need to add 180 seconds at the end so the min/max
- * plots correctly
- */
-int get_maxtime(struct plot_info *pi)
-{
- int seconds = pi->maxtime;
-
- int DURATION_THR = (pi->dive_type == FREEDIVING ? 60 : 600);
- int CEILING = (pi->dive_type == FREEDIVING ? 30 : 60);
-
- if (prefs.zoomed_plot) {
- /* Rounded up to one minute, with at least 2.5 minutes to
- * spare.
- * For dive times shorter than 10 minutes, we use seconds/4 to
- * calculate the space dynamically.
- * This is seamless since 600/4 = 150.
- */
- if (seconds < DURATION_THR)
- return ROUND_UP(seconds + seconds / 4, CEILING);
- else
- return ROUND_UP(seconds + DURATION_THR/4, CEILING);
- } else {
-#ifndef SUBSURFACE_MOBILE
- /* min 30 minutes, rounded up to 5 minutes, with at least 2.5 minutes to spare */
- return MAX(30 * 60, ROUND_UP(seconds + DURATION_THR/4, CEILING * 5));
-#else
- /* just add 2.5 minutes so we have a consistant right margin */
- return seconds + DURATION_THR / 4;
-#endif
- }
-}
-
-/* get the maximum depth to which we want to plot
- * take into account the additional vertical space needed to plot
- * partial pressure graphs */
-int get_maxdepth(struct plot_info *pi)
-{
- unsigned mm = pi->maxdepth;
- int md;
-
- if (prefs.zoomed_plot) {
- /* Rounded up to 10m, with at least 3m to spare */
- md = ROUND_UP(mm + 3000, 10000);
- } else {
- /* Minimum 30m, rounded up to 10m, with at least 3m to spare */
- md = MAX((unsigned)30000, ROUND_UP(mm + 3000, 10000));
- }
- md += pi->maxpp * 9000;
- return md;
-}
-
-/* collect all event names and whether we display them */
-struct ev_select *ev_namelist;
-int evn_allocated;
-int evn_used;
-
-#if WE_DONT_USE_THIS /* we need to implement event filters in Qt */
-int evn_foreach (void (*callback)(const char *, bool *, void *), void *data) {
- int i;
-
- for (i = 0; i < evn_used; i++) {
- /* here we display an event name on screen - so translate */
- callback(translate("gettextFromC", ev_namelist[i].ev_name), &ev_namelist[i].plot_ev, data);
- }
- return i;
-}
-#endif /* WE_DONT_USE_THIS */
-
-void clear_events(void)
-{
- for (int i = 0; i < evn_used; i++)
- free(ev_namelist[i].ev_name);
- evn_used = 0;
-}
-
-void remember_event(const char *eventname)
-{
- int i = 0, len;
-
- if (!eventname || (len = strlen(eventname)) == 0)
- return;
- while (i < evn_used) {
- if (!strncmp(eventname, ev_namelist[i].ev_name, len))
- return;
- i++;
- }
- if (evn_used == evn_allocated) {
- evn_allocated += 10;
- ev_namelist = realloc(ev_namelist, evn_allocated * sizeof(struct ev_select));
- if (!ev_namelist)
- /* we are screwed, but let's just bail out */
- return;
- }
- ev_namelist[evn_used].ev_name = strdup(eventname);
- ev_namelist[evn_used].plot_ev = true;
- evn_used++;
-}
-
-/* UNUSED! */
-static int get_local_sac(struct plot_data *entry1, struct plot_data *entry2, struct dive *dive) __attribute__((unused));
-
-/* Get local sac-rate (in ml/min) between entry1 and entry2 */
-static int get_local_sac(struct plot_data *entry1, struct plot_data *entry2, struct dive *dive)
-{
- int index = entry1->cylinderindex;
- cylinder_t *cyl;
- int duration = entry2->sec - entry1->sec;
- int depth, airuse;
- pressure_t a, b;
- double atm;
-
- if (entry2->cylinderindex != index)
- return 0;
- if (duration <= 0)
- return 0;
- a.mbar = GET_PRESSURE(entry1);
- b.mbar = GET_PRESSURE(entry2);
- if (!b.mbar || a.mbar <= b.mbar)
- return 0;
-
- /* Mean pressure in ATM */
- depth = (entry1->depth + entry2->depth) / 2;
- atm = depth_to_atm(depth, dive);
-
- cyl = dive->cylinder + index;
-
- airuse = gas_volume(cyl, a) - gas_volume(cyl, b);
-
- /* milliliters per minute */
- return airuse / atm * 60 / duration;
-}
-
-static void analyze_plot_info_minmax_minute(struct plot_data *entry, struct plot_data *first, struct plot_data *last, int index)
-{
- struct plot_data *p = entry;
- int time = entry->sec;
- int seconds = 90 * (index + 1);
- struct plot_data *min, *max;
- int avg, nr;
-
- /* Go back 'seconds' in time */
- while (p > first) {
- if (p[-1].sec < time - seconds)
- break;
- p--;
- }
-
- /* Then go forward until we hit an entry past the time */
- min = max = p;
- avg = p->depth;
- nr = 1;
- while (++p < last) {
- int depth = p->depth;
- if (p->sec > time + seconds)
- break;
- avg += depth;
- nr++;
- if (depth < min->depth)
- min = p;
- if (depth > max->depth)
- max = p;
- }
- entry->min[index] = min;
- entry->max[index] = max;
- entry->avg[index] = (avg + nr / 2) / nr;
-}
-
-static void analyze_plot_info_minmax(struct plot_data *entry, struct plot_data *first, struct plot_data *last)
-{
- analyze_plot_info_minmax_minute(entry, first, last, 0);
- analyze_plot_info_minmax_minute(entry, first, last, 1);
- analyze_plot_info_minmax_minute(entry, first, last, 2);
-}
-
-static velocity_t velocity(int speed)
-{
- velocity_t v;
-
- if (speed < -304) /* ascent faster than -60ft/min */
- v = CRAZY;
- else if (speed < -152) /* above -30ft/min */
- v = FAST;
- else if (speed < -76) /* -15ft/min */
- v = MODERATE;
- else if (speed < -25) /* -5ft/min */
- v = SLOW;
- else if (speed < 25) /* very hard to find data, but it appears that the recommendations
- for descent are usually about 2x ascent rate; still, we want
- stable to mean stable */
- v = STABLE;
- else if (speed < 152) /* between 5 and 30ft/min is considered slow */
- v = SLOW;
- else if (speed < 304) /* up to 60ft/min is moderate */
- v = MODERATE;
- else if (speed < 507) /* up to 100ft/min is fast */
- v = FAST;
- else /* more than that is just crazy - you'll blow your ears out */
- v = CRAZY;
-
- return v;
-}
-
-struct plot_info *analyze_plot_info(struct plot_info *pi)
-{
- int i;
- int nr = pi->nr;
-
- /* Smoothing function: 5-point triangular smooth */
- for (i = 2; i < nr; i++) {
- struct plot_data *entry = pi->entry + i;
- int depth;
-
- if (i < nr - 2) {
- depth = entry[-2].depth + 2 * entry[-1].depth + 3 * entry[0].depth + 2 * entry[1].depth + entry[2].depth;
- entry->smoothed = (depth + 4) / 9;
- }
- /* vertical velocity in mm/sec */
- /* Linus wants to smooth this - let's at least look at the samples that aren't FAST or CRAZY */
- if (entry[0].sec - entry[-1].sec) {
- entry->speed = (entry[0].depth - entry[-1].depth) / (entry[0].sec - entry[-1].sec);
- entry->velocity = velocity(entry->speed);
- /* if our samples are short and we aren't too FAST*/
- if (entry[0].sec - entry[-1].sec < 15 && entry->velocity < FAST) {
- int past = -2;
- while (i + past > 0 && entry[0].sec - entry[past].sec < 15)
- past--;
- entry->velocity = velocity((entry[0].depth - entry[past].depth) /
- (entry[0].sec - entry[past].sec));
- }
- } else {
- entry->velocity = STABLE;
- entry->speed = 0;
- }
- }
-
- /* One-, two- and three-minute minmax data */
- for (i = 0; i < nr; i++) {
- struct plot_data *entry = pi->entry + i;
- analyze_plot_info_minmax(entry, pi->entry, pi->entry + nr);
- }
-
- return pi;
-}
-
-/*
- * If the event has an explicit cylinder index,
- * we return that. If it doesn't, we return the best
- * match based on the gasmix.
- *
- * Some dive computers give cylinder indexes, some
- * give just the gas mix.
- */
-int get_cylinder_index(struct dive *dive, struct event *ev)
-{
- int i;
- int best = 0, score = INT_MAX;
- int target_o2, target_he;
- struct gasmix *g;
-
- if (ev->gas.index >= 0)
- return ev->gas.index;
-
- g = get_gasmix_from_event(ev);
- target_o2 = get_o2(g);
- target_he = get_he(g);
-
- /*
- * Try to find a cylinder that best matches the target gas
- * mix.
- */
- for (i = 0; i < MAX_CYLINDERS; i++) {
- cylinder_t *cyl = dive->cylinder + i;
- int delta_o2, delta_he, distance;
-
- if (cylinder_nodata(cyl))
- continue;
-
- delta_o2 = get_o2(&cyl->gasmix) - target_o2;
- delta_he = get_he(&cyl->gasmix) - target_he;
- distance = delta_o2 * delta_o2;
- distance += delta_he * delta_he;
-
- if (distance >= score)
- continue;
- score = distance;
- best = i;
- }
- return best;
-}
-
-struct event *get_next_event(struct event *event, const char *name)
-{
- if (!name || !*name)
- return NULL;
- while (event) {
- if (!strcmp(event->name, name))
- return event;
- event = event->next;
- }
- return event;
-}
-
-static int count_events(struct divecomputer *dc)
-{
- int result = 0;
- struct event *ev = dc->events;
- while (ev != NULL) {
- result++;
- ev = ev->next;
- }
- return result;
-}
-
-static int set_cylinder_index(struct plot_info *pi, int i, int cylinderindex, int end)
-{
- while (i < pi->nr) {
- struct plot_data *entry = pi->entry + i;
- if (entry->sec > end)
- break;
- if (entry->cylinderindex != cylinderindex) {
- entry->cylinderindex = cylinderindex;
- entry->pressure[0] = 0;
- }
- i++;
- }
- return i;
-}
-
-static int set_setpoint(struct plot_info *pi, int i, int setpoint, int end)
-{
- while (i < pi->nr) {
- struct plot_data *entry = pi->entry + i;
- if (entry->sec > end)
- break;
- entry->o2pressure.mbar = setpoint;
- i++;
- }
- return i;
-}
-
-/* normally the first cylinder has index 0... if not, we need to fix this up here */
-static int set_first_cylinder_index(struct plot_info *pi, int i, int cylinderindex, int end)
-{
- while (i < pi->nr) {
- struct plot_data *entry = pi->entry + i;
- if (entry->sec > end)
- break;
- entry->cylinderindex = cylinderindex;
- i++;
- }
- return i;
-}
-
-static void check_gas_change_events(struct dive *dive, struct divecomputer *dc, struct plot_info *pi)
-{
- int i = 0, cylinderindex = 0;
- struct event *ev = get_next_event(dc->events, "gaschange");
-
- // for dive computers that tell us their first gas as an event on the first sample
- // we need to make sure things are setup correctly
- cylinderindex = explicit_first_cylinder(dive, dc);
- set_first_cylinder_index(pi, 0, cylinderindex, INT_MAX);
-
- if (!ev)
- return;
-
- do {
- i = set_cylinder_index(pi, i, cylinderindex, ev->time.seconds);
- cylinderindex = get_cylinder_index(dive, ev);
- ev = get_next_event(ev->next, "gaschange");
- } while (ev);
- set_cylinder_index(pi, i, cylinderindex, INT_MAX);
-}
-
-static void check_setpoint_events(struct dive *dive, struct divecomputer *dc, struct plot_info *pi)
-{
- int i = 0;
- pressure_t setpoint;
- (void) dive;
- setpoint.mbar = 0;
- struct event *ev = get_next_event(dc->events, "SP change");
-
- if (!ev)
- return;
-
- do {
- i = set_setpoint(pi, i, setpoint.mbar, ev->time.seconds);
- setpoint.mbar = ev->value;
- if (setpoint.mbar)
- dc->divemode = CCR;
- ev = get_next_event(ev->next, "SP change");
- } while (ev);
- set_setpoint(pi, i, setpoint.mbar, INT_MAX);
-}
-
-
-struct plot_info calculate_max_limits_new(struct dive *dive, struct divecomputer *given_dc)
-{
- struct divecomputer *dc = &(dive->dc);
- bool seen = false;
- static struct plot_info pi;
- int maxdepth = dive->maxdepth.mm;
- unsigned int maxtime = 0;
- int maxpressure = 0, minpressure = INT_MAX;
- int maxhr = 0, minhr = INT_MAX;
- int mintemp = dive->mintemp.mkelvin;
- int maxtemp = dive->maxtemp.mkelvin;
- int cyl;
-
- /* Get the per-cylinder maximum pressure if they are manual */
- for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) {
- int mbar = dive->cylinder[cyl].start.mbar;
- if (mbar > maxpressure)
- maxpressure = mbar;
- if (mbar < minpressure)
- minpressure = mbar;
- }
-
- /* Then do all the samples from all the dive computers */
- do {
- if (dc == given_dc)
- seen = true;
- int i = dc->samples;
- int lastdepth = 0;
- struct sample *s = dc->sample;
-
- while (--i >= 0) {
- int depth = s->depth.mm;
- int pressure = s->cylinderpressure.mbar;
- int temperature = s->temperature.mkelvin;
- int heartbeat = s->heartbeat;
-
- if (!mintemp && temperature < mintemp)
- mintemp = temperature;
- if (temperature > maxtemp)
- maxtemp = temperature;
-
- if (pressure && pressure < minpressure)
- minpressure = pressure;
- if (pressure > maxpressure)
- maxpressure = pressure;
- if (heartbeat > maxhr)
- maxhr = heartbeat;
- if (heartbeat < minhr)
- minhr = heartbeat;
-
- if (depth > maxdepth)
- maxdepth = s->depth.mm;
- if ((depth > SURFACE_THRESHOLD || lastdepth > SURFACE_THRESHOLD) &&
- s->time.seconds > maxtime)
- maxtime = s->time.seconds;
- lastdepth = depth;
- s++;
- }
- dc = dc->next;
- if (dc == NULL && !seen) {
- dc = given_dc;
- seen = true;
- }
- } while (dc != NULL);
-
- if (minpressure > maxpressure)
- minpressure = 0;
- if (minhr > maxhr)
- minhr = 0;
-
- memset(&pi, 0, sizeof(pi));
- pi.maxdepth = maxdepth;
- pi.maxtime = maxtime;
- pi.maxpressure = maxpressure;
- pi.minpressure = minpressure;
- pi.minhr = minhr;
- pi.maxhr = maxhr;
- pi.mintemp = mintemp;
- pi.maxtemp = maxtemp;
- return pi;
-}
-
-/* copy the previous entry (we know this exists), update time and depth
- * and zero out the sensor pressure (since this is a synthetic entry)
- * increment the entry pointer and the count of synthetic entries. */
-#define INSERT_ENTRY(_time, _depth, _sac) \
- *entry = entry[-1]; \
- entry->sec = _time; \
- entry->depth = _depth; \
- entry->running_sum = (entry - 1)->running_sum + (_time - (entry - 1)->sec) * (_depth + (entry - 1)->depth) / 2; \
- SENSOR_PRESSURE(entry) = 0; \
- entry->sac = _sac; \
- entry++; \
- idx++
-
-struct plot_data *populate_plot_entries(struct dive *dive, struct divecomputer *dc, struct plot_info *pi)
-{
-
- int idx, maxtime, nr, i;
- int lastdepth, lasttime, lasttemp = 0;
- struct plot_data *plot_data;
- struct event *ev = dc->events;
- (void) dive;
- maxtime = pi->maxtime;
-
- /*
- * We want to have a plot_info event at least every 10s (so "maxtime/10+1"),
- * but samples could be more dense than that (so add in dc->samples). We also
- * need to have one for every event (so count events and add that) and
- * additionally we want two surface events around the whole thing (thus the
- * additional 4). There is also one extra space for a final entry
- * that has time > maxtime (because there can be surface samples
- * past "maxtime" in the original sample data)
- */
- nr = dc->samples + 6 + maxtime / 10 + count_events(dc);
- plot_data = calloc(nr, sizeof(struct plot_data));
- pi->entry = plot_data;
- if (!plot_data)
- return NULL;
- pi->nr = nr;
- idx = 2; /* the two extra events at the start */
-
- lastdepth = 0;
- lasttime = 0;
- /* skip events at time = 0 */
- while (ev && ev->time.seconds == 0)
- ev = ev->next;
- for (i = 0; i < dc->samples; i++) {
- struct plot_data *entry = plot_data + idx;
- struct sample *sample = dc->sample + i;
- int time = sample->time.seconds;
- int offset, delta;
- int depth = sample->depth.mm;
- int sac = sample->sac.mliter;
-
- /* Add intermediate plot entries if required */
- delta = time - lasttime;
- if (delta <= 0) {
- time = lasttime;
- delta = 1; // avoid divide by 0
- }
- for (offset = 10; offset < delta; offset += 10) {
- if (lasttime + offset > maxtime)
- break;
-
- /* Add events if they are between plot entries */
- while (ev && (int)ev->time.seconds < lasttime + offset) {
- INSERT_ENTRY(ev->time.seconds, interpolate(lastdepth, depth, ev->time.seconds - lasttime, delta), sac);
- ev = ev->next;
- }
-
- /* now insert the time interpolated entry */
- INSERT_ENTRY(lasttime + offset, interpolate(lastdepth, depth, offset, delta), sac);
-
- /* skip events that happened at this time */
- while (ev && (int)ev->time.seconds == lasttime + offset)
- ev = ev->next;
- }
-
- /* Add events if they are between plot entries */
- while (ev && (int)ev->time.seconds < time) {
- INSERT_ENTRY(ev->time.seconds, interpolate(lastdepth, depth, ev->time.seconds - lasttime, delta), sac);
- ev = ev->next;
- }
-
-
- entry->sec = time;
- entry->depth = depth;
-
- entry->running_sum = (entry - 1)->running_sum + (time - (entry - 1)->sec) * (depth + (entry - 1)->depth) / 2;
- entry->stopdepth = sample->stopdepth.mm;
- entry->stoptime = sample->stoptime.seconds;
- entry->ndl = sample->ndl.seconds;
- entry->tts = sample->tts.seconds;
- pi->has_ndl |= sample->ndl.seconds;
- entry->in_deco = sample->in_deco;
- entry->cns = sample->cns;
- if (dc->divemode == CCR) {
- entry->o2pressure.mbar = entry->o2setpoint.mbar = sample->setpoint.mbar; // for rebreathers
- entry->o2sensor[0].mbar = sample->o2sensor[0].mbar; // for up to three rebreather O2 sensors
- entry->o2sensor[1].mbar = sample->o2sensor[1].mbar;
- entry->o2sensor[2].mbar = sample->o2sensor[2].mbar;
- } else {
- entry->pressures.o2 = sample->setpoint.mbar / 1000.0;
- }
- /* FIXME! sensor index -> cylinder index translation! */
- // entry->cylinderindex = sample->sensor;
- SENSOR_PRESSURE(entry) = sample->cylinderpressure.mbar;
- O2CYLINDER_PRESSURE(entry) = sample->o2cylinderpressure.mbar;
- if (sample->temperature.mkelvin)
- entry->temperature = lasttemp = sample->temperature.mkelvin;
- else
- entry->temperature = lasttemp;
- entry->heartbeat = sample->heartbeat;
- entry->bearing = sample->bearing.degrees;
- entry->sac = sample->sac.mliter;
- if (sample->rbt.seconds)
- entry->rbt = sample->rbt.seconds;
- /* skip events that happened at this time */
- while (ev && (int)ev->time.seconds == time)
- ev = ev->next;
- lasttime = time;
- lastdepth = depth;
- idx++;
-
- if (time > maxtime)
- break;
- }
-
- /* Add two final surface events */
- plot_data[idx++].sec = lasttime + 1;
- plot_data[idx++].sec = lasttime + 2;
- pi->nr = idx;
-
- return plot_data;
-}
-
-#undef INSERT_ENTRY
-
-static void populate_cylinder_pressure_data(int idx, int start, int end, struct plot_info *pi, bool o2flag)
-{
- int i;
-
- /* First: check that none of the entries has sensor pressure for this cylinder index */
- for (i = 0; i < pi->nr; i++) {
- struct plot_data *entry = pi->entry + i;
- if (entry->cylinderindex != idx && !o2flag)
- continue;
- if (CYLINDER_PRESSURE(o2flag, entry))
- return;
- }
-
- /* Then: populate the first entry with the beginning cylinder pressure */
- for (i = 0; i < pi->nr; i++) {
- struct plot_data *entry = pi->entry + i;
- if (entry->cylinderindex != idx && !o2flag)
- continue;
- if (o2flag)
- O2CYLINDER_PRESSURE(entry) = start;
- else
- SENSOR_PRESSURE(entry) = start;
- break;
- }
-
- /* .. and the last entry with the ending cylinder pressure */
- for (i = pi->nr; --i >= 0; /* nothing */) {
- struct plot_data *entry = pi->entry + i;
- if (entry->cylinderindex != idx && !o2flag)
- continue;
- if (o2flag)
- O2CYLINDER_PRESSURE(entry) = end;
- else
- SENSOR_PRESSURE(entry) = end;
- break;
- }
-}
-
-/*
- * Calculate the sac rate between the two plot entries 'first' and 'last'.
- *
- * Everything in between has a cylinder pressure, and it's all the same
- * cylinder.
- */
-static int sac_between(struct dive *dive, struct plot_data *first, struct plot_data *last)
-{
- int airuse;
- double pressuretime;
- pressure_t a, b;
- cylinder_t *cyl;
-
- if (first == last)
- return 0;
-
- /* Calculate air use - trivial */
- a.mbar = GET_PRESSURE(first);
- b.mbar = GET_PRESSURE(last);
- cyl = dive->cylinder + first->cylinderindex;
- airuse = gas_volume(cyl, a) - gas_volume(cyl, b);
- if (airuse <= 0)
- return 0;
-
- /* Calculate depthpressure integrated over time */
- pressuretime = 0.0;
- do {
- int depth = (first[0].depth + first[1].depth) / 2;
- int time = first[1].sec - first[0].sec;
- double atm = depth_to_atm(depth, dive);
-
- pressuretime += atm * time;
- } while (++first < last);
-
- /* Turn "atmseconds" into "atmminutes" */
- pressuretime /= 60;
-
- /* SAC = mliter per minute */
- return rint(airuse / pressuretime);
-}
-
-/*
- * Try to do the momentary sac rate for this entry, averaging over one
- * minute.
- */
-static void fill_sac(struct dive *dive, struct plot_info *pi, int idx)
-{
- struct plot_data *entry = pi->entry + idx;
- struct plot_data *first, *last;
- int time;
-
- if (entry->sac)
- return;
-
- if (!GET_PRESSURE(entry))
- return;
-
- /*
- * Try to go back 30 seconds to get 'first'.
- * Stop if the sensor changed, or if we went back too far.
- */
- first = entry;
- time = entry->sec - 30;
- while (idx > 0) {
- struct plot_data *prev = first-1;
- if (prev->cylinderindex != first->cylinderindex)
- break;
- if (prev->depth < SURFACE_THRESHOLD && first->depth < SURFACE_THRESHOLD)
- break;
- if (prev->sec < time)
- break;
- if (!GET_PRESSURE(prev))
- break;
- idx--;
- first = prev;
- }
-
- /* Now find an entry a minute after the first one */
- last = first;
- time = first->sec + 60;
- while (++idx < pi->nr) {
- struct plot_data *next = last+1;
- if (next->cylinderindex != last->cylinderindex)
- break;
- if (next->depth < SURFACE_THRESHOLD && last->depth < SURFACE_THRESHOLD)
- break;
- if (next->sec > time)
- break;
- if (!GET_PRESSURE(next))
- break;
- last = next;
- }
-
- /* Ok, now calculate the SAC between 'first' and 'last' */
- entry->sac = sac_between(dive, first, last);
-}
-
-static void calculate_sac(struct dive *dive, struct plot_info *pi)
-{
- for (int i = 0; i < pi->nr; i++)
- fill_sac(dive, pi, i);
-}
-
-static void populate_secondary_sensor_data(struct divecomputer *dc, struct plot_info *pi)
-{
- (void) dc;
- (void) pi;
- /* We should try to see if it has interesting pressure data here */
-}
-
-static void setup_gas_sensor_pressure(struct dive *dive, struct divecomputer *dc, struct plot_info *pi)
-{
- int i;
- struct divecomputer *secondary;
-
- /* First, populate the pressures with the manual cylinder data.. */
- for (i = 0; i < MAX_CYLINDERS; i++) {
- cylinder_t *cyl = dive->cylinder + i;
- int start = cyl->start.mbar ?: cyl->sample_start.mbar;
- int end = cyl->end.mbar ?: cyl->sample_end.mbar;
-
- if (!start || !end)
- continue;
-
- populate_cylinder_pressure_data(i, start, end, pi, dive->cylinder[i].cylinder_use == OXYGEN);
- }
-
- /*
- * Here, we should try to walk through all the dive computers,
- * and try to see if they have sensor data different from the
- * primary dive computer (dc).
- */
- secondary = &dive->dc;
- do {
- if (secondary == dc)
- continue;
- populate_secondary_sensor_data(dc, pi);
- } while ((secondary = secondary->next) != NULL);
-}
-
-#ifndef SUBSURFACE_MOBILE
-/* calculate DECO STOP / TTS / NDL */
-static void calculate_ndl_tts(struct plot_data *entry, struct dive *dive, double surface_pressure)
-{
- /* FIXME: This should be configurable */
- /* ascent speed up to first deco stop */
- const int ascent_s_per_step = 1;
- const int ascent_mm_per_step = 200; /* 12 m/min */
- /* ascent speed between deco stops */
- const int ascent_s_per_deco_step = 1;
- const int ascent_mm_per_deco_step = 16; /* 1 m/min */
- /* how long time steps in deco calculations? */
- const int time_stepsize = 60;
- const int deco_stepsize = 3000;
- /* at what depth is the current deco-step? */
- int next_stop = ROUND_UP(deco_allowed_depth(tissue_tolerance_calc(dive, depth_to_bar(entry->depth, dive)),
- surface_pressure, dive, 1), deco_stepsize);
- int ascent_depth = entry->depth;
- /* at what time should we give up and say that we got enuff NDL? */
- int cylinderindex = entry->cylinderindex;
- /* If iterating through a dive, entry->tts_calc needs to be reset */
- entry->tts_calc = 0;
-
- /* If we don't have a ceiling yet, calculate ndl. Don't try to calculate
- * a ndl for lower values than 3m it would take forever */
- if (next_stop == 0) {
- if (entry->depth < 3000) {
- entry->ndl = MAX_PROFILE_DECO;
- return;
- }
- /* stop if the ndl is above max_ndl seconds, and call it plenty of time */
- while (entry->ndl_calc < MAX_PROFILE_DECO && deco_allowed_depth(tissue_tolerance_calc(dive, depth_to_bar(entry->depth, dive)), surface_pressure, dive, 1) <= 0) {
- entry->ndl_calc += time_stepsize;
- add_segment(depth_to_bar(entry->depth, dive),
- &dive->cylinder[cylinderindex].gasmix, time_stepsize, entry->o2pressure.mbar, dive, prefs.bottomsac);
- }
- /* we don't need to calculate anything else */
- return;
- }
-
- /* We are in deco */
- entry->in_deco_calc = true;
-
- /* Add segments for movement to stopdepth */
- for (; ascent_depth > next_stop; ascent_depth -= ascent_mm_per_step, entry->tts_calc += ascent_s_per_step) {
- add_segment(depth_to_bar(ascent_depth, dive),
- &dive->cylinder[cylinderindex].gasmix, ascent_s_per_step, entry->o2pressure.mbar, dive, prefs.decosac);
- next_stop = ROUND_UP(deco_allowed_depth(tissue_tolerance_calc(dive, depth_to_bar(ascent_depth, dive)), surface_pressure, dive, 1), deco_stepsize);
- }
- ascent_depth = next_stop;
-
- /* And how long is the current deco-step? */
- entry->stoptime_calc = 0;
- entry->stopdepth_calc = next_stop;
- next_stop -= deco_stepsize;
-
- /* And how long is the total TTS */
- while (next_stop >= 0) {
- /* save the time for the first stop to show in the graph */
- if (ascent_depth == entry->stopdepth_calc)
- entry->stoptime_calc += time_stepsize;
-
- entry->tts_calc += time_stepsize;
- if (entry->tts_calc > MAX_PROFILE_DECO)
- break;
- add_segment(depth_to_bar(ascent_depth, dive),
- &dive->cylinder[cylinderindex].gasmix, time_stepsize, entry->o2pressure.mbar, dive, prefs.decosac);
-
- if (deco_allowed_depth(tissue_tolerance_calc(dive, depth_to_bar(ascent_depth,dive)), surface_pressure, dive, 1) <= next_stop) {
- /* move to the next stop and add the travel between stops */
- for (; ascent_depth > next_stop; ascent_depth -= ascent_mm_per_deco_step, entry->tts_calc += ascent_s_per_deco_step)
- add_segment(depth_to_bar(ascent_depth, dive),
- &dive->cylinder[cylinderindex].gasmix, ascent_s_per_deco_step, entry->o2pressure.mbar, dive, prefs.decosac);
- ascent_depth = next_stop;
- next_stop -= deco_stepsize;
- }
- }
-}
-
-/* Let's try to do some deco calculations.
- */
-void calculate_deco_information(struct dive *dive, struct divecomputer *dc, struct plot_info *pi, bool print_mode)
-{
- int i, count_iteration = 0;
- double surface_pressure = (dc->surface_pressure.mbar ? dc->surface_pressure.mbar : get_surface_pressure_in_mbar(dive, true)) / 1000.0;
- int last_ndl_tts_calc_time = 0;
- int first_ceiling = 0;
- bool first_iteration = true;
- int final_tts = 0 , time_clear_ceiling = 0, time_deep_ceiling = 0, deco_time = 0, prev_deco_time = 10000000;
- char *cache_data_initial = NULL;
- /* For VPM-B outside the planner, cache the initial deco state for CVA iterations */
- if (prefs.deco_mode == VPMB && !in_planner())
- cache_deco_state(&cache_data_initial);
- /* For VPM-B outside the planner, iterate until deco time converges (usually one or two iterations after the initial)
- * Set maximum number of iterations to 10 just in case */
- while ((abs(prev_deco_time - deco_time) >= 30) && (count_iteration < 10)) {
- for (i = 1; i < pi->nr; i++) {
- struct plot_data *entry = pi->entry + i;
- int j, t0 = (entry - 1)->sec, t1 = entry->sec;
- int time_stepsize = 20;
-
- entry->ambpressure = depth_to_bar(entry->depth, dive);
- entry->gfline = MAX((double)prefs.gflow, (entry->ambpressure - surface_pressure) / (gf_low_pressure_this_dive - surface_pressure) *
- (prefs.gflow - prefs.gfhigh) +
- prefs.gfhigh) *
- (100.0 - AMB_PERCENTAGE) / 100.0 + AMB_PERCENTAGE;
- if (t0 > t1) {
- fprintf(stderr, "non-monotonous dive stamps %d %d\n", t0, t1);
- int xchg = t1;
- t1 = t0;
- t0 = xchg;
- }
- if (t0 != t1 && t1 - t0 < time_stepsize)
- time_stepsize = t1 - t0;
- for (j = t0 + time_stepsize; j <= t1; j += time_stepsize) {
- int depth = interpolate(entry[-1].depth, entry[0].depth, j - t0, t1 - t0);
- add_segment(depth_to_bar(depth, dive),
- &dive->cylinder[entry->cylinderindex].gasmix, time_stepsize, entry->o2pressure.mbar, dive, entry->sac);
- if ((t1 - j < time_stepsize) && (j < t1))
- time_stepsize = t1 - j;
- }
- if (t0 == t1) {
- entry->ceiling = (entry - 1)->ceiling;
- } else {
- /* Keep updating the VPM-B gradients until the start of the ascent phase of the dive. */
- if (prefs.deco_mode == VPMB && !in_planner() && (entry - 1)->ceiling >= first_ceiling && first_iteration == true) {
- nuclear_regeneration(t1);
- vpmb_start_gradient();
- /* For CVA calculations, start by guessing deco time = dive time remaining */
- deco_time = pi->maxtime - t1;
- vpmb_next_gradient(deco_time, surface_pressure / 1000.0);
- }
- entry->ceiling = deco_allowed_depth(tissue_tolerance_calc(dive, depth_to_bar(entry->depth, dive)), surface_pressure, dive, !prefs.calcceiling3m);
- /* If using VPM-B outside the planner, take first_ceiling_pressure as the deepest ceiling */
- if (prefs.deco_mode == VPMB && !in_planner()) {
- if (entry->ceiling >= first_ceiling) {
- time_deep_ceiling = t1;
- first_ceiling = entry->ceiling;
- first_ceiling_pressure.mbar = depth_to_mbar(first_ceiling, dive);
- if (first_iteration) {
- nuclear_regeneration(t1);
- vpmb_start_gradient();
- /* For CVA calculations, start by guessing deco time = dive time remaining */
- deco_time = pi->maxtime - t1;
- vpmb_next_gradient(deco_time, surface_pressure / 1000.0);
- }
- }
- // Use the point where the ceiling clears as the end of deco phase for CVA calculations
- if (entry->ceiling > 0)
- time_clear_ceiling = 0;
- else if (time_clear_ceiling == 0)
- time_clear_ceiling = t1;
- }
- }
- for (j = 0; j < 16; j++) {
- double m_value = buehlmann_inertgas_a[j] + entry->ambpressure / buehlmann_inertgas_b[j];
- entry->ceilings[j] = deco_allowed_depth(tolerated_by_tissue[j], surface_pressure, dive, 1);
- entry->percentages[j] = tissue_inertgas_saturation[j] < entry->ambpressure ?
- tissue_inertgas_saturation[j] / entry->ambpressure * AMB_PERCENTAGE :
- AMB_PERCENTAGE + (tissue_inertgas_saturation[j] - entry->ambpressure) / (m_value - entry->ambpressure) * (100.0 - AMB_PERCENTAGE);
- }
-
- /* should we do more calculations?
- * We don't for print-mode because this info doesn't show up there
- * If the ceiling hasn't cleared by the last data point, we need tts for VPM-B CVA calculation
- * It is not necessary to do these calculation on the first VPMB iteration, except for the last data point */
- if ((prefs.calcndltts && !print_mode && (prefs.deco_mode != VPMB || in_planner() || !first_iteration)) ||
- (prefs.deco_mode == VPMB && !in_planner() && i == pi->nr - 1)) {
- /* only calculate ndl/tts on every 30 seconds */
- if ((entry->sec - last_ndl_tts_calc_time) < 30 && i != pi->nr - 1) {
- struct plot_data *prev_entry = (entry - 1);
- entry->stoptime_calc = prev_entry->stoptime_calc;
- entry->stopdepth_calc = prev_entry->stopdepth_calc;
- entry->tts_calc = prev_entry->tts_calc;
- entry->ndl_calc = prev_entry->ndl_calc;
- continue;
- }
- last_ndl_tts_calc_time = entry->sec;
-
- /* We are going to mess up deco state, so store it for later restore */
- char *cache_data = NULL;
- cache_deco_state(&cache_data);
- calculate_ndl_tts(entry, dive, surface_pressure);
- if (prefs.deco_mode == VPMB && !in_planner() && i == pi->nr - 1)
- final_tts = entry->tts_calc;
- /* Restore "real" deco state for next real time step */
- restore_deco_state(cache_data);
- free(cache_data);
- }
- }
- if (prefs.deco_mode == VPMB && !in_planner()) {
- prev_deco_time = deco_time;
- // Do we need to update deco_time?
- if (final_tts > 0)
- deco_time = pi->maxtime + final_tts - time_deep_ceiling;
- else if (time_clear_ceiling > 0)
- deco_time = time_clear_ceiling - time_deep_ceiling;
- vpmb_next_gradient(deco_time, surface_pressure / 1000.0);
- final_tts = 0;
- last_ndl_tts_calc_time = 0;
- first_ceiling = 0;
- first_iteration = false;
- count_iteration ++;
- restore_deco_state(cache_data_initial);
- } else {
- // With Buhlmann, or not in planner, iterating isn't needed. This makes the while condition false.
- prev_deco_time = deco_time = 0;
- }
- }
- free(cache_data_initial);
-#if DECO_CALC_DEBUG & 1
- dump_tissues();
-#endif
-}
-#endif
-
-/* Function calculate_ccr_po2: This function takes information from one plot_data structure (i.e. one point on
- * the dive profile), containing the oxygen sensor values of a CCR system and, for that plot_data structure,
- * calculates the po2 value from the sensor data. Several rules are applied, depending on how many o2 sensors
- * there are and the differences among the readings from these sensors.
- */
-static int calculate_ccr_po2(struct plot_data *entry, struct divecomputer *dc)
-{
- int sump = 0, minp = 999999, maxp = -999999;
- int diff_limit = 100; // The limit beyond which O2 sensor differences are considered significant (default = 100 mbar)
- int i, np = 0;
-
- for (i = 0; i < dc->no_o2sensors; i++)
- if (entry->o2sensor[i].mbar) { // Valid reading
- ++np;
- sump += entry->o2sensor[i].mbar;
- minp = MIN(minp, entry->o2sensor[i].mbar);
- maxp = MAX(maxp, entry->o2sensor[i].mbar);
- }
- switch (np) {
- case 0: // Uhoh
- return entry->o2pressure.mbar;
- case 1: // Return what we have
- return sump;
- case 2: // Take the average
- return sump / 2;
- case 3: // Voting logic
- if (2 * maxp - sump + minp < diff_limit) { // Upper difference acceptable...
- if (2 * minp - sump + maxp) // ...and lower difference acceptable
- return sump / 3;
- else
- return (sump - minp) / 2;
- } else {
- if (2 * minp - sump + maxp) // ...but lower difference acceptable
- return (sump - maxp) / 2;
- else
- return sump / 3;
- }
- default: // This should not happen
- assert(np <= 3);
- return 0;
- }
-}
-
-static void calculate_gas_information_new(struct dive *dive, struct plot_info *pi)
-{
- int i;
- double amb_pressure;
-
- for (i = 1; i < pi->nr; i++) {
- int fn2, fhe;
- struct plot_data *entry = pi->entry + i;
- int cylinderindex = entry->cylinderindex;
-
- amb_pressure = depth_to_bar(entry->depth, dive);
-
- fill_pressures(&entry->pressures, amb_pressure, &dive->cylinder[cylinderindex].gasmix, entry->o2pressure.mbar / 1000.0, dive->dc.divemode);
- fn2 = (int)(1000.0 * entry->pressures.n2 / amb_pressure);
- fhe = (int)(1000.0 * entry->pressures.he / amb_pressure);
-
- /* Calculate MOD, EAD, END and EADD based on partial pressures calculated before
- * so there is no difference in calculating between OC and CC
- * END takes Oâ‚‚ + Nâ‚‚ (air) into account ("Narcotic" for trimix dives)
- * EAD just uses Nâ‚‚ ("Air" for nitrox dives) */
- pressure_t modpO2 = { .mbar = (int)(prefs.modpO2 * 1000) };
- entry->mod = (double)gas_mod(&dive->cylinder[cylinderindex].gasmix, modpO2, dive, 1).mm;
- entry->end = (entry->depth + 10000) * (1000 - fhe) / 1000.0 - 10000;
- entry->ead = (entry->depth + 10000) * fn2 / (double)N2_IN_AIR - 10000;
- entry->eadd = (entry->depth + 10000) *
- (entry->pressures.o2 / amb_pressure * O2_DENSITY +
- entry->pressures.n2 / amb_pressure * N2_DENSITY +
- entry->pressures.he / amb_pressure * HE_DENSITY) /
- (O2_IN_AIR * O2_DENSITY + N2_IN_AIR * N2_DENSITY) * 1000 - 10000;
- if (entry->mod < 0)
- entry->mod = 0;
- if (entry->ead < 0)
- entry->ead = 0;
- if (entry->end < 0)
- entry->end = 0;
- if (entry->eadd < 0)
- entry->eadd = 0;
- }
-}
-
-void fill_o2_values(struct divecomputer *dc, struct plot_info *pi, struct dive *dive)
-/* In the samples from each dive computer, there may be uninitialised oxygen
- * sensor or setpoint values, e.g. when events were inserted into the dive log
- * or if the dive computer does not report o2 values with every sample. But
- * for drawing the profile a complete series of valid o2 pressure values is
- * required. This function takes the oxygen sensor data and setpoint values
- * from the structures of plotinfo and replaces the zero values with their
- * last known values so that the oxygen sensor data are complete and ready
- * for plotting. This function called by: create_plot_info_new() */
-{
- int i, j;
- pressure_t last_sensor[3], o2pressure;
- pressure_t amb_pressure;
-
- for (i = 0; i < pi->nr; i++) {
- struct plot_data *entry = pi->entry + i;
-
- if (dc->divemode == CCR) {
- if (i == 0) { // For 1st iteration, initialise the last_sensor values
- for (j = 0; j < dc->no_o2sensors; j++)
- last_sensor[j].mbar = pi->entry->o2sensor[j].mbar;
- } else { // Now re-insert the missing oxygen pressure values
- for (j = 0; j < dc->no_o2sensors; j++)
- if (entry->o2sensor[j].mbar)
- last_sensor[j].mbar = entry->o2sensor[j].mbar;
- else
- entry->o2sensor[j].mbar = last_sensor[j].mbar;
- } // having initialised the empty o2 sensor values for this point on the profile,
- amb_pressure.mbar = depth_to_mbar(entry->depth, dive);
- o2pressure.mbar = calculate_ccr_po2(entry, dc); // ...calculate the po2 based on the sensor data
- entry->o2pressure.mbar = MIN(o2pressure.mbar, amb_pressure.mbar);
- } else {
- entry->o2pressure.mbar = 0; // initialise po2 to zero for dctype = OC
- }
- }
-}
-
-#ifdef DEBUG_GAS
-/* A CCR debug function that writes the cylinder pressure and the oxygen values to the file debug_print_profiledata.dat:
- * Called in create_plot_info_new()
- */
-static void debug_print_profiledata(struct plot_info *pi)
-{
- FILE *f1;
- struct plot_data *entry;
- int i;
- if (!(f1 = fopen("debug_print_profiledata.dat", "w"))) {
- printf("File open error for: debug_print_profiledata.dat\n");
- } else {
- fprintf(f1, "id t1 gas gasint t2 t3 dil dilint t4 t5 setpoint sensor1 sensor2 sensor3 t6 po2 fo2\n");
- for (i = 0; i < pi->nr; i++) {
- entry = pi->entry + i;
- fprintf(f1, "%d gas=%8d %8d ; dil=%8d %8d ; o2_sp= %d %d %d %d PO2= %f\n", i, SENSOR_PRESSURE(entry),
- INTERPOLATED_PRESSURE(entry), O2CYLINDER_PRESSURE(entry), INTERPOLATED_O2CYLINDER_PRESSURE(entry),
- entry->o2pressure.mbar, entry->o2sensor[0].mbar, entry->o2sensor[1].mbar, entry->o2sensor[2].mbar, entry->pressures.o2);
- }
- fclose(f1);
- }
-}
-#endif
-
-/*
- * Create a plot-info with smoothing and ranged min/max
- *
- * This also makes sure that we have extra empty events on both
- * sides, so that you can do end-points without having to worry
- * about it.
- */
-void create_plot_info_new(struct dive *dive, struct divecomputer *dc, struct plot_info *pi, bool fast)
-{
- int o2, he, o2max;
-#ifndef SUBSURFACE_MOBILE
- init_decompression(dive);
-#endif
- /* Create the new plot data */
- free((void *)last_pi_entry_new);
-
- get_dive_gas(dive, &o2, &he, &o2max);
- if (dc->divemode == FREEDIVE){
- pi->dive_type = FREEDIVE;
- } else if (he > 0) {
- pi->dive_type = TRIMIX;
- } else {
- if (o2)
- pi->dive_type = NITROX;
- else
- pi->dive_type = AIR;
- }
-
- last_pi_entry_new = populate_plot_entries(dive, dc, pi);
-
- check_gas_change_events(dive, dc, pi); /* Populate the gas index from the gas change events */
- check_setpoint_events(dive, dc, pi); /* Populate setpoints */
- setup_gas_sensor_pressure(dive, dc, pi); /* Try to populate our gas pressure knowledge */
- if (!fast) {
- populate_pressure_information(dive, dc, pi, false); /* .. calculate missing pressure entries for all gasses except o2 */
- if (dc->divemode == CCR) /* For CCR dives.. */
- populate_pressure_information(dive, dc, pi, true); /* .. calculate missing o2 gas pressure entries */
- }
- fill_o2_values(dc, pi, dive); /* .. and insert the O2 sensor data having 0 values. */
- calculate_sac(dive, pi); /* Calculate sac */
-#ifndef SUBSURFACE_MOBILE
- calculate_deco_information(dive, dc, pi, false); /* and ceiling information, using gradient factor values in Preferences) */
-#endif
- calculate_gas_information_new(dive, pi); /* Calculate gas partial pressures */
-
-#ifdef DEBUG_GAS
- debug_print_profiledata(pi);
-#endif
-
- pi->meandepth = dive->dc.meandepth.mm;
- analyze_plot_info(pi);
-}
-
-struct divecomputer *select_dc(struct dive *dive)
-{
- unsigned int max = number_of_computers(dive);
- unsigned int i = dc_number;
-
- /* Reset 'dc_number' if we've switched dives and it is now out of range */
- if (i >= max)
- dc_number = i = 0;
-
- return get_dive_dc(dive, i);
-}
-
-static void plot_string(struct plot_info *pi, struct plot_data *entry, struct membuffer *b, bool has_ndl)
-{
- int pressurevalue, mod, ead, end, eadd;
- const char *depth_unit, *pressure_unit, *temp_unit, *vertical_speed_unit;
- double depthvalue, tempvalue, speedvalue, sacvalue;
- int decimals;
- const char *unit;
-
- depthvalue = get_depth_units(entry->depth, NULL, &depth_unit);
- put_format(b, translate("gettextFromC", "@: %d:%02d\nD: %.1f%s\n"), FRACTION(entry->sec, 60), depthvalue, depth_unit);
- if (GET_PRESSURE(entry)) {
- pressurevalue = get_pressure_units(GET_PRESSURE(entry), &pressure_unit);
- put_format(b, translate("gettextFromC", "P: %d%s\n"), pressurevalue, pressure_unit);
- }
- if (entry->temperature) {
- tempvalue = get_temp_units(entry->temperature, &temp_unit);
- put_format(b, translate("gettextFromC", "T: %.1f%s\n"), tempvalue, temp_unit);
- }
- speedvalue = get_vertical_speed_units(abs(entry->speed), NULL, &vertical_speed_unit);
- /* Ascending speeds are positive, descending are negative */
- if (entry->speed > 0)
- speedvalue *= -1;
- put_format(b, translate("gettextFromC", "V: %.1f%s\n"), speedvalue, vertical_speed_unit);
- sacvalue = get_volume_units(entry->sac, &decimals, &unit);
- if (entry->sac && prefs.show_sac)
- put_format(b, translate("gettextFromC", "SAC: %.*f%s/min\n"), decimals, sacvalue, unit);
- if (entry->cns)
- put_format(b, translate("gettextFromC", "CNS: %u%%\n"), entry->cns);
- if (prefs.pp_graphs.po2)
- put_format(b, translate("gettextFromC", "pO%s: %.2fbar\n"), UTF8_SUBSCRIPT_2, entry->pressures.o2);
- if (prefs.pp_graphs.pn2)
- put_format(b, translate("gettextFromC", "pN%s: %.2fbar\n"), UTF8_SUBSCRIPT_2, entry->pressures.n2);
- if (prefs.pp_graphs.phe)
- put_format(b, translate("gettextFromC", "pHe: %.2fbar\n"), entry->pressures.he);
- if (prefs.mod) {
- mod = (int)get_depth_units(entry->mod, NULL, &depth_unit);
- put_format(b, translate("gettextFromC", "MOD: %d%s\n"), mod, depth_unit);
- }
- eadd = (int)get_depth_units(entry->eadd, NULL, &depth_unit);
- if (prefs.ead) {
- switch (pi->dive_type) {
- case NITROX:
- ead = (int)get_depth_units(entry->ead, NULL, &depth_unit);
- put_format(b, translate("gettextFromC", "EAD: %d%s\nEADD: %d%s\n"), ead, depth_unit, eadd, depth_unit);
- break;
- case TRIMIX:
- end = (int)get_depth_units(entry->end, NULL, &depth_unit);
- put_format(b, translate("gettextFromC", "END: %d%s\nEADD: %d%s\n"), end, depth_unit, eadd, depth_unit);
- break;
- case AIR:
- case FREEDIVING:
- /* nothing */
- break;
- }
- }
- if (entry->stopdepth) {
- depthvalue = get_depth_units(entry->stopdepth, NULL, &depth_unit);
- if (entry->ndl) {
- /* this is a safety stop as we still have ndl */
- if (entry->stoptime)
- put_format(b, translate("gettextFromC", "Safetystop: %umin @ %.0f%s\n"), DIV_UP(entry->stoptime, 60),
- depthvalue, depth_unit);
- else
- put_format(b, translate("gettextFromC", "Safetystop: unkn time @ %.0f%s\n"),
- depthvalue, depth_unit);
- } else {
- /* actual deco stop */
- if (entry->stoptime)
- put_format(b, translate("gettextFromC", "Deco: %umin @ %.0f%s\n"), DIV_UP(entry->stoptime, 60),
- depthvalue, depth_unit);
- else
- put_format(b, translate("gettextFromC", "Deco: unkn time @ %.0f%s\n"),
- depthvalue, depth_unit);
- }
- } else if (entry->in_deco) {
- put_string(b, translate("gettextFromC", "In deco\n"));
- } else if (has_ndl) {
- put_format(b, translate("gettextFromC", "NDL: %umin\n"), DIV_UP(entry->ndl, 60));
- }
- if (entry->tts)
- put_format(b, translate("gettextFromC", "TTS: %umin\n"), DIV_UP(entry->tts, 60));
- if (entry->stopdepth_calc && entry->stoptime_calc) {
- depthvalue = get_depth_units(entry->stopdepth_calc, NULL, &depth_unit);
- put_format(b, translate("gettextFromC", "Deco: %umin @ %.0f%s (calc)\n"), DIV_UP(entry->stoptime_calc, 60),
- depthvalue, depth_unit);
- } else if (entry->in_deco_calc) {
- /* This means that we have no NDL left,
- * and we have no deco stop,
- * so if we just accend to the surface slowly
- * (ascent_mm_per_step / ascent_s_per_step)
- * everything will be ok. */
- put_string(b, translate("gettextFromC", "In deco (calc)\n"));
- } else if (prefs.calcndltts && entry->ndl_calc != 0) {
- if(entry->ndl_calc < MAX_PROFILE_DECO)
- put_format(b, translate("gettextFromC", "NDL: %umin (calc)\n"), DIV_UP(entry->ndl_calc, 60));
- else
- put_format(b, "%s", translate("gettextFromC", "NDL: >2h (calc)\n"));
- }
- if (entry->tts_calc) {
- if (entry->tts_calc < MAX_PROFILE_DECO)
- put_format(b, translate("gettextFromC", "TTS: %umin (calc)\n"), DIV_UP(entry->tts_calc, 60));
- else
- put_format(b, "%s", translate("gettextFromC", "TTS: >2h (calc)\n"));
- }
- if (entry->rbt)
- put_format(b, translate("gettextFromC", "RBT: %umin\n"), DIV_UP(entry->rbt, 60));
- if (entry->ceiling) {
- depthvalue = get_depth_units(entry->ceiling, NULL, &depth_unit);
- put_format(b, translate("gettextFromC", "Calculated ceiling %.0f%s\n"), depthvalue, depth_unit);
- if (prefs.calcalltissues) {
- int k;
- for (k = 0; k < 16; k++) {
- if (entry->ceilings[k]) {
- depthvalue = get_depth_units(entry->ceilings[k], NULL, &depth_unit);
- put_format(b, translate("gettextFromC", "Tissue %.0fmin: %.1f%s\n"), buehlmann_N2_t_halflife[k], depthvalue, depth_unit);
- }
- }
- }
- }
- if (entry->heartbeat && prefs.hrgraph)
- put_format(b, translate("gettextFromC", "heartbeat: %d\n"), entry->heartbeat);
- if (entry->bearing)
- put_format(b, translate("gettextFromC", "bearing: %d\n"), entry->bearing);
- if (entry->running_sum) {
- depthvalue = get_depth_units(entry->running_sum / entry->sec, NULL, &depth_unit);
- put_format(b, translate("gettextFromC", "mean depth to here %.1f%s\n"), depthvalue, depth_unit);
- }
-
- strip_mb(b);
-}
-
-struct plot_data *get_plot_details_new(struct plot_info *pi, int time, struct membuffer *mb)
-{
- struct plot_data *entry = NULL;
- int i;
-
- for (i = 0; i < pi->nr; i++) {
- entry = pi->entry + i;
- if (entry->sec >= time)
- break;
- }
- if (entry)
- plot_string(pi, entry, mb, pi->has_ndl);
- return (entry);
-}
-
-/* Compare two plot_data entries and writes the results into a string */
-void compare_samples(struct plot_data *e1, struct plot_data *e2, char *buf, int bufsize, int sum)
-{
- struct plot_data *start, *stop, *data;
- const char *depth_unit, *pressure_unit, *vertical_speed_unit;
- char *buf2 = malloc(bufsize);
- int avg_speed, max_asc_speed, max_desc_speed;
- int delta_depth, avg_depth, max_depth, min_depth;
- int bar_used, last_pressure, pressurevalue;
- int count, last_sec, delta_time;
-
- double depthvalue, speedvalue;
-
- if (bufsize > 0)
- buf[0] = '\0';
- if (e1 == NULL || e2 == NULL) {
- free(buf2);
- return;
- }
-
- if (e1->sec < e2->sec) {
- start = e1;
- stop = e2;
- } else if (e1->sec > e2->sec) {
- start = e2;
- stop = e1;
- } else {
- free(buf2);
- return;
- }
- count = 0;
- avg_speed = 0;
- max_asc_speed = 0;
- max_desc_speed = 0;
-
- delta_depth = abs(start->depth - stop->depth);
- delta_time = abs(start->sec - stop->sec);
- avg_depth = 0;
- max_depth = 0;
- min_depth = INT_MAX;
- bar_used = 0;
-
- last_sec = start->sec;
- last_pressure = GET_PRESSURE(start);
-
- data = start;
- while (data != stop) {
- data = start + count;
- if (sum)
- avg_speed += abs(data->speed) * (data->sec - last_sec);
- else
- avg_speed += data->speed * (data->sec - last_sec);
- avg_depth += data->depth * (data->sec - last_sec);
-
- if (data->speed > max_desc_speed)
- max_desc_speed = data->speed;
- if (data->speed < max_asc_speed)
- max_asc_speed = data->speed;
-
- if (data->depth < min_depth)
- min_depth = data->depth;
- if (data->depth > max_depth)
- max_depth = data->depth;
- /* Try to detect gas changes */
- if (GET_PRESSURE(data) < last_pressure + 2000)
- bar_used += last_pressure - GET_PRESSURE(data);
-
- count += 1;
- last_sec = data->sec;
- last_pressure = GET_PRESSURE(data);
- }
- avg_depth /= stop->sec - start->sec;
- avg_speed /= stop->sec - start->sec;
-
- snprintf(buf, bufsize, translate("gettextFromC", "%sT: %d:%02d min"), UTF8_DELTA, delta_time / 60, delta_time % 60);
- memcpy(buf2, buf, bufsize);
-
- depthvalue = get_depth_units(delta_depth, NULL, &depth_unit);
- snprintf(buf, bufsize, translate("gettextFromC", "%s %sD:%.1f%s"), buf2, UTF8_DELTA, depthvalue, depth_unit);
- memcpy(buf2, buf, bufsize);
-
- depthvalue = get_depth_units(min_depth, NULL, &depth_unit);
- snprintf(buf, bufsize, translate("gettextFromC", "%s %sD:%.1f%s"), buf2, UTF8_DOWNWARDS_ARROW, depthvalue, depth_unit);
- memcpy(buf2, buf, bufsize);
-
- depthvalue = get_depth_units(max_depth, NULL, &depth_unit);
- snprintf(buf, bufsize, translate("gettextFromC", "%s %sD:%.1f%s"), buf2, UTF8_UPWARDS_ARROW, depthvalue, depth_unit);
- memcpy(buf2, buf, bufsize);
-
- depthvalue = get_depth_units(avg_depth, NULL, &depth_unit);
- snprintf(buf, bufsize, translate("gettextFromC", "%s %sD:%.1f%s\n"), buf2, UTF8_AVERAGE, depthvalue, depth_unit);
- memcpy(buf2, buf, bufsize);
-
- speedvalue = get_vertical_speed_units(abs(max_desc_speed), NULL, &vertical_speed_unit);
- snprintf(buf, bufsize, translate("gettextFromC", "%s%sV:%.2f%s"), buf2, UTF8_DOWNWARDS_ARROW, speedvalue, vertical_speed_unit);
- memcpy(buf2, buf, bufsize);
-
- speedvalue = get_vertical_speed_units(abs(max_asc_speed), NULL, &vertical_speed_unit);
- snprintf(buf, bufsize, translate("gettextFromC", "%s %sV:%.2f%s"), buf2, UTF8_UPWARDS_ARROW, speedvalue, vertical_speed_unit);
- memcpy(buf2, buf, bufsize);
-
- speedvalue = get_vertical_speed_units(abs(avg_speed), NULL, &vertical_speed_unit);
- snprintf(buf, bufsize, translate("gettextFromC", "%s %sV:%.2f%s"), buf2, UTF8_AVERAGE, speedvalue, vertical_speed_unit);
- memcpy(buf2, buf, bufsize);
-
- /* Only print if gas has been used */
- if (bar_used) {
- pressurevalue = get_pressure_units(bar_used, &pressure_unit);
- memcpy(buf2, buf, bufsize);
- snprintf(buf, bufsize, translate("gettextFromC", "%s %sP:%d %s"), buf2, UTF8_DELTA, pressurevalue, pressure_unit);
- }
-
- free(buf2);
-}
diff --git a/subsurface-core/profile.h b/subsurface-core/profile.h
deleted file mode 100644
index abac9dd49..000000000
--- a/subsurface-core/profile.h
+++ /dev/null
@@ -1,111 +0,0 @@
-#ifndef PROFILE_H
-#define PROFILE_H
-
-#include "dive.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum {
- STABLE,
- SLOW,
- MODERATE,
- FAST,
- CRAZY
-} velocity_t;
-
-struct membuffer;
-struct divecomputer;
-struct plot_info;
-struct plot_data {
- unsigned int in_deco : 1;
- int cylinderindex;
- int sec;
- /* pressure[0] is sensor cylinder pressure [when CCR, the pressure of the diluent cylinder]
- * pressure[1] is interpolated cylinder pressure */
- int pressure[2];
- /* o2pressure[0] is o2 cylinder pressure [CCR]
- * o2pressure[1] is interpolated o2 cylinder pressure [CCR] */
- int o2cylinderpressure[2];
- int temperature;
- /* Depth info */
- int depth;
- int ceiling;
- int ceilings[16];
- int percentages[16];
- int ndl;
- int tts;
- int rbt;
- int stoptime;
- int stopdepth;
- int cns;
- int smoothed;
- int sac;
- int running_sum;
- struct gas_pressures pressures;
- pressure_t o2pressure; // for rebreathers, this is consensus measured po2, or setpoint otherwise. 0 for OC.
- pressure_t o2sensor[3]; //for rebreathers with up to 3 PO2 sensors
- pressure_t o2setpoint;
- double mod, ead, end, eadd;
- velocity_t velocity;
- int speed;
- struct plot_data *min[3];
- struct plot_data *max[3];
- int avg[3];
- /* values calculated by us */
- unsigned int in_deco_calc : 1;
- int ndl_calc;
- int tts_calc;
- int stoptime_calc;
- int stopdepth_calc;
- int pressure_time;
- int heartbeat;
- int bearing;
- double ambpressure;
- double gfline;
-};
-
-struct ev_select {
- char *ev_name;
- bool plot_ev;
-};
-
-struct plot_info calculate_max_limits_new(struct dive *dive, struct divecomputer *given_dc);
-void compare_samples(struct plot_data *e1, struct plot_data *e2, char *buf, int bufsize, int sum);
-struct plot_data *populate_plot_entries(struct dive *dive, struct divecomputer *dc, struct plot_info *pi);
-struct plot_info *analyze_plot_info(struct plot_info *pi);
-void create_plot_info_new(struct dive *dive, struct divecomputer *dc, struct plot_info *pi, bool fast);
-void calculate_deco_information(struct dive *dive, struct divecomputer *dc, struct plot_info *pi, bool print_mode);
-struct plot_data *get_plot_details_new(struct plot_info *pi, int time, struct membuffer *);
-
-/*
- * When showing dive profiles, we scale things to the
- * current dive. However, we don't scale past less than
- * 30 minutes or 90 ft, just so that small dives show
- * up as such unless zoom is enabled.
- * We also need to add 180 seconds at the end so the min/max
- * plots correctly
- */
-int get_maxtime(struct plot_info *pi);
-
-/* get the maximum depth to which we want to plot
- * take into account the additional verical space needed to plot
- * partial pressure graphs */
-int get_maxdepth(struct plot_info *pi);
-
-#define SENSOR_PR 0
-#define INTERPOLATED_PR 1
-#define SENSOR_PRESSURE(_entry) (_entry)->pressure[SENSOR_PR]
-#define O2CYLINDER_PRESSURE(_entry) (_entry)->o2cylinderpressure[SENSOR_PR]
-#define CYLINDER_PRESSURE(_o2, _entry) (_o2 ? O2CYLINDER_PRESSURE(_entry) : SENSOR_PRESSURE(_entry))
-#define INTERPOLATED_PRESSURE(_entry) (_entry)->pressure[INTERPOLATED_PR]
-#define INTERPOLATED_O2CYLINDER_PRESSURE(_entry) (_entry)->o2cylinderpressure[INTERPOLATED_PR]
-#define GET_PRESSURE(_entry) (SENSOR_PRESSURE(_entry) ? SENSOR_PRESSURE(_entry) : INTERPOLATED_PRESSURE(_entry))
-#define GET_O2CYLINDER_PRESSURE(_entry) (O2CYLINDER_PRESSURE(_entry) ? O2CYLINDER_PRESSURE(_entry) : INTERPOLATED_O2CYLINDER_PRESSURE(_entry))
-#define SAC_WINDOW 45 /* sliding window in seconds for current SAC calculation */
-
-#ifdef __cplusplus
-}
-#endif
-#endif // PROFILE_H
diff --git a/subsurface-core/qt-gui.h b/subsurface-core/qt-gui.h
deleted file mode 100644
index 92532d22f..000000000
--- a/subsurface-core/qt-gui.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef QT_GUI_H
-#define QT_GUI_H
-
-void init_qt_late();
-void init_ui();
-
-void run_ui();
-void exit_ui();
-
-#if defined(SUBSURFACE_MOBILE)
-#include <QQuickWindow>
-extern QObject *qqWindowObject;
-#endif
-
-#endif // QT_GUI_H
diff --git a/subsurface-core/qt-init.cpp b/subsurface-core/qt-init.cpp
deleted file mode 100644
index b52dfd970..000000000
--- a/subsurface-core/qt-init.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-#include <QApplication>
-#include <QNetworkProxy>
-#include <QLibraryInfo>
-#include <QTextCodec>
-#include "helpers.h"
-
-void init_qt_late()
-{
- QApplication *application = qApp;
- // tell Qt to use system proxies
- // note: on Linux, "system" == "environment variables"
- QNetworkProxyFactory::setUseSystemConfiguration(true);
-
- // for Win32 and Qt5 we try to set the locale codec to UTF-8.
- // this makes QFile::encodeName() work.
-#ifdef Q_OS_WIN
- QTextCodec::setCodecForLocale(QTextCodec::codecForMib(106));
-#endif
-
- QCoreApplication::setOrganizationName("Subsurface");
- QCoreApplication::setOrganizationDomain("subsurface.hohndel.org");
- QCoreApplication::setApplicationName("Subsurface");
- // find plugins installed in the application directory (without this SVGs don't work on Windows)
- QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath());
- QLocale loc;
- QString uiLang = uiLanguage(&loc);
- QLocale::setDefault(loc);
-
- // we don't have translations for English - if we don't check for this
- // Qt will proceed to load the second language in preference order - not what we want
- // on Linux this tends to be en-US, but on the Mac it's just en
- if (!uiLang.startsWith("en") || uiLang.startsWith("en-GB")) {
- qtTranslator = new QTranslator;
- if (qtTranslator->load(loc, "qt", "_", QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
- application->installTranslator(qtTranslator);
- } else {
- qDebug() << "can't find Qt localization for locale" << uiLang << "searching in" << QLibraryInfo::location(QLibraryInfo::TranslationsPath);
- }
- ssrfTranslator = new QTranslator;
- if (ssrfTranslator->load(loc, "subsurface", "_") ||
- ssrfTranslator->load(loc, "subsurface", "_", getSubsurfaceDataPath("translations")) ||
- ssrfTranslator->load(loc, "subsurface", "_", getSubsurfaceDataPath("../translations"))) {
- application->installTranslator(ssrfTranslator);
- } else {
- qDebug() << "can't find Subsurface localization for locale" << uiLang;
- }
- }
-}
diff --git a/subsurface-core/qthelper.cpp b/subsurface-core/qthelper.cpp
deleted file mode 100644
index c45e86388..000000000
--- a/subsurface-core/qthelper.cpp
+++ /dev/null
@@ -1,1615 +0,0 @@
-#include "qthelper.h"
-#include "helpers.h"
-#include "gettextfromc.h"
-#include "statistics.h"
-#include "membuffer.h"
-#include "subsurfacesysinfo.h"
-#include "version.h"
-#include "divecomputer.h"
-#include "time.h"
-#include "gettextfromc.h"
-#include <sys/time.h>
-#include <exif.h>
-#include "file.h"
-#include "prefs-macros.h"
-#include <QFile>
-#include <QRegExp>
-#include <QDir>
-#include <QDebug>
-#include <QSettings>
-#include <QStandardPaths>
-#include <QJsonDocument>
-#include <QNetworkReply>
-#include <QNetworkRequest>
-#include <QNetworkAccessManager>
-#include <QNetworkProxy>
-#include <QDateTime>
-#include <QImageReader>
-#include <QtConcurrent>
-#include <QFont>
-#include <QApplication>
-#include <QTextDocument>
-
-#include <libxslt/documents.h>
-
-const char *existing_filename;
-static QLocale loc;
-
-#define translate(_context, arg) trGettext(arg)
-static const QString DEGREE_SIGNS("dD" UTF8_DEGREE);
-
-QString weight_string(int weight_in_grams)
-{
- QString str;
- if (get_units()->weight == units::KG) {
- int gr = weight_in_grams % 1000;
- int kg = weight_in_grams / 1000;
- if (kg >= 20.0)
- str = QString("%1").arg(kg + (gr >= 500 ? 1 : 0));
- else
- str = QString("%1.%2").arg(kg).arg((unsigned)(gr + 50) / 100);
- } else {
- double lbs = grams_to_lbs(weight_in_grams);
- if (lbs >= 40.0)
- lbs = rint(lbs + 0.5);
- else
- lbs = rint(lbs + 0.05);
- str = QString("%1").arg(lbs, 0, 'f', lbs >= 40.0 ? 0 : 1);
- }
- return (str);
-}
-
-QString distance_string(int distanceInMeters)
-{
- QString str;
- if(get_units()->length == units::METERS) {
- if (distanceInMeters >= 1000)
- str = QString(translate("gettextFromC", "%1km")).arg(distanceInMeters / 1000);
- else
- str = QString(translate("gettextFromC", "%1m")).arg(distanceInMeters);
- } else {
- double miles = m_to_mile(distanceInMeters);
- if (miles >= 1.0)
- str = QString(translate("gettextFromC", "%1mi")).arg((int)miles);
- else
- str = QString(translate("gettextFromC", "%1yd")).arg((int)(miles * 1760));
- }
- return str;
-}
-
-extern "C" const char *printGPSCoords(int lat, int lon)
-{
- unsigned int latdeg, londeg;
- unsigned int latmin, lonmin;
- double latsec, lonsec;
- QString lath, lonh, result;
-
- if (!lat && !lon)
- return strdup("");
-
- if (prefs.coordinates_traditional) {
- lath = lat >= 0 ? translate("gettextFromC", "N") : translate("gettextFromC", "S");
- lonh = lon >= 0 ? translate("gettextFromC", "E") : translate("gettextFromC", "W");
- lat = abs(lat);
- lon = abs(lon);
- latdeg = lat / 1000000U;
- londeg = lon / 1000000U;
- latmin = (lat % 1000000U) * 60U;
- lonmin = (lon % 1000000U) * 60U;
- latsec = (latmin % 1000000) * 60;
- lonsec = (lonmin % 1000000) * 60;
- result.sprintf("%u%s%02d\'%06.3f\"%s %u%s%02d\'%06.3f\"%s",
- latdeg, UTF8_DEGREE, latmin / 1000000, latsec / 1000000, lath.toUtf8().data(),
- londeg, UTF8_DEGREE, lonmin / 1000000, lonsec / 1000000, lonh.toUtf8().data());
- } else {
- result.sprintf("%f %f", (double) lat / 1000000.0, (double) lon / 1000000.0);
- }
- return strdup(result.toUtf8().data());
-}
-
-/**
-* Try to parse in a generic manner a coordinate.
-*/
-static bool parseCoord(const QString& txt, int& pos, const QString& positives,
- const QString& negatives, const QString& others,
- double& value)
-{
- bool numberDefined = false, degreesDefined = false,
- minutesDefined = false, secondsDefined = false;
- double number = 0.0;
- int posBeforeNumber = pos;
- int sign = 0;
- value = 0.0;
- while (pos < txt.size()) {
- if (txt[pos].isDigit()) {
- if (numberDefined)
- return false;
- QRegExp numberRe("(\\d+(?:[\\.,]\\d+)?).*");
- if (!numberRe.exactMatch(txt.mid(pos)))
- return false;
- number = numberRe.cap(1).toDouble();
- numberDefined = true;
- posBeforeNumber = pos;
- pos += numberRe.cap(1).size() - 1;
- } else if (positives.indexOf(txt[pos]) >= 0) {
- if (sign != 0)
- return false;
- sign = 1;
- if (degreesDefined || numberDefined) {
- //sign after the degrees =>
- //at the end of the coordinate
- ++pos;
- break;
- }
- } else if (negatives.indexOf(txt[pos]) >= 0) {
- if (sign != 0) {
- if (others.indexOf(txt[pos]) >= 0)
- //special case for the '-' sign => next coordinate
- break;
- return false;
- }
- sign = -1;
- if (degreesDefined || numberDefined) {
- //sign after the degrees =>
- //at the end of the coordinate
- ++pos;
- break;
- }
- } else if (others.indexOf(txt[pos]) >= 0) {
- //we are at the next coordinate.
- break;
- } else if (DEGREE_SIGNS.indexOf(txt[pos]) >= 0 ||
- (txt[pos].isSpace() && !degreesDefined && numberDefined)) {
- if (!numberDefined)
- return false;
- if (degreesDefined) {
- //next coordinate => need to put back the number
- pos = posBeforeNumber;
- numberDefined = false;
- break;
- }
- value += number;
- numberDefined = false;
- degreesDefined = true;
- } else if (txt[pos] == '\'' || (txt[pos].isSpace() && !minutesDefined && numberDefined)) {
- if (!numberDefined || minutesDefined)
- return false;
- value += number / 60.0;
- numberDefined = false;
- minutesDefined = true;
- } else if (txt[pos] == '"' || (txt[pos].isSpace() && !secondsDefined && numberDefined)) {
- if (!numberDefined || secondsDefined)
- return false;
- value += number / 3600.0;
- numberDefined = false;
- secondsDefined = true;
- } else if ((numberDefined || minutesDefined || secondsDefined) &&
- (txt[pos] == ',' || txt[pos] == ';')) {
- // next coordinate coming up
- // eat the ',' and any subsequent white space
- while (txt[++pos].isSpace())
- /* nothing */ ;
- break;
- } else {
- return false;
- }
- ++pos;
- }
- if (!degreesDefined && numberDefined) {
- value = number; //just a single number => degrees
- } else if (!minutesDefined && numberDefined) {
- value += number / 60.0;
- } else if (!secondsDefined && numberDefined) {
- value += number / 3600.0;
- } else if (numberDefined) {
- return false;
- }
- if (sign == -1) value *= -1.0;
- return true;
-}
-
-/**
-* Parse special coordinate formats that cannot be handled by parseCoord.
-*/
-static bool parseSpecialCoords(const QString& txt, double& latitude, double& longitude) {
- QRegExp xmlFormat("(-?\\d+(?:\\.\\d+)?),?\\s+(-?\\d+(?:\\.\\d+)?)");
- if (xmlFormat.exactMatch(txt)) {
- latitude = xmlFormat.cap(1).toDouble();
- longitude = xmlFormat.cap(2).toDouble();
- return true;
- }
- return false;
-}
-
-bool parseGpsText(const QString &gps_text, double *latitude, double *longitude)
-{
- static const QString POS_LAT = QString("+N") + translate("gettextFromC", "N");
- static const QString NEG_LAT = QString("-S") + translate("gettextFromC", "S");
- static const QString POS_LON = QString("+E") + translate("gettextFromC", "E");
- static const QString NEG_LON = QString("-W") + translate("gettextFromC", "W");
-
- //remove the useless spaces (but keep the ones separating numbers)
- static const QRegExp SPACE_CLEANER("\\s*([" + POS_LAT + NEG_LAT + POS_LON +
- NEG_LON + DEGREE_SIGNS + "'\"\\s])\\s*");
- const QString normalized = gps_text.trimmed().toUpper().replace(SPACE_CLEANER, "\\1");
-
- if (normalized.isEmpty()) {
- *latitude = 0.0;
- *longitude = 0.0;
- return true;
- }
- if (parseSpecialCoords(normalized, *latitude, *longitude))
- return true;
- int pos = 0;
- return parseCoord(normalized, pos, POS_LAT, NEG_LAT, POS_LON + NEG_LON, *latitude) &&
- parseCoord(normalized, pos, POS_LON, NEG_LON, "", *longitude) &&
- pos == normalized.size();
-}
-
-#if 0 // we'll need something like this for the dive site management, eventually
-bool gpsHasChanged(struct dive *dive, struct dive *master, const QString &gps_text, bool *parsed_out)
-{
- double latitude, longitude;
- int latudeg, longudeg;
- bool ignore;
- bool *parsed = parsed_out ?: &ignore;
- *parsed = true;
-
- /* if we have a master and the dive's gps address is different from it,
- * don't change the dive */
- if (master && (master->latitude.udeg != dive->latitude.udeg ||
- master->longitude.udeg != dive->longitude.udeg))
- return false;
-
- if (!(*parsed = parseGpsText(gps_text, &latitude, &longitude)))
- return false;
-
- latudeg = rint(1000000 * latitude);
- longudeg = rint(1000000 * longitude);
-
- /* if dive gps didn't change, nothing changed */
- if (dive->latitude.udeg == latudeg && dive->longitude.udeg == longudeg)
- return false;
- /* ok, update the dive and mark things changed */
- dive->latitude.udeg = latudeg;
- dive->longitude.udeg = longudeg;
- return true;
-}
-#endif
-
-QList<int> getDivesInTrip(dive_trip_t *trip)
-{
- QList<int> ret;
- int i;
- struct dive *d;
- for_each_dive (i, d) {
- if (d->divetrip == trip) {
- ret.push_back(get_divenr(d));
- }
- }
- return ret;
-}
-
-// we need this to be uniq, but also make sure
-// it doesn't change during the life time of a Subsurface session
-// oh, and it has no meaning whatsoever - that's why we have the
-// silly initial number and increment by 3 :-)
-int dive_getUniqID(struct dive *d)
-{
- static QSet<int> ids;
- static int maxId = 83529;
-
- int id = d->id;
- if (id) {
- if (!ids.contains(id)) {
- qDebug() << "WTF - only I am allowed to create IDs";
- ids.insert(id);
- }
- return id;
- }
- maxId += 3;
- id = maxId;
- Q_ASSERT(!ids.contains(id));
- ids.insert(id);
- return id;
-}
-
-
-static xmlDocPtr get_stylesheet_doc(const xmlChar *uri, xmlDictPtr, int, void *, xsltLoadType)
-{
- QFile f(QLatin1String(":/xslt/") + (const char *)uri);
- if (!f.open(QIODevice::ReadOnly)) {
- if (verbose > 0) {
- qDebug() << "cannot open stylesheet" << QLatin1String(":/xslt/") + (const char *)uri;
- return NULL;
- }
- }
- /* Load and parse the data */
- QByteArray source = f.readAll();
-
- xmlDocPtr doc = xmlParseMemory(source, source.size());
- return doc;
-}
-
-extern "C" xsltStylesheetPtr get_stylesheet(const char *name)
-{
- // this needs to be done only once, but doesn't hurt to run every time
- xsltSetLoaderFunc(get_stylesheet_doc);
-
- // get main document:
- xmlDocPtr doc = get_stylesheet_doc((const xmlChar *)name, NULL, 0, NULL, XSLT_LOAD_START);
- if (!doc)
- return NULL;
-
- // xsltSetGenericErrorFunc(stderr, NULL);
- xsltStylesheetPtr xslt = xsltParseStylesheetDoc(doc);
- if (!xslt) {
- xmlFreeDoc(doc);
- return NULL;
- }
-
- return xslt;
-}
-
-
-extern "C" timestamp_t picture_get_timestamp(char *filename)
-{
- EXIFInfo exif;
- memblock mem;
- int retval;
-
- // filename might not be the actual filename, so let's go via the hash.
- if (readfile(localFilePath(QString(filename)).toUtf8().data(), &mem) <= 0)
- return 0;
- retval = exif.parseFrom((const unsigned char *)mem.buffer, (unsigned)mem.size);
- free(mem.buffer);
- if (retval != PARSE_EXIF_SUCCESS)
- return 0;
- return exif.epoch();
-}
-
-extern "C" char *move_away(const char *old_path)
-{
- if (verbose > 1)
- qDebug() << "move away" << old_path;
- QDir oldDir(old_path);
- QDir newDir;
- QString newPath;
- int i = 0;
- do {
- newPath = QString(old_path) + QString(".%1").arg(++i);
- newDir.setPath(newPath);
- } while(newDir.exists());
- if (verbose > 1)
- qDebug() << "renaming to" << newPath;
- if (!oldDir.rename(old_path, newPath)) {
- if (verbose)
- qDebug() << "rename of" << old_path << "to" << newPath << "failed";
- // this next one we only try on Windows... if we are on a different platform
- // we simply give up and return an empty string
-#ifdef WIN32
- if (subsurface_dir_rename(old_path, qPrintable(newPath)) == 0)
-#endif
- return strdup("");
- }
- return strdup(qPrintable(newPath));
-}
-
-extern "C" char *get_file_name(const char *fileName)
-{
- QFileInfo fileInfo(fileName);
- return strdup(fileInfo.fileName().toUtf8());
-}
-
-extern "C" void copy_image_and_overwrite(const char *cfileName, const char *path, const char *cnewName)
-{
- QString fileName(cfileName);
- QString newName(path);
- newName += cnewName;
- QFile file(newName);
- if (file.exists())
- file.remove();
- if (!QFile::copy(fileName, newName))
- qDebug() << "copy of" << fileName << "to" << newName << "failed";
-}
-
-extern "C" bool string_sequence_contains(const char *string_sequence, const char *text)
-{
- if (same_string(text, "") || same_string(string_sequence, ""))
- return false;
-
- QString stringSequence(string_sequence);
- QStringList strings = stringSequence.split(",", QString::SkipEmptyParts);
- Q_FOREACH (const QString& string, strings) {
- if (string.trimmed().compare(QString(text).trimmed(), Qt::CaseInsensitive) == 0)
- return true;
- }
- return false;
-}
-
-static bool lessThan(const QPair<QString, int> &a, const QPair<QString, int> &b)
-{
- return a.second < b.second;
-}
-
-void selectedDivesGasUsed(QVector<QPair<QString, int> > &gasUsedOrdered)
-{
- int i, j;
- struct dive *d;
- QMap<QString, int> gasUsed;
- for_each_dive (i, d) {
- if (!d->selected)
- continue;
- volume_t diveGases[MAX_CYLINDERS] = {};
- get_gas_used(d, diveGases);
- for (j = 0; j < MAX_CYLINDERS; j++)
- if (diveGases[j].mliter) {
- QString gasName = gasname(&d->cylinder[j].gasmix);
- gasUsed[gasName] += diveGases[j].mliter;
- }
- }
- Q_FOREACH(const QString& gas, gasUsed.keys()) {
- gasUsedOrdered.append(qMakePair(gas, gasUsed[gas]));
- }
- qSort(gasUsedOrdered.begin(), gasUsedOrdered.end(), lessThan);
-}
-
-QString getUserAgent()
-{
- QString arch;
- // fill in the system data - use ':' as separator
- // replace all other ':' with ' ' so that this is easy to parse
-#ifdef SUBSURFACE_MOBILE
- QString userAgent = QString("Subsurface-mobile:%1(%2):").arg(subsurface_mobile_version()).arg(subsurface_canonical_version());
-#else
- QString userAgent = QString("Subsurface:%1:").arg(subsurface_canonical_version());
-#endif
- userAgent.append(SubsurfaceSysInfo::prettyOsName().replace(':', ' ') + ":");
- arch = SubsurfaceSysInfo::buildCpuArchitecture().replace(':', ' ');
- userAgent.append(arch);
- if (arch == "i386")
- userAgent.append("/" + SubsurfaceSysInfo::currentCpuArchitecture());
- userAgent.append(":" + uiLanguage(NULL));
- return userAgent;
-
-}
-
-extern "C" const char *subsurface_user_agent()
-{
- static QString uA = getUserAgent();
-
- return strdup(qPrintable(uA));
-}
-
-QString uiLanguage(QLocale *callerLoc)
-{
- QString shortDateFormat;
- QString dateFormat;
- QString timeFormat;
- QSettings s;
- QVariant v;
- s.beginGroup("Language");
-
- if (!s.value("UseSystemLanguage", true).toBool()) {
- loc = QLocale(s.value("UiLanguage", QLocale().uiLanguages().first()).toString());
- } else {
- loc = QLocale(QLocale().uiLanguages().first());
- }
- QStringList languages = loc.uiLanguages();
- QString uiLang;
- if (languages[0].contains('-'))
- uiLang = languages[0];
- else if (languages.count() > 1 && languages[1].contains('-'))
- uiLang = languages[1];
- else if (languages.count() > 2 && languages[2].contains('-'))
- uiLang = languages[2];
- else
- uiLang = languages[0];
- GET_BOOL("time_format_override", time_format_override);
- GET_BOOL("date_format_override", date_format_override);
- GET_TXT("time_format", time_format);
- GET_TXT("date_format", date_format);
- GET_TXT("date_format_short", date_format_short);
- s.endGroup();
-
- // there's a stupid Qt bug on MacOS where uiLanguages doesn't give us the country info
- if (!uiLang.contains('-') && uiLang != loc.bcp47Name()) {
- QLocale loc2(loc.bcp47Name());
- loc = loc2;
- QStringList languages = loc2.uiLanguages();
- if (languages[0].contains('-'))
- uiLang = languages[0];
- else if (languages.count() > 1 && languages[1].contains('-'))
- uiLang = languages[1];
- else if (languages.count() > 2 && languages[2].contains('-'))
- uiLang = languages[2];
- }
- if (callerLoc)
- *callerLoc = loc;
-
- if (!prefs.date_format_override || same_string(prefs.date_format_short, "") || same_string(prefs.date_format, "")) {
- // derive our standard date format from what the locale gives us
- // the short format is fine
- // the long format uses long weekday and month names, so replace those with the short ones
- // for time we don't want the time zone designator and don't want leading zeroes on the hours
- shortDateFormat = loc.dateFormat(QLocale::ShortFormat);
- dateFormat = loc.dateFormat(QLocale::LongFormat);
- dateFormat.replace("dddd,", "ddd").replace("dddd", "ddd").replace("MMMM", "MMM");
- // special hack for Swedish as our switching from long weekday names to short weekday names
- // messes things up there
- dateFormat.replace("'en' 'den' d:'e'", " d");
- if (!prefs.date_format_override || same_string(prefs.date_format, "")) {
- free((void*)prefs.date_format);
- prefs.date_format = strdup(qPrintable(dateFormat));
- }
- if (!prefs.date_format_override || same_string(prefs.date_format_short, "")) {
- free((void*)prefs.date_format_short);
- prefs.date_format_short = strdup(qPrintable(shortDateFormat));
- }
- }
- if (!prefs.time_format_override || same_string(prefs.time_format, "")) {
- timeFormat = loc.timeFormat();
- timeFormat.replace("(t)", "").replace(" t", "").replace("t", "").replace("hh", "h").replace("HH", "H").replace("'kl'.", "");
- timeFormat.replace(".ss", "").replace(":ss", "").replace("ss", "");
- free((void*)prefs.time_format);
- prefs.time_format = strdup(qPrintable(timeFormat));
- }
- return uiLang;
-}
-
-QLocale getLocale()
-{
- return loc;
-}
-
-void set_filename(const char *filename, bool force)
-{
- if (!force && existing_filename)
- return;
- free((void *)existing_filename);
- if (filename)
- existing_filename = strdup(filename);
- else
- existing_filename = NULL;
-}
-
-const QString get_dc_nickname(const char *model, uint32_t deviceid)
-{
- const DiveComputerNode *existNode = dcList.getExact(model, deviceid);
-
- if (existNode && !existNode->nickName.isEmpty())
- return existNode->nickName;
- else
- return model;
-}
-
-QString get_depth_string(int mm, bool showunit, bool showdecimal)
-{
- if (prefs.units.length == units::METERS) {
- double meters = mm / 1000.0;
- return QString("%1%2").arg(meters, 0, 'f', (showdecimal && meters < 20.0) ? 1 : 0).arg(showunit ? translate("gettextFromC", "m") : "");
- } else {
- double feet = mm_to_feet(mm);
- return QString("%1%2").arg(feet, 0, 'f', 0).arg(showunit ? translate("gettextFromC", "ft") : "");
- }
-}
-
-QString get_depth_string(depth_t depth, bool showunit, bool showdecimal)
-{
- return get_depth_string(depth.mm, showunit, showdecimal);
-}
-
-QString get_depth_unit()
-{
- if (prefs.units.length == units::METERS)
- return QString("%1").arg(translate("gettextFromC", "m"));
- else
- return QString("%1").arg(translate("gettextFromC", "ft"));
-}
-
-QString get_weight_string(weight_t weight, bool showunit)
-{
- QString str = weight_string(weight.grams);
- if (get_units()->weight == units::KG) {
- str = QString("%1%2").arg(str).arg(showunit ? translate("gettextFromC", "kg") : "");
- } else {
- str = QString("%1%2").arg(str).arg(showunit ? translate("gettextFromC", "lbs") : "");
- }
- return (str);
-}
-
-QString get_weight_unit()
-{
- if (prefs.units.weight == units::KG)
- return QString("%1").arg(translate("gettextFromC", "kg"));
- else
- return QString("%1").arg(translate("gettextFromC", "lbs"));
-}
-
-/* these methods retrieve used gas per cylinder */
-static unsigned start_pressure(cylinder_t *cyl)
-{
- return cyl->start.mbar ?: cyl->sample_start.mbar;
-}
-
-static unsigned end_pressure(cylinder_t *cyl)
-{
- return cyl->end.mbar ?: cyl->sample_end.mbar;
-}
-
-QString get_cylinder_used_gas_string(cylinder_t *cyl, bool showunit)
-{
- int decimals;
- const char *unit;
- double gas_usage;
- /* Get the cylinder gas use in mbar */
- gas_usage = start_pressure(cyl) - end_pressure(cyl);
- /* Can we turn it into a volume? */
- if (cyl->type.size.mliter) {
- gas_usage = bar_to_atm(gas_usage / 1000);
- gas_usage *= cyl->type.size.mliter;
- gas_usage = get_volume_units(gas_usage, &decimals, &unit);
- } else {
- gas_usage = get_pressure_units(gas_usage, &unit);
- decimals = 0;
- }
- // translate("gettextFromC","%.*f %s"
- return QString("%1 %2").arg(gas_usage, 0, 'f', decimals).arg(showunit ? unit : "");
-}
-
-QString get_temperature_string(temperature_t temp, bool showunit)
-{
- if (temp.mkelvin == 0) {
- return ""; //temperature not defined
- } else if (prefs.units.temperature == units::CELSIUS) {
- double celsius = mkelvin_to_C(temp.mkelvin);
- return QString("%1%2%3").arg(celsius, 0, 'f', 1).arg(showunit ? (UTF8_DEGREE) : "").arg(showunit ? translate("gettextFromC", "C") : "");
- } else {
- double fahrenheit = mkelvin_to_F(temp.mkelvin);
- return QString("%1%2%3").arg(fahrenheit, 0, 'f', 1).arg(showunit ? (UTF8_DEGREE) : "").arg(showunit ? translate("gettextFromC", "F") : "");
- }
-}
-
-QString get_temp_unit()
-{
- if (prefs.units.temperature == units::CELSIUS)
- return QString(UTF8_DEGREE "C");
- else
- return QString(UTF8_DEGREE "F");
-}
-
-QString get_volume_string(volume_t volume, bool showunit)
-{
- const char *unit;
- int decimals;
- double value = get_volume_units(volume.mliter, &decimals, &unit);
- return QString("%1%2").arg(value, 0, 'f', decimals).arg(showunit ? unit : "");
-}
-
-QString get_volume_unit()
-{
- const char *unit;
- (void) get_volume_units(0, NULL, &unit);
- return QString(unit);
-}
-
-QString get_pressure_string(pressure_t pressure, bool showunit)
-{
- if (prefs.units.pressure == units::BAR) {
- double bar = pressure.mbar / 1000.0;
- return QString("%1%2").arg(bar, 0, 'f', 1).arg(showunit ? translate("gettextFromC", "bar") : "");
- } else {
- double psi = mbar_to_PSI(pressure.mbar);
- return QString("%1%2").arg(psi, 0, 'f', 0).arg(showunit ? translate("gettextFromC", "psi") : "");
- }
-}
-
-QString getSubsurfaceDataPath(QString folderToFind)
-{
- QString execdir;
- QDir folder;
-
- // first check if we are running in the build dir, so the path that we
- // are looking for is just a subdirectory of the execution path;
- // this also works on Windows as there we install the dirs
- // under the application path
- execdir = QCoreApplication::applicationDirPath();
- folder = QDir(execdir.append(QDir::separator()).append(folderToFind));
- if (folder.exists())
- return folder.absolutePath();
-
- // next check for the Linux typical $(prefix)/share/subsurface
- execdir = QCoreApplication::applicationDirPath();
- if (execdir.contains("bin")) {
- folder = QDir(execdir.replace("bin", "share/subsurface/").append(folderToFind));
- if (folder.exists())
- return folder.absolutePath();
- }
- // then look for the usual locations on a Mac
- execdir = QCoreApplication::applicationDirPath();
- folder = QDir(execdir.append("/../Resources/share/").append(folderToFind));
- if (folder.exists())
- return folder.absolutePath();
- execdir = QCoreApplication::applicationDirPath();
- folder = QDir(execdir.append("/../Resources/").append(folderToFind));
- if (folder.exists())
- return folder.absolutePath();
- return QString("");
-}
-
-static const char *printing_templates = "printing_templates";
-
-QString getPrintingTemplatePathUser()
-{
- static QString path = QString();
- if (path.isEmpty())
- path = QString(system_default_directory()) + QDir::separator() + QString(printing_templates);
- return path;
-}
-
-QString getPrintingTemplatePathBundle()
-{
- static QString path = QString();
- if (path.isEmpty())
- path = getSubsurfaceDataPath(printing_templates);
- return path;
-}
-
-void copyPath(QString src, QString dst)
-{
- QDir dir(src);
- if (!dir.exists())
- return;
- foreach (QString d, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
- QString dst_path = dst + QDir::separator() + d;
- dir.mkpath(dst_path);
- copyPath(src + QDir::separator() + d, dst_path);
- }
- foreach (QString f, dir.entryList(QDir::Files))
- QFile::copy(src + QDir::separator() + f, dst + QDir::separator() + f);
-}
-
-int gettimezoneoffset(timestamp_t when)
-{
- QDateTime dt1, dt2;
- if (when == 0)
- dt1 = QDateTime::currentDateTime();
- else
- dt1 = QDateTime::fromMSecsSinceEpoch(when * 1000);
- dt2 = dt1.toUTC();
- dt1.setTimeSpec(Qt::UTC);
- return dt2.secsTo(dt1);
-}
-
-int parseLengthToMm(const QString &text)
-{
- int mm;
- QString numOnly = text;
- numOnly.replace(",", ".").remove(QRegExp("[^-0-9.]"));
- if (numOnly.isEmpty())
- return 0;
- double number = numOnly.toDouble();
- if (text.contains(QObject::tr("m"), Qt::CaseInsensitive)) {
- mm = number * 1000;
- } else if (text.contains(QObject::tr("ft"), Qt::CaseInsensitive)) {
- mm = feet_to_mm(number);
- } else {
- switch (prefs.units.length) {
- case units::FEET:
- mm = feet_to_mm(number);
- break;
- case units::METERS:
- mm = number * 1000;
- break;
- default:
- mm = 0;
- }
- }
- return mm;
-
-}
-
-int parseTemperatureToMkelvin(const QString &text)
-{
- int mkelvin;
- QString numOnly = text;
- numOnly.replace(",", ".").remove(QRegExp("[^-0-9.]"));
- if (numOnly.isEmpty())
- return 0;
- double number = numOnly.toDouble();
- if (text.contains(QObject::tr("C"), Qt::CaseInsensitive)) {
- mkelvin = C_to_mkelvin(number);
- } else if (text.contains(QObject::tr("F"), Qt::CaseInsensitive)) {
- mkelvin = F_to_mkelvin(number);
- } else {
- switch (prefs.units.temperature) {
- case units::CELSIUS:
- mkelvin = C_to_mkelvin(number);
- break;
- case units::FAHRENHEIT:
- mkelvin = F_to_mkelvin(number);
- break;
- default:
- mkelvin = 0;
- }
- }
- return mkelvin;
-}
-
-int parseWeightToGrams(const QString &text)
-{
- int grams;
- QString numOnly = text;
- numOnly.replace(",", ".").remove(QRegExp("[^0-9.]"));
- if (numOnly.isEmpty())
- return 0;
- double number = numOnly.toDouble();
- if (text.contains(QObject::tr("kg"), Qt::CaseInsensitive)) {
- grams = rint(number * 1000);
- } else if (text.contains(QObject::tr("lbs"), Qt::CaseInsensitive)) {
- grams = lbs_to_grams(number);
- } else {
- switch (prefs.units.weight) {
- case units::KG:
- grams = rint(number * 1000);
- break;
- case units::LBS:
- grams = lbs_to_grams(number);
- break;
- default:
- grams = 0;
- }
- }
- return grams;
-}
-
-int parsePressureToMbar(const QString &text)
-{
- int mbar;
- QString numOnly = text;
- numOnly.replace(",", ".").remove(QRegExp("[^0-9.]"));
- if (numOnly.isEmpty())
- return 0;
- double number = numOnly.toDouble();
- if (text.contains(QObject::tr("bar"), Qt::CaseInsensitive)) {
- mbar = rint(number * 1000);
- } else if (text.contains(QObject::tr("psi"), Qt::CaseInsensitive)) {
- mbar = psi_to_mbar(number);
- } else {
- switch (prefs.units.pressure) {
- case units::BAR:
- mbar = rint(number * 1000);
- break;
- case units::PSI:
- mbar = psi_to_mbar(number);
- break;
- default:
- mbar = 0;
- }
- }
- return mbar;
-}
-
-int parseGasMixO2(const QString &text)
-{
- QString gasString = text;
- int o2, number;
- if (gasString.contains(QObject::tr("AIR"), Qt::CaseInsensitive)) {
- o2 = O2_IN_AIR;
- } else if (gasString.contains(QObject::tr("EAN"), Qt::CaseInsensitive)) {
- gasString.remove(QRegExp("[^0-9]"));
- number = gasString.toInt();
- o2 = number * 10;
- } else if (gasString.contains("/")) {
- QStringList gasSplit = gasString.split("/");
- number = gasSplit[0].toInt();
- o2 = number * 10;
- } else {
- number = gasString.toInt();
- o2 = number * 10;
- }
- return o2;
-}
-
-int parseGasMixHE(const QString &text)
-{
- QString gasString = text;
- int he, number;
- if (gasString.contains("/")) {
- QStringList gasSplit = gasString.split("/");
- number = gasSplit[1].toInt();
- he = number * 10;
- } else {
- he = 0;
- }
- return he;
-}
-
-QString get_dive_duration_string(timestamp_t when, QString hourText, QString minutesText)
-{
- int hrs, mins;
- mins = (when + 59) / 60;
- hrs = mins / 60;
- mins -= hrs * 60;
-
- QString displayTime;
- if (hrs)
- displayTime = QString("%1%2%3%4").arg(hrs).arg(hourText).arg(mins, 2, 10, QChar('0')).arg(minutesText);
- else
- displayTime = QString("%1%2").arg(mins).arg(minutesText);
-
- return displayTime;
-}
-
-QString get_dive_date_string(timestamp_t when)
-{
- QDateTime ts;
- ts.setMSecsSinceEpoch(when * 1000L);
- return loc.toString(ts.toUTC(), QString(prefs.date_format) + " " + prefs.time_format);
-}
-
-QString get_short_dive_date_string(timestamp_t when)
-{
- QDateTime ts;
- ts.setMSecsSinceEpoch(when * 1000L);
- return loc.toString(ts.toUTC(), QString(prefs.date_format_short) + " " + prefs.time_format);
-}
-
-const char *get_dive_date_c_string(timestamp_t when)
-{
- QString text = get_dive_date_string(when);
- return strdup(text.toUtf8().data());
-}
-
-bool is_same_day(timestamp_t trip_when, timestamp_t dive_when)
-{
- static timestamp_t twhen = (timestamp_t) 0;
- static struct tm tmt;
- struct tm tmd;
-
- utc_mkdate(dive_when, &tmd);
-
- if (twhen != trip_when) {
- twhen = trip_when;
- utc_mkdate(twhen, &tmt);
- }
-
- return ((tmd.tm_mday == tmt.tm_mday) && (tmd.tm_mon == tmt.tm_mon) && (tmd.tm_year == tmt.tm_year));
-}
-
-QString get_trip_date_string(timestamp_t when, int nr, bool getday)
-{
- struct tm tm;
- utc_mkdate(when, &tm);
- QDateTime localTime = QDateTime::fromTime_t(when);
- localTime.setTimeSpec(Qt::UTC);
- QString ret ;
-
- QString suffix = " " + QObject::tr("(%n dive(s))", "", nr);
- if (getday) {
- ret = localTime.date().toString(prefs.date_format) + suffix;
- } else {
- ret = localTime.date().toString("MMM yy") + suffix;
- }
- return ret;
-
-}
-
-extern "C" void reverseGeoLookup(degrees_t latitude, degrees_t longitude, uint32_t uuid)
-{
- QNetworkRequest request;
- QNetworkAccessManager *rgl = new QNetworkAccessManager();
- request.setUrl(QString("http://open.mapquestapi.com/nominatim/v1/reverse.php?format=json&accept-language=%1&lat=%2&lon=%3")
- .arg(uiLanguage(NULL)).arg(latitude.udeg / 1000000.0).arg(longitude.udeg / 1000000.0));
- request.setRawHeader("Accept", "text/json");
- request.setRawHeader("User-Agent", getUserAgent().toUtf8());
- QNetworkReply *reply = rgl->get(request);
- QEventLoop loop;
- QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
- loop.exec();
- QJsonParseError errorObject;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &errorObject);
- if (errorObject.error != QJsonParseError::NoError) {
- qDebug() << errorObject.errorString();
- } else {
- QJsonObject obj = jsonDoc.object();
- QJsonObject address = obj.value("address").toObject();
- qDebug() << "found country:" << address.value("country").toString();
- struct dive_site *ds = get_dive_site_by_uuid(uuid);
- ds->notes = add_to_string(ds->notes, "countrytag: %s", address.value("country").toString().toUtf8().data());
- }
-}
-
-QHash<QString, QByteArray> hashOf;
-QMutex hashOfMutex;
-QHash<QByteArray, QString> localFilenameOf;
-QHash <QString, QImage > thumbnailCache;
-
-extern "C" char * hashstring(char * filename)
-{
- return hashOf[QString(filename)].toHex().data();
-}
-
-const QString hashfile_name()
-{
- return QString(system_default_directory()).append("/hashes");
-}
-
-extern "C" char *hashfile_name_string()
-{
- return strdup(hashfile_name().toUtf8().data());
-}
-
-void read_hashes()
-{
- QFile hashfile(hashfile_name());
- if (hashfile.open(QIODevice::ReadOnly)) {
- QDataStream stream(&hashfile);
- stream >> localFilenameOf;
- stream >> hashOf;
- stream >> thumbnailCache;
- hashfile.close();
- }
-}
-
-void write_hashes()
-{
- QSaveFile hashfile(hashfile_name());
- if (hashfile.open(QIODevice::WriteOnly)) {
- QDataStream stream(&hashfile);
- stream << localFilenameOf;
- stream << hashOf;
- stream << thumbnailCache;
- hashfile.commit();
- } else {
- qDebug() << "cannot open" << hashfile.fileName();
- }
-}
-
-void add_hash(const QString filename, QByteArray hash)
-{
- QMutexLocker locker(&hashOfMutex);
- hashOf[filename] = hash;
- localFilenameOf[hash] = filename;
-}
-
-QByteArray hashFile(const QString filename)
-{
- QCryptographicHash hash(QCryptographicHash::Sha1);
- QFile imagefile(filename);
- if (imagefile.exists() && imagefile.open(QIODevice::ReadOnly)) {
- hash.addData(&imagefile);
- add_hash(filename, hash.result());
- return hash.result();
- } else {
- return QByteArray();
- }
-}
-
-void learnHash(struct picture *picture, QByteArray hash)
-{
- if (picture->hash)
- free(picture->hash);
- QMutexLocker locker(&hashOfMutex);
- hashOf[QString(picture->filename)] = hash;
- picture->hash = strdup(hash.toHex());
-}
-
-QString localFilePath(const QString originalFilename)
-{
- if (hashOf.contains(originalFilename) && localFilenameOf.contains(hashOf[originalFilename]))
- return localFilenameOf[hashOf[originalFilename]];
- else
- return originalFilename;
-}
-
-QString fileFromHash(char *hash)
-{
- return localFilenameOf[QByteArray::fromHex(hash)];
-}
-
-// This needs to operate on a copy of picture as it frees it after finishing!
-void updateHash(struct picture *picture) {
- QByteArray hash = hashFile(fileFromHash(picture->hash));
- learnHash(picture, hash);
- picture_free(picture);
-}
-
-// This needs to operate on a copy of picture as it frees it after finishing!
-void hashPicture(struct picture *picture)
-{
- char *oldHash = copy_string(picture->hash);
- learnHash(picture, hashFile(QString(picture->filename)));
- if (!same_string(picture->hash, "") && !same_string(picture->hash, oldHash))
- mark_divelist_changed((true));
- free(oldHash);
- picture_free(picture);
-}
-
-extern "C" void cache_picture(struct picture *picture)
-{
- QString filename = picture->filename;
- if (!hashOf.contains(filename))
- QtConcurrent::run(hashPicture, clone_picture(picture));
-}
-
-void learnImages(const QDir dir, int max_recursions)
-{
- QStringList filters, files;
-
- if (max_recursions) {
- foreach (QString dirname, dir.entryList(QStringList(), QDir::NoDotAndDotDot | QDir::Dirs)) {
- learnImages(QDir(dir.filePath(dirname)), max_recursions - 1);
- }
- }
-
- foreach (QString format, QImageReader::supportedImageFormats()) {
- filters.append(QString("*.").append(format));
- }
-
- foreach (QString file, dir.entryList(filters, QDir::Files)) {
- files.append(dir.absoluteFilePath(file));
- }
-
- QtConcurrent::blockingMap(files, hashFile);
-}
-
-extern "C" const char *local_file_path(struct picture *picture)
-{
- QString hashString = picture->hash;
- if (hashString.isEmpty()) {
- QByteArray hash = hashFile(picture->filename);
- free(picture->hash);
- picture->hash = strdup(hash.toHex().data());
- }
- QString localFileName = fileFromHash(picture->hash);
- if (localFileName.isEmpty())
- localFileName = picture->filename;
- return strdup(qPrintable(localFileName));
-}
-
-extern "C" bool picture_exists(struct picture *picture)
-{
- QString localFilename = fileFromHash(picture->hash);
- QByteArray hash = hashFile(localFilename);
- return same_string(hash.toHex().data(), picture->hash);
-}
-
-const QString picturedir()
-{
- return QString(system_default_directory()).append("/picturedata/");
-}
-
-extern "C" char *picturedir_string()
-{
- return strdup(picturedir().toUtf8().data());
-}
-
-/* when we get a picture from git storage (local or remote) and can't find the picture
- * based on its hash, we create a local copy with the hash as filename and the appropriate
- * suffix */
-extern "C" void savePictureLocal(struct picture *picture, const char *data, int len)
-{
- QString dirname = picturedir();
- QDir localPictureDir(dirname);
- localPictureDir.mkpath(dirname);
- QString suffix(picture->filename);
- suffix.replace(QRegularExpression(".*\\."), "");
- QString filename(dirname + picture->hash + "." + suffix);
- QSaveFile out(filename);
- if (out.open(QIODevice::WriteOnly)) {
- out.write(data, len);
- out.commit();
- add_hash(filename, QByteArray::fromHex(picture->hash));
- }
-}
-
-extern "C" void picture_load_exif_data(struct picture *p)
-{
- EXIFInfo exif;
- memblock mem;
-
- if (readfile(localFilePath(QString(p->filename)).toUtf8().data(), &mem) <= 0)
- goto picture_load_exit;
- if (exif.parseFrom((const unsigned char *)mem.buffer, (unsigned)mem.size) != PARSE_EXIF_SUCCESS)
- goto picture_load_exit;
- p->longitude.udeg= lrint(1000000.0 * exif.GeoLocation.Longitude);
- p->latitude.udeg = lrint(1000000.0 * exif.GeoLocation.Latitude);
-
-picture_load_exit:
- free(mem.buffer);
- return;
-}
-
-QString get_gas_string(struct gasmix gas)
-{
- uint o2 = (get_o2(&gas) + 5) / 10, he = (get_he(&gas) + 5) / 10;
- QString result = gasmix_is_air(&gas) ? QObject::tr("AIR") : he == 0 ? (o2 == 100 ? QObject::tr("OXYGEN") : QString("EAN%1").arg(o2, 2, 10, QChar('0'))) : QString("%1/%2").arg(o2).arg(he);
- return result;
-}
-
-QString get_divepoint_gas_string(const divedatapoint &p)
-{
- return get_gas_string(p.gasmix);
-}
-
-weight_t string_to_weight(const char *str)
-{
- const char *end;
- double value = strtod_flags(str, &end, 0);
- QString rest = QString(end).trimmed();
- QString local_kg = QObject::tr("kg");
- QString local_lbs = QObject::tr("lbs");
- weight_t weight;
-
- if (rest.startsWith("kg") || rest.startsWith(local_kg))
- goto kg;
- // using just "lb" instead of "lbs" is intentional - some people might enter the singular
- if (rest.startsWith("lb") || rest.startsWith(local_lbs))
- goto lbs;
- if (prefs.units.weight == prefs.units.LBS)
- goto lbs;
-kg:
- weight.grams = rint(value * 1000);
- return weight;
-lbs:
- weight.grams = lbs_to_grams(value);
- return weight;
-}
-
-depth_t string_to_depth(const char *str)
-{
- const char *end;
- double value = strtod_flags(str, &end, 0);
- QString rest = QString(end).trimmed();
- QString local_ft = QObject::tr("ft");
- QString local_m = QObject::tr("m");
- depth_t depth;
-
- if (rest.startsWith("m") || rest.startsWith(local_m))
- goto m;
- if (rest.startsWith("ft") || rest.startsWith(local_ft))
- goto ft;
- if (prefs.units.length == prefs.units.FEET)
- goto ft;
-m:
- depth.mm = rint(value * 1000);
- return depth;
-ft:
- depth.mm = feet_to_mm(value);
- return depth;
-}
-
-pressure_t string_to_pressure(const char *str)
-{
- const char *end;
- double value = strtod_flags(str, &end, 0);
- QString rest = QString(end).trimmed();
- QString local_psi = QObject::tr("psi");
- QString local_bar = QObject::tr("bar");
- pressure_t pressure;
-
- if (rest.startsWith("bar") || rest.startsWith(local_bar))
- goto bar;
- if (rest.startsWith("psi") || rest.startsWith(local_psi))
- goto psi;
- if (prefs.units.pressure == prefs.units.PSI)
- goto psi;
-bar:
- pressure.mbar = rint(value * 1000);
- return pressure;
-psi:
- pressure.mbar = psi_to_mbar(value);
- return pressure;
-}
-
-volume_t string_to_volume(const char *str, pressure_t workp)
-{
- const char *end;
- double value = strtod_flags(str, &end, 0);
- QString rest = QString(end).trimmed();
- QString local_l = QObject::tr("l");
- QString local_cuft = QObject::tr("cuft");
- volume_t volume;
-
- if (rest.startsWith("l") || rest.startsWith("â„“") || rest.startsWith(local_l))
- goto l;
- if (rest.startsWith("cuft") || rest.startsWith(local_cuft))
- goto cuft;
- /*
- * If we don't have explicit units, and there is no working
- * pressure, we're going to assume "liter" even in imperial
- * measurements.
- */
- if (!workp.mbar)
- goto l;
- if (prefs.units.volume == prefs.units.LITER)
- goto l;
-cuft:
- if (workp.mbar)
- value /= bar_to_atm(workp.mbar / 1000.0);
- value = cuft_to_l(value);
-l:
- volume.mliter = rint(value * 1000);
- return volume;
-}
-
-fraction_t string_to_fraction(const char *str)
-{
- const char *end;
- double value = strtod_flags(str, &end, 0);
- fraction_t fraction;
-
- fraction.permille = rint(value * 10);
- return fraction;
-}
-
-int getCloudURL(QString &filename)
-{
- QString email = QString(prefs.cloud_storage_email);
- email.replace(QRegularExpression("[^a-zA-Z0-9@._+-]"), "");
- if (email.isEmpty() || same_string(prefs.cloud_storage_password, ""))
- return report_error("Please configure Cloud storage email and password in the preferences");
- if (email != prefs.cloud_storage_email_encoded) {
- free(prefs.cloud_storage_email_encoded);
- prefs.cloud_storage_email_encoded = strdup(qPrintable(email));
- }
- filename = QString(QString(prefs.cloud_git_url) + "/%1[%1]").arg(email);
- if (verbose)
- qDebug() << "cloud URL set as" << filename;
- return 0;
-}
-
-extern "C" char *cloud_url()
-{
- QString filename;
- getCloudURL(filename);
- return strdup(filename.toUtf8().data());
-}
-
-void loadPreferences()
-{
- QSettings s;
- QVariant v;
-
- uiLanguage(NULL);
- s.beginGroup("Units");
- if (s.value("unit_system").toString() == "metric") {
- prefs.unit_system = METRIC;
- prefs.units = SI_units;
- } else if (s.value("unit_system").toString() == "imperial") {
- prefs.unit_system = IMPERIAL;
- prefs.units = IMPERIAL_units;
- } else {
- prefs.unit_system = PERSONALIZE;
- GET_UNIT("length", length, units::FEET, units::METERS);
- GET_UNIT("pressure", pressure, units::PSI, units::BAR);
- GET_UNIT("volume", volume, units::CUFT, units::LITER);
- GET_UNIT("temperature", temperature, units::FAHRENHEIT, units::CELSIUS);
- GET_UNIT("weight", weight, units::LBS, units::KG);
- }
- GET_UNIT("vertical_speed_time", vertical_speed_time, units::MINUTES, units::SECONDS);
- GET_BOOL("coordinates", coordinates_traditional);
- s.endGroup();
- s.beginGroup("TecDetails");
- GET_BOOL("po2graph", pp_graphs.po2);
- GET_BOOL("pn2graph", pp_graphs.pn2);
- GET_BOOL("phegraph", pp_graphs.phe);
- GET_DOUBLE("po2threshold", pp_graphs.po2_threshold);
- GET_DOUBLE("pn2threshold", pp_graphs.pn2_threshold);
- GET_DOUBLE("phethreshold", pp_graphs.phe_threshold);
- GET_BOOL("mod", mod);
- GET_DOUBLE("modpO2", modpO2);
- GET_BOOL("ead", ead);
- GET_BOOL("redceiling", redceiling);
- GET_BOOL("dcceiling", dcceiling);
- GET_BOOL("calcceiling", calcceiling);
- GET_BOOL("calcceiling3m", calcceiling3m);
- GET_BOOL("calcndltts", calcndltts);
- GET_BOOL("calcalltissues", calcalltissues);
- GET_BOOL("hrgraph", hrgraph);
- GET_BOOL("tankbar", tankbar);
- GET_BOOL("RulerBar", rulergraph);
- GET_BOOL("percentagegraph", percentagegraph);
- GET_INT("gflow", gflow);
- GET_INT("gfhigh", gfhigh);
- GET_BOOL("gf_low_at_maxdepth", gf_low_at_maxdepth);
- GET_BOOL("show_ccr_setpoint",show_ccr_setpoint);
- GET_BOOL("show_ccr_sensors",show_ccr_sensors);
- GET_BOOL("zoomed_plot", zoomed_plot);
- set_gf(prefs.gflow, prefs.gfhigh, prefs.gf_low_at_maxdepth);
- GET_BOOL("show_sac", show_sac);
- GET_BOOL("display_unused_tanks", display_unused_tanks);
- GET_BOOL("show_average_depth", show_average_depth);
- s.endGroup();
-
- s.beginGroup("GeneralSettings");
- GET_TXT("default_filename", default_filename);
- GET_INT("default_file_behavior", default_file_behavior);
- if (prefs.default_file_behavior == UNDEFINED_DEFAULT_FILE) {
- // undefined, so check if there's a filename set and
- // use that, otherwise go with no default file
- if (QString(prefs.default_filename).isEmpty())
- prefs.default_file_behavior = NO_DEFAULT_FILE;
- else
- prefs.default_file_behavior = LOCAL_DEFAULT_FILE;
- }
- GET_TXT("default_cylinder", default_cylinder);
- GET_BOOL("use_default_file", use_default_file);
- GET_INT("defaultsetpoint", defaultsetpoint);
- GET_INT("o2consumption", o2consumption);
- GET_INT("pscr_ratio", pscr_ratio);
- s.endGroup();
-
- s.beginGroup("Display");
- // get the font from the settings or our defaults
- // respect the system default font size if none is explicitly set
- QFont defaultFont = s.value("divelist_font", prefs.divelist_font).value<QFont>();
- if (IS_FP_SAME(system_divelist_default_font_size, -1.0)) {
- prefs.font_size = qApp->font().pointSizeF();
- system_divelist_default_font_size = prefs.font_size; // this way we don't save it on exit
- }
- prefs.font_size = s.value("font_size", prefs.font_size).toFloat();
- // painful effort to ignore previous default fonts on Windows - ridiculous
- QString fontName = defaultFont.toString();
- if (fontName.contains(","))
- fontName = fontName.left(fontName.indexOf(","));
- if (subsurface_ignore_font(fontName.toUtf8().constData())) {
- defaultFont = QFont(prefs.divelist_font);
- } else {
- free((void *)prefs.divelist_font);
- prefs.divelist_font = strdup(fontName.toUtf8().constData());
- }
- defaultFont.setPointSizeF(prefs.font_size);
- qApp->setFont(defaultFont);
- GET_INT("displayinvalid", display_invalid_dives);
- s.endGroup();
-
- s.beginGroup("Animations");
- GET_INT("animation_speed", animation_speed);
- s.endGroup();
-
- s.beginGroup("Network");
- GET_INT_DEF("proxy_type", proxy_type, QNetworkProxy::DefaultProxy);
- GET_TXT("proxy_host", proxy_host);
- GET_INT("proxy_port", proxy_port);
- GET_BOOL("proxy_auth", proxy_auth);
- GET_TXT("proxy_user", proxy_user);
- GET_TXT("proxy_pass", proxy_pass);
- s.endGroup();
-
- s.beginGroup("CloudStorage");
- GET_TXT("email", cloud_storage_email);
-#ifndef SUBSURFACE_MOBILE
- GET_BOOL("save_password_local", save_password_local);
-#else
- // always save the password in Subsurface-mobile
- prefs.save_password_local = true;
-#endif
- if (prefs.save_password_local) { // GET_TEXT macro is not a single statement
- GET_TXT("password", cloud_storage_password);
- }
- GET_INT("cloud_verification_status", cloud_verification_status);
- GET_BOOL("cloud_background_sync", cloud_background_sync);
- GET_BOOL("git_local_only", git_local_only);
-
- // creating the git url here is simply a convenience when C code wants
- // to compare against that git URL - it's always derived from the base URL
- GET_TXT("cloud_base_url", cloud_base_url);
- prefs.cloud_git_url = strdup(qPrintable(QString(prefs.cloud_base_url) + "/git"));
- s.endGroup();
-
- // Subsurface webservice id is stored outside of the groups
- GET_TXT("subsurface_webservice_uid", userid);
-
- // but the related time / distance threshold (only used in the mobile app)
- // are in their own group
- s.beginGroup("locationService");
- GET_INT("distance_threshold", distance_threshold);
- GET_INT("time_threshold", time_threshold);
- s.endGroup();
-
- // GeoManagement
- s.beginGroup("geocoding");
-#ifdef DISABLED
- GET_BOOL("enable_geocoding", geocoding.enable_geocoding);
- GET_BOOL("parse_dive_without_gps", geocoding.parse_dive_without_gps);
- GET_BOOL("tag_existing_dives", geocoding.tag_existing_dives);
-#else
- prefs.geocoding.enable_geocoding = true;
-#endif
- GET_ENUM("cat0", taxonomy_category, geocoding.category[0]);
- GET_ENUM("cat1", taxonomy_category, geocoding.category[1]);
- GET_ENUM("cat2", taxonomy_category, geocoding.category[2]);
- s.endGroup();
-
- // GPS service time and distance thresholds
- s.beginGroup("LocationService");
- GET_INT("time_threshold", time_threshold);
- GET_INT("distance_threshold", distance_threshold);
- s.endGroup();
-}
-
-extern "C" bool isCloudUrl(const char *filename)
-{
- QString email = QString(prefs.cloud_storage_email);
- email.replace(QRegularExpression("[^a-zA-Z0-9@._+-]"), "");
- if (!email.isEmpty() &&
- QString(QString(prefs.cloud_git_url) + "/%1[%1]").arg(email) == filename)
- return true;
- return false;
-}
-
-extern "C" bool getProxyString(char **buffer)
-{
- if (prefs.proxy_type == QNetworkProxy::HttpProxy) {
- QString proxy;
- if (prefs.proxy_auth)
- proxy = QString("http://%1:%2@%3:%4").arg(prefs.proxy_user).arg(prefs.proxy_pass)
- .arg(prefs.proxy_host).arg(prefs.proxy_port);
- else
- proxy = QString("http://%1:%2").arg(prefs.proxy_host).arg(prefs.proxy_port);
- if (buffer)
- *buffer = strdup(qPrintable(proxy));
- return true;
- }
- return false;
-}
-
-extern "C" void subsurface_mkdir(const char *dir)
-{
- QDir directory;
- if (!directory.mkpath(QString(dir)))
- qDebug() << "failed to create path" << dir;
-}
-
-extern "C" void parse_display_units(char *line)
-{
- qDebug() << line;
-}
-
-static QByteArray currentApplicationState;
-
-QByteArray getCurrentAppState()
-{
- return currentApplicationState;
-}
-
-void setCurrentAppState(QByteArray state)
-{
- currentApplicationState = state;
-}
-
-extern "C" bool in_planner()
-{
- return (currentApplicationState == "PlanDive" || currentApplicationState == "EditPlannedDive");
-}
-
-void init_proxy()
-{
- QNetworkProxy proxy;
- proxy.setType(QNetworkProxy::ProxyType(prefs.proxy_type));
- proxy.setHostName(prefs.proxy_host);
- proxy.setPort(prefs.proxy_port);
- if (prefs.proxy_auth) {
- proxy.setUser(prefs.proxy_user);
- proxy.setPassword(prefs.proxy_pass);
- }
- QNetworkProxy::setApplicationProxy(proxy);
-}
-
-QString getUUID()
-{
- QString uuidString;
- QSettings settings;
- settings.beginGroup("UpdateManager");
- if (settings.contains("UUID")) {
- uuidString = settings.value("UUID").toString();
- } else {
- QUuid uuid = QUuid::createUuid();
- uuidString = uuid.toString();
- settings.setValue("UUID", uuidString);
- }
- uuidString.replace("{", "").replace("}", "");
- return uuidString;
-}
diff --git a/subsurface-core/qthelper.h b/subsurface-core/qthelper.h
deleted file mode 100644
index 3a5ef60e4..000000000
--- a/subsurface-core/qthelper.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#ifndef QTHELPER_H
-#define QTHELPER_H
-
-#include <QMultiMap>
-#include <QString>
-#include <stdint.h>
-#include "dive.h"
-#include "divelist.h"
-#include <QTranslator>
-#include <QDir>
-
-// global pointers for our translation
-extern QTranslator *qtTranslator, *ssrfTranslator;
-
-QString weight_string(int weight_in_grams);
-QString distance_string(int distanceInMeters);
-bool gpsHasChanged(struct dive *dive, struct dive *master, const QString &gps_text, bool *parsed_out = 0);
-extern "C" const char *printGPSCoords(int lat, int lon);
-QList<int> getDivesInTrip(dive_trip_t *trip);
-QString get_gas_string(struct gasmix gas);
-QString get_divepoint_gas_string(const divedatapoint& dp);
-void read_hashes();
-void write_hashes();
-void updateHash(struct picture *picture);
-QByteArray hashFile(const QString filename);
-void learnImages(const QDir dir, int max_recursions);
-void add_hash(const QString filename, QByteArray hash);
-void hashPicture(struct picture *picture);
-QString localFilePath(const QString originalFilename);
-QString fileFromHash(char *hash);
-void learnHash(struct picture *picture, QByteArray hash);
-extern "C" void cache_picture(struct picture *picture);
-weight_t string_to_weight(const char *str);
-depth_t string_to_depth(const char *str);
-pressure_t string_to_pressure(const char *str);
-volume_t string_to_volume(const char *str, pressure_t workp);
-fraction_t string_to_fraction(const char *str);
-int getCloudURL(QString &filename);
-void loadPreferences();
-bool parseGpsText(const QString &gps_text, double *latitude, double *longitude);
-QByteArray getCurrentAppState();
-void setCurrentAppState(QByteArray state);
-extern "C" bool in_planner();
-extern "C" void subsurface_mkdir(const char *dir);
-void init_proxy();
-QString getUUID();
-
-#endif // QTHELPER_H
diff --git a/subsurface-core/qthelperfromc.h b/subsurface-core/qthelperfromc.h
deleted file mode 100644
index 32aed8949..000000000
--- a/subsurface-core/qthelperfromc.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef QTHELPERFROMC_H
-#define QTHELPERFROMC_H
-
-bool getProxyString(char **buffer);
-bool canReachCloudServer();
-void updateWindowTitle();
-bool isCloudUrl(const char *filename);
-void subsurface_mkdir(const char *dir);
-char *get_file_name(const char *fileName);
-void copy_image_and_overwrite(const char *cfileName, const char *path, const char *cnewName);
-char *hashstring(char *filename);
-bool picture_exists(struct picture *picture);
-char *move_away(const char *path);
-const char *local_file_path(struct picture *picture);
-void savePictureLocal(struct picture *picture, const char *data, int len);
-void cache_picture(struct picture *picture);
-char *cloud_url();
-char *hashfile_name_string();
-char *picturedir_string();
-const char *subsurface_user_agent();
-
-#endif // QTHELPERFROMC_H
diff --git a/subsurface-core/qtserialbluetooth.cpp b/subsurface-core/qtserialbluetooth.cpp
deleted file mode 100644
index 6b104157a..000000000
--- a/subsurface-core/qtserialbluetooth.cpp
+++ /dev/null
@@ -1,416 +0,0 @@
-#include <errno.h>
-
-#include <QtBluetooth/QBluetoothAddress>
-#include <QtBluetooth/QBluetoothSocket>
-#include <QEventLoop>
-#include <QTimer>
-#include <QDebug>
-
-#include <libdivecomputer/version.h>
-
-#if defined(SSRF_CUSTOM_SERIAL)
-
-#if defined(Q_OS_WIN)
- #include <winsock2.h>
- #include <windows.h>
- #include <ws2bth.h>
-#endif
-
-#include <libdivecomputer/custom_serial.h>
-
-extern "C" {
-typedef struct serial_t {
- /* Library context. */
- dc_context_t *context;
- /*
- * RFCOMM socket used for Bluetooth Serial communication.
- */
-#if defined(Q_OS_WIN)
- SOCKET socket;
-#else
- QBluetoothSocket *socket;
-#endif
- long timeout;
-} serial_t;
-
-static int qt_serial_open(serial_t **out, dc_context_t *context, const char* devaddr)
-{
- if (out == NULL)
- return DC_STATUS_INVALIDARGS;
-
- // Allocate memory.
- serial_t *serial_port = (serial_t *) malloc (sizeof (serial_t));
- if (serial_port == NULL) {
- return DC_STATUS_NOMEMORY;
- }
-
- // Library context.
- serial_port->context = context;
-
- // Default to blocking reads.
- serial_port->timeout = -1;
-
-#if defined(Q_OS_WIN)
- // Create a RFCOMM socket
- serial_port->socket = ::socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
-
- if (serial_port->socket == INVALID_SOCKET) {
- free(serial_port);
- return DC_STATUS_IO;
- }
-
- SOCKADDR_BTH socketBthAddress;
- int socketBthAddressBth = sizeof (socketBthAddress);
- char *address = strdup(devaddr);
-
- ZeroMemory(&socketBthAddress, socketBthAddressBth);
- qDebug() << "Trying to connect to address " << devaddr;
-
- if (WSAStringToAddressA(address,
- AF_BTH,
- NULL,
- (LPSOCKADDR) &socketBthAddress,
- &socketBthAddressBth
- ) != 0) {
- qDebug() << "FAiled to convert the address " << address;
- free(address);
-
- return DC_STATUS_IO;
- }
-
- free(address);
-
- socketBthAddress.addressFamily = AF_BTH;
- socketBthAddress.port = BT_PORT_ANY;
- memset(&socketBthAddress.serviceClassId, 0, sizeof(socketBthAddress.serviceClassId));
- socketBthAddress.serviceClassId = SerialPortServiceClass_UUID;
-
- // Try to connect to the device
- if (::connect(serial_port->socket,
- (struct sockaddr *) &socketBthAddress,
- socketBthAddressBth
- ) != 0) {
- qDebug() << "Failed to connect to device";
-
- return DC_STATUS_NODEVICE;
- }
-
- qDebug() << "Succesfully connected to device";
-#else
- // Create a RFCOMM socket
- serial_port->socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);
-
- // Wait until the connection succeeds or until an error occurs
- QEventLoop loop;
- loop.connect(serial_port->socket, SIGNAL(connected()), SLOT(quit()));
- loop.connect(serial_port->socket, SIGNAL(error(QBluetoothSocket::SocketError)), SLOT(quit()));
-
- // Create a timer. If the connection doesn't succeed after five seconds or no error occurs then stop the opening step
- QTimer timer;
- int msec = 5000;
- timer.setSingleShot(true);
- loop.connect(&timer, SIGNAL(timeout()), SLOT(quit()));
-
-#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
- // First try to connect on RFCOMM channel 1. This is the default channel for most devices
- QBluetoothAddress remoteDeviceAddress(devaddr);
- serial_port->socket->connectToService(remoteDeviceAddress, 1, QIODevice::ReadWrite | QIODevice::Unbuffered);
- timer.start(msec);
- loop.exec();
-
- if (serial_port->socket->state() == QBluetoothSocket::ConnectingState) {
- // It seems that the connection on channel 1 took more than expected. Wait another 15 seconds
- qDebug() << "The connection on RFCOMM channel number 1 took more than expected. Wait another 15 seconds.";
- timer.start(3 * msec);
- loop.exec();
- } else if (serial_port->socket->state() == QBluetoothSocket::UnconnectedState) {
- // Try to connect on channel number 5. Maybe this is a Shearwater Petrel2 device.
- qDebug() << "Connection on channel 1 failed. Trying on channel number 5.";
- serial_port->socket->connectToService(remoteDeviceAddress, 5, QIODevice::ReadWrite | QIODevice::Unbuffered);
- timer.start(msec);
- loop.exec();
-
- if (serial_port->socket->state() == QBluetoothSocket::ConnectingState) {
- // It seems that the connection on channel 5 took more than expected. Wait another 15 seconds
- qDebug() << "The connection on RFCOMM channel number 5 took more than expected. Wait another 15 seconds.";
- timer.start(3 * msec);
- loop.exec();
- }
- }
-#elif defined(Q_OS_ANDROID) || (QT_VERSION >= 0x050500 && defined(Q_OS_MAC))
- // Try to connect to the device using the uuid of the Serial Port Profile service
- QBluetoothAddress remoteDeviceAddress(devaddr);
- serial_port->socket->connectToService(remoteDeviceAddress, QBluetoothUuid(QBluetoothUuid::SerialPort));
- timer.start(msec);
- loop.exec();
-
- if (serial_port->socket->state() == QBluetoothSocket::ConnectingState ||
- serial_port->socket->state() == QBluetoothSocket::ServiceLookupState) {
- // It seems that the connection step took more than expected. Wait another 20 seconds.
- qDebug() << "The connection step took more than expected. Wait another 20 seconds";
- timer.start(4 * msec);
- loop.exec();
- }
-#endif
- if (serial_port->socket->state() != QBluetoothSocket::ConnectedState) {
-
- // Get the latest error and try to match it with one from libdivecomputer
- QBluetoothSocket::SocketError err = serial_port->socket->error();
- qDebug() << "Failed to connect to device " << devaddr << ". Device state " << serial_port->socket->state() << ". Error: " << err;
-
- free (serial_port);
- switch(err) {
- case QBluetoothSocket::HostNotFoundError:
- case QBluetoothSocket::ServiceNotFoundError:
- return DC_STATUS_NODEVICE;
- case QBluetoothSocket::UnsupportedProtocolError:
- return DC_STATUS_PROTOCOL;
-#if QT_VERSION >= 0x050400
- case QBluetoothSocket::OperationError:
- return DC_STATUS_UNSUPPORTED;
-#endif
- case QBluetoothSocket::NetworkError:
- return DC_STATUS_IO;
- default:
- return QBluetoothSocket::UnknownSocketError;
- }
- }
-#endif
- *out = serial_port;
-
- return DC_STATUS_SUCCESS;
-}
-
-static int qt_serial_close(serial_t *device)
-{
- if (device == NULL)
- return DC_STATUS_SUCCESS;
-
-#if defined(Q_OS_WIN)
- // Cleanup
- closesocket(device->socket);
- free(device);
-#else
- if (device->socket == NULL) {
- free(device);
- return DC_STATUS_SUCCESS;
- }
-
- device->socket->close();
-
- delete device->socket;
- free(device);
-#endif
-
- return DC_STATUS_SUCCESS;
-}
-
-static int qt_serial_read(serial_t *device, void* data, unsigned int size)
-{
-#if defined(Q_OS_WIN)
- if (device == NULL)
- return DC_STATUS_INVALIDARGS;
-
- unsigned int nbytes = 0;
- int rc;
-
- while (nbytes < size) {
- rc = recv (device->socket, (char *) data + nbytes, size - nbytes, 0);
-
- if (rc < 0) {
- return -1; // Error during recv call.
- } else if (rc == 0) {
- break; // EOF reached.
- }
-
- nbytes += rc;
- }
-
- return nbytes;
-#else
- if (device == NULL || device->socket == NULL)
- return DC_STATUS_INVALIDARGS;
-
- unsigned int nbytes = 0;
- int rc;
-
- while(nbytes < size && device->socket->state() == QBluetoothSocket::ConnectedState)
- {
- rc = device->socket->read((char *) data + nbytes, size - nbytes);
-
- if (rc < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue; // Retry.
-
- return -1; // Something really bad happened :-(
- } else if (rc == 0) {
- // Wait until the device is available for read operations
- QEventLoop loop;
- QTimer timer;
- timer.setSingleShot(true);
- loop.connect(&timer, SIGNAL(timeout()), SLOT(quit()));
- loop.connect(device->socket, SIGNAL(readyRead()), SLOT(quit()));
- timer.start(device->timeout);
- loop.exec();
-
- if (!timer.isActive())
- return nbytes;
- }
-
- nbytes += rc;
- }
-
- return nbytes;
-#endif
-}
-
-static int qt_serial_write(serial_t *device, const void* data, unsigned int size)
-{
-#if defined(Q_OS_WIN)
- if (device == NULL)
- return DC_STATUS_INVALIDARGS;
-
- unsigned int nbytes = 0;
- int rc;
-
- while (nbytes < size) {
- rc = send(device->socket, (char *) data + nbytes, size - nbytes, 0);
-
- if (rc < 0) {
- return -1; // Error during send call.
- }
-
- nbytes += rc;
- }
-
- return nbytes;
-#else
- if (device == NULL || device->socket == NULL)
- return DC_STATUS_INVALIDARGS;
-
- unsigned int nbytes = 0;
- int rc;
-
- while(nbytes < size && device->socket->state() == QBluetoothSocket::ConnectedState)
- {
- rc = device->socket->write((char *) data + nbytes, size - nbytes);
-
- if (rc < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue; // Retry.
-
- return -1; // Something really bad happened :-(
- } else if (rc == 0) {
- break;
- }
-
- nbytes += rc;
- }
-
- return nbytes;
-#endif
-}
-
-static int qt_serial_flush(serial_t *device, int queue)
-{
- (void)queue;
- if (device == NULL)
- return DC_STATUS_INVALIDARGS;
-#if !defined(Q_OS_WIN)
- if (device->socket == NULL)
- return DC_STATUS_INVALIDARGS;
-#endif
- // TODO: add implementation
-
- return DC_STATUS_SUCCESS;
-}
-
-static int qt_serial_get_received(serial_t *device)
-{
-#if defined(Q_OS_WIN)
- if (device == NULL)
- return DC_STATUS_INVALIDARGS;
-
- // TODO use WSAIoctl to get the information
-
- return 0;
-#else
- if (device == NULL || device->socket == NULL)
- return DC_STATUS_INVALIDARGS;
-
- return device->socket->bytesAvailable();
-#endif
-}
-
-static int qt_serial_get_transmitted(serial_t *device)
-{
-#if defined(Q_OS_WIN)
- if (device == NULL)
- return DC_STATUS_INVALIDARGS;
-
- // TODO add implementation
-
- return 0;
-#else
- if (device == NULL || device->socket == NULL)
- return DC_STATUS_INVALIDARGS;
-
- return device->socket->bytesToWrite();
-#endif
-}
-
-static int qt_serial_set_timeout(serial_t *device, long timeout)
-{
- if (device == NULL)
- return DC_STATUS_INVALIDARGS;
-
- device->timeout = timeout;
-
- return DC_STATUS_SUCCESS;
-}
-
-
-const dc_serial_operations_t qt_serial_ops = {
- .open = qt_serial_open,
- .close = qt_serial_close,
- .read = qt_serial_read,
- .write = qt_serial_write,
- .flush = qt_serial_flush,
- .get_received = qt_serial_get_received,
- .get_transmitted = qt_serial_get_transmitted,
- .set_timeout = qt_serial_set_timeout
-};
-
-extern void dc_serial_init (dc_serial_t *serial, void *data, const dc_serial_operations_t *ops);
-
-dc_status_t dc_serial_qt_open(dc_serial_t **out, dc_context_t *context, const char *devaddr)
-{
- if (out == NULL)
- return DC_STATUS_INVALIDARGS;
-
- // Allocate memory.
- dc_serial_t *serial_device = (dc_serial_t *) malloc (sizeof (dc_serial_t));
-
- if (serial_device == NULL) {
- return DC_STATUS_NOMEMORY;
- }
-
- // Initialize data and function pointers
- dc_serial_init(serial_device, NULL, &qt_serial_ops);
-
- // Open the serial device.
- dc_status_t rc = (dc_status_t)qt_serial_open (&serial_device->port, context, devaddr);
- if (rc != DC_STATUS_SUCCESS) {
- free (serial_device);
- return rc;
- }
-
- // Set the type of the device
- serial_device->type = DC_TRANSPORT_BLUETOOTH;
-
- *out = serial_device;
-
- return DC_STATUS_SUCCESS;
-}
-}
-#endif
diff --git a/subsurface-core/save-git.c b/subsurface-core/save-git.c
deleted file mode 100644
index e22019ab0..000000000
--- a/subsurface-core/save-git.c
+++ /dev/null
@@ -1,1249 +0,0 @@
-// Clang has a bug on zero-initialization of C structs.
-#pragma clang diagnostic ignored "-Wmissing-field-initializers"
-
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <time.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <git2.h>
-
-#include "dive.h"
-#include "divelist.h"
-#include "device.h"
-#include "membuffer.h"
-#include "git-access.h"
-#include "version.h"
-#include "qthelperfromc.h"
-
-#define VA_BUF(b, fmt) do { va_list args; va_start(args, fmt); put_vformat(b, fmt, args); va_end(args); } while (0)
-
-static void cond_put_format(int cond, struct membuffer *b, const char *fmt, ...)
-{
- if (cond) {
- VA_BUF(b, fmt);
- }
-}
-
-#define SAVE(str, x) cond_put_format(dive->x, b, str " %d\n", dive->x)
-
-static void show_gps(struct membuffer *b, degrees_t latitude, degrees_t longitude)
-{
- if (latitude.udeg || longitude.udeg) {
- put_degrees(b, latitude, "gps ", " ");
- put_degrees(b, longitude, "", "\n");
- }
-}
-
-static void quote(struct membuffer *b, const char *text)
-{
- const char *p = text;
-
- for (;;) {
- const char *escape;
-
- switch (*p++) {
- default:
- continue;
- case 0:
- escape = NULL;
- break;
- case 1 ... 8:
- case 11:
- case 12:
- case 14 ... 31:
- escape = "?";
- break;
- case '\\':
- escape = "\\\\";
- break;
- case '"':
- escape = "\\\"";
- break;
- case '\n':
- escape = "\n\t";
- if (*p == '\n')
- escape = "\n";
- break;
- }
- put_bytes(b, text, (p - text - 1));
- if (!escape)
- break;
- put_string(b, escape);
- text = p;
- }
-}
-
-static void show_utf8(struct membuffer *b, const char *prefix, const char *value, const char *postfix)
-{
- if (value) {
- put_format(b, "%s\"", prefix);
- quote(b, value);
- put_format(b, "\"%s", postfix);
- }
-}
-
-static void save_overview(struct membuffer *b, struct dive *dive)
-{
- show_utf8(b, "divemaster ", dive->divemaster, "\n");
- show_utf8(b, "buddy ", dive->buddy, "\n");
- show_utf8(b, "suit ", dive->suit, "\n");
- show_utf8(b, "notes ", dive->notes, "\n");
-}
-
-static void save_tags(struct membuffer *b, struct tag_entry *tags)
-{
- const char *sep = " ";
-
- if (!tags)
- return;
- put_string(b, "tags");
- while (tags) {
- show_utf8(b, sep, tags->tag->source ? : tags->tag->name, "");
- sep = ", ";
- tags = tags->next;
- }
- put_string(b, "\n");
-}
-
-static void save_extra_data(struct membuffer *b, struct extra_data *ed)
-{
- while (ed) {
- if (ed->key && ed->value)
- put_format(b, "keyvalue \"%s\" \"%s\"\n", ed->key ? : "", ed->value ? : "");
- ed = ed->next;
- }
-}
-
-static void put_gasmix(struct membuffer *b, struct gasmix *mix)
-{
- int o2 = mix->o2.permille;
- int he = mix->he.permille;
-
- if (o2) {
- put_format(b, " o2=%u.%u%%", FRACTION(o2, 10));
- if (he)
- put_format(b, " he=%u.%u%%", FRACTION(he, 10));
- }
-}
-
-static void save_cylinder_info(struct membuffer *b, struct dive *dive)
-{
- int i, nr;
-
- nr = nr_cylinders(dive);
- for (i = 0; i < nr; i++) {
- cylinder_t *cylinder = dive->cylinder + i;
- int volume = cylinder->type.size.mliter;
- const char *description = cylinder->type.description;
-
- put_string(b, "cylinder");
- if (volume)
- put_milli(b, " vol=", volume, "l");
- put_pressure(b, cylinder->type.workingpressure, " workpressure=", "bar");
- show_utf8(b, " description=", description, "");
- strip_mb(b);
- put_gasmix(b, &cylinder->gasmix);
- put_pressure(b, cylinder->start, " start=", "bar");
- put_pressure(b, cylinder->end, " end=", "bar");
- if (cylinder->cylinder_use != OC_GAS)
- put_format(b, " use=%s", cylinderuse_text[cylinder->cylinder_use]);
-
- put_string(b, "\n");
- }
-}
-
-static void save_weightsystem_info(struct membuffer *b, struct dive *dive)
-{
- int i, nr;
-
- nr = nr_weightsystems(dive);
- for (i = 0; i < nr; i++) {
- weightsystem_t *ws = dive->weightsystem + i;
- int grams = ws->weight.grams;
- const char *description = ws->description;
-
- put_string(b, "weightsystem");
- put_milli(b, " weight=", grams, "kg");
- show_utf8(b, " description=", description, "");
- put_string(b, "\n");
- }
-}
-
-static void save_dive_temperature(struct membuffer *b, struct dive *dive)
-{
- if (dive->airtemp.mkelvin != dc_airtemp(&dive->dc))
- put_temperature(b, dive->airtemp, "airtemp ", "°C\n");
- if (dive->watertemp.mkelvin != dc_watertemp(&dive->dc))
- put_temperature(b, dive->watertemp, "watertemp ", "°C\n");
-}
-
-static void save_depths(struct membuffer *b, struct divecomputer *dc)
-{
- put_depth(b, dc->maxdepth, "maxdepth ", "m\n");
- put_depth(b, dc->meandepth, "meandepth ", "m\n");
-}
-
-static void save_temperatures(struct membuffer *b, struct divecomputer *dc)
-{
- put_temperature(b, dc->airtemp, "airtemp ", "°C\n");
- put_temperature(b, dc->watertemp, "watertemp ", "°C\n");
-}
-
-static void save_airpressure(struct membuffer *b, struct divecomputer *dc)
-{
- put_pressure(b, dc->surface_pressure, "surfacepressure ", "bar\n");
-}
-
-static void save_salinity(struct membuffer *b, struct divecomputer *dc)
-{
- /* only save if we have a value that isn't the default of sea water */
- if (!dc->salinity || dc->salinity == SEAWATER_SALINITY)
- return;
- put_salinity(b, dc->salinity, "salinity ", "g/l\n");
-}
-
-static void show_date(struct membuffer *b, timestamp_t when)
-{
- struct tm tm;
-
- utc_mkdate(when, &tm);
-
- put_format(b, "date %04u-%02u-%02u\n",
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
- put_format(b, "time %02u:%02u:%02u\n",
- tm.tm_hour, tm.tm_min, tm.tm_sec);
-}
-
-static void show_index(struct membuffer *b, int value, const char *pre, const char *post)
-{
- if (value)
- put_format(b, " %s%d%s", pre, value, post);
-}
-
-/*
- * Samples are saved as densely as possible while still being readable,
- * since they are the bulk of the data.
- *
- * For parsing, look at the units to figure out what the numbers are.
- */
-static void save_sample(struct membuffer *b, struct sample *sample, struct sample *old)
-{
- put_format(b, "%3u:%02u", FRACTION(sample->time.seconds, 60));
- put_milli(b, " ", sample->depth.mm, "m");
- put_temperature(b, sample->temperature, " ", "°C");
- put_pressure(b, sample->cylinderpressure, " ", "bar");
- put_pressure(b, sample->o2cylinderpressure," o2pressure=","bar");
-
- /*
- * We only show sensor information for samples with pressure, and only if it
- * changed from the previous sensor we showed.
- */
- if (sample->cylinderpressure.mbar && sample->sensor != old->sensor) {
- put_format(b, " sensor=%d", sample->sensor);
- old->sensor = sample->sensor;
- }
-
- /* the deco/ndl values are stored whenever they change */
- if (sample->ndl.seconds != old->ndl.seconds) {
- put_format(b, " ndl=%u:%02u", FRACTION(sample->ndl.seconds, 60));
- old->ndl = sample->ndl;
- }
- if (sample->tts.seconds != old->tts.seconds) {
- put_format(b, " tts=%u:%02u", FRACTION(sample->tts.seconds, 60));
- old->tts = sample->tts;
- }
- if (sample->in_deco != old->in_deco) {
- put_format(b, " in_deco=%d", sample->in_deco ? 1 : 0);
- old->in_deco = sample->in_deco;
- }
- if (sample->stoptime.seconds != old->stoptime.seconds) {
- put_format(b, " stoptime=%u:%02u", FRACTION(sample->stoptime.seconds, 60));
- old->stoptime = sample->stoptime;
- }
-
- if (sample->stopdepth.mm != old->stopdepth.mm) {
- put_milli(b, " stopdepth=", sample->stopdepth.mm, "m");
- old->stopdepth = sample->stopdepth;
- }
-
- if (sample->cns != old->cns) {
- put_format(b, " cns=%u%%", sample->cns);
- old->cns = sample->cns;
- }
-
- if (sample->rbt.seconds)
- put_format(b, " rbt=%u:%02u", FRACTION(sample->rbt.seconds, 60));
-
- if (sample->o2sensor[0].mbar != old->o2sensor[0].mbar) {
- put_milli(b, " sensor1=", sample->o2sensor[0].mbar, "bar");
- old->o2sensor[0] = sample->o2sensor[0];
- }
-
- if ((sample->o2sensor[1].mbar) && (sample->o2sensor[1].mbar != old->o2sensor[1].mbar)) {
- put_milli(b, " sensor2=", sample->o2sensor[1].mbar, "bar");
- old->o2sensor[1] = sample->o2sensor[1];
- }
-
- if ((sample->o2sensor[2].mbar) && (sample->o2sensor[2].mbar != old->o2sensor[2].mbar)) {
- put_milli(b, " sensor3=", sample->o2sensor[2].mbar, "bar");
- old->o2sensor[2] = sample->o2sensor[2];
- }
-
- if (sample->setpoint.mbar != old->setpoint.mbar) {
- put_milli(b, " po2=", sample->setpoint.mbar, "bar");
- old->setpoint = sample->setpoint;
- }
- show_index(b, sample->heartbeat, "heartbeat=", "");
- show_index(b, sample->bearing.degrees, "bearing=", "°");
- put_format(b, "\n");
-}
-
-static void save_samples(struct membuffer *b, int nr, struct sample *s)
-{
- struct sample dummy = {};
-
- while (--nr >= 0) {
- save_sample(b, s, &dummy);
- s++;
- }
-}
-
-static void save_one_event(struct membuffer *b, struct event *ev)
-{
- put_format(b, "event %d:%02d", FRACTION(ev->time.seconds, 60));
- show_index(b, ev->type, "type=", "");
- show_index(b, ev->flags, "flags=", "");
- show_index(b, ev->value, "value=", "");
- show_utf8(b, " name=", ev->name, "");
- if (event_is_gaschange(ev)) {
- if (ev->gas.index >= 0) {
- show_index(b, ev->gas.index, "cylinder=", "");
- put_gasmix(b, &ev->gas.mix);
- } else if (!event_gasmix_redundant(ev))
- put_gasmix(b, &ev->gas.mix);
- }
- put_string(b, "\n");
-}
-
-static void save_events(struct membuffer *b, struct event *ev)
-{
- while (ev) {
- save_one_event(b, ev);
- ev = ev->next;
- }
-}
-
-static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer *dc)
-{
- show_utf8(b, "model ", dc->model, "\n");
- if (dc->deviceid)
- put_format(b, "deviceid %08x\n", dc->deviceid);
- if (dc->diveid)
- put_format(b, "diveid %08x\n", dc->diveid);
- if (dc->when && dc->when != dive->when)
- show_date(b, dc->when);
- if (dc->duration.seconds && dc->duration.seconds != dive->dc.duration.seconds)
- put_duration(b, dc->duration, "duration ", "min\n");
- if (dc->divemode != OC) {
- put_format(b, "dctype %s\n", divemode_text[dc->divemode]);
- put_format(b, "numberofoxygensensors %d\n",dc->no_o2sensors);
- }
-
- save_depths(b, dc);
- save_temperatures(b, dc);
- save_airpressure(b, dc);
- save_salinity(b, dc);
- put_duration(b, dc->surfacetime, "surfacetime ", "min\n");
-
- save_extra_data(b, dc->extra_data);
- save_events(b, dc->events);
- save_samples(b, dc->samples, dc->sample);
-}
-
-/*
- * Note that we don't save the date and time or dive
- * number: they are encoded in the filename.
- */
-static void create_dive_buffer(struct dive *dive, struct membuffer *b)
-{
- put_format(b, "duration %u:%02u min\n", FRACTION(dive->dc.duration.seconds, 60));
- SAVE("rating", rating);
- SAVE("visibility", visibility);
- cond_put_format(dive->tripflag == NO_TRIP, b, "notrip\n");
- save_tags(b, dive->tag_list);
- cond_put_format(dive->dive_site_uuid && get_dive_site_by_uuid(dive->dive_site_uuid),
- b, "divesiteid %08x\n", dive->dive_site_uuid);
- if (verbose && dive->dive_site_uuid && !get_dive_site_by_uuid(dive->dive_site_uuid))
- fprintf(stderr, "removed reference to non-existant dive site with uuid %08x\n", dive->dive_site_uuid);
- save_overview(b, dive);
- save_cylinder_info(b, dive);
- save_weightsystem_info(b, dive);
- save_dive_temperature(b, dive);
-}
-
-static struct membuffer error_string_buffer = { 0 };
-
-/*
- * Note that the act of "getting" the error string
- * buffer doesn't de-allocate the buffer, but it does
- * set the buffer length to zero, so that any future
- * error reports will overwrite the string rather than
- * append to it.
- */
-const char *get_error_string(void)
-{
- const char *str;
-
- if (!error_string_buffer.len)
- return "";
- str = mb_cstring(&error_string_buffer);
- error_string_buffer.len = 0;
- return str;
-}
-
-int report_error(const char *fmt, ...)
-{
- struct membuffer *buf = &error_string_buffer;
-
- /* Previous unprinted errors? Add a newline in between */
- if (buf->len)
- put_bytes(buf, "\n", 1);
- VA_BUF(buf, fmt);
- mb_cstring(buf);
- return -1;
-}
-
-void report_message(const char *msg)
-{
- (void)report_error("%s", msg);
-}
-
-/*
- * libgit2 has a "git_treebuilder" concept, but it's broken, and can not
- * be used to do a flat tree (like the git "index") nor a recursive tree.
- * Stupid.
- *
- * So we have to do that "keep track of recursive treebuilder entries"
- * ourselves. We use 'git_treebuilder' for any regular files, and our own
- * data structures for recursive trees.
- *
- * When finally writing it out, we traverse the subdirectories depth-
- * first, writing them out, and then adding the written-out trees to
- * the git_treebuilder they existed in.
- */
-struct dir {
- git_treebuilder *files;
- struct dir *subdirs, *sibling;
- char unique, name[1];
-};
-
-static int tree_insert(git_treebuilder *dir, const char *name, int mkunique, git_oid *id, unsigned mode)
-{
- int ret;
- struct membuffer uniquename = { 0 };
-
- if (mkunique && git_treebuilder_get(dir, name)) {
- char hex[8];
- git_oid_nfmt(hex, 7, id);
- hex[7] = 0;
- put_format(&uniquename, "%s~%s", name, hex);
- name = mb_cstring(&uniquename);
- }
- ret = git_treebuilder_insert(NULL, dir, name, id, mode);
- free_buffer(&uniquename);
- return ret;
-}
-
-/*
- * This does *not* make sure the new subdirectory doesn't
- * alias some existing name. That is actually useful: you
- * can create multiple directories with the same name, and
- * set the "unique" flag, which will then append the SHA1
- * of the directory to the name when it is written.
- */
-static struct dir *new_directory(git_repository *repo, struct dir *parent, struct membuffer *namebuf)
-{
- struct dir *subdir;
- const char *name = mb_cstring(namebuf);
- int len = namebuf->len;
-
- subdir = malloc(sizeof(*subdir)+len);
-
- /*
- * It starts out empty: no subdirectories of its own,
- * and an empty treebuilder list of files.
- */
- subdir->subdirs = NULL;
- git_treebuilder_new(&subdir->files, repo, NULL);
- memcpy(subdir->name, name, len);
- subdir->unique = 0;
- subdir->name[len] = 0;
-
- /* Add it to the list of subdirs of the parent */
- subdir->sibling = parent->subdirs;
- parent->subdirs = subdir;
-
- return subdir;
-}
-
-static struct dir *mktree(git_repository *repo, struct dir *dir, const char *fmt, ...)
-{
- struct membuffer buf = { 0 };
- struct dir *subdir;
-
- VA_BUF(&buf, fmt);
- for (subdir = dir->subdirs; subdir; subdir = subdir->sibling) {
- if (subdir->unique)
- continue;
- if (strncmp(subdir->name, buf.buffer, buf.len))
- continue;
- if (!subdir->name[buf.len])
- break;
- }
- if (!subdir)
- subdir = new_directory(repo, dir, &buf);
- free_buffer(&buf);
- return subdir;
-}
-
-/*
- * The name of a dive is the date and the dive number (and possibly
- * the uniqueness suffix).
- *
- * Note that the time of the dive may not be the same as the
- * time of the directory structure it is created in: the dive
- * might be part of a trip that straddles a month (or even a
- * year).
- *
- * We do *not* want to use localized weekdays and cause peoples save
- * formats to depend on their locale.
- */
-static void create_dive_name(struct dive *dive, struct membuffer *name, struct tm *dirtm)
-{
- struct tm tm;
- static const char weekday[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
-
- utc_mkdate(dive->when, &tm);
- if (tm.tm_year != dirtm->tm_year)
- put_format(name, "%04u-", tm.tm_year + 1900);
- if (tm.tm_mon != dirtm->tm_mon)
- put_format(name, "%02u-", tm.tm_mon+1);
-
- /* a colon is an illegal char in a file name on Windows - use an '=' instead */
- put_format(name, "%02u-%s-%02u=%02u=%02u",
- tm.tm_mday, weekday[tm.tm_wday],
- tm.tm_hour, tm.tm_min, tm.tm_sec);
-}
-
-/*
- * Write a membuffer to the git repo, and free it
- */
-static int blob_insert(git_repository *repo, struct dir *tree, struct membuffer *b, const char *fmt, ...)
-{
- int ret;
- git_oid blob_id;
- struct membuffer name = { 0 };
-
- ret = git_blob_create_frombuffer(&blob_id, repo, b->buffer, b->len);
- free_buffer(b);
- if (ret)
- return ret;
-
- VA_BUF(&name, fmt);
- ret = tree_insert(tree->files, mb_cstring(&name), 1, &blob_id, GIT_FILEMODE_BLOB);
- free_buffer(&name);
- return ret;
-}
-
-static int save_one_divecomputer(git_repository *repo, struct dir *tree, struct dive *dive, struct divecomputer *dc, int idx)
-{
- int ret;
- struct membuffer buf = { 0 };
-
- save_dc(&buf, dive, dc);
- ret = blob_insert(repo, tree, &buf, "Divecomputer%c%03u", idx ? '-' : 0, idx);
- if (ret)
- report_error("divecomputer tree insert failed");
- return ret;
-}
-
-static int save_one_picture(git_repository *repo, struct dir *dir, struct picture *pic)
-{
- int offset = pic->offset.seconds;
- struct membuffer buf = { 0 };
- char sign = '+';
- unsigned h;
- int error;
-
- show_utf8(&buf, "filename ", pic->filename, "\n");
- show_gps(&buf, pic->latitude, pic->longitude);
- show_utf8(&buf, "hash ", pic->hash, "\n");
-
- /* Picture loading will load even negative offsets.. */
- if (offset < 0) {
- offset = -offset;
- sign = '-';
- }
-
- /* Use full hh:mm:ss format to make it all sort nicely */
- h = offset / 3600;
- offset -= h *3600;
- error = blob_insert(repo, dir, &buf, "%c%02u=%02u=%02u",
- sign, h, FRACTION(offset, 60));
-#if 0
- /* storing pictures into git was a mistake. This makes for HUGE git repositories */
- if (!error) {
- /* next store the actual picture; we prefix all picture names
- * with "PIC-" to make things easier on the parsing side */
- struct membuffer namebuf = { 0 };
- const char *localfn = local_file_path(pic);
- put_format(&namebuf, "PIC-%s", pic->hash);
- error = blob_insert_fromdisk(repo, dir, localfn, mb_cstring(&namebuf));
- free((void *)localfn);
- }
-#endif
- return error;
-}
-
-static int save_pictures(git_repository *repo, struct dir *dir, struct dive *dive)
-{
- if (dive->picture_list) {
- dir = mktree(repo, dir, "Pictures");
- FOR_EACH_PICTURE(dive) {
- save_one_picture(repo, dir, picture);
- }
- }
- return 0;
-}
-
-static int save_one_dive(git_repository *repo, struct dir *tree, struct dive *dive, struct tm *tm, bool cached_ok)
-{
- struct divecomputer *dc;
- struct membuffer buf = { 0 }, name = { 0 };
- struct dir *subdir;
- int ret, nr;
-
- /* Create dive directory */
- create_dive_name(dive, &name, tm);
-
- /*
- * If the dive git ID is valid, we just create the whole directory
- * with that ID
- */
- if (cached_ok && dive_cache_is_valid(dive)) {
- git_oid oid;
- git_oid_fromraw(&oid, dive->git_id);
- ret = tree_insert(tree->files, mb_cstring(&name), 1,
- &oid, GIT_FILEMODE_TREE);
- free_buffer(&name);
- if (ret)
- return report_error("cached dive tree insert failed");
- return 0;
- }
-
- subdir = new_directory(repo, tree, &name);
- subdir->unique = 1;
- free_buffer(&name);
-
- create_dive_buffer(dive, &buf);
- nr = dive->number;
- ret = blob_insert(repo, subdir, &buf,
- "Dive%c%d", nr ? '-' : 0, nr);
- if (ret)
- return report_error("dive save-file tree insert failed");
-
- /*
- * Save the dive computer data. If there is only one dive
- * computer, use index 0 for that (which disables the index
- * generation when naming it).
- */
- dc = &dive->dc;
- nr = dc->next ? 1 : 0;
- do {
- save_one_divecomputer(repo, subdir, dive, dc, nr++);
- dc = dc->next;
- } while (dc);
-
- /* Save the picture data, if any */
- save_pictures(repo, subdir, dive);
- return 0;
-}
-
-/*
- * We'll mark the trip directories unique, so this does not
- * need to be unique per se. It could be just "trip". But
- * to make things a bit more user-friendly, we try to take
- * the trip location into account.
- *
- * But no special characters, and no numbers (numbers in the
- * name could be construed as a date).
- *
- * So we might end up with "02-Maui", and then the unique
- * flag will make us write it out as "02-Maui~54b4" or
- * similar.
- */
-#define MAXTRIPNAME 15
-static void create_trip_name(dive_trip_t *trip, struct membuffer *name, struct tm *tm)
-{
- put_format(name, "%02u-", tm->tm_mday);
- if (trip->location) {
- char ascii_loc[MAXTRIPNAME+1], *p = trip->location;
- int i;
-
- for (i = 0; i < MAXTRIPNAME; ) {
- char c = *p++;
- switch (c) {
- case 0:
- case ',':
- case '.':
- break;
-
- case 'a' ... 'z':
- case 'A' ... 'Z':
- ascii_loc[i++] = c;
- continue;
- default:
- continue;
- }
- break;
- }
- if (i > 1) {
- put_bytes(name, ascii_loc, i);
- return;
- }
- }
-
- /* No useful name? */
- put_string(name, "trip");
-}
-
-static int save_trip_description(git_repository *repo, struct dir *dir, dive_trip_t *trip, struct tm *tm)
-{
- int ret;
- git_oid blob_id;
- struct membuffer desc = { 0 };
-
- put_format(&desc, "date %04u-%02u-%02u\n",
- tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
- put_format(&desc, "time %02u:%02u:%02u\n",
- tm->tm_hour, tm->tm_min, tm->tm_sec);
-
- show_utf8(&desc, "location ", trip->location, "\n");
- show_utf8(&desc, "notes ", trip->notes, "\n");
-
- ret = git_blob_create_frombuffer(&blob_id, repo, desc.buffer, desc.len);
- free_buffer(&desc);
- if (ret)
- return report_error("trip blob creation failed");
- ret = tree_insert(dir->files, "00-Trip", 0, &blob_id, GIT_FILEMODE_BLOB);
- if (ret)
- return report_error("trip description tree insert failed");
- return 0;
-}
-
-static void verify_shared_date(timestamp_t when, struct tm *tm)
-{
- struct tm tmp_tm;
-
- utc_mkdate(when, &tmp_tm);
- if (tmp_tm.tm_year != tm->tm_year) {
- tm->tm_year = -1;
- tm->tm_mon = -1;
- }
- if (tmp_tm.tm_mon != tm->tm_mon)
- tm->tm_mon = -1;
-}
-
-#define MIN_TIMESTAMP (0)
-#define MAX_TIMESTAMP (0x7fffffffffffffff)
-
-static int save_one_trip(git_repository *repo, struct dir *tree, dive_trip_t *trip, struct tm *tm, bool cached_ok)
-{
- int i;
- struct dive *dive;
- struct dir *subdir;
- struct membuffer name = { 0 };
- timestamp_t first, last;
-
- /* Create trip directory */
- create_trip_name(trip, &name, tm);
- subdir = new_directory(repo, tree, &name);
- subdir->unique = 1;
- free_buffer(&name);
-
- /* Trip description file */
- save_trip_description(repo, subdir, trip, tm);
-
- /* Make sure we write out the dates to the dives consistently */
- first = MAX_TIMESTAMP;
- last = MIN_TIMESTAMP;
- for_each_dive(i, dive) {
- if (dive->divetrip != trip)
- continue;
- if (dive->when < first)
- first = dive->when;
- if (dive->when > last)
- last = dive->when;
- }
- verify_shared_date(first, tm);
- verify_shared_date(last, tm);
-
- /* Save each dive in the directory */
- for_each_dive(i, dive) {
- if (dive->divetrip == trip)
- save_one_dive(repo, subdir, dive, tm, cached_ok);
- }
-
- return 0;
-}
-
-static void save_units(void *_b)
-{
- struct membuffer *b =_b;
- if (prefs.unit_system == METRIC)
- put_string(b, "units METRIC\n");
- else if (prefs.unit_system == IMPERIAL)
- put_string(b, "units IMPERIAL\n");
- else
- put_format(b, "units PERSONALIZE %s %s %s %s %s %s",
- prefs.units.length == METERS ? "METERS" : "FEET",
- prefs.units.volume == LITER ? "LITER" : "CUFT",
- prefs.units.pressure == BAR ? "BAR" : prefs.units.pressure == PSI ? "PSI" : "PASCAL",
- prefs.units.temperature == CELSIUS ? "CELSIUS" : prefs.units.temperature == FAHRENHEIT ? "FAHRENHEIT" : "KELVIN",
- prefs.units.weight == KG ? "KG" : "LBS",
- prefs.units.vertical_speed_time == SECONDS ? "SECONDS" : "MINUTES");
-}
-
-static void save_userid(void *_b)
-{
- struct membuffer *b = _b;
- if (prefs.save_userid_local)
- put_format(b, "userid %30s\n", prefs.userid);
-}
-
-static void save_one_device(void *_b, const char *model, uint32_t deviceid,
- const char *nickname, const char *serial, const char *firmware)
-{
- struct membuffer *b = _b;
-
- if (nickname && !strcmp(model, nickname))
- nickname = NULL;
- if (serial && !*serial) serial = NULL;
- if (firmware && !*firmware) firmware = NULL;
- if (nickname && !*nickname) nickname = NULL;
- if (!nickname && !serial && !firmware)
- return;
-
- show_utf8(b, "divecomputerid ", model, "");
- put_format(b, " deviceid=%08x", deviceid);
- show_utf8(b, " serial=", serial, "");
- show_utf8(b, " firmware=", firmware, "");
- show_utf8(b, " nickname=", nickname, "");
- put_string(b, "\n");
-}
-
-static void save_settings(git_repository *repo, struct dir *tree)
-{
- struct membuffer b = { 0 };
-
- put_format(&b, "version %d\n", DATAFORMAT_VERSION);
- save_userid(&b);
- call_for_each_dc(&b, save_one_device, false);
- cond_put_format(autogroup, &b, "autogroup\n");
- save_units(&b);
-
- blob_insert(repo, tree, &b, "00-Subsurface");
-}
-
-static void save_divesites(git_repository *repo, struct dir *tree)
-{
- struct dir *subdir;
- struct membuffer dirname = { 0 };
- put_format(&dirname, "01-Divesites");
- subdir = new_directory(repo, tree, &dirname);
-
- for (int i = 0; i < dive_site_table.nr; i++) {
- struct membuffer b = { 0 };
- struct dive_site *ds = get_dive_site(i);
- if (dive_site_is_empty(ds)) {
- int j;
- struct dive *d;
- for_each_dive(j, d) {
- if (d->dive_site_uuid == ds->uuid)
- d->dive_site_uuid = 0;
- }
- delete_dive_site(ds->uuid);
- i--; // since we just deleted that one
- continue;
- } else if (ds->name &&
- (strncmp(ds->name, "Auto-created dive", 17) == 0 ||
- strncmp(ds->name, "New Dive", 8) == 0)) {
- fprintf(stderr, "found an auto divesite %s\n", ds->name);
- // these are the two default names for sites from
- // the web service; if the site isn't used in any
- // dive (really? you didn't rename it?), delete it
- if (!is_dive_site_used(ds->uuid, false)) {
- if (verbose)
- fprintf(stderr, "Deleted unused auto-created dive site %s\n", ds->name);
- delete_dive_site(ds->uuid);
- i--; // since we just deleted that one
- continue;
- }
- }
- struct membuffer site_file_name = { 0 };
- put_format(&site_file_name, "Site-%08x", ds->uuid);
- show_utf8(&b, "name ", ds->name, "\n");
- show_utf8(&b, "description ", ds->description, "\n");
- show_utf8(&b, "notes ", ds->notes, "\n");
- show_gps(&b, ds->latitude, ds->longitude);
- for (int j = 0; j < ds->taxonomy.nr; j++) {
- struct taxonomy *t = &ds->taxonomy.category[j];
- if (t->category != TC_NONE && t->value) {
- put_format(&b, "geo cat %d origin %d ", t->category, t->origin);
- show_utf8(&b, "", t->value, "\n" );
- }
- }
- blob_insert(repo, subdir, &b, mb_cstring(&site_file_name));
- }
-}
-
-static int create_git_tree(git_repository *repo, struct dir *root, bool select_only, bool cached_ok)
-{
- int i;
- struct dive *dive;
- dive_trip_t *trip;
-
- save_settings(repo, root);
-
- save_divesites(repo, root);
-
- for (trip = dive_trip_list; trip != NULL; trip = trip->next)
- trip->index = 0;
-
- /* save the dives */
- int notify_increment = dive_table.nr > 10 ? dive_table.nr / 10 : 1;
- int last_threshold = 0;
- for_each_dive(i, dive) {
- struct tm tm;
- struct dir *tree;
- char buf[] = "save dives x0%";
-
- if (i / notify_increment > last_threshold) {
- // notify of progress - we cover the range of 20..50
- last_threshold = i / notify_increment;
- buf[11] = last_threshold + '0';
- git_storage_update_progress(20 + 3 * last_threshold, buf);
- }
-
- trip = dive->divetrip;
-
- if (select_only) {
- if (!dive->selected)
- continue;
- /* We don't save trips when doing selected dive saves */
- trip = NULL;
- }
-
- /* Create the date-based hierarchy */
- utc_mkdate(trip ? trip->when : dive->when, &tm);
- tree = mktree(repo, root, "%04d", tm.tm_year + 1900);
- tree = mktree(repo, tree, "%02d", tm.tm_mon + 1);
-
- if (trip) {
- /* Did we already save this trip? */
- if (trip->index)
- continue;
- trip->index = 1;
-
- /* Pass that new subdirectory in for save-trip */
- save_one_trip(repo, tree, trip, &tm, cached_ok);
- continue;
- }
-
- save_one_dive(repo, tree, dive, &tm, cached_ok);
- }
- return 0;
-}
-
-/*
- * See if we can find the parent ID that the git data came from
- */
-static git_object *try_to_find_parent(const char *hex_id, git_repository *repo)
-{
- git_oid object_id;
- git_commit *commit;
-
- if (!hex_id)
- return NULL;
- if (git_oid_fromstr(&object_id, hex_id))
- return NULL;
- if (git_commit_lookup(&commit, repo, &object_id))
- return NULL;
- return (git_object *)commit;
-}
-
-static int notify_cb(git_checkout_notify_t why,
- const char *path,
- const git_diff_file *baseline,
- const git_diff_file *target,
- const git_diff_file *workdir,
- void *payload)
-{
- (void) baseline;
- (void) target;
- (void) workdir;
- (void) payload;
- (void) why;
- report_error("File '%s' does not match in working tree", path);
- return 0; /* Continue with checkout */
-}
-
-static git_tree *get_git_tree(git_repository *repo, git_object *parent)
-{
- git_tree *tree;
- if (!parent)
- return NULL;
- if (git_tree_lookup(&tree, repo, git_commit_tree_id((const git_commit *) parent)))
- return NULL;
- return tree;
-}
-
-int update_git_checkout(git_repository *repo, git_object *parent, git_tree *tree)
-{
- git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE;
- opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT | GIT_CHECKOUT_NOTIFY_DIRTY;
- opts.notify_cb = notify_cb;
- opts.baseline = get_git_tree(repo, parent);
- return git_checkout_tree(repo, (git_object *) tree, &opts);
-}
-
-static int get_authorship(git_repository *repo, git_signature **authorp)
-{
-#if LIBGIT2_VER_MAJOR || LIBGIT2_VER_MINOR >= 20
- if (git_signature_default(authorp, repo) == 0)
- return 0;
-#endif
- /* Default name information, with potential OS overrides */
- struct user_info user = {
- .name = "Subsurface",
- .email = "subsurace@subsurface-divelog.org"
- };
-
- subsurface_user_info(&user);
-
- /* git_signature_default() is too recent */
- return git_signature_now(authorp, user.name, user.email);
-}
-
-static void create_commit_message(struct membuffer *msg)
-{
- int nr = dive_table.nr;
- struct dive *dive = get_dive(nr-1);
-
- if (dive) {
- dive_trip_t *trip = dive->divetrip;
- const char *location = get_dive_location(dive) ? : "no location";
- struct divecomputer *dc = &dive->dc;
- const char *sep = "\n";
-
- if (dive->number)
- nr = dive->number;
-
- put_format(msg, "dive %d: %s", nr, location);
- if (trip && trip->location && *trip->location && strcmp(trip->location, location))
- put_format(msg, " (%s)", trip->location);
- put_format(msg, "\n");
- do {
- if (dc->model && *dc->model) {
- put_format(msg, "%s%s", sep, dc->model);
- sep = ", ";
- }
- } while ((dc = dc->next) != NULL);
- put_format(msg, "\n");
- }
- put_format(msg, "Created by subsurface %s\n", subsurface_user_agent());
-}
-
-static int create_new_commit(git_repository *repo, const char *remote, const char *branch, git_oid *tree_id)
-{
- int ret;
- git_reference *ref;
- git_object *parent;
- git_oid commit_id;
- git_signature *author;
- git_commit *commit;
- git_tree *tree;
-
- ret = git_branch_lookup(&ref, repo, branch, GIT_BRANCH_LOCAL);
- switch (ret) {
- default:
- return report_error("Bad branch '%s' (%s)", branch, strerror(errno));
- case GIT_EINVALIDSPEC:
- return report_error("Invalid branch name '%s'", branch);
- case GIT_ENOTFOUND: /* We'll happily create it */
- ref = NULL;
- parent = try_to_find_parent(saved_git_id, repo);
- break;
- case 0:
- if (git_reference_peel(&parent, ref, GIT_OBJ_COMMIT))
- return report_error("Unable to look up parent in branch '%s'", branch);
-
- if (saved_git_id) {
- if (existing_filename)
- fprintf(stderr, "existing filename %s\n", existing_filename);
- const git_oid *id = git_commit_id((const git_commit *) parent);
- /* if we are saving to the same git tree we got this from, let's make
- * sure there is no confusion */
- if (same_string(existing_filename, remote) && git_oid_strcmp(id, saved_git_id))
- return report_error("The git branch does not match the git parent of the source");
- }
-
- /* all good */
- break;
- }
-
- if (git_tree_lookup(&tree, repo, tree_id))
- return report_error("Could not look up newly created tree");
-
- if (get_authorship(repo, &author))
- return report_error("No user name configuration in git repo");
-
- /* If the parent commit has the same tree ID, do not create a new commit */
- if (parent && git_oid_equal(tree_id, git_commit_tree_id((const git_commit *) parent))) {
- /* If the parent already came from the ref, the commit is already there */
- if (ref)
- return 0;
- /* Else we do want to create the new branch, but with the old commit */
- commit = (git_commit *) parent;
- } else {
- struct membuffer commit_msg = { 0 };
-
- create_commit_message(&commit_msg);
- if (git_commit_create_v(&commit_id, repo, NULL, author, author, NULL, mb_cstring(&commit_msg), tree, parent != NULL, parent))
- return report_error("Git commit create failed (%s)", strerror(errno));
- free_buffer(&commit_msg);
-
- if (git_commit_lookup(&commit, repo, &commit_id))
- return report_error("Could not look up newly created commit");
- }
-
- if (!ref) {
- if (git_branch_create(&ref, repo, branch, commit, 0))
- return report_error("Failed to create branch '%s'", branch);
- }
- /*
- * If it's a checked-out branch, try to also update the working
- * tree and index. If that fails (dirty working tree or whatever),
- * this is not technically a save error (we did save things in
- * the object database), but it can cause extreme confusion, so
- * warn about it.
- */
- if (git_branch_is_head(ref) && !git_repository_is_bare(repo)) {
- if (update_git_checkout(repo, parent, tree)) {
- const git_error *err = giterr_last();
- const char *errstr = err ? err->message : strerror(errno);
- report_error("Git branch '%s' is checked out, but worktree is dirty (%s)",
- branch, errstr);
- }
- }
-
- if (git_reference_set_target(&ref, ref, &commit_id, "Subsurface save event"))
- return report_error("Failed to update branch '%s'", branch);
- set_git_id(&commit_id);
-
- git_signature_free(author);
-
- return 0;
-}
-
-static int write_git_tree(git_repository *repo, struct dir *tree, git_oid *result)
-{
- int ret;
- struct dir *subdir;
-
- /* Write out our subdirectories, add them to the treebuilder, and free them */
- while ((subdir = tree->subdirs) != NULL) {
- git_oid id;
-
- if (!write_git_tree(repo, subdir, &id))
- tree_insert(tree->files, subdir->name, subdir->unique, &id, GIT_FILEMODE_TREE);
- tree->subdirs = subdir->sibling;
- free(subdir);
- };
-
- /* .. write out the resulting treebuilder */
- ret = git_treebuilder_write(result, tree->files);
-
- /* .. and free the now useless treebuilder */
- git_treebuilder_free(tree->files);
-
- return ret;
-}
-
-int do_git_save(git_repository *repo, const char *branch, const char *remote, bool select_only, bool create_empty)
-{
- struct dir tree;
- git_oid id;
- bool cached_ok;
-
- if (verbose)
- fprintf(stderr, "git storage: do git save\n");
-
- if (!create_empty) // so we are actually saving the dives
- git_storage_update_progress(19, "start git save");
-
- /*
- * Check if we can do the cached writes - we need to
- * have the original git commit we loaded in the repo
- */
- cached_ok = try_to_find_parent(saved_git_id, repo);
-
- /* Start with an empty tree: no subdirectories, no files */
- tree.name[0] = 0;
- tree.subdirs = NULL;
- if (git_treebuilder_new(&tree.files, repo, NULL))
- return report_error("git treebuilder failed");
-
- if (!create_empty)
- /* Populate our tree data structure */
- if (create_git_tree(repo, &tree, select_only, cached_ok))
- return -1;
-
- if (verbose)
- fprintf(stderr, "git storage, write git tree\n");
-
- if (write_git_tree(repo, &tree, &id))
- return report_error("git tree write failed");
-
- /* And save the tree! */
- if (create_new_commit(repo, remote, branch, &id))
- return report_error("creating commit failed");
-
- if (remote && prefs.cloud_background_sync) {
- /* now sync the tree with the cloud server */
- if (strstr(remote, prefs.cloud_git_url)) {
- return sync_with_remote(repo, remote, branch, RT_HTTPS);
- }
- }
- return 0;
-}
-
-int git_save_dives(struct git_repository *repo, const char *branch, const char *remote, bool select_only)
-{
- int ret;
-
- if (repo == dummy_git_repository)
- return report_error("Unable to open git repository '%s'", branch);
- ret = do_git_save(repo, branch, remote, select_only, false);
- git_repository_free(repo);
- free((void *)branch);
- return ret;
-}
diff --git a/subsurface-core/save-html.c b/subsurface-core/save-html.c
deleted file mode 100644
index 2d0ea9cf3..000000000
--- a/subsurface-core/save-html.c
+++ /dev/null
@@ -1,559 +0,0 @@
-// Clang has a bug on zero-initialization of C structs.
-#pragma clang diagnostic ignored "-Wmissing-field-initializers"
-
-#include "save-html.h"
-#include "qthelperfromc.h"
-#include "gettext.h"
-#include "stdio.h"
-
-void write_attribute(struct membuffer *b, const char *att_name, const char *value, const char *separator)
-{
- if (!value)
- value = "--";
- put_format(b, "\"%s\":\"", att_name);
- put_HTML_quoted(b, value);
- put_format(b, "\"%s", separator);
-}
-
-void save_photos(struct membuffer *b, const char *photos_dir, struct dive *dive)
-{
- struct picture *pic = dive->picture_list;
-
- if (!pic)
- return;
-
- char *separator = "\"photos\":[";
- do {
- put_string(b, separator);
- separator = ", ";
- char *fname = get_file_name(local_file_path(pic));
- put_format(b, "{\"filename\":\"%s\"}", fname);
- copy_image_and_overwrite(local_file_path(pic), photos_dir, fname);
- free(fname);
- pic = pic->next;
- } while (pic);
- put_string(b, "],");
-}
-
-void write_divecomputers(struct membuffer *b, struct dive *dive)
-{
- put_string(b, "\"divecomputers\":[");
- struct divecomputer *dc;
- char *separator = "";
- for_each_dc (dive, dc) {
- put_string(b, separator);
- separator = ", ";
- put_format(b, "{");
- write_attribute(b, "model", dc->model, ", ");
- if (dc->deviceid)
- put_format(b, "\"deviceid\":\"%08x\", ", dc->deviceid);
- else
- put_string(b, "\"deviceid\":\"--\", ");
- if (dc->diveid)
- put_format(b, "\"diveid\":\"%08x\" ", dc->diveid);
- else
- put_string(b, "\"diveid\":\"--\" ");
- put_format(b, "}");
- }
- put_string(b, "],");
-}
-
-void write_dive_status(struct membuffer *b, struct dive *dive)
-{
- put_format(b, "\"sac\":\"%d\",", dive->sac);
- put_format(b, "\"otu\":\"%d\",", dive->otu);
- put_format(b, "\"cns\":\"%d\",", dive->cns);
-}
-
-void put_HTML_bookmarks(struct membuffer *b, struct dive *dive)
-{
- struct event *ev = dive->dc.events;
-
- if (!ev)
- return;
-
- char *separator = "\"events\":[";
- do {
- put_string(b, separator);
- separator = ", ";
- put_format(b, "{\"name\":\"%s\",", ev->name);
- put_format(b, "\"value\":\"%d\",", ev->value);
- put_format(b, "\"type\":\"%d\",", ev->type);
- put_format(b, "\"time\":\"%d\"}", ev->time.seconds);
- ev = ev->next;
- } while (ev);
- put_string(b, "],");
-}
-
-static void put_weightsystem_HTML(struct membuffer *b, struct dive *dive)
-{
- int i, nr;
-
- nr = nr_weightsystems(dive);
-
- put_string(b, "\"Weights\":[");
-
- char *separator = "";
-
- for (i = 0; i < nr; i++) {
- weightsystem_t *ws = dive->weightsystem + i;
- int grams = ws->weight.grams;
- const char *description = ws->description;
-
- put_string(b, separator);
- separator = ", ";
- put_string(b, "{");
- put_HTML_weight_units(b, grams, "\"weight\":\"", "\",");
- write_attribute(b, "description", description, " ");
- put_string(b, "}");
- }
- put_string(b, "],");
-}
-
-static void put_cylinder_HTML(struct membuffer *b, struct dive *dive)
-{
- int i, nr;
- char *separator = "\"Cylinders\":[";
- nr = nr_cylinders(dive);
-
- if (!nr)
- put_string(b, separator);
-
- for (i = 0; i < nr; i++) {
- cylinder_t *cylinder = dive->cylinder + i;
- put_format(b, "%s{", separator);
- separator = ", ";
- write_attribute(b, "Type", cylinder->type.description, ", ");
- if (cylinder->type.size.mliter) {
- int volume = cylinder->type.size.mliter;
- if (prefs.units.volume == CUFT && cylinder->type.workingpressure.mbar)
- volume *= bar_to_atm(cylinder->type.workingpressure.mbar / 1000.0);
- put_HTML_volume_units(b, volume, "\"Size\":\"", " \", ");
- } else {
- write_attribute(b, "Size", "--", ", ");
- }
- put_HTML_pressure_units(b, cylinder->type.workingpressure, "\"WPressure\":\"", " \", ");
-
- if (cylinder->start.mbar) {
- put_HTML_pressure_units(b, cylinder->start, "\"SPressure\":\"", " \", ");
- } else {
- write_attribute(b, "SPressure", "--", ", ");
- }
-
- if (cylinder->end.mbar) {
- put_HTML_pressure_units(b, cylinder->end, "\"EPressure\":\"", " \", ");
- } else {
- write_attribute(b, "EPressure", "--", ", ");
- }
-
- if (cylinder->gasmix.o2.permille) {
- put_format(b, "\"O2\":\"%u.%u%%\",", FRACTION(cylinder->gasmix.o2.permille, 10));
- put_format(b, "\"He\":\"%u.%u%%\"", FRACTION(cylinder->gasmix.he.permille, 10));
- } else {
- write_attribute(b, "O2", "Air", "");
- }
-
- put_string(b, "}");
- }
-
- put_string(b, "],");
-}
-
-
-void put_HTML_samples(struct membuffer *b, struct dive *dive)
-{
- int i;
- put_format(b, "\"maxdepth\":%d,", dive->dc.maxdepth.mm);
- put_format(b, "\"duration\":%d,", dive->dc.duration.seconds);
- struct sample *s = dive->dc.sample;
-
- if (!dive->dc.samples)
- return;
-
- char *separator = "\"samples\":[";
- for (i = 0; i < dive->dc.samples; i++) {
- put_format(b, "%s[%d,%d,%d,%d]", separator, s->time.seconds, s->depth.mm, s->cylinderpressure.mbar, s->temperature.mkelvin);
- separator = ", ";
- s++;
- }
- put_string(b, "],");
-}
-
-void put_HTML_coordinates(struct membuffer *b, struct dive *dive)
-{
- struct dive_site *ds = get_dive_site_for_dive(dive);
- if (!ds)
- return;
- degrees_t latitude = ds->latitude;
- degrees_t longitude = ds->longitude;
-
- //don't put coordinates if in (0,0)
- if (!latitude.udeg && !longitude.udeg)
- return;
-
- put_string(b, "\"coordinates\":{");
- put_degrees(b, latitude, "\"lat\":\"", "\",");
- put_degrees(b, longitude, "\"lon\":\"", "\"");
- put_string(b, "},");
-}
-
-void put_HTML_date(struct membuffer *b, struct dive *dive, const char *pre, const char *post)
-{
- struct tm tm;
- utc_mkdate(dive->when, &tm);
- put_format(b, "%s%04u-%02u-%02u%s", pre, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, post);
-}
-
-void put_HTML_quoted(struct membuffer *b, const char *text)
-{
- int is_html = 1, is_attribute = 1;
- put_quoted(b, text, is_attribute, is_html);
-}
-
-void put_HTML_notes(struct membuffer *b, struct dive *dive, const char *pre, const char *post)
-{
- put_string(b, pre);
- if (dive->notes) {
- put_HTML_quoted(b, dive->notes);
- } else {
- put_string(b, "--");
- }
- put_string(b, post);
-}
-
-void put_HTML_pressure_units(struct membuffer *b, pressure_t pressure, const char *pre, const char *post)
-{
- const char *unit;
- double value;
-
- if (!pressure.mbar) {
- put_format(b, "%s%s", pre, post);
- return;
- }
-
- value = get_pressure_units(pressure.mbar, &unit);
- put_format(b, "%s%.1f %s%s", pre, value, unit, post);
-}
-
-void put_HTML_volume_units(struct membuffer *b, unsigned int ml, const char *pre, const char *post)
-{
- const char *unit;
- double value;
- int frac;
-
- value = get_volume_units(ml, &frac, &unit);
- put_format(b, "%s%.1f %s%s", pre, value, unit, post);
-}
-
-void put_HTML_weight_units(struct membuffer *b, unsigned int grams, const char *pre, const char *post)
-{
- const char *unit;
- double value;
- int frac;
-
- value = get_weight_units(grams, &frac, &unit);
- put_format(b, "%s%.1f %s%s", pre, value, unit, post);
-}
-
-void put_HTML_time(struct membuffer *b, struct dive *dive, const char *pre, const char *post)
-{
- struct tm tm;
- utc_mkdate(dive->when, &tm);
- put_format(b, "%s%02u:%02u:%02u%s", pre, tm.tm_hour, tm.tm_min, tm.tm_sec, post);
-}
-
-void put_HTML_depth(struct membuffer *b, struct dive *dive, const char *pre, const char *post)
-{
- const char *unit;
- double value;
- struct units *units_p = get_units();
-
- if (!dive->maxdepth.mm) {
- put_format(b, "%s--%s", pre, post);
- return;
- }
- value = get_depth_units(dive->maxdepth.mm, NULL, &unit);
-
- switch (units_p->length) {
- case METERS:
- default:
- put_format(b, "%s%.1f %s%s", pre, value, unit, post);
- break;
- case FEET:
- put_format(b, "%s%.0f %s%s", pre, value, unit, post);
- break;
- }
-}
-
-void put_HTML_airtemp(struct membuffer *b, struct dive *dive, const char *pre, const char *post)
-{
- const char *unit;
- double value;
-
- if (!dive->airtemp.mkelvin) {
- put_format(b, "%s--%s", pre, post);
- return;
- }
- value = get_temp_units(dive->airtemp.mkelvin, &unit);
- put_format(b, "%s%.1f %s%s", pre, value, unit, post);
-}
-
-void put_HTML_watertemp(struct membuffer *b, struct dive *dive, const char *pre, const char *post)
-{
- const char *unit;
- double value;
-
- if (!dive->watertemp.mkelvin) {
- put_format(b, "%s--%s", pre, post);
- return;
- }
- value = get_temp_units(dive->watertemp.mkelvin, &unit);
- put_format(b, "%s%.1f %s%s", pre, value, unit, post);
-}
-
-void put_HTML_tags(struct membuffer *b, struct dive *dive, const char *pre, const char *post)
-{
- put_string(b, pre);
- struct tag_entry *tag = dive->tag_list;
-
- if (!tag)
- put_string(b, "[\"--\"");
-
- char *separator = "[";
- while (tag) {
- put_format(b, "%s\"", separator);
- separator = ", ";
- put_HTML_quoted(b, tag->tag->name);
- put_string(b, "\"");
- tag = tag->next;
- }
- put_string(b, "]");
- put_string(b, post);
-}
-
-/* if exporting list_only mode, we neglect exporting the samples, bookmarks and cylinders */
-void write_one_dive(struct membuffer *b, struct dive *dive, const char *photos_dir, int *dive_no, const bool list_only)
-{
- put_string(b, "{");
- put_format(b, "\"number\":%d,", *dive_no);
- put_format(b, "\"subsurface_number\":%d,", dive->number);
- put_HTML_date(b, dive, "\"date\":\"", "\",");
- put_HTML_time(b, dive, "\"time\":\"", "\",");
- write_attribute(b, "location", get_dive_location(dive), ", ");
- put_HTML_coordinates(b, dive);
- put_format(b, "\"rating\":%d,", dive->rating);
- put_format(b, "\"visibility\":%d,", dive->visibility);
- put_format(b, "\"dive_duration\":\"%u:%02u min\",",
- FRACTION(dive->duration.seconds, 60));
- put_string(b, "\"temperature\":{");
- put_HTML_airtemp(b, dive, "\"air\":\"", "\",");
- put_HTML_watertemp(b, dive, "\"water\":\"", "\"");
- put_string(b, " },");
- write_attribute(b, "buddy", dive->buddy, ", ");
- write_attribute(b, "divemaster", dive->divemaster, ", ");
- write_attribute(b, "suit", dive->suit, ", ");
- put_HTML_tags(b, dive, "\"tags\":", ",");
- if (!list_only) {
- put_cylinder_HTML(b, dive);
- put_weightsystem_HTML(b, dive);
- put_HTML_samples(b, dive);
- put_HTML_bookmarks(b, dive);
- write_dive_status(b, dive);
- if (photos_dir && strcmp(photos_dir, ""))
- save_photos(b, photos_dir, dive);
- write_divecomputers(b, dive);
- }
- put_HTML_notes(b, dive, "\"notes\":\"", "\"");
- put_string(b, "}\n");
- (*dive_no)++;
-}
-
-void write_no_trip(struct membuffer *b, int *dive_no, bool selected_only, const char *photos_dir, const bool list_only, char *sep)
-{
- int i;
- struct dive *dive;
- char *separator = "";
- bool found_sel_dive = 0;
-
- for_each_dive (i, dive) {
- // write dive if it doesn't belong to any trip and the dive is selected
- // or we are in exporting all dives mode.
- if (!dive->divetrip && (dive->selected || !selected_only)) {
- if (!found_sel_dive) {
- put_format(b, "%c{", *sep);
- (*sep) = ',';
- put_format(b, "\"name\":\"Other\",");
- put_format(b, "\"dives\":[");
- found_sel_dive = 1;
- }
- put_string(b, separator);
- separator = ", ";
- write_one_dive(b, dive, photos_dir, dive_no, list_only);
- }
- }
- if (found_sel_dive)
- put_format(b, "]}\n\n");
-}
-
-void write_trip(struct membuffer *b, dive_trip_t *trip, int *dive_no, bool selected_only, const char *photos_dir, const bool list_only, char *sep)
-{
- struct dive *dive;
- char *separator = "";
- bool found_sel_dive = 0;
-
- for (dive = trip->dives; dive != NULL; dive = dive->next) {
- if (!dive->selected && selected_only)
- continue;
-
- // save trip if found at least one selected dive.
- if (!found_sel_dive) {
- found_sel_dive = 1;
- put_format(b, "%c {", *sep);
- (*sep) = ',';
- put_format(b, "\"name\":\"%s\",", trip->location);
- put_format(b, "\"dives\":[");
- }
- put_string(b, separator);
- separator = ", ";
- write_one_dive(b, dive, photos_dir, dive_no, list_only);
- }
-
- // close the trip object if contain dives.
- if (found_sel_dive)
- put_format(b, "]}\n\n");
-}
-
-void write_trips(struct membuffer *b, const char *photos_dir, bool selected_only, const bool list_only)
-{
- int i, dive_no = 0;
- struct dive *dive;
- dive_trip_t *trip;
- char sep_ = ' ';
- char *sep = &sep_;
-
- for (trip = dive_trip_list; trip != NULL; trip = trip->next)
- trip->index = 0;
-
- for_each_dive (i, dive) {
- trip = dive->divetrip;
-
- /*Continue if the dive have no trips or we have seen this trip before*/
- if (!trip || trip->index)
- continue;
-
- /* We haven't seen this trip before - save it and all dives */
- trip->index = 1;
- write_trip(b, trip, &dive_no, selected_only, photos_dir, list_only, sep);
- }
-
- /*Save all remaining trips into Others*/
- write_no_trip(b, &dive_no, selected_only, photos_dir, list_only, sep);
-}
-
-void export_list(struct membuffer *b, const char *photos_dir, bool selected_only, const bool list_only)
-{
- put_string(b, "trips=[");
- write_trips(b, photos_dir, selected_only, list_only);
- put_string(b, "]");
-}
-
-void export_HTML(const char *file_name, const char *photos_dir, const bool selected_only, const bool list_only)
-{
- FILE *f;
-
- struct membuffer buf = { 0 };
- export_list(&buf, photos_dir, selected_only, list_only);
-
- f = subsurface_fopen(file_name, "w+");
- if (!f) {
- report_error(translate("gettextFromC", "Can't open file %s"), file_name);
- } else {
- flush_buffer(&buf, f); /*check for writing errors? */
- fclose(f);
- }
- free_buffer(&buf);
-}
-
-void export_translation(const char *file_name)
-{
- FILE *f;
-
- struct membuffer buf = { 0 };
- struct membuffer *b = &buf;
-
- //export translated words here
- put_format(b, "translate={");
-
- //Dive list view
- write_attribute(b, "Number", translate("gettextFromC", "Number"), ", ");
- write_attribute(b, "Date", translate("gettextFromC", "Date"), ", ");
- write_attribute(b, "Time", translate("gettextFromC", "Time"), ", ");
- write_attribute(b, "Location", translate("gettextFromC", "Location"), ", ");
- write_attribute(b, "Air_Temp", translate("gettextFromC", "Air temp."), ", ");
- write_attribute(b, "Water_Temp", translate("gettextFromC", "Water temp."), ", ");
- write_attribute(b, "dives", translate("gettextFromC", "Dives"), ", ");
- write_attribute(b, "Expand_All", translate("gettextFromC", "Expand all"), ", ");
- write_attribute(b, "Collapse_All", translate("gettextFromC", "Collapse all"), ", ");
- write_attribute(b, "trips", translate("gettextFromC", "Trips"), ", ");
- write_attribute(b, "Statistics", translate("gettextFromC", "Statistics"), ", ");
- write_attribute(b, "Advanced_Search", translate("gettextFromC", "Advanced search"), ", ");
-
- //Dive expanded view
- write_attribute(b, "Rating", translate("gettextFromC", "Rating"), ", ");
- write_attribute(b, "Visibility", translate("gettextFromC", "Visibility"), ", ");
- write_attribute(b, "Duration", translate("gettextFromC", "Duration"), ", ");
- write_attribute(b, "DiveMaster", translate("gettextFromC", "Divemaster"), ", ");
- write_attribute(b, "Buddy", translate("gettextFromC", "Buddy"), ", ");
- write_attribute(b, "Suit", translate("gettextFromC", "Suit"), ", ");
- write_attribute(b, "Tags", translate("gettextFromC", "Tags"), ", ");
- write_attribute(b, "Notes", translate("gettextFromC", "Notes"), ", ");
- write_attribute(b, "Show_more_details", translate("gettextFromC", "Show more details"), ", ");
-
- //Yearly statistics view
- write_attribute(b, "Yearly_statistics", translate("gettextFromC", "Yearly statistics"), ", ");
- write_attribute(b, "Year", translate("gettextFromC", "Year"), ", ");
- write_attribute(b, "Total_Time", translate("gettextFromC", "Total time"), ", ");
- write_attribute(b, "Average_Time", translate("gettextFromC", "Average time"), ", ");
- write_attribute(b, "Shortest_Time", translate("gettextFromC", "Shortest time"), ", ");
- write_attribute(b, "Longest_Time", translate("gettextFromC", "Longest time"), ", ");
- write_attribute(b, "Average_Depth", translate("gettextFromC", "Average depth"), ", ");
- write_attribute(b, "Min_Depth", translate("gettextFromC", "Min. depth"), ", ");
- write_attribute(b, "Max_Depth", translate("gettextFromC", "Max. depth"), ", ");
- write_attribute(b, "Average_SAC", translate("gettextFromC", "Average SAC"), ", ");
- write_attribute(b, "Min_SAC", translate("gettextFromC", "Min. SAC"), ", ");
- write_attribute(b, "Max_SAC", translate("gettextFromC", "Max. SAC"), ", ");
- write_attribute(b, "Average_Temp", translate("gettextFromC", "Average temp."), ", ");
- write_attribute(b, "Min_Temp", translate("gettextFromC", "Min. temp."), ", ");
- write_attribute(b, "Max_Temp", translate("gettextFromC", "Max. temp."), ", ");
- write_attribute(b, "Back_to_List", translate("gettextFromC", "Back to list"), ", ");
-
- //dive detailed view
- write_attribute(b, "Dive_No", translate("gettextFromC", "Dive No."), ", ");
- write_attribute(b, "Dive_profile", translate("gettextFromC", "Dive profile"), ", ");
- write_attribute(b, "Dive_information", translate("gettextFromC", "Dive information"), ", ");
- write_attribute(b, "Dive_equipment", translate("gettextFromC", "Dive equipment"), ", ");
- write_attribute(b, "Type", translate("gettextFromC", "Type"), ", ");
- write_attribute(b, "Size", translate("gettextFromC", "Size"), ", ");
- write_attribute(b, "Work_Pressure", translate("gettextFromC", "Work pressure"), ", ");
- write_attribute(b, "Start_Pressure", translate("gettextFromC", "Start pressure"), ", ");
- write_attribute(b, "End_Pressure", translate("gettextFromC", "End pressure"), ", ");
- write_attribute(b, "Gas", translate("gettextFromC", "Gas"), ", ");
- write_attribute(b, "Weight", translate("gettextFromC", "Weight"), ", ");
- write_attribute(b, "Type", translate("gettextFromC", "Type"), ", ");
- write_attribute(b, "Events", translate("gettextFromC", "Events"), ", ");
- write_attribute(b, "Name", translate("gettextFromC", "Name"), ", ");
- write_attribute(b, "Value", translate("gettextFromC", "Value"), ", ");
- write_attribute(b, "Coordinates", translate("gettextFromC", "Coordinates"), ", ");
- write_attribute(b, "Dive_Status", translate("gettextFromC", "Dive status"), " ");
-
- put_format(b, "}");
-
- f = subsurface_fopen(file_name, "w+");
- if (!f) {
- report_error(translate("gettextFromC", "Can't open file %s"), file_name);
- } else {
- flush_buffer(&buf, f); /*check for writing errors? */
- fclose(f);
- }
- free_buffer(&buf);
-}
diff --git a/subsurface-core/save-html.h b/subsurface-core/save-html.h
deleted file mode 100644
index 13bb102b1..000000000
--- a/subsurface-core/save-html.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef HTML_SAVE_H
-#define HTML_SAVE_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "dive.h"
-#include "membuffer.h"
-
-void put_HTML_date(struct membuffer *b, struct dive *dive, const char *pre, const char *post);
-void put_HTML_depth(struct membuffer *b, struct dive *dive, const char *pre, const char *post);
-void put_HTML_airtemp(struct membuffer *b, struct dive *dive, const char *pre, const char *post);
-void put_HTML_watertemp(struct membuffer *b, struct dive *dive, const char *pre, const char *post);
-void put_HTML_time(struct membuffer *b, struct dive *dive, const char *pre, const char *post);
-void put_HTML_notes(struct membuffer *b, struct dive *dive, const char *pre, const char *post);
-void put_HTML_quoted(struct membuffer *b, const char *text);
-void put_HTML_pressure_units(struct membuffer *b, pressure_t pressure, const char *pre, const char *post);
-void put_HTML_weight_units(struct membuffer *b, unsigned int grams, const char *pre, const char *post);
-void put_HTML_volume_units(struct membuffer *b, unsigned int ml, const char *pre, const char *post);
-
-void export_HTML(const char *file_name, const char *photos_dir, const bool selected_only, const bool list_only);
-void export_list(struct membuffer *b, const char *photos_dir, bool selected_only, const bool list_only);
-
-void export_translation(const char *file_name);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/subsurface-core/save-xml.c b/subsurface-core/save-xml.c
deleted file mode 100644
index 2335637e8..000000000
--- a/subsurface-core/save-xml.c
+++ /dev/null
@@ -1,749 +0,0 @@
-// Clang has a bug on zero-initialization of C structs.
-#pragma clang diagnostic ignored "-Wmissing-field-initializers"
-
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <time.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include "dive.h"
-#include "divelist.h"
-#include "device.h"
-#include "membuffer.h"
-#include "strndup.h"
-#include "git-access.h"
-#include "qthelperfromc.h"
-
-/*
- * We're outputting utf8 in xml.
- * We need to quote the characters <, >, &.
- *
- * Technically I don't think we'd necessarily need to quote the control
- * characters, but at least libxml2 doesn't like them. It doesn't even
- * allow them quoted. So we just skip them and replace them with '?'.
- *
- * If we do this for attributes, we need to quote the quotes we use too.
- */
-static void quote(struct membuffer *b, const char *text, int is_attribute)
-{
- int is_html = 0;
- put_quoted(b, text, is_attribute, is_html);
-}
-
-static void show_utf8(struct membuffer *b, const char *text, const char *pre, const char *post, int is_attribute)
-{
- int len;
- char *cleaned;
-
- if (!text)
- return;
- /* remove leading and trailing space */
- /* We need to combine isascii() with isspace(),
- * because we can only trust isspace() with 7-bit ascii,
- * on windows for example */
- while (isascii(*text) && isspace(*text))
- text++;
- len = strlen(text);
- if (!len)
- return;
- while (len && isascii(text[len - 1]) && isspace(text[len - 1]))
- len--;
- cleaned = strndup(text, len);
- put_string(b, pre);
- quote(b, cleaned, is_attribute);
- put_string(b, post);
- free(cleaned);
-}
-
-static void save_depths(struct membuffer *b, struct divecomputer *dc)
-{
- /* What's the point of this dive entry again? */
- if (!dc->maxdepth.mm && !dc->meandepth.mm)
- return;
-
- put_string(b, " <depth");
- put_depth(b, dc->maxdepth, " max='", " m'");
- put_depth(b, dc->meandepth, " mean='", " m'");
- put_string(b, " />\n");
-}
-
-static void save_dive_temperature(struct membuffer *b, struct dive *dive)
-{
- if (!dive->airtemp.mkelvin && !dive->watertemp.mkelvin)
- return;
- if (dive->airtemp.mkelvin == dc_airtemp(&dive->dc) && dive->watertemp.mkelvin == dc_watertemp(&dive->dc))
- return;
-
- put_string(b, " <divetemperature");
- if (dive->airtemp.mkelvin != dc_airtemp(&dive->dc))
- put_temperature(b, dive->airtemp, " air='", " C'");
- if (dive->watertemp.mkelvin != dc_watertemp(&dive->dc))
- put_temperature(b, dive->watertemp, " water='", " C'");
- put_string(b, "/>\n");
-}
-
-static void save_temperatures(struct membuffer *b, struct divecomputer *dc)
-{
- if (!dc->airtemp.mkelvin && !dc->watertemp.mkelvin)
- return;
- put_string(b, " <temperature");
- put_temperature(b, dc->airtemp, " air='", " C'");
- put_temperature(b, dc->watertemp, " water='", " C'");
- put_string(b, " />\n");
-}
-
-static void save_airpressure(struct membuffer *b, struct divecomputer *dc)
-{
- if (!dc->surface_pressure.mbar)
- return;
- put_string(b, " <surface");
- put_pressure(b, dc->surface_pressure, " pressure='", " bar'");
- put_string(b, " />\n");
-}
-
-static void save_salinity(struct membuffer *b, struct divecomputer *dc)
-{
- /* only save if we have a value that isn't the default of sea water */
- if (!dc->salinity || dc->salinity == SEAWATER_SALINITY)
- return;
- put_string(b, " <water");
- put_salinity(b, dc->salinity, " salinity='", " g/l'");
- put_string(b, " />\n");
-}
-
-static void save_overview(struct membuffer *b, struct dive *dive)
-{
- show_utf8(b, dive->divemaster, " <divemaster>", "</divemaster>\n", 0);
- show_utf8(b, dive->buddy, " <buddy>", "</buddy>\n", 0);
- show_utf8(b, dive->notes, " <notes>", "</notes>\n", 0);
- show_utf8(b, dive->suit, " <suit>", "</suit>\n", 0);
-}
-
-static void put_gasmix(struct membuffer *b, struct gasmix *mix)
-{
- int o2 = mix->o2.permille;
- int he = mix->he.permille;
-
- if (o2) {
- put_format(b, " o2='%u.%u%%'", FRACTION(o2, 10));
- if (he)
- put_format(b, " he='%u.%u%%'", FRACTION(he, 10));
- }
-}
-
-static void save_cylinder_info(struct membuffer *b, struct dive *dive)
-{
- int i, nr;
-
- nr = nr_cylinders(dive);
-
- for (i = 0; i < nr; i++) {
- cylinder_t *cylinder = dive->cylinder + i;
- int volume = cylinder->type.size.mliter;
- const char *description = cylinder->type.description;
-
- put_format(b, " <cylinder");
- if (volume)
- put_milli(b, " size='", volume, " l'");
- put_pressure(b, cylinder->type.workingpressure, " workpressure='", " bar'");
- show_utf8(b, description, " description='", "'", 1);
- put_gasmix(b, &cylinder->gasmix);
- put_pressure(b, cylinder->start, " start='", " bar'");
- put_pressure(b, cylinder->end, " end='", " bar'");
- if (cylinder->cylinder_use != OC_GAS)
- show_utf8(b, cylinderuse_text[cylinder->cylinder_use], " use='", "'", 1);
- put_format(b, " />\n");
- }
-}
-
-static void save_weightsystem_info(struct membuffer *b, struct dive *dive)
-{
- int i, nr;
-
- nr = nr_weightsystems(dive);
-
- for (i = 0; i < nr; i++) {
- weightsystem_t *ws = dive->weightsystem + i;
- int grams = ws->weight.grams;
- const char *description = ws->description;
-
- put_format(b, " <weightsystem");
- put_milli(b, " weight='", grams, " kg'");
- show_utf8(b, description, " description='", "'", 1);
- put_format(b, " />\n");
- }
-}
-
-static void show_index(struct membuffer *b, int value, const char *pre, const char *post)
-{
- if (value)
- put_format(b, " %s%d%s", pre, value, post);
-}
-
-static void save_sample(struct membuffer *b, struct sample *sample, struct sample *old)
-{
- put_format(b, " <sample time='%u:%02u min'", FRACTION(sample->time.seconds, 60));
- put_milli(b, " depth='", sample->depth.mm, " m'");
- if (sample->temperature.mkelvin && sample->temperature.mkelvin != old->temperature.mkelvin) {
- put_temperature(b, sample->temperature, " temp='", " C'");
- old->temperature = sample->temperature;
- }
- put_pressure(b, sample->cylinderpressure, " pressure='", " bar'");
- put_pressure(b, sample->o2cylinderpressure, " o2pressure='", " bar'");
-
- /*
- * We only show sensor information for samples with pressure, and only if it
- * changed from the previous sensor we showed.
- */
- if (sample->cylinderpressure.mbar && sample->sensor != old->sensor) {
- put_format(b, " sensor='%d'", sample->sensor);
- old->sensor = sample->sensor;
- }
-
- /* the deco/ndl values are stored whenever they change */
- if (sample->ndl.seconds != old->ndl.seconds) {
- put_format(b, " ndl='%u:%02u min'", FRACTION(sample->ndl.seconds, 60));
- old->ndl = sample->ndl;
- }
- if (sample->tts.seconds != old->tts.seconds) {
- put_format(b, " tts='%u:%02u min'", FRACTION(sample->tts.seconds, 60));
- old->tts = sample->tts;
- }
- if (sample->rbt.seconds)
- put_format(b, " rbt='%u:%02u min'", FRACTION(sample->rbt.seconds, 60));
- if (sample->in_deco != old->in_deco) {
- put_format(b, " in_deco='%d'", sample->in_deco ? 1 : 0);
- old->in_deco = sample->in_deco;
- }
- if (sample->stoptime.seconds != old->stoptime.seconds) {
- put_format(b, " stoptime='%u:%02u min'", FRACTION(sample->stoptime.seconds, 60));
- old->stoptime = sample->stoptime;
- }
-
- if (sample->stopdepth.mm != old->stopdepth.mm) {
- put_milli(b, " stopdepth='", sample->stopdepth.mm, " m'");
- old->stopdepth = sample->stopdepth;
- }
-
- if (sample->cns != old->cns) {
- put_format(b, " cns='%u%%'", sample->cns);
- old->cns = sample->cns;
- }
-
- if ((sample->o2sensor[0].mbar) && (sample->o2sensor[0].mbar != old->o2sensor[0].mbar)) {
- put_milli(b, " sensor1='", sample->o2sensor[0].mbar, " bar'");
- old->o2sensor[0] = sample->o2sensor[0];
- }
-
- if ((sample->o2sensor[1].mbar) && (sample->o2sensor[1].mbar != old->o2sensor[1].mbar)) {
- put_milli(b, " sensor2='", sample->o2sensor[1].mbar, " bar'");
- old->o2sensor[1] = sample->o2sensor[1];
- }
-
- if ((sample->o2sensor[2].mbar) && (sample->o2sensor[2].mbar != old->o2sensor[2].mbar)) {
- put_milli(b, " sensor3='", sample->o2sensor[2].mbar, " bar'");
- old->o2sensor[2] = sample->o2sensor[2];
- }
-
- if (sample->setpoint.mbar != old->setpoint.mbar) {
- put_milli(b, " po2='", sample->setpoint.mbar, " bar'");
- old->setpoint = sample->setpoint;
- }
- show_index(b, sample->heartbeat, "heartbeat='", "'");
- show_index(b, sample->bearing.degrees, "bearing='", "'");
- put_format(b, " />\n");
-}
-
-static void save_one_event(struct membuffer *b, struct event *ev)
-{
- put_format(b, " <event time='%d:%02d min'", FRACTION(ev->time.seconds, 60));
- show_index(b, ev->type, "type='", "'");
- show_index(b, ev->flags, "flags='", "'");
- show_index(b, ev->value, "value='", "'");
- show_utf8(b, ev->name, " name='", "'", 1);
- if (event_is_gaschange(ev)) {
- if (ev->gas.index >= 0) {
- show_index(b, ev->gas.index, "cylinder='", "'");
- put_gasmix(b, &ev->gas.mix);
- } else if (!event_gasmix_redundant(ev))
- put_gasmix(b, &ev->gas.mix);
- }
- put_format(b, " />\n");
-}
-
-
-static void save_events(struct membuffer *b, struct event *ev)
-{
- while (ev) {
- save_one_event(b, ev);
- ev = ev->next;
- }
-}
-
-static void save_tags(struct membuffer *b, struct tag_entry *entry)
-{
- if (entry) {
- const char *sep = " tags='";
- do {
- struct divetag *tag = entry->tag;
- put_string(b, sep);
- /* If the tag has been translated, write the source to the xml file */
- quote(b, tag->source ?: tag->name, 1);
- sep = ", ";
- } while ((entry = entry->next) != NULL);
- put_string(b, "'");
- }
-}
-
-static void save_extra_data(struct membuffer *b, struct extra_data *ed)
-{
- while (ed) {
- if (ed->key && ed->value) {
- put_string(b, " <extradata");
- show_utf8(b, ed->key, " key='", "'", 1);
- show_utf8(b, ed->value, " value='", "'", 1);
- put_string(b, " />\n");
- }
- ed = ed->next;
- }
-}
-
-static void show_date(struct membuffer *b, timestamp_t when)
-{
- struct tm tm;
-
- utc_mkdate(when, &tm);
-
- put_format(b, " date='%04u-%02u-%02u'",
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
- put_format(b, " time='%02u:%02u:%02u'",
- tm.tm_hour, tm.tm_min, tm.tm_sec);
-}
-
-static void save_samples(struct membuffer *b, int nr, struct sample *s)
-{
- struct sample dummy = {};
-
- while (--nr >= 0) {
- save_sample(b, s, &dummy);
- s++;
- }
-}
-
-static void save_dc(struct membuffer *b, struct dive *dive, struct divecomputer *dc)
-{
- put_format(b, " <divecomputer");
- show_utf8(b, dc->model, " model='", "'", 1);
- if (dc->deviceid)
- put_format(b, " deviceid='%08x'", dc->deviceid);
- if (dc->diveid)
- put_format(b, " diveid='%08x'", dc->diveid);
- if (dc->when && dc->when != dive->when)
- show_date(b, dc->when);
- if (dc->duration.seconds && dc->duration.seconds != dive->dc.duration.seconds)
- put_duration(b, dc->duration, " duration='", " min'");
- if (dc->divemode != OC) {
- for (enum dive_comp_type i = 0; i < NUM_DC_TYPE; i++)
- if (dc->divemode == i)
- show_utf8(b, divemode_text[i], " dctype='", "'", 1);
- if (dc->no_o2sensors)
- put_format(b," no_o2sensors='%d'", dc->no_o2sensors);
- }
- put_format(b, ">\n");
- save_depths(b, dc);
- save_temperatures(b, dc);
- save_airpressure(b, dc);
- save_salinity(b, dc);
- put_duration(b, dc->surfacetime, " <surfacetime>", " min</surfacetime>\n");
- save_extra_data(b, dc->extra_data);
- save_events(b, dc->events);
- save_samples(b, dc->samples, dc->sample);
-
- put_format(b, " </divecomputer>\n");
-}
-
-static void save_picture(struct membuffer *b, struct picture *pic)
-{
- put_string(b, " <picture filename='");
- put_quoted(b, pic->filename, true, false);
- put_string(b, "'");
- if (pic->offset.seconds) {
- int offset = pic->offset.seconds;
- char sign = '+';
- if (offset < 0) {
- sign = '-';
- offset = -offset;
- }
- put_format(b, " offset='%c%u:%02u min'", sign, FRACTION(offset, 60));
- }
- if (pic->latitude.udeg || pic->longitude.udeg) {
- put_degrees(b, pic->latitude, " gps='", " ");
- put_degrees(b, pic->longitude, "", "'");
- }
- if (hashstring(pic->filename))
- put_format(b, " hash='%s'", hashstring(pic->filename));
-
- put_string(b, "/>\n");
-}
-
-void save_one_dive_to_mb(struct membuffer *b, struct dive *dive)
-{
- struct divecomputer *dc;
-
- put_string(b, "<dive");
- if (dive->number)
- put_format(b, " number='%d'", dive->number);
- if (dive->tripflag == NO_TRIP)
- put_format(b, " tripflag='NOTRIP'");
- if (dive->rating)
- put_format(b, " rating='%d'", dive->rating);
- if (dive->visibility)
- put_format(b, " visibility='%d'", dive->visibility);
- save_tags(b, dive->tag_list);
- if (dive->dive_site_uuid) {
- if (get_dive_site_by_uuid(dive->dive_site_uuid) != NULL)
- put_format(b, " divesiteid='%8x'", dive->dive_site_uuid);
- else if (verbose)
- fprintf(stderr, "removed reference to non-existant dive site with uuid %08x\n", dive->dive_site_uuid);
- }
- show_date(b, dive->when);
- put_format(b, " duration='%u:%02u min'>\n",
- FRACTION(dive->dc.duration.seconds, 60));
- save_overview(b, dive);
- save_cylinder_info(b, dive);
- save_weightsystem_info(b, dive);
- save_dive_temperature(b, dive);
- /* Save the dive computer data */
- for_each_dc(dive, dc)
- save_dc(b, dive, dc);
- FOR_EACH_PICTURE(dive)
- save_picture(b, picture);
- put_format(b, "</dive>\n");
-}
-
-int save_dive(FILE *f, struct dive *dive)
-{
- struct membuffer buf = { 0 };
-
- save_one_dive_to_mb(&buf, dive);
- flush_buffer(&buf, f);
- /* Error handling? */
- return 0;
-}
-
-static void save_trip(struct membuffer *b, dive_trip_t *trip)
-{
- int i;
- struct dive *dive;
-
- put_format(b, "<trip");
- show_date(b, trip->when);
- show_utf8(b, trip->location, " location=\'", "\'", 1);
- put_format(b, ">\n");
- show_utf8(b, trip->notes, "<notes>", "</notes>\n", 0);
-
- /*
- * Incredibly cheesy: we want to save the dives sorted, and they
- * are sorted in the dive array.. So instead of using the dive
- * list in the trip, we just traverse the global dive array and
- * check the divetrip pointer..
- */
- for_each_dive(i, dive) {
- if (dive->divetrip == trip)
- save_one_dive_to_mb(b, dive);
- }
-
- put_format(b, "</trip>\n");
-}
-
-static void save_one_device(void *_f, const char *model, uint32_t deviceid,
- const char *nickname, const char *serial_nr, const char *firmware)
-{
- struct membuffer *b = _f;
-
- /* Nicknames that are empty or the same as the device model are not interesting */
- if (nickname) {
- if (!*nickname || !strcmp(model, nickname))
- nickname = NULL;
- }
-
- /* Serial numbers that are empty are not interesting */
- if (serial_nr && !*serial_nr)
- serial_nr = NULL;
-
- /* Firmware strings that are empty are not interesting */
- if (firmware && !*firmware)
- firmware = NULL;
-
- /* Do we have anything interesting about this dive computer to save? */
- if (!serial_nr && !nickname && !firmware)
- return;
-
- put_format(b, "<divecomputerid");
- show_utf8(b, model, " model='", "'", 1);
- put_format(b, " deviceid='%08x'", deviceid);
- show_utf8(b, serial_nr, " serial='", "'", 1);
- show_utf8(b, firmware, " firmware='", "'", 1);
- show_utf8(b, nickname, " nickname='", "'", 1);
- put_format(b, "/>\n");
-}
-
-int save_dives(const char *filename)
-{
- return save_dives_logic(filename, false);
-}
-
-void save_dives_buffer(struct membuffer *b, const bool select_only)
-{
- int i;
- struct dive *dive;
- dive_trip_t *trip;
-
- put_format(b, "<divelog program='subsurface' version='%d'>\n<settings>\n", DATAFORMAT_VERSION);
-
- if (prefs.save_userid_local)
- put_format(b, " <userid>%30s</userid>\n", prefs.userid);
-
- /* save the dive computer nicknames, if any */
- call_for_each_dc(b, save_one_device, select_only);
- if (autogroup)
- put_format(b, " <autogroup state='1' />\n");
- put_format(b, "</settings>\n");
-
- /* save the dive sites - to make the output consistent let's sort the table, first */
- dive_site_table_sort();
- put_format(b, "<divesites>\n");
- for (i = 0; i < dive_site_table.nr; i++) {
- int j;
- struct dive *d;
- struct dive_site *ds = get_dive_site(i);
- if (dive_site_is_empty(ds)) {
- for_each_dive(j, d) {
- if (d->dive_site_uuid == ds->uuid)
- d->dive_site_uuid = 0;
- }
- delete_dive_site(ds->uuid);
- i--; // since we just deleted that one
- continue;
- } else if (ds->name &&
- (strncmp(ds->name, "Auto-created dive", 17) == 0 ||
- strncmp(ds->name, "New Dive", 8) == 0)) {
- // these are the two default names for sites from
- // the web service; if the site isn't used in any
- // dive (really? you didn't rename it?), delete it
- if (!is_dive_site_used(ds->uuid, false)) {
- if (verbose)
- fprintf(stderr, "Deleted unused auto-created dive site %s\n", ds->name);
- delete_dive_site(ds->uuid);
- i--; // since we just deleted that one
- continue;
- }
- }
- if (select_only && !is_dive_site_used(ds->uuid, true))
- continue;
-
- put_format(b, "<site uuid='%8x'", ds->uuid);
- show_utf8(b, ds->name, " name='", "'", 1);
- if (ds->latitude.udeg || ds->longitude.udeg) {
- put_degrees(b, ds->latitude, " gps='", " ");
- put_degrees(b, ds->longitude, "", "'");
- }
- show_utf8(b, ds->description, " description='", "'", 1);
- put_format(b, ">\n");
- show_utf8(b, ds->notes, " <notes>", " </notes>\n", 0);
- if (ds->taxonomy.nr) {
- for (int j = 0; j < ds->taxonomy.nr; j++) {
- struct taxonomy *t = &ds->taxonomy.category[j];
- if (t->category != TC_NONE && t->value) {
- put_format(b, " <geo cat='%d'", t->category);
- put_format(b, " origin='%d'", t->origin);
- show_utf8(b, t->value, " value='", "'/>\n", 1);
- }
- }
- }
- put_format(b, "</site>\n");
- }
- put_format(b, "</divesites>\n<dives>\n");
- for (trip = dive_trip_list; trip != NULL; trip = trip->next)
- trip->index = 0;
-
- /* save the dives */
- for_each_dive(i, dive) {
- if (select_only) {
-
- if (!dive->selected)
- continue;
- save_one_dive_to_mb(b, dive);
-
- } else {
- trip = dive->divetrip;
-
- /* Bare dive without a trip? */
- if (!trip) {
- save_one_dive_to_mb(b, dive);
- continue;
- }
-
- /* Have we already seen this trip (and thus saved this dive?) */
- if (trip->index)
- continue;
-
- /* We haven't seen this trip before - save it and all dives */
- trip->index = 1;
- save_trip(b, trip);
- }
- }
- put_format(b, "</dives>\n</divelog>\n");
-}
-
-static void save_backup(const char *name, const char *ext, const char *new_ext)
-{
- int len = strlen(name);
- int a = strlen(ext), b = strlen(new_ext);
- char *newname;
-
- /* len up to and including the final '.' */
- len -= a;
- if (len <= 1)
- return;
- if (name[len - 1] != '.')
- return;
- /* msvc doesn't have strncasecmp, has _strnicmp instead - crazy */
- if (strncasecmp(name + len, ext, a))
- return;
-
- newname = malloc(len + b + 1);
- if (!newname)
- return;
-
- memcpy(newname, name, len);
- memcpy(newname + len, new_ext, b + 1);
-
- /*
- * Ignore errors. Maybe we can't create the backup file,
- * maybe no old file existed. Regardless, we'll write the
- * new file.
- */
- (void) subsurface_rename(name, newname);
- free(newname);
-}
-
-static void try_to_backup(const char *filename)
-{
- char extension[][5] = { "xml", "ssrf", "" };
- int i = 0;
- int flen = strlen(filename);
-
- /* Maybe we might want to make this configurable? */
- while (extension[i][0] != '\0') {
- int elen = strlen(extension[i]);
- if (strcasecmp(filename + flen - elen, extension[i]) == 0) {
- if (last_xml_version < DATAFORMAT_VERSION) {
- int se_len = strlen(extension[i]) + 5;
- char *special_ext = malloc(se_len);
- snprintf(special_ext, se_len, "%s.v%d", extension[i], last_xml_version);
- save_backup(filename, extension[i], special_ext);
- free(special_ext);
- } else {
- save_backup(filename, extension[i], "bak");
- }
- break;
- }
- i++;
- }
-}
-
-int save_dives_logic(const char *filename, const bool select_only)
-{
- struct membuffer buf = { 0 };
- FILE *f;
- void *git;
- const char *branch, *remote;
- int error;
-
- git = is_git_repository(filename, &branch, &remote, false);
- if (git)
- return git_save_dives(git, branch, remote, select_only);
-
- save_dives_buffer(&buf, select_only);
-
- if (same_string(filename, "-")) {
- f = stdout;
- } else {
- try_to_backup(filename);
- error = -1;
- f = subsurface_fopen(filename, "w");
- }
- if (f) {
- flush_buffer(&buf, f);
- error = fclose(f);
- }
- if (error)
- report_error("Save failed (%s)", strerror(errno));
-
- free_buffer(&buf);
- return error;
-}
-
-int export_dives_xslt(const char *filename, const bool selected, const int units, const char *export_xslt)
-{
- FILE *f;
- struct membuffer buf = { 0 };
- xmlDoc *doc;
- xsltStylesheetPtr xslt = NULL;
- xmlDoc *transformed;
- int res = 0;
- char *params[3];
- int pnr = 0;
- char unitstr[3];
-
- if (verbose)
- fprintf(stderr, "export_dives_xslt with stylesheet %s\n", export_xslt);
-
- if (!filename)
- return report_error("No filename for export");
-
- /* Save XML to file and convert it into a memory buffer */
- save_dives_buffer(&buf, selected);
-
- /*
- * Parse the memory buffer into XML document and
- * transform it to selected export format, finally dumping
- * the XML into a character buffer.
- */
- doc = xmlReadMemory(buf.buffer, buf.len, "divelog", NULL, 0);
- free_buffer(&buf);
- if (!doc)
- return report_error("Failed to read XML memory");
-
- /* Convert to export format */
- xslt = get_stylesheet(export_xslt);
- if (!xslt)
- return report_error("Failed to open export conversion stylesheet");
-
- snprintf(unitstr, 3, "%d", units);
- params[pnr++] = "units";
- params[pnr++] = unitstr;
- params[pnr++] = NULL;
-
- transformed = xsltApplyStylesheet(xslt, doc, (const char **)params);
- xmlFreeDoc(doc);
-
- /* Write the transformed export to file */
- f = subsurface_fopen(filename, "w");
- if (f) {
- xsltSaveResultToFile(f, transformed, xslt);
- fclose(f);
- /* Check write errors? */
- } else {
- res = report_error("Failed to open %s for writing (%s)", filename, strerror(errno));
- }
- xsltFreeStylesheet(xslt);
- xmlFreeDoc(transformed);
-
- return res;
-}
diff --git a/subsurface-core/serial_ftdi.c b/subsurface-core/serial_ftdi.c
deleted file mode 100644
index ff1335171..000000000
--- a/subsurface-core/serial_ftdi.c
+++ /dev/null
@@ -1,665 +0,0 @@
-/*
- * libdivecomputer
- *
- * Copyright (C) 2008 Jef Driesen
- * Copyright (C) 2014 Venkatesh Shukla
- * Copyright (C) 2015 Anton Lundin
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
-
-#include <stdlib.h> // malloc, free
-#include <string.h> // strerror
-#include <errno.h> // errno
-#include <sys/time.h> // gettimeofday
-#include <time.h> // nanosleep
-#include <stdio.h>
-
-#include <libusb.h>
-#include <ftdi.h>
-
-#ifndef __ANDROID__
-#define INFO(context, fmt, ...) fprintf(stderr, "INFO: " fmt "\n", ##__VA_ARGS__)
-#define ERROR(context, fmt, ...) fprintf(stderr, "ERROR: " fmt "\n", ##__VA_ARGS__)
-#else
-#include <android/log.h>
-#define INFO(context, fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, __FILE__, "INFO: " fmt "\n", ##__VA_ARGS__)
-#define ERROR(context, fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, __FILE__, "ERROR: " fmt "\n", ##__VA_ARGS__)
-#endif
-//#define SYSERROR(context, errcode) ERROR(__FILE__ ":" __LINE__ ": %s", strerror(errcode))
-#define SYSERROR(context, errcode) ;
-
-#include <libdivecomputer/custom_serial.h>
-
-/* Verbatim copied libdivecomputer enums to support configure */
-typedef enum serial_parity_t {
- SERIAL_PARITY_NONE,
- SERIAL_PARITY_EVEN,
- SERIAL_PARITY_ODD
-} serial_parity_t;
-
-typedef enum serial_flowcontrol_t {
- SERIAL_FLOWCONTROL_NONE,
- SERIAL_FLOWCONTROL_HARDWARE,
- SERIAL_FLOWCONTROL_SOFTWARE
-} serial_flowcontrol_t;
-
-typedef enum serial_queue_t {
- SERIAL_QUEUE_INPUT = 0x01,
- SERIAL_QUEUE_OUTPUT = 0x02,
- SERIAL_QUEUE_BOTH = SERIAL_QUEUE_INPUT | SERIAL_QUEUE_OUTPUT
-} serial_queue_t;
-
-typedef enum serial_line_t {
- SERIAL_LINE_DCD, // Data carrier detect
- SERIAL_LINE_CTS, // Clear to send
- SERIAL_LINE_DSR, // Data set ready
- SERIAL_LINE_RNG, // Ring indicator
-} serial_line_t;
-
-#define VID 0x0403 // Vendor ID of FTDI
-
-#define MAX_BACKOFF 500 // Max milliseconds to wait before timing out.
-
-typedef struct serial_t {
- /* Library context. */
- dc_context_t *context;
- /*
- * The file descriptor corresponding to the serial port.
- * Also a libftdi_ftdi_ctx could be used?
- */
- struct ftdi_context *ftdi_ctx;
- long timeout;
- /*
- * Serial port settings are saved into this variable immediately
- * after the port is opened. These settings are restored when the
- * serial port is closed.
- * Saving this using libftdi context or libusb. Search further.
- * Custom implementation using libftdi functions could be done.
- */
-
- /* Half-duplex settings */
- int halfduplex;
- unsigned int baudrate;
- unsigned int nbits;
-} serial_t;
-
-static int serial_ftdi_get_received (serial_t *device)
-{
- if (device == NULL)
- return -1; // EINVAL (Invalid argument)
-
- // Direct access is not encouraged. But function implementation
- // is not available. The return quantity might be anything.
- // Find out further about its possible values and correct way of
- // access.
- int bytes = device->ftdi_ctx->readbuffer_remaining;
-
- return bytes;
-}
-
-static int serial_ftdi_get_transmitted (serial_t *device)
-{
- if (device == NULL)
- return -1; // EINVAL (Invalid argument)
-
- // This is not possible using libftdi. Look further into it.
- return -1;
-}
-
-static int serial_ftdi_sleep (serial_t *device, unsigned long timeout)
-{
- if (device == NULL)
- return -1;
-
- INFO (device->context, "Sleep: value=%lu", timeout);
-
- struct timespec ts;
- ts.tv_sec = (timeout / 1000);
- ts.tv_nsec = (timeout % 1000) * 1000000;
-
- while (nanosleep (&ts, &ts) != 0) {
- if (errno != EINTR ) {
- SYSERROR (device->context, errno);
- return -1;
- }
- }
-
- return 0;
-}
-
-
-// Used internally for opening ftdi devices
-static int serial_ftdi_open_device (struct ftdi_context *ftdi_ctx)
-{
- int accepted_pids[] = { 0x6001, 0x6010, 0x6011, // Suunto (Smart Interface), Heinrichs Weikamp
- 0xF460, // Oceanic
- 0xF680, // Suunto
- 0x87D0, // Cressi (Leonardo)
- };
- int num_accepted_pids = 6;
- int i, pid, ret;
- for (i = 0; i < num_accepted_pids; i++) {
- pid = accepted_pids[i];
- ret = ftdi_usb_open (ftdi_ctx, VID, pid);
- if (ret == -3) // Device not found
- continue;
- else
- return ret;
- }
- // No supported devices are attached.
- return ret;
-}
-
-//
-// Open the serial port.
-// Initialise ftdi_context and use it to open the device
-//
-//FIXME: ugly forward declaration of serial_ftdi_configure, util we support configure for real...
-static dc_status_t serial_ftdi_configure (serial_t *device, int baudrate, int databits, int parity, int stopbits, int flowcontrol);
-static dc_status_t serial_ftdi_open (serial_t **out, dc_context_t *context, const char* name)
-{
- if (out == NULL)
- return -1; // EINVAL (Invalid argument)
-
- INFO (context, "Open: name=%s", name ? name : "");
-
- // Allocate memory.
- serial_t *device = (serial_t *) malloc (sizeof (serial_t));
- if (device == NULL) {
- SYSERROR (context, errno);
- return DC_STATUS_NOMEMORY;
- }
-
- struct ftdi_context *ftdi_ctx = ftdi_new();
- if (ftdi_ctx == NULL) {
- free(device);
- SYSERROR (context, errno);
- return DC_STATUS_NOMEMORY;
- }
-
- // Library context.
- device->context = context;
-
- // Default to blocking reads.
- device->timeout = -1;
-
- // Default to full-duplex.
- device->halfduplex = 0;
- device->baudrate = 0;
- device->nbits = 0;
-
- // Initialize device ftdi context
- ftdi_init(ftdi_ctx);
-
- if (ftdi_set_interface(ftdi_ctx,INTERFACE_ANY)) {
- free(device);
- ERROR (context, "%s", ftdi_get_error_string(ftdi_ctx));
- return DC_STATUS_IO;
- }
-
- if (serial_ftdi_open_device(ftdi_ctx) < 0) {
- free(device);
- ERROR (context, "%s", ftdi_get_error_string(ftdi_ctx));
- return DC_STATUS_IO;
- }
-
- if (ftdi_usb_reset(ftdi_ctx)) {
- free(device);
- ERROR (context, "%s", ftdi_get_error_string(ftdi_ctx));
- return DC_STATUS_IO;
- }
-
- if (ftdi_usb_purge_buffers(ftdi_ctx)) {
- free(device);
- ERROR (context, "%s", ftdi_get_error_string(ftdi_ctx));
- return DC_STATUS_IO;
- }
-
- device->ftdi_ctx = ftdi_ctx;
-
- //FIXME: remove this when custom-serial have support for configure calls
- serial_ftdi_configure (device, 115200, 8, 0, 1, 0);
-
- *out = device;
-
- return DC_STATUS_SUCCESS;
-}
-
-//
-// Close the serial port.
-//
-static int serial_ftdi_close (serial_t *device)
-{
- if (device == NULL)
- return 0;
-
- // Restore the initial terminal attributes.
- // See if it is possible using libusb or libftdi
-
- int ret = ftdi_usb_close(device->ftdi_ctx);
- if (ret < 0) {
- ERROR (device->context, "Unable to close the ftdi device : %d (%s)\n",
- ret, ftdi_get_error_string(device->ftdi_ctx));
- return ret;
- }
-
- ftdi_free(device->ftdi_ctx);
-
- // Free memory.
- free (device);
-
- return 0;
-}
-
-//
-// Configure the serial port (baudrate, databits, parity, stopbits and flowcontrol).
-//
-static dc_status_t serial_ftdi_configure (serial_t *device, int baudrate, int databits, int parity, int stopbits, int flowcontrol)
-{
- if (device == NULL)
- return -1; // EINVAL (Invalid argument)
-
- INFO (device->context, "Configure: baudrate=%i, databits=%i, parity=%i, stopbits=%i, flowcontrol=%i",
- baudrate, databits, parity, stopbits, flowcontrol);
-
- enum ftdi_bits_type ft_bits;
- enum ftdi_stopbits_type ft_stopbits;
- enum ftdi_parity_type ft_parity;
-
- if (ftdi_set_baudrate(device->ftdi_ctx, baudrate) < 0) {
- ERROR (device->context, "%s", ftdi_get_error_string(device->ftdi_ctx));
- return -1;
- }
-
- // Set the character size.
- switch (databits) {
- case 7:
- ft_bits = BITS_7;
- break;
- case 8:
- ft_bits = BITS_8;
- break;
- default:
- return DC_STATUS_INVALIDARGS;
- }
-
- // Set the parity type.
- switch (parity) {
- case SERIAL_PARITY_NONE: // No parity
- ft_parity = NONE;
- break;
- case SERIAL_PARITY_EVEN: // Even parity
- ft_parity = EVEN;
- break;
- case SERIAL_PARITY_ODD: // Odd parity
- ft_parity = ODD;
- break;
- default:
- return DC_STATUS_INVALIDARGS;
- }
-
- // Set the number of stop bits.
- switch (stopbits) {
- case 1: // One stopbit
- ft_stopbits = STOP_BIT_1;
- break;
- case 2: // Two stopbits
- ft_stopbits = STOP_BIT_2;
- break;
- default:
- return DC_STATUS_INVALIDARGS;
- }
-
- // Set the attributes
- if (ftdi_set_line_property(device->ftdi_ctx, ft_bits, ft_stopbits, ft_parity)) {
- ERROR (device->context, "%s", ftdi_get_error_string(device->ftdi_ctx));
- return DC_STATUS_IO;
- }
-
- // Set the flow control.
- switch (flowcontrol) {
- case SERIAL_FLOWCONTROL_NONE: // No flow control.
- if (ftdi_setflowctrl(device->ftdi_ctx, SIO_DISABLE_FLOW_CTRL) < 0) {
- ERROR (device->context, "%s", ftdi_get_error_string(device->ftdi_ctx));
- return DC_STATUS_IO;
- }
- break;
- case SERIAL_FLOWCONTROL_HARDWARE: // Hardware (RTS/CTS) flow control.
- if (ftdi_setflowctrl(device->ftdi_ctx, SIO_RTS_CTS_HS) < 0) {
- ERROR (device->context, "%s", ftdi_get_error_string(device->ftdi_ctx));
- return DC_STATUS_IO;
- }
- break;
- case SERIAL_FLOWCONTROL_SOFTWARE: // Software (XON/XOFF) flow control.
- if (ftdi_setflowctrl(device->ftdi_ctx, SIO_XON_XOFF_HS) < 0) {
- ERROR (device->context, "%s", ftdi_get_error_string(device->ftdi_ctx));
- return DC_STATUS_IO;
- }
- break;
- default:
- return DC_STATUS_INVALIDARGS;
- }
-
- device->baudrate = baudrate;
- device->nbits = 1 + databits + stopbits + (parity ? 1 : 0);
-
- return DC_STATUS_SUCCESS;
-}
-
-//
-// Configure the serial port (timeouts).
-//
-static int serial_ftdi_set_timeout (serial_t *device, long timeout)
-{
- if (device == NULL)
- return -1; // EINVAL (Invalid argument)
-
- INFO (device->context, "Timeout: value=%li", timeout);
-
- device->timeout = timeout;
-
- return 0;
-}
-
-static int serial_ftdi_set_halfduplex (serial_t *device, int value)
-{
- if (device == NULL)
- return -1; // EINVAL (Invalid argument)
-
- // Most ftdi chips support full duplex operation. ft232rl does.
- // Crosscheck other chips.
-
- device->halfduplex = value;
-
- return 0;
-}
-
-static int serial_ftdi_read (serial_t *device, void *data, unsigned int size)
-{
- if (device == NULL)
- return -1; // EINVAL (Invalid argument)
-
- // The total timeout.
- long timeout = device->timeout;
-
- // The absolute target time.
- struct timeval tve;
-
- static int backoff = 1;
- int init = 1;
- unsigned int nbytes = 0;
- while (nbytes < size) {
- struct timeval tvt;
- if (timeout > 0) {
- struct timeval now;
- if (gettimeofday (&now, NULL) != 0) {
- SYSERROR (device->context, errno);
- return -1;
- }
-
- if (init) {
- // Calculate the initial timeout.
- tvt.tv_sec = (timeout / 1000);
- tvt.tv_usec = (timeout % 1000) * 1000;
- // Calculate the target time.
- timeradd (&now, &tvt, &tve);
- } else {
- // Calculate the remaining timeout.
- if (timercmp (&now, &tve, <))
- timersub (&tve, &now, &tvt);
- else
- timerclear (&tvt);
- }
- init = 0;
- } else if (timeout == 0) {
- timerclear (&tvt);
- }
-
- int n = ftdi_read_data (device->ftdi_ctx, (char *) data + nbytes, size - nbytes);
- if (n < 0) {
- if (n == LIBUSB_ERROR_INTERRUPTED)
- continue; //Retry.
- ERROR (device->context, "%s", ftdi_get_error_string(device->ftdi_ctx));
- return -1; //Error during read call.
- } else if (n == 0) {
- // Exponential backoff.
- if (backoff > MAX_BACKOFF) {
- ERROR(device->context, "%s", "FTDI read timed out.");
- return -1;
- }
- serial_ftdi_sleep (device, backoff);
- backoff *= 2;
- } else {
- // Reset backoff to 1 on success.
- backoff = 1;
- }
-
- nbytes += n;
- }
-
- INFO (device->context, "Read %d bytes", nbytes);
-
- return nbytes;
-}
-
-static int serial_ftdi_write (serial_t *device, const void *data, unsigned int size)
-{
- if (device == NULL)
- return -1; // EINVAL (Invalid argument)
-
- struct timeval tve, tvb;
- if (device->halfduplex) {
- // Get the current time.
- if (gettimeofday (&tvb, NULL) != 0) {
- SYSERROR (device->context, errno);
- return -1;
- }
- }
-
- unsigned int nbytes = 0;
- while (nbytes < size) {
-
- int n = ftdi_write_data (device->ftdi_ctx, (char *) data + nbytes, size - nbytes);
- if (n < 0) {
- if (n == LIBUSB_ERROR_INTERRUPTED)
- continue; // Retry.
- ERROR (device->context, "%s", ftdi_get_error_string(device->ftdi_ctx));
- return -1; // Error during write call.
- } else if (n == 0) {
- break; // EOF.
- }
-
- nbytes += n;
- }
-
- if (device->halfduplex) {
- // Get the current time.
- if (gettimeofday (&tve, NULL) != 0) {
- SYSERROR (device->context, errno);
- return -1;
- }
-
- // Calculate the elapsed time (microseconds).
- struct timeval tvt;
- timersub (&tve, &tvb, &tvt);
- unsigned long elapsed = tvt.tv_sec * 1000000 + tvt.tv_usec;
-
- // Calculate the expected duration (microseconds). A 2 millisecond fudge
- // factor is added because it improves the success rate significantly.
- unsigned long expected = 1000000.0 * device->nbits / device->baudrate * size + 0.5 + 2000;
-
- // Wait for the remaining time.
- if (elapsed < expected) {
- unsigned long remaining = expected - elapsed;
-
- // The remaining time is rounded up to the nearest millisecond to
- // match the Windows implementation. The higher resolution is
- // pointless anyway, since we already added a fudge factor above.
- serial_ftdi_sleep (device, (remaining + 999) / 1000);
- }
- }
-
- INFO (device->context, "Wrote %d bytes", nbytes);
-
- return nbytes;
-}
-
-static int serial_ftdi_flush (serial_t *device, int queue)
-{
- if (device == NULL)
- return -1; // EINVAL (Invalid argument)
-
- INFO (device->context, "Flush: queue=%u, input=%i, output=%i", queue,
- serial_ftdi_get_received (device),
- serial_ftdi_get_transmitted (device));
-
- switch (queue) {
- case SERIAL_QUEUE_INPUT:
- if (ftdi_usb_purge_tx_buffer(device->ftdi_ctx)) {
- ERROR (device->context, "%s", ftdi_get_error_string(device->ftdi_ctx));
- return -1;
- }
- break;
- case SERIAL_QUEUE_OUTPUT:
- if (ftdi_usb_purge_rx_buffer(device->ftdi_ctx)) {
- ERROR (device->context, "%s", ftdi_get_error_string(device->ftdi_ctx));
- return -1;
- }
- break;
- default:
- if (ftdi_usb_purge_buffers(device->ftdi_ctx)) {
- ERROR (device->context, "%s", ftdi_get_error_string(device->ftdi_ctx));
- return -1;
- }
- break;
- }
-
- return 0;
-}
-
-static int serial_ftdi_send_break (serial_t *device)
-{
- if (device == NULL)
- return -1; // EINVAL (Invalid argument)a
-
- INFO (device->context, "Break : One time period.");
-
- // no direct functions for sending break signals in libftdi.
- // there is a suggestion to lower the baudrate and sending NUL
- // and resetting the baudrate up again. But it has flaws.
- // Not implementing it before researching more.
-
- return -1;
-}
-
-static int serial_ftdi_set_break (serial_t *device, int level)
-{
- if (device == NULL)
- return -1; // EINVAL (Invalid argument)
-
- INFO (device->context, "Break: value=%i", level);
-
- // Not implemented in libftdi yet. Research it further.
-
- return -1;
-}
-
-static int serial_ftdi_set_dtr (serial_t *device, int level)
-{
- if (device == NULL)
- return -1; // EINVAL (Invalid argument)
-
- INFO (device->context, "DTR: value=%i", level);
-
- if (ftdi_setdtr(device->ftdi_ctx, level)) {
- ERROR (device->context, "%s", ftdi_get_error_string(device->ftdi_ctx));
- return -1;
- }
-
- return 0;
-}
-
-static int serial_ftdi_set_rts (serial_t *device, int level)
-{
- if (device == NULL)
- return -1; // EINVAL (Invalid argument)
-
- INFO (device->context, "RTS: value=%i", level);
-
- if (ftdi_setrts(device->ftdi_ctx, level)) {
- ERROR (device->context, "%s", ftdi_get_error_string(device->ftdi_ctx));
- return -1;
- }
-
- return 0;
-}
-
-const dc_serial_operations_t serial_ftdi_ops = {
- .open = serial_ftdi_open,
- .close = serial_ftdi_close,
- .read = serial_ftdi_read,
- .write = serial_ftdi_write,
- .flush = serial_ftdi_flush,
- .get_received = serial_ftdi_get_received,
- .get_transmitted = NULL, /*NOT USED ANYWHERE! serial_ftdi_get_transmitted */
- .set_timeout = serial_ftdi_set_timeout
-#ifdef FIXED_SSRF_CUSTOM_SERIAL
- ,
- .configure = serial_ftdi_configure,
-//static int serial_ftdi_configure (serial_t *device, int baudrate, int databits, int parity, int stopbits, int flowcontrol)
- .set_halfduplex = serial_ftdi_set_halfduplex,
-//static int serial_ftdi_set_halfduplex (serial_t *device, int value)
- .send_break = serial_ftdi_send_break,
-//static int serial_ftdi_send_break (serial_t *device)
- .set_break = serial_ftdi_set_break,
-//static int serial_ftdi_set_break (serial_t *device, int level)
- .set_dtr = serial_ftdi_set_dtr,
-//static int serial_ftdi_set_dtr (serial_t *device, int level)
- .set_rts = serial_ftdi_set_rts
-//static int serial_ftdi_set_rts (serial_t *device, int level)
-#endif
-};
-
-dc_status_t dc_serial_ftdi_open(dc_serial_t **out, dc_context_t *context)
-{
- if (out == NULL)
- return DC_STATUS_INVALIDARGS;
-
- // Allocate memory.
- dc_serial_t *serial_device = (dc_serial_t *) malloc (sizeof (dc_serial_t));
-
- if (serial_device == NULL) {
- return DC_STATUS_NOMEMORY;
- }
-
- // Initialize data and function pointers
- dc_serial_init(serial_device, NULL, &serial_ftdi_ops);
-
- // Open the serial device.
- dc_status_t rc = (dc_status_t) serial_ftdi_open (&serial_device->port, context, NULL);
- if (rc != DC_STATUS_SUCCESS) {
- free (serial_device);
- return rc;
- }
-
- // Set the type of the device
- serial_device->type = DC_TRANSPORT_USB;;
-
- *out = serial_device;
-
- return DC_STATUS_SUCCESS;
-}
diff --git a/subsurface-core/sha1.c b/subsurface-core/sha1.c
deleted file mode 100644
index acf8c5d9f..000000000
--- a/subsurface-core/sha1.c
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * SHA1 routine optimized to do word accesses rather than byte accesses,
- * and to avoid unnecessary copies into the context array.
- *
- * This was initially based on the Mozilla SHA1 implementation, although
- * none of the original Mozilla code remains.
- */
-
-/* this is only to get definitions for memcpy(), ntohl() and htonl() */
-#include <string.h>
-#include <stdint.h>
-#ifdef WIN32
-#include <winsock2.h>
-#else
-#include <arpa/inet.h>
-#endif
-#include "sha1.h"
-
-#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
-
-/*
- * Force usage of rol or ror by selecting the one with the smaller constant.
- * It _can_ generate slightly smaller code (a constant of 1 is special), but
- * perhaps more importantly it's possibly faster on any uarch that does a
- * rotate with a loop.
- */
-
-#define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; })
-#define SHA_ROL(x, n) SHA_ASM("rol", x, n)
-#define SHA_ROR(x, n) SHA_ASM("ror", x, n)
-
-#else
-
-#define SHA_ROT(X, l, r) (((X) << (l)) | ((X) >> (r)))
-#define SHA_ROL(X, n) SHA_ROT(X, n, 32 - (n))
-#define SHA_ROR(X, n) SHA_ROT(X, 32 - (n), n)
-
-#endif
-
-/*
- * If you have 32 registers or more, the compiler can (and should)
- * try to change the array[] accesses into registers. However, on
- * machines with less than ~25 registers, that won't really work,
- * and at least gcc will make an unholy mess of it.
- *
- * So to avoid that mess which just slows things down, we force
- * the stores to memory to actually happen (we might be better off
- * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as
- * suggested by Artur Skawina - that will also make gcc unable to
- * try to do the silly "optimize away loads" part because it won't
- * see what the value will be).
- *
- * Ben Herrenschmidt reports that on PPC, the C version comes close
- * to the optimized asm with this (ie on PPC you don't want that
- * 'volatile', since there are lots of registers).
- *
- * On ARM we get the best code generation by forcing a full memory barrier
- * between each SHA_ROUND, otherwise gcc happily get wild with spilling and
- * the stack frame size simply explode and performance goes down the drain.
- */
-
-#if defined(__i386__) || defined(__x86_64__)
-#define setW(x, val) (*(volatile unsigned int *)&W(x) = (val))
-#elif defined(__GNUC__) && defined(__arm__)
-#define setW(x, val) \
- do { \
- W(x) = (val); \
- __asm__("" :: : "memory"); \
- } while (0)
-#else
-#define setW(x, val) (W(x) = (val))
-#endif
-
-/*
- * Performance might be improved if the CPU architecture is OK with
- * unaligned 32-bit loads and a fast ntohl() is available.
- * Otherwise fall back to byte loads and shifts which is portable,
- * and is faster on architectures with memory alignment issues.
- */
-
-#if defined(__i386__) || defined(__x86_64__) || \
- defined(_M_IX86) || defined(_M_X64) || \
- defined(__ppc__) || defined(__ppc64__) || \
- defined(__powerpc__) || defined(__powerpc64__) || \
- defined(__s390__) || defined(__s390x__)
-
-#define get_be32(p) ntohl(*(unsigned int *)(p))
-#define put_be32(p, v) \
- do { \
- *(unsigned int *)(p) = htonl(v); \
- } while (0)
-
-#else
-
-#define get_be32(p) ( \
- (*((unsigned char *)(p) + 0) << 24) | \
- (*((unsigned char *)(p) + 1) << 16) | \
- (*((unsigned char *)(p) + 2) << 8) | \
- (*((unsigned char *)(p) + 3) << 0))
-#define put_be32(p, v) \
- do { \
- unsigned int __v = (v); \
- *((unsigned char *)(p) + 0) = __v >> 24; \
- *((unsigned char *)(p) + 1) = __v >> 16; \
- *((unsigned char *)(p) + 2) = __v >> 8; \
- *((unsigned char *)(p) + 3) = __v >> 0; \
- } while (0)
-
-#endif
-
-/* This "rolls" over the 512-bit array */
-#define W(x) (array[(x) & 15])
-
-/*
- * Where do we get the source from? The first 16 iterations get it from
- * the input data, the next mix it from the 512-bit array.
- */
-#define SHA_SRC(t) get_be32((unsigned char *)block + (t) * 4)
-#define SHA_MIX(t) SHA_ROL(W((t) + 13) ^ W((t) + 8) ^ W((t) + 2) ^ W(t), 1);
-
-#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) \
- do { \
- unsigned int TEMP = input(t); \
- setW(t, TEMP); \
- E += TEMP + SHA_ROL(A, 5) + (fn) + (constant); \
- B = SHA_ROR(B, 2); \
- } while (0)
-
-#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C ^ D) & B) ^ D), 0x5a827999, A, B, C, D, E)
-#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C ^ D) & B) ^ D), 0x5a827999, A, B, C, D, E)
-#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B ^ C ^ D), 0x6ed9eba1, A, B, C, D, E)
-#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B &C) + (D &(B ^ C))), 0x8f1bbcdc, A, B, C, D, E)
-#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B ^ C ^ D), 0xca62c1d6, A, B, C, D, E)
-
-static void blk_SHA1_Block(blk_SHA_CTX *ctx, const void *block)
-{
- unsigned int A, B, C, D, E;
- unsigned int array[16];
-
- A = ctx->H[0];
- B = ctx->H[1];
- C = ctx->H[2];
- D = ctx->H[3];
- E = ctx->H[4];
-
- /* Round 1 - iterations 0-16 take their input from 'block' */
- T_0_15(0, A, B, C, D, E);
- T_0_15(1, E, A, B, C, D);
- T_0_15(2, D, E, A, B, C);
- T_0_15(3, C, D, E, A, B);
- T_0_15(4, B, C, D, E, A);
- T_0_15(5, A, B, C, D, E);
- T_0_15(6, E, A, B, C, D);
- T_0_15(7, D, E, A, B, C);
- T_0_15(8, C, D, E, A, B);
- T_0_15(9, B, C, D, E, A);
- T_0_15(10, A, B, C, D, E);
- T_0_15(11, E, A, B, C, D);
- T_0_15(12, D, E, A, B, C);
- T_0_15(13, C, D, E, A, B);
- T_0_15(14, B, C, D, E, A);
- T_0_15(15, A, B, C, D, E);
-
- /* Round 1 - tail. Input from 512-bit mixing array */
- T_16_19(16, E, A, B, C, D);
- T_16_19(17, D, E, A, B, C);
- T_16_19(18, C, D, E, A, B);
- T_16_19(19, B, C, D, E, A);
-
- /* Round 2 */
- T_20_39(20, A, B, C, D, E);
- T_20_39(21, E, A, B, C, D);
- T_20_39(22, D, E, A, B, C);
- T_20_39(23, C, D, E, A, B);
- T_20_39(24, B, C, D, E, A);
- T_20_39(25, A, B, C, D, E);
- T_20_39(26, E, A, B, C, D);
- T_20_39(27, D, E, A, B, C);
- T_20_39(28, C, D, E, A, B);
- T_20_39(29, B, C, D, E, A);
- T_20_39(30, A, B, C, D, E);
- T_20_39(31, E, A, B, C, D);
- T_20_39(32, D, E, A, B, C);
- T_20_39(33, C, D, E, A, B);
- T_20_39(34, B, C, D, E, A);
- T_20_39(35, A, B, C, D, E);
- T_20_39(36, E, A, B, C, D);
- T_20_39(37, D, E, A, B, C);
- T_20_39(38, C, D, E, A, B);
- T_20_39(39, B, C, D, E, A);
-
- /* Round 3 */
- T_40_59(40, A, B, C, D, E);
- T_40_59(41, E, A, B, C, D);
- T_40_59(42, D, E, A, B, C);
- T_40_59(43, C, D, E, A, B);
- T_40_59(44, B, C, D, E, A);
- T_40_59(45, A, B, C, D, E);
- T_40_59(46, E, A, B, C, D);
- T_40_59(47, D, E, A, B, C);
- T_40_59(48, C, D, E, A, B);
- T_40_59(49, B, C, D, E, A);
- T_40_59(50, A, B, C, D, E);
- T_40_59(51, E, A, B, C, D);
- T_40_59(52, D, E, A, B, C);
- T_40_59(53, C, D, E, A, B);
- T_40_59(54, B, C, D, E, A);
- T_40_59(55, A, B, C, D, E);
- T_40_59(56, E, A, B, C, D);
- T_40_59(57, D, E, A, B, C);
- T_40_59(58, C, D, E, A, B);
- T_40_59(59, B, C, D, E, A);
-
- /* Round 4 */
- T_60_79(60, A, B, C, D, E);
- T_60_79(61, E, A, B, C, D);
- T_60_79(62, D, E, A, B, C);
- T_60_79(63, C, D, E, A, B);
- T_60_79(64, B, C, D, E, A);
- T_60_79(65, A, B, C, D, E);
- T_60_79(66, E, A, B, C, D);
- T_60_79(67, D, E, A, B, C);
- T_60_79(68, C, D, E, A, B);
- T_60_79(69, B, C, D, E, A);
- T_60_79(70, A, B, C, D, E);
- T_60_79(71, E, A, B, C, D);
- T_60_79(72, D, E, A, B, C);
- T_60_79(73, C, D, E, A, B);
- T_60_79(74, B, C, D, E, A);
- T_60_79(75, A, B, C, D, E);
- T_60_79(76, E, A, B, C, D);
- T_60_79(77, D, E, A, B, C);
- T_60_79(78, C, D, E, A, B);
- T_60_79(79, B, C, D, E, A);
-
- ctx->H[0] += A;
- ctx->H[1] += B;
- ctx->H[2] += C;
- ctx->H[3] += D;
- ctx->H[4] += E;
-}
-
-void blk_SHA1_Init(blk_SHA_CTX *ctx)
-{
- ctx->size = 0;
-
- /* Initialize H with the magic constants (see FIPS180 for constants) */
- ctx->H[0] = 0x67452301;
- ctx->H[1] = 0xefcdab89;
- ctx->H[2] = 0x98badcfe;
- ctx->H[3] = 0x10325476;
- ctx->H[4] = 0xc3d2e1f0;
-}
-
-void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len)
-{
- unsigned int lenW = ctx->size & 63;
-
- ctx->size += len;
-
- /* Read the data into W and process blocks as they get full */
- if (lenW) {
- unsigned int left = 64 - lenW;
- if (len < left)
- left = len;
- memcpy(lenW + (char *)ctx->W, data, left);
- lenW = (lenW + left) & 63;
- len -= left;
- data = ((const char *)data + left);
- if (lenW)
- return;
- blk_SHA1_Block(ctx, ctx->W);
- }
- while (len >= 64) {
- blk_SHA1_Block(ctx, data);
- data = ((const char *)data + 64);
- len -= 64;
- }
- if (len)
- memcpy(ctx->W, data, len);
-}
-
-void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx)
-{
- static const unsigned char pad[64] = { 0x80 };
- unsigned int padlen[2];
- int i;
-
- /* Pad with a binary 1 (ie 0x80), then zeroes, then length */
- padlen[0] = htonl((uint32_t)(ctx->size >> 29));
- padlen[1] = htonl((uint32_t)(ctx->size << 3));
-
- i = ctx->size & 63;
- blk_SHA1_Update(ctx, pad, 1 + (63 & (55 - i)));
- blk_SHA1_Update(ctx, padlen, 8);
-
- /* Output hash */
- for (i = 0; i < 5; i++)
- put_be32(hashout + i * 4, ctx->H[i]);
-}
diff --git a/subsurface-core/sha1.h b/subsurface-core/sha1.h
deleted file mode 100644
index cab6ff77d..000000000
--- a/subsurface-core/sha1.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * SHA1 routine optimized to do word accesses rather than byte accesses,
- * and to avoid unnecessary copies into the context array.
- *
- * This was initially based on the Mozilla SHA1 implementation, although
- * none of the original Mozilla code remains.
- */
-#ifndef SHA1_H
-#define SHA1_H
-
-typedef struct
-{
- unsigned long long size;
- unsigned int H[5];
- unsigned int W[16];
-} blk_SHA_CTX;
-
-void blk_SHA1_Init(blk_SHA_CTX *ctx);
-void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, unsigned long len);
-void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx);
-
-/* Make us use the standard names */
-#define SHA_CTX blk_SHA_CTX
-#define SHA1_Init blk_SHA1_Init
-#define SHA1_Update blk_SHA1_Update
-#define SHA1_Final blk_SHA1_Final
-
-/* Trivial helper function */
-static inline void SHA1(const void *dataIn, unsigned long len, unsigned char hashout[20])
-{
- SHA_CTX ctx;
-
- SHA1_Init(&ctx);
- SHA1_Update(&ctx, dataIn, len);
- SHA1_Final(hashout, &ctx);
-}
-
-#endif // SHA1_H
diff --git a/subsurface-core/statistics.c b/subsurface-core/statistics.c
deleted file mode 100644
index 6a05cffc1..000000000
--- a/subsurface-core/statistics.c
+++ /dev/null
@@ -1,404 +0,0 @@
-/* statistics.c
- *
- * core logic for the Info & Stats page -
- * char *get_time_string(int seconds, int maxdays);
- * char *get_minutes(int seconds);
- * void process_all_dives(struct dive *dive, struct dive **prev_dive);
- * void get_selected_dives_text(char *buffer, int size);
- */
-#include "gettext.h"
-#include <string.h>
-#include <ctype.h>
-
-#include "dive.h"
-#include "display.h"
-#include "divelist.h"
-#include "statistics.h"
-
-static stats_t stats;
-stats_t stats_selection;
-stats_t *stats_monthly = NULL;
-stats_t *stats_yearly = NULL;
-stats_t *stats_by_trip = NULL;
-stats_t *stats_by_type = NULL;
-
-static void process_temperatures(struct dive *dp, stats_t *stats)
-{
- int min_temp, mean_temp, max_temp = 0;
-
- max_temp = dp->maxtemp.mkelvin;
- if (max_temp && (!stats->max_temp || max_temp > stats->max_temp))
- stats->max_temp = max_temp;
-
- min_temp = dp->mintemp.mkelvin;
- if (min_temp && (!stats->min_temp || min_temp < stats->min_temp))
- stats->min_temp = min_temp;
-
- if (min_temp || max_temp) {
- mean_temp = min_temp;
- if (mean_temp)
- mean_temp = (mean_temp + max_temp) / 2;
- else
- mean_temp = max_temp;
- stats->combined_temp += get_temp_units(mean_temp, NULL);
- stats->combined_count++;
- }
-}
-
-static void process_dive(struct dive *dp, stats_t *stats)
-{
- int old_tt, sac_time = 0;
- uint32_t duration = dp->duration.seconds;
-
- old_tt = stats->total_time.seconds;
- stats->total_time.seconds += duration;
- if (duration > stats->longest_time.seconds)
- stats->longest_time.seconds = duration;
- if (stats->shortest_time.seconds == 0 || duration < stats->shortest_time.seconds)
- stats->shortest_time.seconds = duration;
- if (dp->maxdepth.mm > stats->max_depth.mm)
- stats->max_depth.mm = dp->maxdepth.mm;
- if (stats->min_depth.mm == 0 || dp->maxdepth.mm < stats->min_depth.mm)
- stats->min_depth.mm = dp->maxdepth.mm;
-
- process_temperatures(dp, stats);
-
- /* Maybe we should drop zero-duration dives */
- if (!duration)
- return;
- stats->avg_depth.mm = (1.0 * old_tt * stats->avg_depth.mm +
- duration * dp->meandepth.mm) /
- stats->total_time.seconds;
- if (dp->sac > 100) { /* less than .1 l/min is bogus, even with a pSCR */
- sac_time = stats->total_sac_time + duration;
- stats->avg_sac.mliter = (1.0 * stats->total_sac_time * stats->avg_sac.mliter +
- duration * dp->sac) /
- sac_time;
- if (dp->sac > stats->max_sac.mliter)
- stats->max_sac.mliter = dp->sac;
- if (stats->min_sac.mliter == 0 || dp->sac < stats->min_sac.mliter)
- stats->min_sac.mliter = dp->sac;
- stats->total_sac_time = sac_time;
- }
-}
-
-char *get_minutes(int seconds)
-{
- static char buf[80];
- snprintf(buf, sizeof(buf), "%d:%.2d", FRACTION(seconds, 60));
- return buf;
-}
-
-void process_all_dives(struct dive *dive, struct dive **prev_dive)
-{
- int idx;
- struct dive *dp;
- struct tm tm;
- int current_year = 0;
- int current_month = 0;
- int year_iter = 0;
- int month_iter = 0;
- int prev_month = 0, prev_year = 0;
- int trip_iter = 0;
- dive_trip_t *trip_ptr = 0;
- unsigned int size, tsize;
-
- *prev_dive = NULL;
- memset(&stats, 0, sizeof(stats));
- if (dive_table.nr > 0) {
- stats.shortest_time.seconds = dive_table.dives[0]->duration.seconds;
- stats.min_depth.mm = dive_table.dives[0]->maxdepth.mm;
- stats.selection_size = dive_table.nr;
- }
-
- /* allocate sufficient space to hold the worst
- * case (one dive per year or all dives during
- * one month) for yearly and monthly statistics*/
-
- free(stats_yearly);
- free(stats_monthly);
- free(stats_by_trip);
- free(stats_by_type);
-
- size = sizeof(stats_t) * (dive_table.nr + 1);
- tsize = sizeof(stats_t) * (NUM_DC_TYPE + 1);
- stats_yearly = malloc(size);
- stats_monthly = malloc(size);
- stats_by_trip = malloc(size);
- stats_by_type = malloc(tsize);
- if (!stats_yearly || !stats_monthly || !stats_by_trip || !stats_by_type)
- return;
- memset(stats_yearly, 0, size);
- memset(stats_monthly, 0, size);
- memset(stats_by_trip, 0, size);
- memset(stats_by_type, 0, tsize);
- stats_yearly[0].is_year = true;
-
- /* Setting the is_trip to true to show the location as first
- * field in the statistics window */
- stats_by_type[0].location = strdup("All (by type stats)");
- stats_by_type[0].is_trip = true;
- stats_by_type[1].location = strdup("OC");
- stats_by_type[1].is_trip = true;
- stats_by_type[2].location = strdup("CCR");
- stats_by_type[2].is_trip = true;
- stats_by_type[3].location = strdup("pSCR");
- stats_by_type[3].is_trip = true;
- stats_by_type[4].location = strdup("Freedive");
- stats_by_type[4].is_trip = true;
-
- /* this relies on the fact that the dives in the dive_table
- * are in chronological order */
- for_each_dive (idx, dp) {
- if (dive && dp->when == dive->when) {
- /* that's the one we are showing */
- if (idx > 0)
- *prev_dive = dive_table.dives[idx - 1];
- }
- process_dive(dp, &stats);
-
- /* yearly statistics */
- utc_mkdate(dp->when, &tm);
- if (current_year == 0)
- current_year = tm.tm_year + 1900;
-
- if (current_year != tm.tm_year + 1900) {
- current_year = tm.tm_year + 1900;
- process_dive(dp, &(stats_yearly[++year_iter]));
- stats_yearly[year_iter].is_year = true;
- } else {
- process_dive(dp, &(stats_yearly[year_iter]));
- }
- stats_yearly[year_iter].selection_size++;
- stats_yearly[year_iter].period = current_year;
-
- /* stats_by_type[0] is all the dives combined */
- stats_by_type[0].selection_size++;
- process_dive(dp, &(stats_by_type[0]));
-
- process_dive(dp, &(stats_by_type[dp->dc.divemode + 1]));
- stats_by_type[dp->dc.divemode + 1].selection_size++;
-
- if (dp->divetrip != NULL) {
- if (trip_ptr != dp->divetrip) {
- trip_ptr = dp->divetrip;
- trip_iter++;
- }
-
- /* stats_by_trip[0] is all the dives combined */
- stats_by_trip[0].selection_size++;
- process_dive(dp, &(stats_by_trip[0]));
- stats_by_trip[0].is_trip = true;
- stats_by_trip[0].location = strdup("All (by trip stats)");
-
- process_dive(dp, &(stats_by_trip[trip_iter]));
- stats_by_trip[trip_iter].selection_size++;
- stats_by_trip[trip_iter].is_trip = true;
- stats_by_trip[trip_iter].location = dp->divetrip->location;
- }
-
- /* monthly statistics */
- if (current_month == 0) {
- current_month = tm.tm_mon + 1;
- } else {
- if (current_month != tm.tm_mon + 1)
- current_month = tm.tm_mon + 1;
- if (prev_month != current_month || prev_year != current_year)
- month_iter++;
- }
- process_dive(dp, &(stats_monthly[month_iter]));
- stats_monthly[month_iter].selection_size++;
- stats_monthly[month_iter].period = current_month;
- prev_month = current_month;
- prev_year = current_year;
- }
-}
-
-/* make sure we skip the selected summary entries */
-void process_selected_dives(void)
-{
- struct dive *dive;
- unsigned int i, nr;
-
- memset(&stats_selection, 0, sizeof(stats_selection));
-
- nr = 0;
- for_each_dive(i, dive) {
- if (dive->selected) {
- process_dive(dive, &stats_selection);
- nr++;
- }
- }
- stats_selection.selection_size = nr;
-}
-
-char *get_time_string_s(int seconds, int maxdays, bool freediving)
-{
- static char buf[80];
- if (maxdays && seconds > 3600 * 24 * maxdays) {
- snprintf(buf, sizeof(buf), translate("gettextFromC", "more than %d days"), maxdays);
- } else {
- int days = seconds / 3600 / 24;
- int hours = (seconds - days * 3600 * 24) / 3600;
- int minutes = (seconds - days * 3600 * 24 - hours * 3600) / 60;
- int secs = (seconds - days * 3600 * 24 - hours * 3600 - minutes*60);
- if (days > 0)
- snprintf(buf, sizeof(buf), translate("gettextFromC", "%dd %dh %dmin"), days, hours, minutes);
- else
- if (freediving && seconds < 3600)
- snprintf(buf, sizeof(buf), translate("gettextFromC", "%dmin %dsecs"), minutes, secs);
- else
- snprintf(buf, sizeof(buf), translate("gettextFromC", "%dh %dmin"), hours, minutes);
- }
- return buf;
-}
-
-/* this gets called when at least two but not all dives are selected */
-static void get_ranges(char *buffer, int size)
-{
- int i, len;
- int first = -1, last = -1;
- struct dive *dive;
-
- snprintf(buffer, size, "%s", translate("gettextFromC", "for dives #"));
- for_each_dive (i, dive) {
- if (!dive->selected)
- continue;
- if (dive->number < 1) {
- /* uhh - weird numbers - bail */
- snprintf(buffer, size, "%s", translate("gettextFromC", "for selected dives"));
- return;
- }
- len = strlen(buffer);
- if (last == -1) {
- snprintf(buffer + len, size - len, "%d", dive->number);
- first = last = dive->number;
- } else {
- if (dive->number == last + 1) {
- last++;
- continue;
- } else {
- if (first == last)
- snprintf(buffer + len, size - len, ", %d", dive->number);
- else if (first + 1 == last)
- snprintf(buffer + len, size - len, ", %d, %d", last, dive->number);
- else
- snprintf(buffer + len, size - len, "-%d, %d", last, dive->number);
- first = last = dive->number;
- }
- }
- }
- len = strlen(buffer);
- if (first != last) {
- if (first + 1 == last)
- snprintf(buffer + len, size - len, ", %d", last);
- else
- snprintf(buffer + len, size - len, "-%d", last);
- }
-}
-
-void get_selected_dives_text(char *buffer, size_t size)
-{
- if (amount_selected == 1) {
- if (current_dive)
- snprintf(buffer, size, translate("gettextFromC", "for dive #%d"), current_dive->number);
- else
- snprintf(buffer, size, "%s", translate("gettextFromC", "for selected dive"));
- } else if (amount_selected == (unsigned int)dive_table.nr) {
- snprintf(buffer, size, "%s", translate("gettextFromC", "for all dives"));
- } else if (amount_selected == 0) {
- snprintf(buffer, size, "%s", translate("gettextFromC", "(no dives)"));
- } else {
- get_ranges(buffer, size);
- if (strlen(buffer) == size - 1) {
- /* add our own ellipse... the way Pango does this is ugly
- * as it will leave partial numbers there which I don't like */
- size_t offset = 4;
- while (offset < size && isdigit(buffer[size - offset]))
- offset++;
- strcpy(buffer + size - offset, "...");
- }
- }
-}
-
-#define SOME_GAS 5000 // 5bar drop in cylinder pressure makes cylinder used
-
-bool is_cylinder_used(struct dive *dive, int idx)
-{
- struct divecomputer *dc;
- bool firstGasExplicit = false;
- if (cylinder_none(&dive->cylinder[idx]))
- return false;
-
- if ((dive->cylinder[idx].start.mbar - dive->cylinder[idx].end.mbar) > SOME_GAS)
- return true;
- for_each_dc(dive, dc) {
- struct event *event = get_next_event(dc->events, "gaschange");
- while (event) {
- if (dc->sample && (event->time.seconds == 0 ||
- (dc->samples && dc->sample[0].time.seconds == event->time.seconds)))
- firstGasExplicit = true;
- if (get_cylinder_index(dive, event) == idx)
- return true;
- event = get_next_event(event->next, "gaschange");
- }
- if (dc->divemode == CCR && (idx == dive->diluent_cylinder_index || idx == dive->oxygen_cylinder_index))
- return true;
- }
- if (idx == 0 && !firstGasExplicit)
- return true;
- return false;
-}
-
-void get_gas_used(struct dive *dive, volume_t gases[MAX_CYLINDERS])
-{
- int idx;
- for (idx = 0; idx < MAX_CYLINDERS; idx++) {
- cylinder_t *cyl = &dive->cylinder[idx];
- pressure_t start, end;
-
- if (!is_cylinder_used(dive, idx))
- continue;
-
- start = cyl->start.mbar ? cyl->start : cyl->sample_start;
- end = cyl->end.mbar ? cyl->end : cyl->sample_end;
- if (end.mbar && start.mbar > end.mbar)
- gases[idx].mliter = gas_volume(cyl, start) - gas_volume(cyl, end);
- }
-}
-
-/* Quite crude reverse-blender-function, but it produces a approx result */
-static void get_gas_parts(struct gasmix mix, volume_t vol, int o2_in_topup, volume_t *o2, volume_t *he)
-{
- volume_t air = {};
-
- if (gasmix_is_air(&mix)) {
- o2->mliter = 0;
- he->mliter = 0;
- return;
- }
-
- air.mliter = rint(((double)vol.mliter * (1000 - get_he(&mix) - get_o2(&mix))) / (1000 - o2_in_topup));
- he->mliter = rint(((double)vol.mliter * get_he(&mix)) / 1000.0);
- o2->mliter += vol.mliter - he->mliter - air.mliter;
-}
-
-void selected_dives_gas_parts(volume_t *o2_tot, volume_t *he_tot)
-{
- int i, j;
- struct dive *d;
- for_each_dive (i, d) {
- if (!d->selected)
- continue;
- volume_t diveGases[MAX_CYLINDERS] = {};
- get_gas_used(d, diveGases);
- for (j = 0; j < MAX_CYLINDERS; j++) {
- if (diveGases[j].mliter) {
- volume_t o2 = {}, he = {};
- get_gas_parts(d->cylinder[j].gasmix, diveGases[j], O2_IN_AIR, &o2, &he);
- o2_tot->mliter += o2.mliter;
- he_tot->mliter += he.mliter;
- }
- }
- }
-}
diff --git a/subsurface-core/statistics.h b/subsurface-core/statistics.h
deleted file mode 100644
index 015c3481e..000000000
--- a/subsurface-core/statistics.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * statistics.h
- *
- * core logic functions called from statistics UI
- * common types and variables
- */
-
-#ifndef STATISTICS_H
-#define STATISTICS_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct
-{
- int period;
- duration_t total_time;
- /* avg_time is simply total_time / nr -- let's not keep this */
- duration_t shortest_time;
- duration_t longest_time;
- depth_t max_depth;
- depth_t min_depth;
- depth_t avg_depth;
- volume_t max_sac;
- volume_t min_sac;
- volume_t avg_sac;
- int max_temp;
- int min_temp;
- double combined_temp;
- unsigned int combined_count;
- unsigned int selection_size;
- unsigned int total_sac_time;
- bool is_year;
- bool is_trip;
- char *location;
-} stats_t;
-extern stats_t stats_selection;
-extern stats_t *stats_yearly;
-extern stats_t *stats_monthly;
-extern stats_t *stats_by_trip;
-extern stats_t *stats_by_type;
-
-extern char *get_time_string_s(int seconds, int maxdays, bool freediving);
-extern char *get_minutes(int seconds);
-extern void process_all_dives(struct dive *dive, struct dive **prev_dive);
-extern void get_selected_dives_text(char *buffer, size_t size);
-extern void get_gas_used(struct dive *dive, volume_t gases[MAX_CYLINDERS]);
-extern void process_selected_dives(void);
-void selected_dives_gas_parts(volume_t *o2_tot, volume_t *he_tot);
-
-inline char *get_time_string(int seconds, int maxdays) {
- return get_time_string_s( seconds, maxdays, false);
-}
-#ifdef __cplusplus
-}
-#endif
-
-#endif // STATISTICS_H
diff --git a/subsurface-core/strndup.h b/subsurface-core/strndup.h
deleted file mode 100644
index 84e18b60f..000000000
--- a/subsurface-core/strndup.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef STRNDUP_H
-#define STRNDUP_H
-#if __WIN32__
-static char *strndup (const char *s, size_t n)
-{
- char *cpy;
- size_t len = strlen(s);
- if (n < len)
- len = n;
- if ((cpy = malloc(len + 1)) !=
- NULL) {
- cpy[len] =
- '\0';
- memcpy(cpy,
- s,
- len);
- }
- return cpy;
-}
-#endif
-#endif /* STRNDUP_H */
diff --git a/subsurface-core/strtod.c b/subsurface-core/strtod.c
deleted file mode 100644
index 81e5d42d1..000000000
--- a/subsurface-core/strtod.c
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Sane helper for 'strtod()'.
- *
- * Sad that we even need this, but the C library version has
- * insane locale behavior, and while the Qt "doDouble()" routines
- * are better in that regard, they don't have an end pointer
- * (having replaced it with the completely idiotic "ok" boolean
- * pointer instead).
- *
- * I wonder what drugs people are on sometimes.
- *
- * Right now we support the following flags to limit the
- * parsing some ways:
- *
- * STRTOD_NO_SIGN - don't accept signs
- * STRTOD_NO_DOT - no decimal dots, I'm European
- * STRTOD_NO_COMMA - no comma, please, I'm C locale
- * STRTOD_NO_EXPONENT - no exponent parsing, I'm human
- *
- * The "negative" flags are so that the common case can just
- * use a flag value of 0, and only if you have some special
- * requirements do you need to state those with explicit flags.
- *
- * So if you want the C locale kind of parsing, you'd use the
- * STRTOD_NO_COMMA flag to disallow a decimal comma. But if you
- * want a more relaxed "Hey, Europeans are people too, even if
- * they have locales with commas", just pass in a zero flag.
- */
-#include <ctype.h>
-#include "dive.h"
-
-double strtod_flags(const char *str, const char **ptr, unsigned int flags)
-{
- char c;
- const char *p = str, *ep;
- double val = 0.0;
- double decimal = 1.0;
- int sign = 0, esign = 0;
- int numbers = 0, dot = 0;
-
- /* skip spaces */
- while (isspace(c = *p++))
- /* */;
-
- /* optional sign */
- if (!(flags & STRTOD_NO_SIGN)) {
- switch (c) {
- case '-':
- sign = 1;
- /* fallthrough */
- case '+':
- c = *p++;
- }
- }
-
- /* Mantissa */
- for (;; c = *p++) {
- if ((c == '.' && !(flags & STRTOD_NO_DOT)) ||
- (c == ',' && !(flags & STRTOD_NO_COMMA))) {
- if (dot)
- goto done;
- dot = 1;
- continue;
- }
- if (c >= '0' && c <= '9') {
- numbers++;
- val = (val * 10) + (c - '0');
- if (dot)
- decimal *= 10;
- continue;
- }
- if (c != 'e' && c != 'E')
- goto done;
- if (flags & STRTOD_NO_EXPONENT)
- goto done;
- break;
- }
-
- if (!numbers)
- goto done;
-
- /* Exponent */
- ep = p;
- c = *ep++;
- switch (c) {
- case '-':
- esign = 1;
- /* fallthrough */
- case '+':
- c = *ep++;
- }
-
- if (c >= '0' && c <= '9') {
- p = ep;
- int exponent = c - '0';
-
- for (;;) {
- c = *p++;
- if (c < '0' || c > '9')
- break;
- exponent *= 10;
- exponent += c - '0';
- }
-
- /* We're not going to bother playing games */
- if (exponent > 308)
- exponent = 308;
-
- while (exponent-- > 0) {
- if (esign)
- decimal *= 10;
- else
- decimal /= 10;
- }
- }
-
-done:
- if (!numbers)
- goto no_conversion;
- if (ptr)
- *ptr = p - 1;
- return (sign ? -val : val) / decimal;
-
-no_conversion:
- if (ptr)
- *ptr = str;
- return 0.0;
-}
diff --git a/subsurface-core/subsurface-qt/DiveObjectHelper.cpp b/subsurface-core/subsurface-qt/DiveObjectHelper.cpp
deleted file mode 100644
index ab88d2f3c..000000000
--- a/subsurface-core/subsurface-qt/DiveObjectHelper.cpp
+++ /dev/null
@@ -1,338 +0,0 @@
-#include "DiveObjectHelper.h"
-
-#include <QDateTime>
-#include <QTextDocument>
-
-#include "../qthelper.h"
-#include "../helpers.h"
-
-static QString EMPTY_DIVE_STRING = QStringLiteral("--");
-enum returnPressureSelector {START_PRESSURE, END_PRESSURE};
-
-static QString getFormattedWeight(struct dive *dive, unsigned int idx)
-{
- weightsystem_t *weight = &dive->weightsystem[idx];
- if (!weight->description)
- return QString(EMPTY_DIVE_STRING);
- QString fmt = QString(weight->description);
- fmt += ", " + get_weight_string(weight->weight, true);
- return fmt;
-}
-
-static QString getFormattedCylinder(struct dive *dive, unsigned int idx)
-{
- cylinder_t *cyl = &dive->cylinder[idx];
- const char *desc = cyl->type.description;
- if (!desc && idx > 0)
- return QString(EMPTY_DIVE_STRING);
- QString fmt = desc ? QString(desc) : QObject::tr("unknown");
- fmt += ", " + get_volume_string(cyl->type.size, true);
- fmt += ", " + get_pressure_string(cyl->type.workingpressure, true);
- fmt += ", " + get_pressure_string(cyl->start, false) + " - " + get_pressure_string(cyl->end, true);
- fmt += ", " + get_gas_string(cyl->gasmix);
- return fmt;
-}
-
-static QString getPressures(struct dive *dive, enum returnPressureSelector ret)
-{
- cylinder_t *cyl = &dive->cylinder[0];
- QString fmt;
- if (ret == START_PRESSURE) {
- if (cyl->start.mbar)
- fmt = get_pressure_string(cyl->start, true);
- else if (cyl->sample_start.mbar)
- fmt = get_pressure_string(cyl->sample_start, true);
- }
- if (ret == END_PRESSURE) {
- if (cyl->end.mbar)
- fmt = get_pressure_string(cyl->end, true);
- else if(cyl->sample_end.mbar)
- fmt = get_pressure_string(cyl->sample_end, true);
- }
- return fmt;
-}
-
-DiveObjectHelper::DiveObjectHelper(struct dive *d) :
- m_dive(d)
-{
-}
-
-DiveObjectHelper::~DiveObjectHelper()
-{
-}
-
-int DiveObjectHelper::number() const
-{
- return m_dive->number;
-}
-
-int DiveObjectHelper::id() const
-{
- return m_dive->id;
-}
-
-QString DiveObjectHelper::date() const
-{
- QDateTime localTime = QDateTime::fromTime_t(m_dive->when - gettimezoneoffset(m_dive->when));
- localTime.setTimeSpec(Qt::UTC);
- return localTime.date().toString(prefs.date_format);
-}
-
-timestamp_t DiveObjectHelper::timestamp() const
-{
- return m_dive->when;
-}
-
-QString DiveObjectHelper::time() const
-{
- QDateTime localTime = QDateTime::fromTime_t(m_dive->when - gettimezoneoffset(m_dive->when));
- localTime.setTimeSpec(Qt::UTC);
- return localTime.time().toString(prefs.time_format);
-}
-
-QString DiveObjectHelper::location() const
-{
- return get_dive_location(m_dive) ? QString::fromUtf8(get_dive_location(m_dive)) : EMPTY_DIVE_STRING;
-}
-
-QString DiveObjectHelper::gps() const
-{
- struct dive_site *ds = get_dive_site_by_uuid(m_dive->dive_site_uuid);
- return ds ? QString(printGPSCoords(ds->latitude.udeg, ds->longitude.udeg)) : QString();
-}
-QString DiveObjectHelper::duration() const
-{
- return get_dive_duration_string(m_dive->duration.seconds, QObject::tr("h:"), QObject::tr("min"));
-}
-
-bool DiveObjectHelper::noDive() const
-{
- return m_dive->duration.seconds == 0 && m_dive->dc.duration.seconds == 0;
-}
-
-QString DiveObjectHelper::depth() const
-{
- return get_depth_string(m_dive->dc.maxdepth.mm, true, true);
-}
-
-QString DiveObjectHelper::divemaster() const
-{
- return m_dive->divemaster ? m_dive->divemaster : EMPTY_DIVE_STRING;
-}
-
-QString DiveObjectHelper::buddy() const
-{
- return m_dive->buddy ? m_dive->buddy : EMPTY_DIVE_STRING;
-}
-
-QString DiveObjectHelper::airTemp() const
-{
- QString temp = get_temperature_string(m_dive->airtemp, true);
- if (temp.isEmpty()) {
- temp = EMPTY_DIVE_STRING;
- }
- return temp;
-}
-
-QString DiveObjectHelper::waterTemp() const
-{
- QString temp = get_temperature_string(m_dive->watertemp, true);
- if (temp.isEmpty()) {
- temp = EMPTY_DIVE_STRING;
- }
- return temp;
-}
-
-QString DiveObjectHelper::notes() const
-{
- QString tmp = m_dive->notes ? QString::fromUtf8(m_dive->notes) : EMPTY_DIVE_STRING;
- if (same_string(m_dive->dc.model, "planned dive")) {
- QTextDocument notes;
- #define _NOTES_BR "&#92n"
- tmp.replace("<thead>", "<thead>" _NOTES_BR)
- .replace("<br>", "<br>" _NOTES_BR)
- .replace("<tr>", "<tr>" _NOTES_BR)
- .replace("</tr>", "</tr>" _NOTES_BR);
- notes.setHtml(tmp);
- tmp = notes.toPlainText();
- tmp.replace(_NOTES_BR, "<br>");
- #undef _NOTES_BR
- } else {
- tmp.replace("\n", "<br>");
- }
- return tmp;
-}
-
-QString DiveObjectHelper::tags() const
-{
- static char buffer[256];
- taglist_get_tagstring(m_dive->tag_list, buffer, 256);
- return QString(buffer);
-}
-
-QString DiveObjectHelper::gas() const
-{
- /*WARNING: here should be the gastlist, returned
- * from the get_gas_string function or this is correct?
- */
- QString gas, gases;
- for (int i = 0; i < MAX_CYLINDERS; i++) {
- if (!is_cylinder_used(m_dive, i))
- continue;
- gas = m_dive->cylinder[i].type.description;
- if (!gas.isEmpty())
- gas += QChar(' ');
- gas += gasname(&m_dive->cylinder[i].gasmix);
- // if has a description and if such gas is not already present
- if (!gas.isEmpty() && gases.indexOf(gas) == -1) {
- if (!gases.isEmpty())
- gases += QString(" / ");
- gases += gas;
- }
- }
- return gases;
-}
-
-QString DiveObjectHelper::sac() const
-{
- if (!m_dive->sac)
- return QString();
- const char *unit;
- int decimal;
- double value = get_volume_units(m_dive->sac, &decimal, &unit);
- return QString::number(value, 'f', decimal).append(unit);
-}
-
-QString DiveObjectHelper::weightList() const
-{
- QString weights;
- for (int i = 0; i < MAX_WEIGHTSYSTEMS; i++) {
- QString w = getFormattedWeight(m_dive, i);
- if (w == EMPTY_DIVE_STRING)
- continue;
- weights += w + "; ";
- }
- return weights;
-}
-
-QStringList DiveObjectHelper::weights() const
-{
- QStringList weights;
- for (int i = 0; i < MAX_WEIGHTSYSTEMS; i++)
- weights << getFormattedWeight(m_dive, i);
- return weights;
-}
-
-bool DiveObjectHelper::singleWeight() const
-{
- return weightsystem_none(&m_dive->weightsystem[1]);
-}
-
-QString DiveObjectHelper::weight(int idx) const
-{
- if ( (idx < 0) || idx > MAX_WEIGHTSYSTEMS )
- return QString(EMPTY_DIVE_STRING);
- return getFormattedWeight(m_dive, idx);
-}
-
-QString DiveObjectHelper::suit() const
-{
- return m_dive->suit ? m_dive->suit : EMPTY_DIVE_STRING;
-}
-
-QString DiveObjectHelper::cylinderList() const
-{
- QString cylinders;
- for (int i = 0; i < MAX_CYLINDERS; i++) {
- QString cyl = getFormattedCylinder(m_dive, i);
- if (cyl == EMPTY_DIVE_STRING)
- continue;
- cylinders += cyl + "; ";
- }
- return cylinders;
-}
-
-QStringList DiveObjectHelper::cylinders() const
-{
- QStringList cylinders;
- for (int i = 0; i < MAX_CYLINDERS; i++)
- cylinders << getFormattedCylinder(m_dive, i);
- return cylinders;
-}
-
-QString DiveObjectHelper::cylinder(int idx) const
-{
- if ( (idx < 0) || idx > MAX_CYLINDERS)
- return QString(EMPTY_DIVE_STRING);
- return getFormattedCylinder(m_dive, idx);
-}
-
-QString DiveObjectHelper::trip() const
-{
- return m_dive->divetrip ? m_dive->divetrip->location : EMPTY_DIVE_STRING;
-}
-
-// combine the pointer address with the trip location so that
-// we detect multiple, destinct trips to the same location
-QString DiveObjectHelper::tripMeta() const
-{
- QString ret = EMPTY_DIVE_STRING;
- if (m_dive->divetrip)
- ret = QString::number((quint64)m_dive->divetrip, 16) + QLatin1Literal("::") + m_dive->divetrip->location;
- return ret;
-}
-
-QString DiveObjectHelper::maxcns() const
-{
- return QString(m_dive->maxcns);
-}
-
-QString DiveObjectHelper::otu() const
-{
- return QString(m_dive->otu);
-}
-
-int DiveObjectHelper::rating() const
-{
- return m_dive->rating;
-}
-
-QString DiveObjectHelper::sumWeight() const
-{
- weight_t sum = { 0 };
- for (int i = 0; i < MAX_WEIGHTSYSTEMS; i++){
- sum.grams += m_dive->weightsystem[i].weight.grams;
- }
- return get_weight_string(sum, true);
-}
-
-QString DiveObjectHelper::getCylinder() const
-{
- QString getCylinder;
- if (is_cylinder_used(m_dive, 1)){
- getCylinder = QObject::tr("Multiple");
- }
- else {
- getCylinder = m_dive->cylinder[0].type.description;
- }
- return getCylinder;
-}
-
-QString DiveObjectHelper::startPressure() const
-{
- QString startPressure = getPressures(m_dive, START_PRESSURE);
- return startPressure;
-}
-
-QString DiveObjectHelper::endPressure() const
-{
- QString endPressure = getPressures(m_dive, END_PRESSURE);
- return endPressure;
-}
-
-QString DiveObjectHelper::firstGas() const
-{
- QString gas;
- gas = get_gas_string(m_dive->cylinder[0].gasmix);
- return gas;
-}
diff --git a/subsurface-core/subsurface-qt/DiveObjectHelper.h b/subsurface-core/subsurface-qt/DiveObjectHelper.h
deleted file mode 100644
index 602775ef8..000000000
--- a/subsurface-core/subsurface-qt/DiveObjectHelper.h
+++ /dev/null
@@ -1,89 +0,0 @@
-#ifndef DIVE_QOBJECT_H
-#define DIVE_QOBJECT_H
-
-#include "../dive.h"
-#include <QObject>
-#include <QString>
-#include <QStringList>
-
-class DiveObjectHelper : public QObject {
- Q_OBJECT
- Q_PROPERTY(int number READ number CONSTANT)
- Q_PROPERTY(int id READ id CONSTANT)
- Q_PROPERTY(int rating READ rating CONSTANT)
- Q_PROPERTY(QString date READ date CONSTANT)
- Q_PROPERTY(QString time READ time CONSTANT)
- Q_PROPERTY(QString location READ location CONSTANT)
- Q_PROPERTY(QString gps READ gps CONSTANT)
- Q_PROPERTY(QString duration READ duration CONSTANT)
- Q_PROPERTY(bool noDive READ noDive CONSTANT)
- Q_PROPERTY(QString depth READ depth CONSTANT)
- Q_PROPERTY(QString divemaster READ divemaster CONSTANT)
- Q_PROPERTY(QString buddy READ buddy CONSTANT)
- Q_PROPERTY(QString airTemp READ airTemp CONSTANT)
- Q_PROPERTY(QString waterTemp READ waterTemp CONSTANT)
- Q_PROPERTY(QString notes READ notes CONSTANT)
- Q_PROPERTY(QString tags READ tags CONSTANT)
- Q_PROPERTY(QString gas READ gas CONSTANT)
- Q_PROPERTY(QString sac READ sac CONSTANT)
- Q_PROPERTY(QString weightList READ weightList CONSTANT)
- Q_PROPERTY(QStringList weights READ weights CONSTANT)
- Q_PROPERTY(bool singleWeight READ singleWeight CONSTANT)
- Q_PROPERTY(QString suit READ suit CONSTANT)
- Q_PROPERTY(QString cylinderList READ cylinderList CONSTANT)
- Q_PROPERTY(QStringList cylinders READ cylinders CONSTANT)
- Q_PROPERTY(QString trip READ trip CONSTANT)
- Q_PROPERTY(QString tripMeta READ tripMeta CONSTANT)
- Q_PROPERTY(QString maxcns READ maxcns CONSTANT)
- Q_PROPERTY(QString otu READ otu CONSTANT)
- Q_PROPERTY(QString sumWeight READ sumWeight CONSTANT)
- Q_PROPERTY(QString getCylinder READ getCylinder CONSTANT)
- Q_PROPERTY(QString startPressure READ startPressure CONSTANT)
- Q_PROPERTY(QString endPressure READ endPressure CONSTANT)
- Q_PROPERTY(QString firstGas READ firstGas CONSTANT)
-public:
- DiveObjectHelper(struct dive *dive = NULL);
- ~DiveObjectHelper();
- int number() const;
- int id() const;
- int rating() const;
- QString date() const;
- timestamp_t timestamp() const;
- QString time() const;
- QString location() const;
- QString gps() const;
- QString duration() const;
- bool noDive() const;
- QString depth() const;
- QString divemaster() const;
- QString buddy() const;
- QString airTemp() const;
- QString waterTemp() const;
- QString notes() const;
- QString tags() const;
- QString gas() const;
- QString sac() const;
- QString weightList() const;
- QStringList weights() const;
- QString weight(int idx) const;
- bool singleWeight() const;
- QString suit() const;
- QString cylinderList() const;
- QStringList cylinders() const;
- QString cylinder(int idx) const;
- QString trip() const;
- QString tripMeta() const;
- QString maxcns() const;
- QString otu() const;
- QString sumWeight() const;
- QString getCylinder() const;
- QString startPressure() const;
- QString endPressure() const;
- QString firstGas() const;
-
-private:
- struct dive *m_dive;
-};
- Q_DECLARE_METATYPE(DiveObjectHelper *)
-
-#endif
diff --git a/subsurface-core/subsurface-qt/SettingsObjectWrapper.cpp b/subsurface-core/subsurface-qt/SettingsObjectWrapper.cpp
deleted file mode 100644
index e43be1a9b..000000000
--- a/subsurface-core/subsurface-qt/SettingsObjectWrapper.cpp
+++ /dev/null
@@ -1,1617 +0,0 @@
-#include "SettingsObjectWrapper.h"
-#include <QSettings>
-#include <QApplication>
-#include <QFont>
-
-#include "../dive.h" // TODO: remove copy_string from dive.h
-
-
-static QString tecDetails = QStringLiteral("TecDetails");
-
-PartialPressureGasSettings::PartialPressureGasSettings(QObject* parent):
- QObject(parent),
- group("TecDetails")
-{
-
-}
-
-short PartialPressureGasSettings::showPo2() const
-{
- return prefs.pp_graphs.po2;
-}
-
-short PartialPressureGasSettings::showPn2() const
-{
- return prefs.pp_graphs.pn2;
-}
-
-short PartialPressureGasSettings::showPhe() const
-{
- return prefs.pp_graphs.phe;
-}
-
-double PartialPressureGasSettings::po2Threshold() const
-{
- return prefs.pp_graphs.po2_threshold;
-}
-
-double PartialPressureGasSettings::pn2Threshold() const
-{
- return prefs.pp_graphs.pn2_threshold;
-}
-
-double PartialPressureGasSettings::pheThreshold() const
-{
- return prefs.pp_graphs.phe_threshold;
-}
-
-void PartialPressureGasSettings::setShowPo2(short value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("po2graph", value);
- prefs.pp_graphs.po2 = value;
- emit showPo2Changed(value);
-}
-
-void PartialPressureGasSettings::setShowPn2(short value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("pn2graph", value);
- prefs.pp_graphs.pn2 = value;
- emit showPn2Changed(value);
-}
-
-void PartialPressureGasSettings::setShowPhe(short value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("phegraph", value);
- prefs.pp_graphs.phe = value;
- emit showPheChanged(value);
-}
-
-void PartialPressureGasSettings::setPo2Threshold(double value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("po2threshold", value);
- prefs.pp_graphs.po2_threshold = value;
- emit po2ThresholdChanged(value);
-}
-
-void PartialPressureGasSettings::setPn2Threshold(double value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("pn2threshold", value);
- prefs.pp_graphs.pn2_threshold = value;
- emit pn2ThresholdChanged(value);
-}
-
-void PartialPressureGasSettings::setPheThreshold(double value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("phethreshold", value);
- prefs.pp_graphs.phe_threshold = value;
- emit pheThresholdChanged(value);
-}
-
-
-TechnicalDetailsSettings::TechnicalDetailsSettings(QObject* parent): QObject(parent)
-{
-
-}
-
-double TechnicalDetailsSettings:: modp02() const
-{
- return prefs.modpO2;
-}
-
-bool TechnicalDetailsSettings::ead() const
-{
- return prefs.ead;
-}
-
-bool TechnicalDetailsSettings::dcceiling() const
-{
- return prefs.dcceiling;
-}
-
-bool TechnicalDetailsSettings::redceiling() const
-{
- return prefs.redceiling;
-}
-
-bool TechnicalDetailsSettings::calcceiling() const
-{
- return prefs.calcceiling;
-}
-
-bool TechnicalDetailsSettings::calcceiling3m() const
-{
- return prefs.calcceiling3m;
-}
-
-bool TechnicalDetailsSettings::calcalltissues() const
-{
- return prefs.calcalltissues;
-}
-
-bool TechnicalDetailsSettings::calcndltts() const
-{
- return prefs.calcndltts;
-}
-
-bool TechnicalDetailsSettings::gflow() const
-{
- return prefs.gflow;
-}
-
-bool TechnicalDetailsSettings::gfhigh() const
-{
- return prefs.gfhigh;
-}
-
-bool TechnicalDetailsSettings::hrgraph() const
-{
- return prefs.hrgraph;
-}
-
-bool TechnicalDetailsSettings::tankBar() const
-{
- return prefs.tankbar;
-}
-
-bool TechnicalDetailsSettings::percentageGraph() const
-{
- return prefs.percentagegraph;
-}
-
-bool TechnicalDetailsSettings::rulerGraph() const
-{
- return prefs.rulergraph;
-}
-
-bool TechnicalDetailsSettings::showCCRSetpoint() const
-{
- return prefs.show_ccr_setpoint;
-}
-
-bool TechnicalDetailsSettings::showCCRSensors() const
-{
- return prefs.show_ccr_sensors;
-}
-
-bool TechnicalDetailsSettings::zoomedPlot() const
-{
- return prefs.zoomed_plot;
-}
-
-bool TechnicalDetailsSettings::showSac() const
-{
- return prefs.show_sac;
-}
-
-bool TechnicalDetailsSettings::gfLowAtMaxDepth() const
-{
- return prefs.gf_low_at_maxdepth;
-}
-
-bool TechnicalDetailsSettings::displayUnusedTanks() const
-{
- return prefs.display_unused_tanks;
-}
-
-bool TechnicalDetailsSettings::showAverageDepth() const
-{
- return prefs.show_average_depth;
-}
-
-bool TechnicalDetailsSettings::mod() const
-{
- return prefs.mod;
-}
-
-bool TechnicalDetailsSettings::showPicturesInProfile() const
-{
- return prefs.show_pictures_in_profile;
-}
-
-void TechnicalDetailsSettings::setModp02(double value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("modpO2", value);
- prefs.modpO2 = value;
- emit modpO2Changed(value);
-}
-
-void TechnicalDetailsSettings::setShowPicturesInProfile(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("show_pictures_in_profile", value);
- prefs.show_pictures_in_profile = value;
- emit showPicturesInProfileChanged(value);
-}
-
-void TechnicalDetailsSettings::setEad(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("ead", value);
- prefs.ead = value;
- emit eadChanged(value);
-}
-
-void TechnicalDetailsSettings::setMod(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("mod", value);
- prefs.mod = value;
- emit modChanged(value);
-}
-
-void TechnicalDetailsSettings::setDCceiling(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("dcceiling", value);
- prefs.dcceiling = value;
- emit dcceilingChanged(value);
-}
-
-void TechnicalDetailsSettings::setRedceiling(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("redceiling", value);
- prefs.redceiling = value;
- emit redceilingChanged(value);
-}
-
-void TechnicalDetailsSettings::setCalcceiling(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("calcceiling", value);
- prefs.calcceiling = value;
- emit calcceilingChanged(value);
-}
-
-void TechnicalDetailsSettings::setCalcceiling3m(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("calcceiling3m", value);
- prefs.calcceiling3m = value;
- emit calcceiling3mChanged(value);
-}
-
-void TechnicalDetailsSettings::setCalcalltissues(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("calcalltissues", value);
- prefs.calcalltissues = value;
- emit calcalltissuesChanged(value);
-}
-
-void TechnicalDetailsSettings::setCalcndltts(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("calcndltts", value);
- prefs.calcndltts = value;
- emit calcndlttsChanged(value);
-}
-
-void TechnicalDetailsSettings::setGflow(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("gflow", value);
- prefs.gflow = value;
- set_gf(prefs.gflow, prefs.gfhigh, prefs.gf_low_at_maxdepth);
- emit gflowChanged(value);
-}
-
-void TechnicalDetailsSettings::setGfhigh(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("gfhigh", value);
- prefs.gfhigh = value;
- set_gf(prefs.gflow, prefs.gfhigh, prefs.gf_low_at_maxdepth);
- emit gfhighChanged(value);
-}
-
-void TechnicalDetailsSettings::setHRgraph(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("hrgraph", value);
- prefs.hrgraph = value;
- emit hrgraphChanged(value);
-}
-
-void TechnicalDetailsSettings::setTankBar(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("tankbar", value);
- prefs.tankbar = value;
- emit tankBarChanged(value);
-}
-
-void TechnicalDetailsSettings::setPercentageGraph(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("percentagegraph", value);
- prefs.percentagegraph = value;
- emit percentageGraphChanged(value);
-}
-
-void TechnicalDetailsSettings::setRulerGraph(bool value)
-{
- /* TODO: search for the QSettings of the RulerBar */
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("RulerBar", value);
- prefs.rulergraph = value;
- emit rulerGraphChanged(value);
-}
-
-void TechnicalDetailsSettings::setShowCCRSetpoint(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("show_ccr_setpoint", value);
- prefs.show_ccr_setpoint = value;
- emit showCCRSetpointChanged(value);
-}
-
-void TechnicalDetailsSettings::setShowCCRSensors(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("show_ccr_sensors", value);
- prefs.show_ccr_sensors = value;
- emit showCCRSensorsChanged(value);
-}
-
-void TechnicalDetailsSettings::setZoomedPlot(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("zoomed_plot", value);
- prefs.zoomed_plot = value;
- emit zoomedPlotChanged(value);
-}
-
-void TechnicalDetailsSettings::setShowSac(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("show_sac", value);
- prefs.show_sac = value;
- emit showSacChanged(value);
-}
-
-void TechnicalDetailsSettings::setGfLowAtMaxDepth(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("gf_low_at_maxdepth", value);
- prefs.gf_low_at_maxdepth = value;
- set_gf(prefs.gflow, prefs.gfhigh, prefs.gf_low_at_maxdepth);
- emit gfLowAtMaxDepthChanged(value);
-}
-
-void TechnicalDetailsSettings::setDisplayUnusedTanks(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("display_unused_tanks", value);
- prefs.display_unused_tanks = value;
- emit displayUnusedTanksChanged(value);
-}
-
-void TechnicalDetailsSettings::setShowAverageDepth(bool value)
-{
- QSettings s;
- s.beginGroup(tecDetails);
- s.setValue("show_average_depth", value);
- prefs.show_average_depth = value;
- emit showAverageDepthChanged(value);
-}
-
-
-
-FacebookSettings::FacebookSettings(QObject *parent) :
- QObject(parent),
- group(QStringLiteral("WebApps")),
- subgroup(QStringLiteral("Facebook"))
-{
-}
-
-QString FacebookSettings::accessToken() const
-{
- return QString(prefs.facebook.access_token);
-}
-
-QString FacebookSettings::userId() const
-{
- return QString(prefs.facebook.user_id);
-}
-
-QString FacebookSettings::albumId() const
-{
- return QString(prefs.facebook.album_id);
-}
-
-void FacebookSettings::setAccessToken (const QString& value)
-{
-#if SAVE_FB_CREDENTIALS
- QSettings s;
- s.beginGroup(group);
- s.beginGroup(subgroup);
- s.setValue("ConnectToken", value);
-#endif
- prefs.facebook.access_token = copy_string(qPrintable(value));
- emit accessTokenChanged(value);
-}
-
-void FacebookSettings::setUserId(const QString& value)
-{
-#if SAVE_FB_CREDENTIALS
- QSettings s;
- s.beginGroup(group);
- s.beginGroup(subgroup);
- s.setValue("UserId", value);
-#endif
- prefs.facebook.user_id = copy_string(qPrintable(value));
- emit userIdChanged(value);
-}
-
-void FacebookSettings::setAlbumId(const QString& value)
-{
-#if SAVE_FB_CREDENTIALS
- QSettings s;
- s.beginGroup(group);
- s.beginGroup(subgroup);
- s.setValue("AlbumId", value);
-#endif
- prefs.facebook.album_id = copy_string(qPrintable(value));
- emit albumIdChanged(value);
-}
-
-
-GeocodingPreferences::GeocodingPreferences(QObject *parent) :
- QObject(parent),
- group(QStringLiteral("geocoding"))
-{
-
-}
-
-bool GeocodingPreferences::enableGeocoding() const
-{
- return prefs.geocoding.enable_geocoding;
-}
-
-bool GeocodingPreferences::parseDiveWithoutGps() const
-{
- return prefs.geocoding.parse_dive_without_gps;
-}
-
-bool GeocodingPreferences::tagExistingDives() const
-{
- return prefs.geocoding.tag_existing_dives;
-}
-
-taxonomy_category GeocodingPreferences::firstTaxonomyCategory() const
-{
- return prefs.geocoding.category[0];
-}
-
-taxonomy_category GeocodingPreferences::secondTaxonomyCategory() const
-{
- return prefs.geocoding.category[1];
-}
-
-taxonomy_category GeocodingPreferences::thirdTaxonomyCategory() const
-{
- return prefs.geocoding.category[2];
-}
-
-void GeocodingPreferences::setEnableGeocoding(bool value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("enable_geocoding", value);
- prefs.geocoding.enable_geocoding = value;
- emit enableGeocodingChanged(value);
-}
-
-void GeocodingPreferences::setParseDiveWithoutGps(bool value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("parse_dives_without_gps", value);
- prefs.geocoding.parse_dive_without_gps = value;
- emit parseDiveWithoutGpsChanged(value);
-}
-
-void GeocodingPreferences::setTagExistingDives(bool value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("tag_existing_dives", value);
- prefs.geocoding.tag_existing_dives = value;
- emit tagExistingDivesChanged(value);
-}
-
-void GeocodingPreferences::setFirstTaxonomyCategory(taxonomy_category value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("cat0", value);
- prefs.show_average_depth = value;
- emit firstTaxonomyCategoryChanged(value);
-}
-
-void GeocodingPreferences::setSecondTaxonomyCategory(taxonomy_category value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("cat1", value);
- prefs.show_average_depth = value;
- emit secondTaxonomyCategoryChanged(value);
-}
-
-void GeocodingPreferences::setThirdTaxonomyCategory(taxonomy_category value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("cat2", value);
- prefs.show_average_depth = value;
- emit thirdTaxonomyCategoryChanged(value);
-}
-
-ProxySettings::ProxySettings(QObject *parent) :
- QObject(parent),
- group(QStringLiteral("Network"))
-{
-}
-
-int ProxySettings::type() const
-{
- return prefs.proxy_type;
-}
-
-QString ProxySettings::host() const
-{
- return prefs.proxy_host;
-}
-
-int ProxySettings::port() const
-{
- return prefs.proxy_port;
-}
-
-short ProxySettings::auth() const
-{
- return prefs.proxy_auth;
-}
-
-QString ProxySettings::user() const
-{
- return prefs.proxy_user;
-}
-
-QString ProxySettings::pass() const
-{
- return prefs.proxy_pass;
-}
-
-void ProxySettings::setType(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("proxy_type", value);
- prefs.proxy_type = value;
- emit typeChanged(value);
-}
-
-void ProxySettings::setHost(const QString& value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("proxy_host", value);
- free(prefs.proxy_host);
- prefs.proxy_host = copy_string(qPrintable(value));;
- emit hostChanged(value);
-}
-
-void ProxySettings::setPort(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("proxy_port", value);
- prefs.proxy_port = value;
- emit portChanged(value);
-}
-
-void ProxySettings::setAuth(short value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("proxy_auth", value);
- prefs.proxy_auth = value;
- emit authChanged(value);
-}
-
-void ProxySettings::setUser(const QString& value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("proxy_user", value);
- free(prefs.proxy_user);
- prefs.proxy_user = copy_string(qPrintable(value));
- emit userChanged(value);
-}
-
-void ProxySettings::setPass(const QString& value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("proxy_pass", value);
- free(prefs.proxy_pass);
- prefs.proxy_pass = copy_string(qPrintable(value));
- emit passChanged(value);
-}
-
-CloudStorageSettings::CloudStorageSettings(QObject *parent) :
- QObject(parent),
- group(QStringLiteral("CloudStorage"))
-{
-
-}
-
-bool CloudStorageSettings::gitLocalOnly() const
-{
- return prefs.git_local_only;
-}
-
-QString CloudStorageSettings::password() const
-{
- return QString(prefs.cloud_storage_password);
-}
-
-QString CloudStorageSettings::newPassword() const
-{
- return QString(prefs.cloud_storage_newpassword);
-}
-
-QString CloudStorageSettings::email() const
-{
- return QString(prefs.cloud_storage_email);
-}
-
-QString CloudStorageSettings::emailEncoded() const
-{
- return QString(prefs.cloud_storage_email_encoded);
-}
-
-bool CloudStorageSettings::savePasswordLocal() const
-{
- return prefs.save_password_local;
-}
-
-short CloudStorageSettings::verificationStatus() const
-{
- return prefs.cloud_verification_status;
-}
-
-bool CloudStorageSettings::backgroundSync() const
-{
- return prefs.cloud_background_sync;
-}
-
-QString CloudStorageSettings::userId() const
-{
- return QString(prefs.userid);
-}
-
-QString CloudStorageSettings::baseUrl() const
-{
- return QString(prefs.cloud_base_url);
-}
-
-QString CloudStorageSettings::gitUrl() const
-{
- return QString(prefs.cloud_git_url);
-}
-
-void CloudStorageSettings::setPassword(const QString& value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("password", value);
- free(prefs.proxy_pass);
- prefs.proxy_pass = copy_string(qPrintable(value));
- emit passwordChanged(value);
-}
-
-void CloudStorageSettings::setNewPassword(const QString& value)
-{
- /*TODO: This looks like wrong, but 'new password' is not saved on disk, why it's on prefs? */
- free(prefs.cloud_storage_newpassword);
- prefs.cloud_storage_newpassword = copy_string(qPrintable(value));
- emit newPasswordChanged(value);
-}
-
-void CloudStorageSettings::setEmail(const QString& value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("email", value);
- free(prefs.cloud_storage_email);
- prefs.cloud_storage_email = copy_string(qPrintable(value));
- emit emailChanged(value);
-}
-
-void CloudStorageSettings::setUserId(const QString& value)
-{
- //WARNING: UserId is stored outside of any group, but it belongs to Cloud Storage.
- QSettings s;
- s.setValue("subsurface_webservice_uid", value);
- free(prefs.userid);
- prefs.userid = copy_string(qPrintable(value));
- emit userIdChanged(value);
-}
-
-void CloudStorageSettings::setEmailEncoded(const QString& value)
-{
- /*TODO: This looks like wrong, but 'email encoded' is not saved on disk, why it's on prefs? */
- free(prefs.cloud_storage_email_encoded);
- prefs.cloud_storage_email_encoded = copy_string(qPrintable(value));
- emit emailEncodedChanged(value);
-}
-
-void CloudStorageSettings::setSavePasswordLocal(bool value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("save_password_local", value);
- prefs.save_password_local = value;
- emit savePasswordLocalChanged(value);
-}
-
-void CloudStorageSettings::setVerificationStatus(short value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("cloud_verification_status", value);
- prefs.cloud_verification_status = value;
- emit verificationStatusChanged(value);
-}
-
-void CloudStorageSettings::setBackgroundSync(bool value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("cloud_background_sync", value);
- prefs.cloud_background_sync = value;
- emit backgroundSyncChanged(value);
-}
-
-void CloudStorageSettings::setBaseUrl(const QString& value)
-{
- free((void*)prefs.cloud_base_url);
- free((void*)prefs.cloud_git_url);
- prefs.cloud_base_url = copy_string(qPrintable(value));
- prefs.cloud_git_url = copy_string(qPrintable(QString(prefs.cloud_base_url) + "/git"));
-}
-
-void CloudStorageSettings::setGitUrl(const QString& value)
-{
- Q_UNUSED(value); /* no op */
-}
-
-void CloudStorageSettings::setGitLocalOnly(bool value)
-{
- prefs.git_local_only = value;
-}
-
-DivePlannerSettings::DivePlannerSettings(QObject *parent) :
- QObject(parent),
- group(QStringLiteral("Planner"))
-{
-}
-
-bool DivePlannerSettings::lastStop() const
-{
- return prefs.last_stop;
-}
-
-bool DivePlannerSettings::verbatimPlan() const
-{
- return prefs.verbatim_plan;
-}
-
-bool DivePlannerSettings::displayRuntime() const
-{
- return prefs.display_runtime;
-}
-
-bool DivePlannerSettings::displayDuration() const
-{
- return prefs.display_duration;
-}
-
-bool DivePlannerSettings::displayTransitions() const
-{
- return prefs.display_transitions;
-}
-
-bool DivePlannerSettings::doo2breaks() const
-{
- return prefs.doo2breaks;
-}
-
-bool DivePlannerSettings::dropStoneMode() const
-{
- return prefs.drop_stone_mode;
-}
-
-bool DivePlannerSettings::safetyStop() const
-{
- return prefs.safetystop;
-}
-
-bool DivePlannerSettings::switchAtRequiredStop() const
-{
- return prefs.switch_at_req_stop;
-}
-
-int DivePlannerSettings::ascrate75() const
-{
- return prefs.ascrate75;
-}
-
-int DivePlannerSettings::ascrate50() const
-{
- return prefs.ascrate50;
-}
-
-int DivePlannerSettings::ascratestops() const
-{
- return prefs.ascratestops;
-}
-
-int DivePlannerSettings::ascratelast6m() const
-{
- return prefs.ascratelast6m;
-}
-
-int DivePlannerSettings::descrate() const
-{
- return prefs.descrate;
-}
-
-int DivePlannerSettings::bottompo2() const
-{
- return prefs.bottompo2;
-}
-
-int DivePlannerSettings::decopo2() const
-{
- return prefs.decopo2;
-}
-
-int DivePlannerSettings::reserveGas() const
-{
- return prefs.reserve_gas;
-}
-
-int DivePlannerSettings::minSwitchDuration() const
-{
- return prefs.min_switch_duration;
-}
-
-int DivePlannerSettings::bottomSac() const
-{
- return prefs.bottomsac;
-}
-
-int DivePlannerSettings::decoSac() const
-{
- return prefs.decosac;
-}
-
-short DivePlannerSettings::conservatismLevel() const
-{
- return prefs.conservatism_level;
-}
-
-deco_mode DivePlannerSettings::decoMode() const
-{
- return prefs.deco_mode;
-}
-
-void DivePlannerSettings::setLastStop(bool value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("last_stop", value);
- prefs.last_stop = value;
- emit lastStopChanged(value);
-}
-
-void DivePlannerSettings::setVerbatimPlan(bool value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("verbatim_plan", value);
- prefs.verbatim_plan = value;
- emit verbatimPlanChanged(value);
-}
-
-void DivePlannerSettings::setDisplayRuntime(bool value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("display_runtime", value);
- prefs.display_runtime = value;
- emit displayRuntimeChanged(value);
-}
-
-void DivePlannerSettings::setDisplayDuration(bool value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("display_duration", value);
- prefs.display_duration = value;
- emit displayDurationChanged(value);
-}
-
-void DivePlannerSettings::setDisplayTransitions(bool value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("display_transitions", value);
- prefs.display_transitions = value;
- emit displayTransitionsChanged(value);
-}
-
-void DivePlannerSettings::setDoo2breaks(bool value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("doo2breaks", value);
- prefs.doo2breaks = value;
- emit doo2breaksChanged(value);
-}
-
-void DivePlannerSettings::setDropStoneMode(bool value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("drop_stone_mode", value);
- prefs.drop_stone_mode = value;
- emit dropStoneModeChanged(value);
-}
-
-void DivePlannerSettings::setSafetyStop(bool value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("safetystop", value);
- prefs.safetystop = value;
- emit safetyStopChanged(value);
-}
-
-void DivePlannerSettings::setSwitchAtRequiredStop(bool value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("switch_at_req_stop", value);
- prefs.switch_at_req_stop = value;
- emit switchAtRequiredStopChanged(value);
-}
-
-void DivePlannerSettings::setAscrate75(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("ascrate75", value);
- prefs.ascrate75 = value;
- emit ascrate75Changed(value);
-}
-
-void DivePlannerSettings::setAscrate50(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("ascrate50", value);
- prefs.ascrate50 = value;
- emit ascrate50Changed(value);
-}
-
-void DivePlannerSettings::setAscratestops(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("ascratestops", value);
- prefs.ascratestops = value;
- emit ascratestopsChanged(value);
-}
-
-void DivePlannerSettings::setAscratelast6m(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("ascratelast6m", value);
- prefs.ascratelast6m = value;
- emit ascratelast6mChanged(value);
-}
-
-void DivePlannerSettings::setDescrate(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("descrate", value);
- prefs.descrate = value;
- emit descrateChanged(value);
-}
-
-void DivePlannerSettings::setBottompo2(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("bottompo2", value);
- prefs.bottompo2 = value;
- emit bottompo2Changed(value);
-}
-
-void DivePlannerSettings::setDecopo2(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("decopo2", value);
- prefs.decopo2 = value;
- emit decopo2Changed(value);
-}
-
-void DivePlannerSettings::setReserveGas(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("reserve_gas", value);
- prefs.reserve_gas = value;
- emit reserveGasChanged(value);
-}
-
-void DivePlannerSettings::setMinSwitchDuration(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("min_switch_duration", value);
- prefs.min_switch_duration = value;
- emit minSwitchDurationChanged(value);
-}
-
-void DivePlannerSettings::setBottomSac(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("bottomsac", value);
- prefs.bottomsac = value;
- emit bottomSacChanged(value);
-}
-
-void DivePlannerSettings::setSecoSac(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("decosac", value);
- prefs.decosac = value;
- emit decoSacChanged(value);
-}
-
-void DivePlannerSettings::setConservatismLevel(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("conservatism", value);
- prefs.conservatism_level = value;
- emit conservatismLevelChanged(value);
-}
-
-void DivePlannerSettings::setDecoMode(deco_mode value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("deco_mode", value);
- prefs.deco_mode = value;
- emit decoModeChanged(value);
-}
-
-UnitsSettings::UnitsSettings(QObject *parent) :
- QObject(parent),
- group(QStringLiteral("Units"))
-{
-
-}
-
-int UnitsSettings::length() const
-{
- return prefs.units.length;
-}
-
-int UnitsSettings::pressure() const
-{
- return prefs.units.pressure;
-}
-
-int UnitsSettings::volume() const
-{
- return prefs.units.volume;
-}
-
-int UnitsSettings::temperature() const
-{
- return prefs.units.temperature;
-}
-
-int UnitsSettings::weight() const
-{
- return prefs.units.weight;
-}
-
-int UnitsSettings::verticalSpeedTime() const
-{
- return prefs.units.vertical_speed_time;
-}
-
-QString UnitsSettings::unitSystem() const
-{
- return QString(); /*FIXME: there's no char * units on the prefs. */
-}
-
-bool UnitsSettings::coordinatesTraditional() const
-{
- return prefs.coordinates_traditional;
-}
-
-void UnitsSettings::setLength(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("length", value);
- prefs.units.length = (units::LENGHT) value;
- emit lengthChanged(value);
-}
-
-void UnitsSettings::setPressure(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("pressure", value);
- prefs.units.pressure = (units::PRESSURE) value;
- emit pressureChanged(value);
-}
-
-void UnitsSettings::setVolume(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("volume", value);
- prefs.units.volume = (units::VOLUME) value;
- emit volumeChanged(value);
-}
-
-void UnitsSettings::setTemperature(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("temperature", value);
- prefs.units.temperature = (units::TEMPERATURE) value;
- emit temperatureChanged(value);
-}
-
-void UnitsSettings::setWeight(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("weight", value);
- prefs.units.weight = (units::WEIGHT) value;
- emit weightChanged(value);
-}
-
-void UnitsSettings::setVerticalSpeedTime(int value)
-{
- QSettings s;
- s.beginGroup(group);
- s.setValue("vertical_speed_time", value);
- prefs.units.vertical_speed_time = (units::TIME) value;
- emit verticalSpeedTimeChanged(value);
-}
-
-void UnitsSettings::setCoordinatesTraditional(bool value)
-{
- QSettings s;
- s.setValue("coordinates", value);
- prefs.coordinates_traditional = value;
- emit coordinatesTraditionalChanged(value);
-}
-
-void UnitsSettings::setUnitSystem(const QString& value)
-{
- QSettings s;
- s.setValue("unit_system", value);
-
- if (value == QStringLiteral("metric")) {
- prefs.unit_system = METRIC;
- prefs.units = SI_units;
- } else if (value == QStringLiteral("imperial")) {
- prefs.unit_system = IMPERIAL;
- prefs.units = IMPERIAL_units;
- } else {
- prefs.unit_system = PERSONALIZE;
- }
-
- emit unitSystemChanged(value);
- // TODO: emit the other values here?
-}
-
-GeneralSettingsObjectWrapper::GeneralSettingsObjectWrapper(QObject *parent) :
- QObject(parent),
- group(QStringLiteral("GeneralSettings"))
-{
-}
-
-QString GeneralSettingsObjectWrapper::defaultFilename() const
-{
- return prefs.default_filename;
-}
-
-QString GeneralSettingsObjectWrapper::defaultCylinder() const
-{
- return prefs.default_cylinder;
-}
-
-short GeneralSettingsObjectWrapper::defaultFileBehavior() const
-{
- return prefs.default_file_behavior;
-}
-
-bool GeneralSettingsObjectWrapper::useDefaultFile() const
-{
- return prefs.use_default_file;
-}
-
-int GeneralSettingsObjectWrapper::defaultSetPoint() const
-{
- return prefs.defaultsetpoint;
-}
-
-int GeneralSettingsObjectWrapper::o2Consumption() const
-{
- return prefs.o2consumption;
-}
-
-int GeneralSettingsObjectWrapper::pscrRatio() const
-{
- return prefs.pscr_ratio;
-}
-
-void GeneralSettingsObjectWrapper::setDefaultFilename(const QString& value)
-{
- QSettings s;
- s.setValue("default_filename", value);
- prefs.default_filename = copy_string(qPrintable(value));
- emit defaultFilenameChanged(value);
-}
-
-void GeneralSettingsObjectWrapper::setDefaultCylinder(const QString& value)
-{
- QSettings s;
- s.setValue("default_cylinder", value);
- prefs.default_cylinder = copy_string(qPrintable(value));
- emit defaultCylinderChanged(value);
-}
-
-void GeneralSettingsObjectWrapper::setDefaultFileBehavior(short value)
-{
- QSettings s;
- s.setValue("default_file_behavior", value);
- prefs.default_file_behavior = value;
- if (prefs.default_file_behavior == UNDEFINED_DEFAULT_FILE) {
- // undefined, so check if there's a filename set and
- // use that, otherwise go with no default file
- if (QString(prefs.default_filename).isEmpty())
- prefs.default_file_behavior = NO_DEFAULT_FILE;
- else
- prefs.default_file_behavior = LOCAL_DEFAULT_FILE;
- }
- emit defaultFileBehaviorChanged(value);
-}
-
-void GeneralSettingsObjectWrapper::setUseDefaultFile(bool value)
-{
- QSettings s;
- s.setValue("use_default_file", value);
- prefs.use_default_file = value;
- emit useDefaultFileChanged(value);
-}
-
-void GeneralSettingsObjectWrapper::setDefaultSetPoint(int value)
-{
- QSettings s;
- s.setValue("defaultsetpoint", value);
- prefs.defaultsetpoint = value;
- emit defaultSetPointChanged(value);
-}
-
-void GeneralSettingsObjectWrapper::setO2Consumption(int value)
-{
- QSettings s;
- s.setValue("o2consumption", value);
- prefs.o2consumption = value;
- emit o2ConsumptionChanged(value);
-}
-
-void GeneralSettingsObjectWrapper::setPscrRatio(int value)
-{
- QSettings s;
- s.setValue("pscr_ratio", value);
- prefs.pscr_ratio = value;
- emit pscrRatioChanged(value);
-}
-
-DisplaySettingsObjectWrapper::DisplaySettingsObjectWrapper(QObject *parent) :
- QObject(parent),
- group(QStringLiteral("Display"))
-{
-}
-
-QString DisplaySettingsObjectWrapper::divelistFont() const
-{
- return prefs.divelist_font;
-}
-
-double DisplaySettingsObjectWrapper::fontSize() const
-{
- return prefs.font_size;
-}
-
-short DisplaySettingsObjectWrapper::displayInvalidDives() const
-{
- return prefs.display_invalid_dives;
-}
-
-void DisplaySettingsObjectWrapper::setDivelistFont(const QString& value)
-{
- QSettings s;
- s.setValue("divelist_font", value);
- QString newValue = value;
- if (value.contains(","))
- newValue = value.left(value.indexOf(","));
-
- if (!subsurface_ignore_font(newValue.toUtf8().constData())) {
- free((void *)prefs.divelist_font);
- prefs.divelist_font = strdup(newValue.toUtf8().constData());
- qApp->setFont(QFont(newValue));
- }
- emit divelistFontChanged(newValue);
-}
-
-void DisplaySettingsObjectWrapper::setFontSize(double value)
-{
- QSettings s;
- s.setValue("font_size", value);
- prefs.font_size = value;
- QFont defaultFont = qApp->font();
- defaultFont.setPointSizeF(prefs.font_size);
- qApp->setFont(defaultFont);
- emit fontSizeChanged(value);
-}
-
-void DisplaySettingsObjectWrapper::setDisplayInvalidDives(short value)
-{
- QSettings s;
- s.setValue("displayinvalid", value);
- prefs.display_invalid_dives = value;
- emit displayInvalidDivesChanged(value);
-}
-
-LanguageSettingsObjectWrapper::LanguageSettingsObjectWrapper(QObject *parent) :
- QObject(parent),
- group("Language")
-{
-}
-
-QString LanguageSettingsObjectWrapper::language() const
-{
- return prefs.locale.language;
-}
-
-QString LanguageSettingsObjectWrapper::timeFormat() const
-{
- return prefs.time_format;
-}
-
-QString LanguageSettingsObjectWrapper::dateFormat() const
-{
- return prefs.date_format;
-}
-
-QString LanguageSettingsObjectWrapper::dateFormatShort() const
-{
- return prefs.date_format_short;
-}
-
-bool LanguageSettingsObjectWrapper::timeFormatOverride() const
-{
- return prefs.time_format_override;
-}
-
-bool LanguageSettingsObjectWrapper::dateFormatOverride() const
-{
- return prefs.date_format_override;
-}
-
-bool LanguageSettingsObjectWrapper::useSystemLanguage() const
-{
- return prefs.locale.use_system_language;
-}
-
-void LanguageSettingsObjectWrapper::setUseSystemLanguage(bool value)
-{
- QSettings s;
- s.setValue("UseSystemLanguage", value);
- prefs.locale.use_system_language = copy_string(qPrintable(value));
- emit useSystemLanguageChanged(value);
-}
-
-void LanguageSettingsObjectWrapper::setLanguage(const QString& value)
-{
- QSettings s;
- s.setValue("UiLanguage", value);
- prefs.locale.language = copy_string(qPrintable(value));
- emit languageChanged(value);
-}
-
-void LanguageSettingsObjectWrapper::setTimeFormat(const QString& value)
-{
- QSettings s;
- s.setValue("time_format", value);
- prefs.time_format = copy_string(qPrintable(value));;
- emit timeFormatChanged(value);
-}
-
-void LanguageSettingsObjectWrapper::setDateFormat(const QString& value)
-{
- QSettings s;
- s.setValue("date_format", value);
- prefs.date_format = copy_string(qPrintable(value));;
- emit dateFormatChanged(value);
-}
-
-void LanguageSettingsObjectWrapper::setDateFormatShort(const QString& value)
-{
- QSettings s;
- s.setValue("date_format_short", value);
- prefs.date_format_short = copy_string(qPrintable(value));;
- emit dateFormatShortChanged(value);
-}
-
-void LanguageSettingsObjectWrapper::setTimeFormatOverride(bool value)
-{
- QSettings s;
- s.setValue("time_format_override", value);
- prefs.time_format_override = value;
- emit timeFormatOverrideChanged(value);
-}
-
-void LanguageSettingsObjectWrapper::setDateFormatOverride(bool value)
-{
- QSettings s;
- s.setValue("date_format_override", value);
- prefs.date_format_override = value;
- emit dateFormatOverrideChanged(value);
-}
-
-AnimationsSettingsObjectWrapper::AnimationsSettingsObjectWrapper(QObject* parent):
- QObject(parent),
- group("Animations")
-
-{
-}
-
-int AnimationsSettingsObjectWrapper::animationSpeed() const
-{
- return prefs.animation_speed;
-}
-
-void AnimationsSettingsObjectWrapper::setAnimationSpeed(int value)
-{
- QSettings s;
- s.setValue("animation_speed", value);
- prefs.animation_speed = value;
- emit animationSpeedChanged(value);
-}
-
-LocationServiceSettingsObjectWrapper::LocationServiceSettingsObjectWrapper(QObject* parent):
- QObject(parent),
- group("locationService")
-{
-}
-
-int LocationServiceSettingsObjectWrapper::distanceThreshold() const
-{
- return prefs.distance_threshold;
-}
-
-int LocationServiceSettingsObjectWrapper::timeThreshold() const
-{
- return prefs.time_threshold;
-}
-
-void LocationServiceSettingsObjectWrapper::setDistanceThreshold(int value)
-{
- QSettings s;
- s.setValue("distance_threshold", value);
- prefs.distance_threshold = value;
- emit distanceThresholdChanged(value);
-}
-
-void LocationServiceSettingsObjectWrapper::setTimeThreshold(int value)
-{
- QSettings s;
- s.setValue("time_threshold", value);
- prefs.time_threshold = value;
- emit timeThresholdChanged( value);
-}
-
-SettingsObjectWrapper::SettingsObjectWrapper(QObject* parent):
-QObject(parent),
- techDetails(new TechnicalDetailsSettings(this)),
- pp_gas(new PartialPressureGasSettings(this)),
- facebook(new FacebookSettings(this)),
- geocoding(new GeocodingPreferences(this)),
- proxy(new ProxySettings(this)),
- cloud_storage(new CloudStorageSettings(this)),
- planner_settings(new DivePlannerSettings(this)),
- unit_settings(new UnitsSettings(this)),
- general_settings(new GeneralSettingsObjectWrapper(this)),
- display_settings(new DisplaySettingsObjectWrapper(this)),
- language_settings(new LanguageSettingsObjectWrapper(this)),
- animation_settings(new AnimationsSettingsObjectWrapper(this)),
- location_settings(new LocationServiceSettingsObjectWrapper(this))
-{
-}
-
-void SettingsObjectWrapper::setSaveUserIdLocal(short int value)
-{
- Q_UNUSED(value);
- //TODO: Find where this is stored on the preferences.
-}
-
-short int SettingsObjectWrapper::saveUserIdLocal() const
-{
- return prefs.save_userid_local;
-}
-
-SettingsObjectWrapper* SettingsObjectWrapper::instance()
-{
- static SettingsObjectWrapper settings;
- return &settings;
-}
diff --git a/subsurface-core/subsurface-qt/SettingsObjectWrapper.h b/subsurface-core/subsurface-qt/SettingsObjectWrapper.h
deleted file mode 100644
index f115e2d86..000000000
--- a/subsurface-core/subsurface-qt/SettingsObjectWrapper.h
+++ /dev/null
@@ -1,642 +0,0 @@
-#ifndef SETTINGSOBJECTWRAPPER_H
-#define SETTINGSOBJECTWRAPPER_H
-
-#include <QObject>
-
-#include "../pref.h"
-#include "../prefs-macros.h"
-
-/* Wrapper class for the Settings. This will allow
- * seamlessy integration of the settings with the QML
- * and QWidget frontends. This class will be huge, since
- * I need tons of properties, one for each option. */
-
-/* Control the state of the Partial Pressure Graphs preferences */
-class PartialPressureGasSettings : public QObject {
- Q_OBJECT
- Q_PROPERTY(short show_po2 READ showPo2 WRITE setShowPo2 NOTIFY showPo2Changed)
- Q_PROPERTY(short show_pn2 READ showPn2 WRITE setShowPn2 NOTIFY showPn2Changed)
- Q_PROPERTY(short show_phe READ showPhe WRITE setShowPhe NOTIFY showPheChanged)
- Q_PROPERTY(double po2_threshold READ po2Threshold WRITE setPo2Threshold NOTIFY po2ThresholdChanged)
- Q_PROPERTY(double pn2_threshold READ pn2Threshold WRITE setPn2Threshold NOTIFY pn2ThresholdChanged)
- Q_PROPERTY(double phe_threshold READ pheThreshold WRITE setPheThreshold NOTIFY pheThresholdChanged)
-
-public:
- PartialPressureGasSettings(QObject *parent);
- short showPo2() const;
- short showPn2() const;
- short showPhe() const;
- double po2Threshold() const;
- double pn2Threshold() const;
- double pheThreshold() const;
-
-public slots:
- void setShowPo2(short value);
- void setShowPn2(short value);
- void setShowPhe(short value);
- void setPo2Threshold(double value);
- void setPn2Threshold(double value);
- void setPheThreshold(double value);
-
-signals:
- void showPo2Changed(short value);
- void showPn2Changed(short value);
- void showPheChanged(short value);
- void po2ThresholdChanged(double value);
- void pn2ThresholdChanged(double value);
- void pheThresholdChanged(double value);
-private:
- QString group;
-};
-
-class TechnicalDetailsSettings : public QObject {
- Q_OBJECT
- Q_PROPERTY(double modpO2 READ modp02 WRITE setModp02 NOTIFY modpO2Changed)
- Q_PROPERTY(bool ead READ ead WRITE setEad NOTIFY eadChanged)
- Q_PROPERTY(bool mod READ mod WRITE setMod NOTIFY modChanged);
- Q_PROPERTY(bool dcceiling READ dcceiling WRITE setDCceiling NOTIFY dcceilingChanged)
- Q_PROPERTY(bool redceiling READ redceiling WRITE setRedceiling NOTIFY redceilingChanged)
- Q_PROPERTY(bool calcceiling READ calcceiling WRITE setCalcceiling NOTIFY calcceilingChanged)
- Q_PROPERTY(bool calcceiling3m READ calcceiling3m WRITE setCalcceiling3m NOTIFY calcceiling3mChanged)
- Q_PROPERTY(bool calcalltissues READ calcalltissues WRITE setCalcalltissues NOTIFY calcalltissuesChanged)
- Q_PROPERTY(bool calcndltts READ calcndltts WRITE setCalcndltts NOTIFY calcndlttsChanged)
- Q_PROPERTY(bool gflow READ gflow WRITE setGflow NOTIFY gflowChanged)
- Q_PROPERTY(bool gfhigh READ gfhigh WRITE setGfhigh NOTIFY gfhighChanged)
- Q_PROPERTY(bool hrgraph READ hrgraph WRITE setHRgraph NOTIFY hrgraphChanged)
- Q_PROPERTY(bool tankbar READ tankBar WRITE setTankBar NOTIFY tankBarChanged)
- Q_PROPERTY(bool percentagegraph READ percentageGraph WRITE setPercentageGraph NOTIFY percentageGraphChanged)
- Q_PROPERTY(bool rulergraph READ rulerGraph WRITE setRulerGraph NOTIFY rulerGraphChanged)
- Q_PROPERTY(bool show_ccr_setpoint READ showCCRSetpoint WRITE setShowCCRSetpoint NOTIFY showCCRSetpointChanged)
- Q_PROPERTY(bool show_ccr_sensors READ showCCRSensors WRITE setShowCCRSensors NOTIFY showCCRSensorsChanged)
- Q_PROPERTY(bool zoomed_plot READ zoomedPlot WRITE setZoomedPlot NOTIFY zoomedPlotChanged)
- Q_PROPERTY(bool show_sac READ showSac WRITE setShowSac NOTIFY showSacChanged)
- Q_PROPERTY(bool gf_low_at_maxdepth READ gfLowAtMaxDepth WRITE setGfLowAtMaxDepth NOTIFY gfLowAtMaxDepthChanged)
- Q_PROPERTY(bool display_unused_tanks READ displayUnusedTanks WRITE setDisplayUnusedTanks NOTIFY displayUnusedTanksChanged)
- Q_PROPERTY(bool show_average_depth READ showAverageDepth WRITE setShowAverageDepth NOTIFY showAverageDepthChanged)
- Q_PROPERTY(bool show_pictures_in_profile READ showPicturesInProfile WRITE setShowPicturesInProfile NOTIFY showPicturesInProfileChanged)
-public:
- TechnicalDetailsSettings(QObject *parent);
-
- double modp02() const;
- bool ead() const;
- bool mod() const;
- bool dcceiling() const;
- bool redceiling() const;
- bool calcceiling() const;
- bool calcceiling3m() const;
- bool calcalltissues() const;
- bool calcndltts() const;
- bool gflow() const;
- bool gfhigh() const;
- bool hrgraph() const;
- bool tankBar() const;
- bool percentageGraph() const;
- bool rulerGraph() const;
- bool showCCRSetpoint() const;
- bool showCCRSensors() const;
- bool zoomedPlot() const;
- bool showSac() const;
- bool gfLowAtMaxDepth() const;
- bool displayUnusedTanks() const;
- bool showAverageDepth() const;
- bool showPicturesInProfile() const;
-
-public slots:
- void setMod(bool value);
- void setModp02(double value);
- void setEad(bool value);
- void setDCceiling(bool value);
- void setRedceiling(bool value);
- void setCalcceiling(bool value);
- void setCalcceiling3m(bool value);
- void setCalcalltissues(bool value);
- void setCalcndltts(bool value);
- void setGflow(bool value);
- void setGfhigh(bool value);
- void setHRgraph(bool value);
- void setTankBar(bool value);
- void setPercentageGraph(bool value);
- void setRulerGraph(bool value);
- void setShowCCRSetpoint(bool value);
- void setShowCCRSensors(bool value);
- void setZoomedPlot(bool value);
- void setShowSac(bool value);
- void setGfLowAtMaxDepth(bool value);
- void setDisplayUnusedTanks(bool value);
- void setShowAverageDepth(bool value);
- void setShowPicturesInProfile(bool value);
-
-signals:
- void modpO2Changed(double value);
- void eadChanged(bool value);
- void modChanged(bool value);
- void dcceilingChanged(bool value);
- void redceilingChanged(bool value);
- void calcceilingChanged(bool value);
- void calcceiling3mChanged(bool value);
- void calcalltissuesChanged(bool value);
- void calcndlttsChanged(bool value);
- void gflowChanged(bool value);
- void gfhighChanged(bool value);
- void hrgraphChanged(bool value);
- void tankBarChanged(bool value);
- void percentageGraphChanged(bool value);
- void rulerGraphChanged(bool value);
- void showCCRSetpointChanged(bool value);
- void showCCRSensorsChanged(bool value);
- void zoomedPlotChanged(bool value);
- void showSacChanged(bool value);
- void gfLowAtMaxDepthChanged(bool value);
- void displayUnusedTanksChanged(bool value);
- void showAverageDepthChanged(bool value);
- void showPicturesInProfileChanged(bool value);
-};
-
-/* Control the state of the Facebook preferences */
-class FacebookSettings : public QObject {
- Q_OBJECT
- Q_PROPERTY(QString accessToken READ accessToken WRITE setAccessToken NOTIFY accessTokenChanged)
- Q_PROPERTY(QString userId READ userId WRITE setUserId NOTIFY userIdChanged)
- Q_PROPERTY(QString albumId READ albumId WRITE setAlbumId NOTIFY albumIdChanged)
-
-public:
- FacebookSettings(QObject *parent);
- QString accessToken() const;
- QString userId() const;
- QString albumId() const;
-
-public slots:
- void setAccessToken (const QString& value);
- void setUserId(const QString& value);
- void setAlbumId(const QString& value);
-
-signals:
- void accessTokenChanged(const QString& value);
- void userIdChanged(const QString& value);
- void albumIdChanged(const QString& value);
-private:
- QString group;
- QString subgroup;
-};
-
-/* Control the state of the Geocoding preferences */
-class GeocodingPreferences : public QObject {
- Q_OBJECT
- Q_PROPERTY(bool enable_geocoding READ enableGeocoding WRITE setEnableGeocoding NOTIFY enableGeocodingChanged)
- Q_PROPERTY(bool parse_dive_without_gps READ parseDiveWithoutGps WRITE setParseDiveWithoutGps NOTIFY parseDiveWithoutGpsChanged)
- Q_PROPERTY(bool tag_existing_dives READ tagExistingDives WRITE setTagExistingDives NOTIFY tagExistingDivesChanged)
- Q_PROPERTY(taxonomy_category first_category READ firstTaxonomyCategory WRITE setFirstTaxonomyCategory NOTIFY firstTaxonomyCategoryChanged)
- Q_PROPERTY(taxonomy_category second_category READ secondTaxonomyCategory WRITE setSecondTaxonomyCategory NOTIFY secondTaxonomyCategoryChanged)
- Q_PROPERTY(taxonomy_category third_category READ thirdTaxonomyCategory WRITE setThirdTaxonomyCategory NOTIFY thirdTaxonomyCategoryChanged)
-public:
- GeocodingPreferences(QObject *parent);
- bool enableGeocoding() const;
- bool parseDiveWithoutGps() const;
- bool tagExistingDives() const;
- taxonomy_category firstTaxonomyCategory() const;
- taxonomy_category secondTaxonomyCategory() const;
- taxonomy_category thirdTaxonomyCategory() const;
-
-public slots:
- void setEnableGeocoding(bool value);
- void setParseDiveWithoutGps(bool value);
- void setTagExistingDives(bool value);
- void setFirstTaxonomyCategory(taxonomy_category value);
- void setSecondTaxonomyCategory(taxonomy_category value);
- void setThirdTaxonomyCategory(taxonomy_category value);
-
-signals:
- void enableGeocodingChanged(bool value);
- void parseDiveWithoutGpsChanged(bool value);
- void tagExistingDivesChanged(bool value);
- void firstTaxonomyCategoryChanged(taxonomy_category value);
- void secondTaxonomyCategoryChanged(taxonomy_category value);
- void thirdTaxonomyCategoryChanged(taxonomy_category value);
-private:
- QString group;
-};
-
-class ProxySettings : public QObject {
- Q_OBJECT
- Q_PROPERTY(int type READ type WRITE setType NOTIFY typeChanged)
- Q_PROPERTY(QString host READ host WRITE setHost NOTIFY hostChanged)
- Q_PROPERTY(int port READ port WRITE setPort NOTIFY portChanged)
- Q_PROPERTY(short auth READ auth WRITE setAuth NOTIFY authChanged)
- Q_PROPERTY(QString user READ user WRITE setUser NOTIFY userChanged)
- Q_PROPERTY(QString pass READ pass WRITE setPass NOTIFY passChanged)
-
-public:
- ProxySettings(QObject *parent);
- int type() const;
- QString host() const;
- int port() const;
- short auth() const;
- QString user() const;
- QString pass() const;
-
-public slots:
- void setType(int value);
- void setHost(const QString& value);
- void setPort(int value);
- void setAuth(short value);
- void setUser(const QString& value);
- void setPass(const QString& value);
-
-signals:
- void typeChanged(int value);
- void hostChanged(const QString& value);
- void portChanged(int value);
- void authChanged(short value);
- void userChanged(const QString& value);
- void passChanged(const QString& value);
-private:
- QString group;
-};
-
-class CloudStorageSettings : public QObject {
- Q_OBJECT
- Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)
- Q_PROPERTY(QString newpassword READ newPassword WRITE setNewPassword NOTIFY newPasswordChanged)
- Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY emailChanged)
- Q_PROPERTY(QString email_encoded READ emailEncoded WRITE setEmailEncoded NOTIFY emailEncodedChanged)
- Q_PROPERTY(QString userid READ userId WRITE setUserId NOTIFY userIdChanged)
- Q_PROPERTY(QString base_url READ baseUrl WRITE setBaseUrl NOTIFY baseUrlChanged)
- Q_PROPERTY(QString git_url READ gitUrl WRITE setGitUrl NOTIFY gitUrlChanged)
- Q_PROPERTY(bool git_local_only READ gitLocalOnly WRITE setGitLocalOnly NOTIFY gitLocalOnlyChanged)
- Q_PROPERTY(bool save_password_local READ savePasswordLocal WRITE setSavePasswordLocal NOTIFY savePasswordLocalChanged)
- Q_PROPERTY(short verification_status READ verificationStatus WRITE setVerificationStatus NOTIFY verificationStatusChanged)
- Q_PROPERTY(bool background_sync READ backgroundSync WRITE setBackgroundSync NOTIFY backgroundSyncChanged)
-public:
- CloudStorageSettings(QObject *parent);
- QString password() const;
- QString newPassword() const;
- QString email() const;
- QString emailEncoded() const;
- QString userId() const;
- QString baseUrl() const;
- QString gitUrl() const;
- bool savePasswordLocal() const;
- short verificationStatus() const;
- bool backgroundSync() const;
- bool gitLocalOnly() const;
-
-public slots:
- void setPassword(const QString& value);
- void setNewPassword(const QString& value);
- void setEmail(const QString& value);
- void setEmailEncoded(const QString& value);
- void setUserId(const QString& value);
- void setBaseUrl(const QString& value);
- void setGitUrl(const QString& value);
- void setSavePasswordLocal(bool value);
- void setVerificationStatus(short value);
- void setBackgroundSync(bool value);
- void setGitLocalOnly(bool value);
-
-signals:
- void passwordChanged(const QString& value);
- void newPasswordChanged(const QString& value);
- void emailChanged(const QString& value);
- void emailEncodedChanged(const QString& value);
- void userIdChanged(const QString& value);
- void baseUrlChanged(const QString& value);
- void gitUrlChanged(const QString& value);
- void savePasswordLocalChanged(bool value);
- void verificationStatusChanged(short value);
- void backgroundSyncChanged(bool value);
- void gitLocalOnlyChanged(bool value);
-private:
- QString group;
-};
-
-class DivePlannerSettings : public QObject {
- Q_OBJECT
- Q_PROPERTY(bool last_stop READ lastStop WRITE setLastStop NOTIFY lastStopChanged)
- Q_PROPERTY(bool verbatim_plan READ verbatimPlan WRITE setVerbatimPlan NOTIFY verbatimPlanChanged)
- Q_PROPERTY(bool display_runtime READ displayRuntime WRITE setDisplayRuntime NOTIFY displayRuntimeChanged)
- Q_PROPERTY(bool display_duration READ displayDuration WRITE setDisplayDuration NOTIFY displayDurationChanged)
- Q_PROPERTY(bool display_transitions READ displayTransitions WRITE setDisplayTransitions NOTIFY displayTransitionsChanged)
- Q_PROPERTY(bool doo2breaks READ doo2breaks WRITE setDoo2breaks NOTIFY doo2breaksChanged)
- Q_PROPERTY(bool drop_stone_mode READ dropStoneMode WRITE setDropStoneMode NOTIFY dropStoneModeChanged)
- Q_PROPERTY(bool safetystop READ safetyStop WRITE setSafetyStop NOTIFY safetyStopChanged)
- Q_PROPERTY(bool switch_at_req_stop READ switchAtRequiredStop WRITE setSwitchAtRequiredStop NOTIFY switchAtRequiredStopChanged)
- Q_PROPERTY(int ascrate75 READ ascrate75 WRITE setAscrate75 NOTIFY ascrate75Changed)
- Q_PROPERTY(int ascrate50 READ ascrate50 WRITE setAscrate50 NOTIFY ascrate50Changed)
- Q_PROPERTY(int ascratestops READ ascratestops WRITE setAscratestops NOTIFY ascratestopsChanged)
- Q_PROPERTY(int ascratelast6m READ ascratelast6m WRITE setAscratelast6m NOTIFY ascratelast6mChanged)
- Q_PROPERTY(int descrate READ descrate WRITE setDescrate NOTIFY descrateChanged)
- Q_PROPERTY(int bottompo2 READ bottompo2 WRITE setBottompo2 NOTIFY bottompo2Changed)
- Q_PROPERTY(int decopo2 READ decopo2 WRITE setDecopo2 NOTIFY decopo2Changed)
- Q_PROPERTY(int reserve_gas READ reserveGas WRITE setReserveGas NOTIFY reserveGasChanged)
- Q_PROPERTY(int min_switch_duration READ minSwitchDuration WRITE setMinSwitchDuration NOTIFY minSwitchDurationChanged)
- Q_PROPERTY(int bottomsac READ bottomSac WRITE setBottomSac NOTIFY bottomSacChanged)
- Q_PROPERTY(int decosac READ decoSac WRITE setSecoSac NOTIFY decoSacChanged)
- Q_PROPERTY(short conservatism_level READ conservatismLevel WRITE setConservatismLevel NOTIFY conservatismLevelChanged)
- Q_PROPERTY(deco_mode decoMode READ decoMode WRITE setDecoMode NOTIFY decoModeChanged)
-
-public:
- DivePlannerSettings(QObject *parent = 0);
- bool lastStop() const;
- bool verbatimPlan() const;
- bool displayRuntime() const;
- bool displayDuration() const;
- bool displayTransitions() const;
- bool doo2breaks() const;
- bool dropStoneMode() const;
- bool safetyStop() const;
- bool switchAtRequiredStop() const;
- int ascrate75() const;
- int ascrate50() const;
- int ascratestops() const;
- int ascratelast6m() const;
- int descrate() const;
- int bottompo2() const;
- int decopo2() const;
- int reserveGas() const;
- int minSwitchDuration() const;
- int bottomSac() const;
- int decoSac() const;
- short conservatismLevel() const;
- deco_mode decoMode() const;
-
-public slots:
- void setLastStop(bool value);
- void setVerbatimPlan(bool value);
- void setDisplayRuntime(bool value);
- void setDisplayDuration(bool value);
- void setDisplayTransitions(bool value);
- void setDoo2breaks(bool value);
- void setDropStoneMode(bool value);
- void setSafetyStop(bool value);
- void setSwitchAtRequiredStop(bool value);
- void setAscrate75(int value);
- void setAscrate50(int value);
- void setAscratestops(int value);
- void setAscratelast6m(int value);
- void setDescrate(int value);
- void setBottompo2(int value);
- void setDecopo2(int value);
- void setReserveGas(int value);
- void setMinSwitchDuration(int value);
- void setBottomSac(int value);
- void setSecoSac(int value);
- void setConservatismLevel(int value);
- void setDecoMode(deco_mode value);
-
-signals:
- void lastStopChanged(bool value);
- void verbatimPlanChanged(bool value);
- void displayRuntimeChanged(bool value);
- void displayDurationChanged(bool value);
- void displayTransitionsChanged(bool value);
- void doo2breaksChanged(bool value);
- void dropStoneModeChanged(bool value);
- void safetyStopChanged(bool value);
- void switchAtRequiredStopChanged(bool value);
- void ascrate75Changed(int value);
- void ascrate50Changed(int value);
- void ascratestopsChanged(int value);
- void ascratelast6mChanged(int value);
- void descrateChanged(int value);
- void bottompo2Changed(int value);
- void decopo2Changed(int value);
- void reserveGasChanged(int value);
- void minSwitchDurationChanged(int value);
- void bottomSacChanged(int value);
- void decoSacChanged(int value);
- void conservatismLevelChanged(int value);
- void decoModeChanged(deco_mode value);
-
-private:
- QString group;
-};
-
-class UnitsSettings : public QObject {
- Q_OBJECT
- Q_PROPERTY(int length READ length WRITE setLength NOTIFY lengthChanged)
- Q_PROPERTY(int pressure READ pressure WRITE setPressure NOTIFY pressureChanged)
- Q_PROPERTY(int volume READ volume WRITE setVolume NOTIFY volumeChanged)
- Q_PROPERTY(int temperature READ temperature WRITE setTemperature NOTIFY temperatureChanged)
- Q_PROPERTY(int weight READ weight WRITE setWeight NOTIFY weightChanged)
- Q_PROPERTY(QString unit_system READ unitSystem WRITE setUnitSystem NOTIFY unitSystemChanged)
- Q_PROPERTY(bool coordinates_traditional READ coordinatesTraditional WRITE setCoordinatesTraditional NOTIFY coordinatesTraditionalChanged)
- Q_PROPERTY(int vertical_speed_time READ verticalSpeedTime WRITE setVerticalSpeedTime NOTIFY verticalSpeedTimeChanged)
-
-public:
- UnitsSettings(QObject *parent = 0);
- int length() const;
- int pressure() const;
- int volume() const;
- int temperature() const;
- int weight() const;
- int verticalSpeedTime() const;
- QString unitSystem() const;
- bool coordinatesTraditional() const;
-
-public slots:
- void setLength(int value);
- void setPressure(int value);
- void setVolume(int value);
- void setTemperature(int value);
- void setWeight(int value);
- void setVerticalSpeedTime(int value);
- void setUnitSystem(const QString& value);
- void setCoordinatesTraditional(bool value);
-
-signals:
- void lengthChanged(int value);
- void pressureChanged(int value);
- void volumeChanged(int value);
- void temperatureChanged(int value);
- void weightChanged(int value);
- void verticalSpeedTimeChanged(int value);
- void unitSystemChanged(const QString& value);
- void coordinatesTraditionalChanged(bool value);
-private:
- QString group;
-};
-
-class GeneralSettingsObjectWrapper : public QObject {
- Q_OBJECT
- Q_PROPERTY(QString default_filename READ defaultFilename WRITE setDefaultFilename NOTIFY defaultFilenameChanged)
- Q_PROPERTY(QString default_cylinder READ defaultCylinder WRITE setDefaultCylinder NOTIFY defaultCylinderChanged)
- Q_PROPERTY(short default_file_behavior READ defaultFileBehavior WRITE setDefaultFileBehavior NOTIFY defaultFileBehaviorChanged)
- Q_PROPERTY(bool use_default_file READ useDefaultFile WRITE setUseDefaultFile NOTIFY useDefaultFileChanged)
- Q_PROPERTY(int defaultsetpoint READ defaultSetPoint WRITE setDefaultSetPoint NOTIFY defaultSetPointChanged)
- Q_PROPERTY(int o2consumption READ o2Consumption WRITE setO2Consumption NOTIFY o2ConsumptionChanged)
- Q_PROPERTY(int pscr_ratio READ pscrRatio WRITE setPscrRatio NOTIFY pscrRatioChanged)
-
-public:
- GeneralSettingsObjectWrapper(QObject *parent);
- QString defaultFilename() const;
- QString defaultCylinder() const;
- short defaultFileBehavior() const;
- bool useDefaultFile() const;
- int defaultSetPoint() const;
- int o2Consumption() const;
- int pscrRatio() const;
-
-public slots:
- void setDefaultFilename (const QString& value);
- void setDefaultCylinder (const QString& value);
- void setDefaultFileBehavior (short value);
- void setUseDefaultFile (bool value);
- void setDefaultSetPoint (int value);
- void setO2Consumption (int value);
- void setPscrRatio (int value);
-
-signals:
- void defaultFilenameChanged(const QString& value);
- void defaultCylinderChanged(const QString& value);
- void defaultFileBehaviorChanged(short value);
- void useDefaultFileChanged(bool value);
- void defaultSetPointChanged(int value);
- void o2ConsumptionChanged(int value);
- void pscrRatioChanged(int value);
-private:
- QString group;
-};
-
-class DisplaySettingsObjectWrapper : public QObject {
- Q_OBJECT
- Q_PROPERTY(QString divelist_font READ divelistFont WRITE setDivelistFont NOTIFY divelistFontChanged)
- Q_PROPERTY(double font_size READ fontSize WRITE setFontSize NOTIFY fontSizeChanged)
- Q_PROPERTY(short display_invalid_dives READ displayInvalidDives WRITE setDisplayInvalidDives NOTIFY displayInvalidDivesChanged)
-public:
- DisplaySettingsObjectWrapper(QObject *parent);
- QString divelistFont() const;
- double fontSize() const;
- short displayInvalidDives() const;
-public slots:
- void setDivelistFont(const QString& value);
- void setFontSize(double value);
- void setDisplayInvalidDives(short value);
-signals:
- void divelistFontChanged(const QString& value);
- void fontSizeChanged(double value);
- void displayInvalidDivesChanged(short value);
-private:
- QString group;
-};
-
-class LanguageSettingsObjectWrapper : public QObject {
- Q_OBJECT
- Q_PROPERTY(QString language READ language WRITE setLanguage NOTIFY languageChanged)
- Q_PROPERTY(QString time_format READ timeFormat WRITE setTimeFormat NOTIFY timeFormatChanged)
- Q_PROPERTY(QString date_format READ dateFormat WRITE setDateFormat NOTIFY dateFormatChanged)
- Q_PROPERTY(QString date_format_short READ dateFormatShort WRITE setDateFormatShort NOTIFY dateFormatShortChanged)
- Q_PROPERTY(bool time_format_override READ timeFormatOverride WRITE setTimeFormatOverride NOTIFY timeFormatOverrideChanged)
- Q_PROPERTY(bool date_format_override READ dateFormatOverride WRITE setDateFormatOverride NOTIFY dateFormatOverrideChanged)
- Q_PROPERTY(bool use_system_language READ useSystemLanguage WRITE setUseSystemLanguage NOTIFY useSystemLanguageChanged)
-
-public:
- LanguageSettingsObjectWrapper(QObject *parent);
- QString language() const;
- QString timeFormat() const;
- QString dateFormat() const;
- QString dateFormatShort() const;
- bool timeFormatOverride() const;
- bool dateFormatOverride() const;
- bool useSystemLanguage() const;
-
-public slots:
- void setLanguage (const QString& value);
- void setTimeFormat (const QString& value);
- void setDateFormat (const QString& value);
- void setDateFormatShort (const QString& value);
- void setTimeFormatOverride (bool value);
- void setDateFormatOverride (bool value);
- void setUseSystemLanguage (bool value);
-signals:
- void languageChanged(const QString& value);
- void timeFormatChanged(const QString& value);
- void dateFormatChanged(const QString& value);
- void dateFormatShortChanged(const QString& value);
- void timeFormatOverrideChanged(bool value);
- void dateFormatOverrideChanged(bool value);
- void useSystemLanguageChanged(bool value);
-
-private:
- QString group;
-};
-
-class AnimationsSettingsObjectWrapper : public QObject {
- Q_OBJECT
- Q_PROPERTY(int animation_speed READ animationSpeed WRITE setAnimationSpeed NOTIFY animationSpeedChanged)
-public:
- AnimationsSettingsObjectWrapper(QObject *parent);
- int animationSpeed() const;
-
-public slots:
- void setAnimationSpeed(int value);
-
-signals:
- void animationSpeedChanged(int value);
-
-private:
- QString group;
-};
-
-class LocationServiceSettingsObjectWrapper : public QObject {
- Q_OBJECT
- Q_PROPERTY(int time_threshold READ timeThreshold WRITE setTimeThreshold NOTIFY timeThresholdChanged)
- Q_PROPERTY(int distance_threshold READ distanceThreshold WRITE setDistanceThreshold NOTIFY distanceThresholdChanged)
-public:
- LocationServiceSettingsObjectWrapper(QObject *parent);
- int timeThreshold() const;
- int distanceThreshold() const;
-public slots:
- void setTimeThreshold(int value);
- void setDistanceThreshold(int value);
-signals:
- void timeThresholdChanged(int value);
- void distanceThresholdChanged(int value);
-private:
- QString group;
-};
-
-class SettingsObjectWrapper : public QObject {
- Q_OBJECT
- Q_PROPERTY(short save_userid_local READ saveUserIdLocal WRITE setSaveUserIdLocal NOTIFY saveUserIdLocalChanged)
-
- Q_PROPERTY(TechnicalDetailsSettings* techical_details MEMBER techDetails CONSTANT)
- Q_PROPERTY(PartialPressureGasSettings* pp_gas MEMBER pp_gas CONSTANT)
- Q_PROPERTY(FacebookSettings* facebook MEMBER facebook CONSTANT)
- Q_PROPERTY(GeocodingPreferences* geocoding MEMBER geocoding CONSTANT)
- Q_PROPERTY(ProxySettings* proxy MEMBER proxy CONSTANT)
- Q_PROPERTY(CloudStorageSettings* cloud_storage MEMBER cloud_storage CONSTANT)
- Q_PROPERTY(DivePlannerSettings* planner MEMBER planner_settings CONSTANT)
- Q_PROPERTY(UnitsSettings* units MEMBER unit_settings CONSTANT)
-
- Q_PROPERTY(GeneralSettingsObjectWrapper* general MEMBER general_settings CONSTANT)
- Q_PROPERTY(DisplaySettingsObjectWrapper* display MEMBER display_settings CONSTANT)
- Q_PROPERTY(LanguageSettingsObjectWrapper* language MEMBER language_settings CONSTANT)
- Q_PROPERTY(AnimationsSettingsObjectWrapper* animation MEMBER animation_settings CONSTANT)
- Q_PROPERTY(LocationServiceSettingsObjectWrapper* Location MEMBER location_settings CONSTANT)
-public:
- static SettingsObjectWrapper *instance();
- short saveUserIdLocal() const;
-
- TechnicalDetailsSettings *techDetails;
- PartialPressureGasSettings *pp_gas;
- FacebookSettings *facebook;
- GeocodingPreferences *geocoding;
- ProxySettings *proxy;
- CloudStorageSettings *cloud_storage;
- DivePlannerSettings *planner_settings;
- UnitsSettings *unit_settings;
- GeneralSettingsObjectWrapper *general_settings;
- DisplaySettingsObjectWrapper *display_settings;
- LanguageSettingsObjectWrapper *language_settings;
- AnimationsSettingsObjectWrapper *animation_settings;
- LocationServiceSettingsObjectWrapper *location_settings;
-
-public slots:
- void setSaveUserIdLocal(short value);
-private:
- SettingsObjectWrapper(QObject *parent = NULL);
-signals:
- void saveUserIdLocalChanged(short value);
-};
-
-#endif
diff --git a/subsurface-core/subsurfacestartup.c b/subsurface-core/subsurfacestartup.c
deleted file mode 100644
index 6e0dede1c..000000000
--- a/subsurface-core/subsurfacestartup.c
+++ /dev/null
@@ -1,310 +0,0 @@
-#include "subsurfacestartup.h"
-#include "version.h"
-#include <stdbool.h>
-#include <string.h>
-#include "gettext.h"
-#include "qthelperfromc.h"
-#include "git-access.h"
-#include "libdivecomputer/version.h"
-
-struct preferences prefs, informational_prefs;
-struct preferences default_prefs = {
- .cloud_base_url = "https://cloud.subsurface-divelog.org/",
- .units = SI_UNITS,
- .unit_system = METRIC,
- .coordinates_traditional = true,
- .pp_graphs = {
- .po2 = false,
- .pn2 = false,
- .phe = false,
- .po2_threshold = 1.6,
- .pn2_threshold = 4.0,
- .phe_threshold = 13.0,
- },
- .mod = false,
- .modpO2 = 1.6,
- .ead = false,
- .hrgraph = false,
- .percentagegraph = false,
- .dcceiling = true,
- .redceiling = false,
- .calcceiling = false,
- .calcceiling3m = false,
- .calcndltts = false,
- .gflow = 30,
- .gfhigh = 75,
- .animation_speed = 500,
- .gf_low_at_maxdepth = false,
- .show_ccr_setpoint = false,
- .show_ccr_sensors = false,
- .font_size = -1,
- .display_invalid_dives = false,
- .show_sac = false,
- .display_unused_tanks = false,
- .show_average_depth = true,
- .ascrate75 = 9000 / 60,
- .ascrate50 = 6000 / 60,
- .ascratestops = 6000 / 60,
- .ascratelast6m = 1000 / 60,
- .descrate = 18000 / 60,
- .bottompo2 = 1400,
- .decopo2 = 1600,
- .doo2breaks = false,
- .drop_stone_mode = false,
- .switch_at_req_stop = false,
- .min_switch_duration = 60,
- .last_stop = false,
- .verbatim_plan = false,
- .display_runtime = true,
- .display_duration = true,
- .display_transitions = true,
- .safetystop = true,
- .bottomsac = 20000,
- .decosac = 17000,
- .reserve_gas=40000,
- .o2consumption = 720,
- .pscr_ratio = 100,
- .show_pictures_in_profile = true,
- .tankbar = false,
- .facebook = {
- .user_id = NULL,
- .album_id = NULL,
- .access_token = NULL
- },
- .defaultsetpoint = 1100,
- .cloud_background_sync = true,
- .geocoding = {
- .enable_geocoding = true,
- .parse_dive_without_gps = false,
- .tag_existing_dives = false,
- .category = { 0 }
- },
- .deco_mode = BUEHLMANN,
- .conservatism_level = 3,
- .distance_threshold = 1000,
- .time_threshold = 600
-};
-
-int run_survey;
-
-struct units *get_units()
-{
- return &prefs.units;
-}
-
-/* random helper functions, used here or elsewhere */
-static int sortfn(const void *_a, const void *_b)
-{
- const struct dive *a = (const struct dive *)*(void **)_a;
- const struct dive *b = (const struct dive *)*(void **)_b;
-
- if (a->when < b->when)
- return -1;
- if (a->when > b->when)
- return 1;
- return 0;
-}
-
-void sort_table(struct dive_table *table)
-{
- qsort(table->dives, table->nr, sizeof(struct dive *), sortfn);
-}
-
-const char *weekday(int wday)
-{
- static const char wday_array[7][7] = {
- /*++GETTEXT: these are three letter days - we allow up to six code bytes */
- QT_TRANSLATE_NOOP("gettextFromC", "Sun"), QT_TRANSLATE_NOOP("gettextFromC", "Mon"), QT_TRANSLATE_NOOP("gettextFromC", "Tue"), QT_TRANSLATE_NOOP("gettextFromC", "Wed"), QT_TRANSLATE_NOOP("gettextFromC", "Thu"), QT_TRANSLATE_NOOP("gettextFromC", "Fri"), QT_TRANSLATE_NOOP("gettextFromC", "Sat")
- };
- return translate("gettextFromC", wday_array[wday]);
-}
-
-const char *monthname(int mon)
-{
- static const char month_array[12][7] = {
- /*++GETTEXT: these are three letter months - we allow up to six code bytes*/
- QT_TRANSLATE_NOOP("gettextFromC", "Jan"), QT_TRANSLATE_NOOP("gettextFromC", "Feb"), QT_TRANSLATE_NOOP("gettextFromC", "Mar"), QT_TRANSLATE_NOOP("gettextFromC", "Apr"), QT_TRANSLATE_NOOP("gettextFromC", "May"), QT_TRANSLATE_NOOP("gettextFromC", "Jun"),
- QT_TRANSLATE_NOOP("gettextFromC", "Jul"), QT_TRANSLATE_NOOP("gettextFromC", "Aug"), QT_TRANSLATE_NOOP("gettextFromC", "Sep"), QT_TRANSLATE_NOOP("gettextFromC", "Oct"), QT_TRANSLATE_NOOP("gettextFromC", "Nov"), QT_TRANSLATE_NOOP("gettextFromC", "Dec"),
- };
- return translate("gettextFromC", month_array[mon]);
-}
-
-/*
- * track whether we switched to importing dives
- */
-bool imported = false;
-
-static void print_version()
-{
- printf("Subsurface v%s, ", subsurface_git_version());
- printf("built with libdivecomputer v%s\n", dc_version(NULL));
-}
-
-void print_files()
-{
- const char *branch = 0;
- const char *remote = 0;
- const char *filename, *local_git;
-
- filename = cloud_url();
-
- is_git_repository(filename, &branch, &remote, true);
- printf("\nFile locations:\n\n");
- if (branch && remote) {
- local_git = get_local_dir(remote, branch);
- printf("Local git storage: %s\n", local_git);
- } else {
- printf("Unable to get local git directory\n");
- }
- char *tmp = cloud_url();
- printf("Cloud URL: %s\n", tmp);
- free(tmp);
- tmp = hashfile_name_string();
- printf("Image hashes: %s\n", tmp);
- free(tmp);
- tmp = picturedir_string();
- printf("Local picture directory: %s\n\n", tmp);
- free(tmp);
-}
-
-static void print_help()
-{
- print_version();
- printf("\nUsage: subsurface [options] [logfile ...] [--import logfile ...]");
- printf("\n\noptions include:");
- printf("\n --help|-h This help text");
- printf("\n --import logfile ... Logs before this option is treated as base, everything after is imported");
- printf("\n --verbose|-v Verbose debug (repeat to increase verbosity)");
- printf("\n --version Prints current version");
- printf("\n --survey Offer to submit a user survey");
- printf("\n --win32console Create a dedicated console if needed (Windows only). Add option before everything else\n\n");
-}
-
-void parse_argument(const char *arg)
-{
- const char *p = arg + 1;
-
- do {
- switch (*p) {
- case 'h':
- print_help();
- exit(0);
- case 'v':
- verbose++;
- continue;
- case 'q':
- quit++;
- continue;
- case '-':
- /* long options with -- */
- if (strcmp(arg, "--help") == 0) {
- print_help();
- exit(0);
- }
- if (strcmp(arg, "--import") == 0) {
- imported = true; /* mark the dives so far as the base, * everything after is imported */
- return;
- }
- if (strcmp(arg, "--verbose") == 0) {
- verbose++;
- return;
- }
- if (strcmp(arg, "--version") == 0) {
- print_version();
- exit(0);
- }
- if (strcmp(arg, "--survey") == 0) {
- run_survey = true;
- return;
- }
- if (strcmp(arg, "--allow_run_as_root") == 0) {
- ++force_root;
- return;
- }
- if (strcmp(arg, "--win32console") == 0)
- return;
- /* fallthrough */
- case 'p':
- /* ignore process serial number argument when run as native macosx app */
- if (strncmp(arg, "-psQT_TR_NOOP(", 5) == 0) {
- return;
- }
- /* fallthrough */
- default:
- fprintf(stderr, "Bad argument '%s'\n", arg);
- exit(1);
- }
- } while (*++p);
-}
-
-/*
- * Under a POSIX setup, the locale string should have a format
- * like [language[_territory][.codeset][@modifier]].
- *
- * So search for the underscore, and see if the "territory" is
- * US, and turn on imperial units by default.
- *
- * I guess Burma and Liberia should trigger this too. I'm too
- * lazy to look up the territory names, though.
- */
-void setup_system_prefs(void)
-{
- const char *env;
-
- subsurface_OS_pref_setup();
- default_prefs.divelist_font = strdup(system_divelist_default_font);
- default_prefs.font_size = system_divelist_default_font_size;
- default_prefs.default_filename = system_default_filename();
-
- env = getenv("LC_MEASUREMENT");
- if (!env)
- env = getenv("LC_ALL");
- if (!env)
- env = getenv("LANG");
- if (!env)
- return;
- env = strchr(env, '_');
- if (!env)
- return;
- env++;
- if (strncmp(env, "US", 2))
- return;
-
- default_prefs.units = IMPERIAL_units;
-}
-
-/* copy a preferences block, including making copies of all included strings */
-void copy_prefs(struct preferences *src, struct preferences *dest)
-{
- *dest = *src;
- dest->divelist_font = copy_string(src->divelist_font);
- dest->default_filename = copy_string(src->default_filename);
- dest->default_cylinder = copy_string(src->default_cylinder);
- dest->cloud_base_url = copy_string(src->cloud_base_url);
- dest->cloud_git_url = copy_string(src->cloud_git_url);
- dest->userid = copy_string(src->userid);
- dest->proxy_host = copy_string(src->proxy_host);
- dest->proxy_user = copy_string(src->proxy_user);
- dest->proxy_pass = copy_string(src->proxy_pass);
- dest->time_format = copy_string(src->time_format);
- dest->date_format = copy_string(src->date_format);
- dest->date_format_short = copy_string(src->date_format_short);
- dest->cloud_storage_password = copy_string(src->cloud_storage_password);
- dest->cloud_storage_newpassword = copy_string(src->cloud_storage_newpassword);
- dest->cloud_storage_email = copy_string(src->cloud_storage_email);
- dest->cloud_storage_email_encoded = copy_string(src->cloud_storage_email_encoded);
- dest->facebook.access_token = copy_string(src->facebook.access_token);
- dest->facebook.user_id = copy_string(src->facebook.user_id);
- dest->facebook.album_id = copy_string(src->facebook.album_id);
-}
-
-/*
- * Free strduped prefs before exit.
- *
- * These are not real leaks but they plug the holes found by eg.
- * valgrind so you can find the real leaks.
- */
-void free_prefs(void)
-{
- // nop
-}
diff --git a/subsurface-core/subsurfacestartup.h b/subsurface-core/subsurfacestartup.h
deleted file mode 100644
index 3ccc24aa4..000000000
--- a/subsurface-core/subsurfacestartup.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef SUBSURFACESTARTUP_H
-#define SUBSURFACESTARTUP_H
-
-#include "dive.h"
-#include "divelist.h"
-#include "libdivecomputer.h"
-
-#ifdef __cplusplus
-extern "C" {
-#else
-#include <stdbool.h>
-#endif
-
-extern bool imported;
-
-void setup_system_prefs(void);
-void parse_argument(const char *arg);
-void free_prefs(void);
-void copy_prefs(struct preferences *src, struct preferences *dest);
-void print_files(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // SUBSURFACESTARTUP_H
diff --git a/subsurface-core/subsurfacesysinfo.cpp b/subsurface-core/subsurfacesysinfo.cpp
deleted file mode 100644
index a7173b169..000000000
--- a/subsurface-core/subsurfacesysinfo.cpp
+++ /dev/null
@@ -1,620 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Copyright (C) 2014 Intel Corporation
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "subsurfacesysinfo.h"
-#include <QString>
-
-#ifdef Q_OS_UNIX
-#include <sys/utsname.h>
-#endif
-
-#if QT_VERSION < QT_VERSION_CHECK(5, 4, 0)
-
-#ifndef QStringLiteral
-# define QStringLiteral QString::fromUtf8
-#endif
-
-#ifdef Q_OS_UNIX
-#include <qplatformdefs.h>
-#endif
-
-#ifdef __APPLE__
-#include <CoreServices/CoreServices.h>
-#endif
-
-// --- this is a copy of Qt 5.4's src/corelib/global/archdetect.cpp ---
-
-// main part: processor type
-#if defined(Q_PROCESSOR_ALPHA)
-# define ARCH_PROCESSOR "alpha"
-#elif defined(Q_PROCESSOR_ARM)
-# define ARCH_PROCESSOR "arm"
-#elif defined(Q_PROCESSOR_AVR32)
-# define ARCH_PROCESSOR "avr32"
-#elif defined(Q_PROCESSOR_BLACKFIN)
-# define ARCH_PROCESSOR "bfin"
-#elif defined(Q_PROCESSOR_X86_32)
-# define ARCH_PROCESSOR "i386"
-#elif defined(Q_PROCESSOR_X86_64)
-# define ARCH_PROCESSOR "x86_64"
-#elif defined(Q_PROCESSOR_IA64)
-# define ARCH_PROCESSOR "ia64"
-#elif defined(Q_PROCESSOR_MIPS)
-# define ARCH_PROCESSOR "mips"
-#elif defined(Q_PROCESSOR_POWER)
-# define ARCH_PROCESSOR "power"
-#elif defined(Q_PROCESSOR_S390)
-# define ARCH_PROCESSOR "s390"
-#elif defined(Q_PROCESSOR_SH)
-# define ARCH_PROCESSOR "sh"
-#elif defined(Q_PROCESSOR_SPARC)
-# define ARCH_PROCESSOR "sparc"
-#else
-# define ARCH_PROCESSOR "unknown"
-#endif
-
-// endinanness
-#if defined(Q_LITTLE_ENDIAN)
-# define ARCH_ENDIANNESS "little_endian"
-#elif defined(Q_BIG_ENDIAN)
-# define ARCH_ENDIANNESS "big_endian"
-#endif
-
-// pointer type
-#if defined(Q_OS_WIN64) || (defined(Q_OS_WINRT) && defined(_M_X64))
-# define ARCH_POINTER "llp64"
-#elif defined(__LP64__) || QT_POINTER_SIZE - 0 == 8
-# define ARCH_POINTER "lp64"
-#else
-# define ARCH_POINTER "ilp32"
-#endif
-
-// secondary: ABI string (includes the dash)
-#if defined(__ARM_EABI__) || defined(__mips_eabi)
-# define ARCH_ABI1 "-eabi"
-#elif defined(_MIPS_SIM)
-# if _MIPS_SIM == _ABIO32
-# define ARCH_ABI1 "-o32"
-# elif _MIPS_SIM == _ABIN32
-# define ARCH_ABI1 "-n32"
-# elif _MIPS_SIM == _ABI64
-# define ARCH_ABI1 "-n64"
-# elif _MIPS_SIM == _ABIO64
-# define ARCH_ABI1 "-o64"
-# endif
-#else
-# define ARCH_ABI1 ""
-#endif
-#if defined(__ARM_PCS_VFP) || defined(__mips_hard_float)
-# define ARCH_ABI2 "-hardfloat"
-#else
-# define ARCH_ABI2 ""
-#endif
-
-#define ARCH_ABI ARCH_ABI1 ARCH_ABI2
-
-#define ARCH_FULL ARCH_PROCESSOR "-" ARCH_ENDIANNESS "-" ARCH_POINTER ARCH_ABI
-
-// --- end of archdetect.cpp ---
-// copied from Qt 5.4.1's src/corelib/global/qglobal.cpp
-
-#if defined(Q_OS_WIN) || defined(Q_OS_CYGWIN) || defined(Q_OS_WINCE) || defined(Q_OS_WINRT)
-
-QT_BEGIN_INCLUDE_NAMESPACE
-#include "qt_windows.h"
-QT_END_INCLUDE_NAMESPACE
-
-#ifndef Q_OS_WINRT
-# ifndef Q_OS_WINCE
-// Fallback for determining Windows versions >= 8 by looping using the
-// version check macros. Note that it will return build number=0 to avoid
-// inefficient looping.
-static inline void determineWinOsVersionFallbackPost8(OSVERSIONINFO *result)
-{
- result->dwBuildNumber = 0;
- DWORDLONG conditionMask = 0;
- VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
- VER_SET_CONDITION(conditionMask, VER_PLATFORMID, VER_EQUAL);
- OSVERSIONINFOEX checkVersion = { sizeof(OSVERSIONINFOEX), result->dwMajorVersion, 0,
- result->dwBuildNumber, result->dwPlatformId, {'\0'}, 0, 0, 0, 0, 0 };
- for ( ; VerifyVersionInfo(&checkVersion, VER_MAJORVERSION | VER_PLATFORMID, conditionMask); ++checkVersion.dwMajorVersion)
- result->dwMajorVersion = checkVersion.dwMajorVersion;
- conditionMask = 0;
- checkVersion.dwMajorVersion = result->dwMajorVersion;
- checkVersion.dwMinorVersion = 0;
- VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_EQUAL);
- VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
- VER_SET_CONDITION(conditionMask, VER_PLATFORMID, VER_EQUAL);
- for ( ; VerifyVersionInfo(&checkVersion, VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID, conditionMask); ++checkVersion.dwMinorVersion)
- result->dwMinorVersion = checkVersion.dwMinorVersion;
-}
-
-# endif // !Q_OS_WINCE
-
-static inline OSVERSIONINFO winOsVersion()
-{
- OSVERSIONINFO result = { sizeof(OSVERSIONINFO), 0, 0, 0, 0, {'\0'}};
- // GetVersionEx() has been deprecated in Windows 8.1 and will return
- // only Windows 8 from that version on.
-# if defined(_MSC_VER) && _MSC_VER >= 1800
-# pragma warning( push )
-# pragma warning( disable : 4996 )
-# endif
- GetVersionEx(&result);
-# if defined(_MSC_VER) && _MSC_VER >= 1800
-# pragma warning( pop )
-# endif
-# ifndef Q_OS_WINCE
- if (result.dwMajorVersion == 6 && result.dwMinorVersion == 2) {
- determineWinOsVersionFallbackPost8(&result);
- }
-# endif // !Q_OS_WINCE
- return result;
-}
-#endif // !Q_OS_WINRT
-
-static const char *winVer_helper()
-{
- switch (int(SubsurfaceSysInfo::WindowsVersion)) {
- case SubsurfaceSysInfo::WV_NT:
- return "NT";
- case SubsurfaceSysInfo::WV_2000:
- return "2000";
- case SubsurfaceSysInfo::WV_XP:
- return "XP";
- case SubsurfaceSysInfo::WV_2003:
- return "2003";
- case SubsurfaceSysInfo::WV_VISTA:
- return "Vista";
- case SubsurfaceSysInfo::WV_WINDOWS7:
- return "7";
- case SubsurfaceSysInfo::WV_WINDOWS8:
- return "8";
- case SubsurfaceSysInfo::WV_WINDOWS8_1:
- return "8.1";
-
- case SubsurfaceSysInfo::WV_CE:
- return "CE";
- case SubsurfaceSysInfo::WV_CENET:
- return "CENET";
- case SubsurfaceSysInfo::WV_CE_5:
- return "CE5";
- case SubsurfaceSysInfo::WV_CE_6:
- return "CE6";
- }
- // unknown, future version
- return 0;
-}
-#endif
-
-#if defined(Q_OS_UNIX)
-# if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_FREEBSD)
-# define USE_ETC_OS_RELEASE
-struct QUnixOSVersion
-{
- // from /etc/os-release
- QString productType; // $ID
- QString productVersion; // $VERSION_ID
- QString prettyName; // $PRETTY_NAME
-};
-
-static QString unquote(const char *begin, const char *end)
-{
- if (*begin == '"') {
- Q_ASSERT(end[-1] == '"');
- return QString::fromLatin1(begin + 1, end - begin - 2);
- }
- return QString::fromLatin1(begin, end - begin);
-}
-
-static bool readEtcOsRelease(QUnixOSVersion &v)
-{
- // we're avoiding QFile here
- int fd = QT_OPEN("/etc/os-release", O_RDONLY);
- if (fd == -1)
- return false;
-
- QT_STATBUF sbuf;
- if (QT_FSTAT(fd, &sbuf) == -1) {
- QT_CLOSE(fd);
- return false;
- }
-
- QByteArray buffer(sbuf.st_size, Qt::Uninitialized);
- buffer.resize(QT_READ(fd, buffer.data(), sbuf.st_size));
- QT_CLOSE(fd);
-
- const char *ptr = buffer.constData();
- const char *end = buffer.constEnd();
- const char *eol;
- for ( ; ptr != end; ptr = eol + 1) {
- static const char idString[] = "ID=";
- static const char prettyNameString[] = "PRETTY_NAME=";
- static const char versionIdString[] = "VERSION_ID=";
-
- // find the end of the line after ptr
- eol = static_cast<const char *>(memchr(ptr, '\n', end - ptr));
- if (!eol)
- eol = end - 1;
-
- // note: we're doing a binary search here, so comparison
- // must always be sorted
- int cmp = strncmp(ptr, idString, strlen(idString));
- if (cmp < 0)
- continue;
- if (cmp == 0) {
- ptr += strlen(idString);
- v.productType = unquote(ptr, eol);
- continue;
- }
-
- cmp = strncmp(ptr, prettyNameString, strlen(prettyNameString));
- if (cmp < 0)
- continue;
- if (cmp == 0) {
- ptr += strlen(prettyNameString);
- v.prettyName = unquote(ptr, eol);
- continue;
- }
-
- cmp = strncmp(ptr, versionIdString, strlen(versionIdString));
- if (cmp < 0)
- continue;
- if (cmp == 0) {
- ptr += strlen(versionIdString);
- v.productVersion = unquote(ptr, eol);
- continue;
- }
- }
-
- return true;
-}
-# endif // USE_ETC_OS_RELEASE
-#endif // Q_OS_UNIX
-
-static QString unknownText()
-{
- return QStringLiteral("unknown");
-}
-
-QString SubsurfaceSysInfo::buildCpuArchitecture()
-{
- return QStringLiteral(ARCH_PROCESSOR);
-}
-
-QString SubsurfaceSysInfo::currentCpuArchitecture()
-{
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
- // We don't need to catch all the CPU architectures in this function;
- // only those where the host CPU might be different than the build target
- // (usually, 64-bit platforms).
- SYSTEM_INFO info;
- GetNativeSystemInfo(&info);
- switch (info.wProcessorArchitecture) {
-# ifdef PROCESSOR_ARCHITECTURE_AMD64
- case PROCESSOR_ARCHITECTURE_AMD64:
- return QStringLiteral("x86_64");
-# endif
-# ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
- case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
-# endif
- case PROCESSOR_ARCHITECTURE_IA64:
- return QStringLiteral("ia64");
- }
-#elif defined(Q_OS_UNIX)
- long ret = -1;
- struct utsname u;
-
-# if defined(Q_OS_SOLARIS)
- // We need a special call for Solaris because uname(2) on x86 returns "i86pc" for
- // both 32- and 64-bit CPUs. Reference:
- // http://docs.oracle.com/cd/E18752_01/html/816-5167/sysinfo-2.html#REFMAN2sysinfo-2
- // http://fxr.watson.org/fxr/source/common/syscall/systeminfo.c?v=OPENSOLARIS
- // http://fxr.watson.org/fxr/source/common/conf/param.c?v=OPENSOLARIS;im=10#L530
- if (ret == -1)
- ret = sysinfo(SI_ARCHITECTURE_64, u.machine, sizeof u.machine);
-# endif
-
- if (ret == -1)
- ret = uname(&u);
-
- // we could use detectUnixVersion() above, but we only need a field no other function does
- if (ret != -1) {
- // the use of QT_BUILD_INTERNAL here is simply to ensure all branches build
- // as we don't often build on some of the less common platforms
-# if defined(Q_PROCESSOR_ARM) || defined(QT_BUILD_INTERNAL)
- if (strcmp(u.machine, "aarch64") == 0)
- return QStringLiteral("arm64");
- if (strncmp(u.machine, "armv", 4) == 0)
- return QStringLiteral("arm");
-# endif
-# if defined(Q_PROCESSOR_POWER) || defined(QT_BUILD_INTERNAL)
- // harmonize "powerpc" and "ppc" to "power"
- if (strncmp(u.machine, "ppc", 3) == 0)
- return QLatin1String("power") + QLatin1String(u.machine + 3);
- if (strncmp(u.machine, "powerpc", 7) == 0)
- return QLatin1String("power") + QLatin1String(u.machine + 7);
- if (strcmp(u.machine, "Power Macintosh") == 0)
- return QLatin1String("power");
-# endif
-# if defined(Q_PROCESSOR_SPARC) || defined(QT_BUILD_INTERNAL)
- // Solaris sysinfo(2) (above) uses "sparcv9", but uname -m says "sun4u";
- // Linux says "sparc64"
- if (strcmp(u.machine, "sun4u") == 0 || strcmp(u.machine, "sparc64") == 0)
- return QStringLiteral("sparcv9");
- if (strcmp(u.machine, "sparc32") == 0)
- return QStringLiteral("sparc");
-# endif
-# if defined(Q_PROCESSOR_X86) || defined(QT_BUILD_INTERNAL)
- // harmonize all "i?86" to "i386"
- if (strlen(u.machine) == 4 && u.machine[0] == 'i'
- && u.machine[2] == '8' && u.machine[3] == '6')
- return QStringLiteral("i386");
- if (strcmp(u.machine, "amd64") == 0) // Solaris
- return QStringLiteral("x86_64");
-# endif
- return QString::fromLatin1(u.machine);
- }
-#endif
- return buildCpuArchitecture();
-}
-
-
-QString SubsurfaceSysInfo::buildAbi()
-{
- return QLatin1String(ARCH_FULL);
-}
-
-QString SubsurfaceSysInfo::kernelType()
-{
-#if defined(Q_OS_WINCE)
- return QStringLiteral("wince");
-#elif defined(Q_OS_WIN)
- return QStringLiteral("winnt");
-#elif defined(Q_OS_UNIX)
- struct utsname u;
- if (uname(&u) == 0)
- return QString::fromLatin1(u.sysname).toLower();
-#endif
- return unknownText();
-}
-
-QString SubsurfaceSysInfo::kernelVersion()
-{
-#ifdef Q_OS_WINRT
- // TBD
- return QString();
-#elif defined(Q_OS_WIN)
- const OSVERSIONINFO osver = winOsVersion();
- return QString::number(int(osver.dwMajorVersion)) + QLatin1Char('.') + QString::number(int(osver.dwMinorVersion))
- + QLatin1Char('.') + QString::number(int(osver.dwBuildNumber));
-#else
- struct utsname u;
- if (uname(&u) == 0)
- return QString::fromLatin1(u.release);
- return QString();
-#endif
-}
-
-QString SubsurfaceSysInfo::productType()
-{
- // similar, but not identical to QFileSelectorPrivate::platformSelectors
-#if defined(Q_OS_WINPHONE)
- return QStringLiteral("winphone");
-#elif defined(Q_OS_WINRT)
- return QStringLiteral("winrt");
-#elif defined(Q_OS_WINCE)
- return QStringLiteral("wince");
-#elif defined(Q_OS_WIN)
- return QStringLiteral("windows");
-
-#elif defined(Q_OS_BLACKBERRY)
- return QStringLiteral("blackberry");
-#elif defined(Q_OS_QNX)
- return QStringLiteral("qnx");
-
-#elif defined(Q_OS_ANDROID)
- return QStringLiteral("android");
-
-#elif defined(Q_OS_IOS)
- return QStringLiteral("ios");
-#elif defined(Q_OS_OSX)
- return QStringLiteral("osx");
-#elif defined(Q_OS_DARWIN)
- return QStringLiteral("darwin");
-
-#elif defined(USE_ETC_OS_RELEASE) // Q_OS_UNIX
- QUnixOSVersion unixOsVersion;
- readEtcOsRelease(unixOsVersion);
- if (!unixOsVersion.productType.isEmpty())
- return unixOsVersion.productType;
-#endif
- return unknownText();
-}
-
-QString SubsurfaceSysInfo::productVersion()
-{
-#if defined(Q_OS_IOS)
- int major = (int(MacintoshVersion) >> 4) & 0xf;
- int minor = int(MacintoshVersion) & 0xf;
- if (Q_LIKELY(major < 10 && minor < 10)) {
- char buf[4] = { char(major + '0'), '.', char(minor + '0'), '\0' };
- return QString::fromLatin1(buf, 3);
- }
- return QString::number(major) + QLatin1Char('.') + QString::number(minor);
-#elif defined(Q_OS_OSX)
- int minor = int(MacintoshVersion) - 2; // we're not running on Mac OS 9
- Q_ASSERT(minor < 100);
- char buf[] = "10.0\0";
- if (Q_LIKELY(minor < 10)) {
- buf[3] += minor;
- } else {
- buf[3] += minor / 10;
- buf[4] = '0' + minor % 10;
- }
- return QString::fromLatin1(buf);
-#elif defined(Q_OS_WIN)
- const char *version = winVer_helper();
- if (version)
- return QString::fromLatin1(version).toLower();
- // fall through
-
- // Android and Blackberry should not fall through to the Unix code
-#elif defined(Q_OS_ANDROID)
- // TBD
-#elif defined(Q_OS_BLACKBERRY)
- deviceinfo_details_t *deviceInfo;
- if (deviceinfo_get_details(&deviceInfo) == BPS_SUCCESS) {
- QString bbVersion = QString::fromLatin1(deviceinfo_details_get_device_os_version(deviceInfo));
- deviceinfo_free_details(&deviceInfo);
- return bbVersion;
- }
-#elif defined(USE_ETC_OS_RELEASE) // Q_OS_UNIX
- QUnixOSVersion unixOsVersion;
- readEtcOsRelease(unixOsVersion);
- if (!unixOsVersion.productVersion.isEmpty())
- return unixOsVersion.productVersion;
-#endif
-
- // fallback
- return unknownText();
-}
-
-QString SubsurfaceSysInfo::prettyProductName()
-{
-#if defined(Q_OS_IOS)
- return QLatin1String("iOS ") + productVersion();
-#elif defined(Q_OS_OSX)
- // get the known codenames
- const char *basename = 0;
- switch (int(MacintoshVersion)) {
- case MV_CHEETAH:
- case MV_PUMA:
- case MV_JAGUAR:
- case MV_PANTHER:
- case MV_TIGER:
- // This version of Qt does not run on those versions of OS X
- // so this case label will never be reached
- Q_UNREACHABLE();
- break;
- case MV_LEOPARD:
- basename = "Mac OS X Leopard (";
- break;
- case MV_SNOWLEOPARD:
- basename = "Mac OS X Snow Leopard (";
- break;
- case MV_LION:
- basename = "Mac OS X Lion (";
- break;
- case MV_MOUNTAINLION:
- basename = "OS X Mountain Lion (";
- break;
- case MV_MAVERICKS:
- basename = "OS X Mavericks (";
- break;
-#ifdef MV_YOSEMITE
- case MV_YOSEMITE:
-#else
- case 0x000C: // MV_YOSEMITE
-#endif
- basename = "OS X Yosemite (";
- break;
-#ifdef MV_ELCAPITAN
- case MV_ELCAPITAN :
-#else
- case 0x000D: // MV_ELCAPITAN
-#endif
- basename = "OS X El Capitan (";
- break;
- }
- if (basename)
- return QLatin1String(basename) + productVersion() + QLatin1Char(')');
-
- // a future version of OS X
- return QLatin1String("OS X ") + productVersion();
-#elif defined(Q_OS_WINPHONE)
- return QLatin1String("Windows Phone ") + QLatin1String(winVer_helper());
-#elif defined(Q_OS_WIN)
- return QLatin1String("Windows ") + QLatin1String(winVer_helper());
-#elif defined(Q_OS_ANDROID)
- return QLatin1String("Android ") + productVersion();
-#elif defined(Q_OS_BLACKBERRY)
- return QLatin1String("BlackBerry ") + productVersion();
-#elif defined(Q_OS_UNIX)
-# ifdef USE_ETC_OS_RELEASE
- QUnixOSVersion unixOsVersion;
- readEtcOsRelease(unixOsVersion);
- if (!unixOsVersion.prettyName.isEmpty())
- return unixOsVersion.prettyName;
-# endif
- struct utsname u;
- if (uname(&u) == 0)
- return QString::fromLatin1(u.sysname) + QLatin1Char(' ') + QString::fromLatin1(u.release);
-#endif
- return unknownText();
-}
-
-#endif // Qt >= 5.4
-
-QString SubsurfaceSysInfo::prettyOsName()
-{
- // Matches the pre-release version of Qt 5.4
- QString pretty = prettyProductName();
-#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID)
- // QSysInfo::kernelType() returns lowercase ("linux" instead of "Linux")
- struct utsname u;
- if (uname(&u) == 0)
- return QString::fromLatin1(u.sysname) + QLatin1String(" (") + pretty + QLatin1Char(')');
-#endif
- return pretty;
-}
-
-extern "C" {
-bool isWin7Or8()
-{
-#ifdef Q_OS_WIN
- return (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based) >= QSysInfo::WV_WINDOWS7;
-#else
- return false;
-#endif
-}
-}
diff --git a/subsurface-core/subsurfacesysinfo.h b/subsurface-core/subsurfacesysinfo.h
deleted file mode 100644
index b2c267b83..000000000
--- a/subsurface-core/subsurfacesysinfo.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Copyright (C) 2014 Intel Corporation
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef SUBSURFACESYSINFO_H
-#define SUBSURFACESYSINFO_H
-
-#include <QSysInfo>
-
-class SubsurfaceSysInfo : public QSysInfo {
-public:
-#if QT_VERSION < 0x050400
- static QString buildCpuArchitecture();
- static QString currentCpuArchitecture();
- static QString buildAbi();
-
- static QString kernelType();
- static QString kernelVersion();
- static QString productType();
- static QString productVersion();
- static QString prettyProductName();
-#endif
- static QString prettyOsName();
-};
-
-
-#endif // SUBSURFACESYSINFO_H
diff --git a/subsurface-core/taxonomy.c b/subsurface-core/taxonomy.c
deleted file mode 100644
index 670d85ad0..000000000
--- a/subsurface-core/taxonomy.c
+++ /dev/null
@@ -1,48 +0,0 @@
-#include "taxonomy.h"
-#include "gettext.h"
-#include <stdlib.h>
-
-char *taxonomy_category_names[TC_NR_CATEGORIES] = {
- QT_TRANSLATE_NOOP("getTextFromC", "None"),
- QT_TRANSLATE_NOOP("getTextFromC", "Ocean"),
- QT_TRANSLATE_NOOP("getTextFromC", "Country"),
- QT_TRANSLATE_NOOP("getTextFromC", "State"),
- QT_TRANSLATE_NOOP("getTextFromC", "County"),
- QT_TRANSLATE_NOOP("getTextFromC", "Town"),
- QT_TRANSLATE_NOOP("getTextFromC", "City")
-};
-
-// these are the names for geoname.org
-char *taxonomy_api_names[TC_NR_CATEGORIES] = {
- "none",
- "name",
- "countryName",
- "adminName1",
- "adminName2",
- "toponymName",
- "adminName3"
-};
-
-struct taxonomy *alloc_taxonomy()
-{
- return calloc(TC_NR_CATEGORIES, sizeof(struct taxonomy));
-}
-
-void free_taxonomy(struct taxonomy_data *t)
-{
- if (t) {
- for (int i = 0; i < t->nr; i++)
- free((void *)t->category[i].value);
- free(t->category);
- t->category = NULL;
- t->nr = 0;
- }
-}
-
-int taxonomy_index_for_category(struct taxonomy_data *t, enum taxonomy_category cat)
-{
- for (int i = 0; i < t->nr; i++)
- if (t->category[i].category == cat)
- return i;
- return -1;
-}
diff --git a/subsurface-core/taxonomy.h b/subsurface-core/taxonomy.h
deleted file mode 100644
index 51245d562..000000000
--- a/subsurface-core/taxonomy.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef TAXONOMY_H
-#define TAXONOMY_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-enum taxonomy_category {
- TC_NONE,
- TC_OCEAN,
- TC_COUNTRY,
- TC_ADMIN_L1,
- TC_ADMIN_L2,
- TC_LOCALNAME,
- TC_ADMIN_L3,
- TC_NR_CATEGORIES
-};
-
-extern char *taxonomy_category_names[TC_NR_CATEGORIES];
-extern char *taxonomy_api_names[TC_NR_CATEGORIES];
-
-struct taxonomy {
- int category; /* the category for this tag: ocean, country, admin_l1, admin_l2, localname, etc */
- const char *value; /* the value returned, parsed, or manually entered for that category */
- enum { GEOCODED, PARSED, MANUAL, COPIED } origin;
-};
-
-/* the data block contains 3 taxonomy structures - unused ones have a tag value of NONE */
-struct taxonomy_data {
- int nr;
- struct taxonomy *category;
-};
-
-struct taxonomy *alloc_taxonomy();
-void free_taxonomy(struct taxonomy_data *t);
-int taxonomy_index_for_category(struct taxonomy_data *t, enum taxonomy_category cat);
-
-#ifdef __cplusplus
-}
-#endif
-#endif // TAXONOMY_H
diff --git a/subsurface-core/time.c b/subsurface-core/time.c
deleted file mode 100644
index 0893f19d8..000000000
--- a/subsurface-core/time.c
+++ /dev/null
@@ -1,98 +0,0 @@
-#include <string.h>
-#include "dive.h"
-
-/*
- * Convert 64-bit timestamp to 'struct tm' in UTC.
- *
- * On 32-bit machines, only do 64-bit arithmetic for the seconds
- * part, after that we do everything in 'long'. 64-bit divides
- * are unnecessary once you're counting minutes (32-bit minutes:
- * 8000+ years).
- */
-void utc_mkdate(timestamp_t timestamp, struct tm *tm)
-{
- static const unsigned int mdays[] = {
- 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
- };
- static const unsigned int mdays_leap[] = {
- 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
- };
- unsigned long val;
- unsigned int leapyears;
- int m;
- const unsigned int *mp;
-
- memset(tm, 0, sizeof(*tm));
-
- /* seconds since 1970 -> minutes since 1970 */
- tm->tm_sec = timestamp % 60;
- val = timestamp /= 60;
-
- /* Do the simple stuff */
- tm->tm_min = val % 60;
- val /= 60;
- tm->tm_hour = val % 24;
- val /= 24;
-
- /* Jan 1, 1970 was a Thursday (tm_wday=4) */
- tm->tm_wday = (val + 4) % 7;
-
- /*
- * Now we're in "days since Jan 1, 1970". To make things easier,
- * let's make it "days since Jan 1, 1968", since that's a leap-year
- */
- val += 365 + 366;
-
- /* This only works up until 2099 (2100 isn't a leap-year) */
- leapyears = val / (365 * 4 + 1);
- val %= (365 * 4 + 1);
- tm->tm_year = 68 + leapyears * 4;
-
- /* Handle the leap-year itself */
- mp = mdays_leap;
- if (val > 365) {
- tm->tm_year++;
- val -= 366;
- tm->tm_year += val / 365;
- val %= 365;
- mp = mdays;
- }
-
- for (m = 0; m < 12; m++) {
- if (val < *mp)
- break;
- val -= *mp++;
- }
- tm->tm_mday = val + 1;
- tm->tm_mon = m;
-}
-
-timestamp_t utc_mktime(struct tm *tm)
-{
- static const int mdays[] = {
- 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
- };
- int year = tm->tm_year;
- int month = tm->tm_mon;
- int day = tm->tm_mday;
-
- /* First normalize relative to 1900 */
- if (year < 70)
- year += 100;
- else if (year > 1900)
- year -= 1900;
-
- /* Normalized to Jan 1, 1970: unix time */
- year -= 70;
-
- if (year < 0 || year > 129) /* algo only works for 1970-2099 */
- return -1;
- if (month < 0 || month > 11) /* array bounds */
- return -1;
- if (month < 2 || (year + 2) % 4)
- day--;
- if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0)
- return -1;
- return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24 * 60 * 60UL +
- tm->tm_hour * 60 * 60 + tm->tm_min * 60 + tm->tm_sec;
-}
diff --git a/subsurface-core/uemis-downloader.c b/subsurface-core/uemis-downloader.c
deleted file mode 100644
index b9b532303..000000000
--- a/subsurface-core/uemis-downloader.c
+++ /dev/null
@@ -1,1403 +0,0 @@
-/*
- * uemis-downloader.c
- *
- * Copyright (c) Dirk Hohndel <dirk@hohndel.org>
- * released under GPL2
- *
- * very (VERY) loosely based on the algorithms found in Java code by Fabian Gast <fgast@only640k.net>
- * which was released under the BSD-STYLE BEER WARE LICENSE
- * I believe that I only used the information about HOW to do this (download data from the Uemis
- * Zurich) but did not actually use any of his copyrighted code, therefore the license under which
- * he released his code does not apply to this new implementation in C
- *
- * Modified by Guido Lerch guido.lerch@gmail.com in August 2015
- */
-#include <fcntl.h>
-#include <dirent.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-
-#include "gettext.h"
-#include "libdivecomputer.h"
-#include "uemis.h"
-#include "divelist.h"
-
-#define ERR_FS_ALMOST_FULL QT_TRANSLATE_NOOP("gettextFromC", "Uemis Zurich: the file system is almost full.\nDisconnect/reconnect the dive computer\nand click \'Retry\'")
-#define ERR_FS_FULL QT_TRANSLATE_NOOP("gettextFromC", "Uemis Zurich: the file system is full.\nDisconnect/reconnect the dive computer\nand click Retry")
-#define ERR_FS_SHORT_WRITE QT_TRANSLATE_NOOP("gettextFromC", "Short write to req.txt file.\nIs the Uemis Zurich plugged in correctly?")
-#define ERR_NO_FILES QT_TRANSLATE_NOOP("gettextFromC", "No dives to download.")
-#define BUFLEN 2048
-#define BUFLEN 2048
-#define NUM_PARAM_BUFS 10
-
-// debugging setup
-// #define UEMIS_DEBUG 1 + 2 + 4 + 8 + 16 + 32
-
-#define UEMIS_MAX_FILES 4000
-#define UEMIS_MEM_FULL 1
-#define UEMIS_MEM_OK 0
-#define UEMIS_SPOT_BLOCK_SIZE 1
-#define UEMIS_DIVE_DETAILS_SIZE 2
-#define UEMIS_LOG_BLOCK_SIZE 10
-#define UEMIS_CHECK_LOG 1
-#define UEMIS_CHECK_DETAILS 2
-#define UEMIS_CHECK_SINGLE_DIVE 3
-
-#if UEMIS_DEBUG
-const char *home, *user, *d_time;
-static int debug_round = 0;
-#define debugfile stderr
-#endif
-
-#if UEMIS_DEBUG & 64 /* we are reading from a copy of the filesystem, not the device - no need to wait */
-#define UEMIS_TIMEOUT 50 /* 50ns */
-#define UEMIS_LONG_TIMEOUT 500 /* 500ns */
-#define UEMIS_MAX_TIMEOUT 2000 /* 2ms */
-#else
-#define UEMIS_TIMEOUT 50000 /* 50ms */
-#define UEMIS_LONG_TIMEOUT 500000 /* 500ms */
-#define UEMIS_MAX_TIMEOUT 2000000 /* 2s */
-#endif
-
-static char *param_buff[NUM_PARAM_BUFS];
-static char *reqtxt_path;
-static int reqtxt_file;
-static int filenr;
-static int number_of_files;
-static char *mbuf = NULL;
-static int mbuf_size = 0;
-
-static int max_mem_used = -1;
-static int next_table_index = 0;
-static int dive_to_read = 0;
-
-static int max_deleted_seen = -1;
-
-/* helper function to parse the Uemis data structures */
-static void uemis_ts(char *buffer, void *_when)
-{
- struct tm tm;
- timestamp_t *when = _when;
-
- memset(&tm, 0, sizeof(tm));
- sscanf(buffer, "%d-%d-%dT%d:%d:%d",
- &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
- &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
- tm.tm_mon -= 1;
- tm.tm_year -= 1900;
- *when = utc_mktime(&tm);
-}
-
-/* float minutes */
-static void uemis_duration(char *buffer, duration_t *duration)
-{
- duration->seconds = rint(ascii_strtod(buffer, NULL) * 60);
-}
-
-/* int cm */
-static void uemis_depth(char *buffer, depth_t *depth)
-{
- depth->mm = atoi(buffer) * 10;
-}
-
-static void uemis_get_index(char *buffer, int *idx)
-{
- *idx = atoi(buffer);
-}
-
-/* space separated */
-static void uemis_add_string(const char *buffer, char **text, const char *delimit)
-{
- /* do nothing if this is an empty buffer (Uemis sometimes returns a single
- * space for empty buffers) */
- if (!buffer || !*buffer || (*buffer == ' ' && *(buffer + 1) == '\0'))
- return;
- if (!*text) {
- *text = strdup(buffer);
- } else {
- char *buf = malloc(strlen(buffer) + strlen(*text) + 2);
- strcpy(buf, *text);
- strcat(buf, delimit);
- strcat(buf, buffer);
- free(*text);
- *text = buf;
- }
-}
-
-/* still unclear if it ever reports lbs */
-static void uemis_get_weight(char *buffer, weightsystem_t *weight, int diveid)
-{
- weight->weight.grams = uemis_get_weight_unit(diveid) ?
- lbs_to_grams(ascii_strtod(buffer, NULL)) :
- ascii_strtod(buffer, NULL) * 1000;
- weight->description = strdup(translate("gettextFromC", "unknown"));
-}
-
-static struct dive *uemis_start_dive(uint32_t deviceid)
-{
- struct dive *dive = alloc_dive();
- dive->downloaded = true;
- dive->dc.model = strdup("Uemis Zurich");
- dive->dc.deviceid = deviceid;
- return dive;
-}
-
-static struct dive *get_dive_by_uemis_diveid(device_data_t *devdata, uint32_t object_id)
-{
- for (int i = 0; i < devdata->download_table->nr; i++) {
- if (object_id == devdata->download_table->dives[i]->dc.diveid)
- return devdata->download_table->dives[i];
- }
- return NULL;
-}
-
-static void record_uemis_dive(device_data_t *devdata, struct dive *dive)
-{
- if (devdata->create_new_trip) {
- if (!devdata->trip)
- devdata->trip = create_and_hookup_trip_from_dive(dive);
- else
- add_dive_to_trip(dive, devdata->trip);
- }
- record_dive_to_table(dive, devdata->download_table);
-}
-
-/* send text to the importer progress bar */
-static void uemis_info(const char *fmt, ...)
-{
- static char buffer[256];
- va_list ap;
-
- va_start(ap, fmt);
- vsnprintf(buffer, sizeof(buffer), fmt, ap);
- va_end(ap);
- progress_bar_text = buffer;
- if (verbose)
- fprintf(stderr, "Uemis downloader: %s\n", buffer);
-}
-
-static long bytes_available(int file)
-{
- long result;
- long now = lseek(file, 0, SEEK_CUR);
- if (now == -1)
- return 0;
- result = lseek(file, 0, SEEK_END);
- lseek(file, now, SEEK_SET);
- if (now == -1 || result == -1)
- return 0;
- return result;
-}
-
-static int number_of_file(char *path)
-{
- int count = 0;
-#ifdef WIN32
- struct _wdirent *entry;
- _WDIR *dirp = (_WDIR *)subsurface_opendir(path);
-#else
- struct dirent *entry;
- DIR *dirp = (DIR *)subsurface_opendir(path);
-#endif
-
- while (dirp) {
-#ifdef WIN32
- entry = _wreaddir(dirp);
- if (!entry)
- break;
-#else
- entry = readdir(dirp);
- if (!entry)
- break;
- if (strstr(entry->d_name, ".TXT") || strstr(entry->d_name, ".txt")) /* If the entry is a regular file */
-#endif
- count++;
- }
-#ifdef WIN32
- _wclosedir(dirp);
-#else
- closedir(dirp);
-#endif
- return count;
-}
-
-static char *build_filename(const char *path, const char *name)
-{
- int len = strlen(path) + strlen(name) + 2;
- char *buf = malloc(len);
-#if WIN32
- snprintf(buf, len, "%s\\%s", path, name);
-#else
- snprintf(buf, len, "%s/%s", path, name);
-#endif
- return buf;
-}
-
-/* Check if there's a req.txt file and get the starting filenr from it.
- * Test for the maximum number of ANS files (I believe this is always
- * 4000 but in case there are differences depending on firmware, this
- * code is easy enough */
-static bool uemis_init(const char *path)
-{
- char *ans_path;
- int i;
-
- if (!path)
- return false;
- /* let's check if this is indeed a Uemis DC */
- reqtxt_path = build_filename(path, "req.txt");
- reqtxt_file = subsurface_open(reqtxt_path, O_RDONLY | O_CREAT, 0666);
- if (reqtxt_file < 0) {
-#if UEMIS_DEBUG & 1
- fprintf(debugfile, ":EE req.txt can't be opened\n");
-#endif
- return false;
- }
- if (bytes_available(reqtxt_file) > 5) {
- char tmp[6];
- read(reqtxt_file, tmp, 5);
- tmp[5] = '\0';
-#if UEMIS_DEBUG & 2
- fprintf(debugfile, "::r req.txt \"%s\"\n", tmp);
-#endif
- if (sscanf(tmp + 1, "%d", &filenr) != 1)
- return false;
- } else {
- filenr = 0;
-#if UEMIS_DEBUG & 2
- fprintf(debugfile, "::r req.txt skipped as there were fewer than 5 bytes\n");
-#endif
- }
- close(reqtxt_file);
-
- /* It would be nice if we could simply go back to the first set of
- * ANS files. But with a FAT filesystem that isn't possible */
- ans_path = build_filename(path, "ANS");
- number_of_files = number_of_file(ans_path);
- free(ans_path);
- /* initialize the array in which we collect the answers */
- for (i = 0; i < NUM_PARAM_BUFS; i++)
- param_buff[i] = "";
- return true;
-}
-
-static void str_append_with_delim(char *s, char *t)
-{
- int len = strlen(s);
- snprintf(s + len, BUFLEN - len, "%s{", t);
-}
-
-/* The communication protocol with the DC is truly funky.
- * After you write your request to the req.txt file you call this function.
- * It writes the number of the next ANS file at the beginning of the req.txt
- * file (prefixed by 'n' or 'r') and then again at the very end of it, after
- * the full request (this time without the prefix).
- * Then it syncs (not needed on Windows) and closes the file. */
-static void trigger_response(int file, char *command, int nr, long tailpos)
-{
- char fl[10];
-
- snprintf(fl, 8, "%s%04d", command, nr);
-#if UEMIS_DEBUG & 4
- fprintf(debugfile, ":tr %s (after seeks)\n", fl);
-#endif
- if (lseek(file, 0, SEEK_SET) == -1)
- goto fs_error;
- if (write(file, fl, strlen(fl)) == -1)
- goto fs_error;
- if (lseek(file, tailpos, SEEK_SET) == -1)
- goto fs_error;
- if (write(file, fl + 1, strlen(fl + 1)) == -1)
- goto fs_error;
-#ifndef WIN32
- fsync(file);
-#endif
-fs_error:
- close(file);
-}
-
-static char *next_token(char **buf)
-{
- char *q, *p = strchr(*buf, '{');
- if (p)
- *p = '\0';
- else
- p = *buf + strlen(*buf) - 1;
- q = *buf;
- *buf = p + 1;
- return q;
-}
-
-/* poor man's tokenizer that understands a quoted delimiter ('{') */
-static char *next_segment(char *buf, int *offset, int size)
-{
- int i = *offset;
- int seg_size;
- bool done = false;
- char *segment;
-
- while (!done) {
- if (i < size) {
- if (i < size - 1 && buf[i] == '\\' &&
- (buf[i + 1] == '\\' || buf[i + 1] == '{'))
- memcpy(buf + i, buf + i + 1, size - i - 1);
- else if (buf[i] == '{')
- done = true;
- i++;
- } else {
- done = true;
- }
- }
- seg_size = i - *offset - 1;
- if (seg_size < 0)
- seg_size = 0;
- segment = malloc(seg_size + 1);
- memcpy(segment, buf + *offset, seg_size);
- segment[seg_size] = '\0';
- *offset = i;
- return segment;
-}
-
-/* a dynamically growing buffer to store the potentially massive responses.
- * The binary data block can be more than 100k in size (base64 encoded) */
-static void buffer_add(char **buffer, int *buffer_size, char *buf)
-{
- if (!buf)
- return;
- if (!*buffer) {
- *buffer = strdup(buf);
- *buffer_size = strlen(*buffer) + 1;
- } else {
- *buffer_size += strlen(buf);
- *buffer = realloc(*buffer, *buffer_size);
- strcat(*buffer, buf);
- }
-#if UEMIS_DEBUG & 8
- fprintf(debugfile, "added \"%s\" to buffer - new length %d\n", buf, *buffer_size);
-#endif
-}
-
-/* are there more ANS files we can check? */
-static bool next_file(int max)
-{
- if (filenr >= max)
- return false;
- filenr++;
- return true;
-}
-
-/* try and do a quick decode - without trying to get to fancy in case the data
- * straddles a block boundary...
- * we are parsing something that looks like this:
- * object_id{int{2{date{ts{2011-04-05T12:38:04{duration{float{12.000...
- */
-static char *first_object_id_val(char *buf)
-{
- char *object, *bufend;
- if (!buf)
- return NULL;
- bufend = buf + strlen(buf);
- object = strstr(buf, "object_id");
- if (object && object + 14 < bufend) {
- /* get the value */
- char tmp[36];
- char *p = object + 14;
- char *t = tmp;
-
-#if UEMIS_DEBUG & 4
- char debugbuf[50];
- strncpy(debugbuf, object, 49);
- debugbuf[49] = '\0';
- fprintf(debugfile, "buf |%s|\n", debugbuf);
-#endif
- while (p < bufend && *p != '{' && t < tmp + 9)
- *t++ = *p++;
- if (*p == '{') {
- /* found the object_id - let's quickly look for the date */
- if (strncmp(p, "{date{ts{", 9) == 0 && strstr(p, "{duration{") != NULL) {
- /* cool - that was easy */
- *t++ = ',';
- *t++ = ' ';
- /* skip the 9 characters we just found and take the date, ignoring the seconds
- * and replace the silly 'T' in the middle with a space */
- strncpy(t, p + 9, 16);
- if (*(t + 10) == 'T')
- *(t + 10) = ' ';
- t += 16;
- }
- *t = '\0';
- return strdup(tmp);
- }
- }
- return NULL;
-}
-
-/* ultra-simplistic; it doesn't deal with the case when the object_id is
- * split across two chunks. It also doesn't deal with the discrepancy between
- * object_id and dive number as understood by the dive computer */
-static void show_progress(char *buf, const char *what)
-{
- char *val = first_object_id_val(buf);
- if (val) {
-/* let the user know what we are working on */
-#if UEMIS_DEBUG & 8
- fprintf(debugfile, "reading %s\n %s\n %s\n", what, val, buf);
-#endif
- uemis_info(translate("gettextFromC", "%s %s"), what, val);
- free(val);
- }
-}
-
-static void uemis_increased_timeout(int *timeout)
-{
- if (*timeout < UEMIS_MAX_TIMEOUT)
- *timeout += UEMIS_LONG_TIMEOUT;
- usleep(*timeout);
-}
-
-/* send a request to the dive computer and collect the answer */
-static bool uemis_get_answer(const char *path, char *request, int n_param_in,
- int n_param_out, const char **error_text)
-{
- int i = 0, file_length;
- char sb[BUFLEN];
- char fl[13];
- char tmp[101];
- const char *what = translate("gettextFromC", "data");
- bool searching = true;
- bool assembling_mbuf = false;
- bool ismulti = false;
- bool found_answer = false;
- bool more_files = true;
- bool answer_in_mbuf = false;
- char *ans_path;
- int ans_file;
- int timeout = UEMIS_LONG_TIMEOUT;
-
- reqtxt_file = subsurface_open(reqtxt_path, O_RDWR | O_CREAT, 0666);
- if (reqtxt_file == -1) {
- *error_text = "can't open req.txt";
-#ifdef UEMIS_DEBUG
- fprintf(debugfile, "open %s failed with errno %d\n", reqtxt_path, errno);
-#endif
- return false;
- }
- snprintf(sb, BUFLEN, "n%04d12345678", filenr);
- str_append_with_delim(sb, request);
- for (i = 0; i < n_param_in; i++)
- str_append_with_delim(sb, param_buff[i]);
- if (!strcmp(request, "getDivelogs") || !strcmp(request, "getDeviceData") || !strcmp(request, "getDirectory") ||
- !strcmp(request, "getDivespot") || !strcmp(request, "getDive")) {
- answer_in_mbuf = true;
- str_append_with_delim(sb, "");
- if (!strcmp(request, "getDivelogs"))
- what = translate("gettextFromC", "divelog #");
- else if (!strcmp(request, "getDivespot"))
- what = translate("gettextFromC", "divespot #");
- else if (!strcmp(request, "getDive"))
- what = translate("gettextFromC", "details for #");
- }
- str_append_with_delim(sb, "");
- file_length = strlen(sb);
- snprintf(fl, 10, "%08d", file_length - 13);
- memcpy(sb + 5, fl, strlen(fl));
-#if UEMIS_DEBUG & 4
- fprintf(debugfile, "::w req.txt \"%s\"\n", sb);
-#endif
- int written = write(reqtxt_file, sb, strlen(sb));
- if (written == -1 || (size_t)written != strlen(sb)) {
- *error_text = translate("gettextFromC", ERR_FS_SHORT_WRITE);
- return false;
- }
- if (!next_file(number_of_files)) {
- *error_text = translate("gettextFromC", ERR_FS_FULL);
- more_files = false;
- }
- trigger_response(reqtxt_file, "n", filenr, file_length);
- usleep(timeout);
- free(mbuf);
- mbuf = NULL;
- mbuf_size = 0;
- while (searching || assembling_mbuf) {
- if (import_thread_cancelled)
- return false;
- progress_bar_fraction = filenr / (double)UEMIS_MAX_FILES;
- snprintf(fl, 13, "ANS%d.TXT", filenr - 1);
- ans_path = build_filename(build_filename(path, "ANS"), fl);
- ans_file = subsurface_open(ans_path, O_RDONLY, 0666);
- read(ans_file, tmp, 100);
- close(ans_file);
-#if UEMIS_DEBUG & 8
- tmp[100] = '\0';
- fprintf(debugfile, "::t %s \"%s\"\n", ans_path, tmp);
-#elif UEMIS_DEBUG & 4
- char pbuf[4];
- pbuf[0] = tmp[0];
- pbuf[1] = tmp[1];
- pbuf[2] = tmp[2];
- pbuf[3] = 0;
- fprintf(debugfile, "::t %s \"%s...\"\n", ans_path, pbuf);
-#endif
- free(ans_path);
- if (tmp[0] == '1') {
- searching = false;
- if (tmp[1] == 'm') {
- assembling_mbuf = true;
- ismulti = true;
- }
- if (tmp[2] == 'e')
- assembling_mbuf = false;
- if (assembling_mbuf) {
- if (!next_file(number_of_files)) {
- *error_text = translate("gettextFromC", ERR_FS_FULL);
- more_files = false;
- assembling_mbuf = false;
- }
- reqtxt_file = subsurface_open(reqtxt_path, O_RDWR | O_CREAT, 0666);
- if (reqtxt_file == -1) {
- *error_text = "can't open req.txt";
- fprintf(stderr, "open %s failed with errno %d\n", reqtxt_path, errno);
- return false;
- }
- trigger_response(reqtxt_file, "n", filenr, file_length);
- }
- } else {
- if (!next_file(number_of_files - 1)) {
- *error_text = translate("gettextFromC", ERR_FS_FULL);
- more_files = false;
- assembling_mbuf = false;
- searching = false;
- }
- reqtxt_file = subsurface_open(reqtxt_path, O_RDWR | O_CREAT, 0666);
- if (reqtxt_file == -1) {
- *error_text = "can't open req.txt";
- fprintf(stderr, "open %s failed with errno %d\n", reqtxt_path, errno);
- return false;
- }
- trigger_response(reqtxt_file, "r", filenr, file_length);
- uemis_increased_timeout(&timeout);
- }
- if (ismulti && more_files && tmp[0] == '1') {
- int size;
- snprintf(fl, 13, "ANS%d.TXT", assembling_mbuf ? filenr - 2 : filenr - 1);
- ans_path = build_filename(build_filename(path, "ANS"), fl);
- ans_file = subsurface_open(ans_path, O_RDONLY, 0666);
- free(ans_path);
- size = bytes_available(ans_file);
- if (size > 3) {
- char *buf;
- int r;
- if (lseek(ans_file, 3, SEEK_CUR) == -1)
- goto fs_error;
- buf = malloc(size - 2);
- if ((r = read(ans_file, buf, size - 3)) != size - 3) {
- free(buf);
- goto fs_error;
- }
- buf[r] = '\0';
- buffer_add(&mbuf, &mbuf_size, buf);
- show_progress(buf, what);
- free(buf);
- param_buff[3]++;
- }
- close(ans_file);
- timeout = UEMIS_TIMEOUT;
- usleep(UEMIS_TIMEOUT);
- }
- }
- if (more_files) {
- int size = 0, j = 0;
- char *buf = NULL;
-
- if (!ismulti) {
- snprintf(fl, 13, "ANS%d.TXT", filenr - 1);
- ans_path = build_filename(build_filename(path, "ANS"), fl);
- ans_file = subsurface_open(ans_path, O_RDONLY, 0666);
- free(ans_path);
- size = bytes_available(ans_file);
- if (size > 3) {
- int r;
- if (lseek(ans_file, 3, SEEK_CUR) == -1)
- goto fs_error;
- buf = malloc(size - 2);
- if ((r = read(ans_file, buf, size - 3)) != size - 3) {
- free(buf);
- goto fs_error;
- }
- buf[r] = '\0';
- buffer_add(&mbuf, &mbuf_size, buf);
- show_progress(buf, what);
-#if UEMIS_DEBUG & 8
- fprintf(debugfile, "::r %s \"%s\"\n", fl, buf);
-#endif
- }
- size -= 3;
- close(ans_file);
- } else {
- ismulti = false;
- }
-#if UEMIS_DEBUG & 8
- fprintf(debugfile, ":r: %s\n", buf);
-#endif
- if (!answer_in_mbuf)
- for (i = 0; i < n_param_out && j < size; i++)
- param_buff[i] = next_segment(buf, &j, size);
- found_answer = true;
- free(buf);
- }
-#if UEMIS_DEBUG & 1
- for (i = 0; i < n_param_out; i++)
- fprintf(debugfile, "::: %d: %s\n", i, param_buff[i]);
-#endif
- return found_answer;
-fs_error:
- close(ans_file);
- return false;
-}
-
-static bool parse_divespot(char *buf)
-{
- char *bp = buf + 1;
- char *tp = next_token(&bp);
- char *tag, *type, *val;
- char locationstring[1024] = "";
- int divespot, len;
- double latitude = 0.0, longitude = 0.0;
-
- // dive spot got deleted, so fail here
- if (strstr(bp, "deleted{bool{true"))
- return false;
- // not a dive spot, fail here
- if (strcmp(tp, "divespot"))
- return false;
- do
- tag = next_token(&bp);
- while (*tag && strcmp(tag, "object_id"));
- if (!*tag)
- return false;
- next_token(&bp);
- val = next_token(&bp);
- divespot = atoi(val);
- do {
- tag = next_token(&bp);
- type = next_token(&bp);
- val = next_token(&bp);
- if (!strcmp(type, "string") && *val && strcmp(val, " ")) {
- len = strlen(locationstring);
- snprintf(locationstring + len, sizeof(locationstring) - len,
- "%s%s", len ? ", " : "", val);
- } else if (!strcmp(type, "float")) {
- if (!strcmp(tag, "longitude"))
- longitude = ascii_strtod(val, NULL);
- else if (!strcmp(tag, "latitude"))
- latitude = ascii_strtod(val, NULL);
- }
- } while (tag && *tag);
-
- uemis_set_divelocation(divespot, locationstring, longitude, latitude);
- return true;
-}
-
-static char *suit[] = {"", QT_TRANSLATE_NOOP("gettextFromC", "wetsuit"), QT_TRANSLATE_NOOP("gettextFromC", "semidry"), QT_TRANSLATE_NOOP("gettextFromC", "drysuit")};
-static char *suit_type[] = {"", QT_TRANSLATE_NOOP("gettextFromC", "shorty"), QT_TRANSLATE_NOOP("gettextFromC", "vest"), QT_TRANSLATE_NOOP("gettextFromC", "long john"), QT_TRANSLATE_NOOP("gettextFromC", "jacket"), QT_TRANSLATE_NOOP("gettextFromC", "full suit"), QT_TRANSLATE_NOOP("gettextFromC", "2 pcs full suit")};
-static char *suit_thickness[] = {"", "0.5-2mm", "2-3mm", "3-5mm", "5-7mm", "8mm+", QT_TRANSLATE_NOOP("gettextFromC", "membrane")};
-
-static void parse_tag(struct dive *dive, char *tag, char *val)
-{
- /* we can ignore computer_id, water and gas as those are redundant
- * with the binary data and would just get overwritten */
-#if UEMIS_DEBUG & 4
- if (strcmp(tag, "file_content"))
- fprintf(debugfile, "Adding to dive %d : %s = %s\n", dive->dc.diveid, tag, val);
-#endif
- if (!strcmp(tag, "date")) {
- uemis_ts(val, &dive->when);
- } else if (!strcmp(tag, "duration")) {
- uemis_duration(val, &dive->dc.duration);
- } else if (!strcmp(tag, "depth")) {
- uemis_depth(val, &dive->dc.maxdepth);
- } else if (!strcmp(tag, "file_content")) {
- uemis_parse_divelog_binary(val, dive);
- } else if (!strcmp(tag, "altitude")) {
- uemis_get_index(val, &dive->dc.surface_pressure.mbar);
- } else if (!strcmp(tag, "f32Weight")) {
- uemis_get_weight(val, &dive->weightsystem[0], dive->dc.diveid);
- } else if (!strcmp(tag, "notes")) {
- uemis_add_string(val, &dive->notes, " ");
- } else if (!strcmp(tag, "u8DiveSuit")) {
- if (*suit[atoi(val)])
- uemis_add_string(translate("gettextFromC", suit[atoi(val)]), &dive->suit, " ");
- } else if (!strcmp(tag, "u8DiveSuitType")) {
- if (*suit_type[atoi(val)])
- uemis_add_string(translate("gettextFromC", suit_type[atoi(val)]), &dive->suit, " ");
- } else if (!strcmp(tag, "u8SuitThickness")) {
- if (*suit_thickness[atoi(val)])
- uemis_add_string(translate("gettextFromC", suit_thickness[atoi(val)]), &dive->suit, " ");
- } else if (!strcmp(tag, "nickname")) {
- uemis_add_string(val, &dive->buddy, ",");
- }
-}
-
-static bool uemis_delete_dive(device_data_t *devdata, uint32_t diveid)
-{
- struct dive *dive = NULL;
-
- if (devdata->download_table->dives[devdata->download_table->nr - 1]->dc.diveid == diveid) {
- /* we hit the last one in the array */
- dive = devdata->download_table->dives[devdata->download_table->nr - 1];
- } else {
- for (int i = 0; i < devdata->download_table->nr - 1; i++) {
- if (devdata->download_table->dives[i]->dc.diveid == diveid) {
- dive = devdata->download_table->dives[i];
- for (int x = i; x < devdata->download_table->nr - 1; x++)
- devdata->download_table->dives[i] = devdata->download_table->dives[x + 1];
- }
- }
- }
- if (dive) {
- devdata->download_table->dives[--devdata->download_table->nr] = NULL;
- if (dive->tripflag != TF_NONE)
- remove_dive_from_trip(dive, false);
-
- free(dive->dc.sample);
- free((void *)dive->notes);
- free((void *)dive->divemaster);
- free((void *)dive->buddy);
- free((void *)dive->suit);
- taglist_free(dive->tag_list);
- free(dive);
-
- return true;
- }
- return false;
-}
-
-/* This function is called for both divelog and dive information that we get
- * from the SDA (what an insane design, btw). The object_id in the divelog
- * matches the logfilenr in the dive information (which has its own, often
- * different object_id) - we use this as the diveid.
- * We create the dive when parsing the divelog and then later, when we parse
- * the dive information we locate the already created dive via its diveid.
- * Most things just get parsed and converted into our internal data structures,
- * but the dive location API is even more crazy. We just get an id that is an
- * index into yet another data store that we read out later. In order to
- * correctly populate the location and gps data from that we need to remember
- * the addresses of those fields for every dive that references the divespot. */
-static bool process_raw_buffer(device_data_t *devdata, uint32_t deviceid, char *inbuf, char **max_divenr, int *for_dive)
-{
- char *buf = strdup(inbuf);
- char *tp, *bp, *tag, *type, *val;
- bool done = false;
- int inbuflen = strlen(inbuf);
- char *endptr = buf + inbuflen;
- bool is_log = false, is_dive = false;
- char *sections[10];
- size_t s, nr_sections = 0;
- struct dive *dive = NULL;
- char dive_no[10];
-
-#if UEMIS_DEBUG & 8
- fprintf(debugfile, "p_r_b %s\n", inbuf);
-#endif
- if (for_dive)
- *for_dive = -1;
- bp = buf + 1;
- tp = next_token(&bp);
- if (strcmp(tp, "divelog") == 0) {
- /* this is a divelog */
- is_log = true;
- tp = next_token(&bp);
- /* is it a valid entry or nothing ? */
- if (strcmp(tp, "1.0") != 0 || strstr(inbuf, "divelog{1.0{{{{")) {
- free(buf);
- return false;
- }
- } else if (strcmp(tp, "dive") == 0) {
- /* this is dive detail */
- is_dive = true;
- tp = next_token(&bp);
- if (strcmp(tp, "1.0") != 0) {
- free(buf);
- return false;
- }
- } else {
- /* don't understand the buffer */
- free(buf);
- return false;
- }
- if (is_log) {
- dive = uemis_start_dive(deviceid);
- } else {
- /* remember, we don't know if this is the right entry,
- * so first test if this is even a valid entry */
- if (strstr(inbuf, "deleted{bool{true")) {
-#if UEMIS_DEBUG & 2
- fprintf(debugfile, "p_r_b entry deleted\n");
-#endif
- /* oops, this one isn't valid, suggest to try the previous one */
- free(buf);
- return false;
- }
- /* quickhack and workaround to capture the original dive_no
- * I am doing this so I don't have to change the original design
- * but when parsing a dive we never parse the dive number because
- * at the time it's being read the *dive variable is not set because
- * the dive_no tag comes before the object_id in the uemis ans file
- */
- dive_no[0] = '\0';
- char *dive_no_buf = strdup(inbuf);
- char *dive_no_ptr = strstr(dive_no_buf, "dive_no{int{") + 12;
- if (dive_no_ptr) {
- char *dive_no_end = strstr(dive_no_ptr, "{");
- if (dive_no_end) {
- *dive_no_end = '\0';
- strncpy(dive_no, dive_no_ptr, 9);
- dive_no[9] = '\0';
- }
- }
- free(dive_no_buf);
- }
- while (!done) {
- /* the valid buffer ends with a series of delimiters */
- if (bp >= endptr - 2 || !strcmp(bp, "{{"))
- break;
- tag = next_token(&bp);
- /* we also end if we get an empty tag */
- if (*tag == '\0')
- break;
- for (s = 0; s < nr_sections; s++)
- if (!strcmp(tag, sections[s])) {
- tag = next_token(&bp);
- break;
- }
- type = next_token(&bp);
- if (!strcmp(type, "1.0")) {
- /* this tells us the sections that will follow; the tag here
- * is of the format dive-<section> */
- sections[nr_sections] = strchr(tag, '-') + 1;
-#if UEMIS_DEBUG & 4
- fprintf(debugfile, "Expect to find section %s\n", sections[nr_sections]);
-#endif
- if (nr_sections < sizeof(sections) - 1)
- nr_sections++;
- continue;
- }
- val = next_token(&bp);
-#if UEMIS_DEBUG & 8
- if (strlen(val) < 20)
- fprintf(debugfile, "Parsed %s, %s, %s\n*************************\n", tag, type, val);
-#endif
- if (is_log && strcmp(tag, "object_id") == 0) {
- free(*max_divenr);
- *max_divenr = strdup(val);
- dive->dc.diveid = atoi(val);
-#if UEMIS_DEBUG % 2
- fprintf(debugfile, "Adding new dive from log with object_id %d.\n", atoi(val));
-#endif
- } else if (is_dive && strcmp(tag, "logfilenr") == 0) {
- /* this one tells us which dive we are adding data to */
- dive = get_dive_by_uemis_diveid(devdata, atoi(val));
- if (strcmp(dive_no, "0"))
- dive->number = atoi(dive_no);
- if (for_dive)
- *for_dive = atoi(val);
- } else if (!is_log && dive && !strcmp(tag, "divespot_id")) {
- int divespot_id = atoi(val);
- if (divespot_id != -1) {
- dive->dive_site_uuid = create_dive_site("from Uemis", dive->when);
- uemis_mark_divelocation(dive->dc.diveid, divespot_id, dive->dive_site_uuid);
- }
-#if UEMIS_DEBUG & 2
- fprintf(debugfile, "Created divesite %d for diveid : %d\n", dive->dive_site_uuid, dive->dc.diveid);
-#endif
- } else if (dive) {
- parse_tag(dive, tag, val);
- }
- if (is_log && !strcmp(tag, "file_content"))
- done = true;
- /* done with one dive (got the file_content tag), but there could be more:
- * a '{' indicates the end of the record - but we need to see another "{{"
- * later in the buffer to know that the next record is complete (it could
- * be a short read because of some error */
- if (done && ++bp < endptr && *bp != '{' && strstr(bp, "{{")) {
- done = false;
- record_uemis_dive(devdata, dive);
- mark_divelist_changed(true);
- dive = uemis_start_dive(deviceid);
- }
- }
- if (is_log) {
- if (dive->dc.diveid) {
- record_uemis_dive(devdata, dive);
- mark_divelist_changed(true);
- } else { /* partial dive */
- free(dive);
- free(buf);
- return false;
- }
- }
- free(buf);
- return true;
-}
-
-static char *uemis_get_divenr(char *deviceidstr, int force)
-{
- uint32_t deviceid, maxdiveid = 0;
- int i;
- char divenr[10];
- struct dive_table *table;
- deviceid = atoi(deviceidstr);
-
- /*
- * If we are are retrying after a disconnect/reconnect, we
- * will look up the highest dive number in the dives we
- * already have.
- *
- * Also, if "force_download" is true, do this even if we
- * don't have any dives (maxdiveid will remain zero)
- */
- if (force || downloadTable.nr)
- table = &downloadTable;
- else
- table = &dive_table;
-
- for (i = 0; i < table->nr; i++) {
- struct dive *d = table->dives[i];
- struct divecomputer *dc;
- if (!d)
- continue;
- for_each_dc (d, dc) {
- if (dc->model && !strcmp(dc->model, "Uemis Zurich") &&
- (dc->deviceid == 0 || dc->deviceid == 0x7fffffff || dc->deviceid == deviceid) &&
- dc->diveid > maxdiveid)
- maxdiveid = dc->diveid;
- }
- }
- if (max_deleted_seen >= 0 && maxdiveid < (uint32_t)max_deleted_seen) {
- maxdiveid = max_deleted_seen;
-#if UEMIS_DEBUG & 4
- fprintf(debugfile, "overriding max seen with max deleted seen %d\n", max_deleted_seen);
-#endif
- }
- snprintf(divenr, 10, "%d", maxdiveid);
- return strdup(divenr);
-}
-
-#if UEMIS_DEBUG
-static int bufCnt = 0;
-static bool do_dump_buffer_to_file(char *buf, char *prefix)
-{
- char path[100];
- char date[40];
- char obid[40];
- if (!buf)
- return false;
-
- if (strstr(buf, "date{ts{"))
- strncpy(date, strstr(buf, "date{ts{"), sizeof(date));
- else
- strncpy(date, "date{ts{no-date{", sizeof(date));
-
- if (!strstr(buf, "object_id{int{"))
- return false;
-
- strncpy(obid, strstr(buf, "object_id{int{"), sizeof(obid));
- char *ptr1 = strstr(date, "date{ts{");
- char *ptr2 = strstr(obid, "object_id{int{");
- char *pdate = next_token(&ptr1);
- pdate = next_token(&ptr1);
- pdate = next_token(&ptr1);
- char *pobid = next_token(&ptr2);
- pobid = next_token(&ptr2);
- pobid = next_token(&ptr2);
- snprintf(path, sizeof(path), "/%s/%s/UEMIS Dump/%s_%s_Uemis_dump_%s_in_round_%d_%d.txt", home, user, prefix, pdate, pobid, debug_round, bufCnt);
- int dumpFile = subsurface_open(path, O_RDWR | O_CREAT, 0666);
- if (dumpFile == -1)
- return false;
- write(dumpFile, buf, strlen(buf));
- close(dumpFile);
- bufCnt++;
- return true;
-}
-#endif
-
-/* do some more sophisticated calculations here to try and predict if the next round of
- * divelog/divedetail reads will fit into the UEMIS buffer,
- * filenr holds now the uemis filenr after having read several logs including the dive details,
- * fCapacity will five us the average number of files needed for all currently loaded data
- * remember the maximum file usage per dive
- * return : UEMIS_MEM_OK if there is enough memory for a full round
- * UEMIS_MEM_CRITICAL if the memory is good for reading the dive logs
- * UEMIS_MEM_FULL if the memory is exhausted
- */
-static int get_memory(struct dive_table *td, int checkpoint)
-{
- if (td->nr <= 0)
- return UEMIS_MEM_OK;
-
- switch (checkpoint) {
- case UEMIS_CHECK_LOG:
- if (filenr / td->nr > max_mem_used)
- max_mem_used = filenr / td->nr;
-
- /* check if a full block of dive logs + dive details and dive spot fit into the UEMIS buffer */
-#if UEMIS_DEBUG & 4
- fprintf(debugfile, "max_mem_used %d (from td->nr %d) * block_size %d > max_files %d - filenr %d?\n", max_mem_used, td->nr, UEMIS_LOG_BLOCK_SIZE, UEMIS_MAX_FILES, filenr);
-#endif
- if (max_mem_used * UEMIS_LOG_BLOCK_SIZE > UEMIS_MAX_FILES - filenr)
- return UEMIS_MEM_FULL;
- break;
- case UEMIS_CHECK_DETAILS:
- /* check if the next set of dive details and dive spot fit into the UEMIS buffer */
- if ((UEMIS_DIVE_DETAILS_SIZE + UEMIS_SPOT_BLOCK_SIZE) * UEMIS_LOG_BLOCK_SIZE > UEMIS_MAX_FILES - filenr)
- return UEMIS_MEM_FULL;
- break;
- case UEMIS_CHECK_SINGLE_DIVE:
- if (UEMIS_DIVE_DETAILS_SIZE + UEMIS_SPOT_BLOCK_SIZE > UEMIS_MAX_FILES - filenr)
- return UEMIS_MEM_FULL;
- break;
- }
- return UEMIS_MEM_OK;
-}
-
-/* mark a dive as deleted by setting download to false
- * this will be picked up by some cleaning statement later */
-static void do_delete_dives(struct dive_table *td, int idx)
-{
- for (int x = idx; x < td->nr; x++)
- td->dives[x]->downloaded = false;
-}
-
-static bool load_uemis_divespot(const char *mountpath, int divespot_id)
-{
- char divespotnr[10];
- snprintf(divespotnr, sizeof(divespotnr), "%d", divespot_id);
- param_buff[2] = divespotnr;
-#if UEMIS_DEBUG & 2
- fprintf(debugfile, "getDivespot %d\n", divespot_id);
-#endif
- bool success = uemis_get_answer(mountpath, "getDivespot", 3, 0, NULL);
- if (mbuf && success) {
-#if UEMIS_DEBUG & 16
- do_dump_buffer_to_file(mbuf, "Spot");
-#endif
- return parse_divespot(mbuf);
- }
- return false;
-}
-
-static void get_uemis_divespot(const char *mountpath, int divespot_id, struct dive *dive)
-{
- struct dive_site *nds = get_dive_site_by_uuid(dive->dive_site_uuid);
- if (nds && nds->name && strstr(nds->name,"from Uemis")) {
- if (load_uemis_divespot(mountpath, divespot_id)) {
- /* get the divesite based on the diveid, this should give us
- * the newly created site
- */
- struct dive_site *ods = NULL;
- /* with the divesite name we got from parse_dive, that is called on load_uemis_divespot
- * we search all existing divesites if we have one with the same name already. The function
- * returns the first found which is luckily not the newly created.
- */
- (void)get_dive_site_uuid_by_name(nds->name, &ods);
- if (ods) {
- /* if the uuid's are the same, the new site is a duplicate and can be deleted */
- if (nds->uuid != ods->uuid) {
- delete_dive_site(nds->uuid);
- dive->dive_site_uuid = ods->uuid;
- }
- }
- } else {
- /* if we can't load the dive site details, delete the site we
- * created in process_raw_buffer
- */
- delete_dive_site(dive->dive_site_uuid);
- }
- }
-}
-
-static bool get_matching_dive(int idx, char *newmax, int *uemis_mem_status, struct device_data_t *data, const char *mountpath, const char deviceidnr)
-{
- struct dive *dive = data->download_table->dives[idx];
- char log_file_no_to_find[20];
- char dive_to_read_buf[10];
- bool found = false;
- bool found_below = false;
- bool found_above = false;
- int deleted_files = 0;
-
- snprintf(log_file_no_to_find, sizeof(log_file_no_to_find), "logfilenr{int{%d", dive->dc.diveid);
-#if UEMIS_DEBUG & 2
- fprintf(debugfile, "Looking for dive details to go with divelog id %d\n", dive->dc.diveid);
-#endif
- while (!found) {
- if (import_thread_cancelled)
- break;
- snprintf(dive_to_read_buf, sizeof(dive_to_read_buf), "%d", dive_to_read);
- param_buff[2] = dive_to_read_buf;
- (void)uemis_get_answer(mountpath, "getDive", 3, 0, NULL);
-#if UEMIS_DEBUG & 16
- do_dump_buffer_to_file(mbuf, "Dive");
-#endif
- *uemis_mem_status = get_memory(data->download_table, UEMIS_CHECK_SINGLE_DIVE);
- if (*uemis_mem_status == UEMIS_MEM_OK) {
- /* if the memory isn's completely full we can try to read more divelog vs. dive details
- * UEMIS_MEM_CRITICAL means not enough space for a full round but the dive details
- * and the divespots should fit into the UEMIS memory
- * The match we do here is to map the object_id to the logfilenr, we do this
- * by iterating through the last set of loaded divelogs and then find the corresponding
- * dive with the matching logfilenr */
- if (mbuf) {
- if (strstr(mbuf, log_file_no_to_find)) {
- /* we found the logfilenr that matches our object_id from the divelog we were looking for
- * we mark the search successful even if the dive has been deleted. */
- found = true;
- if (strstr(mbuf, "deleted{bool{true") == NULL) {
- process_raw_buffer(data, deviceidnr, mbuf, &newmax, NULL);
- /* remember the last log file number as it is very likely that subsequent dives
- * have the same or higher logfile number.
- * UEMIS unfortunately deletes dives by deleting the dive details and not the logs. */
-#if UEMIS_DEBUG & 2
- d_time = get_dive_date_c_string(dive->when);
- fprintf(debugfile, "Matching divelog id %d from %s with dive details %d\n", dive->dc.diveid, d_time, dive_to_read);
-#endif
- int divespot_id = uemis_get_divespot_id_by_diveid(dive->dc.diveid);
- if (divespot_id >= 0)
- get_uemis_divespot(mountpath, divespot_id, dive);
-
- } else {
- /* in this case we found a deleted file, so let's increment */
-#if UEMIS_DEBUG & 2
- d_time = get_dive_date_c_string(dive->when);
- fprintf(debugfile, "TRY matching divelog id %d from %s with dive details %d but details are deleted\n", dive->dc.diveid, d_time, dive_to_read);
-#endif
- deleted_files++;
- max_deleted_seen = dive_to_read;
- /* mark this log entry as deleted and cleanup later, otherwise we mess up our array */
- dive->downloaded = false;
-#if UEMIS_DEBUG & 2
- fprintf(debugfile, "Deleted dive from %s, with id %d from table -- newmax is %s\n", d_time, dive->dc.diveid, newmax);
-#endif
- }
- } else {
- uint32_t nr_found = 0;
- char *logfilenr = strstr(mbuf, "logfilenr");
- if (logfilenr) {
- sscanf(logfilenr, "logfilenr{int{%u", &nr_found);
- if (nr_found >= dive->dc.diveid || nr_found == 0) {
- found_above = true;
- dive_to_read = dive_to_read - 2;
- } else {
- found_below = true;
- }
- if (dive_to_read < -1)
- dive_to_read = -1;
- }
- }
- }
- if (found_above && found_below)
- break;
- dive_to_read++;
- } else {
- /* At this point the memory of the UEMIS is full, let's cleanup all divelog files were
- * we could not match the details to. */
- do_delete_dives(data->download_table, idx);
- return false;
- }
- }
- /* decrement iDiveToRead by the amount of deleted entries found to assure
- * we are not missing any valid matches when processing subsequent logs */
- dive_to_read = (dive_to_read - deleted_files > 0 ? dive_to_read - deleted_files : 0);
- deleted_files = 0;
- return true;
-}
-
-const char *do_uemis_import(device_data_t *data)
-{
- const char *mountpath = data->devname;
- short force_download = data->force_download;
- char *newmax = NULL;
- int first, start, end = -2;
- uint32_t deviceidnr;
- char *deviceid = NULL;
- const char *result = NULL;
- char *endptr;
- bool success, once = true;
- int match_dive_and_log = 0;
- int uemis_mem_status = UEMIS_MEM_OK;
-
-#if UEMIS_DEBUG
- home = getenv("HOME");
- user = getenv("LOGNAME");
-#endif
- uemis_info(translate("gettextFromC", "Initialise communication"));
- if (!uemis_init(mountpath)) {
- free(reqtxt_path);
- return translate("gettextFromC", "Uemis init failed");
- }
-
- if (!uemis_get_answer(mountpath, "getDeviceId", 0, 1, &result))
- goto bail;
- deviceid = strdup(param_buff[0]);
- deviceidnr = atoi(deviceid);
-
- /* param_buff[0] is still valid */
- if (!uemis_get_answer(mountpath, "initSession", 1, 6, &result))
- goto bail;
-
- uemis_info(translate("gettextFromC", "Start download"));
- if (!uemis_get_answer(mountpath, "processSync", 0, 2, &result))
- goto bail;
-
- /* before starting the long download, check if user pressed cancel */
- if (import_thread_cancelled)
- goto bail;
-
- param_buff[1] = "notempty";
- newmax = uemis_get_divenr(deviceid, force_download);
- if (verbose)
- fprintf(stderr, "Uemis downloader: start looking at dive nr %s", newmax);
-
- first = start = atoi(newmax);
- dive_to_read = first;
- for (;;) {
-#if UEMIS_DEBUG & 2
- debug_round++;
-#endif
-#if UEMIS_DEBUG & 4
- fprintf(debugfile, "d_u_i inner loop start %d end %d newmax %s\n", start, end, newmax);
-#endif
- /* start at the last filled download table index */
- match_dive_and_log = data->download_table->nr;
- sprintf(newmax, "%d", start);
- param_buff[2] = newmax;
- param_buff[3] = 0;
- success = uemis_get_answer(mountpath, "getDivelogs", 3, 0, &result);
- uemis_mem_status = get_memory(data->download_table, UEMIS_CHECK_DETAILS);
- /* first, remove any leading garbage... this needs to start with a '{' */
- char *realmbuf = mbuf;
- if (mbuf)
- realmbuf = strchr(mbuf, '{');
- if (success && realmbuf && uemis_mem_status != UEMIS_MEM_FULL) {
-#if UEMIS_DEBUG & 16
- do_dump_buffer_to_file(realmbuf, "Divelogs");
-#endif
- /* process the buffer we have assembled */
- if (!process_raw_buffer(data, deviceidnr, realmbuf, &newmax, NULL)) {
- /* if no dives were downloaded, mark end appropriately */
- if (end == -2)
- end = start - 1;
- success = false;
- }
- if (once) {
- char *t = first_object_id_val(realmbuf);
- if (t && atoi(t) > start)
- start = atoi(t);
- free(t);
- once = false;
- }
- /* clean up mbuf */
- endptr = strstr(realmbuf, "{{{");
- if (endptr)
- *(endptr + 2) = '\0';
- /* last object_id we parsed */
- sscanf(newmax, "%d", &end);
-#if UEMIS_DEBUG & 4
- fprintf(debugfile, "d_u_i after download and parse start %d end %d newmax %s progress %4.2f\n", start, end, newmax, progress_bar_fraction);
-#endif
- /* The way this works is that I am reading the current dive from what has been loaded during the getDiveLogs call to the UEMIS.
- * As the object_id of the divelog entry and the object_id of the dive details are not necessarily the same, the match needs
- * to happen based on the logfilenr.
- * What the following part does is to optimize the mapping by using
- * dive_to_read = the dive details entry that need to be read using the object_id
- * logFileNoToFind = map the logfilenr of the dive details with the object_id = diveid from the get dive logs */
- for (int i = match_dive_and_log; i < data->download_table->nr; i++) {
- bool success = get_matching_dive(i, newmax, &uemis_mem_status, data, mountpath, deviceidnr);
- if (!success)
- break;
- if (import_thread_cancelled)
- break;
- }
-
- start = end;
-
- /* Do some memory checking here */
- uemis_mem_status = get_memory(data->download_table, UEMIS_CHECK_LOG);
- if (uemis_mem_status != UEMIS_MEM_OK) {
-#if UEMIS_DEBUG & 4
- fprintf(debugfile, "d_u_i out of memory, bailing\n");
-#endif
- break;
- }
- /* if the user clicked cancel, exit gracefully */
- if (import_thread_cancelled) {
-#if UEMIS_DEBUG & 4
- fprintf(debugfile, "d_u_i thread canceled, bailing\n");
-#endif
- break;
- }
- /* if we got an error or got nothing back, stop trying */
- if (!success || !param_buff[3]) {
-#if UEMIS_DEBUG & 4
- fprintf(debugfile, "d_u_i after download nothing found, giving up\n");
-#endif
- break;
- }
-#if UEMIS_DEBUG & 2
- if (debug_round != -1)
- if (debug_round-- == 0) {
- fprintf(debugfile, "d_u_i debug_round is now 0, bailing\n");
- goto bail;
- }
-#endif
- } else {
- /* some of the loading from the UEMIS failed at the divelog level
- * if the memory status = full, we can't even load the divespots and/or buddies.
- * The loaded block of divelogs is useless and all new loaded divelogs need to
- * be deleted from the download_table.
- */
- if (uemis_mem_status == UEMIS_MEM_FULL)
- do_delete_dives(data->download_table, match_dive_and_log);
-#if UEMIS_DEBUG & 4
- fprintf(debugfile, "d_u_i out of memory, bailing instead of processing\n");
-#endif
- break;
- }
- }
-
- if (end == -2 && sscanf(newmax, "%d", &end) != 1)
- end = start;
-
-#if UEMIS_DEBUG & 2
- fprintf(debugfile, "Done: read from object_id %d to %d\n", first, end);
-#endif
-
- /* Regardless on where we are with the memory situation, it's time now
- * to see if we have to clean some dead bodies from our download table */
- next_table_index = 0;
- while (next_table_index < data->download_table->nr) {
- if (!data->download_table->dives[next_table_index]->downloaded)
- uemis_delete_dive(data, data->download_table->dives[next_table_index]->dc.diveid);
- else
- next_table_index++;
- }
-
- if (uemis_mem_status != UEMIS_MEM_OK)
- result = translate("gettextFromC", ERR_FS_ALMOST_FULL);
-
-bail:
- (void)uemis_get_answer(mountpath, "terminateSync", 0, 3, &result);
- if (!strcmp(param_buff[0], "error")) {
- if (!strcmp(param_buff[2], "Out of Memory"))
- result = translate("gettextFromC", ERR_FS_FULL);
- else
- result = param_buff[2];
- }
- free(deviceid);
- free(reqtxt_path);
- if (!data->download_table->nr)
- result = translate("gettextFromC", ERR_NO_FILES);
- return result;
-}
diff --git a/subsurface-core/uemis.c b/subsurface-core/uemis.c
deleted file mode 100644
index 5635d5630..000000000
--- a/subsurface-core/uemis.c
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
- * uemis.c
- *
- * UEMIS SDA file importer
- * AUTHOR: Dirk Hohndel - Copyright 2011
- *
- * Licensed under the MIT license.
- */
-#include <stdio.h>
-#include <string.h>
-
-#include "gettext.h"
-
-#include "dive.h"
-#include "uemis.h"
-#include <libdivecomputer/parser.h>
-#include <libdivecomputer/version.h>
-
-/*
- * following code is based on code found in at base64.sourceforge.net/b64.c
- * AUTHOR: Bob Trower 08/04/01
- * COPYRIGHT: Copyright (c) Trantor Standard Systems Inc., 2001
- * NOTE: This source code may be used as you wish, subject to
- * the MIT license.
- */
-/*
- * Translation Table to decode (created by Bob Trower)
- */
-static const char cd64[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
-
-/*
- * decodeblock -- decode 4 '6-bit' characters into 3 8-bit binary bytes
- */
-static void decodeblock(unsigned char in[4], unsigned char out[3])
-{
- out[0] = (unsigned char)(in[0] << 2 | in[1] >> 4);
- out[1] = (unsigned char)(in[1] << 4 | in[2] >> 2);
- out[2] = (unsigned char)(((in[2] << 6) & 0xc0) | in[3]);
-}
-
-/*
- * decode a base64 encoded stream discarding padding, line breaks and noise
- */
-static void decode(uint8_t *inbuf, uint8_t *outbuf, int inbuf_len)
-{
- uint8_t in[4], out[3], v;
- int i, len, indx_in = 0, indx_out = 0;
-
- while (indx_in < inbuf_len) {
- for (len = 0, i = 0; i < 4 && (indx_in < inbuf_len); i++) {
- v = 0;
- while ((indx_in < inbuf_len) && v == 0) {
- v = inbuf[indx_in++];
- v = ((v < 43 || v > 122) ? 0 : cd64[v - 43]);
- if (v)
- v = ((v == '$') ? 0 : v - 61);
- }
- if (indx_in < inbuf_len) {
- len++;
- if (v)
- in[i] = (v - 1);
- } else
- in[i] = 0;
- }
- if (len) {
- decodeblock(in, out);
- for (i = 0; i < len - 1; i++)
- outbuf[indx_out++] = out[i];
- }
- }
-}
-/* end code from Bob Trower */
-
-/*
- * convert the base64 data blog
- */
-static int uemis_convert_base64(char *base64, uint8_t **data)
-{
- int len, datalen;
-
- len = strlen(base64);
- datalen = (len / 4 + 1) * 3;
- if (datalen < 0x123 + 0x25)
- /* less than header + 1 sample??? */
- fprintf(stderr, "suspiciously short data block %d\n", datalen);
-
- *data = malloc(datalen);
- if (!*data) {
- fprintf(stderr, "Out of memory\n");
- return 0;
- }
- decode((unsigned char *)base64, *data, len);
-
- if (memcmp(*data, "Dive\01\00\00", 7))
- fprintf(stderr, "Missing Dive100 header\n");
-
- return datalen;
-}
-
-struct uemis_helper {
- uint32_t diveid;
- int lbs;
- int divespot;
- int dive_site_uuid;
- struct uemis_helper *next;
-};
-static struct uemis_helper *uemis_helper = NULL;
-
-static struct uemis_helper *uemis_get_helper(uint32_t diveid)
-{
- struct uemis_helper **php = &uemis_helper;
- struct uemis_helper *hp = *php;
-
- while (hp) {
- if (hp->diveid == diveid)
- return hp;
- if (hp->next) {
- hp = hp->next;
- continue;
- }
- php = &hp->next;
- break;
- }
- hp = *php = calloc(1, sizeof(struct uemis_helper));
- hp->diveid = diveid;
- hp->next = NULL;
- return hp;
-}
-
-static void uemis_weight_unit(int diveid, int lbs)
-{
- struct uemis_helper *hp = uemis_get_helper(diveid);
- if (hp)
- hp->lbs = lbs;
-}
-
-int uemis_get_weight_unit(uint32_t diveid)
-{
- struct uemis_helper *hp = uemis_helper;
- while (hp) {
- if (hp->diveid == diveid)
- return hp->lbs;
- hp = hp->next;
- }
- /* odd - we should have found this; default to kg */
- return 0;
-}
-
-void uemis_mark_divelocation(int diveid, int divespot, uint32_t dive_site_uuid)
-{
- struct uemis_helper *hp = uemis_get_helper(diveid);
- hp->divespot = divespot;
- hp->dive_site_uuid = dive_site_uuid;
-}
-
-/* support finding a dive spot based on the diveid */
-int uemis_get_divespot_id_by_diveid(uint32_t diveid)
-{
- struct uemis_helper *hp = uemis_helper;
- while (hp) {
- if (hp->diveid == diveid)
- return hp->divespot;
- hp = hp->next;
- }
- return -1;
-}
-
-void uemis_set_divelocation(int divespot, char *text, double longitude, double latitude)
-{
- struct uemis_helper *hp = uemis_helper;
- while (hp) {
- if (hp->divespot == divespot) {
- struct dive_site *ds = get_dive_site_by_uuid(hp->dive_site_uuid);
- if (ds) {
- ds->name = strdup(text);
- ds->longitude.udeg = round(longitude * 1000000);
- ds->latitude.udeg = round(latitude * 1000000);
- }
- }
- hp = hp->next;
- }
-}
-
-/* Create events from the flag bits and other data in the sample;
- * These bits basically represent what is displayed on screen at sample time.
- * Many of these 'warnings' are way hyper-active and seriously clutter the
- * profile plot - so these are disabled by default
- *
- * we mark all the strings for translation, but we store the untranslated
- * strings and only convert them when displaying them on screen - this way
- * when we write them to the XML file we'll always have the English strings,
- * regardless of locale
- */
-static void uemis_event(struct dive *dive, struct divecomputer *dc, struct sample *sample, uemis_sample_t *u_sample)
-{
- uint8_t *flags = u_sample->flags;
- int stopdepth;
- static int lastndl;
-
- if (flags[1] & 0x01)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Safety stop violation"));
- if (flags[1] & 0x08)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Speed alarm"));
-#if WANT_CRAZY_WARNINGS
- if (flags[1] & 0x06) /* both bits 1 and 2 are a warning */
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Speed warning"));
- if (flags[1] & 0x10)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "pOâ‚‚ green warning"));
-#endif
- if (flags[1] & 0x20)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "pOâ‚‚ ascend warning"));
- if (flags[1] & 0x40)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "pOâ‚‚ ascend alarm"));
- /* flags[2] reflects the deco / time bar
- * flags[3] reflects more display details on deco and pO2 */
- if (flags[4] & 0x01)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Tank pressure info"));
- if (flags[4] & 0x04)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "RGT warning"));
- if (flags[4] & 0x08)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "RGT alert"));
- if (flags[4] & 0x40)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Tank change suggested"));
- if (flags[4] & 0x80)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Depth limit exceeded"));
- if (flags[5] & 0x01)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Max deco time warning"));
- if (flags[5] & 0x04)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Dive time info"));
- if (flags[5] & 0x08)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Dive time alert"));
- if (flags[5] & 0x10)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Marker"));
- if (flags[6] & 0x02)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "No tank data"));
- if (flags[6] & 0x04)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Low battery warning"));
- if (flags[6] & 0x08)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "Low battery alert"));
-/* flags[7] reflects the little on screen icons that remind of previous
- * warnings / alerts - not useful for events */
-
-#if UEMIS_DEBUG & 32
- int i, j;
- for (i = 0; i < 8; i++) {
- printf(" %d: ", 29 + i);
- for (j = 7; j >= 0; j--)
- printf("%c", flags[i] & 1 << j ? '1' : '0');
- }
- printf("\n");
-#endif
- /* now add deco / NDL
- * we don't use events but store this in the sample - that makes much more sense
- * for the way we display this information
- * What we know about the encoding so far:
- * flags[3].bit0 | flags[5].bit1 != 0 ==> in deco
- * flags[0].bit7 == 1 ==> Safety Stop
- * otherwise NDL */
- stopdepth = rel_mbar_to_depth(u_sample->hold_depth, dive);
- if ((flags[3] & 1) | (flags[5] & 2)) {
- /* deco */
- sample->in_deco = true;
- sample->stopdepth.mm = stopdepth;
- sample->stoptime.seconds = u_sample->hold_time * 60;
- sample->ndl.seconds = 0;
- } else if (flags[0] & 128) {
- /* safety stop - distinguished from deco stop by having
- * both ndl and stop information */
- sample->in_deco = false;
- sample->stopdepth.mm = stopdepth;
- sample->stoptime.seconds = u_sample->hold_time * 60;
- sample->ndl.seconds = lastndl;
- } else {
- /* NDL */
- sample->in_deco = false;
- lastndl = sample->ndl.seconds = u_sample->hold_time * 60;
- sample->stopdepth.mm = 0;
- sample->stoptime.seconds = 0;
- }
-#if UEMIS_DEBUG & 32
- printf("%dm:%ds: p_amb_tol:%d surface:%d holdtime:%d holddepth:%d/%d ---> stopdepth:%d stoptime:%d ndl:%d\n",
- sample->time.seconds / 60, sample->time.seconds % 60, u_sample->p_amb_tol, dive->dc.surface_pressure.mbar,
- u_sample->hold_time, u_sample->hold_depth, stopdepth, sample->stopdepth.mm, sample->stoptime.seconds, sample->ndl.seconds);
-#endif
-}
-
-/*
- * parse uemis base64 data blob into struct dive
- */
-void uemis_parse_divelog_binary(char *base64, void *datap)
-{
- int datalen;
- int i;
- uint8_t *data;
- struct sample *sample = NULL;
- uemis_sample_t *u_sample;
- struct dive *dive = datap;
- struct divecomputer *dc = &dive->dc;
- int template, gasoffset;
- uint8_t active = 0;
- char version[5];
-
- datalen = uemis_convert_base64(base64, &data);
- dive->dc.airtemp.mkelvin = C_to_mkelvin((*(uint16_t *)(data + 45)) / 10.0);
- dive->dc.surface_pressure.mbar = *(uint16_t *)(data + 43);
- if (*(uint8_t *)(data + 19))
- dive->dc.salinity = SEAWATER_SALINITY; /* avg grams per 10l sea water */
- else
- dive->dc.salinity = FRESHWATER_SALINITY; /* grams per 10l fresh water */
-
- /* this will allow us to find the last dive read so far from this computer */
- dc->model = strdup("Uemis Zurich");
- dc->deviceid = *(uint32_t *)(data + 9);
- dc->diveid = *(uint16_t *)(data + 7);
- /* remember the weight units used in this dive - we may need this later when
- * parsing the weight */
- uemis_weight_unit(dc->diveid, *(uint8_t *)(data + 24));
- /* dive template in use:
- 0 = air
- 1 = nitrox (B)
- 2 = nitrox (B+D)
- 3 = nitrox (B+T+D)
- uemis cylinder data is insane - it stores seven tank settings in a block
- and the template tells us which of the four groups of tanks we need to look at
- */
- gasoffset = template = *(uint8_t *)(data + 115);
- if (template == 3)
- gasoffset = 4;
- if (template == 0)
- template = 1;
- for (i = 0; i < template; i++) {
- float volume = *(float *)(data + 116 + 25 * (gasoffset + i)) * 1000.0;
- /* uemis always assumes a working pressure of 202.6bar (!?!?) - I first thought
- * it was 3000psi, but testing against all my dives gets me that strange number.
- * Still, that's of course completely bogus and shows they don't get how
- * cylinders are named in non-metric parts of the world...
- * we store the incorrect working pressure to get the SAC calculations "close"
- * but the user will have to correct this manually
- */
- dive->cylinder[i].type.size.mliter = rint(volume);
- dive->cylinder[i].type.workingpressure.mbar = 202600;
- dive->cylinder[i].gasmix.o2.permille = *(uint8_t *)(data + 120 + 25 * (gasoffset + i)) * 10;
- dive->cylinder[i].gasmix.he.permille = 0;
- }
- /* first byte of divelog data is at offset 0x123 */
- i = 0x123;
- u_sample = (uemis_sample_t *)(data + i);
- while ((i <= datalen) && (data[i] != 0 || data[i + 1] != 0)) {
- if (u_sample->active_tank != active) {
- if (u_sample->active_tank >= MAX_CYLINDERS) {
- fprintf(stderr, "got invalid sensor #%d was #%d\n", u_sample->active_tank, active);
- } else {
- active = u_sample->active_tank;
- add_gas_switch_event(dive, dc, u_sample->dive_time, active);
- }
- }
- sample = prepare_sample(dc);
- sample->time.seconds = u_sample->dive_time;
- sample->depth.mm = rel_mbar_to_depth(u_sample->water_pressure, dive);
- sample->temperature.mkelvin = C_to_mkelvin(u_sample->dive_temperature / 10.0);
- sample->sensor = active;
- sample->cylinderpressure.mbar =
- (u_sample->tank_pressure_high * 256 + u_sample->tank_pressure_low) * 10;
- sample->cns = u_sample->cns;
- uemis_event(dive, dc, sample, u_sample);
- finish_sample(dc);
- i += 0x25;
- u_sample++;
- }
- if (sample)
- dive->dc.duration.seconds = sample->time.seconds - 1;
-
- /* get data from the footer */
- char buffer[24];
-
- snprintf(version, sizeof(version), "%1u.%02u", data[18], data[17]);
- add_extra_data(dc, "FW Version", version);
- snprintf(buffer, sizeof(buffer), "%08x", *(uint32_t *)(data + 9));
- add_extra_data(dc, "Serial", buffer);
- snprintf(buffer, sizeof(buffer), "%d", *(uint16_t *)(data + i + 35));
- add_extra_data(dc, "main battery after dive", buffer);
- snprintf(buffer, sizeof(buffer), "%0u:%02u", FRACTION(*(uint16_t *)(data + i + 24), 60));
- add_extra_data(dc, "no fly time", buffer);
- snprintf(buffer, sizeof(buffer), "%0u:%02u", FRACTION(*(uint16_t *)(data + i + 26), 60));
- add_extra_data(dc, "no dive time", buffer);
- snprintf(buffer, sizeof(buffer), "%0u:%02u", FRACTION(*(uint16_t *)(data + i + 28), 60));
- add_extra_data(dc, "desat time", buffer);
- snprintf(buffer, sizeof(buffer), "%u", *(uint16_t *)(data + i + 30));
- add_extra_data(dc, "allowed altitude", buffer);
-
- return;
-}
diff --git a/subsurface-core/uemis.h b/subsurface-core/uemis.h
deleted file mode 100644
index 1758b4b32..000000000
--- a/subsurface-core/uemis.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * defines and prototypes for the uemis Zurich SDA file parser
- */
-
-#ifndef UEMIS_H
-#define UEMIS_H
-
-#include <stdint.h>
-#include "dive.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void uemis_parse_divelog_binary(char *base64, void *divep);
-int uemis_get_weight_unit(uint32_t diveid);
-void uemis_mark_divelocation(int diveid, int divespot, uint32_t dive_site_uuid);
-void uemis_set_divelocation(int divespot, char *text, double longitude, double latitude);
-int uemis_get_divespot_id_by_diveid(uint32_t diveid);
-
-typedef struct
-{
- uint16_t dive_time;
- uint16_t water_pressure; // (in cbar)
- uint16_t dive_temperature; // (in dC)
- uint8_t ascent_speed; // (units unclear)
- uint8_t work_fact;
- uint8_t cold_fact;
- uint8_t bubble_fact;
- uint16_t ascent_time;
- uint16_t ascent_time_opt;
- uint16_t p_amb_tol;
- uint16_t satt;
- uint16_t hold_depth;
- uint16_t hold_time;
- uint8_t active_tank;
- // bloody glib, when compiled for Windows, forces the whole program to use
- // the Windows packing rules. So to avoid problems on Windows (and since
- // only tank_pressure is currently used and that exactly once) I give in and
- // make this silly low byte / high byte 8bit entries
- uint8_t tank_pressure_low; // (in cbar)
- uint8_t tank_pressure_high;
- uint8_t consumption_low; // (units unclear)
- uint8_t consumption_high;
- uint8_t rgt; // (remaining gas time in minutes)
- uint8_t cns;
- uint8_t flags[8];
-} __attribute((packed)) uemis_sample_t;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // UEMIS_H
diff --git a/subsurface-core/units.h b/subsurface-core/units.h
deleted file mode 100644
index 029bb64fa..000000000
--- a/subsurface-core/units.h
+++ /dev/null
@@ -1,280 +0,0 @@
-#ifndef UNITS_H
-#define UNITS_H
-
-#include <math.h>
-#ifndef M_PI
-#define M_PI 3.14159265358979323846
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define O2_IN_AIR 209 // permille
-#define N2_IN_AIR 781
-#define O2_DENSITY 1429 // mg/Liter
-#define N2_DENSITY 1251
-#define HE_DENSITY 179
-#define SURFACE_PRESSURE 1013 // mbar
-#define SURFACE_PRESSURE_STRING "1013"
-#define ZERO_C_IN_MKELVIN 273150 // mKelvin
-
-#ifdef __cplusplus
-#define M_OR_FT(_m, _f) ((prefs.units.length == units::METERS) ? ((_m) * 1000) : (feet_to_mm(_f)))
-#else
-#define M_OR_FT(_m, _f) ((prefs.units.length == METERS) ? ((_m) * 1000) : (feet_to_mm(_f)))
-#endif
-
-/* Salinity is expressed in weight in grams per 10l */
-#define SEAWATER_SALINITY 10300
-#define FRESHWATER_SALINITY 10000
-
-#include <stdint.h>
-/*
- * Some silly typedefs to make our units very explicit.
- *
- * Also, the units are chosen so that values can be expressible as
- * integers, so that we never have FP rounding issues. And they
- * are small enough that converting to/from imperial units doesn't
- * really matter.
- *
- * We also strive to make '0' a meaningless number saying "not
- * initialized", since many values are things that may not have
- * been reported (eg cylinder pressure or temperature from dive
- * computers that don't support them). But sometimes -1 is an even
- * more explicit way of saying "not there".
- *
- * Thus "millibar" for pressure, for example, or "millikelvin" for
- * temperatures. Doing temperatures in celsius or fahrenheit would
- * make for loss of precision when converting from one to the other,
- * and using millikelvin is SI-like but also means that a temperature
- * of '0' is clearly just a missing temperature or cylinder pressure.
- *
- * Also strive to use units that can not possibly be mistaken for a
- * valid value in a "normal" system without conversion. If the max
- * depth of a dive is '20000', you probably didn't convert from mm on
- * output, or if the max depth gets reported as "0.2ft" it was either
- * a really boring dive, or there was some missing input conversion,
- * and a 60-ft dive got recorded as 60mm.
- *
- * Doing these as "structs containing value" means that we always
- * have to explicitly write out those units in order to get at the
- * actual value. So there is hopefully little fear of using a value
- * in millikelvin as Fahrenheit by mistake.
- *
- * We don't actually use these all yet, so maybe they'll change, but
- * I made a number of types as guidelines.
- */
-typedef int64_t timestamp_t;
-
-typedef struct
-{
- uint32_t seconds; // durations up to 68 yrs
-} duration_t;
-
-typedef struct
-{
- int32_t seconds; // offsets up to +/- 34 yrs
-} offset_t;
-
-typedef struct
-{
- int32_t mm;
-} depth_t; // depth to 2000 km
-
-typedef struct
-{
- int32_t mbar; // pressure up to 2000 bar
-} pressure_t;
-
-typedef struct
-{
- uint16_t mbar;
-} o2pressure_t; // pressure up to 65 bar
-
-typedef struct
-{
- int16_t degrees;
-} bearing_t; // compass bearing
-
-typedef struct
-{
- uint32_t mkelvin; // up to 1750 degrees K (temperatures in K are always positive)
-} temperature_t;
-
-typedef struct
-{
- int mliter;
-} volume_t;
-
-typedef struct
-{
- int permille;
-} fraction_t;
-
-typedef struct
-{
- int grams;
-} weight_t;
-
-typedef struct
-{
- int udeg;
-} degrees_t;
-
-static inline double udeg_to_radians(int udeg)
-{
- return (udeg * M_PI) / (1000000.0 * 180.0);
-}
-
-static inline double grams_to_lbs(int grams)
-{
- return grams / 453.6;
-}
-
-static inline int lbs_to_grams(double lbs)
-{
- return rint(lbs * 453.6);
-}
-
-static inline double ml_to_cuft(int ml)
-{
- return ml / 28316.8466;
-}
-
-static inline double cuft_to_l(double cuft)
-{
- return cuft * 28.3168466;
-}
-
-static inline double mm_to_feet(int mm)
-{
- return mm * 0.00328084;
-}
-
-static inline double m_to_mile(int m)
-{
- return m / 1609.344;
-}
-
-static inline unsigned long feet_to_mm(double feet)
-{
- return rint(feet * 304.8);
-}
-
-static inline int to_feet(depth_t depth)
-{
- return rint(mm_to_feet(depth.mm));
-}
-
-static inline double mkelvin_to_C(int mkelvin)
-{
- return (mkelvin - ZERO_C_IN_MKELVIN) / 1000.0;
-}
-
-static inline double mkelvin_to_F(int mkelvin)
-{
- return mkelvin * 9 / 5000.0 - 459.670;
-}
-
-static inline unsigned long F_to_mkelvin(double f)
-{
- return rint((f - 32) * 1000 / 1.8 + ZERO_C_IN_MKELVIN);
-}
-
-static inline unsigned long C_to_mkelvin(double c)
-{
- return rint(c * 1000 + ZERO_C_IN_MKELVIN);
-}
-
-static inline double psi_to_bar(double psi)
-{
- return psi / 14.5037738;
-}
-
-static inline long psi_to_mbar(double psi)
-{
- return rint(psi_to_bar(psi) * 1000);
-}
-
-static inline int to_PSI(pressure_t pressure)
-{
- return rint(pressure.mbar * 0.0145037738);
-}
-
-static inline double bar_to_atm(double bar)
-{
- return bar / SURFACE_PRESSURE * 1000;
-}
-
-static inline double mbar_to_atm(int mbar)
-{
- return (double)mbar / SURFACE_PRESSURE;
-}
-
-static inline int mbar_to_PSI(int mbar)
-{
- pressure_t p = { mbar };
- return to_PSI(p);
-}
-
-/*
- * We keep our internal data in well-specified units, but
- * the input and output may come in some random format. This
- * keeps track of those units.
- */
-/* turns out in Win32 PASCAL is defined as a calling convention */
-#ifdef WIN32
-#undef PASCAL
-#endif
-struct units {
- enum LENGHT {
- METERS,
- FEET
- } length;
- enum VOLUME {
- LITER,
- CUFT
- } volume;
- enum PRESSURE {
- BAR,
- PSI,
- PASCAL
- } pressure;
- enum TEMPERATURE {
- CELSIUS,
- FAHRENHEIT,
- KELVIN
- } temperature;
- enum WEIGHT {
- KG,
- LBS
- } weight;
- enum TIME {
- SECONDS,
- MINUTES
- } vertical_speed_time;
-};
-
-/*
- * We're going to default to SI units for input. Yes,
- * technically the SI unit for pressure is Pascal, but
- * we default to bar (10^5 pascal), which people
- * actually use. Similarly, C instead of Kelvin.
- * And kg instead of g.
- */
-#define SI_UNITS \
- { \
- .length = METERS, .volume = LITER, .pressure = BAR, .temperature = CELSIUS, .weight = KG, .vertical_speed_time = MINUTES \
- }
-
-#define IMPERIAL_UNITS \
- { \
- .length = FEET, .volume = CUFT, .pressure = PSI, .temperature = FAHRENHEIT, .weight = LBS, .vertical_speed_time = MINUTES \
- }
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/subsurface-core/version.c b/subsurface-core/version.c
deleted file mode 100644
index 764e4d2db..000000000
--- a/subsurface-core/version.c
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "ssrf-version.h"
-
-const char *subsurface_git_version(void)
-{
- return GIT_VERSION_STRING;
-}
-
-const char *subsurface_canonical_version(void)
-{
- return CANONICAL_VERSION_STRING;
-}
-
-#ifdef SUBSURFACE_MOBILE
-const char *subsurface_mobile_version(void)
-{
- return MOBILE_VERSION_STRING;
-}
-#endif
diff --git a/subsurface-core/version.h b/subsurface-core/version.h
deleted file mode 100644
index 0a3204bd9..000000000
--- a/subsurface-core/version.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef VERSION_H
-#define VERSION_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-const char *subsurface_git_version(void);
-const char *subsurface_canonical_version(void);
-
-#ifdef SUBSURFACE_MOBILE
-const char *subsurface_mobile_version(void);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/subsurface-core/webservice.h b/subsurface-core/webservice.h
deleted file mode 100644
index 052b8aae7..000000000
--- a/subsurface-core/webservice.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef WEBSERVICE_H
-#define WEBSERVICE_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-//extern void webservice_download_dialog(void);
-//extern bool webservice_request_user_xml(const gchar *, gchar **, unsigned int *, unsigned int *);
-extern int divelogde_upload(char *fn, char **error);
-extern unsigned int download_dialog_parse_response(char *xmldata, unsigned int len);
-
-enum {
- DD_STATUS_OK,
- DD_STATUS_ERROR_CONNECT,
- DD_STATUS_ERROR_ID,
- DD_STATUS_ERROR_PARSE,
-};
-
-
-#ifdef __cplusplus
-}
-#endif
-#endif // WEBSERVICE_H
diff --git a/subsurface-core/windows.c b/subsurface-core/windows.c
deleted file mode 100644
index 58d3beaad..000000000
--- a/subsurface-core/windows.c
+++ /dev/null
@@ -1,454 +0,0 @@
-/* windows.c */
-/* implements Windows specific functions */
-#include <io.h>
-#include "dive.h"
-#include "display.h"
-#undef _WIN32_WINNT
-#define _WIN32_WINNT 0x500
-#include <windows.h>
-#include <shlobj.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <assert.h>
-#include <dirent.h>
-#include <zip.h>
-#include <lmcons.h>
-
-const char non_standard_system_divelist_default_font[] = "Calibri";
-const char current_system_divelist_default_font[] = "Segoe UI";
-const char *system_divelist_default_font = non_standard_system_divelist_default_font;
-double system_divelist_default_font_size = -1;
-
-void subsurface_user_info(struct user_info *user)
-{ /* Encourage use of at least libgit2-0.20 */ }
-
-extern bool isWin7Or8();
-
-void subsurface_OS_pref_setup(void)
-{
- if (isWin7Or8())
- system_divelist_default_font = current_system_divelist_default_font;
-}
-
-bool subsurface_ignore_font(const char *font)
-{
- // if this is running on a recent enough version of Windows and the font
- // passed in is the pre 4.3 default font, ignore it
- if (isWin7Or8() && strcmp(font, non_standard_system_divelist_default_font) == 0)
- return true;
- return false;
-}
-
-/* this function returns the Win32 Roaming path for the current user as UTF-8.
- * it never returns NULL but fallsback to .\ instead!
- * the append argument will append a wchar_t string to the end of the path.
- */
-static const char *system_default_path_append(const wchar_t *append)
-{
- wchar_t wpath[MAX_PATH] = { 0 };
- const char *fname = "system_default_path_append()";
-
- /* obtain the user path via SHGetFolderPathW.
- * this API is deprecated but still supported on modern Win32.
- * fallback to .\ if it fails.
- */
- if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, wpath))) {
- fprintf(stderr, "%s: cannot obtain path!\n", fname);
- wpath[0] = L'.';
- wpath[1] = L'\0';
- }
-
- wcscat(wpath, L"\\Subsurface");
- if (append) {
- wcscat(wpath, L"\\");
- wcscat(wpath, append);
- }
-
- /* attempt to convert the UTF-16 string to UTF-8.
- * resize the buffer and fallback to .\Subsurface if it fails.
- */
- const int wsz = wcslen(wpath);
- const int sz = WideCharToMultiByte(CP_UTF8, 0, wpath, wsz, NULL, 0, NULL, NULL);
- char *path = (char *)malloc(sz + 1);
- if (!sz)
- goto fallback;
- if (WideCharToMultiByte(CP_UTF8, 0, wpath, wsz, path, sz, NULL, NULL)) {
- path[sz] = '\0';
- return path;
- }
-
-fallback:
- fprintf(stderr, "%s: cannot obtain path as UTF-8!\n", fname);
- const char *local = ".\\Subsurface";
- const int len = strlen(local) + 1;
- path = (char *)realloc(path, len);
- memset(path, 0, len);
- strcat(path, local);
- return path;
-}
-
-/* by passing NULL to system_default_path_append() we obtain the pure path.
- * '\' not included at the end.
- */
-const char *system_default_directory(void)
-{
- static const char *path = NULL;
- if (!path)
- path = system_default_path_append(NULL);
- return path;
-}
-
-/* obtain the Roaming path and append "\\<USERNAME>.xml" to it.
- */
-const char *system_default_filename(void)
-{
- static wchar_t filename[UNLEN + 5] = { 0 };
- if (!*filename) {
- wchar_t username[UNLEN + 1] = { 0 };
- DWORD username_len = UNLEN + 1;
- GetUserNameW(username, &username_len);
- wcscat(filename, username);
- wcscat(filename, L".xml");
- }
- static const char *path = NULL;
- if (!path)
- path = system_default_path_append(filename);
- return path;
-}
-
-int enumerate_devices(device_callback_t callback, void *userdata, int dc_type)
-{
- int index = -1;
- DWORD i;
- if (dc_type != DC_TYPE_UEMIS) {
- // Open the registry key.
- HKEY hKey;
- LONG rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_QUERY_VALUE, &hKey);
- if (rc != ERROR_SUCCESS) {
- return -1;
- }
-
- // Get the number of values.
- DWORD count = 0;
- rc = RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL);
- if (rc != ERROR_SUCCESS) {
- RegCloseKey(hKey);
- return -1;
- }
- for (i = 0; i < count; ++i) {
- // Get the value name, data and type.
- char name[512], data[512];
- DWORD name_len = sizeof(name);
- DWORD data_len = sizeof(data);
- DWORD type = 0;
- rc = RegEnumValue(hKey, i, name, &name_len, NULL, &type, (LPBYTE)data, &data_len);
- if (rc != ERROR_SUCCESS) {
- RegCloseKey(hKey);
- return -1;
- }
-
- // Ignore non-string values.
- if (type != REG_SZ)
- continue;
-
- // Prevent a possible buffer overflow.
- if (data_len >= sizeof(data)) {
- RegCloseKey(hKey);
- return -1;
- }
-
- // Null terminate the string.
- data[data_len] = 0;
-
- callback(data, userdata);
- index++;
- if (is_default_dive_computer_device(name))
- index = i;
- }
-
- RegCloseKey(hKey);
- }
- if (dc_type != DC_TYPE_SERIAL) {
- int i;
- int count_drives = 0;
- const int bufdef = 512;
- const char *dlabels[] = {"UEMISSDA", NULL};
- char bufname[bufdef], bufval[bufdef], *p;
- DWORD bufname_len;
-
- /* add drive letters that match labels */
- memset(bufname, 0, bufdef);
- bufname_len = bufdef;
- if (GetLogicalDriveStringsA(bufname_len, bufname)) {
- p = bufname;
-
- while (*p) {
- memset(bufval, 0, bufdef);
- if (GetVolumeInformationA(p, bufval, bufdef, NULL, NULL, NULL, NULL, 0)) {
- for (i = 0; dlabels[i] != NULL; i++)
- if (!strcmp(bufval, dlabels[i])) {
- char data[512];
- snprintf(data, sizeof(data), "%s (%s)", p, dlabels[i]);
- callback(data, userdata);
- if (is_default_dive_computer_device(p))
- index = count_drives;
- count_drives++;
- }
- }
- p = &p[strlen(p) + 1];
- }
- if (count_drives == 1) /* we found exactly one Uemis "drive" */
- index = 0; /* make it the selected "device" */
- }
- }
- return index;
-}
-
-/* this function converts a utf-8 string to win32's utf-16 2 byte string.
- * the caller function should manage the allocated memory.
- */
-static wchar_t *utf8_to_utf16_fl(const char *utf8, char *file, int line)
-{
- assert(utf8 != NULL);
- assert(file != NULL);
- assert(line);
- /* estimate buffer size */
- const int sz = strlen(utf8) + 1;
- wchar_t *utf16 = (wchar_t *)malloc(sizeof(wchar_t) * sz);
- if (!utf16) {
- fprintf(stderr, "%s:%d: %s %d.", file, line, "cannot allocate buffer of size", sz);
- return NULL;
- }
- if (MultiByteToWideChar(CP_UTF8, 0, utf8, -1, utf16, sz))
- return utf16;
- fprintf(stderr, "%s:%d: %s", file, line, "cannot convert string.");
- free((void *)utf16);
- return NULL;
-}
-
-#define utf8_to_utf16(s) utf8_to_utf16_fl(s, __FILE__, __LINE__)
-
-/* bellow we provide a set of wrappers for some I/O functions to use wchar_t.
- * on win32 this solves the issue that we need paths to be utf-16 encoded.
- */
-int subsurface_rename(const char *path, const char *newpath)
-{
- int ret = -1;
- if (!path || !newpath)
- return ret;
-
- wchar_t *wpath = utf8_to_utf16(path);
- wchar_t *wnewpath = utf8_to_utf16(newpath);
-
- if (wpath && wnewpath)
- ret = _wrename(wpath, wnewpath);
- free((void *)wpath);
- free((void *)wnewpath);
- return ret;
-}
-
-// if the QDir based rename fails, we try this one
-int subsurface_dir_rename(const char *path, const char *newpath)
-{
- // check if the folder exists
- BOOL exists = FALSE;
- DWORD attrib = GetFileAttributes(path);
- if (attrib != INVALID_FILE_ATTRIBUTES && attrib & FILE_ATTRIBUTE_DIRECTORY)
- exists = TRUE;
- if (!exists && verbose) {
- fprintf(stderr, "folder not found or path is not a folder: %s\n", path);
- return EXIT_FAILURE;
- }
-
- // list of error codes:
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx
- DWORD errorCode;
-
- // if this fails something has already obatained (more) exclusive access to the folder
- HANDLE h = CreateFile(path, GENERIC_WRITE, FILE_SHARE_WRITE |
- FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
- if (h == INVALID_HANDLE_VALUE) {
- errorCode = GetLastError();
- if (verbose)
- fprintf(stderr, "cannot obtain exclusive write access for folder: %u\n", (unsigned int)errorCode );
- return EXIT_FAILURE;
- } else {
- if (verbose)
- fprintf(stderr, "exclusive write access obtained...closing handle!");
- CloseHandle(h);
-
- // attempt to rename
- BOOL result = MoveFile(path, newpath);
- if (!result) {
- errorCode = GetLastError();
- if (verbose)
- fprintf(stderr, "rename failed: %u\n", (unsigned int)errorCode);
- return EXIT_FAILURE;
- }
- if (verbose > 1)
- fprintf(stderr, "folder rename success: %s ---> %s\n", path, newpath);
- }
- return EXIT_SUCCESS;
-}
-
-int subsurface_open(const char *path, int oflags, mode_t mode)
-{
- int ret = -1;
- if (!path)
- return ret;
- wchar_t *wpath = utf8_to_utf16(path);
- if (wpath)
- ret = _wopen(wpath, oflags, mode);
- free((void *)wpath);
- return ret;
-}
-
-FILE *subsurface_fopen(const char *path, const char *mode)
-{
- FILE *ret = NULL;
- if (!path)
- return ret;
- wchar_t *wpath = utf8_to_utf16(path);
- if (wpath) {
- const int len = strlen(mode);
- wchar_t wmode[len + 1];
- for (int i = 0; i < len; i++)
- wmode[i] = (wchar_t)mode[i];
- wmode[len] = 0;
- ret = _wfopen(wpath, wmode);
- }
- free((void *)wpath);
- return ret;
-}
-
-/* here we return a void pointer instead of _WDIR or DIR pointer */
-void *subsurface_opendir(const char *path)
-{
- _WDIR *ret = NULL;
- if (!path)
- return ret;
- wchar_t *wpath = utf8_to_utf16(path);
- if (wpath)
- ret = _wopendir(wpath);
- free((void *)wpath);
- return (void *)ret;
-}
-
-int subsurface_access(const char *path, int mode)
-{
- int ret = -1;
- if (!path)
- return ret;
- wchar_t *wpath = utf8_to_utf16(path);
- if (wpath)
- ret = _waccess(wpath, mode);
- free((void *)wpath);
- return ret;
-}
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-struct zip *subsurface_zip_open_readonly(const char *path, int flags, int *errorp)
-{
-#if defined(LIBZIP_VERSION_MAJOR)
- /* libzip 0.10 has zip_fdopen, let's use it since zip_open doesn't have a
- * wchar_t version */
- int fd = subsurface_open(path, O_RDONLY | O_BINARY, 0);
- struct zip *ret = zip_fdopen(fd, flags, errorp);
- if (!ret)
- close(fd);
- return ret;
-#else
- return zip_open(path, flags, errorp);
-#endif
-}
-
-int subsurface_zip_close(struct zip *zip)
-{
- return zip_close(zip);
-}
-
-/* win32 console */
-static struct {
- bool allocated;
- UINT cp;
- FILE *out, *err;
-} console_desc;
-
-void subsurface_console_init(bool dedicated)
-{
- (void)console_desc;
- /* if this is a console app already, do nothing */
-#ifndef WIN32_CONSOLE_APP
- /* just in case of multiple calls */
- memset((void *)&console_desc, 0, sizeof(console_desc));
- /* the AttachConsole(..) call can be used to determine if the parent process
- * is a terminal. if it succeeds, there is no need for a dedicated console
- * window and we don't need to call the AllocConsole() function. on the other
- * hand if the user has set the 'dedicated' flag to 'true' and if AttachConsole()
- * has failed, we create a dedicated console window.
- */
- console_desc.allocated = AttachConsole(ATTACH_PARENT_PROCESS);
- if (console_desc.allocated)
- dedicated = false;
- if (!console_desc.allocated && dedicated)
- console_desc.allocated = AllocConsole();
- if (!console_desc.allocated)
- return;
-
- console_desc.cp = GetConsoleCP();
- SetConsoleOutputCP(CP_UTF8); /* make the ouput utf8 */
-
- /* set some console modes; we don't need to reset these back.
- * ENABLE_EXTENDED_FLAGS = 0x0080, ENABLE_QUICK_EDIT_MODE = 0x0040 */
- HANDLE h_in = GetStdHandle(STD_INPUT_HANDLE);
- if (h_in) {
- SetConsoleMode(h_in, 0x0080 | 0x0040);
- CloseHandle(h_in);
- }
-
- /* dedicated only; disable the 'x' button as it will close the main process as well */
- HWND h_cw = GetConsoleWindow();
- if (h_cw && dedicated) {
- SetWindowTextA(h_cw, "Subsurface Console");
- HMENU h_menu = GetSystemMenu(h_cw, 0);
- if (h_menu) {
- EnableMenuItem(h_menu, SC_CLOSE, MF_BYCOMMAND | MF_DISABLED);
- DrawMenuBar(h_cw);
- }
- SetConsoleCtrlHandler(NULL, TRUE); /* disable the CTRL handler */
- }
-
- /* redirect; on win32, CON is a reserved pipe target, like NUL */
- console_desc.out = freopen("CON", "w", stdout);
- console_desc.err = freopen("CON", "w", stderr);
- if (!dedicated)
- puts(""); /* add an empty line */
-#endif
-}
-
-void subsurface_console_exit(void)
-{
-#ifndef WIN32_CONSOLE_APP
- if (!console_desc.allocated)
- return;
-
- /* close handles */
- if (console_desc.out)
- fclose(console_desc.out);
- if (console_desc.err)
- fclose(console_desc.err);
-
- /* reset code page and free */
- SetConsoleOutputCP(console_desc.cp);
- FreeConsole();
-#endif
-}
-
-bool subsurface_user_is_root()
-{
- /* FIXME: Detect admin rights */
- return (false);
-}
diff --git a/subsurface-core/windowtitleupdate.cpp b/subsurface-core/windowtitleupdate.cpp
deleted file mode 100644
index 963455f1d..000000000
--- a/subsurface-core/windowtitleupdate.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-#include "windowtitleupdate.h"
-
-WindowTitleUpdate *WindowTitleUpdate::m_instance = NULL;
-
-WindowTitleUpdate::WindowTitleUpdate(QObject *parent) : QObject(parent)
-{
- Q_ASSERT_X(m_instance == NULL, "WindowTitleUpdate", "WindowTitleUpdate recreated!");
-
- m_instance = this;
-}
-
-WindowTitleUpdate *WindowTitleUpdate::instance()
-{
- return m_instance;
-}
-
-WindowTitleUpdate::~WindowTitleUpdate()
-{
- m_instance = NULL;
-}
-
-void WindowTitleUpdate::emitSignal()
-{
- emit updateTitle();
-}
-
-extern "C" void updateWindowTitle()
-{
- WindowTitleUpdate *wt = WindowTitleUpdate::instance();
- if (wt)
- wt->emitSignal();
-}
diff --git a/subsurface-core/windowtitleupdate.h b/subsurface-core/windowtitleupdate.h
deleted file mode 100644
index 8650e5868..000000000
--- a/subsurface-core/windowtitleupdate.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef WINDOWTITLEUPDATE_H
-#define WINDOWTITLEUPDATE_H
-
-#include <QObject>
-
-class WindowTitleUpdate : public QObject
-{
- Q_OBJECT
-public:
- explicit WindowTitleUpdate(QObject *parent = 0);
- ~WindowTitleUpdate();
- static WindowTitleUpdate *instance();
- void emitSignal();
-signals:
- void updateTitle();
-private:
- static WindowTitleUpdate *m_instance;
-};
-
-#endif // WINDOWTITLEUPDATE_H
diff --git a/subsurface-core/worldmap-options.h b/subsurface-core/worldmap-options.h
deleted file mode 100644
index 177443563..000000000
--- a/subsurface-core/worldmap-options.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef WORLDMAP_OPTIONS_H
-#define WORLDMAP_OPTIONS_H
-
-const char *map_options = "center: new google.maps.LatLng(0,0),\n\tzoom: 3,\n\tminZoom: 2,\n\tmapTypeId: google.maps.MapTypeId.SATELLITE\n\t";
-const char *css = "\n\thtml { height: 100% }\n\tbody { height: 100%; margin: 0; padding: 0 }\n\t#map-canvas { height: 100% }\n";
-
-#endif // WORLDMAP-OPTIONS_H
diff --git a/subsurface-core/worldmap-save.c b/subsurface-core/worldmap-save.c
deleted file mode 100644
index e7e8bcc30..000000000
--- a/subsurface-core/worldmap-save.c
+++ /dev/null
@@ -1,117 +0,0 @@
-// Clang has a bug on zero-initialization of C structs.
-#pragma clang diagnostic ignored "-Wmissing-field-initializers"
-
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-
-#include "dive.h"
-#include "membuffer.h"
-#include "save-html.h"
-#include "worldmap-save.h"
-#include "worldmap-options.h"
-#include "gettext.h"
-
-char *getGoogleApi()
-{
- /* google maps api auth*/
- return "https://maps.googleapis.com/maps/api/js?key=AIzaSyDzo9PWsqYDDSddVswg_13rpD9oH_dLuoQ";
-}
-
-void writeMarkers(struct membuffer *b, const bool selected_only)
-{
- int i, dive_no = 0;
- struct dive *dive;
- char pre[1000], post[1000];
-
- for_each_dive (i, dive) {
- if (selected_only) {
- if (!dive->selected)
- continue;
- }
- struct dive_site *ds = get_dive_site_for_dive(dive);
- if (!ds || !dive_site_has_gps_location(ds))
- continue;
- put_degrees(b, ds->latitude, "temp = new google.maps.Marker({position: new google.maps.LatLng(", "");
- put_degrees(b, ds->longitude, ",", ")});\n");
- put_string(b, "markers.push(temp);\ntempinfowindow = new google.maps.InfoWindow({content: '<div id=\"content\">'+'<div id=\"siteNotice\">'+'</div>'+'<div id=\"bodyContent\">");
- snprintf(pre, sizeof(pre), "<p>%s ", translate("gettextFromC", "Date:"));
- put_HTML_date(b, dive, pre, "</p>");
- snprintf(pre, sizeof(pre), "<p>%s ", translate("gettextFromC", "Time:"));
- put_HTML_time(b, dive, pre, "</p>");
- snprintf(pre, sizeof(pre), "<p>%s ", translate("gettextFromC", "Duration:"));
- snprintf(post, sizeof(post), " %s</p>", translate("gettextFromC", "min"));
- put_duration(b, dive->duration, pre, post);
- put_string(b, "<p> ");
- put_HTML_quoted(b, translate("gettextFromC", "Max. depth:"));
- put_HTML_depth(b, dive, " ", "</p>");
- put_string(b, "<p> ");
- put_HTML_quoted(b, translate("gettextFromC", "Air temp.:"));
- put_HTML_airtemp(b, dive, " ", "</p>");
- put_string(b, "<p> ");
- put_HTML_quoted(b, translate("gettextFromC", "Water temp.:"));
- put_HTML_watertemp(b, dive, " ", "</p>");
- snprintf(pre, sizeof(pre), "<p>%s <b>", translate("gettextFromC", "Location:"));
- put_string(b, pre);
- put_HTML_quoted(b, get_dive_location(dive));
- put_string(b, "</b></p>");
- snprintf(pre, sizeof(pre), "<p> %s ", translate("gettextFromC", "Notes:"));
- put_HTML_notes(b, dive, pre, " </p>");
- put_string(b, "</p>'+'</div>'+'</div>'});\ninfowindows.push(tempinfowindow);\n");
- put_format(b, "google.maps.event.addListener(markers[%d], 'mouseover', function() {\ninfowindows[%d].open(map,markers[%d]);}", dive_no, dive_no, dive_no);
- put_format(b, ");google.maps.event.addListener(markers[%d], 'mouseout', function() {\ninfowindows[%d].close();});\n", dive_no, dive_no);
- dive_no++;
- }
-}
-
-void insert_html_header(struct membuffer *b)
-{
- put_string(b, "<!DOCTYPE html>\n<html>\n<head>\n");
- put_string(b, "<meta name=\"viewport\" content=\"initial-scale=1.0, user-scalable=no\" />\n<title>World Map</title>\n");
- put_string(b, "<meta charset=\"UTF-8\">");
-}
-
-void insert_css(struct membuffer *b)
-{
- put_format(b, "<style type=\"text/css\">%s</style>\n", css);
-}
-
-void insert_javascript(struct membuffer *b, const bool selected_only)
-{
- put_string(b, "<script type=\"text/javascript\" src=\"");
- put_string(b, getGoogleApi());
- put_string(b, "&amp;sensor=false\">\n</script>\n<script type=\"text/javascript\">\nvar map;\n");
- put_format(b, "function initialize() {\nvar mapOptions = {\n\t%s,", map_options);
- put_string(b, "rotateControl: false,\n\tstreetViewControl: false,\n\tmapTypeControl: false\n};\n");
- put_string(b, "map = new google.maps.Map(document.getElementById(\"map-canvas\"),mapOptions);\nvar markers = new Array();");
- put_string(b, "\nvar infowindows = new Array();\nvar temp;\nvar tempinfowindow;\n");
- writeMarkers(b, selected_only);
- put_string(b, "\nfor(var i=0;i<markers.length;i++)\n\tmarkers[i].setMap(map);\n}\n");
- put_string(b, "google.maps.event.addDomListener(window, 'load', initialize);</script>\n");
-}
-
-void export(struct membuffer *b, const bool selected_only)
-{
- insert_html_header(b);
- insert_css(b);
- insert_javascript(b, selected_only);
- put_string(b, "\t</head>\n<body>\n<div id=\"map-canvas\"></div>\n</body>\n</html>");
-}
-
-void export_worldmap_HTML(const char *file_name, const bool selected_only)
-{
- FILE *f;
-
- struct membuffer buf = { 0 };
- export(&buf, selected_only);
-
- f = subsurface_fopen(file_name, "w+");
- if (!f) {
- report_error(translate("gettextFromC", "Can't open file %s"), file_name);
- } else {
- flush_buffer(&buf, f); /*check for writing errors? */
- fclose(f);
- }
- free_buffer(&buf);
-}
diff --git a/subsurface-core/worldmap-save.h b/subsurface-core/worldmap-save.h
deleted file mode 100644
index 102ea40e5..000000000
--- a/subsurface-core/worldmap-save.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef WORLDMAP_SAVE_H
-#define WORLDMAP_SAVE_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern void export_worldmap_HTML(const char *file_name, const bool selected_only);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif