]> err.no Git - gant/commitdiff
Fix up download support, change file names
authorTollef Fog Heen <tfheen@err.no>
Sat, 10 Sep 2011 08:50:30 +0000 (10:50 +0200)
committerTollef Fog Heen <tfheen@err.no>
Sat, 10 Sep 2011 08:50:30 +0000 (10:50 +0200)
- Make downloading more than one chunk work
- Make the output file name be the date and time of the workout
- Only download incomplete or not-seen files
- Add support for a single command to list the directory entries

fr60.c

diff --git a/fr60.c b/fr60.c
index 75ee63a64404f78c812524f65057df9306d7cc66..35b84b9f24461c43611edb159d5d414a445ccc7f 100644 (file)
--- a/fr60.c
+++ b/fr60.c
 #include <time.h>
 #include <math.h>
 #include <ctype.h>
+#include <stdarg.h>
 
 #include "antdefs.h"
 
+#define FIT_TIMESTAMP_PATTERN "%F.%H.%M.%S"
+#define FIT_FILE_NAME_PATTERN "antfs-dump.%s.fit"
+
 #define ANTFS_DIR_MAX_SIZE 256  // fairly arbitrary
 
 static int sentauth;
@@ -46,6 +50,9 @@ static int verbose;
 static int downloadfinished = 0;
 static int downloadstarted = 0;
 static int downloadingfilenum = -1;
+static int dl_s_fn = -1;
+static int dl_s_offset = -1;
+static int dl_s_crc = -1;
 static int sentid = 0;
 
 static uint mydev = 0;
@@ -63,6 +70,7 @@ static int nphase0;
 
 static char *authfile;
 static char *progname;
+static char *action = NULL;
 
 static uchar *blast;
 
@@ -115,18 +123,52 @@ struct pair_msg {
 struct antfs_dir_header {
        uchar version;         // ?
        uchar header_length;   // ?
-       uchar unknown1[6];     // probably unused (seems to always be zero)
+       uchar time_format;     /* 0 – Device will use time as
+                                 described in the Date parameter
+                                 below, if the correct time is not
+                                 known (prior to initial time sync)
+                                 the device will use Time Format U*1
+                                 system time instead.
+
+                                 1 - Device will only use system time
+                                 (seconds since power up).
+
+                                 2 – Device will only use the Date
+                                 parameter as a counter.
+                              */
+
+       uchar reserved[5];     // probably unused (seems to always be zero)
        uint system_time;      // seconds since last reset
        uint real_time;        // UTC, seconds since 1989-12-31 00:00
 };
 
+#define FILETYPE_FIT 128
+
+#define SF_FIT_DEVICE 1
+#define SF_FIT_HW_SETTINGS 2
+#define SF_FIT_SPORT_SETTINGS 3
+#define SF_FIT_ACTIVITY 4
+#define SF_FIT_WORKOUT 5
+#define SF_FIT_COURSE 6
+#define SF_FIT_WEIGHT 9
+#define SF_FIT_TOTALS 10
+#define SF_FIT_GOALS 11
+#define SF_FIT_BLOOD_PRESSURE 14
+
 struct antfs_dir_entry {
        ushort fileno;
        uchar filetype;        // 128 = FIT file
-       uchar subtype;
-       ushort unknown1;
-       uchar unknown2;
+       uchar subfiletype;
+       uchar identifier[2];
+       uchar app_flags;
        uchar flags;
+  // Bit 0 and 1 are reserved
+#define FFLAG_CRYPTO (1 << 2)
+#define FFLAG_APPEND (1 << 3)
+#define FFLAG_ARCHIVE (1 << 4)
+#define FFLAG_ERASE (1 << 5)
+#define FFLAG_WRITE (1 << 6)
+#define FFLAG_READ (1 << 7)
        uint size;
        uint last_modified;
 };
@@ -189,9 +231,10 @@ void do_open_channel(uchar chan, int net) {
 }
 
 void
-start_download(uchar chan, int fileno, int offset, int crcseed)
+start_download(uchar chan, int fileno, int offset, int crcseed, int initial)
 {
        uchar getfilepkt[100];
+       int max_size = 0;
 
        (void)chan;(void)fileno;(void)getfilepkt;
 
@@ -199,7 +242,7 @@ start_download(uchar chan, int fileno, int offset, int crcseed)
        dl_s_offset = offset;
        dl_s_crc = crcseed;
 
-       sprintf((char*)getfilepkt, "4409%02x%02x%02x%02x%02x%02x" "00%02x%02x%02x00000000",
+       sprintf((char*)getfilepkt, "4409%02x%02x%02x%02x%02x%02x" "%02x%02x%02x%02x%02x%02x%02x%02x",
                (fileno&0xff),
                (fileno>>8)&0xff,
                offset & 0xff,
@@ -207,15 +250,20 @@ start_download(uchar chan, int fileno, int offset, int crcseed)
                (offset >> 16) & 0xff,
                (offset >> 24) & 0xff,
                (offset == 0 ? 1 : 0),
+               (initial == 1 ? 1 : 0),
                crcseed & 0xff,
-               (crcseed >> 8) & 0xff);
+               (crcseed >> 8) & 0xff,
+               max_size & 0xff,
+               (max_size >> 8) & 0xff,
+               (max_size >> 16) & 0xff,
+               (max_size >> 24) & 0xff);
        printf("starting download: %s\n", getfilepkt);
        ANT_SendBurstTransferA(chan, getfilepkt, strlen((char*)getfilepkt)/16);
 }
 
 void
 retry_transfer(uchar chan) {
-       start_download(chan, dl_s_fn, dl_s_offset, dl_s_crc);
+       start_download(chan, dl_s_fn, dl_s_offset, dl_s_crc, 0);
 }
 
 size_t
@@ -227,25 +275,53 @@ format_time(char* s, char *format, size_t max, time_t tm)
        return strftime(s, max, format, &local_tm);
 }
 
