summaryrefslogtreecommitdiffstats
path: root/subsurface-core/cochran.c
diff options
context:
space:
mode:
authorGravatar Dirk Hohndel <dirk@hohndel.org>2016-04-04 22:02:03 -0700
committerGravatar Dirk Hohndel <dirk@hohndel.org>2016-04-04 22:33:58 -0700
commit7be962bfc2879a72c32ff67518731347dcdff6de (patch)
treed05bf7ab234a448ee37a15b608e2b939f2285d07 /subsurface-core/cochran.c
parent2d760a7bff71c46c5aeba37c40d236ea16eefea2 (diff)
downloadsubsurface-7be962bfc2879a72c32ff67518731347dcdff6de.tar.gz
Move subsurface-core to core and qt-mobile to mobile-widgets
Having subsurface-core as a directory name really messes with autocomplete and is obviously redundant. Simmilarly, qt-mobile caused an autocomplete conflict and also was inconsistent with the desktop-widget name for the directory containing the "other" UI. And while cleaning up the resulting change in the path name for include files, I decided to clean up those even more to make them consistent overall. This could have been handled in more commits, but since this requires a make clean before the build, it seemed more sensible to do it all in one. Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Diffstat (limited to 'subsurface-core/cochran.c')
-rw-r--r--subsurface-core/cochran.c809
1 files changed, 0 insertions, 809 deletions
diff --git a/subsurface-core/cochran.c b/subsurface-core/cochran.c
deleted file mode 100644
index b42ed8233..000000000
--- a/subsurface-core/cochran.c
+++ /dev/null
@@ -1,809 +0,0 @@
-// Clang has a bug on zero-initialization of C structs.
-#pragma clang diagnostic ignored "-Wmissing-field-initializers"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include "dive.h"
-#include "file.h"
-#include "units.h"
-#include "gettext.h"
-#include "cochran.h"
-#include "divelist.h"
-
-#include <libdivecomputer/parser.h>
-
-#define POUND 0.45359237
-#define FEET 0.3048
-#define INCH 0.0254
-#define GRAVITY 9.80665
-#define ATM 101325.0
-#define BAR 100000.0
-#define FSW (ATM / 33.0)
-#define MSW (BAR / 10.0)
-#define PSI ((POUND * GRAVITY) / (INCH * INCH))
-
-// Some say 0x4a14 and 0x4b14 are the right number for this offset
-// This works with CAN files from Analyst 4.01v and computers
-// such as Commander, Gemini, EMC-16, and EMC-20H
-#define LOG_ENTRY_OFFSET 0x4914
-
-enum cochran_type {
- TYPE_GEMINI,
- TYPE_COMMANDER,
- TYPE_EMC
-};
-
-struct config {
- enum cochran_type type;
- unsigned int logbook_size;
- unsigned int sample_size;
-} config;
-
-
-// Convert 4 bytes into an INT
-#define array_uint16_le(p) ((unsigned int) (p)[0] \
- + ((p)[1]<<8) )
-#define array_uint32_le(p) ((unsigned int) (p)[0] \
- + ((p)[1]<<8) + ((p)[2]<<16) \
- + ((p)[3]<<24))
-
-/*
- * The Cochran file format is designed to be annoying to read. It's roughly:
- *
- * 0x00000: room for 65534 4-byte words, giving the starting offsets
- * of the dives themselves.
- *
- * 0x3fff8: the size of the file + 1
- * 0x3ffff: 0 (high 32 bits of filesize? Bogus: the offsets into the file
- * are 32-bit, so it can't be a large file anyway)
- *
- * 0x40000: byte 0x46
- * 0x40001: "block 0": 256 byte encryption key
- * 0x40101: the random modulus, or length of the key to use
- * 0x40102: block 1: Version and date of Analyst and a feature string identifying
- * the computer features and the features of the file
- * 0x40138: Computer configuration page 1, 512 bytes
- * 0x40338: Computer configuration page 2, 512 bytes
- * 0x40538: Misc data (tissues) 1500 bytes
- * 0x40b14: Ownership data 512 bytes ???
- *
- * 0x4171c: Ownership data 512 bytes ??? <copy>
- *
- * 0x45415: Time stamp 17 bytes
- * 0x45426: Computer configuration page 1, 512 bytes <copy>
- * 0x45626: Computer configuration page 2, 512 bytes <copy>
- *
- */
-static unsigned int partial_decode(unsigned int start, unsigned int end,
- const unsigned char *decode, unsigned offset, unsigned mod,
- const unsigned char *buf, unsigned int size, unsigned char *dst)
-{
- unsigned i, sum = 0;
-
- for (i = start; i < end; i++) {
- unsigned char d = decode[offset++];
- if (i >= size)
- break;
- if (offset == mod)
- offset = 0;
- d += buf[i];
- if (dst)
- dst[i] = d;
- sum += d;
- }
- return sum;
-}
-
-#ifdef COCHRAN_DEBUG
-
-#define hexchar(n) ("0123456789abcdef"[(n) & 15])
-
-static int show_line(unsigned offset, const unsigned char *data,
- unsigned size, int show_empty)
-{
- unsigned char bits;
- int i, off;
- char buffer[120];
-
- if (size > 16)
- size = 16;
-
- bits = 0;
- memset(buffer, ' ', sizeof(buffer));
- off = sprintf(buffer, "%06x ", offset);
- for (i = 0; i < size; i++) {
- char *hex = buffer + off + 3 * i;
- char *asc = buffer + off + 50 + i;
- unsigned char byte = data[i];
-
- hex[0] = hexchar(byte >> 4);
- hex[1] = hexchar(byte);
- bits |= byte;
- if (byte < 32 || byte > 126)
- byte = '.';
- asc[0] = byte;
- asc[1] = 0;
- }
-
- if (bits) {
- puts(buffer);
- return 1;
- }
- if (show_empty)
- puts("...");
- return 0;
-}
-
-static void cochran_debug_write(const unsigned char *data, unsigned size)
-{
- return;
-
- int show = 1, i;
- for (i = 0; i < size; i += 16)
- show = show_line(i, data + i, size - i, show);
-}
-
-static void cochran_debug_sample(const char *s, unsigned int seconds)
-{
- switch (config.type) {
- case TYPE_GEMINI:
- switch (seconds % 4) {
- case 0:
- printf("Hex: %02x %02x ", s[0], s[1]);
- break;
- case 1:
- printf("Hex: %02x %02x ", s[0], s[1]);
- break;
- case 2:
- printf("Hex: %02x %02x ", s[0], s[1]);
- break;
- case 3:
- printf("Hex: %02x %02x ", s[0], s[1]);
- break;
- }
- break;
- case TYPE_COMMANDER:
- switch (seconds % 2) {
- case 0:
- printf("Hex: %02x %02x ", s[0], s[1]);
- break;
- case 1:
- printf("Hex: %02x %02x ", s[0], s[1]);
- break;
- }
- break;
- case TYPE_EMC:
- switch (seconds % 2) {
- case 0:
- printf("Hex: %02x %02x %02x ", s[0], s[1], s[2]);
- break;
- case 1:
- printf("Hex: %02x %02x %02x ", s[0], s[1], s[2]);
- break;
- }
- break;
- }
-
- printf ("%02dh %02dm %02ds: Depth: %-5.2f, ", seconds / 3660,
- (seconds % 3660) / 60, seconds % 60, depth);
-}
-
-#endif // COCHRAN_DEBUG
-
-static void cochran_parse_header(const unsigned char *decode, unsigned mod,
- const unsigned char *in, unsigned size)
-{
- unsigned char *buf = malloc(size);
-
- /* Do the "null decode" using a one-byte decode array of '\0' */
- /* Copies in plaintext, will be overwritten later */
- partial_decode(0, 0x0102, (const unsigned char *)"", 0, 1, in, size, buf);
-
- /*
- * The header scrambling is different form the dive
- * scrambling. Oh yay!
- */
- partial_decode(0x0102, 0x010e, decode, 0, mod, in, size, buf);
- partial_decode(0x010e, 0x0b14, decode, 0, mod, in, size, buf);
- partial_decode(0x0b14, 0x1b14, decode, 0, mod, in, size, buf);
- partial_decode(0x1b14, 0x2b14, decode, 0, mod, in, size, buf);
- partial_decode(0x2b14, 0x3b14, decode, 0, mod, in, size, buf);
- partial_decode(0x3b14, 0x5414, decode, 0, mod, in, size, buf);
- partial_decode(0x5414, size, decode, 0, mod, in, size, buf);
-
- // Detect log type
- switch (buf[0x133]) {
- case '2': // Cochran Commander, version II log format
- config.logbook_size = 256;
- if (buf[0x132] == 0x10) {
- config.type = TYPE_GEMINI;
- config.sample_size = 2; // Gemini with tank PSI samples
- } else {
- config.type = TYPE_COMMANDER;
- config.sample_size = 2; // Commander
- }
- break;
- case '3': // Cochran EMC, version III log format
- config.type = TYPE_EMC;
- config.logbook_size = 512;
- config.sample_size = 3;
- break;
- default:
- printf ("Unknown log format v%c\n", buf[0x137]);
- free(buf);
- exit(1);
- break;
- }
-
-#ifdef COCHRAN_DEBUG
- puts("Header\n======\n\n");
- cochran_debug_write(buf, size);
-#endif
-
- free(buf);
-}
-
-/*
-* Bytes expected after a pre-dive event code
-*/
-static int cochran_predive_event_bytes(unsigned char code)
-{
- int x = 0;
- int gem_event_bytes[15][2] = {{0x00, 10}, {0x02, 17}, {0x08, 18},
- {0x09, 18}, {0x0c, 18}, {0x0d, 18},
- {0x0e, 18},
- {-1, 0}};
- int cmdr_event_bytes[15][2] = {{0x00, 16}, {0x01, 20}, {0x02, 17},
- {0x03, 16}, {0x06, 18}, {0x07, 18},
- {0x08, 18}, {0x09, 18}, {0x0a, 18},
- {0x0b, 20}, {0x0c, 18}, {0x0d, 18},
- {0x0e, 18}, {0x10, 20},
- {-1, 0}};
- int emc_event_bytes[15][2] = {{0x00, 18}, {0x01, 22}, {0x02, 19},
- {0x03, 18}, {0x06, 20}, {0x07, 20},
- {0x0a, 20}, {0x0b, 20}, {0x0f, 18},
- {0x10, 20},
- {-1, 0}};
-
- switch (config.type) {
- case TYPE_GEMINI:
- while (gem_event_bytes[x][0] != code && gem_event_bytes[x][0] != -1)
- x++;
- return gem_event_bytes[x][1];
- break;
- case TYPE_COMMANDER:
- while (cmdr_event_bytes[x][0] != code && cmdr_event_bytes[x][0] != -1)
- x++;
- return cmdr_event_bytes[x][1];
- break;
- case TYPE_EMC:
- while (emc_event_bytes[x][0] != code && emc_event_bytes[x][0] != -1)
- x++;
- return emc_event_bytes[x][1];
- break;
- }
-
- return 0;
-}
-
-int cochran_dive_event_bytes(unsigned char event)
-{
- return (event == 0xAD || event == 0xAB) ? 4 : 0;
-}
-
-static void cochran_dive_event(struct divecomputer *dc, const unsigned char *s,
- unsigned int seconds, unsigned int *in_deco,
- unsigned int *deco_ceiling, unsigned int *deco_time)
-{
- switch (s[0]) {
- case 0xC5: // Deco obligation begins
- *in_deco = 1;
- add_event(dc, seconds, SAMPLE_EVENT_DECOSTOP,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "deco stop"));
- break;
- case 0xDB: // Deco obligation ends
- *in_deco = 0;
- add_event(dc, seconds, SAMPLE_EVENT_DECOSTOP,
- SAMPLE_FLAGS_END, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "deco stop"));
- break;
- case 0xAD: // Raise deco ceiling 10 ft
- *deco_ceiling -= 10; // ft
- *deco_time = (array_uint16_le(s + 3) + 1) * 60;
- break;
- case 0xAB: // Lower deco ceiling 10 ft
- *deco_ceiling += 10; // ft
- *deco_time = (array_uint16_le(s + 3) + 1) * 60;
- break;
- case 0xA8: // Entered Post Dive interval mode (surfaced)
- break;
- case 0xA9: // Exited PDI mode (re-submierged)
- break;
- case 0xBD: // Switched to normal PO2 setting
- break;
- case 0xC0: // Switched to FO2 21% mode (generally upon surface)
- break;
- case 0xC1: // "Ascent rate alarm
- add_event(dc, seconds, SAMPLE_EVENT_ASCENT,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "ascent"));
- break;
- case 0xC2: // Low battery warning
-#ifdef SAMPLE_EVENT_BATTERY
- add_event(dc, seconds, SAMPLE_EVENT_BATTERY,
- SAMPLE_FLAGS_NONE, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "battery"));
-#endif
- break;
- case 0xC3: // CNS warning
- add_event(dc, seconds, SAMPLE_EVENT_OLF,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "OLF"));
- break;
- case 0xC4: // Depth alarm begin
- add_event(dc, seconds, SAMPLE_EVENT_MAXDEPTH,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "maxdepth"));
- break;
- case 0xC8: // PPO2 alarm begin
- add_event(dc, seconds, SAMPLE_EVENT_PO2,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "pO₂"));
- break;
- case 0xCC: // Low cylinder 1 pressure";
- break;
- case 0xCD: // Switch to deco blend setting
- add_event(dc, seconds, SAMPLE_EVENT_GASCHANGE,
- SAMPLE_FLAGS_NONE, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "gaschange"));
- break;
- case 0xCE: // NDL alarm begin
- add_event(dc, seconds, SAMPLE_EVENT_RBT,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "rbt"));
- break;
- case 0xD0: // Breathing rate alarm begin
- break;
- case 0xD3: // Low gas 1 flow rate alarm begin";
- break;
- case 0xD6: // Ceiling alarm begin
- add_event(dc, seconds, SAMPLE_EVENT_CEILING,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "ceiling"));
- break;
- case 0xD8: // End decompression mode
- *in_deco = 0;
- add_event(dc, seconds, SAMPLE_EVENT_DECOSTOP,
- SAMPLE_FLAGS_END, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "deco stop"));
- break;
- case 0xE1: // Ascent alarm end
- add_event(dc, seconds, SAMPLE_EVENT_ASCENT,
- SAMPLE_FLAGS_END, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "ascent"));
- break;
- case 0xE2: // Low transmitter battery alarm
- add_event(dc, seconds, SAMPLE_EVENT_TRANSMITTER,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "transmitter"));
- break;
- case 0xE3: // Switch to FO2 mode
- break;
- case 0xE5: // Switched to PO2 mode
- break;
- case 0xE8: // PO2 too low alarm
- add_event(dc, seconds, SAMPLE_EVENT_PO2,
- SAMPLE_FLAGS_BEGIN, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "pO₂"));
- break;
- case 0xEE: // NDL alarm end
- add_event(dc, seconds, SAMPLE_EVENT_RBT,
- SAMPLE_FLAGS_END, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "rbt"));
- break;
- case 0xEF: // Switch to blend 2
- add_event(dc, seconds, SAMPLE_EVENT_GASCHANGE,
- SAMPLE_FLAGS_NONE, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "gaschange"));
- break;
- case 0xF0: // Breathing rate alarm end
- break;
- case 0xF3: // Switch to blend 1 (often at dive start)
- add_event(dc, seconds, SAMPLE_EVENT_GASCHANGE,
- SAMPLE_FLAGS_NONE, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "gaschange"));
- break;
- case 0xF6: // Ceiling alarm end
- add_event(dc, seconds, SAMPLE_EVENT_CEILING,
- SAMPLE_FLAGS_END, 0,
- QT_TRANSLATE_NOOP("gettextFromC", "ceiling"));
- break;
- default:
- break;
- }
-}
-
-/*
-* Parse sample data, extract events and build a dive
-*/
-static void cochran_parse_samples(struct dive *dive, const unsigned char *log,
- const unsigned char *samples, unsigned int size,
- unsigned int *duration, double *max_depth,
- double *avg_depth, double *min_temp)
-{
- const unsigned char *s;
- unsigned int offset = 0, seconds = 0;
- double depth = 0, temp = 0, depth_sample = 0, psi = 0, sgc_rate = 0;
- int ascent_rate = 0;
- unsigned int ndl = 0;
- unsigned int in_deco = 0, deco_ceiling = 0, deco_time = 0;
-
- struct divecomputer *dc = &dive->dc;
- struct sample *sample;
-
- // Initialize stat variables
- *max_depth = 0, *avg_depth = 0, *min_temp = 0xFF;
-
- // Get starting depth and temp (tank PSI???)
- switch (config.type) {
- case TYPE_GEMINI:
- depth = (float) (log[CMD_START_DEPTH]
- + log[CMD_START_DEPTH + 1] * 256) / 4;
- temp = log[CMD_START_TEMP];
- psi = log[CMD_START_PSI] + log[CMD_START_PSI + 1] * 256;
- sgc_rate = (float)(log[CMD_START_SGC]
- + log[CMD_START_SGC + 1] * 256) / 2;
- break;
- case TYPE_COMMANDER:
- depth = (float) (log[CMD_START_DEPTH]
- + log[CMD_START_DEPTH + 1] * 256) / 4;
- temp = log[CMD_START_TEMP];
- break;
-
- case TYPE_EMC:
- depth = (float) log [EMC_START_DEPTH] / 256
- + log[EMC_START_DEPTH + 1];
- temp = log[EMC_START_TEMP];
- break;
- }
-
- // Skip past pre-dive events
- unsigned int x = 0;
- if (samples[x] != 0x40) {
- unsigned int c;
- while ((samples[x] & 0x80) == 0 && samples[x] != 0x40 && x < size) {
- c = cochran_predive_event_bytes(samples[x]) + 1;
-#ifdef COCHRAN_DEBUG
- printf("Predive event: ", samples[x]);
- for (int y = 0; y < c; y++) printf("%02x ", samples[x + y]);
- putchar('\n');
-#endif
- x += c;
- }
- }
-
- // Now process samples
- offset = x;
- while (offset < size) {
- s = samples + offset;
-
- // Start with an empty sample
- sample = prepare_sample(dc);
- sample->time.seconds = seconds;
-
- // Check for event
- if (s[0] & 0x80) {
- cochran_dive_event(dc, s, seconds, &in_deco, &deco_ceiling, &deco_time);
- offset += cochran_dive_event_bytes(s[0]) + 1;
- continue;
- }
-
- // Depth is in every sample
- depth_sample = (float)(s[0] & 0x3F) / 4 * (s[0] & 0x40 ? -1 : 1);
- depth += depth_sample;
-
-#ifdef COCHRAN_DEBUG
- cochran_debug_sample(s, seconds);
-#endif
-
- switch (config.type) {
- case TYPE_COMMANDER:
- switch (seconds % 2) {
- case 0: // Ascent rate
- ascent_rate = (s[1] & 0x7f) * (s[1] & 0x80 ? 1: -1);
- break;
- case 1: // Temperature
- temp = s[1] / 2 + 20;
- break;
- }
- break;
- case TYPE_GEMINI:
- // Gemini with tank pressure and SAC rate.
- switch (seconds % 4) {
- case 0: // Ascent rate
- ascent_rate = (s[1] & 0x7f) * (s[1] & 0x80 ? 1 : -1);
- break;
- case 2: // PSI change
- psi -= (float)(s[1] & 0x7f) * (s[1] & 0x80 ? 1 : -1) / 4;
- break;
- case 1: // SGC rate
- sgc_rate -= (float)(s[1] & 0x7f) * (s[1] & 0x80 ? 1 : -1) / 2;
- break;
- case 3: // Temperature
- temp = (float)s[1] / 2 + 20;
- break;
- }
- break;
- case TYPE_EMC:
- switch (seconds % 2) {
- case 0: // Ascent rate
- ascent_rate = (s[1] & 0x7f) * (s[1] & 0x80 ? 1: -1);
- break;
- case 1: // Temperature
- temp = (float)s[1] / 2 + 20;
- break;
- }
- // Get NDL and deco information
- switch (seconds % 24) {
- case 20:
- if (in_deco) {
- // Fist stop time
- //first_deco_time = (s[2] + s[5] * 256 + 1) * 60; // seconds
- ndl = 0;
- } else {
- // NDL
- ndl = (s[2] + s[5] * 256 + 1) * 60; // seconds
- deco_time = 0;
- }
- break;
- case 22:
- if (in_deco) {
- // Total stop time
- deco_time = (s[2] + s[5] * 256 + 1) * 60; // seconds
- ndl = 0;
- }
- break;
- }
- }
-
- // Track dive stats
- if (depth > *max_depth) *max_depth = depth;
- if (temp < *min_temp) *min_temp = temp;
- *avg_depth = (*avg_depth * seconds + depth) / (seconds + 1);
-
- sample->depth.mm = depth * FEET * 1000;
- sample->ndl.seconds = ndl;
- sample->in_deco = in_deco;
- sample->stoptime.seconds = deco_time;
- sample->stopdepth.mm = deco_ceiling * FEET * 1000;
- sample->temperature.mkelvin = C_to_mkelvin((temp - 32) / 1.8);
- sample->sensor = 0;
- sample->cylinderpressure.mbar = psi * PSI / 100;
-
- finish_sample(dc);
-
- offset += config.sample_size;
- seconds++;
- }
- (void)ascent_rate; // mark the variable as unused
-
- if (seconds > 0)
- *duration = seconds - 1;
-}
-
-static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
- const unsigned char *in, unsigned size)
-{
- unsigned char *buf = malloc(size);
- struct dive *dive;
- struct divecomputer *dc;
- struct tm tm = {0};
- uint32_t csum[5];
-
- double max_depth, avg_depth, min_temp;
- unsigned int duration = 0, corrupt_dive = 0;
-
- /*
- * The scrambling has odd boundaries. I think the boundaries
- * match some data structure size, but I don't know. They were
- * discovered the same way we dynamically discover the decode
- * size: automatically looking for least random output.
- *
- * The boundaries are also this confused "off-by-one" thing,
- * the same way the file size is off by one. It's as if the
- * cochran software forgot to write one byte at the beginning.
- */
- partial_decode(0, 0x0fff, decode, 1, mod, in, size, buf);
- partial_decode(0x0fff, 0x1fff, decode, 0, mod, in, size, buf);
- partial_decode(0x1fff, 0x2fff, decode, 0, mod, in, size, buf);
- partial_decode(0x2fff, 0x48ff, decode, 0, mod, in, size, buf);
-
- /*
- * This is not all the descrambling you need - the above are just
- * what appears to be the fixed-size blocks. The rest is also
- * scrambled, but there seems to be size differences in the data,
- * so this just descrambles part of it:
- */
- // Decode log entry (512 bytes + random prefix)
- partial_decode(0x48ff, 0x4914 + config.logbook_size, decode,
- 0, mod, in, size, buf);
-
- unsigned int sample_size = size - 0x4914 - config.logbook_size;
- int g;
-
- // Decode sample data
- partial_decode(0x4914 + config.logbook_size, size, decode,
- 0, mod, in, size, buf);
-
-#ifdef COCHRAN_DEBUG
- // Display pre-logbook data
- puts("\nPre Logbook Data\n");
- cochran_debug_write(buf, 0x4914);
-
- // Display log book
- puts("\nLogbook Data\n");
- cochran_debug_write(buf + 0x4914, config.logbook_size + 0x400);
-
- // Display sample data
- puts("\nSample Data\n");
-#endif
-
- dive = alloc_dive();
- dc = &dive->dc;
-
- unsigned char *log = (buf + 0x4914);
-
- switch (config.type) {
- case TYPE_GEMINI:
- case TYPE_COMMANDER:
- if (config.type == TYPE_GEMINI) {
- dc->model = "Gemini";
- dc->deviceid = buf[0x18c] * 256 + buf[0x18d]; // serial no
- fill_default_cylinder(&dive->cylinder[0]);
- dive->cylinder[0].gasmix.o2.permille = (log[CMD_O2_PERCENT] / 256
- + log[CMD_O2_PERCENT + 1]) * 10;
- dive->cylinder[0].gasmix.he.permille = 0;
- } else {
- dc->model = "Commander";
- dc->deviceid = array_uint32_le(buf + 0x31e); // serial no
- for (g = 0; g < 2; g++) {
- fill_default_cylinder(&dive->cylinder[g]);
- dive->cylinder[g].gasmix.o2.permille = (log[CMD_O2_PERCENT + g * 2] / 256
- + log[CMD_O2_PERCENT + g * 2 + 1]) * 10;
- dive->cylinder[g].gasmix.he.permille = 0;
- }
- }
-
- tm.tm_year = log[CMD_YEAR];
- tm.tm_mon = log[CMD_MON] - 1;
- tm.tm_mday = log[CMD_DAY];
- tm.tm_hour = log[CMD_HOUR];
- tm.tm_min = log[CMD_MIN];
- tm.tm_sec = log[CMD_SEC];
- tm.tm_isdst = -1;
-
- dive->when = dc->when = utc_mktime(&tm);
- dive->number = log[CMD_NUMBER] + log[CMD_NUMBER + 1] * 256 + 1;
- dc->duration.seconds = (log[CMD_BT] + log[CMD_BT + 1] * 256) * 60;
- dc->surfacetime.seconds = (log[CMD_SIT] + log[CMD_SIT + 1] * 256) * 60;
- dc->maxdepth.mm = (log[CMD_MAX_DEPTH] +
- log[CMD_MAX_DEPTH + 1] * 256) / 4 * FEET * 1000;
- dc->meandepth.mm = (log[CMD_AVG_DEPTH] +
- log[CMD_AVG_DEPTH + 1] * 256) / 4 * FEET * 1000;
- dc->watertemp.mkelvin = C_to_mkelvin((log[CMD_MIN_TEMP] / 32) - 1.8);
- dc->surface_pressure.mbar = ATM / BAR * pow(1 - 0.0000225577
- * (double) log[CMD_ALTITUDE] * 250 * FEET, 5.25588) * 1000;
- dc->salinity = 10000 + 150 * log[CMD_WATER_CONDUCTIVITY];
-
- SHA1(log + CMD_NUMBER, 2, (unsigned char *)csum);
- dc->diveid = csum[0];
-
- if (log[CMD_MAX_DEPTH] == 0xff && log[CMD_MAX_DEPTH + 1] == 0xff)
- corrupt_dive = 1;
-
- break;
- case TYPE_EMC:
- dc->model = "EMC";
- dc->deviceid = array_uint32_le(buf + 0x31e); // serial no
- for (g = 0; g < 4; g++) {
- fill_default_cylinder(&dive->cylinder[g]);
- dive->cylinder[g].gasmix.o2.permille =
- (log[EMC_O2_PERCENT + g * 2] / 256
- + log[EMC_O2_PERCENT + g * 2 + 1]) * 10;
- dive->cylinder[g].gasmix.he.permille =
- (log[EMC_HE_PERCENT + g * 2] / 256
- + log[EMC_HE_PERCENT + g * 2 + 1]) * 10;
- }
-
- tm.tm_year = log[EMC_YEAR];
- tm.tm_mon = log[EMC_MON] - 1;
- tm.tm_mday = log[EMC_DAY];
- tm.tm_hour = log[EMC_HOUR];
- tm.tm_min = log[EMC_MIN];
- tm.tm_sec = log[EMC_SEC];
- tm.tm_isdst = -1;
-
- dive->when = dc->when = utc_mktime(&tm);
- dive->number = log[EMC_NUMBER] + log[EMC_NUMBER + 1] * 256 + 1;
- dc->duration.seconds = (log[EMC_BT] + log[EMC_BT + 1] * 256) * 60;
- dc->surfacetime.seconds = (log[EMC_SIT] + log[EMC_SIT + 1] * 256) * 60;
- dc->maxdepth.mm = (log[EMC_MAX_DEPTH] +
- log[EMC_MAX_DEPTH + 1] * 256) / 4 * FEET * 1000;
- dc->meandepth.mm = (log[EMC_AVG_DEPTH] +
- log[EMC_AVG_DEPTH + 1] * 256) / 4 * FEET * 1000;
- dc->watertemp.mkelvin = C_to_mkelvin((log[EMC_MIN_TEMP] - 32) / 1.8);
- dc->surface_pressure.mbar = ATM / BAR * pow(1 - 0.0000225577
- * (double) log[EMC_ALTITUDE] * 250 * FEET, 5.25588) * 1000;
- dc->salinity = 10000 + 150 * (log[EMC_WATER_CONDUCTIVITY] & 0x3);
-
- SHA1(log + EMC_NUMBER, 2, (unsigned char *)csum);
- dc->diveid = csum[0];
-
- if (log[EMC_MAX_DEPTH] == 0xff && log[EMC_MAX_DEPTH + 1] == 0xff)
- corrupt_dive = 1;
-
- break;
- }
-
- cochran_parse_samples(dive, buf + 0x4914, buf + 0x4914
- + config.logbook_size, sample_size,
- &duration, &max_depth, &avg_depth, &min_temp);
-
- // Check for corrupt dive
- if (corrupt_dive) {
- dc->maxdepth.mm = max_depth * FEET * 1000;
- dc->meandepth.mm = avg_depth * FEET * 1000;
- dc->watertemp.mkelvin = C_to_mkelvin((min_temp - 32) / 1.8);
- dc->duration.seconds = duration;
- }
-
- dive->downloaded = true;
- record_dive(dive);
- mark_divelist_changed(true);
-
- free(buf);
-}
-
-int try_to_open_cochran(const char *filename, struct memblock *mem)
-{
- (void) filename;
- unsigned int i;
- unsigned int mod;
- unsigned int *offsets, dive1, dive2;
- unsigned char *decode = mem->buffer + 0x40001;
-
- if (mem->size < 0x40000)
- return 0;
-
- offsets = (unsigned int *) mem->buffer;
- dive1 = offsets[0];
- dive2 = offsets[1];
-
- if (dive1 < 0x40000 || dive2 < dive1 || dive2 > mem->size)
- return 0;
-
- mod = decode[0x100] + 1;
- cochran_parse_header(decode, mod, mem->buffer + 0x40000, dive1 - 0x40000);
-
- // Decode each dive
- for (i = 0; i < 65534; i++) {
- dive1 = offsets[i];
- dive2 = offsets[i + 1];
- if (dive2 < dive1)
- break;
- if (dive2 > mem->size)
- break;
-
- cochran_parse_dive(decode, mod, mem->buffer + dive1,
- dive2 - dive1);
- }
-
- return 1; // no further processing needed
-}