polytrack_codes/v2/
mod.rs1#[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 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}