+void
+print_dir_entry(struct antfs_dir_entry *e)
+{
+       char textual_time[256];
+       format_time(textual_time, "%c", 256, e->last_modified);
+       printf("%d: type=(%d,%d) identifier=0x%02x%02x app_flags=%d flags=0x%02x size=%d last_modified=%d (%s)\n",
+              e->fileno,
+              e->filetype,
+              e->subfiletype,
+              e->identifier[0],
+              e->identifier[1],
+              e->app_flags,
+              e->flags,
+              e->size,
+              e->last_modified,
+              textual_time);
+}
+
 void
 decode_antfs_dir(const uchar* blast, uint blsize)
 {
        struct antfs_dir_header hdr;
        char textual_time[256];
-       if (24 + ANTFSHEADERSIZE > blsize) {
-               fprintf(stderr, "truncated ant-fs directory header (blsize=%d)\n", blsize);
-               exit(1);
-       }
-       assert(sizeof(struct antfs_dir_header) == ANTFSHEADERSIZE);
+       int file_offset;
+       int dir_start_offset = 24;
 
-       memcpy(&hdr, blast + 24, ANTFSHEADERSIZE);
-       printf("system time: %d\n", hdr.system_time);
-       format_time(textual_time, "%c", 256, hdr.real_time);
-       printf("real time: %d (%s)\n", hdr.real_time, textual_time);
+       if (antfsdir_used == 0) {
+               if (24 + ANTFSHEADERSIZE > blsize) {
+                       fprintf(stderr, "truncated ant-fs directory header (blsize=%d)\n", blsize);
+                       exit(1);
+               }
+               assert(sizeof(struct antfs_dir_header) == ANTFSHEADERSIZE);
+
+               memcpy(&hdr, blast + 24, ANTFSHEADERSIZE);
+               printf("system time: %d\n", hdr.system_time);
+               format_time(textual_time, "%c", 256, hdr.real_time);
+               printf("real time: %d (%s)\n", hdr.real_time, textual_time);
+               dir_start_offset = 40;
+       } else {
+               if (24 + ANTFSDIRSIZE > blsize) {
+                       fprintf(stderr, "truncated ant-fs directory block (blsize=%d)\n", blsize);
+                       exit(1);
+               }
+       }
 
-       antfsdir_used = 0;
-       for ( ;; ) {
-               uint pos = 40 + antfsdir_used * sizeof(struct antfs_dir_entry);
+       for (file_offset = 0 ;; file_offset++) {
+               uint pos = dir_start_offset + file_offset * sizeof(struct antfs_dir_entry);
                assert(sizeof(struct antfs_dir_entry) == ANTFSDIRSIZE);
                if (pos + ANTFSDIRSIZE > blsize) {
                        if (blast[pos] == 0) {
@@ -254,69 +330,93 @@ decode_antfs_dir(const uchar* blast, uint blsize)
                        fprintf(stderr, "premature end of ant-fs directory after %d entries (blsize=%d)\n", antfsdir_used, blsize);
                        exit(1);
                }
-               memcpy(antfsdir + antfsdir_used, blast + pos, ANTFSDIRSIZE);
-               if (verbose) {
-                       format_time(textual_time, 256, antfsdir[antfsdir_used].last_modified);
-
-                       printf("directory entry %d: pos=%d, type=(%d,%d) unknown1=%d unknown2=%d flags=0x%02x size=%d last_modified=%d (%s)\n",
-                               antfsdir_used,
-                               antfsdir[antfsdir_used].fileno,
-                               antfsdir[antfsdir_used].filetype,
-                               antfsdir[antfsdir_used].subtype,
-                               antfsdir[antfsdir_used].unknown1,
-                               antfsdir[antfsdir_used].unknown2,
-                               antfsdir[antfsdir_used].flags,
-                               antfsdir[antfsdir_used].size,
-                               antfsdir[antfsdir_used].last_modified,
-                               textual_time);
-               }
-               if (antfsdir[antfsdir_used].fileno == 0) {
+
+               print_dir_entry((struct antfs_dir_entry*) (blast+pos));
+               if (((struct antfs_dir_entry*) blast)->fileno == 0) {
                        break;
                }
+
+               memcpy(antfsdir + antfsdir_used, blast + pos, ANTFSDIRSIZE);
+/*             print_dir_entry(&antfsdir[antfsdir_used]);*/
                ++antfsdir_used;
        }
        D("%d entries in directory\n", antfsdir_used);
 }
 
-void
-save_antfs_file(const uchar* blast, uint blsize)
+int
+save_antfs_file(const uchar chan, const uchar* blast, uint blsize)
 {
        uint size;
        int storefd;
        char filename[256];
+       char ts[256];
        int nw;
+       uint offset, remaining;
 
        D("received ant-fs file %d\n", antfsdir[downloadingfilenum].fileno);
        if (blsize < 24) {
                fprintf(stderr, "didn't receive full ant-fs header\n");
                exit(1);
        }
-       size = blast[12] + blast[13]*256 + blast[14]*256*256 + blast[15]*256*256*256;
-       if (size > blsize - 24 || size > blsize - 24 + 16) {
-               if (dbg) printf("unexpected ant-fs packet: header says %d bytes, but received %d\n", size, blsize - 24);
+       remaining = blast[12] + blast[13]*256 + blast[14]*256*256 + blast[15]*256*256*256;
+       offset = blast[16] + blast[17]*256 + blast[18]*256*256 + blast[19]*256*256*256;
+       size = blast[20] + blast[21]*256 + blast[22]*256*256 + blast[23]*256*256*256;
+       printf("save_antfs_file: remaining=%d offset=%d size=%d\n", remaining, offset, size);
+       if (remaining > blsize - 24 || remaining > blsize - 24 + 16) {
+               fprintf(stderr, "unexpected ant-fs packet: header says %d bytes, but received %d\n", size, blsize - 24);
                exit(1);
        }
-       format_time(ts, "%F.%H.%M.%S", 256, antfsdir[downloadingfilenum].last_modified);
-       sprintf(filename, "antfs-dump.%s.fit", ts);
+       format_time(ts, FIT_TIMESTAMP_PATTERN, 256, antfsdir[downloadingfilenum].last_modified);
+       sprintf(filename, FIT_FILE_NAME_PATTERN, ts);
        storefd = open(filename, O_WRONLY | O_CREAT, 0644);
        if (storefd < 0) {
                perror(filename);
                exit(1);
        }
-       nw = write(storefd, blast + 24, size);
-       if (nw < 0 || (uint)nw != size) {
+       if (lseek(storefd, offset, SEEK_SET) < 0) {
+               perror("lseek");
+               exit(1);
+       }
+
+       nw = write(storefd, blast + 24, remaining);
+       if (nw < 0 || (uint)nw != remaining) {
                fprintf(stderr, "store write failed fd %d %d\n", storefd, nw);
                perror("write");
                exit(1);
        }
        close(storefd);
        if (verbose) printf("%s (%d bytes)\n", filename, size);
+       ANT_RequestMessage(chan, MESG_CHANNEL_STATUS_ID); //informative
+       if (offset+remaining < size) {
+               printf("=> Starting download: %d %d %d\n", offset, remaining, size);
+               start_download(chan, antfsdir[downloadingfilenum].fileno,offset+remaining, blast[blsize-1] * 256 + blast[blsize-2], 0);
+               return 1;
+       }
+       return 0;
+}
+
+static int interesting_file(struct antfs_dir_entry *e, int dir_used) {
+       char ts[256], filename[256];
+       struct stat statbuf;
+       if (!(e->filetype == FILETYPE_FIT &&
+             ( e->subfiletype == SF_FIT_ACTIVITY ||
+               e->subfiletype == SF_FIT_WORKOUT ) &&
+             e->fileno < dir_used))
+               return 0;
+
+       format_time(ts, FIT_TIMESTAMP_PATTERN, 256, e->last_modified);
+       sprintf(filename, FIT_FILE_NAME_PATTERN, ts);
+       if (stat(filename, &statbuf) == 0 &&
+           S_ISREG(statbuf.st_mode) &&
+           statbuf.st_size == e->size)
+               return 0;
+
+       return 1;
 }
 
 uchar
 chevent(uchar chan, uchar event)
 {
-       uchar last;
        struct ack_msg ack;
        struct auth_msg auth;
        struct pair_msg pair;
@@ -471,7 +571,7 @@ chevent(uchar chan, uchar event)
                                downloadstarted = 1;
                                downloadingfilenum = -1;
                                D("downloading directory\n");
-                               start_download(chan, 0);
+                               start_download(chan, 0, 0, 0, 1);
                        }
                        if (downloadfinished) {
                                D("finished download\n");
@@ -521,26 +621,75 @@ chevent(uchar chan, uchar event)
                                        printf(".");
                        printf("\n");
                }
+
                if (blsize >= 10 && blast[8] == 0x44 && blast[9] == 0x89) {
                        if (downloadingfilenum == -1) {
-                               if (dbg) printf("received ant-fs directory\n");
+                               uint offset, remaining, size;
+                               D("received ant-fs directory\n");
+
+                               remaining = blast[12] + blast[13]*256 + blast[14]*256*256 + blast[15]*256*256*256;
+                               offset = blast[16] + blast[17]*256 + blast[18]*256*256 + blast[19]*256*256*256;
+                               size = blast[20] + blast[21]*256 + blast[22]*256*256 + blast[23]*256*256*256;
+
                                decode_antfs_dir(blast, blsize);
 
+                               if (offset+remaining < size) {
+                                       start_download(chan, 0, offset+remaining, blast[blsize-1] * 256 + blast[blsize-2], 0);
+                                       return 1;
+                               }
+
+                               D("=)== used: %d\n", antfsdir_used);
+
+                               if (action && strcmp(action, "ls") == 0) {
+                                       exit(0);
+                               }
+
                                if (antfsdir_used > 0) {
-                                       downloadingfilenum = 0;
-                                       if (dbg) printf("downloading first file (%d)\n", antfsdir[downloadingfilenum].fileno);
-                                       start_download(chan, antfsdir[downloadingfilenum].fileno);
+                                       if (action && strncmp(action, "get=", 4) == 0) {
+                                               int i;
+
+                                               downloadingfilenum = atoi(action+4);
+                                               for (i = 0; i < antfsdir_used; i++) {
+                                                       if (downloadingfilenum == antfsdir[i].fileno) {
+                                                               downloadingfilenum = i;
+                                                               break;
+                                                       }
+                                               }
+                                               if (i == antfsdir_used) {
+                                                       fprintf(stderr, "File not found, can't download\n");
+                                                       exit(1);
+                                               }
+                                       } else {
+                                               downloadingfilenum = 0;
+                                               while (! interesting_file(&antfsdir[downloadingfilenum], antfsdir_used)) {
+                                                       D("Skipping %d; not .fit file/not interesting\n", downloadingfilenum);
+                                                       downloadingfilenum++;
+                                               }
+                                       }
+                                       if (downloadingfilenum == antfsdir_used) {
+                                               printf("No files to download");
+                                       } else {
+                                               D("downloading first file (%d)\n", antfsdir[downloadingfilenum].fileno);
+                                               start_download(chan, antfsdir[downloadingfilenum].fileno, 0, 0, 1);
+                                       }
                                }
                        } else {
-                               save_antfs_file(blast, blsize);
-
-                               if (downloadingfilenum >= antfsdir_used - 1) {
-                                       if (dbg) printf("all files downloaded\n");
-                                       downloadfinished = 1;
-                               } else {
-                                       ++downloadingfilenum;
-                                       if (dbg) printf("downloading next file (%d)\n", antfsdir[downloadingfilenum].fileno);
-                                       start_download(chan, antfsdir[downloadingfilenum].fileno);
+                               if (save_antfs_file(chan, blast, blsize) == 0) {
+                                       if (downloadingfilenum >= antfsdir_used - 1 ||
+                                           (action && strncmp(action, "get=", 4) == 0)) {
+                                               D("all files downloaded\n");
+                                               downloadfinished = 1;
+                                       } else {
+                                               downloadingfilenum++;
+                                               while (! interesting_file(&antfsdir[downloadingfilenum], antfsdir_used)) {
+                                                       D("Skipping %d; not .fit file\n", downloadingfilenum);
+                                                       downloadingfilenum++;
+                                               }
+                                               if (downloadingfilenum < antfsdir_used) {
+                                                       D("downloading next file (%d)\n", antfsdir[downloadingfilenum].fileno);
+                                                       start_download(chan, antfsdir[downloadingfilenum].fileno, 0, 0, 1);
+                                               }
+                                       }
                                }
                        }
                }
@@ -635,9 +784,10 @@ revent(uchar chan, uchar event)
        case EVENT_TRANSFER_RX_FAILED:
                D("RX Transfer failed %d\n", ebuf[1]);
                // this may not be the best idea in all cases...
-               if (downloadingfilenum >= 0) {
+               retry_transfer(chan);
+/*             if (downloadingfilenum >= 0) {
                        start_download(chan, antfsdir[downloadingfilenum].fileno);
-               }
+                       }*/
                break;
        case EVENT_TRANSFER_TX_FAILED:
                fprintf(stderr, "TX Transfer failed %d\n", ebuf[1]);
@@ -747,7 +897,7 @@ main(int ac, char *av[])
                        sprintf(authfile, "%s/.gant", getenv("HOME"));
        }
        progname = av[0];
-       while ((c = getopt(ac, av, "a:d:i:m:PpvDrnzf:")) != -1) {
+       while ((c = getopt(ac, av, "l:a:d:i:m:PpvDrnzf:")) != -1) {
                switch(c) {
                        case 'a':
                                authfile = optarg;
@@ -761,6 +911,9 @@ main(int ac, char *av[])
                        case 'i':
                                myid = atoi(optarg);
                                break;
+                       case 'l':
+                               action = strdup(optarg);
+                               break;
                        case 'm':
                                mydev = atoi(optarg);
                                break;