From fe9a3d9a107ccc7ff50f830ca2a61889828e79b9 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 10 Aug 2020 12:10:29 +0200 Subject: [PATCH 1/3] Add a function to insert data anywhere in the buffer The memory buffer already supported appending and prepending data, but not inserting data anywhere in the buffer. That's exactly what the new function does. The free space is still maintained at either the start or the end of the buffer. --- include/libdivecomputer/buffer.h | 3 ++ src/buffer.c | 66 ++++++++++++++++++++++++++++++++ src/libdivecomputer.symbols | 1 + 3 files changed, 70 insertions(+) diff --git a/include/libdivecomputer/buffer.h b/include/libdivecomputer/buffer.h index 2af4822..8cbf27f 100644 --- a/include/libdivecomputer/buffer.h +++ b/include/libdivecomputer/buffer.h @@ -51,6 +51,9 @@ dc_buffer_append (dc_buffer_t *buffer, const unsigned char data[], size_t size); int dc_buffer_prepend (dc_buffer_t *buffer, const unsigned char data[], size_t size); +int +dc_buffer_insert (dc_buffer_t *buffer, size_t offset, const unsigned char data[], size_t size); + int dc_buffer_slice (dc_buffer_t *buffer, size_t offset, size_t size); diff --git a/src/buffer.c b/src/buffer.c index 5b203fb..86cc8c4 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -231,6 +231,72 @@ dc_buffer_prepend (dc_buffer_t *buffer, const unsigned char data[], size_t size) } +int +dc_buffer_insert (dc_buffer_t *buffer, size_t offset, const unsigned char data[], size_t size) +{ + if (buffer == NULL) + return 0; + + if (offset > buffer->size) + return 0; + + size_t head = buffer->offset; + size_t tail = buffer->capacity - (buffer->offset + buffer->size); + + unsigned char *ptr = buffer->data + buffer->offset; + + if (size <= head) { + if (buffer->size) + memmove (ptr - size, ptr, offset); + buffer->offset -= size; + } else if (size <= tail) { + if (buffer->size) + memmove (ptr + offset + size, ptr + offset, buffer->size - offset); + } else if (size <= tail + head) { + size_t n = buffer->size + size; + size_t available = buffer->capacity - n; + + size_t tmp_offset = head > tail ? available : 0; + + unsigned char *tmp = buffer->data; + + if (buffer->size) { + memmove (tmp + tmp_offset, ptr, offset); + memmove (tmp + tmp_offset + offset + size, ptr + offset, buffer->size - offset); + } + + buffer->offset = tmp_offset; + } else { + size_t n = buffer->size + size; + size_t capacity = dc_buffer_expand_calc (buffer, n); + size_t available = capacity - n; + + size_t tmp_offset = head > tail ? available : 0; + + unsigned char *tmp = (unsigned char *) malloc (capacity); + if (tmp == NULL) + return 0; + + if (buffer->size) { + memcpy (tmp + tmp_offset, ptr, offset); + memcpy (tmp + tmp_offset + offset + size, ptr + offset, buffer->size - offset); + } + + free (buffer->data); + buffer->data = tmp; + buffer->capacity = capacity; + buffer->offset = tmp_offset; + } + + if (size) + memcpy (buffer->data + buffer->offset + offset, data, size); + + buffer->size += size; + + return 1; +} + + int dc_buffer_slice (dc_buffer_t *buffer, size_t offset, size_t size) { diff --git a/src/libdivecomputer.symbols b/src/libdivecomputer.symbols index 85caae5..800e217 100644 --- a/src/libdivecomputer.symbols +++ b/src/libdivecomputer.symbols @@ -8,6 +8,7 @@ dc_buffer_reserve dc_buffer_resize dc_buffer_append dc_buffer_prepend +dc_buffer_insert dc_buffer_slice dc_buffer_get_size dc_buffer_get_data From 57cd11fffe4c44d6323a4d5b2bfd04e9e0f5a7a4 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Tue, 15 Sep 2020 21:24:45 +0200 Subject: [PATCH 2/3] Add the extra 5 bytes containing the divemode The logbook header has 5 bytes extra, which are not present in the dive header. Those 5 extra bytes contain the dive mode, which is required for parsing the dive data. Therefore, insert all 5 bytes again and update the parser. The remaining 4 bytes appear to be some 32 bit address. They are not used for anything right now, but are preserved as well in case they are needed in the future. --- src/cressi_goa.c | 14 +++++++++++++- src/cressi_goa_parser.c | 43 +++++++++++++++++++++++++++++++---------- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/cressi_goa.c b/src/cressi_goa.c index f6df023..e414743 100644 --- a/src/cressi_goa.c +++ b/src/cressi_goa.c @@ -485,7 +485,19 @@ cressi_goa_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v goto error_free_dive; } - if (callback && !callback(dive_data, dive_size, dive_data + FP_OFFSET - 5, sizeof(device->fingerprint), userdata)) + // Those 5 extra bytes contain the dive mode, which is required for + // parsing the dive data. Therefore, insert all 5 bytes again. The + // remaining 4 bytes appear to be some 32 bit address. + if (!dc_buffer_insert (dive, 2, logbook_data + offset + 2, 5)) { + ERROR (abstract->context, "Out of memory."); + status = DC_STATUS_NOMEMORY; + goto error_free_dive; + } + + dive_data = dc_buffer_get_data (dive); + dive_size = dc_buffer_get_size (dive); + + if (callback && !callback(dive_data, dive_size, dive_data + FP_OFFSET, sizeof(device->fingerprint), userdata)) break; } diff --git a/src/cressi_goa_parser.c b/src/cressi_goa_parser.c index d7599dc..579d662 100644 --- a/src/cressi_goa_parser.c +++ b/src/cressi_goa_parser.c @@ -28,11 +28,16 @@ #define ISINSTANCE(parser) dc_device_isinstance((parser), &cressi_goa_parser_vtable) -#define SZ_HEADER 0x5C +#define SZ_HEADER 0x61 #define DEPTH 0 #define TEMPERATURE 3 +#define SCUBA 0 +#define NITROX 1 +#define FREEDIVE 2 +#define GAUGE 3 + typedef struct cressi_goa_parser_t cressi_goa_parser_t; struct cressi_goa_parser_t { @@ -100,14 +105,14 @@ cressi_goa_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) if (abstract->size < SZ_HEADER) return DC_STATUS_DATAFORMAT; - const unsigned char *p = abstract->data; + const unsigned char *p = abstract->data + 0x11; if (datetime) { - datetime->year = array_uint16_le(p + 0x0C); - datetime->month = p[0x0E]; - datetime->day = p[0x0F]; - datetime->hour = p[0x10]; - datetime->minute = p[0x11]; + datetime->year = array_uint16_le(p); + datetime->month = p[2]; + datetime->day = p[3]; + datetime->hour = p[4]; + datetime->minute = p[5]; datetime->second = 0; datetime->timezone = DC_TIMEZONE_NONE; } @@ -119,10 +124,12 @@ static dc_status_t cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value) { cressi_goa_parser_t *parser = (cressi_goa_parser_t *) abstract; + const unsigned char *data = abstract->data; + if (abstract->size < SZ_HEADER) return DC_STATUS_DATAFORMAT; - const unsigned char *data = abstract->data; + unsigned int divemode = data[2]; if (!parser->cached) { sample_statistics_t statistics = SAMPLE_STATISTICS_INITIALIZER; @@ -140,7 +147,7 @@ cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsign if (value) { switch (type) { case DC_FIELD_DIVETIME: - *((unsigned int *) value) = array_uint16_le (data + 0x14); + *((unsigned int *) value) = array_uint16_le (data + 0x19); break; case DC_FIELD_MAXDEPTH: *((double *) value) = parser->maxdepth; @@ -150,9 +157,25 @@ cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsign break; case DC_FIELD_GASMIX: gasmix->helium = 0.0; - gasmix->oxygen = data[0x1B + 2 * flags] / 100.0; + gasmix->oxygen = data[0x20 + 2 * flags] / 100.0; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; break; + case DC_FIELD_DIVEMODE: + switch (divemode) { + case SCUBA: + case NITROX: + *((dc_divemode_t *) value) = DC_DIVEMODE_OC; + break; + case GAUGE: + *((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE; + break; + case FREEDIVE: + *((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE; + break; + default: + return DC_STATUS_DATAFORMAT; + } + break; default: return DC_STATUS_UNSUPPORTED; } From c77a311a895c36bdbeec5cf0da571b73f02193f9 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sun, 24 Nov 2019 08:24:36 +0100 Subject: [PATCH 3/3] Add support for Cressi Goa gauge and freedives The gauge and freedives have a slightly different data format compared to scuba dives. The main difference is the size of the header and two new sample types. --- src/cressi_goa_parser.c | 117 ++++++++++++++++++++++++++++++---------- 1 file changed, 88 insertions(+), 29 deletions(-) diff --git a/src/cressi_goa_parser.c b/src/cressi_goa_parser.c index 579d662..35312db 100644 --- a/src/cressi_goa_parser.c +++ b/src/cressi_goa_parser.c @@ -28,9 +28,16 @@ #define ISINSTANCE(parser) dc_device_isinstance((parser), &cressi_goa_parser_vtable) -#define SZ_HEADER 0x61 +#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array)) + +#define SZ_HEADER 23 +#define SZ_HEADER_SCUBA 0x61 +#define SZ_HEADER_FREEDIVE 0x2B +#define SZ_HEADER_GAUGE 0x2D #define DEPTH 0 +#define DEPTH2 1 +#define TIME 2 #define TEMPERATURE 3 #define SCUBA 0 @@ -63,6 +70,13 @@ static const dc_parser_vtable_t cressi_goa_parser_vtable = { NULL /* destroy */ }; +static const unsigned int headersizes[] = { + SZ_HEADER_SCUBA, + SZ_HEADER_SCUBA, + SZ_HEADER_FREEDIVE, + SZ_HEADER_GAUGE, +}; + dc_status_t cressi_goa_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model) { @@ -102,7 +116,19 @@ cressi_goa_parser_set_data (dc_parser_t *abstract, const unsigned char *data, un static dc_status_t cressi_goa_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) { - if (abstract->size < SZ_HEADER) + const unsigned char *data = abstract->data; + unsigned int size = abstract->size; + + if (size < SZ_HEADER) + return DC_STATUS_DATAFORMAT; + + unsigned int divemode = data[2]; + if (divemode >= C_ARRAY_SIZE(headersizes)) { + return DC_STATUS_DATAFORMAT; + } + + unsigned int headersize = headersizes[divemode]; + if (size < headersize) return DC_STATUS_DATAFORMAT; const unsigned char *p = abstract->data + 0x11; @@ -125,11 +151,19 @@ cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsign { cressi_goa_parser_t *parser = (cressi_goa_parser_t *) abstract; const unsigned char *data = abstract->data; + unsigned int size = abstract->size; - if (abstract->size < SZ_HEADER) + if (size < SZ_HEADER) return DC_STATUS_DATAFORMAT; unsigned int divemode = data[2]; + if (divemode >= C_ARRAY_SIZE(headersizes)) { + return DC_STATUS_DATAFORMAT; + } + + unsigned int headersize = headersizes[divemode]; + if (size < headersize) + return DC_STATUS_DATAFORMAT; if (!parser->cached) { sample_statistics_t statistics = SAMPLE_STATISTICS_INITIALIZER; @@ -153,7 +187,7 @@ cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsign *((double *) value) = parser->maxdepth; break; case DC_FIELD_GASMIX_COUNT: - *((unsigned int *) value) = 2; + *((unsigned int *) value) = divemode == SCUBA || divemode == NITROX ? 2 : 0; break; case DC_FIELD_GASMIX: gasmix->helium = 0.0; @@ -190,12 +224,28 @@ cressi_goa_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c const unsigned char *data = abstract->data; unsigned int size = abstract->size; - unsigned int time = 0; - unsigned int interval = 5; - unsigned int complete = 1; - unsigned int gasmix_previous = 0xFFFFFFFF; + if (size < SZ_HEADER) + return DC_STATUS_DATAFORMAT; - unsigned int offset = SZ_HEADER; + unsigned int divemode = data[2]; + if (divemode >= C_ARRAY_SIZE(headersizes)) { + return DC_STATUS_DATAFORMAT; + } + + unsigned int headersize = headersizes[divemode]; + if (size < headersize) + return DC_STATUS_DATAFORMAT; + + unsigned int interval = divemode == FREEDIVE ? 2 : 5; + + unsigned int time = 0; + unsigned int depth = 0; + unsigned int gasmix = 0, gasmix_previous = 0xFFFFFFFF; + unsigned int temperature = 0; + unsigned int have_temperature = 0; + unsigned int complete = 0; + + unsigned int offset = headersize; while (offset + 2 <= size) { dc_sample_value_t sample = {0}; @@ -204,35 +254,44 @@ cressi_goa_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c unsigned int type = (raw & 0x0003); unsigned int value = (raw & 0xFFFC) >> 2; - if (complete) { - // Time (seconds). + if (type == DEPTH || type == DEPTH2) { + depth = (value & 0x07FF); + gasmix = (value & 0x0800) >> 11; time += interval; - sample.time = time; - if (callback) callback (DC_SAMPLE_TIME, sample, userdata); - complete = 0; + complete = 1; + } else if (type == TEMPERATURE) { + temperature = value; + have_temperature = 1; + } else if (type == TIME) { + time += value; } - if (type == DEPTH) { + if (complete) { + // Time (seconds). + sample.time = time; + if (callback) callback (DC_SAMPLE_TIME, sample, userdata); + + // Temperature (1/10 °C). + if (have_temperature) { + sample.temperature = temperature / 10.0; + if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); + have_temperature = 0; + } + // Depth (1/10 m). - unsigned int depth = value & 0x07FF; sample.depth = depth / 10.0; if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); - // Gas change. - unsigned int gasmix = (value & 0x0800) >> 11; - if (gasmix != gasmix_previous) { - sample.gasmix = gasmix; - if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); - gasmix_previous = gasmix; + // Gas change + if (divemode == SCUBA || divemode == NITROX) { + if (gasmix != gasmix_previous) { + sample.gasmix = gasmix; + if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); + gasmix_previous = gasmix; + } } - complete = 1; - } else if (type == TEMPERATURE) { - // Temperature (1/10 °C). - sample.temperature = value / 10.0; - if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); - } else { - WARNING(abstract->context, "Unknown sample type %u.", type); + complete = 0; } offset += 2;