Merge pull request #39 from subsurface/garminBigEndian

Garmin: attempt to parse big endian .FIT files
This commit is contained in:
Dirk Hohndel 2022-10-06 13:39:16 -07:00 committed by GitHub
commit 43a503f6f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -139,6 +139,7 @@ typedef struct garmin_parser_t {
} gps;
struct dc_field_cache cache;
unsigned char is_big_endian; // instead of bool
} garmin_parser_t;
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);
};
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(name, 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)) \
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; \
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 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;
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);
fields = data[4];
DEBUG(garmin->base.context, "Define local type %d: %02x %02x %04x %02x %s",
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;
}
DEBUG(garmin->base.context, "Define local type %d: %02x %s %04x %02x %s",
type, data[0], data[1] ? "big-endian" : "little-endian", msg, fields, desc->msg_name);
if (fields > MAXFIELDS) {
ERROR(garmin->base.context, "Too many fields in description: %d (max %d)\n", fields, MAXFIELDS);
return -1;
@ -1280,13 +1293,16 @@ traverse_data(struct garmin_parser_t *garmin)
hdrsize = data[0];
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);
if (memcmp(data+8, ".FIT", 4))
if (memcmp(data+8, ".FIT", 4)) {
DEBUG(garmin->base.context, " missing .FIT marker");
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;
}
garmin->dive.protocol = protocol;
garmin->dive.profile = profile;