#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;
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;
static char *authfile;
static char *progname;
+static char *action = NULL;
static uchar *blast;
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;
};
}
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;
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,
(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
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) {
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;
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");
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);
+ }
+ }
}
}
}
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]);
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;
case 'i':
myid = atoi(optarg);
break;
+ case 'l':
+ action = strdup(optarg);
+ break;
case 'm':
mydev = atoi(optarg);
break;