-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfit.ts
121 lines (97 loc) · 3.11 KB
/
fit.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { BinaryReader } from "./deps.ts";
import { DefinitionRecord } from "./definition_record.ts";
import { FileHeader } from "./file_header.ts";
import { RecordHeader } from "./record_header.ts";
import { DataRecord, dataRecord } from "./data_record.ts";
import { Message } from "./message.ts";
import { calculateCrc } from "./crc.ts";
export class Fit {
header: FileHeader;
messages: Message[] = [];
constructor(io: BinaryReader) {
this.header = new FileHeader(io);
if (!this.validate(io)) {
console.error("Invalid or malformed .FIT file.");
Deno.exit(1);
}
this.messages = [];
const finished = [];
const defs: { [index: number]: DefinitionRecord } = {};
try {
while (io.position < this.header.dataSize + this.header.size) {
const h = new RecordHeader(io);
if (h.isDefinition()) {
if (h.hasDevDefs()) {
console.log("developer fields.");
Deno.exit(0);
}
const d = new DefinitionRecord(io, h.localMesssageType);
if (defs[h.localMesssageType] !== undefined) {
finished.push(defs[h.localMesssageType]);
}
defs[h.localMesssageType] = d;
} else if (h.isData()) {
const d = defs[h.localMesssageType];
const record: DataRecord = dataRecord(io, d);
if (d.globalMsgNum === 206) {
console.log("developer fields");
Deno.exit(0);
} else {
d.dataRecords.push(record);
}
} else if (h.hasTimestamp()) {
console.log("timestamp fields");
Deno.exit(0);
}
}
for (const [_key, def] of Object.entries(defs)) {
finished.push(def);
}
const groupBy = (xs: DefinitionRecord[]) => {
return xs.reduce(
(
rv: { [index: number]: DefinitionRecord[] },
x: DefinitionRecord,
) => {
(rv[x.globalMsgNum] = rv[x.globalMsgNum] || []).push(x);
return rv;
},
{},
);
};
const grouped: { [index: number]: DefinitionRecord[] } = groupBy(
finished,
);
for (const [key, obj] of Object.entries(grouped)) {
const message = new Message(Number(key), obj);
if (message.name !== undefined) {
this.messages.push(message);
}
}
} catch (err) {
console.error(err);
Deno.exit(1);
}
}
validate(io: BinaryReader): boolean {
if (this.header.size === 14) {
io.seek(0);
// The header CRC doesn't include bytes 13 and 14, which hold the header CRC...
const crc = calculateCrc(io, 0, 12);
if (crc !== this.header.crc) {
return false;
}
io.seek(14);
}
const fileCrcPosition = this.header.size + this.header.dataSize;
io.seek(fileCrcPosition);
const fileCrc = io.readUint8() + (io.readUint8() << 8);
const start = this.header.size === 12 ? 0 : this.header.size;
io.seek(start);
if (fileCrc !== calculateCrc(io, start, fileCrcPosition)) {
return false;
}
io.seek(this.header.size);
return true;
}
}