Skip to main content

polytrack_codes/
lib.rs

1pub mod tools;
2pub mod v1;
3pub mod v2;
4pub mod v3;
5pub mod v4;
6pub mod v5;
7pub mod v6;
8
9use type_equalities::{IsEqual, trivial_eq};
10
11use crate::tools::hash_vec;
12
13pub trait Track
14where
15    // It would be more correct to enforce equality of the `Track` instead of the `Track::Metadata`.
16    // But to avoid a one-time-use `TypeFunction`, it is done this way.
17    // If the associated types match correctly, then there is no need to fight with this trait bound, it will automatically be resolved.
18    Self::Metadata: IsEqual<<<<Self::Part as Part>::Block as Block>::Track as Track>::Metadata>,
19{
20    type Part: Part;
21
22    type Metadata: Copy;
23    fn meta(&self) -> Self::Metadata;
24    fn parts(&self) -> Vec<Self::Part>;
25
26    fn decode_meta(data: &[u8], offset: &mut usize) -> Option<Self::Metadata>;
27    fn encode_meta(&self, data: &mut Vec<u8>);
28    fn from_data(metadata: Self::Metadata, parts: Vec<Self::Part>) -> Self;
29
30    type TrackInfo;
31    fn decode_track_code(
32        track_code: &str,
33    ) -> Option<(
34        /* name */ String,
35        Self::TrackInfo,
36        /* track_data */ Vec<u8>,
37    )>;
38    fn encode_track_code(name: String, info: Self::TrackInfo, track_data: &[u8]) -> Option<String>;
39}
40pub trait Part: Sized {
41    type Block: Block;
42
43    // ASSUMPTION: All PolyTrack version's `Part` consist of only these three data (holds true atleast upto 0.6.2)
44    // If assumption is broken, this trait will need a bit of a rewrite
45    fn id(&self) -> u8;
46    fn amount(&self) -> u32;
47    fn blocks(&self) -> Vec<Self::Block>;
48    fn from_data(id: u8, amount: u32, blocks: Vec<Self::Block>) -> Self;
49
50    fn decode_header(data: &[u8], offset: &mut usize) -> Option<(u8, u32)>;
51    fn encode_header(&self, data: &mut Vec<u8>);
52    fn decode(
53        data: &[u8],
54        offset: &mut usize,
55        meta: <<Self::Block as Block>::Track as Track>::Metadata,
56    ) -> Option<Self> {
57        let (id, amount) = Self::decode_header(data, offset)?;
58        let mut blocks = Vec::new();
59        for _ in 0..amount {
60            blocks.push(Self::Block::decode(data, offset, id, meta)?);
61        }
62        Some(Self::from_data(id, amount, blocks))
63    }
64    fn encode(&self, data: &mut Vec<u8>, meta: <<Self::Block as Block>::Track as Track>::Metadata) {
65        self.encode_header(data);
66        for block in &self.blocks() {
67            block.encode(data, meta);
68        }
69    }
70}
71pub trait Block: Sized {
72    type Track: Track;
73
74    type Extra;
75    fn extra_data(&self) -> Self::Extra;
76
77    // because it changes i32 -> u32 in v4 -> v5
78    type Coord;
79    fn pos(&self) -> (Self::Coord, Self::Coord, Self::Coord);
80    fn rot(&self) -> u8;
81
82    fn decode(
83        data: &[u8],
84        offset: &mut usize,
85        id: u8,
86        meta: <Self::Track as Track>::Metadata,
87    ) -> Option<Self>;
88    fn encode(&self, data: &mut Vec<u8>, meta: <Self::Track as Track>::Metadata);
89}
90
91pub fn decode_track_data<T: Track>(data: &[u8]) -> Option<T> {
92    let mut offset = 0;
93    // ASSUMPTION: All metadata lives before the main data (holds true for v5+, which are the only ones with nontrivial metadata)
94    let meta = T::decode_meta(data, &mut offset)?;
95    let mut parts = Vec::new();
96    while offset < data.len() {
97        parts.push(T::Part::decode(
98            data,
99            &mut offset,
100            trivial_eq::<
101                // Rust should be able to figure this out itself smh.
102                _,
103                <<<T::Part as Part>::Block as Block>::Track as Track>::Metadata,
104            >()
105            .coerce(meta),
106        )?);
107    }
108
109    Some(T::from_data(meta, parts))
110}
111pub fn encode_track_data<T: Track>(track: &T) -> Vec<u8> {
112    let mut data = Vec::new();
113    let meta = track.meta();
114
115    // ASSUMPTION: All metadata lives before the main data (holds true for v5+, which are the only ones with nontrivial metadata)
116    track.encode_meta(&mut data);
117    for part in track.parts() {
118        part.encode(
119            &mut data,
120            trivial_eq::<
121                // Rust should be able to figure this out itself smh.
122                _,
123                <<<T::Part as Part>::Block as Block>::Track as Track>::Metadata,
124            >()
125            .coerce(meta),
126        );
127    }
128
129    data
130}
131pub fn export_to_id<T: Track>(track_code: &str) -> Option<String> {
132    let (_, _, track_data) = T::decode_track_code(track_code)?;
133    let id = hash_vec(track_data);
134    Some(id)
135}