From 0cbd5366fd70dc8032ad3c3642d4e1d7a244acad Mon Sep 17 00:00:00 2001 From: gered Date: Tue, 3 Sep 2024 00:00:07 -0400 Subject: [PATCH] add app_root_dir helper function. integrate with all examples --- examples/audio_playback/src/main.rs | 10 +++---- examples/balls/src/main.rs | 5 ++-- examples/balls_v2/src/states.rs | 3 +- examples/imgui_integration/src/context.rs | 20 +++++++------ examples/slimed/src/main.rs | 35 +++++++++++++---------- examples/slimed/src/states.rs | 18 +++++++++--- ggdt/src/system/mod.rs | 9 ++++++ ggdt/src/utils/io.rs | 30 ++++++++++++++++++- 8 files changed, 92 insertions(+), 38 deletions(-) diff --git a/examples/audio_playback/src/main.rs b/examples/audio_playback/src/main.rs index c41612a..5e51c35 100644 --- a/examples/audio_playback/src/main.rs +++ b/examples/audio_playback/src/main.rs @@ -58,11 +58,11 @@ fn main() -> Result<()> { let mut volume = 1.0; let sounds = [ - load_and_convert_wav(Path::new("./assets/pickup-coin.wav"), system.res.audio.spec())?, - load_and_convert_wav(Path::new("./assets/powerup.wav"), system.res.audio.spec())?, - load_and_convert_wav(Path::new("./assets/explosion.wav"), system.res.audio.spec())?, - load_and_convert_wav(Path::new("./assets/jump.wav"), system.res.audio.spec())?, - load_and_convert_wav(Path::new("./assets/laser-shoot.wav"), system.res.audio.spec())?, + load_and_convert_wav(system.app_root_dir.join("./assets/pickup-coin.wav").as_path(), system.res.audio.spec())?, + load_and_convert_wav(system.app_root_dir.join("./assets/powerup.wav").as_path(), system.res.audio.spec())?, + load_and_convert_wav(system.app_root_dir.join("./assets/explosion.wav").as_path(), system.res.audio.spec())?, + load_and_convert_wav(system.app_root_dir.join("./assets/jump.wav").as_path(), system.res.audio.spec())?, + load_and_convert_wav(system.app_root_dir.join("./assets/laser-shoot.wav").as_path(), system.res.audio.spec())?, ]; let mut statuses = [AudioChannelStatus { size: 0, position: 0, playing: false }; NUM_CHANNELS]; diff --git a/examples/balls/src/main.rs b/examples/balls/src/main.rs index 97a256f..45e6025 100644 --- a/examples/balls/src/main.rs +++ b/examples/balls/src/main.rs @@ -1,5 +1,3 @@ -use std::path::Path; - use anyhow::Result; use ggdt::prelude::*; @@ -26,7 +24,8 @@ fn main() -> Result<()> { let font = BitmaskFont::new_vga_font()?; - let (balls_bmp, balls_palette) = IndexedBitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?; + let (balls_bmp, balls_palette) = + IndexedBitmap::load_pcx_file(system.app_root_dir.join("./assets/balls.pcx").as_path())?; system.res.palette = balls_palette.clone(); let mut sprites = Vec::::new(); diff --git a/examples/balls_v2/src/states.rs b/examples/balls_v2/src/states.rs index 5d1487d..85b2f0c 100644 --- a/examples/balls_v2/src/states.rs +++ b/examples/balls_v2/src/states.rs @@ -25,7 +25,8 @@ impl Game { pub fn new(mut system: System) -> Result { let font = BitmaskFont::new_vga_font()?; - let (balls_bmp, balls_palette) = IndexedBitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?; + let (balls_bmp, balls_palette) = + IndexedBitmap::load_pcx_file(system.app_root_dir.join("./assets/balls.pcx").as_path())?; system.res.palette = balls_palette.clone(); let mut sprites = Vec::new(); diff --git a/examples/imgui_integration/src/context.rs b/examples/imgui_integration/src/context.rs index b662cb3..fec691c 100644 --- a/examples/imgui_integration/src/context.rs +++ b/examples/imgui_integration/src/context.rs @@ -1,5 +1,4 @@ use std::collections::HashMap; -use std::path::Path; use std::rc::Rc; use anyhow::Result; @@ -91,17 +90,20 @@ impl AppContext for GameContext { impl GameContext { pub fn new(system: System) -> Result { - let palette = load_palette(Path::new("./assets/db16.pal"))?; + let palette = load_palette(system.app_root_dir.join("./assets/db16.pal").as_path())?; - let font = load_font(Path::new("./assets/dp.fnt"))?; - let small_font = load_font(Path::new("./assets/small.fnt"))?; + let font = load_font(system.app_root_dir.join("./assets/dp.fnt").as_path())?; + let small_font = load_font(system.app_root_dir.join("./assets/small.fnt").as_path())?; - let tiles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/tiles.pcx"))?); - let green_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/green_slime.pcx"))?); - let blue_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/blue_slime.pcx"))?); - let orange_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/orange_slime.pcx"))?); + let tiles = Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/tiles.pcx").as_path())?); + let green_slime = + Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/green_slime.pcx").as_path())?); + let blue_slime = + Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/blue_slime.pcx").as_path())?); + let orange_slime = + Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/orange_slime.pcx").as_path())?); - let tilemap = TileMap::load_from(Path::new("./assets/arena.map.json"))?; + let tilemap = TileMap::load_from(system.app_root_dir.join("./assets/arena.map.json").as_path())?; let entities = Entities::new(); let component_systems = ComponentSystems::new(); diff --git a/examples/slimed/src/main.rs b/examples/slimed/src/main.rs index 80a9e3a..d5f888b 100644 --- a/examples/slimed/src/main.rs +++ b/examples/slimed/src/main.rs @@ -1,7 +1,6 @@ extern crate core; use std::collections::HashMap; -use std::path::Path; use std::rc::Rc; use anyhow::{Context, Result}; @@ -108,30 +107,36 @@ impl AppContext for Game { impl Game { pub fn new(mut system: System) -> Result { - let palette = load_palette(Path::new("./assets/db16.pal"))?; + let palette = load_palette(system.app_root_dir.join("./assets/db16.pal").as_path())?; system.res.palette = palette.clone(); - let font = load_font(Path::new("./assets/dp.fnt"))?; + let font = load_font(system.app_root_dir.join("./assets/dp.fnt").as_path())?; - let tiles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/tiles.pcx"))?); - let hero_male = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/hero_male.pcx"))?); - let hero_female = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/hero_female.pcx"))?); - let green_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/green_slime.pcx"))?); - let blue_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/blue_slime.pcx"))?); - let orange_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/orange_slime.pcx"))?); - let fist = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/fist.pcx"))?); - let sword = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/sword.pcx"))?); - let particles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/particles.pcx"))?); - let items = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/items.pcx"))?); + let tiles = Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/tiles.pcx").as_path())?); + let hero_male = + Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/hero_male.pcx").as_path())?); + let hero_female = + Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/hero_female.pcx").as_path())?); + let green_slime = + Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/green_slime.pcx").as_path())?); + let blue_slime = + Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/blue_slime.pcx").as_path())?); + let orange_slime = + Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/orange_slime.pcx").as_path())?); + let fist = Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/fist.pcx").as_path())?); + let sword = Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/sword.pcx").as_path())?); + let particles = + Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/particles.pcx").as_path())?); + let items = Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/items.pcx").as_path())?); - let mut ui = load_bitmap_atlas(Path::new("./assets/ui.pcx"))?; + let mut ui = load_bitmap_atlas(system.app_root_dir.join("./assets/ui.pcx").as_path())?; ui.add(Rect::new(0, 0, 16, 16))?; ui.add(Rect::new(16, 0, 16, 16))?; for i in 0..8 { ui.add(Rect::new(i * 8, 16, 8, 8))?; } - let tilemap = TileMap::load_from(Path::new("./assets/title_screen.map.json"))?; + let tilemap = TileMap::load_from(system.app_root_dir.join("./assets/title_screen.map.json").as_path())?; let entities = Entities::new(); let component_systems = ComponentSystems::new(); diff --git a/examples/slimed/src/states.rs b/examples/slimed/src/states.rs index 453b6ef..03d07e5 100644 --- a/examples/slimed/src/states.rs +++ b/examples/slimed/src/states.rs @@ -1,5 +1,3 @@ -use std::path::Path; - use ggdt::prelude::*; use crate::entities::*; @@ -88,7 +86,13 @@ impl AppState for MainMenuState { fn state_change(&mut self, new_state: State, old_state: State, context: &mut Game) { match new_state { State::Pending | State::Resume => { - init_everything(context, Path::new("./assets/title_screen.map.json"), 0.2, 1.0, 32); + init_everything( + context, + context.core.system.app_root_dir.join("./assets/title_screen.map.json").as_path(), + 0.2, + 1.0, + 32, + ); } State::TransitionIn => { self.fade = 0.0; @@ -219,7 +223,13 @@ impl AppState for GamePlayState { fn state_change(&mut self, new_state: State, old_state: State, context: &mut Game) { match new_state { State::Pending => { - init_everything(context, Path::new("./assets/arena.map.json"), 0.5, 2.0, 100); + init_everything( + context, + context.core.system.app_root_dir.join("./assets/arena.map.json").as_path(), + 0.5, + 2.0, + 100, + ); spawn_player_randomly(&mut context.core); } State::TransitionIn => { diff --git a/ggdt/src/system/mod.rs b/ggdt/src/system/mod.rs index 07471ff..8252efe 100644 --- a/ggdt/src/system/mod.rs +++ b/ggdt/src/system/mod.rs @@ -1,6 +1,7 @@ use thiserror::Error; use crate::audio::AudioError; +use crate::utils::app_root_dir; mod event; mod framebuffer; @@ -51,6 +52,9 @@ pub enum SystemError { #[error("SystemResources error: {0}")] SystemResourcesError(#[from] SystemResourcesError), + + #[error("System I/O error")] + IOError(#[from] std::io::Error), } /// Builder for configuring and constructing an instance of [`System`]. @@ -195,6 +199,8 @@ impl SystemBuilder { let event_pump = SystemEventPump::from(sdl_event_pump); + let app_root_dir = app_root_dir()?; + Ok(System { sdl_context, sdl_audio_subsystem, @@ -202,6 +208,7 @@ impl SystemBuilder { sdl_timer_subsystem, res: system_resources, event_pump, + app_root_dir, vsync: self.vsync, target_framerate: self.target_framerate, target_framerate_delta: None, @@ -231,6 +238,8 @@ where pub res: SystemResType, pub event_pump: SystemEventPump, + + pub app_root_dir: std::path::PathBuf, } impl std::fmt::Debug for System diff --git a/ggdt/src/utils/io.rs b/ggdt/src/utils/io.rs index a4915fe..d23cf92 100644 --- a/ggdt/src/utils/io.rs +++ b/ggdt/src/utils/io.rs @@ -1,5 +1,8 @@ use byteorder::{ReadBytesExt, WriteBytesExt}; -use std::io::{Error, SeekFrom}; +use std::{ + io::{Error, SeekFrom}, + path::PathBuf, +}; /// Provides a convenience method for determining the total size of a stream. This is provided /// as a temporary alternative to [std::io::Seek::stream_len] which is currently marked unstable. @@ -34,3 +37,28 @@ pub trait WriteType { fn write(&self, writer: &mut T) -> Result<(), Self::ErrorType>; } + +/// Returns the application root directory (the directory that the application executable is +/// located in). +/// +/// First tries to automatically detect this from the `CARGO_MANIFEST_DIR` environment variable, +/// if present, to catch scenarios where the application is running from a Cargo workspace as a +/// sub-project/binary within that workspace. In such a case, the application root directory is +/// the same as that sub-project/binary's `Cargo.toml`. +/// +/// If `CARGO_MANIFEST_DIR` is not present, then an attempt is made to determine the application +/// root directory from the running executable's location. +/// +/// If this fails for some reason, then this returns the current working directory. +pub fn app_root_dir() -> Result { + if let Some(manifest_path) = std::env::var_os("CARGO_MANIFEST_DIR") { + return Ok(PathBuf::from(manifest_path)); + } + + let mut exe_path = std::env::current_exe()?.canonicalize()?; + if exe_path.pop() { + return Ok(exe_path); + } + + std::env::current_dir() +}