pak_db/
lib.rs

1#![doc = include_str!("../README.md")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/MrVintage710/pak/refs/heads/main/docs/icon.png")]
3
4use std::{collections::HashMap, fs::File, io::{BufReader, Read, Seek, SeekFrom}, path::Path, sync::{Arc, RwLock, RwLockWriteGuard, Weak}};
5use btree::PakTree;
6use group::{DeserializeGroup};
7use meta::{PakMeta, PakSizing};
8use pointer::{PakPointer, PakUntypedPointer};
9use query::PakQueryExpression;
10use serde::Deserialize;
11
12use crate::{error::PakResult};
13
14#[cfg(test)]
15mod test;
16
17pub mod meta;
18pub mod group;
19pub mod index;
20pub mod value;
21pub(crate) mod btree;
22pub mod query;
23pub mod error;
24pub mod pointer;
25pub mod builder;
26
27//==============================================================================================
28//        Pak File
29//==============================================================================================
30
31pub const PAK_FILE_VERSION : &'static str = "1.1";
32
33pub const PAK_SIZING_STRUCT_SIZE_IN_BYTES : u64 = 32;
34
35/// Represents a Pak file. This struct provides access to the metadata and data stored within the Pak file.
36pub struct Pak {
37    inner : Arc<PakInner>
38}
39
40impl Pak {
41    /// Creates a new Pak instance from a [PakSource](crate::PakSource).
42    pub fn new<S>(mut source : S) -> PakResult<Self> where S : PakSource + Send + Sync + 'static {
43        let sizing_pointer = PakPointer::new_untyped(0, PAK_SIZING_STRUCT_SIZE_IN_BYTES);
44        let sizing_buffer = source.read(&sizing_pointer, 0)?;
45        let sizing : PakSizing = bincode::deserialize(&sizing_buffer)?;
46        
47        let meta_pointer = PakPointer::new_untyped(PAK_SIZING_STRUCT_SIZE_IN_BYTES, sizing.meta_size);
48        let meta_buffer = source.read(&meta_pointer, 0)?;
49        let meta : PakMeta = bincode::deserialize(&meta_buffer)?;
50        
51        let inner = Arc::new(PakInner { sizing, source : RwLock::new(Box::new(source)), meta });
52
53        Ok(Self { inner })
54    }
55    
56    /// Loads a Pak from the specified file path. This will not load the entire pak file into memory, just the header.
57    pub fn new_from_file<P>(path : P) -> PakResult<Self> where P : AsRef<Path> {
58        let file = File::open(path)?;
59        Self::new(BufReader::new(file))
60    }
61    
62    /// Loads an object from the pak file via queried indices. This will only load the necessary data into memory.
63    pub fn query<T>(&self, query : impl PakQueryExpression<T>) -> PakResult<T::ReturnType> where T : DeserializeGroup  {
64        let pointers = query.execute(self)?.into_iter().collect();
65        T::deserialize_group(self, pointers)
66    }
67    
68    /// Loads an object from the pak file via queried indices. This will only load the necessary data into memory.
69    pub fn query_pql<T>(&self, pql : &str) -> PakResult<T::ReturnType> where T : DeserializeGroup + 'static  {
70        let query = crate::query::pql::pql(pql)?;
71        self.query::<T>(query)
72    }
73    
74    /// Returns the size of the pak file in bytes.
75    pub fn size(&self) -> u64 {
76        24 + self.inner.sizing.meta_size + self.inner.sizing.indices_size + self.inner.sizing.vault_size
77    }
78    
79    /// Returns the name given to the pak file.
80    pub fn name(&self) -> &str {
81        &self.inner.meta.name
82    }
83    
84    /// Returns the version of the pak file.
85    pub fn version(&self) -> &str {
86        &self.inner.meta.version
87    }
88    
89    /// Returns the author of the pak file.
90    pub fn author(&self) -> &str {
91        &self.inner.meta.author
92    }
93    
94    /// Returns the description of the pak file.
95    pub fn description(&self) -> &str {
96        &self.inner.meta.description
97    }
98    
99    /// This returns the extra data that can be saved in the metadata. This can throw an error if the
100    /// wrong type is asked for.
101    pub fn get_extra<T>(&self) -> PakResult<T> where T : for<'de> Deserialize<'de> {
102        Ok(bincode::deserialize(&self.inner.meta.extra)?)
103    }
104    
105    /// Read Data directly with a pointer. 
106    pub fn read_err<T>(&self, pointer : &PakPointer) -> PakResult<T> where T : for<'de> Deserialize<'de> {
107        self.inner.read_err(pointer)
108    }
109    
110    pub fn read<T>(&self, pointer : &PakPointer) -> Option<T> where T : for<'de> Deserialize<'de> {
111        self.inner.read(pointer)
112    }
113    
114    pub fn identifier(&self) -> &str {
115        &self.inner.meta.identifier
116    }
117    
118    pub(crate) fn weak(&self) -> Weak<PakInner> {
119        Arc::downgrade(&self.inner)
120    }
121    
122    pub fn check_identifier(&self, id : &str) -> PakResult<()> {
123        if self.identifier() != id { return Err(error::PakError::PakIdentifierMismatch)}
124        Ok(())
125    }
126    
127    pub(crate) fn get_tree(&self, key : &str) -> PakResult<PakTree<'_>> {
128        PakTree::new(self, key)
129    }
130    
131    pub(crate) fn fetch_indices(&self) -> PakResult<HashMap<String, PakUntypedPointer>> {
132        let pointer = PakPointer::new_untyped(self.get_indices_start(), self.inner.sizing.indices_size);
133        let Ok(mut source) = self.inner.source.write() else { return Err(error::PakError::SourceInUse) };
134        let buffer = source.read(&pointer, 0)?;
135        let indices = bincode::deserialize(&buffer)?;
136        Ok(indices)
137    }
138    
139    pub(crate) fn fetch_all_pointers_of<T>(&self) -> PakResult<Vec<PakPointer>> where T : DeserializeGroup {
140        let Ok(mut source) = self.inner.source.write() else { return Err(error::PakError::SourceInUse) };
141        let lists_pointer = PakPointer::new_untyped(self.get_list_start(), self.inner.sizing.list_size);
142        let lists_buffer = source.read(&lists_pointer, 0)?;
143        let lists : HashMap<String, PakPointer> = bincode::deserialize(&lists_buffer)?;
144        let values = T::get_types().into_iter()
145            .filter_map(|type_name| lists.get(type_name))
146            .filter_map(|pointer| self.inner.read_internal::<Vec<PakPointer>>(pointer, &mut source).ok())
147            .flatten()
148            .collect::<Vec<_>>();
149        Ok(values)
150    }
151    
152    pub(crate) fn get_list_start(&self) -> u64 {
153        self.inner.get_list_start()
154    }
155    
156    pub(crate) fn get_indices_start(&self) -> u64 {
157        self.inner.get_indices_start()
158    }
159}
160
161//==============================================================================================
162//        PakInner
163//==============================================================================================
164
165pub(crate) struct PakInner {
166    sizing : PakSizing,
167    meta : PakMeta,
168    source : RwLock<Box<dyn PakSource + Send + Sync + 'static>>,
169}
170
171impl PakInner {
172    fn read_err<T>(&self, pointer : &PakPointer) -> PakResult<T> where T : for<'de> Deserialize<'de> {
173        if !pointer.type_is_match::<T>() { return Err(error::PakError::TypeMismatchError(pointer.type_name().to_string(), std::any::type_name::<T>().to_string())) }
174        let Ok(mut source) = self.source.write() else { return Err(error::PakError::SourceInUse)};
175        self.read_internal(pointer, &mut source)
176    }
177    
178    fn read<T>(&self, pointer : &PakPointer) -> Option<T> where T : for<'de> Deserialize<'de> {
179        self.read_err::<T>(pointer).ok()
180    }
181    
182    fn read_internal<T>(&self, pointer : &PakPointer, source : &mut RwLockWriteGuard<Box<dyn PakSource + Send + Sync + 'static>>) -> PakResult<T> where T : for<'de> Deserialize<'de> {
183        let buffer = source.read(pointer, self.get_vault_start())?;
184        let res = bincode::deserialize(&buffer)?;
185        Ok(res)
186    }
187    
188    fn get_vault_start(&self) -> u64 {
189        // To be honest, I'm not sure why this start is offset by 8, it just is and I am to scared to ask.
190        self.get_list_start() + self.sizing.list_size + 8
191    }
192    
193    fn get_list_start(&self) -> u64 {
194        self.get_indices_start() + self.sizing.indices_size
195    }
196    
197    fn get_indices_start(&self) -> u64 {
198        PAK_SIZING_STRUCT_SIZE_IN_BYTES + self.sizing.meta_size
199    }
200}
201
202//==============================================================================================
203//        PakSource
204//==============================================================================================
205
206///This is where a Pak file will load from. This trait is automatically implemented for any type that implements [Read](std::io::Read) and [Seek](std::io::Seek).
207pub trait PakSource {
208    ///Returns data from the source based on a [PakPointer](crate::PakPointer)
209    fn read(&mut self, pointer : &PakPointer, offset : u64) -> PakResult<Vec<u8>>;
210}
211
212impl <R> PakSource for R where R : Read + Seek {
213    fn read(&mut self, pointer : &PakPointer, offset : u64) -> PakResult<Vec<u8>> {
214        let mut buffer = vec![0u8; pointer.size() as usize];
215        self.seek(SeekFrom::Start(pointer.offset() + offset))?;
216        self.read_exact(&mut buffer)?;
217        Ok(buffer)
218    }
219}
220