polytrack_codes/v2/
mod.rs1#![allow(clippy::cast_possible_truncation)]
2#[cfg(test)]
3mod tests;
4
5use crate::tools::{Track, read::*};
6use base64::prelude::*;
7
8#[derive(Debug, PartialEq, Eq)]
9pub struct TrackInfo {
10 pub parts: Vec<Part>,
11}
12
13#[derive(Debug, PartialEq, Eq)]
14pub struct Part {
15 pub id: u8,
16 pub amount: u32,
17 pub blocks: Vec<Block>,
18}
19
20#[derive(Debug, PartialEq, Eq)]
21pub struct Block {
22 pub x: i32,
23 pub y: i32,
24 pub z: i32,
25
26 pub rotation: u8,
27}
28
29fn decode(input: &str) -> Option<Vec<u8>> {
30 let input = input.replace('-', "+").replace('_', "/");
31 let base64_decoded = BASE64_STANDARD_NO_PAD.decode(input).ok()?;
32 Some(base64_decoded)
33}
34
35#[must_use]
36pub fn decode_track_code(track_code: &str) -> Option<Track> {
37 let track_code = track_code.get(3..)?;
38 let metadata = decode(track_code.get(..2)?)?;
39 let name_len = *metadata.first()? as usize;
40 let name = track_code.get(2..2 + name_len)?.to_string();
41 let track_data = decode(track_code.get(2 + name_len..)?)?;
42 Some(Track {
43 name,
44 author: None,
45 track_data,
46 })
47}
48
49#[must_use]
50pub fn decode_track_data(data: &[u8]) -> Option<TrackInfo> {
51 let mut offset = 0;
52 let mut parts = Vec::new();
53 while offset < data.len() {
54 let id = read_u16(data, &mut offset)? as u8;
55 let amount = read_u32(data, &mut offset)?;
56
57 let mut blocks = Vec::new();
58 for _ in 0..amount {
59 let x = read_i24(data, &mut offset)? - i32::pow(2, 23);
60 let y = read_i24(data, &mut offset)?;
61 let z = read_i24(data, &mut offset)? - i32::pow(2, 23);
62
63 let rotation = read_u8(data, &mut offset)? & 3;
64
65 blocks.push(Block { x, y, z, rotation });
66 }
67 parts.push(Part { id, amount, blocks });
68 }
69
70 Some(TrackInfo { parts })
71}