2019-04-26 03:30:46 +00:00
|
|
|
use dot_vox::DotVoxData;
|
|
|
|
use image::DynamicImage;
|
|
|
|
use lazy_static::lazy_static;
|
|
|
|
use std::{
|
|
|
|
any::Any,
|
|
|
|
collections::HashMap,
|
|
|
|
fs::File,
|
|
|
|
io::Read,
|
2019-04-28 02:12:30 +00:00
|
|
|
sync::{Arc, RwLock},
|
2019-04-26 03:30:46 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub enum Error {
|
2019-05-08 01:59:35 +00:00
|
|
|
/// An asset of a different type has already been loaded with this specifier
|
2019-04-26 03:30:46 +00:00
|
|
|
InvalidType,
|
2019-04-29 00:23:14 +00:00
|
|
|
/// Asset does not exist
|
|
|
|
NotFound(String),
|
2019-04-26 03:30:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Arc<dyn Any + 'static + Sync + Send>> for Error {
|
2019-04-29 00:23:14 +00:00
|
|
|
fn from(_: Arc<dyn Any + 'static + Sync + Send>) -> Self {
|
2019-04-26 03:30:46 +00:00
|
|
|
Error::InvalidType
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<std::io::Error> for Error {
|
2019-04-29 00:23:14 +00:00
|
|
|
fn from(err: std::io::Error) -> Self {
|
|
|
|
Error::NotFound(format!("{:?}", err))
|
2019-04-26 03:30:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lazy_static! {
|
|
|
|
static ref ASSETS: RwLock<HashMap<String, Arc<dyn Any + 'static + Sync + Send>>> =
|
|
|
|
RwLock::new(HashMap::new());
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Function used to load assets
|
2019-04-29 00:23:14 +00:00
|
|
|
/// loaded assets are cached in a global singleton hashmap
|
2019-04-26 03:30:46 +00:00
|
|
|
/// Example usage:
|
2019-05-07 18:36:07 +00:00
|
|
|
/// ```no_run
|
2019-04-26 03:30:46 +00:00
|
|
|
/// use image::DynamicImage;
|
2019-05-07 18:36:07 +00:00
|
|
|
/// use veloren_common::assets;
|
2019-05-07 05:40:03 +00:00
|
|
|
///
|
2019-05-07 18:36:07 +00:00
|
|
|
/// let my_image = assets::load::<DynamicImage>("core.ui.backgrounds.city").unwrap();
|
2019-04-26 03:30:46 +00:00
|
|
|
/// ```
|
|
|
|
pub fn load<A: Asset + 'static>(specifier: &str) -> Result<Arc<A>, Error> {
|
|
|
|
Ok(ASSETS
|
2019-05-07 05:40:03 +00:00
|
|
|
.write()
|
|
|
|
.unwrap()
|
2019-04-26 03:30:46 +00:00
|
|
|
.entry(specifier.to_string())
|
|
|
|
.or_insert(Arc::new(A::load(specifier)?))
|
|
|
|
.clone()
|
|
|
|
.downcast()?)
|
|
|
|
}
|
|
|
|
|
2019-04-29 00:23:14 +00:00
|
|
|
/// Function used to load assets that will panic if the asset is not found
|
|
|
|
/// Use this to load essential assets
|
|
|
|
/// loaded assets are cached in a global singleton hashmap
|
|
|
|
/// Example usage:
|
2019-05-07 18:36:07 +00:00
|
|
|
/// ```no_run
|
2019-04-29 00:23:14 +00:00
|
|
|
/// use image::DynamicImage;
|
2019-05-07 18:36:07 +00:00
|
|
|
/// use veloren_common::assets;
|
2019-05-07 05:40:03 +00:00
|
|
|
///
|
2019-05-07 18:36:07 +00:00
|
|
|
/// let my_image = assets::load_expect::<DynamicImage>("core.ui.backgrounds.city");
|
2019-04-29 00:23:14 +00:00
|
|
|
/// ```
|
|
|
|
pub fn load_expect<A: Asset + 'static>(specifier: &str) -> Arc<A> {
|
2019-05-07 05:40:03 +00:00
|
|
|
load(specifier).expect(&format!("Failed loading essential asset: {}", specifier))
|
2019-04-29 00:23:14 +00:00
|
|
|
}
|
|
|
|
|
2019-04-26 03:30:46 +00:00
|
|
|
/// Asset Trait
|
|
|
|
pub trait Asset: Send + Sync + Sized {
|
|
|
|
fn load(specifier: &str) -> Result<Self, Error>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Asset for DynamicImage {
|
|
|
|
fn load(specifier: &str) -> Result<Self, Error> {
|
2019-05-07 05:40:03 +00:00
|
|
|
Ok(image::load_from_memory(load_from_path(specifier)?.as_slice()).unwrap())
|
2019-04-26 03:30:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Asset for DotVoxData {
|
|
|
|
fn load(specifier: &str) -> Result<Self, Error> {
|
2019-05-07 05:40:03 +00:00
|
|
|
Ok(dot_vox::load_bytes(load_from_path(specifier)?.as_slice()).unwrap())
|
2019-04-26 03:30:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: System to load file from specifiers (eg "core.ui.backgrounds.city")
|
2019-04-29 00:23:14 +00:00
|
|
|
fn try_open_with_path(name: &str) -> Option<File> {
|
2019-04-30 20:43:55 +00:00
|
|
|
debug!("Trying to access \"{}\"", name);
|
2019-04-29 00:32:02 +00:00
|
|
|
// TODO: don't do this?
|
2019-04-29 00:23:14 +00:00
|
|
|
// if it's stupid and it works..,
|
|
|
|
[
|
2019-04-26 03:30:46 +00:00
|
|
|
"assets".to_string(),
|
|
|
|
"../assets".to_string(), /* optimizations */
|
2019-04-29 00:23:14 +00:00
|
|
|
"../../assets".to_string(),
|
|
|
|
[env!("CARGO_MANIFEST_DIR"), "/../assets"].concat(),
|
2019-04-26 03:30:46 +00:00
|
|
|
[env!("CARGO_MANIFEST_DIR"), "/assets"].concat(),
|
|
|
|
[env!("CARGO_MANIFEST_DIR"), "/../../assets"].concat(),
|
|
|
|
"../../../assets".to_string(),
|
|
|
|
[env!("CARGO_MANIFEST_DIR"), "/../../../assets"].concat(),
|
2019-04-29 00:23:14 +00:00
|
|
|
]
|
|
|
|
.into_iter()
|
|
|
|
.map(|bp| [bp, name].concat())
|
2019-04-30 18:01:47 +00:00
|
|
|
.find_map(|ref filename| File::open(filename).ok())
|
2019-04-26 03:30:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn load_from_path(name: &str) -> Result<Vec<u8>, Error> {
|
2019-04-29 00:23:14 +00:00
|
|
|
match try_open_with_path(name) {
|
2019-04-26 03:30:46 +00:00
|
|
|
Some(mut f) => {
|
2019-04-29 00:23:14 +00:00
|
|
|
let mut content = Vec::<u8>::new();
|
2019-04-28 02:12:30 +00:00
|
|
|
f.read_to_end(&mut content)?;
|
2019-04-26 03:30:46 +00:00
|
|
|
Ok(content)
|
2019-04-29 00:23:14 +00:00
|
|
|
}
|
2019-05-07 05:40:03 +00:00
|
|
|
None => Err(Error::NotFound(name.to_owned())),
|
2019-04-26 03:30:46 +00:00
|
|
|
}
|
|
|
|
}
|