mindustry logic execution, map- and schematic- parsing and rendering
Diffstat (limited to 'src/data/schematic.rs')
| -rw-r--r-- | src/data/schematic.rs | 397 |
1 files changed, 95 insertions, 302 deletions
diff --git a/src/data/schematic.rs b/src/data/schematic.rs index 19026f2..75378ba 100644 --- a/src/data/schematic.rs +++ b/src/data/schematic.rs @@ -1,24 +1,22 @@ //! schematic parsing use std::collections::hash_map::Entry; use std::collections::HashMap; -use std::error::Error; use std::fmt::{self, Write}; use std::iter::FusedIterator; use std::slice::Iter; - -use image::RgbaImage; +use thiserror::Error; use crate::block::{self, Block, BlockRegistry, Rotation, State}; use crate::data::base64; use crate::data::dynamic::{self, DynData, DynSerializer}; use crate::data::{self, DataRead, DataWrite, GridPos, Serializer}; -use crate::item::storage::Storage as ItemStorage; +use crate::item::storage::ItemStorage; use crate::registry::RegistryEntry; /// biggest schematic -pub const MAX_DIMENSION: u16 = 128; +pub const MAX_DIMENSION: u16 = 256; /// most possible blocks -pub const MAX_BLOCKS: u32 = 128 * 128; +pub const MAX_BLOCKS: u32 = 256 * 256; /// a placement in a schematic pub struct Placement<'l> { @@ -47,7 +45,7 @@ impl<'l> Placement<'l> { } /// draws this placement in particular - pub fn image(&self) -> RgbaImage { + pub fn image(&self) -> crate::data::renderer::ImageHolder { self.block.image(self.get_state()) } @@ -602,12 +600,12 @@ impl<'l> Schematic<'l> { } } if left > 0 || top > 0 || right > 0 || bottom > 0 { - return Err(ResizeError::Truncated { + return Err(TruncatedError { right, top, left, bottom, - }); + })?; } self.width = w; self.height = h; @@ -664,25 +662,16 @@ impl<'l> Schematic<'l> { } /// error created by creating a new schematic -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Error)] pub enum NewError { + #[error("invalid schematic width ({0})")] Width(u16), + #[error("invalid schematic height ({0})")] Height(u16), } - -impl fmt::Display for NewError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Width(w) => write!(f, "invalid schematic width ({w})"), - Self::Height(h) => write!(f, "invalid schematic height ({h})"), - } - } -} - -impl Error for NewError {} - /// error created by doing stuff out of bounds -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Error)] +#[error("position {x} / {y} out of bounds {w} / {h}")] pub struct PosError { pub x: u16, pub y: u16, @@ -690,23 +679,9 @@ pub struct PosError { pub h: u16, } -impl fmt::Display for PosError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "position {x} / {y} out of bounds {w} / {h}", - x = self.x, - y = self.y, - w = self.w, - h = self.h - ) - } -} - -impl Error for PosError {} - -#[derive(Debug)] +#[derive(Debug, Error)] pub enum PlaceError { + #[error("invalid block placement {x} / {y} (size {sz}) within {w} / {h}")] Bounds { x: u16, y: u16, @@ -714,111 +689,63 @@ pub enum PlaceError { w: u16, h: u16, }, - Overlap { - x: u16, - y: u16, - }, - Deserialize(block::DeserializeError), -} - -impl From<block::DeserializeError> for PlaceError { - fn from(value: block::DeserializeError) -> Self { - PlaceError::Deserialize(value) - } + #[error("overlapping an existing block at {x} / {y}")] + Overlap { x: u16, y: u16 }, + #[error("block state deserialization failed")] + Deserialize(#[from] block::DeserializeError), } -impl fmt::Display for PlaceError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Bounds { x, y, sz, w, h } => write!( - f, - "invalid block placement {x} / {y} (size {sz}) within {w} / {h}" - ), - Self::Overlap { x, y } => write!(f, "overlapping an existing block at {x} / {y}"), - Self::Deserialize(..) => f.write_str("block state deserialization failed"), - } - } -} - -impl Error for PlaceError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - PlaceError::Deserialize(e) => Some(e), - _ => None, - } - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Debug, Error)] pub enum ResizeError { + #[error("invalid target width ({0})")] TargetWidth(u16), + #[error("invalid target height ({0})")] TargetHeight(u16), - XOffset { - dx: i16, - old_w: u16, - new_w: u16, - }, - YOffset { - dy: i16, - old_h: u16, - new_h: u16, - }, - Truncated { - right: u16, - top: u16, - left: u16, - bottom: u16, - }, + #[error("horizontal offset {dx} not in [-{new_w}, {old_w}]")] + XOffset { dx: i16, old_w: u16, new_w: u16 }, + #[error("vertical offset {dy} not in [-{new_h}, {old_h}]")] + YOffset { dy: i16, old_h: u16, new_h: u16 }, + #[error(transparent)] + Truncated(#[from] TruncatedError), +} + +#[derive(Error, Debug)] +pub struct TruncatedError { + right: u16, + top: u16, + left: u16, + bottom: u16, } -impl fmt::Display for ResizeError { +impl fmt::Display for TruncatedError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::TargetWidth(w) => write!(f, "invalid target width ({w})"), - Self::TargetHeight(w) => write!(f, "invalid target height ({w})"), - Self::XOffset { dx, old_w, new_w } => { - write!(f, "horizontal offset {dx} not in [-{new_w}, {old_w}]") - } - Self::YOffset { dy, old_h, new_h } => { - write!(f, "vertical offset {dy} not in [-{new_h}, {old_h}]") - } - Self::Truncated { - right, - top, - left, - bottom, - } => { - macro_rules! fmt_dir { - ($f:ident, $first:ident, $name:expr, $value:expr) => { - if $value != 0 { - if $first { - f.write_str(" (")?; - $first = false; - } else { - f.write_str(", ")?; - } - write!(f, "{}: {}", $name, $value)?; - } - }; + macro_rules! fmt_dir { + ($f:ident, $first:ident, $name:expr, $value:expr) => { + if $value != 0 { + if $first { + f.write_str(" (")?; + $first = false; + } else { + f.write_str(", ")?; + } + write!(f, "{}: {}", $name, $value)?; } + }; + } - f.write_str("truncated blocks")?; - let mut first = true; - fmt_dir!(f, first, "right", *right); - fmt_dir!(f, first, "top", *top); - fmt_dir!(f, first, "left", *left); - fmt_dir!(f, first, "bottom", *bottom); - if !first { - f.write_char(')')?; - } - Ok(()) - } + f.write_str("truncated blocks")?; + let mut first = true; + fmt_dir!(f, first, "right", self.right); + fmt_dir!(f, first, "top", self.top); + fmt_dir!(f, first, "left", self.left); + fmt_dir!(f, first, "bottom", self.bottom); + if !first { + f.write_char(')')?; } + Ok(()) } } -impl Error for ResizeError {} - impl fmt::Debug for Schematic<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, f) @@ -1011,8 +938,8 @@ impl<'l> Serializer<Schematic<'l>> for SchematicSerializer<'l> { if version > 1 { return Err(ReadError::Version(version)); } - let mut buff = buff.deflate()?; - let mut buff = DataRead::new(&mut buff); + let buff = buff.deflate()?; + let mut buff = DataRead::new(&buff); let w = buff.read_i16()?; let h = buff.read_i16()?; if w < 0 || h < 0 || w as u16 > MAX_DIMENSION || h as u16 > MAX_DIMENSION { @@ -1028,7 +955,7 @@ impl<'l> Serializer<Schematic<'l>> for SchematicSerializer<'l> { block_table.reserve(num_table as usize); for _ in 0..num_table { let name = buff.read_utf()?; - match self.0.get(&name) { + match self.0.get(name) { None => return Err(ReadError::NoSuchBlock(name.to_owned())), Some(b) => block_table.push(b), } @@ -1113,122 +1040,44 @@ impl<'l> Serializer<Schematic<'l>> for SchematicSerializer<'l> { } } -#[derive(Debug)] +#[derive(Debug, Error)] pub enum ReadError { - Read(data::ReadError), + #[error("failed to read from buffer")] + Read(#[from] data::ReadError), + #[error("incorrect header ({0:08X})")] Header(u32), + #[error("unsupported version ({0})")] Version(u8), + #[error("invalid schematic dimensions ({0} * {1})")] Dimensions(i16, i16), + #[error("invalid block table size ({0})")] TableSize(i8), + #[error("unknown block {0:?}")] NoSuchBlock(String), + #[error("invalid total block count ({0})")] BlockCount(i32), + #[error("invalid block index ({0} / {1})")] BlockIndex(i8, usize), - BlockConfig(block::DataConvertError), - ReadState(dynamic::ReadError), - Placement(PlaceError), -} - -impl From<data::ReadError> for ReadError { - fn from(value: data::ReadError) -> Self { - Self::Read(value) - } + #[error("block config conversion failed")] + BlockConfig(#[from] block::DataConvertError), + #[error("failed to read block data")] + ReadState(#[from] dynamic::ReadError), + #[error("deserialized block could not be placed")] + Placement(#[from] PlaceError), } -impl From<dynamic::ReadError> for ReadError { - fn from(value: dynamic::ReadError) -> Self { - Self::ReadState(value) - } -} - -impl From<block::DataConvertError> for ReadError { - fn from(value: block::DataConvertError) -> Self { - Self::BlockConfig(value) - } -} - -impl From<PlaceError> for ReadError { - fn from(value: PlaceError) -> Self { - Self::Placement(value) - } -} - -impl fmt::Display for ReadError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Read(..) => f.write_str("failed to read from buffer"), - Self::Header(hdr) => write!(f, "incorrect header ({hdr:08X})"), - Self::Version(ver) => write!(f, "unsupported version ({ver})"), - Self::Dimensions(w, h) => write!(f, "invalid schematic dimensions ({w} * {h})"), - Self::TableSize(cnt) => write!(f, "invalid block table size ({cnt})"), - Self::NoSuchBlock(name) => write!(f, "unknown block {name:?}"), - Self::BlockCount(cnt) => write!(f, "invalid total block count ({cnt})"), - Self::BlockIndex(idx, cnt) => write!(f, "invalid block index ({idx} / {cnt})"), - Self::BlockConfig(..) => f.write_str("block config conversion failed"), - Self::ReadState(..) => f.write_str("failed to read block data"), - Self::Placement(..) => f.write_str("deserialized block could not be placed"), - } - } -} - -impl Error for ReadError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::Read(e) => Some(e), - Self::BlockConfig(e) => Some(e), - Self::ReadState(e) => Some(e), - Self::Placement(e) => Some(e), - _ => None, - } - } -} - -#[derive(Debug)] +#[derive(Debug, Error)] pub enum WriteError { - Write(data::WriteError), + #[error("failed to write data to buffer")] + Write(#[from] data::WriteError), + #[error("tag list too long ({0})")] TagCount(usize), + #[error("block table too long ({0})")] TableSize(usize), - StateSerialize(block::SerializeError), - WriteState(dynamic::WriteError), -} - -impl From<data::WriteError> for WriteError { - fn from(value: data::WriteError) -> Self { - Self::Write(value) - } -} - -impl From<block::SerializeError> for WriteError { - fn from(value: block::SerializeError) -> Self { - Self::StateSerialize(value) - } -} - -impl From<dynamic::WriteError> for WriteError { - fn from(value: dynamic::WriteError) -> Self { - Self::WriteState(value) - } -} - -impl fmt::Display for WriteError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Write(..) => f.write_str("failed to write data to buffer"), - Self::TagCount(len) => write!(f, "tag list too long ({len})"), - Self::TableSize(len) => write!(f, "block table too long ({len})"), - Self::StateSerialize(e) => e.fmt(f), - Self::WriteState(..) => f.write_str("failed to write block data"), - } - } -} - -impl Error for WriteError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::Write(e) => Some(e), - Self::StateSerialize(e) => e.source(), - _ => None, - } - } + #[error(transparent)] + StateSerialize(#[from] block::SerializeError), + #[error("failed to write block data")] + WriteState(#[from] dynamic::WriteError), } impl<'l> SchematicSerializer<'l> { @@ -1265,76 +1114,20 @@ impl<'l> SchematicSerializer<'l> { } } -#[derive(Debug)] +#[derive(Debug, Error)] pub enum R64Error { - Base64(base64::DecodeError), - Content(ReadError), + #[error("base-64 decoding failed")] + Base64(#[from] base64::DecodeError), + #[error(transparent)] + Content(#[from] ReadError), } -impl From<base64::DecodeError> for R64Error { - fn from(value: base64::DecodeError) -> Self { - Self::Base64(value) - } -} - -impl From<ReadError> for R64Error { - fn from(value: ReadError) -> Self { - Self::Content(value) - } -} - -impl fmt::Display for R64Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Base64(..) => f.write_str("base-64 decoding failed"), - Self::Content(e) => e.fmt(f), - } - } -} - -impl Error for R64Error { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::Base64(e) => Some(e), - Self::Content(e) => e.source(), - } - } -} - -#[derive(Debug)] +#[derive(Debug, Error)] pub enum W64Error { - Base64(base64::EncodeError), - Content(WriteError), -} - -impl From<base64::EncodeError> for W64Error { - fn from(value: base64::EncodeError) -> Self { - Self::Base64(value) - } -} - -impl From<WriteError> for W64Error { - fn from(value: WriteError) -> Self { - Self::Content(value) - } -} - -impl fmt::Display for W64Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Base64(..) => f.write_str("base-64 encoding failed"), - Self::Content(e) => e.fmt(f), - } - } -} - -impl Error for W64Error { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::Base64(e) => Some(e), - Self::Content(e) => e.source(), - } - } + #[error("base-64 encoding failed")] + Base64(#[from] base64::EncodeError), + #[error(transparent)] + Content(#[from] WriteError), } pub struct PosIter { |