tf_asset_loader/
source.rs

1use crate::LoaderError;
2use std::fs::read;
3use std::io::ErrorKind;
4use std::path::PathBuf;
5
6/// Trait for the various sources that assets can be loaded from
7pub trait AssetSource {
8    /// Check if a path exists in the source
9    fn has(&self, path: &str) -> Result<bool, LoaderError>;
10
11    /// Load an asset from the source by path if it exists
12    fn load(&self, path: &str) -> Result<Option<Vec<u8>>, LoaderError>;
13}
14
15impl AssetSource for PathBuf {
16    fn has(&self, path: &str) -> Result<bool, LoaderError> {
17        Ok(self.join(path).exists())
18    }
19
20    fn load(&self, path: &str) -> Result<Option<Vec<u8>>, LoaderError> {
21        match read(self.join(path)) {
22            Ok(data) => Ok(Some(data)),
23            Err(e) if e.kind() == ErrorKind::NotFound => Ok(None),
24            Err(e) => Err(e.into()),
25        }
26    }
27}
28
29#[cfg(feature = "vpk")]
30mod vdf {
31    use super::AssetSource;
32    use crate::LoaderError;
33    use vpk::VPK;
34
35    impl AssetSource for VPK {
36        fn has(&self, path: &str) -> Result<bool, LoaderError> {
37            Ok(self.tree.contains_key(path))
38        }
39
40        fn load(&self, path: &str) -> Result<Option<Vec<u8>>, LoaderError> {
41            if let Some(entry) = self.tree.get(path) {
42                Ok(Some(entry.get()?.into()))
43            } else {
44                Ok(None)
45            }
46        }
47    }
48}
49
50#[cfg(feature = "bsp")]
51mod vbsp {
52    use super::AssetSource;
53    use crate::LoaderError;
54    use vbsp::Packfile;
55
56    impl AssetSource for Packfile {
57        fn has(&self, path: &str) -> Result<bool, LoaderError> {
58            Ok(self.has(path)?)
59        }
60
61        fn load(&self, path: &str) -> Result<Option<Vec<u8>>, LoaderError> {
62            Ok(self.get(path)?)
63        }
64    }
65}
66
67#[cfg(feature = "zip")]
68mod zip {
69    use super::AssetSource;
70    use crate::LoaderError;
71    use std::io::{Read, Seek};
72    use std::sync::Mutex;
73    use zip::result::ZipError;
74    use zip::ZipArchive;
75
76    impl<Reader: Read + Seek> AssetSource for Mutex<ZipArchive<Reader>> {
77        fn has(&self, path: &str) -> Result<bool, LoaderError> {
78            match self.lock().unwrap().by_name(path) {
79                Ok(_) => Ok(true),
80                Err(ZipError::FileNotFound) => Ok(false),
81                Err(e) => Err(e.into()),
82            }
83        }
84
85        fn load(&self, path: &str) -> Result<Option<Vec<u8>>, LoaderError> {
86            let mut zip = self.lock().unwrap();
87            let mut entry = match zip.by_name(path) {
88                Ok(entry) => entry,
89                Err(ZipError::FileNotFound) => {
90                    return Ok(None);
91                }
92                Err(e) => {
93                    return Err(e.into());
94                }
95            };
96            let mut buff = vec![0; entry.size() as usize];
97            entry.read_exact(&mut buff)?;
98            Ok(Some(buff))
99        }
100    }
101}