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,
|
2019-05-13 23:28:17 +00:00
|
|
|
io::BufReader,
|
2019-04-26 03:30:46 +00:00
|
|
|
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-17 09:22:32 +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-05-17 09:22:32 +00:00
|
|
|
/// Asset does not exist.
|
2019-04-29 00:23:14 +00:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2019-05-17 09:22:32 +00:00
|
|
|
/// Function used to load assets.
|
|
|
|
/// 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-05-17 09:22:32 +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.
|
2019-04-29 00:23:14 +00:00
|
|
|
/// 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-13 23:28:17 +00:00
|
|
|
let mut buf = Vec::new();
|
|
|
|
load_from_path(specifier)?.read_to_end(&mut buf)?;
|
|
|
|
Ok(image::load_from_memory(&buf).unwrap())
|
2019-04-26 03:30:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Asset for DotVoxData {
|
|
|
|
fn load(specifier: &str) -> Result<Self, Error> {
|
2019-05-13 23:28:17 +00:00
|
|
|
let mut buf = Vec::new();
|
|
|
|
load_from_path(specifier)?.read_to_end(&mut buf)?;
|
|
|
|
Ok(dot_vox::load_bytes(&buf).unwrap())
|
2019-04-26 03:30:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-17 09:22:32 +00:00
|
|
|
// TODO: System to load file from specifiers (e.g.: "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-05-17 09:22:32 +00:00
|
|
|
// TODO: Don't do this?
|
|
|
|
// If it's stupid but it works...
|
2019-04-29 00:23:14 +00:00
|
|
|
[
|
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
|
|
|
}
|
|
|
|
|
2019-05-13 23:28:17 +00:00
|
|
|
pub fn load_from_path(name: &str) -> Result<BufReader<File>, Error> {
|
2019-04-29 00:23:14 +00:00
|
|
|
match try_open_with_path(name) {
|
2019-05-13 23:28:17 +00:00
|
|
|
Some(mut f) => Ok(BufReader::new(f)),
|
2019-05-07 05:40:03 +00:00
|
|
|
None => Err(Error::NotFound(name.to_owned())),
|
2019-04-26 03:30:46 +00:00
|
|
|
}
|
|
|
|
}
|