1use std::iter::FromIterator;
2use std::time::{UNIX_EPOCH, Duration};
3use std::collections::{BTreeMap};
4use nom::*;
5use ::crc::crc32::{checksum_ieee};
6use ::crc::crc16::{checksum_usb};
7
8mod varint;
9mod crc;
10
11use varint::{take_signed_leb128, take_unsigned_leb128};
12use crate::flagscolumn::{FlagsColumn};
13use crate::rwtfile::{RWTFMAGIC, RWTFTRAILER, RWTFHeader, RWTFile};
14use crate::metadata::{RWTFMetadata, TrackType};
15use crate::section::{Column, Section, SectionType};
16use crate::decode::crc::{CRC};
17
18trait Parsable {
19 type Return;
20
21 fn parse(i: &[u8]) -> IResult<&[u8], Self::Return>;
22}
23
24#[derive(Debug)]
28struct ParsedHeader {
29 metadata_table_offset: u16,
30 data_offset: u16,
31 crc: CRC<u16>
32}
33
34impl Parsable for RWTFHeader {
35 type Return = (Self, ParsedHeader);
36
37 fn parse(i: &[u8]) -> IResult<&[u8], Self::Return> {
38 do_parse!(i,
39 tag!(RWTFMAGIC) >>
40 file_version: le_u8 >>
41 le_u24 >>
42 creator_version: le_u8 >>
43 le_u24 >>
44 metadata_table_offset: le_u16 >>
45 data_offset: le_u16 >>
46 le_u16 >>
47 crc: le_u16 >>
48 ((RWTFHeader{file_version,
49 creator_version},
50 ParsedHeader{metadata_table_offset,
51 data_offset,
52 crc: CRC::new(crc, checksum_usb(&i[0..22]))})))
53 }
54}
55
56#[derive(Debug)]
60enum RWTFMetadataEntry {
61 TrackType(TrackType),
62 CreatedAt(u64),
63 Unknown,
64}
65
66fn parse_metadata_table_entry_data(i: &[u8], tag: u8) -> IResult<&[u8], RWTFMetadataEntry> {
67 match tag {
68 0x00 => {
69 match do_parse!(i,
70 _size: le_u16 >>
71 track_type_tag: le_u8 >>
72 id: le_u32 >>
73 (TrackType::from_tag(track_type_tag, id))) {
74 Ok((rest, tt)) => match tt {
75 Some(tt) => Ok((rest, RWTFMetadataEntry::TrackType(tt))),
76 None => Err(Err::Error(Context::Code(i, ErrorKind::Custom(0)))),
77 },
78 Err(_) => Err(Err::Error(Context::Code(i, ErrorKind::Custom(0)))),
79 }
80 }
81 0x01 => {
82 do_parse!(i,
83 _size: le_u16 >>
84 timestamp: le_u64 >>
85 (RWTFMetadataEntry::CreatedAt(timestamp)))
86 }
87 _ => {
88 let (rest, size) = le_u16(i)?;
89 let (rest, _data) = take!(rest, size)?;
90 Ok((rest, RWTFMetadataEntry::Unknown))
92 }
93 }
94}
95
96fn parse_metadata_table_entry(i: &[u8]) -> IResult<&[u8], RWTFMetadataEntry> {
97 do_parse!(i,
98 tag: le_u8 >>
99 entry: apply!(parse_metadata_table_entry_data, tag) >>
100 (entry))
101}
102
103impl Parsable for RWTFMetadata {
104 type Return = (Self, CRC<u16>);
105
106 fn parse(i: &[u8]) -> IResult<&[u8], Self::Return> {
107 let (rest, entries) = do_parse!(i,
108 count: le_u8 >>
109 entries: many_m_n!(count as usize, count as usize, parse_metadata_table_entry) >>
110 (entries))?;
111
112 let diff = i.offset(rest);
113 let (rest, crc) = le_u16(rest)?;
114
115 let mut created_at = None;
116 let mut track_type = None;
117
118 for entry in entries {
119 match entry {
120 RWTFMetadataEntry::TrackType(tt) => {
121 track_type = Some(tt)
122 },
123 RWTFMetadataEntry::CreatedAt(time) => {
124 created_at = UNIX_EPOCH.checked_add(Duration::new(time, 0));
125 },
126 RWTFMetadataEntry::Unknown => {},
127 }
128 }
129
130 Ok((rest, (RWTFMetadata::new(created_at, track_type),
131 CRC::new(crc, checksum_usb(&i[..diff])))))
132 }
133}
134
135impl FlagsColumn {
139 fn parse_flags_column<'a, 'b>(i: &'a [u8], types_table: &'b TypesTable, points: u32) -> IResult<&'a [u8], FlagsColumn> {
140 let width = (types_table.entries.len() + 7) / 8;
141
142 let fields = BTreeMap::from_iter(types_table.entries.iter().enumerate().map(|(i, entry)| (entry.name.clone(), i)));
143
144 let mut data = BTreeMap::new();
145 let mut remainder = i;
146 for i in 0..points {
147 let (rest, bitfield_bytes) = take!(remainder, width)?;
148 remainder = rest;
149
150 let mut bitfield_array = [0; 8];
151 for i in 0..8 {
152 bitfield_array[i] = *bitfield_bytes.get(i).unwrap_or(&0);
153 }
154
155 let bitfield_integer = u64::from_le_bytes(bitfield_array);
156
157 if bitfield_integer > 0 {
158 data.insert(i as usize, bitfield_integer);
159 }
160 }
161
162 Ok((remainder, FlagsColumn{fields: fields,
163 data: data,
164 max: (points - 1) as usize}))
165 }
166}
167
168fn parse_section_type(i: &[u8]) -> IResult<&[u8], SectionType> {
172 let (rest, tag) = le_u8(i)?;
173 match SectionType::from_tag(tag) {
174 Some(st) => Ok((rest, st)),
175 None => Err(Err::Error(Context::Code(i, ErrorKind::Custom(0)))),
176 }
177}
178
179#[derive(Debug)]
180enum ColumnType {
181 Numbers,
182 LongFloat,
183 ShortFloat,
184 Base64,
185 String,
186 Bool,
187 IDs,
188}
189
190impl ColumnType {
191 fn from_tag(tag: u8) -> Option<Self> {
192 match tag {
193 0x00 => Some(ColumnType::Numbers),
194 0x01 => Some(ColumnType::LongFloat),
195 0x02 => Some(ColumnType::ShortFloat),
196 0x03 => Some(ColumnType::Base64),
197 0x04 => Some(ColumnType::String),
198 0x05 => Some(ColumnType::Bool),
199 0x06 => Some(ColumnType::IDs),
200 _ => None
201 }
202 }
203}
204
205#[derive(Debug)]
206pub struct SectionHeader {
207 section_type: SectionType,
208 points: u32,
209 size: u64,
210 crc: CRC<u16>,
211}
212
213fn parse_section_header(i: &[u8]) -> IResult<&[u8], SectionHeader> {
214 let (rest, section_type) = parse_section_type(i)?;
215 let (rest, points) = le_u24(rest)?;
216 let (rest, size) = le_u64(rest)?;
217
218 let diff = i.offset(rest);
219 let (rest, crc) = le_u16(rest)?;
220
221 Ok((rest, SectionHeader{section_type,
222 points,
223 size,
224 crc: CRC::new(crc, checksum_usb(&i[..diff]))}))
225}
226
227#[derive(Debug)]
228struct TypesTableEntry {
229 column_type: ColumnType,
230 name: String,
231}
232
233fn parse_column_type(i: &[u8]) -> IResult<&[u8], ColumnType> {
234 let (rest, tag) = le_u8(i)?;
235 match ColumnType::from_tag(tag) {
236 Some(c) => Ok((rest, c)),
237 None => Err(Err::Error(Context::Code(i, ErrorKind::Custom(0)))),
238 }
239}
240
241fn parse_types_table_entry(i: &[u8]) -> IResult<&[u8], TypesTableEntry> {
242 do_parse!(i,
243 column_type: parse_column_type >>
244 name_len: le_u8 >>
245 name: take!(name_len) >>
246 (TypesTableEntry{column_type,
247 name: String::from_utf8_lossy(name).into_owned()}))
248}
249
250#[derive(Debug)]
251pub struct TypesTable {
252 entries: Vec<TypesTableEntry>,
253 crc: CRC<u16>,
254}
255
256fn parse_types_table(i: &[u8]) -> IResult<&[u8], TypesTable> {
257 let (rest, entries) = do_parse!(i,
258 count: le_u8 >>
259 entries: many_m_n!(count as usize, count as usize, parse_types_table_entry) >>
260 (entries))?;
261 let diff = i.offset(rest);
262 let (rest, crc) = le_u16(rest)?;
263
264 Ok((rest, TypesTable{entries,
265 crc: CRC::new(crc, checksum_usb(&i[..diff]))}))
266}
267
268fn parse_number_row<'a>(i: &'a [u8]) -> IResult<&'a [u8], i64> {
269 take_signed_leb128(i)
270}
271
272fn parse_bytes_row<'a>(i: &'a [u8]) -> IResult<&'a [u8], &'a [u8]> {
273 do_parse!(i,
274 len: take_unsigned_leb128 >>
275 bytes: take!(len) >>
276 (bytes))
277
278}
279
280fn parse_bool_row<'a>(i: &'a [u8]) -> IResult<&'a [u8], bool> {
281 do_parse!(i,
282 b: le_u8 >>
283 ({
284 if b == 0 {
285 false
286 } else {
287 true
288 }
289 }))
290}
291
292fn parse_ids_row<'a>(i: &'a [u8]) -> IResult<&'a [u8], Vec<u64>> {
293 do_parse!(i,
294 count: take_unsigned_leb128 >>
295 entries: many_m_n!(count as usize, count as usize, take_unsigned_leb128) >>
296 (entries))
297}
298
299fn parse_column<'a>(i: &'a [u8], column: &TypesTableEntry, flags: &FlagsColumn) -> IResult<&'a [u8], Column> {
300 match column.column_type {
301 ColumnType::Numbers => {
302 let mut m = BTreeMap::new();
303 let mut remainder = i;
304 let mut last = 0;
305 for index in 0..flags.len() {
306 if flags.is_present(index, &column.name) {
307 let (rest, delta) = parse_number_row(remainder)?;
308 remainder = rest;
309 let v = last + delta;
310 last = v;
311 m.insert(index, v);
312 } else {
313 remainder = &remainder[1..];
315 }
316 }
317
318 Ok((remainder, Column::Numbers(m)))
319 }
320 ColumnType::LongFloat => {
321 let mut m = BTreeMap::new();
322 let mut remainder = i;
323 let mut last = 0;
324 for index in 0..flags.len() {
325 if flags.is_present(index, &column.name) {
326 let (rest, delta) = parse_number_row(remainder)?;
327 remainder = rest;
328 let v = last + delta;
329 last = v;
330 m.insert(index, v as f64 / 10000000.0);
331 } else {
332 remainder = &remainder[1..];
334 }
335 }
336
337 Ok((remainder, Column::LongFloat(m)))
338 }
339 ColumnType::ShortFloat => {
340 let mut m = BTreeMap::new();
341 let mut remainder = i;
342 let mut last = 0;
343 for index in 0..flags.len() {
344 if flags.is_present(index, &column.name) {
345 let (rest, delta) = parse_number_row(remainder)?;
346 remainder = rest;
347 let v = last + delta;
348 last = v;
349 m.insert(index, v as f64 / 1000.0);
350 } else {
351 remainder = &remainder[1..];
353 }
354 }
355
356 Ok((remainder, Column::ShortFloat(m)))
357 }
358 ColumnType::Base64 => {
359 let mut m = BTreeMap::new();
360 let mut remainder = i;
361 for index in 0..flags.len() {
362 if flags.is_present(index, &column.name) {
363 let (rest, bytes) = parse_bytes_row(remainder)?;
364 remainder = rest;
365 m.insert(index, bytes.to_vec());
366 } else {
367 remainder = &remainder[1..];
369 }
370 }
371
372 Ok((remainder, Column::Base64(m)))
373 }
374 ColumnType::String => {
375 let mut m = BTreeMap::new();
376 let mut remainder = i;
377 for index in 0..flags.len() {
378 if flags.is_present(index, &column.name) {
379 let (rest, bytes) = parse_bytes_row(remainder)?;
380 remainder = rest;
381 m.insert(index, String::from_utf8_lossy(bytes).into_owned());
382 } else {
383 remainder = &remainder[1..];
385 }
386 }
387
388 Ok((remainder, Column::String(m)))
389 }
390 ColumnType::Bool => {
391 let mut m = BTreeMap::new();
392 let mut remainder = i;
393 for index in 0..flags.len() {
394 if flags.is_present(index, &column.name) {
395 let (rest, b) = parse_bool_row(remainder)?;
396 remainder = rest;
397 m.insert(index, b);
398 } else {
399 remainder = &remainder[1..];
401 }
402 }
403
404 Ok((remainder, Column::Bool(m)))
405 }
406 ColumnType::IDs => {
407 let mut m = BTreeMap::new();
408 let mut remainder = i;
409 for index in 0..flags.len() {
410 if flags.is_present(index, &column.name) {
411 let (rest, b) = parse_ids_row(remainder)?;
412 remainder = rest;
413 m.insert(index, b);
414 } else {
415 remainder = &remainder[1..];
417 }
418 }
419
420 Ok((remainder, Column::IDs(m)))
421 }
422 }
423}
424
425impl Parsable for Section {
426 type Return = Option<Self>;
427
428 fn parse(i: &[u8]) -> IResult<&[u8], Self::Return> {
429 let (rest, section_header) = alt!(i,
430 tag!(&RWTFTRAILER) => { |_| None } |
431 parse_section_header => {|header| Some(header)})?;
432
433 if let Some(header) = section_header {
434 let (rest, types_table) = parse_types_table(rest)?;
435
436 let data_column_start = i.offset(rest);
437 let (mut rest, flags) = FlagsColumn::parse_flags_column(&rest, &types_table, header.points)?;
438
439 let mut m = BTreeMap::new();
440 for column in types_table.entries.iter() {
441 let (new_rest, data) = parse_column(&rest, &column, &flags)?;
442 rest = new_rest;
443 m.insert(column.name.clone(), data);
444 }
445
446 let data_column_end = i.offset(rest);
447 let (rest, crc) = le_u32(&rest)?;
448 let _actual_crc = CRC::new(crc, checksum_ieee(&i[data_column_start..data_column_end])); Ok((rest, Some(Section{section_type: header.section_type,
451 max: flags.max(),
452 flags: flags,
453 columns: m})))
454 } else {
455 Ok((rest, None))
456 }
457 }
458}
459
460impl Parsable for RWTFile {
464 type Return = Self;
465
466 fn parse(i: &[u8]) -> IResult<&[u8], Self::Return> {
467 let (_rest, (header, header_details)) = RWTFHeader::parse(i)?;
468 let (_rest, (metadata, _metadata_crc)) = RWTFMetadata::parse(&i[header_details.metadata_table_offset as usize..])?;
469 let mut remainder = &i[header_details.data_offset as usize..];
472
473 let mut track_points = None;
474 let mut course_points = None;
475
476 loop {
477 let (rest, section) = Section::parse(remainder)?;
478 remainder = rest;
479
480 if let Some(section) = section {
481 match section.section_type {
482 SectionType::TrackPoints => track_points = Some(section),
483 SectionType::CoursePoints => course_points = Some(section),
484 SectionType::Continuation => panic!("SectionType::Continuation unsupported"),
485 }
486 } else {
487 break;
489 }
490 }
491
492 Ok((remainder, RWTFile{header,
493 metadata,
494 track_points: track_points.unwrap_or(Section::new(SectionType::TrackPoints)),
495 course_points: course_points.unwrap_or(Section::new(SectionType::CoursePoints))}))
496 }
497}
498
499pub fn parse_rwtf(i: &[u8]) -> IResult<&[u8], RWTFile> {
500 RWTFile::parse(i)
501}
502
503#[cfg(test)]
504mod tests {
505
506}