From 7798f4c95f4b40b20d22e979de261ed07fb4abb8 Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Sat, 7 Aug 2021 18:07:31 +0100 Subject: [PATCH] nicer way of using mutable backing storages --- agb/examples/chicken.rs | 8 +-- agb/src/display/example_logo.rs | 9 +--- agb/src/display/tiled0.rs | 88 ++++++++++++++++++++++++++++----- 3 files changed, 79 insertions(+), 26 deletions(-) diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index 704a0460..86cbfd15 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -3,7 +3,7 @@ extern crate agb; use agb::{ - display::{object::ObjectStandard, HEIGHT, WIDTH}, + display::{object::ObjectStandard, tiled0::Map, HEIGHT, WIDTH}, input::Button, }; use core::convert::TryInto; @@ -52,11 +52,7 @@ pub fn main() -> ! { gfx.set_background_tilemap(0, &MAP_TILES); let mut background = gfx.get_background().unwrap(); - background.set_map(agb::display::tiled0::Map { - store: MAP_MAP.as_ref(), - dimensions: (32_u32, 32_u32).into(), - default: 0, - }); + background.set_map(Map::new(&MAP_MAP, (32_u32, 32_u32).into(), 0)); background.show(); background.commit(); diff --git a/agb/src/display/example_logo.rs b/agb/src/display/example_logo.rs index 85e64bf4..de8ef3d9 100644 --- a/agb/src/display/example_logo.rs +++ b/agb/src/display/example_logo.rs @@ -2,9 +2,8 @@ use crate::display::tiled0::Tiled0; crate::include_gfx!("gfx/agb_logo.toml"); -use super::tiled0::Map; - pub fn display_logo(gfx: &mut Tiled0) { + use super::tiled0::Map; gfx.set_background_palettes(agb_logo::test_logo.palettes); gfx.set_background_tilemap(0, agb_logo::test_logo.tiles); @@ -16,11 +15,7 @@ pub fn display_logo(gfx: &mut Tiled0) { entries[tile_id as usize] = tile_id | (palette_entry << 12); } - back.set_map(Map { - store: entries.as_ref(), - dimensions: (30_u32, 20_u32).into(), - default: 0, - }); + back.set_map(Map::new(&entries, (30_u32, 20_u32).into(), 0)); back.show(); } diff --git a/agb/src/display/tiled0.rs b/agb/src/display/tiled0.rs index 782a7693..1d9df4b8 100644 --- a/agb/src/display/tiled0.rs +++ b/agb/src/display/tiled0.rs @@ -30,30 +30,86 @@ pub enum BackgroundSize { S64x64 = 3, } -pub trait MapStorage: Deref {} -impl MapStorage for &[u16] {} -impl MapStorage for &mut [u16] {} +#[derive(PartialEq, Eq, Clone, Copy)] +enum Mutability { + Immutable, + Mutable, +} + +struct MapStorage<'a> { + s: *const [u16], + mutability: Mutability, + _phantom: core::marker::PhantomData<&'a ()>, +} + +impl<'a> Index for MapStorage<'a> { + type Output = u16; + fn index(&self, index: usize) -> &Self::Output { + &self.get()[index] + } +} + +impl<'a> MapStorage<'a> { + fn new(store: &[u16]) -> MapStorage { + MapStorage { + s: store as *const _, + mutability: Mutability::Immutable, + _phantom: core::marker::PhantomData, + } + } + fn new_mutable(store: &mut [u16]) -> MapStorage { + MapStorage { + s: store as *const _, + mutability: Mutability::Mutable, + _phantom: core::marker::PhantomData, + } + } + fn get(&self) -> &[u16] { + unsafe { &*self.s } + } + fn get_mut(&mut self) -> &mut [u16] { + assert!( + self.mutability == Mutability::Mutable, + "backing storage must be mutable in order to get internal storage mutably" + ); + unsafe { &mut *(self.s as *mut _) } + } +} /// The map background is the method of drawing game maps to the screen. It /// automatically handles copying the correct portion of a provided map to the /// assigned block depending on given coordinates. -pub struct Background { +pub struct Background<'a> { background: u8, block: u8, commited_position: Vector2D, shadowed_position: Vector2D, poisoned: bool, shadowed_register: u16, - map: Option>, + map: Option>, } -pub struct Map { - pub store: S, +pub struct Map<'a> { + store: MapStorage<'a>, pub dimensions: Vector2D, pub default: u16, } -impl<'a, S: MapStorage> Map { +impl<'a> Map<'a> { + pub fn new(map: &[u16], dimensions: Vector2D, default: u16) -> Map { + Map { + store: MapStorage::new(map), + dimensions, + default, + } + } + pub fn new_mutable(map: &mut [u16], dimensions: Vector2D, default: u16) -> Map { + Map { + store: MapStorage::new_mutable(map), + dimensions, + default, + } + } fn get_position(&self, x: i32, y: i32) -> u16 { if x < 0 || x as u32 >= self.dimensions.x { self.default @@ -63,10 +119,16 @@ impl<'a, S: MapStorage> Map { self.store[y as usize * self.dimensions.x as usize + x as usize] } } + pub fn get_store(&self) -> &[u16] { + self.store.get() + } + pub fn get_mutable_store(&mut self) -> &mut [u16] { + self.store.get_mut() + } } -impl<'a, S: MapStorage> Background { - unsafe fn new(background: u8, block: u8) -> Background { +impl<'a> Background<'a> { + unsafe fn new(background: u8, block: u8) -> Background<'a> { let mut b = Background { background, block, @@ -135,12 +197,12 @@ impl<'a, S: MapStorage> Background { self.shadowed_position = position; } - pub fn get_map(&mut self) -> Option<&mut Map> { + pub fn get_map(&mut self) -> Option<&mut Map<'a>> { self.poisoned = true; self.map.as_mut() } - pub fn set_map(&mut self, map: Map) { + pub fn set_map(&mut self, map: Map<'a>) { self.poisoned = true; self.map = Some(map); } @@ -266,7 +328,7 @@ impl Tiled0 { } /// Gets a map background if possible and assigns an unused block to it. - pub fn get_background(&mut self) -> Result, &'static str> { + pub fn get_background(&mut self) -> Result { if self.num_backgrounds >= 4 { return Err("too many backgrounds created, maximum is 4"); }