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;