Import: Move the Import of .FIT Files to 'Import log files'

Move the import of .FIT files into the 'Import log files' menu item,
where most people will be looking for it. This also naturally opens a
file selection dialog, which is more intuitive than having to select
this in the dive computer import dialog.
Also fix a bug affecting file imports if the log files contain
coordinates - the dive log needs to be set in the import data structure.
And refactor the file dialog file filters to make it more natural to add
more entries.

Requires https://github.com/subsurface/libdc/pull/72 to work.

Signed-off-by: Michael Keller <github@ike.ch>
This commit is contained in:
Michael Keller 2025-01-01 16:50:21 +13:00
parent feb70907e5
commit 5889c1a3f8
12 changed files with 77 additions and 72 deletions

View file

@ -98,6 +98,7 @@ set(SUBSURFACE_CORE_LIB_SRCS
filterpreset.h filterpreset.h
filterpresettable.cpp filterpresettable.cpp
filterpresettable.h filterpresettable.h
fit_file.cpp
format.cpp format.cpp
format.h format.h
fulltext.cpp fulltext.cpp

View file

@ -14,22 +14,25 @@
#include "libdivecomputer.h" #include "libdivecomputer.h"
// As supplied by Divesoft // As supplied by Divesoft
static const char divesoft_liberty_serial_prefix[] = "7026"; static constexpr std::string_view divesoft_liberty_serial_prefix = "7026";
static const char divesoft_freedom_serial_prefix[] = "7044"; static constexpr std::string_view divesoft_freedom_serial_prefix = "7044";
static const char divesoft_freedom_plus_serial_prefix[] = "7273"; static constexpr std::string_view divesoft_freedom_plus_serial_prefix = "7273";
// From libdivecomputer // From libdivecomputer
static const int divesoft_liberty_model = 10; static constexpr int divesoft_liberty_model = 10;
static const int divesoft_freedom_model = 19; static constexpr int divesoft_freedom_model = 19;
int divesoft_import(const std::unique_ptr<std::vector<unsigned char>> &buffer, struct divelog *log) int divesoft_import(const std::string &buffer, struct divelog *log)
{ {
std::string model_identifier = buffer.substr(52, 4);
int model = 0; int model = 0;
if (strncmp((char *)(buffer->data() + 52), divesoft_liberty_serial_prefix, 4) == 0) if (model_identifier == divesoft_liberty_serial_prefix)
model = divesoft_liberty_model; model = divesoft_liberty_model;
else if (strncmp((char *)(buffer->data() + 52), divesoft_freedom_serial_prefix, 4) == 0 || strncmp((char *)(buffer->data() + 52), divesoft_freedom_plus_serial_prefix, 4) == 0) else if (model_identifier == divesoft_freedom_serial_prefix || model_identifier == divesoft_freedom_plus_serial_prefix)
model = divesoft_freedom_model; model = divesoft_freedom_model;
device_data_t devdata; device_data_t devdata;
devdata.log = log;
int ret = prepare_device_descriptor(model, DC_FAMILY_DIVESOFT_FREEDOM, devdata); int ret = prepare_device_descriptor(model, DC_FAMILY_DIVESOFT_FREEDOM, devdata);
if (ret == 0) if (ret == 0)
return report_error("%s", translate("gettextFromC", "Unknown DC")); return report_error("%s", translate("gettextFromC", "Unknown DC"));
@ -38,7 +41,7 @@ int divesoft_import(const std::unique_ptr<std::vector<unsigned char>> &buffer, s
d->dcs[0].model = devdata.vendor + " " + devdata.model + " (Imported from file)"; d->dcs[0].model = devdata.vendor + " " + devdata.model + " (Imported from file)";
// Parse the dive data // Parse the dive data
dc_status_t rc = libdc_buffer_parser(d.get(), &devdata, buffer->data(), buffer->size()); dc_status_t rc = libdc_buffer_parser(d.get(), &devdata, (const unsigned char *)buffer.data(), buffer.size());
if (rc != DC_STATUS_SUCCESS) if (rc != DC_STATUS_SUCCESS)
return report_error(translate("gettextFromC", "Error - %s - parsing dive %d"), errmsg(rc), d->number); return report_error(translate("gettextFromC", "Error - %s - parsing dive %d"), errmsg(rc), d->number);

View file

@ -267,43 +267,6 @@ bool remote_repo_uptodate(const char *filename, struct git_info *info)
return false; return false;
} }
static std::unique_ptr<std::vector<unsigned char>> read_into_buffer(const char *file)
{
const char *failed_to_read_msg = translate("gettextFromC", "Failed to read '%s'");
struct stat file_status;
if (stat(file, &file_status) < 0) {
report_error(failed_to_read_msg, file);
return NULL;
}
FILE *archive;
if ((archive = subsurface_fopen(file, "rb")) == NULL) {
report_error(failed_to_read_msg, file);
return NULL;
}
// Read dive's raw data
auto buffer = std::make_unique<std::vector<unsigned char>>(file_status.st_size, 0);
int i = 0, c;
while ((c = getc(archive)) != EOF) {
(*buffer)[i] = c;
i++;
}
if (ferror(archive)) {
report_error(failed_to_read_msg, file);
fclose(archive);
return NULL;
}
fclose(archive);
return buffer;
}
int parse_file(const char *filename, struct divelog *log) int parse_file(const char *filename, struct divelog *log)
{ {
struct git_info info; struct git_info info;
@ -341,13 +304,8 @@ int parse_file(const char *filename, struct divelog *log)
} }
/* Divesoft Freedom */ /* Divesoft Freedom */
if (fmt && (!strcasecmp(fmt + 1, "DLF"))) { if (fmt && (!strcasecmp(fmt + 1, "DLF")))
auto buffer = read_into_buffer(filename); return divesoft_import(mem, log);
if (buffer == NULL)
return -1;
return divesoft_import(buffer, log);
}
/* DataTrak/Wlog */ /* DataTrak/Wlog */
if (fmt && !strcasecmp(fmt + 1, "LOG")) { if (fmt && !strcasecmp(fmt + 1, "LOG")) {
@ -362,13 +320,12 @@ int parse_file(const char *filename, struct divelog *log)
} }
/* OSTCtools */ /* OSTCtools */
if (fmt && (!strcasecmp(fmt + 1, "DIVE"))) { if (fmt && (!strcasecmp(fmt + 1, "DIVE")))
auto buffer = read_into_buffer(filename); return ostctools_import(mem, log);
if (buffer == NULL)
return -1;
return ostctools_import(buffer, log); /* FIT (Garmin et al.) file format */
} if (fmt && (!strcasecmp(fmt + 1, "FIT")))
return fit_file_import(mem, log);
/* Scubapro Logtrak files */ /* Scubapro Logtrak files */
if (fmt && (!strcasecmp(fmt+1, "script"))) { if (fmt && (!strcasecmp(fmt+1, "script"))) {

View file

@ -13,8 +13,9 @@
struct divelog; struct divelog;
struct zip; struct zip;
extern int ostctools_import(const std::unique_ptr<std::vector<unsigned char>> &buffer, struct divelog *log); extern int ostctools_import(std::string &buffer, struct divelog *log);
extern int divesoft_import(const std::unique_ptr<std::vector<unsigned char>> &buffer, struct divelog *log); extern int divesoft_import(const std::string &buffer, struct divelog *log);
extern int fit_file_import(const std::string &buffer, struct divelog *log);
extern int parse_file(const char *filename, struct divelog *log); extern int parse_file(const char *filename, struct divelog *log);
extern int try_to_open_zip(const char *filename, struct divelog *log); extern int try_to_open_zip(const char *filename, struct divelog *log);

38
core/fit_file.cpp Normal file
View file

@ -0,0 +1,38 @@
// 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 "format.h"
#include "libdivecomputer.h"
int fit_file_import(const std::string &buffer, struct divelog *log)
{
int model = 0;
device_data_t devdata;
devdata.log = log;
int ret = prepare_device_descriptor(model, DC_FAMILY_GARMIN, devdata);
if (ret == 0)
return report_error("%s", translate("gettextFromC", "Unknown DC"));
auto d = std::make_unique<dive>();
d->dcs[0].model = devdata.vendor + " " + devdata.model + " (Imported from file)";
// Parse the dive data
dc_status_t rc = libdc_buffer_parser(d.get(), &devdata, (const unsigned char *)buffer.data(), buffer.size());
if (rc != DC_STATUS_SUCCESS)
return report_error(translate("gettextFromC", "Error - %s - parsing dive %d"), errmsg(rc), d->number);
log->dives.record_dive(std::move(d));
return 1;
}

View file

@ -483,6 +483,7 @@ static int asd_dive_parser(const std::string &input, struct dive *asd_dive, stru
const unsigned char str_seq[] = {0xff, 0xfe, 0xff}, dc_profile_begin[4] = {0x01, 0x00, 0x00, 0xFF}; const unsigned char str_seq[] = {0xff, 0xfe, 0xff}, dc_profile_begin[4] = {0x01, 0x00, 0x00, 0xFF};
unsigned char *dc_data; unsigned char *dc_data;
auto devdata = std::make_unique<device_data_t>(); auto devdata = std::make_unique<device_data_t>();
devdata->log = log;
asd_dive->dcs[0].serial.resize(64); asd_dive->dcs[0].serial.resize(64);
weightsystem_t ws; weightsystem_t ws;
std::string tmp, d_locat, d_point, d_coords, notes, viz, w_type, w_surf, weather, buddies, equipment; std::string tmp, d_locat, d_point, d_coords, notes, viz, w_type, w_surf, weather, buddies, equipment;

View file

@ -482,6 +482,7 @@ int logtrak_import(const std::string &mem, struct divelog *log)
*ltd_gf_high = NULL, *ltd_log_id = NULL, *ltd_airtemp = NULL; *ltd_gf_high = NULL, *ltd_log_id = NULL, *ltd_airtemp = NULL;
auto lt_dive = std::make_unique<dive>(); auto lt_dive = std::make_unique<dive>();
auto devdata = std::make_unique<device_data_t>(); auto devdata = std::make_unique<device_data_t>();
devdata->log = log;
dive_count++; dive_count++;
Lt_String ltd_notes; Lt_String ltd_notes;
int rc; int rc;

View file

@ -1577,6 +1577,7 @@ dc_status_t libdc_buffer_parser(struct dive *dive, device_data_t *data, const un
case DC_FAMILY_HW_FROG: case DC_FAMILY_HW_FROG:
case DC_FAMILY_HW_OSTC3: case DC_FAMILY_HW_OSTC3:
case DC_FAMILY_DIVESOFT_FREEDOM: case DC_FAMILY_DIVESOFT_FREEDOM:
case DC_FAMILY_GARMIN:
rc = dc_parser_new2(&parser, data->context, data->descriptor, buffer, size); rc = dc_parser_new2(&parser, data->context, data->descriptor, buffer, size);
break; break;
default: default:

View file

@ -19,26 +19,26 @@
* each file. So it's not necessary to iterate once and again on a parsing * 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. * 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) int ostctools_import(std::string &buffer, struct divelog *log)
{ {
if (buffer->size() < 456) if (buffer.size() < 456)
return report_error("%s", translate("gettextFromC", "Invalid OSTCTools file")); return report_error("%s", translate("gettextFromC", "Invalid OSTCTools file"));
// Read dive number from the log // Read dive number from the log
auto ostcdive = std::make_unique<dive>(); auto ostcdive = std::make_unique<dive>();
ostcdive->number = (*buffer)[258] + ((*buffer)[259] << 8); ostcdive->number = (unsigned char)buffer[258] + ((unsigned char)buffer[259] << 8);
// Read device's serial number // Read device's serial number
unsigned int serial = (*buffer)[265] + ((*buffer)[266] << 8); unsigned int serial = (unsigned char)buffer[265] + ((unsigned char)buffer[266] << 8);
// Trim the buffer to the actual dive data // Trim the buffer to the actual dive data
buffer->erase(buffer->begin(), buffer->begin() + 456); buffer = buffer.substr(456);
unsigned int i = 0; unsigned int i = 0;
bool end_marker = false; bool end_marker = false;
for (auto c: *buffer) { for (auto c: buffer) {
i++; i++;
if (c == 0xFD) { if ((unsigned char)c == 0xFD) {
if (end_marker) if (end_marker)
break; break;
else else
@ -48,14 +48,14 @@ int ostctools_import(const std::unique_ptr<std::vector<unsigned char>> &buffer,
} }
} }
if (end_marker) if (end_marker)
buffer->erase(buffer->begin() + i, buffer->end()); buffer = buffer.substr(0, i);
// Try to determine the dc family based on the header type // Try to determine the dc family based on the header type
dc_family_t dc_fam; dc_family_t dc_fam;
if ((*buffer)[2] == 0x20 || (*buffer)[2] == 0x21) { if ((unsigned char)buffer[2] == 0x20 || (unsigned char)buffer[2] == 0x21) {
dc_fam = DC_FAMILY_HW_OSTC; dc_fam = DC_FAMILY_HW_OSTC;
} else { } else {
switch ((*buffer)[8]) { switch ((unsigned char)buffer[8]) {
case 0x22: case 0x22:
dc_fam = DC_FAMILY_HW_FROG; dc_fam = DC_FAMILY_HW_FROG;
break; break;
@ -93,13 +93,14 @@ int ostctools_import(const std::unique_ptr<std::vector<unsigned char>> &buffer,
// Prepare data to pass to libdivecomputer. // Prepare data to pass to libdivecomputer.
device_data_t devdata; device_data_t devdata;
devdata.log = log;
int ret = prepare_device_descriptor(model, dc_fam, devdata); int ret = prepare_device_descriptor(model, dc_fam, devdata);
if (ret == 0) if (ret == 0)
return report_error(translate("gettextFromC", "Unknown DC in dive %d"), ostcdive->number); return report_error(translate("gettextFromC", "Unknown DC in dive %d"), ostcdive->number);
ostcdive->dcs[0].model = devdata.vendor + " " + devdata.model + " (Imported from OSTCTools)"; ostcdive->dcs[0].model = devdata.vendor + " " + devdata.model + " (Imported from OSTCTools)";
// Parse the dive data // Parse the dive data
dc_status_t rc = libdc_buffer_parser(ostcdive.get(), &devdata, buffer->data(), buffer->size()); dc_status_t rc = libdc_buffer_parser(ostcdive.get(), &devdata, (unsigned char *)buffer.data(), buffer.size());
if (rc != DC_STATUS_SUCCESS) if (rc != DC_STATUS_SUCCESS)
return report_error(translate("gettextFromC", "Error - %s - parsing dive %d"), errmsg(rc), ostcdive->number); return report_error(translate("gettextFromC", "Error - %s - parsing dive %d"), errmsg(rc), ostcdive->number);

Binary file not shown.

Binary file not shown.

View file

@ -899,6 +899,7 @@ void smartrak_import(const char *file, struct divelog *log)
} }
while (mdb_table.fetch_row()) { while (mdb_table.fetch_row()) {
device_data_t devdata; device_data_t devdata;
devdata.log = log;
dc_family_t dc_fam = DC_FAMILY_NULL; dc_family_t dc_fam = DC_FAMILY_NULL;
unsigned char *prf_buffer, *hdr_buffer; unsigned char *prf_buffer, *hdr_buffer;
auto smtkdive = std::make_unique<dive>(); auto smtkdive = std::make_unique<dive>();