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:
parent
565bb2af02
commit
4d8cdaaf8e
@ -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;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user