1use crate::{
2 archive::{cache_archive::CacheArchive, Archive, ArchiveError},
3 djb2::djb2_hash,
4 js5_compression::{Js5Compression, Js5CompressionError},
5 js5_index::{Js5Index, Js5IndexError},
6 store::{store_open, Store, StoreError},
7 Cache,
8};
9use std::{collections::HashMap, io};
10use thiserror::Error;
11
12const ARCHIVESET: usize = (1 << 24) - 1;
13const UNPACKED_CACHE_SIZE_DEFAULT: usize = 1024;
14
15#[derive(Error, Debug)]
16pub enum CacheError {
17 #[error("IO error: {0}")]
18 Io(#[from] io::Error),
19 #[error("JS5 compression error: {0}")]
20 Js5Compression(#[from] Js5CompressionError),
21 #[error("JS5 index error: {0}")]
22 Js5Index(#[from] Js5IndexError),
23 #[error("Store error: {0}")]
24 Store(#[from] StoreError),
25 #[error("ArchiveError: {0}")]
26 ArchiveError(#[from] ArchiveError),
27 #[error("failed getting CacheArchive {0} from cache")]
28 ArchiveNotFound(u8),
29 #[error("failed reading CacheArchive {0} from cache")]
30 ArchiveRead(u8),
31}
32
33impl Cache {
34 pub fn open(input_path: &str) -> Result<Cache, CacheError> {
40 Self::open_with_store(store_open(input_path)?)
41 }
42
43 pub fn open_with_store(store: Box<dyn Store>) -> Result<Cache, CacheError> {
49 let mut cache = Self {
50 store,
51 archives: HashMap::new(),
52 _unpacked_cache_size: UNPACKED_CACHE_SIZE_DEFAULT,
53 };
54 cache.init()?;
55
56 Ok(cache)
58 }
59
60 fn init(&mut self) -> Result<(), CacheError> {
61 for archive in self.store.list(ARCHIVESET as u8)? {
62 let compressed = self.store.read(ARCHIVESET as u8, archive)?;
63
64 let buf = Js5Compression::uncompress(compressed, None)?;
65
66 let js5_index = Js5Index::read(buf)?;
67
68 let cache_archive = CacheArchive {
69 is_dirty: false,
70 index: js5_index,
71 archive: archive as u8,
72 unpacked_cache: HashMap::new(),
73 };
74
75 self.archives.insert(archive as u8, cache_archive);
76 }
77
78 Ok(())
79 }
80
81 pub fn read(
90 &mut self,
91 archive: u8,
92 group: u32,
93 file: u16,
94 xtea_keys: Option<[u32; 4]>,
95 ) -> Result<Vec<u8>, CacheError> {
96 Ok(self
97 .archives
98 .get_mut(&archive)
99 .ok_or(CacheError::ArchiveNotFound(archive))?
100 .read(group, file, xtea_keys, self.store.as_ref())?)
101 }
102
103 pub fn read_named_group(
112 &mut self,
113 archive: u8,
114 group: &str,
115 file: u16,
116 xtea_keys: Option<[u32; 4]>,
117 ) -> Result<Vec<u8>, CacheError> {
118 Ok(self
119 .archives
120 .get_mut(&archive)
121 .ok_or(CacheError::ArchiveNotFound(archive))?
122 .read_named_group(djb2_hash(group), file, xtea_keys, self.store.as_ref())?)
123 }
124}