1use std::io::Write;
2
3use crate::{ImperatorError, ImperatorErrorKind};
4
5#[inline]
7fn take<const N: usize>(data: &[u8]) -> [u8; N] {
8 debug_assert!(data.len() >= N);
9 unsafe { *(data.as_ptr() as *const [u8; N]) }
10}
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum SaveHeaderKind {
15 Text,
17
18 Binary,
20
21 UnifiedText,
24
25 UnifiedBinary,
28
29 SplitText,
32
33 SplitBinary,
36
37 Other(u16),
39}
40
41impl SaveHeaderKind {
42 pub fn new(kind: u16) -> SaveHeaderKind {
43 match kind {
44 0 => SaveHeaderKind::Text,
45 1 => SaveHeaderKind::Binary,
46 2 => SaveHeaderKind::UnifiedText,
47 3 => SaveHeaderKind::UnifiedBinary,
48 4 => SaveHeaderKind::SplitText,
49 5 => SaveHeaderKind::SplitBinary,
50 x => SaveHeaderKind::Other(x),
51 }
52 }
53
54 pub fn value(&self) -> u16 {
55 match self {
56 SaveHeaderKind::Text => 0,
57 SaveHeaderKind::Binary => 1,
58 SaveHeaderKind::UnifiedText => 2,
59 SaveHeaderKind::UnifiedBinary => 3,
60 SaveHeaderKind::SplitText => 4,
61 SaveHeaderKind::SplitBinary => 5,
62 SaveHeaderKind::Other(x) => *x,
63 }
64 }
65
66 pub fn is_binary(&self) -> bool {
67 matches!(
68 self,
69 SaveHeaderKind::Binary | SaveHeaderKind::UnifiedBinary | SaveHeaderKind::SplitBinary
70 )
71 }
72
73 pub fn is_text(&self) -> bool {
74 matches!(
75 self,
76 SaveHeaderKind::Text | SaveHeaderKind::UnifiedText | SaveHeaderKind::SplitText
77 )
78 }
79}
80
81#[derive(Debug, Clone, PartialEq, Eq)]
83pub struct SaveHeader {
84 unknown: [u8; 2],
85 kind: SaveHeaderKind,
86 random: [u8; 8],
87 meta_len: u64,
88 header_len: usize,
89}
90
91impl SaveHeader {
92 pub fn from_slice(data: &[u8]) -> Result<Self, ImperatorError> {
93 if data.len() < 24 {
94 return Err(ImperatorErrorKind::InvalidHeader.into());
95 }
96
97 if !matches!(&data[..3], [b'S', b'A', b'V']) {
98 return Err(ImperatorErrorKind::InvalidHeader.into());
99 }
100
101 let unknown = take::<2>(&data[3..5]);
102 let kind_hex =
103 std::str::from_utf8(&data[5..7]).map_err(|_| ImperatorErrorKind::InvalidHeader)?;
104 let kind =
105 u16::from_str_radix(kind_hex, 16).map_err(|_| ImperatorErrorKind::InvalidHeader)?;
106 let random = take::<8>(&data[7..15]);
107
108 let meta_hex =
109 std::str::from_utf8(&data[15..23]).map_err(|_| ImperatorErrorKind::InvalidHeader)?;
110 let meta_len =
111 u64::from_str_radix(meta_hex, 16).map_err(|_| ImperatorErrorKind::InvalidHeader)?;
112
113 let header_len = if data[23] == b'\r' && data.get(24) == Some(&b'\n') {
114 25
115 } else if data[23] == b'\n' {
116 24
117 } else {
118 return Err(ImperatorErrorKind::InvalidHeader.into());
119 };
120
121 Ok(SaveHeader {
122 unknown,
123 kind: SaveHeaderKind::new(kind),
124 random,
125 meta_len,
126 header_len,
127 })
128 }
129
130 pub fn kind(&self) -> SaveHeaderKind {
131 self.kind
132 }
133
134 pub fn set_kind(&mut self, kind: SaveHeaderKind) {
135 self.kind = kind;
136 }
137
138 pub fn header_len(&self) -> usize {
139 self.header_len
140 }
141
142 pub fn metadata_len(&self) -> u64 {
143 self.meta_len
144 }
145
146 pub fn set_metadata_len(&mut self, len: u64) {
147 self.meta_len = len
148 }
149
150 pub fn write<W>(&self, mut writer: W) -> std::io::Result<()>
151 where
152 W: Write,
153 {
154 writer.write_all(b"SAV")?;
155 writer.write_all(&self.unknown)?;
156 write!(writer, "{0:02x}", self.kind.value())?;
157 writer.write_all(&self.random)?;
158 write!(writer, "{0:08x}", self.meta_len)?;
159 if self.header_len() == 25 {
160 writer.write_all(b"\r")?;
161 }
162 writer.write_all(b"\n")?;
163 Ok(())
164 }
165}