polytrack_codes/v4/
mod.rs

1#![allow(clippy::cast_possible_truncation)]
2#[cfg(test)]
3mod tests;
4
5use crate::tools::{self, Track, hash_vec, read::*};
6
7pub const CP_IDS: [u8; 4] = [52, 65, 75, 77];
8
9#[derive(Debug, PartialEq, Eq)]
10pub struct TrackInfo {
11    pub parts: Vec<Part>,
12}
13
14#[derive(Debug, PartialEq, Eq)]
15pub struct Part {
16    pub id: u8,
17    pub amount: u32,
18    pub blocks: Vec<Block>,
19}
20
21#[derive(Debug, PartialEq, Eq)]
22pub struct Block {
23    pub x: i32,
24    pub y: i32,
25    pub z: i32,
26
27    pub rotation: u8,
28    pub cp_order: Option<u16>,
29}
30
31#[must_use]
32pub fn decode_track_code(track_code: &str) -> Option<Track> {
33    let track_code = track_code.get(2..)?;
34    let metadata = tools::decode(track_code.get(..2)?)?;
35    let name_len = *metadata.first()? as usize;
36    let track_name_raw = tools::decode(track_code.get(2..2 + name_len)?)?;
37    let name = String::from_utf8(track_name_raw).ok()?;
38    let track_data = tools::decompress(&tools::decode(track_code.get(2 + name_len..)?)?)?;
39    Some(Track {
40        name,
41        author: None,
42        track_data,
43    })
44}
45
46#[must_use]
47pub fn decode_track_data(data: &[u8]) -> Option<TrackInfo> {
48    let mut offset = 0;
49    let mut parts = Vec::new();
50    while offset < data.len() {
51        let id = read_u16(data, &mut offset)? as u8;
52        let amount = read_u32(data, &mut offset)?;
53
54        let mut blocks = Vec::new();
55        for _ in 0..amount {
56            let x = read_i24(data, &mut offset)? - i32::pow(2, 23);
57            let y = read_i24(data, &mut offset)?;
58            let z = read_i24(data, &mut offset)? - i32::pow(2, 23);
59
60            let rotation = read_u8(data, &mut offset)? & 3;
61
62            let cp_order = if CP_IDS.contains(&id) {
63                Some(read_u16(data, &mut offset)?)
64            } else {
65                None
66            };
67            blocks.push(Block {
68                x,
69                y,
70                z,
71                rotation,
72                cp_order,
73            });
74        }
75        parts.push(Part { id, amount, blocks });
76    }
77
78    Some(TrackInfo { parts })
79}
80
81#[must_use]
82pub fn export_to_id(track_code: &str) -> Option<String> {
83    let track_data = decode_track_code(track_code)?;
84    let id = hash_vec(track_data.track_data);
85    Some(id)
86}