subsurface/core/ostctools.cpp
Michael Keller 7f42acfdfb Import: Add Parsing for Divesoft Freedom+ Format Files.
Switch the Divesoft log file importer to use the parser in
libdivecomputer. This adds support for the newer Divesoft Freedom+ log
file format.
Also refactor the OSTCTools log file importer to share common
functionality.

Signed-off-by: Michael Keller <github@ike.ch>
2024-11-19 08:47:00 +13:00

120 lines
3.4 KiB
C++

// SPDX-License-Identifier: GPL-2.0
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "errorhelper.h"
#include "subsurface-string.h"
#include "gettext.h"
#include "dive.h"
#include "divelist.h"
#include "divelog.h"
#include "extradata.h"
#include "file.h"
#include "format.h"
#include "libdivecomputer.h"
/*
* OSTCTools stores the raw dive data in heavily padded files, one dive
* each file. So it's not necessary to iterate once and again on a parsing
* function. Actually there's only one kind of archive for every DC model.
*/
int ostctools_import(const std::unique_ptr<std::vector<unsigned char>> &buffer, struct divelog *log)
{
if (buffer->size() < 456)
return report_error("%s", translate("gettextFromC", "Invalid OSTCTools file"));
// Read dive number from the log
auto ostcdive = std::make_unique<dive>();
ostcdive->number = (*buffer)[258] + ((*buffer)[259] << 8);
// Read device's serial number
unsigned int serial = (*buffer)[265] + ((*buffer)[266] << 8);
// Trim the buffer to the actual dive data
buffer->erase(buffer->begin(), buffer->begin() + 456);
unsigned int i = 0;
bool end_marker = false;
for (auto c: *buffer) {
i++;
if (c == 0xFD) {
if (end_marker)
break;
else
end_marker = true;
} else {
end_marker = false;
}
}
if (end_marker)
buffer->erase(buffer->begin() + i, buffer->end());
// Try to determine the dc family based on the header type
dc_family_t dc_fam;
if ((*buffer)[2] == 0x20 || (*buffer)[2] == 0x21) {
dc_fam = DC_FAMILY_HW_OSTC;
} else {
switch ((*buffer)[8]) {
case 0x22:
dc_fam = DC_FAMILY_HW_FROG;
break;
case 0x23:
case 0x24:
dc_fam = DC_FAMILY_HW_OSTC3;
break;
default:
return report_error(translate("gettextFromC", "Unknown DC in dive %d"), ostcdive->number);
}
}
// Try to determine the model based on serial number
int model;
switch (dc_fam) {
case DC_FAMILY_HW_OSTC:
if (serial > 7000)
model = 3; //2C
else if (serial > 2048)
model = 2; //2N
else if (serial > 300)
model = 1; //MK2
else
model = 0; //OSTC
break;
case DC_FAMILY_HW_FROG:
model = 0;
break;
default:
if (serial > 10000)
model = 0x12; //Sport
else
model = 0x0A; //OSTC3
}
// Prepare data to pass to libdivecomputer.
device_data_t devdata;
int ret = prepare_device_descriptor(model, dc_fam, devdata);
if (ret == 0)
return report_error(translate("gettextFromC", "Unknown DC in dive %d"), ostcdive->number);
ostcdive->dcs[0].model = devdata.vendor + " " + devdata.model + " (Imported from OSTCTools)";
// Parse the dive data
dc_status_t rc = libdc_buffer_parser(ostcdive.get(), &devdata, buffer->data(), buffer->size());
if (rc != DC_STATUS_SUCCESS)
return report_error(translate("gettextFromC", "Error - %s - parsing dive %d"), errmsg(rc), ostcdive->number);
// Serial number is not part of the header nor the profile, so libdc won't
// catch it. If Serial is part of the extra_data, and set to zero, replace it.
ostcdive->dcs[0].serial = std::to_string(serial);
auto it = find_if(ostcdive->dcs[0].extra_data.begin(), ostcdive->dcs[0].extra_data.end(),
[](auto &ed) { return ed.key == "Serial"; });
if (it != ostcdive->dcs[0].extra_data.end() && it->value == "0")
it->value = ostcdive->dcs[0].serial.c_str();
else if (it == ostcdive->dcs[0].extra_data.end())
add_extra_data(&ostcdive->dcs[0], "Serial", ostcdive->dcs[0].serial);
log->dives.record_dive(std::move(ostcdive));
return 1;
}