polytrack_codes/v2/
mod.rs1#![allow(clippy::cast_possible_truncation)]
2#[cfg(test)]
3mod tests;
4
5use crate::tools::Track;
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 #[inline]
52 fn read_u8(buf: &[u8], offset: &mut usize) -> Option<u8> {
53 let res = buf.get(*offset).copied();
54 *offset += 1;
55 res
56 }
57 #[inline]
58 fn read_u16(buf: &[u8], offset: &mut usize) -> Option<u16> {
59 let res = Some(u16::from(*buf.get(*offset)?) | (u16::from(*buf.get(*offset + 1)?) << 8));
60 *offset += 2;
61 res
62 }
63 #[inline]
64 fn read_u32(buf: &[u8], offset: &mut usize) -> Option<u32> {
65 let res = Some(
66 u32::from(*buf.get(*offset)?)
67 | (u32::from(*buf.get(*offset + 1)?) << 8)
68 | (u32::from(*buf.get(*offset + 2)?) << 16)
69 | (u32::from(*buf.get(*offset + 3)?) << 24),
70 );
71 *offset += 4;
72 res
73 }
74 #[inline]
75 fn read_i24(buf: &[u8], offset: &mut usize) -> Option<i32> {
76 let res = Some(
77 i32::from(*buf.get(*offset)?)
78 | (i32::from(*buf.get(*offset + 1)?) << 8)
79 | (i32::from(*buf.get(*offset + 2)?) << 16),
80 );
81 *offset += 3;
82 res
83 }
84
85 let mut offset = 0;
86 let mut parts = Vec::new();
87 while offset < data.len() {
88 let id = read_u16(data, &mut offset)? as u8;
89 let amount = read_u32(data, &mut offset)?;
90
91 let mut blocks = Vec::new();
92 for _ in 0..amount {
93 let x = read_i24(data, &mut offset)? - i32::pow(2, 23);
94 let y = read_i24(data, &mut offset)?;
95 let z = read_i24(data, &mut offset)? - i32::pow(2, 23);
96
97 let rotation = read_u8(data, &mut offset)? & 3;
98
99 blocks.push(Block { x, y, z, rotation });
100 }
101 parts.push(Part { id, amount, blocks });
102 }
103
104 Some(TrackInfo { parts })
105}