Skip to main content

polytrack_codes/v3/
mod.rs

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