polytrack_codes/v2/
mod.rs

1#![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}