From 4aa6eb1320d6024ecaa1e5584e2de9e0705e8cee Mon Sep 17 00:00:00 2001 From: Latisha Date: Sun, 4 Oct 2026 18:12:18 +0800 Subject: [PATCH] suunto_nautic: stop foreach at a dive that failed to download foreach skipped any dive whose download failed and still returned DC_STATUS_SUCCESS. Hosts store the newest dive's fingerprint after a successful foreach, so a link drop mid-download moved the fingerprint past dives that were never fetched, and no later sync picked them up. Now foreach stops at the first dive that fails and returns its status, having delivered only the newer dives. Only an entry whose transfer completed and matched its listed size but holds no decodable dive (an empty/aborted logbook entry) is still skipped. A still-short dive is delivered once with a warning, as before. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/suunto_nautic.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/suunto_nautic.c b/src/suunto_nautic.c index 54c30d5..c4cf4b6 100644 --- a/src/suunto_nautic.c +++ b/src/suunto_nautic.c @@ -1255,14 +1255,18 @@ suunto_nautic_device_list (dc_device_t *abstract, dc_buffer_t *out) // is known (non-zero) the download is checked against it: the listed size is // compressed /Data + /Summary data bytes, exactly. A mismatch is retried once // and then reported through `*incomplete`, with the dive left in `raw`. +// `*unreadable` is set on a failure where the transfer completed and matched +// the listing but holds no decodable dive (an empty/aborted logbook entry): +// downloading it again cannot help. Any other failure may succeed later. static dc_status_t suunto_nautic_device_download_dive (dc_device_t *abstract, const char *logbook_id, unsigned int listed_size, - dc_buffer_t *raw, int *incomplete) + dc_buffer_t *raw, int *incomplete, int *unreadable) { suunto_nautic_device_t *device = (suunto_nautic_device_t *) abstract; dc_status_t status = DC_STATUS_SUCCESS; *incomplete = 0; + *unreadable = 0; char path[128]; int n = snprintf (path, sizeof (path), "/Logbook/byId/%s/Data", logbook_id); @@ -1322,6 +1326,7 @@ suunto_nautic_device_download_dive (dc_device_t *abstract, const char *logbook_i dc_buffer_get_data (compressed), dc_buffer_get_size (compressed), raw); if (status != DC_STATUS_SUCCESS) { ERROR (abstract->context, "Failed to decompress the logbook entry."); + *unreadable = status == DC_STATUS_DATAFORMAT && !*incomplete; goto done; } @@ -1329,6 +1334,7 @@ suunto_nautic_device_download_dive (dc_device_t *abstract, const char *logbook_i memcmp (dc_buffer_get_data (raw), SBEM_MAGIC, sizeof (SBEM_MAGIC)) != 0) { ERROR (abstract->context, "Unexpected magic in the decompressed data."); status = DC_STATUS_DATAFORMAT; + *unreadable = !*incomplete; goto done; } @@ -1368,8 +1374,8 @@ suunto_nautic_device_download (dc_device_t *abstract, const char *logbook_id, dc suunto_nautic_entries_free (&entries); } - int incomplete = 0; - dc_status_t status = suunto_nautic_device_download_dive (abstract, logbook_id, listed_size, raw, &incomplete); + int incomplete = 0, unreadable = 0; + dc_status_t status = suunto_nautic_device_download_dive (abstract, logbook_id, listed_size, raw, &incomplete, &unreadable); if (status == DC_STATUS_SUCCESS && incomplete) return DC_STATUS_DATAFORMAT; return status; @@ -1696,17 +1702,22 @@ suunto_nautic_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback continue; dc_buffer_clear (raw); - int incomplete = 0; - status = suunto_nautic_device_download_dive (abstract, logbook_id, entries.sizes[i], raw, &incomplete); - if (status != DC_STATUS_SUCCESS) { - // A logbook can contain empty/aborted entries (a zero-length - // session is listed in /Logbook/Entries but downloads to no - // profile data and fails the SBEM magic check). Skip with a - // warning rather than aborting the whole enumeration. - WARNING (abstract->context, "Skipping logbook entry %s (download failed, likely an empty/aborted dive).", logbook_id); + int incomplete = 0, unreadable = 0; + status = suunto_nautic_device_download_dive (abstract, logbook_id, entries.sizes[i], raw, &incomplete, &unreadable); + if (status != DC_STATUS_SUCCESS && unreadable) { + // An empty/aborted entry: listed, but with nothing to decode. + WARNING (abstract->context, "Skipping logbook entry %s (no decodable dive data).", logbook_id); status = DC_STATUS_SUCCESS; continue; } + if (status != DC_STATUS_SUCCESS) { + // Stop at the first dive that may download later: the host takes + // its fingerprint from the newest dive, so carrying on past a gap + // would mark this dive as synced. + ERROR (abstract->context, "Failed to download logbook entry %s; it and any older new dives are left for the next sync.", + logbook_id); + break; + } // Delivered anyway: skipping it would move the fingerprint past a // dive that a later sync could never fetch again. @@ -1724,5 +1735,5 @@ suunto_nautic_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback dc_buffer_free (raw); suunto_nautic_entries_free (&entries); - return DC_STATUS_SUCCESS; + return status; }