polytrack_codes/v2/
mod.rs1#![allow(clippy::cast_possible_truncation)]
2#[cfg(test)]
3mod tests;
4
5use crate::tools::prelude::*;
6use base64::prelude::*;
7
8#[derive(Debug, PartialEq, Eq, Clone)]
9pub struct TrackInfo {
10 pub parts: Vec<Part>,
11}
12
13#[derive(Debug, PartialEq, Eq, Clone)]
14pub struct Part {
15 pub id: u8,
16 pub amount: u32,
17 pub blocks: Vec<Block>,
18}
19
20#[derive(Debug, PartialEq, Eq, Clone)]
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
35fn encode(input: &[u8]) -> String {
36 let base64_encoded = BASE64_STANDARD_NO_PAD.encode(input);
37 base64_encoded.replace('+', "-").replace('/', "_")
38}
39
40#[must_use]
41pub fn decode_track_code(track_code: &str) -> Option<Track> {
42 let track_code = track_code.get(3..)?;
43 let metadata = decode(track_code.get(..2)?)?;
44 let name_len = *metadata.first()? as usize;
45 let name = track_code.get(2..2 + name_len)?.to_string();
46 let track_data = decode(track_code.get(2 + name_len..)?)?;
47 Some(Track {
48 name,
49 author: None,
50 last_modified: None,
51 track_data,
52 })
53}
54
55#[must_use]
56pub fn encode_track_code(track: &Track) -> Option<String> {
62 let track_data = encode(&track.track_data);
63
64 let metadata = encode(&[(track.name.len()) as u8]);
65
66 let track_code = String::from("v1n") + &metadata + &track.name + &track_data;
68 Some(track_code)
69}
70
71#[must_use]
72pub fn decode_track_data(data: &[u8]) -> Option<TrackInfo> {
73 let mut offset = 0;
74 let mut parts = Vec::new();
75 while offset < data.len() {
76 let id = read_u16(data, &mut offset)? as u8;
77 let amount = read_u32(data, &mut offset)?;
78
79 let mut blocks = Vec::new();
80 for _ in 0..amount {
81 let x = read_i24(data, &mut offset)? - i32::pow(2, 23);
82 let y = read_i24(data, &mut offset)?;
83 let z = read_i24(data, &mut offset)? - i32::pow(2, 23);
84
85 let rotation = read_u8(data, &mut offset)? & 3;
86
87 blocks.push(Block { x, y, z, rotation });
88 }
89 parts.push(Part { id, amount, blocks });
90 }
91
92 Some(TrackInfo { parts })
93}
94
95#[must_use]
96pub fn encode_track_data(track_info: &TrackInfo) -> Option<Vec<u8>> {
98 let mut data = Vec::new();
99 for part in &track_info.parts {
100 write_u16(&mut data, part.id.into());
101 write_u32(&mut data, part.amount);
102 for block in &part.blocks {
103 write_u24(&mut data, (block.x + i32::pow(2, 23)).cast_unsigned());
104 write_u24(&mut data, block.y.cast_unsigned());
105 write_u24(&mut data, (block.z + i32::pow(2, 23)).cast_unsigned());
106 data.push(block.rotation);
107 }
108 }
109
110 Some(data)
111}