1use std::io;
2
3use cs_datetime_parse::{OutOfRangeError, ParseError};
4use terra_types::TerrariaFileType;
5use thiserror::Error;
6
7use crate::{MAGIC_NUMBER, SUPPORTED_VERSIONS};
8
9pub type Result<T> = core::result::Result<T, Error>;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12#[non_exhaustive]
13pub enum OffsetType {
14 Header,
15 Tiles,
16 Chests,
17 Signs,
18 Entities,
19 TileEntites,
20 WeightedPressurePlates,
21 Homes,
22 Bestiary,
23 JourneyPowers,
24 Footer,
25}
26
27impl OffsetType {
28 pub const ALL: [OffsetType; 11] = [
29 OffsetType::Header,
30 OffsetType::Tiles,
31 OffsetType::Chests,
32 OffsetType::Signs,
33 OffsetType::Entities,
34 OffsetType::TileEntites,
35 OffsetType::WeightedPressurePlates,
36 OffsetType::Homes,
37 OffsetType::Bestiary,
38 OffsetType::JourneyPowers,
39 OffsetType::Footer,
40 ];
41
42 pub const fn name(self) -> &'static str {
43 match self {
44 Self::Header => "header",
45 Self::Tiles => "tiles",
46 Self::Chests => "chests",
47 Self::Signs => "signs",
48 Self::Entities => "entities",
49 Self::TileEntites => "tile entities",
50 Self::WeightedPressurePlates => "weighted pressure plates",
51 Self::Homes => "homes",
52 Self::Bestiary => "bestiary",
53 Self::JourneyPowers => "journey powers",
54 Self::Footer => "footer",
55 }
56 }
57}
58
59#[derive(Error, Debug)]
60pub enum Error {
61 #[error("io error")]
62 Io(#[from] io::Error),
63
64 #[error("invalid magic number expected b\"{}\", found b\"{}\"", format_u8_array(MAGIC_NUMBER), format_u8_array(.found))]
65 InvalidMagicNumber { found: Vec<u8> },
66
67 #[error("Creation time {0}")]
68 CreationTimeWrite(OutOfRangeError),
69
70 #[error("Last played {0}")]
71 LastPlayedWrite(OutOfRangeError),
72
73 #[error("Creation time {0}")]
74 CreationTimeRead(ParseError),
75
76 #[error("Last played{0}")]
77 LastPlayedRead(ParseError),
78
79 #[error("the version {0} is not supported, supported versions: {SUPPORTED_VERSIONS:?}")]
80 UnsupportedVersion(i32),
81
82 #[error("invalid file type expected {}, found {}", TerrariaFileType::World.name(), .0.name())]
83 InvalidFileType(TerrariaFileType),
84
85 #[error("invalid footer")]
86 InvalidFooter,
87
88 #[error("invalid {} offset expected 0x{expected:X} found 0x{found:X}", .offset_type.name())]
89 InvalidOffset {
90 offset_type: OffsetType,
91 expected: u64,
92 found: u64,
93 },
94
95 #[error("unknown tile entity({})", .id)]
96 UnknownTileEntity { id: u8 },
97
98 #[error("{0}")]
99 SerdeError(serde_json::Error),
100}
101
102fn format_u8_array(array: &[u8]) -> String {
103 let mut text = String::with_capacity(array.len());
104 for byte in array {
105 if !byte.is_ascii_graphic() {
106 text.push_str("\\x");
107 text.push_str(&byte.to_string());
108 continue;
109 }
110
111 let char = *byte as char;
112 if char == '\\' {
113 text.push_str("\\\\");
114 continue;
115 }
116
117 text.push(char);
118 }
119
120 text
121}