rscache/
util.rs

1//! Helpful utility functions, macros and structs.
2
3#[allow(unused_assignments)]
4mod huffman;
5#[allow(clippy::many_single_char_names, clippy::too_many_lines)]
6mod isaac_rand;
7
8pub use huffman::Huffman;
9pub use isaac_rand::IsaacRand;
10
11use std::{
12    collections::HashMap,
13    io::{self, BufReader},
14};
15
16use crate::extension::ReadExt;
17
18macro_rules! impl_osrs_loader {
19    ($ldr:ident, $def:ty, index_id: $idx_id:expr $(, archive_id: $arc_id:expr)?) => {
20        impl $ldr {
21            #[allow(unreachable_code)]
22            pub fn new(cache: &Cache) -> crate::Result<Self> {
23                $(
24                    let map = <$def>::fetch_from_archive(cache, $idx_id, $arc_id)?;
25
26                    return Ok(Self(map));
27                )?
28
29                let map = <$def>::fetch_from_index(cache, $idx_id)?;
30
31                Ok(Self(map))
32            }
33
34            pub fn load(&self, id: u16) -> Option<&$def> {
35                self.0.get(&id)
36            }
37        }
38
39        impl_iter_for_loader!($ldr, u16, $def);
40    };
41}
42
43#[cfg(feature = "rs3")]
44macro_rules! impl_rs3_loader {
45    ($ldr:ident, $def:ty, index_id: $idx_id:expr) => {
46        impl $ldr {
47            pub fn new(cache: &Cache) -> crate::Result<Self> {
48                let map = <$def>::fetch_from_index(cache, $idx_id)?;
49
50                Ok(Self(map))
51            }
52
53            pub fn load(&self, id: u32) -> Option<&$def> {
54                self.0.get(&id)
55            }
56        }
57
58        impl_iter_for_loader!($ldr, u32, $def);
59    };
60}
61
62macro_rules! impl_iter_for_loader {
63    ($ldr:ident, $id:ty, $def:ty) => {
64        impl $ldr {
65            #[inline]
66            pub fn iter(&self) -> hash_map::Iter<'_, $id, $def> {
67                self.0.iter()
68            }
69
70            #[inline]
71            pub fn iter_mut(&mut self) -> hash_map::IterMut<'_, $id, $def> {
72                self.0.iter_mut()
73            }
74        }
75
76        impl IntoIterator for $ldr {
77            type Item = ($id, $def);
78            type IntoIter = hash_map::IntoIter<$id, $def>;
79
80            #[inline]
81            fn into_iter(self) -> Self::IntoIter {
82                self.0.into_iter()
83            }
84        }
85
86        impl<'a> IntoIterator for &'a $ldr {
87            type Item = (&'a $id, &'a $def);
88            type IntoIter = hash_map::Iter<'a, $id, $def>;
89            #[inline]
90            fn into_iter(self) -> Self::IntoIter {
91                self.0.iter()
92            }
93        }
94
95        impl<'a> IntoIterator for &'a mut $ldr {
96            type Item = (&'a $id, &'a mut $def);
97            type IntoIter = hash_map::IterMut<'a, $id, $def>;
98
99            #[inline]
100            fn into_iter(self) -> Self::IntoIter {
101                self.0.iter_mut()
102            }
103        }
104    };
105}
106
107/// djd2 module for string hashing
108pub mod djd2 {
109
110    /// Hashes the string
111    ///
112    /// # Errors
113    ///
114    /// Can panic if `nth(n)` returns `None` if n >= strings iter length.
115    ///
116    /// # Examples
117    ///
118    /// ```
119    /// let hash = rscache::util::djd2::hash("huffman");
120    /// assert_eq!(hash, 1258058669);
121    /// ```
122    pub fn hash<T: AsRef<str>>(string: T) -> i32 {
123        let string = string.as_ref();
124        let mut hash = 0;
125
126        for index in 0..string.len() {
127            hash =
128                string.chars().nth(index).unwrap_or_else(|| {
129                    panic!("index {} not valid in str len {}", index, string.len())
130                }) as i32
131                    + ((hash << 5) - hash);
132        }
133        hash
134    }
135}
136
137/// Useful for decoding parameters when reading from definition buffers.
138///
139/// # Errors
140///
141/// Can return `std::io::Error` if reading from the `BufReader<&[u8]>` fails.
142pub fn read_parameters(reader: &mut BufReader<&[u8]>) -> io::Result<HashMap<u32, String>> {
143    let len = reader.read_u8()?;
144    let mut map = HashMap::new();
145
146    for _ in 0..len {
147        let is_string = reader.read_u8()? == 1;
148        let key = reader.read_u24()?;
149        let value = if is_string {
150            reader.read_string()?
151        } else {
152            reader.read_i32()?.to_string()
153        };
154
155        map.insert(key, value);
156    }
157
158    Ok(map)
159}