Skip to main content

polytrack_codes/v2/
mod.rs

1#[cfg(test)]
2mod tests;
3
4use base64::prelude::*;
5
6use crate::tools::prelude::*;
7use crate::{Block, Part, Track};
8
9#[derive(Debug, PartialEq, Eq, Clone)]
10pub struct V2Track {
11    pub parts: Vec<V2Part>,
12}
13#[derive(Debug, PartialEq, Eq, Clone)]
14pub struct V2Part {
15    pub id: u8,
16    pub amount: u32,
17    pub blocks: Vec<V2Block>,
18}
19#[derive(Debug, PartialEq, Eq, Clone)]
20pub struct V2Block {
21    pub x: i32,
22    pub y: i32,
23    pub z: i32,
24    pub rotation: u8,
25}
26
27impl Track for V2Track {
28    type Part = V2Part;
29
30    type Metadata = ();
31    fn meta(&self) -> Self::Metadata {}
32    fn parts(&self) -> Vec<Self::Part> {
33        self.parts.clone()
34    }
35
36    fn decode_meta(_: &[u8], _: &mut usize) -> Option<Self::Metadata> {
37        Some(())
38    }
39    fn encode_meta(&self, _: &mut Vec<u8>) {}
40    fn from_data(_: Self::Metadata, parts: Vec<Self::Part>) -> Self {
41        Self { parts }
42    }
43
44    type TrackInfo = ();
45    fn decode_track_code(track_code: &str) -> Option<(String, Self::TrackInfo, Vec<u8>)> {
46        let track_code = track_code.get(3..)?;
47        let metadata = decode(track_code.get(..2)?)?;
48        let name_len = *metadata.first()? as usize;
49        let name = track_code.get(2..2 + name_len)?.to_string();
50        let track_data = decode(track_code.get(2 + name_len..)?)?;
51
52        Some((name, (), track_data))
53    }
54    fn encode_track_code(name: String, _: Self::TrackInfo, track_data: &[u8]) -> Option<String> {
55        let track_data = encode(track_data);
56
57        let metadata = encode(&[(name.len()) as u8]);
58
59        // prepend the "v1n"
60        let track_code = String::from("v1n") + &metadata + &name + &track_data;
61        Some(track_code)
62    }
63}
64impl Part for V2Part {
65    type Block = V2Block;
66
67    fn id(&self) -> u8 {
68        self.id
69    }
70    fn amount(&self) -> u32 {
71        self.amount
72    }
73    fn blocks(&self) -> Vec<Self::Block> {
74        self.blocks.clone()
75    }
76    fn from_data(id: u8, amount: u32, blocks: Vec<Self::Block>) -> Self {
77        Self { id, amount, blocks }
78    }
79
80    fn decode_header(data: &[u8], offset: &mut usize) -> Option<(u8, u32)> {
81        Some((read_u16(data, offset)? as u8, read_u32(data, offset)?))
82    }
83    fn encode_header(&self, data: &mut Vec<u8>) {
84        write_u16(data, self.id.into());
85        write_u32(data, self.amount);
86    }
87}
88impl Block for V2Block {
89    type Track = V2Track;
90
91    type Extra = ();
92    fn extra_data(&self) -> Self::Extra {}
93
94    type Coord = i32;
95    fn pos(&self) -> (Self::Coord, Self::Coord, Self::Coord) {
96        (self.x, self.y, self.z)
97    }
98    fn rot(&self) -> u8 {
99        self.rotation
100    }
101
102    fn decode(
103        data: &[u8],
104        offset: &mut usize,
105        _: u8,
106        _: <Self::Track as Track>::Metadata,
107    ) -> Option<Self> {
108        let x = read_i24(data, offset)? - i32::pow(2, 23);
109        let y = read_i24(data, offset)?;
110        let z = read_i24(data, offset)? - i32::pow(2, 23);
111
112        let rotation = read_u8(data, offset)? & 3;
113
114        Some(Self { x, y, z, rotation })
115    }
116    fn encode(&self, data: &mut Vec<u8>, _: <Self::Track as Track>::Metadata) {
117        write_u24(data, (self.x + i32::pow(2, 23)).cast_unsigned());
118        write_u24(data, self.y.cast_unsigned());
119        write_u24(data, (self.z + i32::pow(2, 23)).cast_unsigned());
120        data.push(self.rotation);
121    }
122}
123
124fn decode(input: &str) -> Option<Vec<u8>> {
125    let input = input.replace('-', "+").replace('_', "/");
126    let base64_decoded = BASE64_STANDARD_NO_PAD.decode(input).ok()?;
127    Some(base64_decoded)
128}
129
130fn encode(input: &[u8]) -> String {
131    let base64_encoded = BASE64_STANDARD_NO_PAD.encode(input);
132    base64_encoded.replace('+', "-").replace('/', "_")
133}