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::{cell::RefCell, collections::HashMap, fmt::Debug, fs::{self, File}, io::{BufReader, Cursor, Read, Seek, SeekFrom}, path::Path};
5use btree::{PakTree, PakTreeBuilder};
6use index::PakIndex;
7use item::{PakItemDeserialize, PakItemDeserializeGroup, PakItemSearchable, PakItemSerialize};
8use meta::{PakMeta, PakSizing};
9use pointer::{PakPointer, PakTypedPointer, PakUntypedPointer};
10use query::PakQueryExpression;
11
12use crate::error::PakResult;
13
14#[cfg(test)]
15mod test;
16
17pub mod meta;
18pub mod item;
19pub mod index;
20pub mod value;
21pub(crate) mod btree;
22pub mod query;
23pub mod error;
24pub mod pointer;
25
26//==============================================================================================
27//        Pak File
28//==============================================================================================
29
30/// Represents a Pak file. This struct provides access to the metadata and data stored within the Pak file.
31pub struct Pak {
32    sizing : PakSizing,
33    meta : PakMeta,
34    source : RefCell<Box<dyn PakSource>>
35}
36
37impl Pak {
38    /// Creates a new Pak instance from a [PakSource](crate::PakSource).
39    pub fn new<S>(mut source : S) -> PakResult<Self> where S : PakSource + 'static {
40        let sizing_pointer = PakPointer::new_untyped(0, 24);
41        let sizing_buffer = source.read(&sizing_pointer, 0)?;
42        let sizing : PakSizing = bincode::deserialize(&sizing_buffer)?;
43        
44        let meta_pointer = PakPointer::new_untyped(24, sizing.meta_size);
45        let meta_buffer = source.read(&meta_pointer, 0)?;
46        let meta : PakMeta = bincode::deserialize(&meta_buffer)?;
47
48        Ok(Self { sizing, source : RefCell::new(Box::new(source)), meta })
49    }
50    
51    /// Loads a Pak from the specified file path. This will not load the entire pak file into memory, just the header.
52    pub fn new_from_file<P>(path : P) -> PakResult<Self> where P : AsRef<Path> {
53        let file = File::open(path)?;
54        Self::new(BufReader::new(file))
55    }
56    
57    /// Loads an object from the pak file via queried indices. This will only load the necessary data into memory.
58    pub fn query<T>(&self, query : impl PakQueryExpression) -> PakResult<T::ReturnType> where T : PakItemDeserializeGroup  {
59        let pointers = query.execute(self)?.into_iter().map(|i| i.into_pointer()).collect();
60        T::deserialize_group(self, pointers)
61    }
62    
63    /// Returns the size of the pak file in bytes.
64    pub fn size(&self) -> u64 {
65        24 + self.sizing.meta_size + self.sizing.indices_size + self.sizing.vault_size
66    }
67    
68    /// Returns the name given to the pak file.
69    pub fn name(&self) -> &str {
70        &self.meta.name
71    }
72    
73    /// Returns the version of the pak file.
74    pub fn version(&self) -> &str {
75        &self.meta.version
76    }
77    
78    /// Returns the author of the pak file.
79    pub fn author(&self) -> &str {
80        &self.meta.author
81    }
82    
83    /// Returns the description of the pak file.
84    pub fn description(&self) -> &str {
85        &self.meta.description
86    }
87    
88    pub(crate) fn read_err<T>(&self, pointer : &PakPointer) -> PakResult<T> where T : PakItemDeserialize {
89        if !pointer.type_is_match::<T>() { return Err(error::PakError::TypeMismatchError(pointer.type_name().to_string(), std::any::type_name::<T>().to_string())) }
90        let buffer = self.source.borrow_mut().read(pointer, self.get_vault_start())?;
91        let res = T::from_bytes(&buffer)?;
92        Ok(res)
93    }
94    
95    pub(crate) fn read<T>(&self, pointer : &PakPointer) -> Option<T> where T : PakItemDeserialize {
96        let res = self.read_err(pointer);
97        match res {
98            Ok(res) => Some(res),
99            Err(_) => None,
100        }
101    }
102    
103    pub(crate) fn get_tree(&self, key : &str) -> PakResult<PakTree> {
104        PakTree::new(self, key)
105    }
106    
107    pub(crate) fn fetch_indices(&self) -> PakResult<HashMap<String, PakUntypedPointer>> {
108        let pointer = PakPointer::new_untyped(self.get_indices_start(), self.sizing.indices_size);
109        let buffer = self.source.borrow_mut().read(&pointer, 0)?;
110        let indices = bincode::deserialize(&buffer)?;
111        Ok(indices)
112    }
113    
114    pub(crate) fn get_vault_start(&self) -> u64 {
115        // To be honest, I'm not sure why this start is offset by 8, it just is and I am to scared to ask.
116        24 + self.sizing.meta_size + self.sizing.indices_size + 8
117    }
118    
119    pub(crate) fn get_indices_start(&self) -> u64 {
120        24 + self.sizing.meta_size
121    }
122    
123}
124
125//==============================================================================================
126//        PakSource
127//==============================================================================================
128
129///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).
130pub trait PakSource {
131    ///Returns data from the source based on a [PakPointer](crate::PakPointer)
132    fn read(&mut self, pointer : &PakPointer, offset : u64) -> PakResult<Vec<u8>>;
133}
134
135impl <R> PakSource for R where R : Read + Seek {
136    fn read(&mut self, pointer : &PakPointer, offset : u64) -> PakResult<Vec<u8>> {
137        let mut buffer = vec![0u8; pointer.size() as usize];
138        self.seek(SeekFrom::Start(pointer.offset() + offset))?;
139        self.read_exact(&mut buffer)?;
140        Ok(buffer)
141    }
142}
143
144//==============================================================================================
145//        PakBuilder
146//==============================================================================================
147
148/// When it is time to create the pak file, this struct is used to build it. Remember that this struct doen't have the ability to read data that has been paked or delete data that has been paked.
149pub struct PakBuilder {
150    chunks : Vec<PakVaultReference>,
151    size_in_bytes : u64,
152    vault : Vec<u8>,
153    name: String,
154    description: String,
155    author: String,
156}
157
158impl PakBuilder {
159    /// Creates a new instance of PakBuilder.
160    pub fn new() -> Self {
161        Self {
162            vault : Vec::new(),
163            chunks : Vec::new(),
164            size_in_bytes : 0,
165            name: String::new(),
166            description: String::new(),
167            author: String::new(),
168        }
169    }
170    
171    /// Adds an item to the pak file that does not support searching. Takes anything that implements [PakItemSerialize](crate::PakItemSerialize).
172    pub fn pak_no_search<T: PakItemSerialize>(&mut self, item : T) -> PakResult<PakPointer> {
173        let bytes = item.into_bytes()?;
174        let pointer = PakPointer::new_typed::<T>(self.size_in_bytes, bytes.len() as u64);
175        self.size_in_bytes += bytes.len() as u64;
176        self.vault.extend(bytes);
177        self.chunks.push(PakVaultReference { pointer: pointer.clone().into_typed::<T>(), indices: vec![] });
178        Ok(pointer)
179    }
180    
181    /// Adds an item to the pak file that supports searching. Takes anything that implements [PakItemSerialize](crate::PakItemSerialize) and [PakItemSearchable](crate::PakItemSearchable).
182    pub fn pak<T : PakItemSerialize + PakItemSearchable>(&mut self, item : T) -> PakResult<PakPointer> {
183        let indices = item.get_indices();
184        let bytes = item.into_bytes()?;
185        let pointer = PakPointer::new_typed::<T>(self.size_in_bytes, bytes.len() as u64);
186        self.size_in_bytes += bytes.len() as u64;
187        self.vault.extend(bytes);
188        self.chunks.push(PakVaultReference { pointer: pointer.clone().into_typed::<T>(), indices: indices.clone() });
189        Ok(pointer)
190    }
191    
192    /// The current size of the pak file in bytes.
193    pub fn size(&self) -> u64 {
194        self.size_in_bytes
195    }
196    
197    /// The number of items in the pak file.
198    pub fn len(&self) -> usize {
199        self.chunks.len()
200    }
201    
202    /// Adds a name to the pak file's metadata.
203    pub fn with_name(mut self, name: &str) -> Self {
204        self.name = name.to_string();
205        self
206    }
207    
208    /// Adds a description to the pak file's metadata.
209    pub fn with_description(mut self, description: &str) -> Self {
210        self.description = description.to_string();
211        self
212    }
213    
214    /// Adds an author to the pak file's metadata.
215    pub fn with_author(mut self, author: &str) -> Self {
216        self.author = author.to_string();
217        self
218    }
219    
220    /// Sets the name of the pak file's metadata.
221    pub fn set_name(&mut self, name: &str) {
222        self.name = name.to_string();
223    }
224    
225    /// Sets the description of the pak file's metadata.
226    pub fn set_description(&mut self, description: &str) {
227        self.description = description.to_string();
228    }
229    
230    /// Sets the author of the pak file's metadata.
231    pub fn set_author(&mut self, author: &str) {
232        self.author = author.to_string();
233    }
234    
235    /// Builds the pak file and writes it to the specified path. This also returns a [Pak](crate::Pak) object that is attached to that file.
236    pub fn build_file(self, path : impl AsRef<Path>) -> PakResult<Pak> {
237        let (out, sizing, meta) = self.build_internal()?;
238        
239        fs::write(&path, out)?;
240        let pak  = Pak {
241            sizing,
242            meta,
243            source: RefCell::new(Box::new(BufReader::new(File::open(path)?))),
244        };
245        Ok(pak)
246    }
247    
248    /// Builds the pak file and writes it to the specified path. This also returns a [Pak](crate::Pak) object that is attached to that slice of memory.
249    pub fn build_in_memory(self) -> PakResult<Pak> {
250        let (out, sizing, meta) = self.build_internal()?;
251        
252        let pak = Pak {
253            sizing,
254            meta,
255            source: RefCell::new(Box::new(Cursor::new(out))),
256        };
257        Ok(pak)
258    }
259    
260    fn build_internal(mut self)  -> PakResult<(Vec<u8>, PakSizing, PakMeta)> {
261        let mut map : HashMap<String, PakTreeBuilder> = HashMap::new();
262        for chunk in &self.chunks {
263            for index in &chunk.indices{
264                map.entry(index.key.clone())
265                    .or_insert(PakTreeBuilder::new(6))
266                    .access()
267                    .insert(index.value.clone(), chunk.pointer.clone())
268                ;
269            }
270        }
271        
272        let mut pointer_map : HashMap<String, PakUntypedPointer> = HashMap::new();
273        for (key, tree) in map {
274            let pointer = tree.into_pak(&mut self)?;
275            pointer_map.insert(key, pointer.as_untyped());
276        }
277        
278        let meta = PakMeta {
279            name: self.name,
280            description: self.description,
281            author: self.author,
282            version: "1.0".to_string(),
283        };
284        
285        let sizing = PakSizing {
286            meta_size: bincode::serialized_size(&meta)?,
287            indices_size: bincode::serialized_size(&pointer_map)?,
288            vault_size: bincode::serialized_size(&self.vault)?,
289        };
290        
291        let mut sizing_out = bincode::serialize(&sizing)?;
292        let mut meta_out = bincode::serialize(&meta)?;
293        let mut pointer_map_out = bincode::serialize(&pointer_map)?;
294        let mut vault_out = bincode::serialize(&self.vault)?;
295        
296        let mut out = Vec::<u8>::new();
297        out.append(&mut sizing_out);
298        out.append(&mut meta_out);
299        out.append(&mut pointer_map_out);
300        out.append(&mut vault_out);
301        Ok((out, sizing, meta))
302    }
303    
304}
305
306//==============================================================================================
307//        PakVaultReference
308//==============================================================================================
309
310#[derive(Debug, Clone)]
311pub(crate) struct PakVaultReference {
312    pointer : PakTypedPointer,
313    indices : Vec<PakIndex>
314}