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/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..35312db 100644 --- a/src/cressi_goa_parser.c +++ b/src/cressi_goa_parser.c @@ -28,11 +28,23 @@ #define ISINSTANCE(parser) dc_device_isinstance((parser), &cressi_goa_parser_vtable) -#define SZ_HEADER 0x5C +#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 +#define NITROX 1 +#define FREEDIVE 2 +#define GAUGE 3 + typedef struct cressi_goa_parser_t cressi_goa_parser_t; struct cressi_goa_parser_t { @@ -58,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) { @@ -97,17 +116,29 @@ 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; - const unsigned char *p = abstract->data; + 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; 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 +150,20 @@ 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; - if (abstract->size < SZ_HEADER) + const unsigned char *data = abstract->data; + unsigned int size = abstract->size; + + if (size < SZ_HEADER) return DC_STATUS_DATAFORMAT; - const unsigned char *data = abstract->data; + 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; @@ -140,19 +181,35 @@ 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; 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; - 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; } @@ -167,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}; @@ -181,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; 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