From: Tollef Fog Heen Date: Sat, 10 Sep 2011 08:50:30 +0000 (+0200) Subject: Fix up download support, change file names X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=09d2d794242d57b68c93622bbfa6836c1b7fef3e;p=gant Fix up download support, change file names - 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 --- diff --git a/fr60.c b/fr60.c index 75ee63a..35b84b9 100644 --- a/fr60.c +++ b/fr60.c @@ -15,9 +15,13 @@ #include #include #include +#include #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;