terra-wld 0.3.0

Terraria world parser
Documentation
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
}