summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/configuredivecomputerthreads.cpp1
-rw-r--r--core/datatrak.c354
-rw-r--r--core/datatrak.h73
-rw-r--r--core/exif.cpp17
-rw-r--r--core/file.c5
-rw-r--r--core/file.h2
-rw-r--r--core/gpslocation.cpp5
-rw-r--r--core/gpslocation.h1
-rw-r--r--core/helpers.h2
-rw-r--r--core/prefs-macros.h7
-rw-r--r--core/qthelper.cpp36
-rw-r--r--core/qthelper.h1
-rw-r--r--core/subsurface-qt/SettingsObjectWrapper.cpp17
-rw-r--r--core/subsurface-qt/SettingsObjectWrapper.h4
-rw-r--r--core/units.h25
-rw-r--r--desktop-widgets/divelistview.cpp7
-rw-r--r--desktop-widgets/divepicturewidget.cpp2
-rw-r--r--desktop-widgets/modeldelegates.cpp1
-rw-r--r--desktop-widgets/preferences/preferences_units.cpp4
-rw-r--r--desktop-widgets/preferences/preferences_units.ui37
-rw-r--r--desktop-widgets/simplewidgets.cpp8
-rw-r--r--desktop-widgets/subsurfacewebservices.cpp3
-rw-r--r--desktop-widgets/tab-widgets/maintab.cpp2
-rwxr-xr-xdives/images/data_after_EOI.jpgbin0 -> 715263 bytes
-rw-r--r--dives/images/wreck.jpg (renamed from wreck.jpg)bin116727 -> 116727 bytes
-rw-r--r--qt-models/divetripmodel.cpp16
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/testparse.cpp22
-rw-r--r--tests/testparse.h1
-rw-r--r--tests/testpicture.cpp40
-rw-r--r--xslt/uddf-export.xslt41
-rw-r--r--xslt/uddf.xslt25
32 files changed, 425 insertions, 335 deletions
diff --git a/core/configuredivecomputerthreads.cpp b/core/configuredivecomputerthreads.cpp
index 39c3b5c63..ff592b717 100644
--- a/core/configuredivecomputerthreads.cpp
+++ b/core/configuredivecomputerthreads.cpp
@@ -373,7 +373,6 @@ static dc_status_t read_ostc4_settings(dc_device_t *device, DeviceDetails *m_dev
dc_event_progress_t progress;
progress.current = 0;
progress.maximum = 23;
- unsigned char hardware[1];
EMIT_PROGRESS();
diff --git a/core/datatrak.c b/core/datatrak.c
index eaf78b3b7..4896974e2 100644
--- a/core/datatrak.c
+++ b/core/datatrak.c
@@ -8,19 +8,17 @@
#include <stdio.h>
#include <string.h>
#include <time.h>
-
+#include "gettext.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);
+#include "file.h"
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;
+unsigned long tmp_4bytes, maxbuf;
static unsigned int two_bytes_to_int(unsigned char x, unsigned char y)
{
@@ -89,116 +87,87 @@ static char *to_utf8(unsigned char *in_string)
}
/*
- * Subsurface sample structure doesn't support the flags and alarms in the dt .log
- * so will treat them as dc events.
+ * Reads the header of a datatrak buffer and returns the number of
+ * dives; zero on error (meaning this isn't a datatrak file).
+ * All other info in the header is useless for Subsurface.
*/
-static struct sample *dtrak_profile(struct dive *dt_dive, FILE *archivo)
+static int read_file_header(unsigned char *buffer)
{
- 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, QT_TRANSLATE_NOOP("gettextFromC", "rbt"));
- if (byte[2] != 0)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "ascent"));
- if (byte[3] != 0)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "ceiling"));
- if (byte[4] != 0)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "workload"));
- if (byte[5] != 0)
- add_event(dc, sample->time.seconds, 0, 0, 0, QT_TRANSLATE_NOOP("gettextFromC", "transmitter"));
- if (j == 3) {
- read_bytes(1);
- if (is_O2) {
- read_bytes(1);
- o2percent = tmp_1byte;
- }
- j = 0;
- }
- free(byte);
+ int n = 0;
- // 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;
+ if (two_bytes_to_int(buffer[0], buffer[1]) == 0xA100)
+ n = two_bytes_to_int(buffer[7], buffer[6]);
+ return n;
}
/*
- * Reads the header of a file and returns the header struct
- * If it's not a DATATRAK file returns header zero initalized
+ * Fills a device_data_t structure based on the info from g_models table, using
+ * the dc's model number as start point.
+ * Returns libdc's equivalent model number (also from g_models) or zero if
+ * this a manual dive.
*/
-static dtrakheader read_file_header(FILE *archivo)
+static int dtrak_prepare_data(int model, device_data_t *dev_data)
{
- dtrakheader fileheader = { 0 };
- const short headerbytes = 12;
- unsigned char *lector = (unsigned char *)malloc(headerbytes);
+ dc_descriptor_t *d = NULL;
+ int i = 0;
- 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 dive log"));
- 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;
+ while (model != g_models[i].model_num && g_models[i].model_num != 0xEE)
+ i++;
+ dev_data->model = copy_string(g_models[i].name);
+ sscanf(g_models[i].name,"%m[A-Za-z] ", &dev_data->vendor);
+ dev_data->product = copy_string(strchr(g_models[i].name, ' ') + 1);
+
+ d = get_descriptor(g_models[i].type, g_models[i].libdc_num);
+ if (d)
+ dev_data->descriptor = d;
+ else
+ return 0;
+ return g_models[i].libdc_num;
}
-#define CHECK(_func, _val) if ((_func) != (_val)) goto bail
+/*
+ * Reads the size of a datatrak profile from actual position in buffer *ptr,
+ * zero padds it with a faked header and inserts the model number for
+ * libdivecomputer parsing. Puts the completed buffer in a pre-allocated
+ * compl_buffer, and returns status.
+ */
+static dc_status_t dt_libdc_buffer(unsigned char *ptr, int prf_length, int dc_model, unsigned char *compl_buffer)
+{
+ if (compl_buffer == NULL)
+ return DC_STATUS_NOMEMORY;
+ compl_buffer[3] = (unsigned char) dc_model;
+ memcpy(compl_buffer + 18, ptr, prf_length);
+ return DC_STATUS_SUCCESS;
+}
/*
- * Parses the dive extracting its data and filling a subsurface's dive structure
+ * Parses a mem buffer extracting its data and filling a subsurface's dive structure.
+ * Returns a pointer to last position in buffer, or NULL on failure.
*/
-bool dt_dive_parser(FILE *archivo, struct dive *dt_dive)
+unsigned char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive)
{
- unsigned char n;
- int profile_length;
+ int rc, profile_length, n = 0, libdc_model;
char *tmp_notes_str = NULL;
unsigned char *tmp_string1 = NULL,
*locality = NULL,
- *dive_point = NULL;
+ *dive_point = NULL,
+ *compl_buffer,
+ *membuf = runner;
char buffer[1024];
- struct divecomputer *dc = &dt_dive->dc;
-
- is_nitrox = is_O2 = is_SCR = 0;
+ device_data_t *devdata = calloc(1, sizeof(device_data_t));
/*
- * Parse byte to byte till next dive entry
+ * Reset global variables for new dive
*/
- 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);
+ is_nitrox = is_O2 = is_SCR = 0;
/*
- * Found dive header 0xA000, verify second byte
+ * Parse byte to byte till next dive entry
*/
- 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;
+ while (membuf[0] != 0xA0 || membuf[1] != 0x00) {
+ JUMP(membuf, 1);
}
+ JUMP(membuf, 2);
/*
* Begin parsing
@@ -206,12 +175,10 @@ bool dt_dive_parser(FILE *archivo, struct dive *dt_dive)
*/
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);
/*
@@ -345,7 +312,7 @@ bool dt_dive_parser(FILE *archivo, struct dive *dt_dive)
/*
* 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
+ * Dtrak doesn't record init and end pressures, but consumed bar, so let's
* init a default pressure of 200 bar.
*/
read_bytes(2);
@@ -448,7 +415,6 @@ bool dt_dive_parser(FILE *archivo, struct dive *dt_dive)
taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "search")));
free(byte);
-
/*
* Dive Activity 2 - Bit table, use tags again
*/
@@ -508,14 +474,9 @@ bool dt_dive_parser(FILE *archivo, struct dive *dt_dive)
}
/*
- * Alarms 1 - Bit table - Not in Subsurface, we use the profile
+ * Alarms 1 and Alarms2 - Bit tables - Not in Subsurface, we use the profile
*/
- read_bytes(1);
-
- /*
- * Alarms 2 - Bit table - Not in Subsurface, we use the profile
- */
- read_bytes(1);
+ JUMP(membuf, 2);
/*
* Dive number (in datatrak, after import user has to renumber)
@@ -526,134 +487,71 @@ bool dt_dive_parser(FILE *archivo, struct dive *dt_dive)
/*
* Computer timestamp - Useless for Subsurface
*/
- read_bytes(4);
+ JUMP(membuf, 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.
+ * Model number to check against equivalence with libdivecomputer table.
+ * The number also defines if the model is nitrox or O2 capable.
*/
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");
+ switch (tmp_1byte & 0xF0) {
+ case 0xF0:
+ is_nitrox = 1;
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");
+ case 0xA0:
+ is_O2 = 1;
break;
default:
- dt_dive->dc.model = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Unknown"));
+ is_nitrox = 0;
+ is_O2 = 0;
break;
}
- if ((tmp_1byte & 0xF0) == 0xF0)
- is_nitrox = 1;
- if ((tmp_1byte & 0xF0) == 0xA0)
- is_O2 = 1;
+ libdc_model = dtrak_prepare_data(tmp_1byte, devdata);
+ if (!libdc_model)
+ report_error(translate("gettextFromC", "[Warning] Manual dive # %d\n"), dt_dive->number);
+ dt_dive->dc.model = copy_string(devdata->model);
/*
* Air usage, unknown use. Probably allows or deny manually entering gas
* comsumption based on dc model - Useless for Subsurface
+ * And 6 bytes without known use.
*/
- read_bytes(1);
- if (fseek(archivo, 6, 1) != 0) // jump over 6 bytes whitout known use
- goto bail;
+ JUMP(membuf, 7);
+
/*
* 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)
+
+ /*
+ * Profile parsing, only if we have a profile and a dc model.
+ * If just a profile, skip parsing and seek the buffer to the end of dive.
+ */
+ if (profile_length != 0 && libdc_model != 0) {
+ compl_buffer = (unsigned char *) calloc(18 + profile_length, 1);
+ rc = dt_libdc_buffer(membuf, profile_length, libdc_model, compl_buffer);
+ if (rc == DC_STATUS_SUCCESS) {
+ libdc_buffer_parser(dt_dive, devdata, compl_buffer, profile_length + 18);
+ } else {
+ report_error(translate("gettextFromC", "[Error] Out of memory for dive %d. Abort parsing."), dt_dive->number);
+ free(compl_buffer);
+ free(devdata);
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 =
- lrint((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 numerator = is_O2 ? (profile_length - 25) * 3 : (profile_length - 24) * 3;
- int denominator = is_O2 ? 8 : 7;
- int samplenum = (numerator / denominator) + (((numerator % denominator) != 0) ? 1 : 0);
-
- 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);
+ if (is_nitrox)
+ dt_dive->cylinder[0].gasmix.o2.permille =
+ lrint(membuf[23] & 0x0F ? 20.0 + 2 * (membuf[23] & 0x0F) : 21.0) * 10;
+ if (is_O2)
+ dt_dive->cylinder[0].gasmix.o2.permille = membuf[23] * 10;
+ free(compl_buffer);
}
+ JUMP(membuf, profile_length);
+
/*
* Initialize some dive data not supported by Datatrak/WLog
*/
- if (!strcmp(dt_dive->dc.model, "Manually entered dive"))
+ if (!libdc_model)
dt_dive->dc.deviceid = 0;
else
dt_dive->dc.deviceid = 0xffffffff;
@@ -663,41 +561,51 @@ bool dt_dive_parser(FILE *archivo, struct dive *dt_dive)
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;
-
+ free(devdata);
+ return membuf;
bail:
- return false;
+ return NULL;
}
-
-void datatrak_import(const char *file, struct dive_table *table)
+/*
+ * Main function call from file.c memblock is allocated (and freed) there.
+ * If parsing is aborted due to errors, stores correctly parsed dives.
+ */
+int datatrak_import(struct memblock *mem, struct dive_table *table)
{
- FILE *archivo;
- dtrakheader *fileheader = (dtrakheader *)malloc(sizeof(dtrakheader));
- int i = 0;
+ unsigned char *runner;
+ int i = 0, numdives = 0, rc = 0;
+
+ maxbuf = (long) mem->buffer + mem->size;
- 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, zero on error
+ numdives = read_file_header((unsigned char *)mem->buffer);
+ if (!numdives) {
+ report_error(translate("gettextFromC", "[Error] File is not a DataTrak file. Aborted"));
+ goto bail;
}
+ // Point to the expected begining of 1st. dive data
+ runner = (unsigned char *)mem->buffer;
+ JUMP(runner, 12);
- /*
- * Verify fileheader, get number of dives in datatrak divelog
- */
- *fileheader = read_file_header(archivo);
- while (i < fileheader->divesNum) {
+ // Secuential parsing. Abort if received NULL from dt_dive_parser.
+ while ((i < numdives) && ((long) runner < maxbuf)) {
struct dive *ptdive = alloc_dive();
- if (!dt_dive_parser(archivo, ptdive)) {
+ runner = dt_dive_parser(runner, ptdive);
+ if (runner == NULL) {
report_error(translate("gettextFromC", "Error: no dive"));
free(ptdive);
+ rc = 1;
+ goto out;
} else {
record_dive(ptdive);
}
i++;
}
+out:
taglist_cleanup(&g_tag_list);
- fclose(archivo);
sort_table(table);
- free(fileheader);
+ return rc;
+bail:
+ return 1;
}
diff --git a/core/datatrak.h b/core/datatrak.h
index a774c6018..7aea1741f 100644
--- a/core/datatrak.h
+++ b/core/datatrak.h
@@ -3,40 +3,71 @@
#define DATATRAK_HEADER_H
#include <string.h>
+#include "libdivecomputer.h"
-typedef struct dtrakheader_ {
- int header; //Must be 0xA100;
- int divesNum;
- int dc_serial_1;
- int dc_serial_2;
-} dtrakheader;
+struct models_table_t {
+ int model_num;
+ int libdc_num;
+ const char *name;
+ dc_family_t type;
+};
+/*
+ * Set of known models and equivalences with libdivecomputer.
+ * Not included 0x14, 0x24, 0x41, and 0x73 known to exist, but not its model name.
+ * Unknown model equivalence is set to Air X which should cover most profiles.
+ * Nitrox and 02 models seems to keep its number more seriously than earlier
+ * series and OEMs. Info for unknown models is always welcome.
+ */
+static const struct models_table_t g_models[] = {
+ {0x00, 0x00, "Manually entered dive", DC_FAMILY_NULL},
+ {0x1B, 0x3F, "Uwatec Aladin Pro", DC_FAMILY_UWATEC_ALADIN},
+ {0x1C, 0x1C, "Uwatec Aladin Air", DC_FAMILY_UWATEC_ALADIN},
+ {0x1D, 0x3F, "Spiro Monitor 2 plus", DC_FAMILY_UWATEC_ALADIN},
+ {0x1E, 0x3E, "Uwatec Aladin Sport", DC_FAMILY_UWATEC_ALADIN},
+ {0x1F, 0x3F, "Uwatec Aladin Pro", DC_FAMILY_UWATEC_ALADIN},
+ {0x34, 0x44, "Uwatec Aladin Air X/Z", DC_FAMILY_UWATEC_ALADIN},
+ {0x3D, 0x3F, "Spiro Monitor 2 plus", DC_FAMILY_UWATEC_ALADIN},
+ {0x3E, 0x3E, "Uwatec Aladin Sport", DC_FAMILY_UWATEC_ALADIN},
+ {0x3F, 0x3F, "Uwatec Aladin Pro", DC_FAMILY_UWATEC_ALADIN},
+ {0x44, 0x44, "Uwatec Aladin Air X/Z", DC_FAMILY_UWATEC_ALADIN},
+ {0x48, 0x1C, "Spiro Monitor 3 Air", DC_FAMILY_UWATEC_ALADIN},
+ {0xA4, 0xA4, "Uwatec Aladin Air X/Z O2", DC_FAMILY_UWATEC_ALADIN},
+ {0xB1, 0x3E, "Citizen Hyper Aqualand", DC_FAMILY_UWATEC_ALADIN},
+ {0xB2, 0x3F, "Citizen ProMaster", DC_FAMILY_UWATEC_ALADIN},
+ {0xB3, 0x3F, "Mares Guardian", DC_FAMILY_UWATEC_ALADIN},
+ {0xF4, 0xF4, "Uwatec Aladin Air X/Z Nitrox", DC_FAMILY_UWATEC_ALADIN},
+ {0xFF, 0xFF, "Uwatec Aladin Pro Nitrox", DC_FAMILY_UWATEC_ALADIN},
+ {0xEE, 0x44, "Uwatec Unknown model", DC_FAMILY_UWATEC_ALADIN},
+};
+
+extern struct sample *add_sample(struct sample *sample, int time, struct divecomputer *dc);
+
+#define JUMP(_ptr, _n) if ((long) (_ptr += _n) > maxbuf) goto bail
+#define CHECK(_ptr, _n) if ((long) _ptr + _n > maxbuf) goto bail
#define read_bytes(_n) \
switch (_n) { \
case 1: \
- if (fread (&lector_bytes, sizeof(char), _n, archivo) != _n) \
- goto bail; \
- tmp_1byte = lector_bytes[0]; \
+ CHECK(membuf, _n); \
+ tmp_1byte = membuf[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]); \
+ CHECK(membuf, _n); \
+ tmp_2bytes = two_bytes_to_int (membuf[1], membuf[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]); \
+ CHECK(membuf, _n); \
+ tmp_4bytes = four_bytes_to_long(membuf[3], membuf[2], membuf[1], membuf[0]); \
break; \
- }
+ } \
+ JUMP(membuf, _n);
#define read_string(_property) \
+ CHECK(membuf, tmp_1byte); \
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##tmp = memcpy(_property##tmp, membuf, tmp_1byte);\
_property = (unsigned char *)strcat(to_utf8(_property##tmp), ""); \
- free(_property##tmp);
+ free(_property##tmp);\
+ JUMP(membuf, tmp_1byte);
#endif // DATATRAK_HEADER_H
diff --git a/core/exif.cpp b/core/exif.cpp
index 9d1643a0a..8c47a514f 100644
--- a/core/exif.cpp
+++ b/core/exif.cpp
@@ -420,23 +420,6 @@ int easyexif::EXIFInfo::parseFrom(const unsigned char *buf, unsigned len) {
if (!buf || len < 4) return PARSE_EXIF_ERROR_NO_JPEG;
if (buf[0] != 0xFF || buf[1] != 0xD8) return PARSE_EXIF_ERROR_NO_JPEG;
- // Sanity check: some cameras pad the JPEG image with null bytes at the end.
- // Normally, we should able to find the JPEG end marker 0xFFD9 at the end
- // of the image, but not always. As long as there are null/0xFF bytes at the
- // end of the image buffer, keep decrementing len until an 0xFFD9 is found,
- // or some other bytes are. If the first non-zero/0xFF bytes from the end are
- // not 0xFFD9, then we can be reasonably sure that the buffer is not a JPEG.
- while (len > 2) {
- if (buf[len - 1] == 0 || buf[len - 1] == 0xFF) {
- len--;
- } else {
- if (buf[len - 1] != 0xD9 || buf[len - 2] != 0xFF) {
- return PARSE_EXIF_ERROR_NO_JPEG;
- } else {
- break;
- }
- }
- }
clear();
// Scan for EXIF header (bytes 0xFF 0xE1) and do a sanity check by
diff --git a/core/file.c b/core/file.c
index cde47cfef..7b24da8e7 100644
--- a/core/file.c
+++ b/core/file.c
@@ -534,8 +534,9 @@ int parse_file(const char *filename)
/* DataTrak/Wlog */
if (fmt && !strcasecmp(fmt + 1, "LOG")) {
- datatrak_import(filename, &dive_table);
- return 0;
+ ret = datatrak_import(&mem, &dive_table);
+ free(mem.buffer);
+ return ret;
}
/* OSTCtools */
diff --git a/core/file.h b/core/file.h
index 8c5647ed2..423d471f9 100644
--- a/core/file.h
+++ b/core/file.h
@@ -9,7 +9,7 @@ struct memblock {
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 int datatrak_import(struct memblock *mem, struct dive_table *table);
extern void ostctools_import(const char *file, struct dive_table *table);
#ifdef __cplusplus
diff --git a/core/gpslocation.cpp b/core/gpslocation.cpp
index 1c5d378cf..30b101419 100644
--- a/core/gpslocation.cpp
+++ b/core/gpslocation.cpp
@@ -46,6 +46,11 @@ GpsLocation *GpsLocation::instance()
return m_Instance;
}
+bool GpsLocation::hasInstance()
+{
+ return m_Instance != NULL;
+}
+
GpsLocation::~GpsLocation()
{
m_Instance = NULL;
diff --git a/core/gpslocation.h b/core/gpslocation.h
index 34e0708ff..9922997f1 100644
--- a/core/gpslocation.h
+++ b/core/gpslocation.h
@@ -27,6 +27,7 @@ public:
GpsLocation(void (*showMsgCB)(const char *msg), QObject *parent);
~GpsLocation();
static GpsLocation *instance();
+ static bool hasInstance();
bool applyLocations();
int getGpsNum() const;
QString getUserid(QString user, QString passwd);
diff --git a/core/helpers.h b/core/helpers.h
index d694be941..a6e152cbf 100644
--- a/core/helpers.h
+++ b/core/helpers.h
@@ -36,7 +36,7 @@ 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_duration_string(timestamp_t when, QString hourText, QString minutesText, QString secondsText = "", bool isFreeDive = false);
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);
diff --git a/core/prefs-macros.h b/core/prefs-macros.h
index bd1fc9a33..9208fb82e 100644
--- a/core/prefs-macros.h
+++ b/core/prefs-macros.h
@@ -11,6 +11,13 @@
else \
prefs.units.field = default_prefs.units.field
+#define GET_UNIT3(name, field, f, l, type) \
+ v = s.value(QString(name)); \
+ if (v.isValid() && v.toInt() >= (f) && v.toInt() <= (l)) \
+ prefs.units.field = (type)v.toInt(); \
+ else \
+ prefs.units.field = default_prefs.units.field
+
#define GET_BOOL(name, field) \
v = s.value(QString(name)); \
if (v.isValid()) \
diff --git a/core/qthelper.cpp b/core/qthelper.cpp
index e5a046a05..45e402fc7 100644
--- a/core/qthelper.cpp
+++ b/core/qthelper.cpp
@@ -924,19 +924,23 @@ int parseGasMixHE(const QString &text)
return he;
}
-QString get_dive_duration_string(timestamp_t when, QString hourText, QString minutesText)
+QString get_dive_duration_string(timestamp_t when, QString hourText, QString minutesText, QString secondsText, bool isFreeDive)
{
- int hrs, mins;
+ int hrs, mins, fullmins, secs;
mins = (when + 59) / 60;
+ fullmins = when / 60;
+ secs = when - 60 * fullmins;
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);
-
+ if (prefs.units.duration_units == units::ALWAYS_HOURS || (prefs.units.duration_units == units::MIXED && hrs)) {
+ mins -= hrs * 60;
+ displayTime = QString("%1%2%3%4").arg(hrs).arg(hourText).arg(mins, 2, 10, QChar('0')).arg(hourText == ":" ? "" : minutesText);
+ } else if (isFreeDive) {
+ displayTime = QString("%1%2%3%4").arg(fullmins).arg(minutesText).arg(secs, 2, 10, QChar('0')).arg(secondsText);
+ } else {
+ displayTime = QString("%1%2").arg(mins).arg(hourText == ":" ? "" : minutesText);
+ }
return displayTime;
}
@@ -964,7 +968,7 @@ extern "C" const char *get_current_date()
{
QDateTime ts(QDateTime::currentDateTime());;
QString current_date;
-
+
current_date = loc.toString(ts, QString(prefs.date_format_short));
return strdup(current_date.toUtf8().data());
@@ -1173,9 +1177,18 @@ extern "C" void cache_picture(struct picture *picture)
QtConcurrent::run(hashPicture, clone_picture(picture));
}
+QStringList imageExtensionFilters() {
+ QStringList filters;
+ foreach (QString format, QImageReader::supportedImageFormats()) {
+ filters.append(QString("*.").append(format));
+ }
+ return filters;
+}
+
void learnImages(const QDir dir, int max_recursions)
{
- QStringList filters, files;
+ QStringList files;
+ QStringList filters = imageExtensionFilters();
if (max_recursions) {
foreach (QString dirname, dir.entryList(QStringList(), QDir::NoDotAndDotDot | QDir::Dirs)) {
@@ -1183,9 +1196,6 @@ void learnImages(const QDir dir, int max_recursions)
}
}
- foreach (QString format, QImageReader::supportedImageFormats()) {
- filters.append(QString("*.").append(format));
- }
foreach (QString file, dir.entryList(filters, QDir::Files)) {
files.append(dir.absoluteFilePath(file));
diff --git a/core/qthelper.h b/core/qthelper.h
index 8d06ce93e..ff91b771a 100644
--- a/core/qthelper.h
+++ b/core/qthelper.h
@@ -46,6 +46,7 @@ extern "C" enum deco_mode decoMode();
extern "C" void subsurface_mkdir(const char *dir);
void init_proxy();
QString getUUID();
+QStringList imageExtensionFilters();
char *intdup(int index);
extern "C" int parse_seabear_header(const char *filename, char **params, int pnr);
diff --git a/core/subsurface-qt/SettingsObjectWrapper.cpp b/core/subsurface-qt/SettingsObjectWrapper.cpp
index 2752567ae..25161a904 100644
--- a/core/subsurface-qt/SettingsObjectWrapper.cpp
+++ b/core/subsurface-qt/SettingsObjectWrapper.cpp
@@ -1626,6 +1626,11 @@ int UnitsSettings::verticalSpeedTime() const
return prefs.units.vertical_speed_time;
}
+int UnitsSettings::durationUnits() const
+{
+ return prefs.units.duration_units;
+}
+
QString UnitsSettings::unitSystem() const
{
return prefs.unit_system == METRIC ? QStringLiteral("metric")
@@ -1705,6 +1710,17 @@ void UnitsSettings::setVerticalSpeedTime(int value)
emit verticalSpeedTimeChanged(value);
}
+void UnitsSettings::setDurationUnits(int value)
+{
+ if (value == prefs.units.duration_units)
+ return;
+ QSettings s;
+ s.beginGroup(group);
+ s.setValue("duration_units", value);
+ prefs.units.duration_units = (units::DURATION) value;
+ emit durationUnitChanged(value);
+}
+
void UnitsSettings::setCoordinatesTraditional(bool value)
{
if (value == prefs.coordinates_traditional)
@@ -2180,6 +2196,7 @@ void SettingsObjectWrapper::load()
GET_UNIT("weight", weight, units::LBS, units::KG);
}
GET_UNIT("vertical_speed_time", vertical_speed_time, units::MINUTES, units::SECONDS);
+ GET_UNIT3("duration_units", duration_units, units::MIXED, units::ALWAYS_HOURS, units::DURATION);
GET_BOOL("coordinates", coordinates_traditional);
s.endGroup();
s.beginGroup("TecDetails");
diff --git a/core/subsurface-qt/SettingsObjectWrapper.h b/core/subsurface-qt/SettingsObjectWrapper.h
index bb0e9db62..7116e682e 100644
--- a/core/subsurface-qt/SettingsObjectWrapper.h
+++ b/core/subsurface-qt/SettingsObjectWrapper.h
@@ -511,6 +511,7 @@ class UnitsSettings : public QObject {
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)
+ Q_PROPERTY(int duration_units READ durationUnits WRITE setDurationUnits NOTIFY durationUnitChanged)
public:
UnitsSettings(QObject *parent = 0);
@@ -520,6 +521,7 @@ public:
int temperature() const;
int weight() const;
int verticalSpeedTime() const;
+ int durationUnits() const;
QString unitSystem() const;
bool coordinatesTraditional() const;
@@ -530,6 +532,7 @@ public slots:
void setTemperature(int value);
void setWeight(int value);
void setVerticalSpeedTime(int value);
+ void setDurationUnits(int value);
void setUnitSystem(const QString& value);
void setCoordinatesTraditional(bool value);
@@ -542,6 +545,7 @@ signals:
void verticalSpeedTimeChanged(int value);
void unitSystemChanged(const QString& value);
void coordinatesTraditionalChanged(bool value);
+ void durationUnitChanged(int value);
private:
const QString group = QStringLiteral("Units");
};
diff --git a/core/units.h b/core/units.h
index c92c23d3a..7e4c2e7d2 100644
--- a/core/units.h
+++ b/core/units.h
@@ -255,6 +255,11 @@ struct units {
SECONDS,
MINUTES
} vertical_speed_time;
+ enum DURATION {
+ MIXED,
+ MINUTES_ONLY,
+ ALWAYS_HOURS
+ } duration_units;
};
/*
@@ -264,15 +269,17 @@ struct units {
* 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 \
- }
+#define SI_UNITS \
+ { \
+ .length = METERS, .volume = LITER, .pressure = BAR, .temperature = CELSIUS, .weight = KG, \
+ .vertical_speed_time = MINUTES, .duration_units = MIXED \
+ }
+
+#define IMPERIAL_UNITS \
+ { \
+ .length = FEET, .volume = CUFT, .pressure = PSI, .temperature = FAHRENHEIT, .weight = LBS, \
+ .vertical_speed_time = MINUTES, .duration_units = MIXED \
+ }
#ifdef __cplusplus
}
diff --git a/desktop-widgets/divelistview.cpp b/desktop-widgets/divelistview.cpp
index 9c7c30578..a92afadbd 100644
--- a/desktop-widgets/divelistview.cpp
+++ b/desktop-widgets/divelistview.cpp
@@ -917,7 +917,12 @@ void DiveListView::shiftTimes()
void DiveListView::loadImages()
{
- QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Open image files"), lastUsedImageDir(), tr("Image files (*.jpg *.jpeg *.pnm *.tif *.tiff)"));
+ QStringList filters = imageExtensionFilters();
+ QStringList fileNames = QFileDialog::getOpenFileNames(this,
+ tr("Open image files"),
+ lastUsedImageDir(),
+ tr("Image files (%1)").arg(filters.join(" ")));
+
if (fileNames.isEmpty())
return;
updateLastUsedImageDir(QFileInfo(fileNames[0]).dir().path());
diff --git a/desktop-widgets/divepicturewidget.cpp b/desktop-widgets/divepicturewidget.cpp
index 92a61cba7..fcdd010da 100644
--- a/desktop-widgets/divepicturewidget.cpp
+++ b/desktop-widgets/divepicturewidget.cpp
@@ -31,7 +31,7 @@ void DivePictureWidget::doubleClicked(const QModelIndex &index)
void DivePictureWidget::mousePressEvent(QMouseEvent *event)
{
- ulong doubleClickInterval = static_cast<ulong>(qApp->styleHints()->mouseDoubleClickInterval());
+ int doubleClickInterval = qApp->styleHints()->mouseDoubleClickInterval();
static qint64 lasttime = 0L;
qint64 timestamp = QDateTime::currentDateTime().toMSecsSinceEpoch();
diff --git a/desktop-widgets/modeldelegates.cpp b/desktop-widgets/modeldelegates.cpp
index 9ab02cd88..d22e7cbb5 100644
--- a/desktop-widgets/modeldelegates.cpp
+++ b/desktop-widgets/modeldelegates.cpp
@@ -113,7 +113,6 @@ struct CurrSelected {
QWidget *ComboBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(option)
- MainWindow *m = MainWindow::instance();
QComboBox *comboDelegate = new QComboBox(parent);
comboDelegate->setModel(model);
comboDelegate->setEditable(true);
diff --git a/desktop-widgets/preferences/preferences_units.cpp b/desktop-widgets/preferences/preferences_units.cpp
index 2717688a6..9ff6eb0af 100644
--- a/desktop-widgets/preferences/preferences_units.cpp
+++ b/desktop-widgets/preferences/preferences_units.cpp
@@ -40,6 +40,9 @@ void PreferencesUnits::refreshSettings()
ui->vertical_speed_minutes->setChecked(prefs.units.vertical_speed_time == units::MINUTES);
ui->vertical_speed_seconds->setChecked(prefs.units.vertical_speed_time == units::SECONDS);
+ ui->duration_mixed->setChecked(prefs.units.duration_units == units::MIXED);
+ ui->duration_no_hours->setChecked(prefs.units.duration_units == units::MINUTES_ONLY);
+ ui->duration_show_hours->setChecked(prefs.units.duration_units == units::ALWAYS_HOURS);
}
void PreferencesUnits::syncSettings()
@@ -56,4 +59,5 @@ void PreferencesUnits::syncSettings()
units->setWeight(ui->lbs->isChecked() ? units::LBS : units::KG);
units->setVerticalSpeedTime(ui->vertical_speed_minutes->isChecked() ? units::MINUTES : units::SECONDS);
units->setCoordinatesTraditional(ui->gpsTraditional->isChecked());
+ units->setDurationUnits(ui->duration_mixed->isChecked() ? units::MIXED : (ui->duration_no_hours->isChecked() ? units::MINUTES_ONLY : units::ALWAYS_HOURS));
}
diff --git a/desktop-widgets/preferences/preferences_units.ui b/desktop-widgets/preferences/preferences_units.ui
index 4093181d4..49ef80a22 100644
--- a/desktop-widgets/preferences/preferences_units.ui
+++ b/desktop-widgets/preferences/preferences_units.ui
@@ -232,6 +232,43 @@
</widget>
</item>
<item>
+ <widget class="QGroupBox">
+ <property name="title">
+ <string>Duration units</string>
+ </property>
+ <layout class="QGridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" >
+ <property name="text">
+ <string>Show hours in duration</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QRadioButton" name="duration_show_hours">
+ <property name="text">
+ <string>hh:mm (always)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QRadioButton" name="duration_no_hours">
+ <property name="text">
+ <string>mm (always)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QRadioButton" name="duration_mixed">
+ <property name="text">
+ <string>mm (for dives shorter than 1 hour), hh:mm (otherwise)</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
<widget class="QGroupBox" name="groupBox_11">
<property name="title">
<string>GPS coordinates</string>
diff --git a/desktop-widgets/simplewidgets.cpp b/desktop-widgets/simplewidgets.cpp
index 8d3b94e8f..e6bbe87d9 100644
--- a/desktop-widgets/simplewidgets.cpp
+++ b/desktop-widgets/simplewidgets.cpp
@@ -19,6 +19,7 @@
#include "core/display.h"
#include "profile-widget/profilewidget2.h"
#include "desktop-widgets/undocommands.h"
+#include "core/qthelper.h"
class MinMaxAvgWidgetPrivate {
public:
@@ -309,7 +310,7 @@ void ShiftImageTimesDialog::syncCameraClicked()
QStringList fileNames = QFileDialog::getOpenFileNames(this,
tr("Open image file"),
DiveListView::lastUsedImageDir(),
- tr("Image files (*.jpg *.jpeg *.pnm *.tif *.tiff)"));
+ tr("Image files (*.jpg *.jpeg)"));
if (fileNames.isEmpty())
return;
@@ -401,7 +402,10 @@ void ShiftImageTimesDialog::updateInvalid()
// We've found invalid image
timestamp = picture_get_timestamp(fileName.toUtf8().data());
time_first.setTime_t(timestamp + m_amount);
- ui.invalidFilesText->append(fileName + " " + time_first.toString());
+ if (timestamp == 0)
+ ui.invalidFilesText->append(fileName + " - " + tr("No Exif date/time found"));
+ else
+ ui.invalidFilesText->append(fileName + " - " + time_first.toString());
allValid = false;
}
diff --git a/desktop-widgets/subsurfacewebservices.cpp b/desktop-widgets/subsurfacewebservices.cpp
index 2d8681a8a..1182af097 100644
--- a/desktop-widgets/subsurfacewebservices.cpp
+++ b/desktop-widgets/subsurfacewebservices.cpp
@@ -398,7 +398,8 @@ SubsurfaceWebServices::SubsurfaceWebServices(QWidget *parent, Qt::WindowFlags f)
if (userid.isEmpty() &&
!same_string(prefs.cloud_storage_email, "") &&
- !same_string(prefs.cloud_storage_password, ""))
+ !same_string(prefs.cloud_storage_password, "") &&
+ GpsLocation::hasInstance())
userid = GpsLocation::instance()->getUserid(prefs.cloud_storage_email, prefs.cloud_storage_password);
ui.userID->setText(userid);
diff --git a/desktop-widgets/tab-widgets/maintab.cpp b/desktop-widgets/tab-widgets/maintab.cpp
index 31b269b9b..14b5f2b7f 100644
--- a/desktop-widgets/tab-widgets/maintab.cpp
+++ b/desktop-widgets/tab-widgets/maintab.cpp
@@ -777,6 +777,8 @@ void MainTab::acceptChanges()
MainWindow::instance()->dive_list()->unselectDives();
selected_dive = get_divenr(added_dive);
amount_selected = 1;
+ // finally, make sure we get the tags
+ saveTags();
} else if (MainWindow::instance() && MainWindow::instance()->dive_list()->selectedTrips().count() == 1) {
/* now figure out if things have changed */
if (displayedTrip.notes && !same_string(displayedTrip.notes, currentTrip->notes)) {
diff --git a/dives/images/data_after_EOI.jpg b/dives/images/data_after_EOI.jpg
new file mode 100755
index 000000000..62d6b152b
--- /dev/null
+++ b/dives/images/data_after_EOI.jpg
Binary files differ
diff --git a/wreck.jpg b/dives/images/wreck.jpg
index 1ebfde9f0..1ebfde9f0 100644
--- a/wreck.jpg
+++ b/dives/images/wreck.jpg
Binary files differ
diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp
index dadf81dfd..c57541ccd 100644
--- a/qt-models/divetripmodel.cpp
+++ b/qt-models/divetripmodel.cpp
@@ -342,22 +342,8 @@ int DiveItem::countPhotos(dive *dive) const
QString DiveItem::displayDuration() const
{
- int hrs, mins, fullmins, secs;
struct dive *dive = get_dive_by_uniq_id(diveId);
- mins = (dive->duration.seconds + 59) / 60;
- fullmins = dive->duration.seconds / 60;
- secs = dive->duration.seconds - 60 * fullmins;
- hrs = mins / 60;
- mins -= hrs * 60;
-
- QString displayTime;
- if (hrs)
- displayTime = QString("%1:%2").arg(hrs).arg(mins, 2, 10, QChar('0'));
- else if (mins < 15 || dive->dc.divemode == FREEDIVE)
- displayTime = QString("%1m%2s").arg(fullmins).arg(secs, 2, 10, QChar('0'));
- else
- displayTime = QString("%1").arg(mins);
- return displayTime;
+ return get_dive_duration_string(dive->duration.seconds, ":", "m", "s", dive->dc.divemode == FREEDIVE);
}
QString DiveItem::displayTemperature() const
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 294f26d05..2efacd706 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -27,7 +27,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
# - test binaries dependencies (see TEST macro)
set(WINDOWS_STAGING_TESTS ${CMAKE_BINARY_DIR}/staging_tests)
install(DIRECTORY ${SUBSURFACE_SOURCE}/dives DESTINATION ${WINDOWS_STAGING_TESTS})
- install(FILES ${SUBSURFACE_SOURCE}/wreck.jpg DESTINATION ${WINDOWS_STAGING_TESTS})
# Check if we can run tests locally using wine
# Add a fake test used to ensure data is deployed to WINDOWS_STAGING_TESTS before running
diff --git a/tests/testparse.cpp b/tests/testparse.cpp
index f9947fe95..9cea8c6c8 100644
--- a/tests/testparse.cpp
+++ b/tests/testparse.cpp
@@ -52,7 +52,7 @@ int TestParse::parseCSV(int units, std::string file)
char *params[55];
int pnr = 0;
- params[pnr++] = strdup(strdup("numberField"));
+ params[pnr++] = strdup("numberField");
params[pnr++] = intdup(0);
params[pnr++] = strdup("dateField");
params[pnr++] = intdup(1);
@@ -297,7 +297,7 @@ int TestParse::parseCSVmanual(int units, std::string file)
char *params[55];
int pnr = 0;
- params[pnr++] = strdup(strdup("numberField"));
+ params[pnr++] = strdup("numberField");
params[pnr++] = intdup(0);
params[pnr++] = strdup("dateField");
params[pnr++] = intdup(1);
@@ -370,9 +370,27 @@ void TestParse::exportCSVDiveDetails()
clear_dive_file_data();
}
+void TestParse::exportUDDF()
+{
+ parse_file(SUBSURFACE_TEST_DATA "/dives/test40.xml");
+
+ export_dives_xslt("testuddfexport.uddf", 0, 1, "uddf-export.xslt");
+
+ clear_dive_file_data();
+
+ parse_file("testuddfexport.uddf");
+ export_dives_xslt("testuddfexport2.uddf", 0, 1, "uddf-export.xslt");
+
+ FILE_COMPARE("testuddfexport.uddf",
+ "testuddfexport2.uddf");
+
+ clear_dive_file_data();
+}
+
void TestParse::testExport()
{
exportCSVDiveDetails();
+ exportUDDF();
}
diff --git a/tests/testparse.h b/tests/testparse.h
index 762b922e0..80c4dacfb 100644
--- a/tests/testparse.h
+++ b/tests/testparse.h
@@ -26,6 +26,7 @@ private slots:
int parseCSVmanual(int, std::string);
void exportCSVDiveDetails();
+ void exportUDDF();
void testExport();
private:
diff --git a/tests/testpicture.cpp b/tests/testpicture.cpp
index 3021ece46..38e8e3d5b 100644
--- a/tests/testpicture.cpp
+++ b/tests/testpicture.cpp
@@ -15,28 +15,36 @@ void TestPicture::initTestCase()
void TestPicture::addPicture()
{
struct dive *dive;
- struct picture *pic;
+ struct picture *pic1, *pic2;
verbose = 1;
QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test44.xml"), 0);
dive = get_dive(0);
QVERIFY(dive != NULL);
- pic = dive->picture_list;
+ pic1 = dive->picture_list;
// So far no picture in dive
- QVERIFY(pic == NULL);
-
- dive_create_picture(dive, SUBSURFACE_TEST_DATA "/wreck.jpg", 0, false);
- pic = dive->picture_list;
- // Now there is a picture
- QVERIFY(pic != NULL);
- // Appearing at time 21:01
- QVERIFY(pic->offset.seconds == 1261);
- QVERIFY(pic->latitude.udeg == 47934500);
- QVERIFY(pic->longitude.udeg == 11334500);
-
- QVERIFY(pic->hash == NULL);
- learnHash(pic, hashFile(localFilePath(pic->filename)));
- QCOMPARE(pic->hash, "929ad9499b7ae7a9e39ef63eb6c239604ac2adfa");
+ QVERIFY(pic1 == NULL);
+
+ dive_create_picture(dive, SUBSURFACE_TEST_DATA "/dives/images/wreck.jpg", 0, false);
+ dive_create_picture(dive, SUBSURFACE_TEST_DATA "/dives/images/data_after_EOI.jpg", 0, false);
+ pic1 = dive->picture_list;
+ pic2 = pic1->next;
+ // Now there are two picture2
+ QVERIFY(pic1 != NULL);
+ QVERIFY(pic2 != NULL);
+ // 1st appearing at time 21:01
+ // 2nd appearing at time 22:01
+ QVERIFY(pic1->offset.seconds == 1261);
+ QVERIFY(pic1->latitude.udeg == 47934500);
+ QVERIFY(pic1->longitude.udeg == 11334500);
+ QVERIFY(pic2->offset.seconds == 1321);
+
+ QVERIFY(pic1->hash == NULL);
+ QVERIFY(pic2->hash == NULL);
+ learnHash(pic1, hashFile(localFilePath(pic1->filename)));
+ learnHash(pic2, hashFile(localFilePath(pic2->filename)));
+ QCOMPARE(pic1->hash, "929ad9499b7ae7a9e39ef63eb6c239604ac2adfa");
+ QCOMPARE(pic2->hash, "fa8bd48f8f24017a81e1204f52300bd98b43d4a7");
}
diff --git a/xslt/uddf-export.xslt b/xslt/uddf-export.xslt
index 53e1e84a7..bf4735f38 100644
--- a/xslt/uddf-export.xslt
+++ b/xslt/uddf-export.xslt
@@ -3,6 +3,7 @@
<xsl:include href="commonTemplates.xsl"/>
<xsl:strip-space elements="*"/>
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
+ <xsl:param name="units" select="units"/>
<xsl:key name="gases" match="cylinder" use="concat(substring-before(@o2, '.'), '/', substring-before(@he, '.'))" />
<xsl:key name="images" match="picture" use="concat(../../dive/@number|../dive/@number, ':', @filename, '@', @offset)" />
@@ -179,12 +180,33 @@
<profiledata>
<xsl:for-each select="trip">
- <repetitiongroup id="{generate-id(.)}">
+ <repetitiongroup>
+ <xsl:attribute name="id">
+ <xsl:choose>
+ <xsl:when test="$test != ''">
+ <xsl:value-of select="generate-id(.)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="'testid1'" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+
<xsl:apply-templates select="dive"/>
</repetitiongroup>
</xsl:for-each>
<xsl:for-each select="dive">
- <repetitiongroup id="{generate-id(.)}">
+ <repetitiongroup>
+ <xsl:attribute name="id">
+ <xsl:choose>
+ <xsl:when test="string-length($units) = 0 or $units = 0">
+ <xsl:value-of select="generate-id(.)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="'testid2'" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
<xsl:apply-templates select="."/>
</repetitiongroup>
</xsl:for-each>
@@ -246,7 +268,18 @@
</xsl:template>
<xsl:template match="dive">
- <dive id="{generate-id(.)}" xmlns="http://www.streit.cc/uddf/3.2/">
+ <dive xmlns="http://www.streit.cc/uddf/3.2/">
+ <xsl:attribute name="id">
+ <xsl:choose>
+ <xsl:when test="string-length($units) = 0 or $units = 0">
+ <xsl:value-of select="generate-id(.)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="'testid3'" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+
<informationbeforedive>
<xsl:variable name="buddylist">
@@ -505,7 +538,7 @@
</xsl:for-each>
<depth>
- <xsl:value-of select="substring-before(./@depth, ' ')"/>
+ <xsl:value-of select="round(substring-before(./@depth, ' ') * 100) div 100"/>
</depth>
<divetime>
diff --git a/xslt/uddf.xslt b/xslt/uddf.xslt
index 81435b301..e5c0f33f1 100644
--- a/xslt/uddf.xslt
+++ b/xslt/uddf.xslt
@@ -11,7 +11,16 @@
<xsl:template match="/">
<divelog program="subsurface-import" version="2">
<settings>
- <divecomputerid deviceid="ffffffff">
+ <divecomputerid>
+ <xsl:attribute name="deviceid">
+ <xsl:value-of select="/uddf/diver/owner/equipment/divecomputer/@id|/u:uddf/u:diver/u:owner/u:equipment/u:divecomputer/@id|/u1:uddf/u1:diver/u1:owner/u1:equipment/u1:divecomputer/@id" />
+ </xsl:attribute>
+ <xsl:attribute name="model">
+ <xsl:value-of select="/uddf/diver/owner/equipment/divecomputer/model|/u:uddf/u:diver/u:owner/u:equipment/u:divecomputer/u:model|/u1:uddf/u1:diver/u1:owner/u1:equipment/u1:divecomputer/u1:model" />
+ </xsl:attribute>
+ <xsl:attribute name="nickname">
+ <xsl:value-of select="/uddf/diver/owner/equipment/divecomputer/name|/u:uddf/u:diver/u:owner/u:equipment/u:divecomputer/u:name|/u1:uddf/u1:diver/u1:owner/u1:equipment/u1:divecomputer/u1:name" />
+ </xsl:attribute>
<xsl:choose>
<xsl:when test="/UDDF/history != ''">
<xsl:apply-templates select="/UDDF/history"/>
@@ -335,9 +344,19 @@
</xsl:for-each>
- <divecomputer deviceid="ffffffff">
+ <divecomputer>
+ <xsl:attribute name="deviceid">
+ <xsl:value-of select="/uddf/diver/owner/equipment/divecomputer/@id|/u:uddf/u:diver/u:owner/u:equipment/u:divecomputer/@id|/u1:uddf/u1:diver/u1:owner/u1:equipment/u1:divecomputer/@id" />
+ </xsl:attribute>
<xsl:attribute name="model">
- <xsl:value-of select="/uddf/generator/name|/u:uddf/u:generator/u:name|/u1:uddf/u1:generator/u1:name|/UDDF/history/modified/application/name"/>
+ <xsl:choose>
+ <xsl:when test="/uddf/diver/owner/equipment/divecomputer/model|/u:uddf/u:diver/u:owner/u:equipment/u:divecomputer/u:model|/u1:uddf/u1:diver/u1:owner/u1:equipment/u1:divecomputer/u1:model != ''">
+ <xsl:value-of select="/uddf/diver/owner/equipment/divecomputer/model|/u:uddf/u:diver/u:owner/u:equipment/u:divecomputer/u:model|/u1:uddf/u1:diver/u1:owner/u1:equipment/u1:divecomputer/u1:model" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="/uddf/generator/name|/u:uddf/u:generator/u:name|/u1:uddf/u1:generator/u1:name|/UDDF/history/modified/application/name"/>
+ </xsl:otherwise>
+ </xsl:choose>
</xsl:attribute>
<depth>