Garmin: attempt to parse big endian .FIT files

The only examples I have seen so far are small incomplete files that
don't contain the dive profile, so I'm unsure if this is sufficient (and
why there are big endian files in the first place).

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
Dirk Hohndel 2022-10-05 14:40:46 -07:00
parent 565bb2af02
commit 4d8cdaaf8e

View File

@ -139,6 +139,7 @@ typedef struct garmin_parser_t {
} gps; } gps;
struct dc_field_cache cache; struct dc_field_cache cache;
unsigned char is_big_endian; // instead of bool
} garmin_parser_t; } garmin_parser_t;
typedef int (*garmin_data_cb_t)(unsigned char type, const unsigned char *data, int len, void *user); typedef int (*garmin_data_cb_t)(unsigned char type, const unsigned char *data, int len, void *user);
@ -390,6 +391,19 @@ struct field_desc {
void (*parse)(struct garmin_parser_t *, unsigned char base_type, const unsigned char *data); void (*parse)(struct garmin_parser_t *, unsigned char base_type, const unsigned char *data);
}; };
static inline int base_type_is_integer(unsigned char base_type)
{
return !memcmp(base_type_info[base_type].type_name + 1, "INT", 3);
}
static inline unsigned int array_uint_endian(const unsigned char *p, unsigned int type_size, unsigned char bigendian)
{
if (bigendian)
return array_uint_be(p, type_size);
else
return array_uint_le(p, type_size);
}
#define DECLARE_FIELD(msg, name, type) __DECLARE_FIELD(msg##_##name, type) #define DECLARE_FIELD(msg, name, type) __DECLARE_FIELD(msg##_##name, type)
#define __DECLARE_FIELD(name, type) \ #define __DECLARE_FIELD(name, type) \
static void parse_##name(struct garmin_parser_t *, const type); \ static void parse_##name(struct garmin_parser_t *, const type); \
@ -397,10 +411,14 @@ struct field_desc {
{ \ { \
if (strcmp(#type, base_type_info[base_type].type_name)) \ if (strcmp(#type, base_type_info[base_type].type_name)) \
fprintf(stderr, "%s: %s should be %s\n", #name, #type, base_type_info[base_type].type_name); \ fprintf(stderr, "%s: %s should be %s\n", #name, #type, base_type_info[base_type].type_name); \
type val = *(type *)p; \ type val; \
if (base_type_info[base_type].type_size > 1 && base_type_is_integer(base_type)) \
val = (type)array_uint_endian(p, base_type_info[base_type].type_size, g->is_big_endian); \
else \
val = *(type *)p; \
if (val == type##_INVAL) return; \ if (val == type##_INVAL) return; \
DEBUG(g->base.context, "%s (%s): %lld", #name, #type, (long long)val); \ DEBUG(g->base.context, "%s (%s): %lld", #name, #type, (long long)val); \
parse_##name(g, *(type *)p); \ parse_##name(g, val); \
} \ } \
static const struct field_desc name##_field_##type = { #name, parse_##name##_##type }; \ static const struct field_desc name##_field_##type = { #name, parse_##name##_##type }; \
static void parse_##name(struct garmin_parser_t *garmin, type data) static void parse_##name(struct garmin_parser_t *garmin, type data)
@ -1217,18 +1235,13 @@ static int traverse_definition(struct garmin_parser_t *garmin,
struct type_desc *desc = garmin->type_desc + type; struct type_desc *desc = garmin->type_desc + type;
int fields, devfields, len; int fields, devfields, len;
msg = array_uint16_le(data+2); // data[1] tells us if this is big or little endian
garmin->is_big_endian = data[1] != 0;
msg = array_uint_endian(data + 2, 2, garmin->is_big_endian);
desc->msg_desc = lookup_msg_desc(msg, type, &desc->msg_name); desc->msg_desc = lookup_msg_desc(msg, type, &desc->msg_name);
fields = data[4]; fields = data[4];
DEBUG(garmin->base.context, "Define local type %d: %02x %s %04x %02x %s",
DEBUG(garmin->base.context, "Define local type %d: %02x %02x %04x %02x %s", type, data[0], data[1] ? "big-endian" : "little-endian", msg, fields, desc->msg_name);
type, data[0], data[1], msg, fields, desc->msg_name);
if (data[1]) {
ERROR(garmin->base.context, "Only handling little-endian definitions\n");
return -1;
}
if (fields > MAXFIELDS) { if (fields > MAXFIELDS) {
ERROR(garmin->base.context, "Too many fields in description: %d (max %d)\n", fields, MAXFIELDS); ERROR(garmin->base.context, "Too many fields in description: %d (max %d)\n", fields, MAXFIELDS);
return -1; return -1;
@ -1280,13 +1293,16 @@ traverse_data(struct garmin_parser_t *garmin)
hdrsize = data[0]; hdrsize = data[0];
protocol = data[1]; protocol = data[1];
profile = array_uint16_le(data+2); profile = array_uint16_le(data+2); // these two fields are always little endian
datasize = array_uint32_le(data+4); datasize = array_uint32_le(data+4);
if (memcmp(data+8, ".FIT", 4)) if (memcmp(data+8, ".FIT", 4)) {
DEBUG(garmin->base.context, " missing .FIT marker");
return DC_STATUS_IO; return DC_STATUS_IO;
if (hdrsize < 12 || datasize > len || datasize + hdrsize + 2 > len) }
if (hdrsize < 12 || datasize > len || datasize + hdrsize + 2 > len) {
DEBUG(garmin->base.context, " inconsistent size information hdrsize %d datasize %d len %d", hdrsize, datasize, len);
return DC_STATUS_IO; return DC_STATUS_IO;
}
garmin->dive.protocol = protocol; garmin->dive.protocol = protocol;
garmin->dive.profile = profile; garmin->dive.profile = profile;