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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
//! Main cache implementation and traits.


use std::{ 
    path::Path,
    collections::HashMap,
};

use nom::{
    combinator::cond,
	number::complete::{
		be_u32,
    },
};

use crate::{ 
    store::Store, 
    cksm::{ Checksum, Entry },
    idx::Index,
    arc::{ self, Archive },
    error::ReadError, 
    util,
    codec,
};

use crc::crc32;

pub const MAIN_DATA: &str = "main_file_cache.dat2";
pub const MAIN_MUSIC_DATA: &str = "main_file_cache.dat2m";
pub const IDX_PREFIX: &str = "main_file_cache.idx";
pub const REFERENCE_TABLE: u8 = 255;

/// The core of a cache.

pub trait CacheCore: CacheRead + Sized {
    fn new<P: AsRef<Path>>(path: P) -> crate::Result<Self>;
}

/// The read functionality of a cache.

pub trait CacheRead {
    fn read(&self, index_id: u8, archive_id: u32) -> crate::Result<Vec<u8>>;
}

/// Main cache struct providing basic utilities.

pub struct Cache<S: Store> {
	store: S,
	indices: HashMap<u8, Index>
}

impl<S: Store> Cache<S> {
    /// Constructs a new `Cache<S>` with the given store.

    ///

    /// # Errors

    /// 

    /// If this function encounters any form of I/O or other error, a `CacheError`

    /// is returned which wrapps the underlying error.

    /// 

    /// # Examples

    ///

    /// ```

    /// use rscache::{ Cache, store::MemoryStore };

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

    /// 

    /// let cache: Cache<MemoryStore> = Cache::new("./data/cache")?;

    /// # Ok(())

    /// # }

    /// ```

    #[inline]
    pub fn new<P: AsRef<Path>>(path: P) -> crate::Result<Self> {
        CacheCore::new(path)
    }

    /// Reads from the internal store.

    /// 

    /// A lookup is performed on the specified index to find the sector id and the total length

    /// of the buffer that needs to be read from the `main_file_cache.dat2`.

    /// 

    /// If the lookup is successfull the data is gathered into a `Vec<u8>`.

    /// 

    /// # Errors

    /// 

    /// Returns an `IndexNotFound` error if the specified `index_id` is not a valid `Index`.\

    /// Returns an `ArchiveNotFound` error if the specified `archive_id` is not a valid `Archive`.

    /// 

    /// # Examples

    /// ```

    /// # use rscache::{ Cache, store::MemoryStore };

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

    /// # let path = "./data/cache";

    /// let cache: Cache<MemoryStore> = Cache::new(path)?;

    /// 

    /// let index_id = 2; // Config index

    /// let archive_id = 10; // Random archive.

    /// 

    /// let buffer = cache.read(index_id, archive_id)?;

    /// # Ok(())

    /// # }

    /// ```

    #[inline]
    pub fn read(&self, index_id: u8, archive_id: u32) -> crate::Result<Vec<u8>> {
        CacheRead::read(self, index_id, archive_id)
    }

    /// Creates a `Checksum` which can be used to validate the cache data

    /// that the client received during the update protocol.

    /// 

    /// NOTE: The RuneScape client doesn't have a valid crc for index 16.

    /// This checksum sets the crc and revision for index 16 both to 0.

    /// The crc for index 16 should be skipped.

    /// 

    /// # Errors

    /// 

    /// Returns an error when a buffer read from the reference

    /// table could not be decoded / decompressed.

    /// 

    /// # Examples

    /// 

    /// ```

    /// # use rscache::{ Cache, store::MemoryStore };

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

    /// # let path = "./data/cache";

    /// # let cache: Cache<MemoryStore> = Cache::new(path)?;

    /// let checksum = cache.create_checksum()?;

    /// #    Ok(())

    /// # }

    /// ```

    #[inline]
    pub fn create_checksum(&self) -> crate::Result<Checksum> {
        let mut checksum = Checksum::new();

        for index_id in 0..self.index_count() as u32 {
            if index_id == 16 {
                checksum.push(Entry { crc: 0, revision: 0 });
                continue;
            }

            if let Ok(buffer) = self.read(REFERENCE_TABLE, index_id) {	
                if !buffer.is_empty() {
                    let data = codec::decode(&buffer)?;

                    let (_, version) = cond(data[0] >= 6, be_u32)(&data[1..5])?;
                    let version = if let Some(version) = version { version } else { 0 };

                    checksum.push(Entry { 
                        crc: crc32::checksum_ieee(&buffer), 
                        revision: version,
                    });
                }
            };
        }

        Ok(checksum)
    }

