vach/loader/
archive.rs

1use std::{
2	collections::HashMap,
3	io::{Read, Seek, SeekFrom},
4	ops::DerefMut,
5	str,
6	sync::{Arc, Mutex},
7};
8
9use super::resource::Resource;
10use crate::global::{error::*, flags::Flags, header::Header, reg_entry::RegistryEntry};
11
12#[cfg(feature = "crypto")]
13use crate::crypto;
14
15#[cfg(feature = "compression")]
16use crate::global::compressor::*;
17
18/// Parses an Archive from a read handle.
19/// > Wraps handle in a [`Mutex`] internally for shared access, use [`fetch_mut`](Archive::fetch_mut) for lock-free access.
20#[derive(Debug)]
21pub struct Archive<T> {
22	/// Wrapping `handle` in a Mutex means that we only ever lock when reading from the underlying buffer, thus ensuring maximum performance across threads
23	/// Since all other work is done per thread
24	handle: Mutex<T>,
25
26	// Registry Data
27	header: Header,
28	entries: HashMap<Arc<str>, RegistryEntry>,
29
30	// Optional parts
31	#[cfg(feature = "crypto")]
32	decryptor: Option<crypto::Encryptor>,
33	#[cfg(feature = "crypto")]
34	key: Option<crypto::VerifyingKey>,
35}
36
37impl<T> std::fmt::Display for Archive<T> {
38	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39		let bytes = self
40			.entries
41			.values()
42			.map(|re| re.offset)
43			.reduce(|a, b| a + b)
44			.unwrap_or(0);
45
46		write!(
47			f,
48			"[Archive Header] Version: {}, Members: {}, Compressed Size: {bytes}B, Header-Flags: <{:#x} : {:#016b}>",
49			self.header.version,
50			self.entries.len(),
51			self.header.flags.bits,
52			self.header.flags.bits,
53		)
54	}
55}
56
57impl<T> Archive<T> {
58	/// Consume the [`Archive`] and return the underlying source. Only valid if internal [`Mutex`] isn't poisoned
59	pub fn into_inner(self) -> Result<T, std::sync::PoisonError<T>> {
60		self.handle.into_inner()
61	}
62
63	// Decompress and|or Decrypt some data
64	#[inline(never)]
65	fn process(&self, entry: &RegistryEntry, mut raw: Vec<u8>) -> InternalResult<(Vec<u8>, bool)> {
66		/* Literally the hottest function in the block (🕶) */
67
68		// buffer_a originally contains the raw data
69		let mut decrypted = None;
70		let mut verified = false;
71
72		// Signature validation
73		// Validate signature only if a public key is present
74		#[cfg(feature = "crypto")]
75		if let Some(pk) = self.key {
76			// If there is an error the data is flagged as invalid
77			if let Some(signature) = entry.signature {
78				let raw_size = raw.len();
79
80				let entry_bytes = entry.to_bytes(true)?;
81				raw.extend_from_slice(&entry_bytes);
82
83				verified = pk.verify_strict(&raw, &signature).is_ok();
84				raw.truncate(raw_size);
85			}
86		}
87
88		// 1: Decryption layer
89		if entry.flags.contains(Flags::ENCRYPTED_FLAG) {
90			#[cfg(feature = "crypto")]
91			match self.decryptor.as_ref() {
92				Some(dc) => {
93					decrypted = Some(dc.decrypt(&raw)?);
94				},
95				None => return Err(InternalError::NoKeypairError),
96			}
97
98			#[cfg(not(feature = "crypto"))]
99			return Err(InternalError::MissingFeatureError("crypto"));
100		}
101
102		// 2: Decompression layer
103		if entry.flags.contains(Flags::COMPRESSED_FLAG) {
104			#[cfg(feature = "compression")]
105			{
106				let (source, mut target) = match decrypted {
107					// data was decrypted and stored.
108					Some(vec) => {
109						raw.clear();
110						(vec, raw)
111					},
112					// data was not decrypted nor stored.
113					None => {
114						let capacity = raw.capacity();
115						(raw, Vec::with_capacity(capacity))
116					},
117				};
118
119				if entry.flags.contains(Flags::LZ4_COMPRESSED) {
120					Compressor::new(source.as_slice()).decompress(CompressionAlgorithm::LZ4, &mut target)?
121				} else if entry.flags.contains(Flags::BROTLI_COMPRESSED) {
122					Compressor::new(source.as_slice()).decompress(CompressionAlgorithm::Brotli(0), &mut target)?
123				} else if entry.flags.contains(Flags::SNAPPY_COMPRESSED) {
124					Compressor::new(source.as_slice()).decompress(CompressionAlgorithm::Snappy, &mut target)?
125				} else {
126					return InternalResult::Err(InternalError::OtherError(
127						format!(
128							"Unable to determine the compression algorithm used for entry: {}",
129							entry
130						)
131						.into(),
132					));
133				};
134
135				Ok((target, verified))
136			}
137
138			#[cfg(not(feature = "compression"))]
139			Err(InternalError::MissingFeatureError("compression"))
140		} else {
141			match decrypted {
142				Some(decrypted) => Ok((decrypted, verified)),
143				None => Ok((raw, verified)),
144			}
145		}
146	}
147}
148
149impl<T> Archive<T>
150where
151	T: Seek + Read,
152{
153	/// Parses an [`Archive`] from the given source
154	pub fn new(mut handle: T) -> InternalResult<Archive<T>> {
155		// Start reading from the start of the input
156		handle.seek(SeekFrom::Start(0))?;
157
158		let header = Header::from_handle(&mut handle)?;
159		header.validate()?;
160
161		// Generate and store Registry Entries
162		let mut entries = HashMap::new();
163
164		// Construct entries map
165		for _ in 0..header.capacity {
166			let entry = RegistryEntry::from_handle(&mut handle)?;
167			entries.insert(entry.id.clone(), entry);
168		}
169
170		let archive = Archive {
171			header,
172			handle: Mutex::new(handle),
173			entries,
174
175			#[cfg(feature = "crypto")]
176			key: None,
177			#[cfg(feature = "crypto")]
178			decryptor: None,
179		};
180
181		Ok(archive)
182	}
183
184	/// Parse an [`Archive`], with an optional [`VerifyingKey`](crypto::VerifyingKey).
185	#[cfg(feature = "crypto")]
186	pub fn with_key(mut handle: T, vk: &ed25519_dalek::VerifyingKey) -> InternalResult<Archive<T>> {
187		// Start reading from the start of the input
188		handle.seek(SeekFrom::Start(0))?;
189
190		let header = Header::from_handle(&mut handle)?;
191		header.validate()?;
192
193		// Generate and store Registry Entries
194		let mut entries = HashMap::new();
195
196		// Construct entries map
197		for _ in 0..header.capacity {
198			let entry = RegistryEntry::from_handle(&mut handle)?;
199			entries.insert(entry.id.clone(), entry);
200		}
201
202		let archive = Archive {
203			header,
204			handle: Mutex::new(handle),
205			entries,
206
207			key: Some(vk.clone()),
208			decryptor: Some(crypto::Encryptor::new(vk)),
209		};
210
211		Ok(archive)
212	}
213
214	/// Fetch a [`RegistryEntry`] from this [`Archive`].
215	/// This can be used for debugging, as the [`RegistryEntry`] holds information on data with the adjacent ID.
216	pub fn fetch_entry(&self, id: impl AsRef<str>) -> Option<RegistryEntry> {
217		self.entries.get(id.as_ref()).cloned()
218	}
219
220	/// Returns an immutable reference to the underlying [`HashMap`]. This hashmap stores [`RegistryEntry`] values and uses `String` keys.
221	#[inline(always)]
222	pub fn entries(&self) -> &HashMap<Arc<str>, RegistryEntry> {
223		&self.entries
224	}
225
226	/// Global flags extracted from the `Header` section of the source
227	#[inline(always)]
228	pub fn flags(&self) -> &Flags {
229		&self.header.flags
230	}
231}
232
233impl<T> Archive<T>
234where
235	T: Read + Seek,
236{
237	/// Given a data source and a [`RegistryEntry`], gets the adjacent raw data
238	pub(crate) fn read_raw(handle: &mut T, entry: &RegistryEntry) -> InternalResult<Vec<u8>> {
239		let mut buffer = Vec::with_capacity(entry.offset as usize + 64);
240		handle.seek(SeekFrom::Start(entry.location))?;
241
242		let mut take = handle.take(entry.offset);
243		take.read_to_end(&mut buffer)?;
244
245		Ok(buffer)
246	}
247
248	/// Cheaper alternative to [`fetch`](Archive::fetch) that doesn't lock the underlying [Mutex]
249	pub fn fetch_mut(&mut self, id: impl AsRef<str>) -> InternalResult<Resource> {
250		// The reason for this function's unnecessary complexity is it uses the provided functions independently, thus preventing an unnecessary allocation [MAYBE TOO MUCH?]
251		if let Some(entry) = self.fetch_entry(&id) {
252			let raw = Archive::read_raw(self.handle.get_mut().unwrap(), &entry)?;
253
254			// Prepare contextual variables
255			// Decompress and|or decrypt the data
256			let (buffer, verified) = self.process(&entry, raw)?;
257
258			Ok(Resource {
259				content_version: entry.content_version,
260				flags: entry.flags,
261				data: buffer.into_boxed_slice(),
262				verified,
263			})
264		} else {
265			Err(InternalError::MissingResourceError(id.as_ref().to_string()))
266		}
267	}
268
269	/// Fetch a [`Resource`] with the given `ID`.
270	/// > Locks the underlying [`Mutex`], for a cheaper non-locking operation refer to `Archive::fetch_mut`
271	pub fn fetch(&self, id: impl AsRef<str>) -> InternalResult<Resource> {
272		// The reason for this function's unnecessary complexity is it uses the provided functions independently, thus preventing an unnecessary allocation [MAYBE TOO MUCH?]
273		if let Some(entry) = self.fetch_entry(&id) {
274			let raw = {
275				let mut guard = self.handle.lock().unwrap();
276				Archive::read_raw(guard.deref_mut(), &entry)?
277			};
278
279			// Prepare contextual variables
280			// Decompress and|or decrypt the data
281			let (buffer, is_secure) = self.process(&entry, raw)?;
282
283			Ok(Resource {
284				content_version: entry.content_version,
285				flags: entry.flags,
286				data: buffer.into_boxed_slice(),
287				verified: is_secure,
288			})
289		} else {
290			Err(InternalError::MissingResourceError(id.as_ref().to_string()))
291		}
292	}
293}