Refactor the Mares Genius and Horizon parser
The current implementation assumes a fixed order for the different record types to share some code with the older models. See commits [1] and [2] for more details. The main disadvantages of this approach are the extra complexity and the limited flexibility to adapt to future changes to the data format. To make the parsing code more future proof, split the code into separate functions for the Genius/Horizon and the older models. [1] Commit 8b06f2c31d437d6e067c21e7263b5ccc33539537 (Horizon) [2] Commit feec939a2924095f07e030aa98d839b40d4cb6cc (Genius)
This commit is contained in:
parent
4516842a5a
commit
f20d9cb972
@ -289,28 +289,6 @@ static const dc_parser_vtable_t mares_iconhd_parser_vtable = {
|
||||
NULL /* destroy */
|
||||
};
|
||||
|
||||
static unsigned int
|
||||
mares_genius_isvalid (const unsigned char data[], size_t size, unsigned int type)
|
||||
{
|
||||
if (size < 10) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int head = array_uint32_be(data);
|
||||
unsigned int tail = array_uint32_be(data + size - 4);
|
||||
if (head != type || tail != type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned short crc = array_uint16_le(data + size - 6);
|
||||
unsigned short ccrc = checksum_crc16_ccitt(data + 4, size - 10, 0x0000, 0x0000);
|
||||
if (crc != ccrc) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
mares_iconhd_cache (mares_iconhd_parser_t *parser)
|
||||
{
|
||||
@ -554,31 +532,23 @@ mares_genius_cache (mares_iconhd_parser_t *parser)
|
||||
// Get the dive mode.
|
||||
unsigned int mode = settings & 0xF;
|
||||
|
||||
// Get the sample size.
|
||||
unsigned int samplesize = logformat == 1 ? SDPT_SIZE: DPRS_SIZE;
|
||||
|
||||
// Calculate the total number of bytes for this dive.
|
||||
unsigned int nbytes = headersize + 4 + DSTR_SIZE + TISS_SIZE + nsamples * samplesize + (nsamples / 4) * AIRS_SIZE + DEND_SIZE;
|
||||
if (nbytes > size) {
|
||||
ERROR (abstract->context, "Buffer overflow detected!");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
// Get the profile type and version.
|
||||
unsigned int profile_type = array_uint16_le (data + headersize);
|
||||
unsigned int profile_minor = data[headersize + 2];
|
||||
unsigned int profile_major = data[headersize + 3];
|
||||
|
||||
// Get the surface timeout setting (in minutes).
|
||||
// For older firmware versions the value is hardcoded to 3 minutes, but
|
||||
// starting with the newer v01.02.00 firmware the value is configurable and
|
||||
// stored in the settings. To detect whether the setting is available, we
|
||||
// need to check the profile version instead of the header version.
|
||||
unsigned int surftime = 3;
|
||||
if (headersize + 4 <= size) {
|
||||
// Get the profile type and version.
|
||||
unsigned int profile_type = array_uint16_le (data + headersize);
|
||||
unsigned int profile_minor = data[headersize + 2];
|
||||
unsigned int profile_major = data[headersize + 3];
|
||||
|
||||
if (profile_type == 0 &&
|
||||
OBJVERSION(profile_major,profile_minor) >= OBJVERSION(1,0)) {
|
||||
surftime = (settings >> 13) & 0x3F;
|
||||
}
|
||||
}
|
||||
|
||||
// Gas mixes and tanks.
|
||||
unsigned int ntanks = 0;
|
||||
@ -628,7 +598,7 @@ mares_genius_cache (mares_iconhd_parser_t *parser)
|
||||
parser->logformat = logformat;
|
||||
parser->mode = mode;
|
||||
parser->nsamples = nsamples;
|
||||
parser->samplesize = samplesize;
|
||||
parser->samplesize = 0;
|
||||
parser->headersize = headersize;
|
||||
parser->settings = settings;
|
||||
parser->surftime = surftime * 60;
|
||||
@ -926,15 +896,9 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
|
||||
|
||||
|
||||
static dc_status_t
|
||||
mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||
mares_iconhd_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||
{
|
||||
mares_iconhd_parser_t *parser = (mares_iconhd_parser_t *) abstract;
|
||||
|
||||
// Cache the parser data.
|
||||
dc_status_t rc = mares_iconhd_parser_cache (parser);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
const unsigned char *data = abstract->data;
|
||||
|
||||
// Previous gas mix - initialize with impossible value
|
||||
@ -942,40 +906,6 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
|
||||
unsigned int offset = 4;
|
||||
unsigned int marker = 0;
|
||||
if (ISGENIUS(parser->model)) {
|
||||
// Skip the dive header.
|
||||
data += parser->headersize;
|
||||
|
||||
// Check the profile type and version.
|
||||
unsigned int type = array_uint16_le (data);
|
||||
unsigned int minor = data[2];
|
||||
unsigned int major = data[3];
|
||||
if (type > 1 ||
|
||||
(type == 0 && OBJVERSION(major,minor) > OBJVERSION(1,0)) ||
|
||||
(type == 1 && OBJVERSION(major,minor) > OBJVERSION(0,2))) {
|
||||
ERROR (abstract->context, "Unsupported object type (%u) or version (%u.%u).",
|
||||
type, major, minor);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
// Skip the DSTR record.
|
||||
if (!mares_genius_isvalid (data + offset, DSTR_SIZE, DSTR_TYPE)) {
|
||||
ERROR (abstract->context, "Invalid DSTR record.");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
offset += DSTR_SIZE;
|
||||
|
||||
// Skip the TISS record.
|
||||
if (!mares_genius_isvalid (data + offset, TISS_SIZE, TISS_TYPE)) {
|
||||
ERROR (abstract->context, "Invalid TISS record.");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
offset += TISS_SIZE;
|
||||
|
||||
// Size of the record type marker.
|
||||
marker = 4;
|
||||
}
|
||||
|
||||
unsigned int time = 0;
|
||||
unsigned int nsamples = 0;
|
||||
while (nsamples < parser->nsamples) {
|
||||
@ -1012,7 +942,7 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
|
||||
offset += 2;
|
||||
}
|
||||
} else if (!ISGENIUS(parser->model) && parser->mode == ICONHD_FREEDIVE) {
|
||||
} else if (parser->mode == ICONHD_FREEDIVE) {
|
||||
unsigned int maxdepth = array_uint16_le (data + offset + 0);
|
||||
unsigned int divetime = array_uint16_le (data + offset + 2);
|
||||
unsigned int surftime = array_uint16_le (data + offset + 4);
|
||||
@ -1038,17 +968,152 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
offset += parser->samplesize;
|
||||
nsamples++;
|
||||
} else {
|
||||
unsigned int depth = array_uint16_le (data + offset + 0);
|
||||
unsigned int temperature = array_uint16_le (data + offset + 2) & 0x0FFF;
|
||||
unsigned int gasmix = (data[offset + 3] & 0xF0) >> 4;
|
||||
|
||||
// Time (seconds).
|
||||
time += parser->interval;
|
||||
sample.time = time;
|
||||
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
|
||||
|
||||
// Depth (1/10 m).
|
||||
sample.depth = depth / 10.0;
|
||||
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
|
||||
|
||||
// Temperature (1/10 °C).
|
||||
sample.temperature = temperature / 10.0;
|
||||
if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
|
||||
|
||||
// Current gas mix
|
||||
if (parser->ngasmixes > 0) {
|
||||
if (gasmix >= parser->ngasmixes) {
|
||||
ERROR (abstract->context, "Invalid gas mix index.");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
if (gasmix != gasmix_previous) {
|
||||
sample.gasmix = gasmix;
|
||||
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
|
||||
gasmix_previous = gasmix;
|
||||
}
|
||||
}
|
||||
|
||||
offset += parser->samplesize;
|
||||
nsamples++;
|
||||
|
||||
// Some extra data.
|
||||
if (parser->layout->tanks != UNSUPPORTED && (nsamples % 4) == 0) {
|
||||
// Pressure (1/100 bar).
|
||||
unsigned int pressure = array_uint16_le(data + offset + marker + 0);
|
||||
if (gasmix < parser->ntanks) {
|
||||
sample.pressure.tank = gasmix;
|
||||
sample.pressure.value = pressure / 100.0;
|
||||
if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
|
||||
} else if (pressure != 0) {
|
||||
WARNING (abstract->context, "Invalid tank with non-zero pressure.");
|
||||
}
|
||||
|
||||
offset += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
mares_genius_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||
{
|
||||
mares_iconhd_parser_t *parser = (mares_iconhd_parser_t *) abstract;
|
||||
const unsigned char *data = abstract->data;
|
||||
const unsigned int size = abstract->size;
|
||||
|
||||
// Previous gas mix - initialize with impossible value
|
||||
unsigned int gasmix_previous = 0xFFFFFFFF;
|
||||
unsigned int tank = 0xFFFFFFFF;
|
||||
|
||||
// Skip the dive header.
|
||||
unsigned int offset = parser->headersize;
|
||||
|
||||
if (offset + 4 > size) {
|
||||
ERROR (abstract->context, "Buffer overflow detected!");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
// Check the profile type and version.
|
||||
unsigned int profile_type = array_uint16_le (data + offset);
|
||||
unsigned int profile_minor = data[offset + 2];
|
||||
unsigned int profile_major = data[offset + 3];
|
||||
if (profile_type > 1 ||
|
||||
(profile_type == 0 && OBJVERSION(profile_major,profile_minor) > OBJVERSION(1,0)) ||
|
||||
(profile_type == 1 && OBJVERSION(profile_major,profile_minor) > OBJVERSION(0,2))) {
|
||||
ERROR (abstract->context, "Unsupported object type (%u) or version (%u.%u).",
|
||||
profile_type, profile_major, profile_minor);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
offset += 4;
|
||||
|
||||
unsigned int time = 0;
|
||||
unsigned int marker = 4;
|
||||
while (offset < size) {
|
||||
dc_sample_value_t sample = {0};
|
||||
|
||||
if (offset + 10 > size) {
|
||||
ERROR (abstract->context, "Buffer overflow detected!");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
// Get the record type and length.
|
||||
unsigned int type = array_uint32_be(data + offset);
|
||||
unsigned int length = 0;
|
||||
switch (type) {
|
||||
case DSTR_TYPE:
|
||||
length = DSTR_SIZE;
|
||||
break;
|
||||
case TISS_TYPE:
|
||||
length = TISS_SIZE;
|
||||
break;
|
||||
case DPRS_TYPE:
|
||||
length = DPRS_SIZE;
|
||||
break;
|
||||
case SDPT_TYPE:
|
||||
length = SDPT_SIZE;
|
||||
break;
|
||||
case AIRS_TYPE:
|
||||
length = AIRS_SIZE;
|
||||
break;
|
||||
case DEND_TYPE:
|
||||
length = DEND_SIZE;
|
||||
break;
|
||||
default:
|
||||
ERROR (abstract->context, "Unknown record type (%08x).", type);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
if (offset + length > size) {
|
||||
ERROR (abstract->context, "Buffer overflow detected!");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
unsigned int etype = array_uint32_be(data + offset + length - 4);
|
||||
if (etype != type) {
|
||||
ERROR (abstract->context, "Invalid record end type (%08x).", etype);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
unsigned short crc = array_uint16_le(data + offset + length - 6);
|
||||
unsigned short ccrc = checksum_crc16_ccitt(data + offset + 4, length - 10, 0x0000, 0x0000);
|
||||
if (crc != ccrc) {
|
||||
ERROR (abstract->context, "Invalid record checksum (%04x %04x).", crc, ccrc);
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
if (type == DPRS_TYPE || type == SDPT_TYPE) {
|
||||
unsigned int depth = 0, temperature = 0;
|
||||
unsigned int gasmix = 0, alarms = 0;
|
||||
unsigned int decostop = 0, decodepth = 0, decotime = 0, tts = 0;
|
||||
unsigned int bookmark = 0;
|
||||
if (ISGENIUS(parser->model)) {
|
||||
if (parser->logformat == 1) {
|
||||
if (!mares_genius_isvalid (data + offset, SDPT_SIZE, SDPT_TYPE)) {
|
||||
ERROR (abstract->context, "Invalid SDPT record.");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
if (type == SDPT_TYPE) {
|
||||
unsigned int misc = 0, deco = 0;
|
||||
depth = array_uint16_le (data + offset + marker + 2);
|
||||
temperature = array_uint16_le (data + offset + marker + 6);
|
||||
@ -1066,11 +1131,6 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
decotime = deco & 0xFF;
|
||||
}
|
||||
} else {
|
||||
if (!mares_genius_isvalid (data + offset, DPRS_SIZE, DPRS_TYPE)) {
|
||||
ERROR (abstract->context, "Invalid DPRS record.");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
unsigned int misc = 0;
|
||||
depth = array_uint16_le (data + offset + marker + 0);
|
||||
temperature = array_uint16_le (data + offset + marker + 4);
|
||||
@ -1082,11 +1142,6 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
decostop = (misc >> 18) & 0x01;
|
||||
decodepth = (misc >> 19) & 0x7F;
|
||||
}
|
||||
} else {
|
||||
depth = array_uint16_le (data + offset + 0);
|
||||
temperature = array_uint16_le (data + offset + 2) & 0x0FFF;
|
||||
gasmix = (data[offset + 3] & 0xF0) >> 4;
|
||||
}
|
||||
|
||||
// Time (seconds).
|
||||
time += parser->interval;
|
||||
@ -1114,6 +1169,9 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
}
|
||||
}
|
||||
|
||||
// Current tank
|
||||
tank = gasmix;
|
||||
|
||||
// Bookmark
|
||||
if (bookmark) {
|
||||
sample.event.type = SAMPLE_EVENT_BOOKMARK;
|
||||
@ -1123,7 +1181,6 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
|
||||
}
|
||||
|
||||
if (ISGENIUS(parser->model)) {
|
||||
// Deco stop / NDL.
|
||||
if (decostop) {
|
||||
sample.deco.type = DC_DECO_DECOSTOP;
|
||||
@ -1163,42 +1220,37 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
|
||||
if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
offset += parser->samplesize;
|
||||
nsamples++;
|
||||
|
||||
// Some extra data.
|
||||
if (parser->layout->tanks != UNSUPPORTED && (nsamples % 4) == 0) {
|
||||
if (ISGENIUS(parser->model) &&
|
||||
!mares_genius_isvalid (data + offset, AIRS_SIZE, AIRS_TYPE)) {
|
||||
ERROR (abstract->context, "Invalid AIRS record.");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
|
||||
} else if (type == AIRS_TYPE) {
|
||||
// Pressure (1/100 bar).
|
||||
unsigned int pressure = array_uint16_le(data + offset + marker + 0);
|
||||
if (gasmix < parser->ntanks) {
|
||||
sample.pressure.tank = gasmix;
|
||||
if (tank < parser->ntanks) {
|
||||
sample.pressure.tank = tank;
|
||||
sample.pressure.value = pressure / 100.0;
|
||||
if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
|
||||
} else if (pressure != 0) {
|
||||
WARNING (abstract->context, "Invalid tank with non-zero pressure.");
|
||||
}
|
||||
|
||||
offset += ISGENIUS(parser->model) ? AIRS_SIZE : 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ISGENIUS(parser->model)) {
|
||||
// Skip the DEND record.
|
||||
if (!mares_genius_isvalid (data + offset, DEND_SIZE, DEND_TYPE)) {
|
||||
ERROR (abstract->context, "Invalid DEND record.");
|
||||
return DC_STATUS_DATAFORMAT;
|
||||
}
|
||||
offset += DEND_SIZE;
|
||||
offset += length;
|
||||
}
|
||||
|
||||
return DC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dc_status_t
|
||||
mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
|
||||
{
|
||||
mares_iconhd_parser_t *parser = (mares_iconhd_parser_t *) abstract;
|
||||
|
||||
// Cache the parser data.
|
||||
dc_status_t rc = mares_iconhd_parser_cache (parser);
|
||||
if (rc != DC_STATUS_SUCCESS)
|
||||
return rc;
|
||||
|
||||
if (ISGENIUS(parser->model)) {
|
||||
return mares_genius_foreach (abstract, callback, userdata);
|
||||
} else {
|
||||
return mares_iconhd_foreach (abstract, callback, userdata);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user