From 518ec33ba3bf9c5e9966ef108c1673223337b0fa Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 14 Sep 2011 21:00:49 -0700 Subject: First pass to parse uemis Zurich '.SDA' files This is missing a ton of the information in the .SDA files It only parses the divelog.SDA file, not the dive.SDA file It ignores the information on the gas(es) used and all the data on the tanks. It still draws some strange artefacts at the end of the dive But it correctly hooks into the import dialogue, it gives you a file select box (somewhere, I'm sure, a gtk developer cries quietly) and then parses enough of this file to serve as a proof of concept. Signed-off-by: Dirk Hohndel Signed-off-by: Linus Torvalds --- Makefile | 5 +- libdivecomputer.c | 8 ++ uemis.c | 275 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ uemis.h | 15 +++ 4 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 uemis.c create mode 100644 uemis.h diff --git a/Makefile b/Makefile index 858f4a868..c78b806bb 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ LIBDIVECOMPUTERINCLUDES = $(LIBDIVECOMPUTERDIR)/include/libdivecomputer LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib/libdivecomputer.a OBJS = main.o dive.o profile.o info.o equipment.o divelist.o \ - parse-xml.o save-xml.o libdivecomputer.o print.o + parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o divelog: $(OBJS) $(CC) $(LDFLAGS) -o divelog $(OBJS) \ @@ -46,3 +46,6 @@ libdivecomputer.o: libdivecomputer.c dive.h display.h $(CC) $(CFLAGS) `pkg-config --cflags gtk+-2.0 glib-2.0` \ -I$(LIBDIVECOMPUTERINCLUDES) \ -c libdivecomputer.c + +uemis.o: uemis.c uemis.h + $(CC) $(CFLAGS) `pkg-config --cflags gtk+-2.0 glib-2.0` -c uemis.c diff --git a/libdivecomputer.c b/libdivecomputer.c index 06ab06639..2b62fa365 100644 --- a/libdivecomputer.c +++ b/libdivecomputer.c @@ -18,6 +18,9 @@ #include #include +/* handling uemis Zurich SDA files */ +#include "uemis.h" + /* * I'd love to do a while-loop here for pending events, but * that seems to screw up with the dive computer reading timing. @@ -437,6 +440,10 @@ static void do_import(device_data_t *data) device_t *device = NULL; device_status_t rc; + if (data->type == DEVICE_TYPE_UEMIS) { + return uemis_import(); + } + rc = device_open(data->devname, data->type, &device); if (rc != DEVICE_STATUS_SUCCESS) { error("Unable to open %s (%s)", data->name, data->devname); @@ -502,6 +509,7 @@ struct device_list { { "Cressi Edy", DEVICE_TYPE_CRESSI_EDY }, { "Zeagle N2iTiON 3", DEVICE_TYPE_ZEAGLE_N2ITION3 }, { "Atomics Cobalt", DEVICE_TYPE_ATOMICS_COBALT }, + { "Uemis Zurich SDA", DEVICE_TYPE_UEMIS }, { NULL } }; diff --git a/uemis.c b/uemis.c new file mode 100644 index 000000000..63541353b --- /dev/null +++ b/uemis.c @@ -0,0 +1,275 @@ +/* + * uemis.c + * + * UEMIS SDA file importer + * AUTHOR: Dirk Hohndel - Copyright 2011 + * + * Licensed under the MIT license. + */ + +#include +#include +#include +#include +#include +#define __USE_XOPEN +#include +#include + +#include + +#include "dive.h" +#include "display.h" +#include "uemis.h" + +/* + * following code is based on code found in at base64.sourceforge.net/b64.c + * AUTHOR: Bob Trower 08/04/01 + * COPYRIGHT: Copyright (c) Trantor Standard Systems Inc., 2001 + * NOTE: This source code may be used as you wish, subject to + * the MIT license. + */ +/* + * Translation Table to decode (created by Bob Trower) + */ +static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq"; + +/* + * decodeblock -- decode 4 '6-bit' characters into 3 8-bit binary bytes + */ +static void decodeblock( unsigned char in[4], unsigned char out[3] ) { + out[ 0 ] = (unsigned char ) (in[0] << 2 | in[1] >> 4); + out[ 1 ] = (unsigned char ) (in[1] << 4 | in[2] >> 2); + out[ 2 ] = (unsigned char ) (((in[2] << 6) & 0xc0) | in[3]); +} + +/* + * decode a base64 encoded stream discarding padding, line breaks and noise + */ +static void decode( uint8_t *inbuf, uint8_t *outbuf, int inbuf_len ) { + uint8_t in[4], out[3], v; + int i,len,indx_in=0,indx_out=0; + + while (indx_in < inbuf_len) { + for (len = 0, i = 0; i < 4 && (indx_in < inbuf_len); i++ ) { + v = 0; + while ((indx_in < inbuf_len) && v == 0) { + v = inbuf[indx_in++]; + v = ((v < 43 || v > 122) ? 0 : cd64[ v - 43 ]); + if (v) + v = ((v == '$') ? 0 : v - 61); + } + if (indx_in < inbuf_len) { + len++; + if (v) + in[i] = (v - 1); + } + else + in[i] = 0; + } + if( len ) { + decodeblock( in, out ); + for( i = 0; i < len - 1; i++ ) + outbuf[indx_out++] = out[i]; + } + } +} +/* end code from Bob Trower */ + +/* small helper functions */ +/* simpleregex allocates (and reallocates) the found buffer + * don't forget to free it when you are done + */ +static int simpleregex(char *buffer, char *regex, char **found) { + int status; + regex_t re; + regmatch_t match[5]; + + if (regcomp(&re, regex, 0) !=0) { + fprintf(stderr,"internal error, regex failed!\n"); + exit(1); + } + status = regexec(&re,buffer,5,match,0); + if(status == 0) { + *found = realloc(*found,match[1].rm_eo-match[1].rm_so + 1); + strncpy(*found,buffer+match[1].rm_so,match[1].rm_eo-match[1].rm_so); + (*found)[match[1].rm_eo-match[1].rm_so] = '\0'; + } + return(status == 0); +} + +/* read in line of arbitrary length (important for SDA files that can + * have lines that are tens of kB long + * don't forget to free it when you are done + */ +#define MYGETL_INCR 1024 +static char * mygetline(FILE * f) { + size_t size = 0; + size_t len = 0; + char * buf = NULL; + + do { + size += MYGETL_INCR; + if ((buf = realloc(buf,size)) == NULL) + break; + fgets(buf+len,MYGETL_INCR,f); + len = strlen(buf); + } while (!feof(f) && buf[len-1]!='\n'); + return buf; +} + +/* text matching, used to build very poor man's XML parser */ +int matchit(FILE *infd, char *regex, char *typeregex, char **found) { + char *buffer; + + while (!feof(infd)) { + buffer = mygetline(infd); + if (buffer && simpleregex(buffer,regex,found)) { + buffer = mygetline(infd); + if (buffer && simpleregex(buffer,typeregex,found)) { + return 1; + } + } + } + return 0; +} + + +/* + * convert the base64 data blog + */ +int uemis_convert_base64(char *base64, uint8_t **data) { + int len,datalen; + + len = strlen(base64); + datalen = (len/4 + 1)*3; + if (datalen < 0x123+0x25) { + /* less than header + 1 sample??? */ + fprintf(stderr,"suspiciously short data block\n"); + } + *data = malloc(datalen); + if (! *data) { + datalen = 0; + fprintf(stderr,"Out of memory\n"); + goto bail; + } + decode(base64, *data, len); + + if (memcmp(data,"Dive\01\00\00",7)) + fprintf(stderr,"Missing Dive100 header\n"); + +bail: + return datalen; +} + +/* + * parse uemis base64 data blob into struct dive + */ +static void parse_divelog_binary(char *base64, struct dive **divep) { + int datalen; + int i; + uint8_t *data; + struct sample *sample; + + datalen = uemis_convert_base64(base64, &data); + + /* first byte of divelog data is at offset 0x123 */ + i = 0x123; + while ((i < datalen) && (*(uint16_t *)(data+i))) { + /* it seems that a dive_time of 0 indicates the end of the valid readings */ + sample = prepare_sample(divep); + sample->time.seconds = *(uint16_t *)(data+i); + sample->depth.mm = (*(uint16_t *)(data+i+2) - 100) / 0.101428571 + 0.5; + sample->temperature.mkelvin = (*(uint16_t *)(data+i+4) * 100) + 273150; + sample->cylinderpressure.mbar= *(uint16_t *)(data+i+23) * 10; + sample->cylinderindex = *(uint8_t *)(data+i+22); + finish_sample(*divep, sample); + i += 0x25; + } + return; +} + +void +parse_uemis_file(char *divelogfilename,GError **error) { + char *found=NULL; + struct tm tm; + struct dive *dive; + + FILE *divelogfile = fopen(divelogfilename,"r"); + + dive = alloc_dive(); + + if (! matchit(divelogfile,"val key=\"date\"","\\([^<]*\\)",&found)) { + /* some error handling */ + goto bail; + } + strptime(found, "%Y-%m-%dT%H:%M:%S", &tm); + dive->when = utc_mktime(&tm); + if (! matchit(divelogfile,"", + "\\([0-9.]*\\)", &found)) { + /* some error handling */ + goto bail; + } + dive->duration.seconds = 60.0 * atof(found); + + if (! matchit(divelogfile,"", + "\\([0-9.]*\\)", &found)) { + /* some error handling */ + goto bail; + } + dive->maxdepth.mm = atof(found) / 0.10143 + 0.5; + + if (! matchit(divelogfile,"", + ">\\([a-zA-Z0-9+/]*\\)<", &found)) { + /* some error handling */ + goto bail; + } + parse_divelog_binary(found,&dive); + record_dive(dive); +bail: + if (found) + free(found); +} + +/* + * parse the two files extracted from the SDA + */ +void +uemis_import() { + GtkWidget *dialog; + GtkFileFilter *filter = gtk_file_filter_new (); + gtk_file_filter_add_pattern (filter, "*.SDA"); + gtk_file_filter_set_name(filter, "uemis Zurich SDA files"); + dialog = gtk_file_chooser_dialog_new("Open File", + GTK_WINDOW(main_window), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),filter); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + GSList *filenames; + char *filename; + filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); + + GError *error = NULL; + while(filenames != NULL) { + filename = (char *)filenames->data; + parse_uemis_file(filename, &error); + if (error != NULL) + { + report_error(error); + g_error_free(error); + error = NULL; + } + + g_free(filename); + filenames = g_slist_next(filenames); + } + g_slist_free(filenames); + report_dives(); + } + gtk_widget_destroy(dialog); +} diff --git a/uemis.h b/uemis.h new file mode 100644 index 000000000..1b6c026d1 --- /dev/null +++ b/uemis.h @@ -0,0 +1,15 @@ +#ifndef UEMIS_H +#define UEMIS_H + +/* + * defines and prototypes for the uemis Zurich SDA file parser + * we add this to the list of dive computers that is supported + * in libdivecomputer by using a negative value for the type enum + */ +#define DEVICE_TYPE_UEMIS (-1) + +void uemis_import(); + +extern GtkWidget *main_window; + +#endif /* DIVE_H */ -- cgit v1.2.3-70-g09d2