    /// Constructs a buffer which contains the huffman table.

    /// 

    /// # Errors

    /// 

    /// Returns an error if the huffman archive could not be found or 

    /// if the decode / decompression of the huffman table failed.

    /// 

    /// # Examples

    /// ```

    /// # use rscache::{ Cache, store::MemoryStore };

    /// # struct Huffman;

    /// # impl Huffman {

    /// #   pub fn new(buffer: Vec<u8>) -> Self { Self {} }

    /// # }

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

    /// # let cache: Cache<MemoryStore> = Cache::new("./data/cache")?;

    /// let huffman_table = cache.huffman_table()?;

    /// let huffman = Huffman::new(huffman_table);

    /// # Ok(())

    /// # }

    /// ```

    #[inline]
    pub fn huffman_table(&self) -> crate::Result<Vec<u8>> {
        let index_id = 10;

        let archive = self.archive_by_name(index_id, "huffman")?;
        let buffer = self.store.read(&archive)?;
		
		Ok(codec::decode(&buffer)?)
    }

    /// Searches for the archive which contains the given name hash in the given

    /// index_id.

    /// 

    /// # Errors

    /// 

    /// Panics if the string couldn't be hashed by the djd2 hasher.

    /// 

    /// Returns an `IndexNotFound` error if the specified `index_id` is not a valid `Index`.\

    /// Returns an `ArchiveNotFound` error if the specified `archive_id` is not a valid `Archive`.\

    /// Returns an `NameNotInArchive` error if the `name` hash is not present in this archive.

    /// 

    /// # Examples

    /// ```

    /// # use rscache::{ Cache, store::MemoryStore, codec };

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

    /// # let path = "./data/cache";

    /// # let cache: Cache<MemoryStore> = Cache::new(path)?;

    /// let index_id = 10;

    /// let archive = cache.archive_by_name(index_id, "huffman")?;

    /// # Ok(())

    /// # }

    /// ```

    #[inline]
    pub fn archive_by_name(&self, index_id: u8, name: &str) -> crate::Result<Archive> {
        let index = match self.indices.get(&index_id) {
            Some(index) => index,
            None => return Err(ReadError::IndexNotFound(index_id).into())
        };
        let hash = util::djd2::hash(name);

        let buffer = self.read(REFERENCE_TABLE, index_id as u32)?;
        let data = codec::decode(&buffer)?;

        let archives = arc::parse_archive_data(&data)?;

        for archive_data in archives {
            if archive_data.hash == hash {
                match index.archives.get(&(archive_data.id as u32)) {
                    Some(archive) => return Ok(*archive),
                    None => return Err(
                        ReadError::ArchiveNotFound(index_id, archive_data.id as u32).into()
                    ),
                }
            }
        }

        Err(ReadError::NameNotInArchive(hash, name.to_owned(), index_id).into())
    }

    /// Simply returns the index count, by getting the `len()` of 

    /// the underlying `indices` vector.

    /// 

    /// # Examples

    /// 

    /// ```

    /// # use rscache::{ Cache, store::MemoryStore };

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

    /// # let cache: Cache<MemoryStore> = Cache::new("./data/cache")?;

    /// for index in 0..cache.index_count() {

    ///     // ...

    /// }

    /// 

    /// assert_eq!(22, cache.index_count());

    /// # Ok(())

    /// # }

    /// ```

    #[inline]
    pub fn index_count(&self) -> usize {
        self.indices.len()
    }
}

impl<S: Store> CacheCore for Cache<S> {
    #[inline]
    fn new<P: AsRef<Path>>(path: P) -> crate::Result<Self> {
        let path = path.as_ref();

        let store = util::load_store(path)?;
        let indices = util::load_indices(path)?;

        Ok(Self { store, indices })
    }
}

impl<S: Store> CacheRead for Cache<S> {
    #[inline]
    fn read(&self, index_id: u8, archive_id: u32) -> crate::Result<Vec<u8>> {
        let index = match self.indices.get(&index_id) {
            Some(index) => index,
            None => return Err(ReadError::IndexNotFound(index_id).into())
        };

        let archive = match index.archives.get(&archive_id) {
            Some(archive) => archive,
            None => return Err(ReadError::ArchiveNotFound(index_id, archive_id).into())
        };

        Ok(self.store.read(archive)?)
    }
}