<div dir="ltr"><div>It works fine with the files I tried.  Just a couple of things:</div><div><br></div>I think you need to add ...<br><br>  extern int try_to_open_liquivision(const char *filename, struct memblock *mem);<br><br>... in file.h, my compiler complains:<br><br>  file.c:361:10: warning: implicit declaration of function 'try_to_open_liquivision' is invalid in C99.<br><br>Also, there seems to be a = vs == problem in:<br><br>  liquivision.c:225:23: note: use '=' to turn this equality comparison into an assignment<br>    sample-><a href="http://depth.mm">depth.mm</a> == array_uint16_le(ds + (d - 1) * 2) * 10; // cm->mm<br><br><br>Henrik<br><br><br>On Fri, Nov 7, 2014 at 6:10 PM, Henrik Brautaset Aronsen <<a href="mailto:subsurface@henrik.synth.no">subsurface@henrik.synth.no</a>> wrote:<br>><br>> Great work!<br>><br>> H<br>><br>> On Fri, Nov 7, 2014 at 5:30 PM, John Van Ostrand <<a href="mailto:john@vanostrand.com">john@vanostrand.com</a>> wrote:<br>>><br>>> Support includes cylinder pressures and works for v3.0 log files.<br>>> ---<br>>>  CMakeLists.txt       |   1 +<br>>>  file.c               |   2 +<br>>>  liquivision.c        | 352 +++++++++++++++++++++++++++++++++++++++++++++++++++<br>>>  qt-ui/mainwindow.cpp |   8 +-<br>>>  <a href="http://subsurface.pro">subsurface.pro</a>       |   1 +<br>>>  5 files changed, 361 insertions(+), 3 deletions(-)<br>>>  create mode 100644 liquivision.c<br>>><br>>> diff --git a/CMakeLists.txt b/CMakeLists.txt<br>>> index c04e968..868ad4f 100644<br>>> --- a/CMakeLists.txt<br>>> +++ b/CMakeLists.txt<br>>> @@ -85,6 +85,7 @@ SET(SUBSURFACE_CORE_LIB_SRCS<br>>>         equipment.c<br>>>         file.c<br>>>         libdivecomputer.c<br>>> +       liquivision.c<br>>>         load-git.c<br>>>         membuffer.c<br>>>         parse-xml.c<br>>> diff --git a/file.c b/file.c<br>>> index bab7909..dadc11f 100644<br>>> --- a/file.c<br>>> +++ b/file.c<br>>> @@ -357,6 +357,8 @@ static int open_by_filename(const char *filename, const char *fmt, struct memblo<br>>>         /* Cochran export comma-separated-value files */<br>>>         if (!strcasecmp(fmt, "DPT"))<br>>>                 return try_to_open_csv(filename, mem, CSV_DEPTH);<br>>> +       if (!strcasecmp(fmt, "LVD"))<br>>> +               return try_to_open_liquivision(filename, mem);<br>>>         if (!strcasecmp(fmt, "TMP"))<br>>>                 return try_to_open_csv(filename, mem, CSV_TEMP);<br>>>         if (!strcasecmp(fmt, "HP1"))<br>>> diff --git a/liquivision.c b/liquivision.c<br>>> new file mode 100644<br>>> index 0000000..bb71bfd<br>>> --- /dev/null<br>>> +++ b/liquivision.c<br>>> @@ -0,0 +1,352 @@<br>>> +#include <string.h><br>>> +<br>>> +#include "dive.h"<br>>> +#include "divelist.h"<br>>> +#include "file.h"<br>>> +<br>>> +<br>>> +// Convert bytes into an INT<br>>> +#define array_uint16_le(p) ((unsigned int) (p)[0] \<br>>> +                                                       + ((p)[1]<<8) )<br>>> +#define array_uint32_le(p) ((unsigned int) (p)[0] \<br>>> +                                                       + ((p)[1]<<8) + ((p)[2]<<16) \<br>>> +                                                       + ((p)[3]<<24))<br>>> +<br>>> +<br>>> +static void<br>>> +parse_dives (int log_version, const unsigned char *buf, unsigned int buf_size) {<br>>> +       unsigned int ptr = 0;<br>>> +       unsigned char model;<br>>> +<br>>> +       struct dive *dive;<br>>> +       struct divecomputer *dc;<br>>> +       struct sample *sample;<br>>> +<br>>> +       while (ptr < buf_size) {<br>>> +               dive = alloc_dive();<br>>> +               dc = &dive->dc;<br>>> +<br>>> +               // Model 0=Xen, 1,2=Xeo, 4=Lynx, other=Liquivision<br>>> +               model = *(buf + ptr);<br>>> +               switch (model) {<br>>> +               case 0:<br>>> +                       dc->model = "Xen";<br>>> +                       break;<br>>> +               case 1:<br>>> +               case 2:<br>>> +                       dc->model = "Xeo";<br>>> +                       break;<br>>> +               case 4:<br>>> +                       dc->model = "Lynx";<br>>> +                       break;<br>>> +               default:<br>>> +                       dc->model = "LiquiVision";<br>>> +                       break;<br>>> +               }<br>>> +               ptr++;<br>>> +<br>>> +               // Dive location, assemble Location and Place<br>>> +               unsigned int len, place_len;<br>>> +               len = array_uint32_le(buf + ptr);<br>>> +               ptr += 4;<br>>> +               place_len = array_uint32_le(buf + ptr + len);<br>>> +<br>>> +               if (len && place_len) {<br>>> +                       dive->location = malloc(len + place_len + 4);<br>>> +                       memset(dive->location, 0, len + place_len + 4);<br>>> +                       memcpy(dive->location, buf + ptr, len);<br>>> +                       memcpy(dive->location + len, ", ", 2);<br>>> +                       memcpy(dive->location + len + 2, buf + ptr + len + 4, place_len);<br>>> +               } else if (len) {<br>>> +                       dive->location = strndup(buf + ptr, len);<br>>> +               } else if (place_len) {<br>>> +                       dive->location = strndup(buf + ptr + len + 4, place_len);<br>>> +               }<br>>> +<br>>> +               ptr += len + 4 + place_len;<br>>> +<br>>> +               // Dive comment<br>>> +               len = array_uint32_le(buf + ptr);<br>>> +               ptr += 4;<br>>> +<br>>> +               // Blank notes are better than the default text<br>>> +               if (len && strncmp(buf + ptr, "Comment ...", 11)) {<br>>> +                       dive->notes = strndup(buf + ptr, len);<br>>> +               }<br>>> +               ptr += len;<br>>> +<br>>> +               dive->id = array_uint32_le(buf + ptr);<br>>> +               ptr += 4;<br>>> +<br>>> +               dive->number = array_uint16_le(buf + ptr) + 1;<br>>> +               ptr += 2;<br>>> +<br>>> +               dive->duration.seconds = array_uint32_le(buf + ptr);    // seconds<br>>> +               ptr += 4;<br>>> +<br>>> +               dive-><a href="http://maxdepth.mm">maxdepth.mm</a> = array_uint16_le(buf + ptr) * 10;    // cm->mm<br>>> +               ptr += 2;<br>>> +<br>>> +               dive-><a href="http://meandepth.mm">meandepth.mm</a> = array_uint16_le(buf + ptr) * 10;   // cm->mm<br>>> +               ptr += 2;<br>>> +<br>>> +               dive->when = array_uint32_le(buf + ptr);<br>>> +               ptr += 4;<br>>> +<br>>> +               //unsigned int end_time = array_uint32_le(buf + ptr);<br>>> +               ptr += 4;<br>>> +<br>>> +               //unsigned int sit = array_uint32_le(buf + ptr);<br>>> +               ptr += 4;<br>>> +               //if (sit == 0xffffffff) {<br>>> +               //}<br>>> +<br>>> +               dive->surface_pressure.mbar = array_uint16_le(buf + ptr);               // ???<br>>> +               ptr += 2;<br>>> +<br>>> +               //unsigned int rep_dive = array_uint16_le(buf + ptr);<br>>> +               ptr += 2;<br>>> +<br>>> +               dive->mintemp.mkelvin =  C_to_mkelvin((float)array_uint16_le(buf + ptr)/10);// C->mK<br>>> +               ptr += 2;<br>>> +<br>>> +               dive->maxtemp.mkelvin =  C_to_mkelvin((float)array_uint16_le(buf + ptr)/10);// C->mK<br>>> +               ptr += 2;<br>>> +<br>>> +               dive->salinity = *(buf + ptr);  // ???<br>>> +               ptr += 1;<br>>> +<br>>> +               unsigned int sample_count = array_uint32_le(buf + ptr);<br>>> +               ptr += 4;<br>>> +<br>>> +               // Sample interval<br>>> +               unsigned char sample_interval;<br>>> +               sample_interval = 1;<br>>> +<br>>> +               unsigned char intervals[6] = {1,2,5,10,30,60};<br>>> +               if (*(buf + ptr) < 6)<br>>> +                       sample_interval = intervals[*(buf + ptr)];<br>>> +               ptr += 1;<br>>> +<br>>> +               float start_cns = 0;<br>>> +               unsigned char dive_mode = 0, algorithm = 0;<br>>> +               if (array_uint32_le(buf + ptr) != sample_count) {<br>>> +                       // Xeo, with CNS and OTU<br>>> +                       start_cns = *(float *) (buf + ptr);<br>>> +                       ptr += 4;<br>>> +                       dive->cns = *(float *) (buf + ptr);     // end cns<br>>> +                       ptr += 4;<br>>> +                       dive->otu = *(float *) (buf + ptr);<br>>> +                       ptr += 4;<br>>> +                       dive_mode = *(buf + ptr++);     // 0=Deco, 1=Gauge, 2=None<br>>> +                       algorithm = *(buf + ptr++);     // 0=ZH-L16C+GF<br>>> +                       sample_count = array_uint32_le(buf + ptr);<br>>> +               }<br>>> +               ptr += 4;<br>>> +<br>>> +               // Parse dive samples<br>>> +               const unsigned char *ds = buf + ptr;<br>>> +               const unsigned char *ts = buf + ptr + sample_count * 2 + 4;<br>>> +               const unsigned char *ps = buf + ptr + sample_count * 4 + 4;<br>>> +               unsigned int ps_count = array_uint32_le(ps);<br>>> +               ps += 4;<br>>> +<br>>> +               // Bump ptr<br>>> +               ptr += sample_count * 4 + 4;<br>>> +<br>>> +               // Handle events<br>>> +               unsigned int event;<br>>> +               unsigned int ps_ptr;<br>>> +               ps_ptr = 0;<br>>> +<br>>> +               unsigned int d = 0, e;<br>>> +               int event_time, mbar, sensor;<br>>> +<br>>> +               // Loop through events<br>>> +               for (e = 0; e < ps_count; e++) {<br>>> +                       // Get event<br>>> +                       event = array_uint16_le(ps + ps_ptr);<br>>> +                       ps_ptr += 2;<br>>> +<br>>> +                       switch (event) {<br>>> +                       case 0x0002:    //      Unknown<br>>> +                       case 0x0004:    //      Unknown<br>>> +                               ps_ptr +=  4;<br>>> +                               continue;<br>>> +                       case 0x0005:    // Unknown<br>>> +                               ps_ptr += 6;<br>>> +                               continue;<br>>> +                       case 0x0007:    // Gas<br>>> +                               // 4 byte time<br>>> +                               // 1 byte O2, 1 bye He<br>>> +                               ps_ptr += 6;<br>>> +                               continue;<br>>> +                       case 0x0008:<br>>> +                               // 4 byte time<br>>> +                               // 2 byte gas set point 2<br>>> +                               ps_ptr += 6;<br>>> +                               continue;<br>>> +                       case 0x000f:<br>>> +                               // Tank pressure<br>>> +                               event_time = array_uint32_le(ps + ps_ptr);<br>>> +                               sensor = 0; //array_uint16_le(ps + ps_ptr + 4);<br>>> +                               mbar = array_uint16_le(ps + ps_ptr + 6) * 10; // cb->mb<br>>> +                               // 1 byte PSR<br>>> +                               // 1 byte ST<br>>> +                               ps_ptr += 10;<br>>> +                               break;<br>>> +                       case 0x0010:<br>>> +                               ps_ptr += 26;<br>>> +                               continue;<br>>> +                       case 0x0015:    // Unknown<br>>> +                               ps_ptr += 2;<br>>> +                               continue;<br>>> +                       default:<br>>> +                               ps_ptr += 4;<br>>> +                               continue;<br>>> +                       }<br>>> +<br>>> +                       int sample_time, next_time, last_time;<br>>> +                       int depth_mm, last_depth, temp_mk, last_temp;<br>>> +<br>>> +                       while (true) {<br>>> +                               sample = prepare_sample(dc);<br>>> +<br>>> +                               // Get sample times<br>>> +                               sample_time = d * sample_interval;<br>>> +                               depth_mm = array_uint16_le(ds + d * 2) * 10; // cm->mm<br>>> +                               temp_mk = C_to_mkelvin(array_uint16_le(ts + d * 2) / 10); // dC->mK<br>>> +                               next_time = (d < sample_count - 1 ? (d + 1) * sample_interval : sample_time);<br>>> +                               last_time = (d ? (d - 1) * sample_interval : 0);<br>>> +<br>>> +                               if (d == sample_count) {<br>>> +                                       // We still have events to record<br>>> +                                       sample->time.seconds = event_time;<br>>> +                                       sample-><a href="http://depth.mm">depth.mm</a> == array_uint16_le(ds + (d - 1) * 2) * 10; // cm->mm<br>>> +                                       sample->temperature.mkelvin = C_to_mkelvin(array_uint16_le(ts + (d - 1) * 2) / 10); // dC->mK<br>>> +                                       sample->sensor = sensor;<br>>> +                                       sample->cylinderpressure.mbar = mbar;<br>>> +                                       finish_sample(dc);<br>>> +<br>>> +                                       break;<br>>> +                               } else if (event_time > sample_time) {<br>>> +                                       // Record sample and loop<br>>> +                                       sample->time.seconds = sample_time;<br>>> +                                       sample-><a href="http://depth.mm">depth.mm</a> = depth_mm;<br>>> +                                       sample->temperature.mkelvin = temp_mk;<br>>> +                                       finish_sample(dc);<br>>> +                                       d++;<br>>> +<br>>> +                                       continue;<br>>> +                               } else if (event_time == sample_time) {<br>>> +                                       sample->time.seconds = sample_time;<br>>> +                                       sample-><a href="http://depth.mm">depth.mm</a> = depth_mm;<br>>> +                                       sample->temperature.mkelvin = temp_mk;<br>>> +                                       sample->sensor = sensor;<br>>> +                                       sample->cylinderpressure.mbar = mbar;<br>>> +                                       finish_sample(dc);<br>>> +<br>>> +                                       break;<br>>> +                               } else {        // Event is prior to sample<br>>> +                                       sample->time.seconds = event_time;<br>>> +                                       sample->sensor = sensor;<br>>> +                                       sample->cylinderpressure.mbar = mbar;<br>>> +                                       if (last_time == sample_time) {<br>>> +                                               sample-><a href="http://depth.mm">depth.mm</a> = depth_mm;<br>>> +                                               sample->temperature.mkelvin = temp_mk;<br>>> +                                       } else {<br>>> +                                               // Extrapolate<br>>> +                                               last_depth = array_uint16_le(ds + (d - 1) * 2) * 10; // cm->mm<br>>> +                                               last_temp = C_to_mkelvin(array_uint16_le(ts + (d - 1) * 2) / 10); // dC->mK<br>>> +                                               sample-><a href="http://depth.mm">depth.mm</a> = last_depth + (depth_mm - last_depth)<br>>> +                                                       * (event_time - last_time) / sample_interval;<br>>> +                                               sample->temperature.mkelvin = last_temp + (temp_mk - last_temp)<br>>> +                                                       * (event_time - last_time) / sample_interval;<br>>> +                                       }<br>>> +                                       finish_sample(dc);<br>>> +<br>>> +                                       break;<br>>> +                               }<br>>> +                       } // while (true);<br>>> +               } // for each event sample<br>>> +<br>>> +               // record trailing depth samples<br>>> +               for ( ;d < sample_count; d++) {<br>>> +                       sample = prepare_sample(dc);<br>>> +                       sample->time.seconds = d * sample_interval;<br>>> +<br>>> +                       sample-><a href="http://depth.mm">depth.mm</a> = array_uint16_le(ds + d * 2) * 10; // cm->mm<br>>> +                       sample->temperature.mkelvin =<br>>> +                               C_to_mkelvin((float)array_uint16_le(ts + d * 2) / 10);<br>>> +                       finish_sample(dc);<br>>> +               }<br>>> +<br>>> +               if (log_version == 3 && model == 4) {<br>>> +                       // Advance to begin of next dive<br>>> +                       switch (array_uint16_le(ps + ps_ptr)) {<br>>> +                       case 0x0000:<br>>> +                               ps_ptr += 5;<br>>> +                               break;<br>>> +                       case 0x0100:<br>>> +                               ps_ptr += 7;<br>>> +                               break;<br>>> +                       case 0x0200:<br>>> +                               ps_ptr += 9;<br>>> +                               break;<br>>> +                       case 0x0300:<br>>> +                               ps_ptr += 11;<br>>> +                               break;<br>>> +                       case 0x0b0b:<br>>> +                               ps_ptr += 27;<br>>> +                               break;<br>>> +                       }<br>>> +<br>>> +                       while (*(ps + ps_ptr) != 0x04)<br>>> +                               ps_ptr++;<br>>> +               }<br>>> +<br>>> +               // End dive<br>>> +               dive->downloaded = true;<br>>> +               record_dive(dive);<br>>> +               mark_divelist_changed(true);<br>>> +<br>>> +               // Advance ptr for next dive<br>>> +               ptr += ps_ptr + 4;<br>>> +       } // while<br>>> +<br>>> +       save_dives("/tmp/test.xml");<br>>> +}<br>>> +<br>>> +<br>>> +int<br>>> +try_to_open_liquivision(const char *filename, struct memblock *mem)<br>>> +{<br>>> +       void *name;<br>>> +       const unsigned char *buf = mem->buffer;<br>>> +       unsigned int buf_size = mem->size;<br>>> +       unsigned int ptr;<br>>> +       int log_version;<br>>> +<br>>> +       // Get name<br>>> +       unsigned int len = array_uint32_le(buf);<br>>> +       if (len) {<br>>> +               name = malloc(len);<br>>> +               strncpy(name, buf + 4, len);<br>>> +       }<br>>> +       ptr = 4 + len;<br>>> +<br>>> +       unsigned int dive_count = array_uint32_le(buf + ptr);<br>>> +       if (dive_count == 0xffffffff) {<br>>> +               // File version 3.0<br>>> +               log_version = 3;<br>>> +               ptr += 6;<br>>> +               dive_count = array_uint32_le(buf + ptr);<br>>> +       } else {<br>>> +               log_version = 2;<br>>> +       }<br>>> +       ptr += 4;<br>>> +<br>>> +       parse_dives(log_version, buf + ptr, buf_size - ptr);<br>>> +<br>>> +       return 1;<br>>> +}<br>>> diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp<br>>> index cc2d970..5811d4e 100644<br>>> --- a/qt-ui/mainwindow.cpp<br>>> +++ b/qt-ui/mainwindow.cpp<br>>> @@ -725,7 +725,8 @@ QString MainWindow::filter()<br>>>         QString f;<br>>>         f += "ALL ( *.ssrf *.xml *.XML *.uddf *.udcf *.UDFC *.jlb *.JLB ";<br>>>         f += "*.sde *.SDE *.dld *.DLD ";<br>>> -       f += "*.db *.can";<br>>> +       f += "*.db *.can ";<br>>> +       f += "*.lvd ";<br>>>         f += ");;";<br>>><br>>>         f += "Subsurface (*.ssrf);;";<br>>> @@ -737,7 +738,8 @@ QString MainWindow::filter()<br>>>         f += "SDE (*.sde *.SDE);;";<br>>>         f += "DLD (*.dld *.DLD);;";<br>>>         f += "DB (*.db);;";<br>>> -       f += "CAN (*.can)";<br>>> +       f += "CAN (*.can);;";<br>>> +       f += "LVD (*.lvd)";<br>>><br>>>         return f;<br>>>  }<br>>> @@ -1247,7 +1249,7 @@ void MainWindow::loadFiles(const QStringList fileNames)<br>>>  void MainWindow::on_actionImportDiveLog_triggered()<br>>>  {<br>>>         QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Open dive log file"), lastUsedDir(),<br>>> -               tr("Dive log files (*.xml *.uddf *.udcf *.csv *.jlb *.dld *.sde *.db *.can);;"<br>>> +               tr("Dive log files (*.xml *.uddf *.udcf *.csv *.jlb *.dld *.sde *.db *.can *.lvd);;"<br>>>                         "XML files (*.xml);;UDDF/UDCF files(*.uddf *.udcf);;JDiveLog files(*.jlb);;"<br>>>                         "Suunto files(*.sde *.db);;CSV files(*.csv);;MkVI files(*.txt);;All files(*)"));<br>>><br>>> diff --git a/<a href="http://subsurface.pro">subsurface.pro</a> b/<a href="http://subsurface.pro">subsurface.pro</a><br>>> index 7b38fd6..07e7b29 100644<br>>> --- a/<a href="http://subsurface.pro">subsurface.pro</a><br>>> +++ b/<a href="http://subsurface.pro">subsurface.pro</a><br>>> @@ -120,6 +120,7 @@ SOURCES =  \<br>>>         file.c \<br>>>         gettextfromc.cpp \<br>>>         libdivecomputer.c \<br>>> +       liquivision.c \<br>>>         load-git.c \<br>>>         main.cpp \<br>>>         membuffer.c \<br>>> --<br>>> 1.8.3.1<br>>><br>>> _______________________________________________<br>>> subsurface mailing list<br>>> <a href="mailto:subsurface@subsurface-divelog.org">subsurface@subsurface-divelog.org</a><br>>> <a href="http://lists.subsurface-divelog.org/cgi-bin/mailman/listinfo/subsurface">http://lists.subsurface-divelog.org/cgi-bin/mailman/listinfo/subsurface</a><br>><br>><br></div>