1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
//! Helpful utility functions, macros and structs.


/// All OSRS specific utilities.

pub mod osrs;

use std::{ 
    path::Path, 
    collections::HashMap,
    fs::File,
    io::{ BufReader, self, Read }
};

use crate::{
    store::Store,
    cache::{ MAIN_DATA, IDX_PREFIX, REFERENCE_TABLE},
    idx::Index,
    Definition,
    Cache,
    codec,
    arc,
    ext::ReadExt,
};

#[macro_use]
macro_rules! impl_loader {
   ($ldr:ident, $def:ty, $defs_field:ident, archive_id: $arc_id:expr) => {
        impl $ldr {
            #[inline]
            pub fn new<S: Store>(cache: &Cache<S>) -> crate::Result<Self> {
                Loader::new(cache)
            }

            #[inline]
            pub fn load(&self, id: u16) -> Option<&$def> {
                Loader::load(self, id)
            }
        }

        impl Loader<$def> for $ldr {
            #[inline]
            fn new<S: Store>(cache: &Cache<S>) -> crate::Result<$ldr> {
                let $defs_field = util::parse_defs(cache, $arc_id)?;

                Ok($ldr { $defs_field })
            }

            #[inline]
            fn load(&self, id: u16) -> Option<&$def> {
                self.$defs_field.get(&id)
            }
        }
   };
}

/// djd2 module for hashing strings

pub mod djd2 {

    /// Hashes the string

    /// 

    /// # Errors

    /// 

    /// Can panic if `nth(n)` returns `None` if n >= strings iter length.

    /// 

    /// # Examples

    /// 

    /// ```

    /// let hash = rscache::util::djd2::hash("huffman");

    /// assert_eq!(hash, 1258058669);

    /// ``` 

    #[inline]
    pub fn hash(string: &str) -> i32 {
        let mut hash = 0;

        for index in 0..string.len() {
            hash = string.chars()
                .nth(index).unwrap_or_else(|| panic!("index {} not valid in str len {}", index, string.len())) as i32 + ((hash << 5) - hash);
        }
        
        hash
    }
}

/// Loads the given store.

/// 

/// This will load the main cache file and open the chosen store

/// with it.

/// 

/// # Errors

/// 

/// Returns an `std::io::Error` if the path is incorrect.

/// 

/// # Examples 

/// 

/// ```

/// # use std::{ fs::File, path::Path, collections::HashMap };

/// # use rscache::arc::Archive;

/// use rscache::{ Store, util };

/// 

/// # fn main() -> rscache::Result<()> {

/// let store: CustomStore = util::load_store("./data/cache")?;

/// # Ok(())

/// # }

/// 

/// 

/// struct CustomStore;

/// 

/// impl Store for CustomStore {

///     fn new(mut main_file: File) -> rscache::Result<Self> {

///         // snip

/// 

///         Ok(Self {  })

///     }

/// # fn read(&self, archive: &Archive) -> rscache::Result<Vec<u8>> {

/// # unimplemented!()

/// # }

/// }

/// ```

#[inline]
pub fn load_store<S: Store, P: AsRef<Path>>(path: P) -> crate::Result<S> {
    let path = path.as_ref();
    let main_file = File::open(path.join(MAIN_DATA))?;
    
    S::new(main_file)
}

/// Loads all indices present in the cache folder.

/// 

/// The `u8` in `HashMap<u8, Index>` represents the id of the index.

/// 

/// # Errors

/// 

/// Can return multiple errors: if the index couldnt be parsed or the index 

/// couldn't be opened.

#[inline]
pub fn load_indices<P: AsRef<Path>>(path: P) -> crate::Result<HashMap<u8, Index>> {
    let path = path.as_ref();
	let mut indices = HashMap::new();

	for index_id in 0..=REFERENCE_TABLE {
		let path = path.join(format!("{}{}", IDX_PREFIX, index_id));

		if path.exists() {
			let mut index = File::open(path)?;
			let mut index_buffer = Vec::new();

			index.read_to_end(&mut index_buffer)?;
			indices.insert(index_id, Index::new(index_id, &index_buffer)?);
		}
	}

	Ok(indices)
}

/// Parses all definitions read from the passed `Cache<S>` from `archive_id`.

/// 

/// # Errors

/// 

/// Can return multiple errors: if reading, decoding or parsing definition buffers fail.

/// 

/// # Examples

/// 

/// ```

/// # use std::collections::HashMap;

/// # use rscache::{ OsrsCache, util, def::osrs::ItemDefinition };

/// # fn main() -> rscache::Result<()> {

/// # let cache = OsrsCache::new("./data/cache")?;

/// let archive_id = 10; // Archive containing item definitions.

/// let item_defs: HashMap<u16, ItemDefinition> = util::parse_defs(&cache, archive_id)?;

/// # Ok(())

/// # }

/// ```

#[inline]
pub fn parse_defs<T: Definition, S: Store>(cache: &Cache<S>, archive_id: u32) -> crate::Result<HashMap<u16, T>> {
    let buffer = cache.read(REFERENCE_TABLE, 2)?;
    let buffer = codec::decode(&buffer)?;
    
    let archives = arc::parse_archive_data(&buffer)?;
    let entry_count = archives[archive_id as usize - 1].entry_count;
    
    let buffer = cache.read(2, archive_id)?;
    let buffer = codec::decode(&buffer)?;

    let archive_data = arc::parse_content(&buffer, entry_count)?;

    let mut definitions = HashMap::new();
    for (id, buffer) in archive_data {
        definitions.insert(id, T::new(id, &buffer)?);
    }

    Ok(definitions)
}

/// Useful for decoding parameters when reading from definition buffers.

/// 

/// # Errors

/// 

/// Can return `std::io::Error` if reading from the `BufReader<&[u8]>` fails.

#[inline]
pub fn read_parameters(reader: &mut BufReader<&[u8]>) -> io::Result<HashMap<u32, String>> {
    let len = reader.read_u8()?;
    let mut map = HashMap::new();

    for _ in 0..len {
        let is_string = reader.read_u8()? == 1;
        let key = reader.read_u24()?;
        
        let value = if is_string {
            reader.read_string()?
        } else {
            reader.read_i32()?.to_string()
        };

        map.insert(key, value);
    }

    Ok(map)
}