use std::io;
use cs_datetime_parse::{OutOfRangeError, ParseError};
use terra_types::TerrariaFileType;
use thiserror::Error;
use crate::{MAGIC_NUMBER, SUPPORTED_VERSIONS};
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum OffsetType {
Header,
Tiles,
Chests,
Signs,
Entities,
TileEntites,
WeightedPressurePlates,
Homes,
Bestiary,
JourneyPowers,
Footer,
}
impl OffsetType {
pub const ALL: [OffsetType; 11] = [
OffsetType::Header,
OffsetType::Tiles,
OffsetType::Chests,
OffsetType::Signs,
OffsetType::Entities,
OffsetType::TileEntites,
OffsetType::WeightedPressurePlates,
OffsetType::Homes,
OffsetType::Bestiary,
OffsetType::JourneyPowers,
OffsetType::Footer,
];
pub const fn name(self) -> &'static str {
match self {
Self::Header => "header",
Self::Tiles => "tiles",
Self::Chests => "chests",
Self::Signs => "signs",
Self::Entities => "entities",
Self::TileEntites => "tile entities",
Self::WeightedPressurePlates => "weighted pressure plates",
Self::Homes => "homes",
Self::Bestiary => "bestiary",
Self::JourneyPowers => "journey powers",
Self::Footer => "footer",
}
}
}
#[derive(Error, Debug)]
pub enum Error {
#[error("io error")]
Io(#[from] io::Error),
#[error("invalid magic number expected b\"{}\", found b\"{}\"", format_u8_array(MAGIC_NUMBER), format_u8_array(.found))]
InvalidMagicNumber { found: Vec<u8> },
#[error("Creation time {0}")]
CreationTimeWrite(OutOfRangeError),
#[error("Last played {0}")]
LastPlayedWrite(OutOfRangeError),
#[error("Creation time {0}")]
CreationTimeRead(ParseError),
#[error("Last played{0}")]
LastPlayedRead(ParseError),
#[error("the version {0} is not supported, supported versions: {SUPPORTED_VERSIONS:?}")]
UnsupportedVersion(i32),
#[error("invalid file type expected {}, found {}", TerrariaFileType::World.name(), .0.name())]
InvalidFileType(TerrariaFileType),
#[error("invalid footer")]
InvalidFooter,
#[error("invalid {} offset expected 0x{expected:X} found 0x{found:X}", .offset_type.name())]
InvalidOffset {
offset_type: OffsetType,
expected: u64,
found: u64,
},
#[error("unknown tile entity({})", .id)]
UnknownTileEntity { id: u8 },
#[error("{0}")]
SerdeError(serde_json::Error),
}
fn format_u8_array(array: &[u8]) -> String {
let mut text = String::with_capacity(array.len());
for byte in array {
if !byte.is_ascii_graphic() {
text.push_str("\\x");
text.push_str(&byte.to_string());
continue;
}
let char = *byte as char;
if char == '\\' {
text.push_str("\\\\");
continue;
}
text.push(char);
}
text
}