aboutsummaryrefslogtreecommitdiff
path: root/games/rstnode/rst-client/src/assets.rs
diff options
context:
space:
mode:
Diffstat (limited to 'games/rstnode/rst-client/src/assets.rs')
-rw-r--r--games/rstnode/rst-client/src/assets.rs138
1 files changed, 134 insertions, 4 deletions
diff --git a/games/rstnode/rst-client/src/assets.rs b/games/rstnode/rst-client/src/assets.rs
index 7e368b8d4c16..d1ccdaf875ed 100644
--- a/games/rstnode/rst-client/src/assets.rs
+++ b/games/rstnode/rst-client/src/assets.rs
@@ -1,5 +1,19 @@
+use crate::{error::LoadError, GameSettings};
+use cairo::{Context, Format, ImageSurface, Rectangle};
use ggez::graphics::Image;
-use std::{collections::BTreeMap, path::Path};
+use librsvg::{CairoRenderer, Loader};
+use std::{
+ collections::BTreeMap,
+ error::Error,
+ ffi::OsStr,
+ fs::{read_dir, File},
+ io::BufWriter,
+ io::Read,
+ path::{Path, PathBuf},
+};
+use tempfile::tempdir;
+
+pub type Result<T> = std::result::Result<T, LoadError>;
/// Construct a `node` prefixed URI
pub fn node(tt: &str) -> URI {
@@ -24,21 +38,137 @@ impl From<String> for URI {
}
/// Asset loader
+#[derive(Debug)]
pub struct Assets {
inner: BTreeMap<URI, Image>,
}
impl Assets {
- pub fn load(p: &Path) -> Self {
+ fn new() -> Self {
Self {
inner: Default::default(),
}
}
+
+ pub fn find<U: Into<URI>>(&self, u: U) -> Option<Image> {
+ self.inner.get(&u.into()).map(|i| i.clone())
+ }
+
+ /// Load an asset directory path
+ fn load_tree(&mut self, ctx: &mut ggez::Context, tmpdir: &Path, p: &Path) -> Result<()> {
+ let err: LoadError = p.to_str().unwrap().into();
+
+ read_dir(p)
+ .map_err(|_| err)?
+ .map(|e| {
+ let e = e.unwrap();
+ let p = e.path();
+
+ let ext = OsStr::new("svg");
+
+ if p.is_dir() {
+ debug!(
+ "Entering directory {}",
+ p.file_name().unwrap().to_str().unwrap()
+ );
+ self.load_tree(ctx, tmpdir, p.as_path())?;
+ } else if p.extension() == Some(ext) {
+ let png = load_svg(tmpdir, p.as_path())?;
+
+ let basepath = p.with_extension("");
+
+ let uri_cat = p.parent().unwrap().file_name().unwrap().to_str().unwrap();
+ let name = basepath.file_name().unwrap().to_str().unwrap();
+ let uri = format!("{}/{}", uri_cat, name);
+ let path_str = png.as_path().to_str().unwrap();
+
+ let mut content = vec![];
+ let mut f = File::open(png.as_path()).map_err(|_| {
+ LoadError::from(format!("No such file: {}", path_str).as_str())
+ })?;
+
+ f.read_to_end(&mut content).map_err(|e| {
+ LoadError::from(
+ format!("Read error for {}: {}", path_str, e.to_string()).as_str(),
+ )
+ })?;
+
+ self.inner.insert(
+ uri.into(),
+ Image::from_bytes(ctx, content.as_slice()).map_err(|e| {
+ LoadError::from(
+ format!("Read error for {}: {}", path_str, e.to_string()).as_str(),
+ )
+ })?,
+ );
+ }
+
+ Ok(())
+ })
+ .fold(Ok(()), |acc, res| match (acc, res) {
+ (Ok(_), Ok(_)) => Ok(()),
+ (Ok(_), Err(e)) => Err(e),
+ (Err(e), _) => Err(e),
+ })
+ }
}
+/// Load all game assets into the game
+///
+/// This function performs three main steps.
+///
+/// 1. Check that the provided path is a directory
+/// 2. Recursively load Directories and files and call
+/// [`load_svg`](self::load_svg) on each `.svg` file
+/// 3. Re-load newly converted assets into [`Assets`](self::Assets)
+pub fn load_tree(ctx: &mut ggez::Context, settings: &GameSettings) -> Result<Assets> {
+ let path = match settings.assets.clone() {
+ Some(s) => Ok(s),
+ None => Err(LoadError::from("No assets path set!")),
+ }?;
+
+ debug!(
+ "Starting assets loading harness on {}",
+ path.to_str().unwrap()
+ );
+
+ let tmpdir = tempdir().unwrap();
+ let mut assets = Assets::new();
+ assets.load_tree(ctx, tmpdir.path(), path.as_path())?;
+ info!("Asset loading complete!");
+ Ok(assets)
+}
/// A utility function to take an SVG and render it to a raster image
/// according to a render spec
-fn load_svg(p: &Path) -> () {
-
+pub fn load_svg(tmpdir: &Path, p: &Path) -> Result<PathBuf> {
+ let err: LoadError = p.to_str().unwrap().into();
+
+ let handle = Loader::new().read_path(p).map_err(|_| err.clone())?;
+ let renderer = CairoRenderer::new(&handle);
+
+ let surf = ImageSurface::create(Format::ARgb32, 256, 256).map_err(|_| err.clone())?;
+ let cr = Context::new(&surf);
+
+ renderer
+ .render_document(
+ &cr,
+ &Rectangle {
+ x: 0.0,
+ y: 0.0,
+ width: 256.0,
+ height: 256.0,
+ },
+ )
+ .map_err(|_| err.clone())?;
+
+ let png = p.with_extension("png");
+ let name = png
+ .file_name()
+ .map_or_else(|| Err(err.clone()), |name| Ok(name))?;
+
+ let out = tmpdir.join(name.clone());
+ let mut file = BufWriter::new(File::create(out.clone()).map_err(|_| err.clone())?);
+ surf.write_to_png(&mut file).map_err(|_| err.clone())?;
+ Ok(out.to_path_buf())
}