1use std::{
15 array::TryFromSliceError,
16 io::{Read, Write},
17};
18
19use chrono::{DateTime, Duration, NaiveDateTime, TimeDelta};
20use sha1::{Digest, Sha1};
21
22use crate::{
23 crc::crc32,
24 footer::{FooterType, ctgp_footer::CTGPFooter, sp_footer::SPFooter},
25 header::{Header, mii::Mii},
26 input_data::InputData,
27};
28
29pub mod byte_handler;
30pub mod crc;
31pub mod footer;
32pub mod header;
33pub mod input_data;
34
35#[cfg(test)]
36mod tests;
37
38#[derive(thiserror::Error, Debug)]
40pub enum GhostError {
41 #[error("Data length too short for a ghost")]
43 DataLengthTooShort,
44 #[error("Header Error: {0}")]
46 HeaderError(#[from] header::HeaderError),
47 #[error("Mii Error: {0}")]
49 MiiError(#[from] header::mii::MiiError),
50 #[error("Input Data Error: {0}")]
52 InputDataError(#[from] input_data::InputDataError),
53 #[error("CTGP Footer Error: {0}")]
55 CTGPFooterError(#[from] footer::ctgp_footer::CTGPFooterError),
56 #[error("ByteHandler Error: {0}")]
58 ByteHandlerError(#[from] byte_handler::ByteHandlerError),
59 #[error("Try From Slice Error: {0}")]
61 TryFromSliceError(#[from] TryFromSliceError),
62 #[error("IO Error: {0}")]
64 IOError(#[from] std::io::Error),
65}
66
67pub struct Ghost {
75 raw_data: Vec<u8>,
77 header: Header,
79 input_data: InputData,
81 base_crc32: u32,
83 footer: Option<FooterType>,
85 file_crc32: u32,
87 should_preserve_external_footer: bool,
89}
90
91impl Ghost {
92 pub fn new_from_file<T: AsRef<std::path::Path>>(path: T) -> Result<Self, GhostError> {
99 let mut buf = Vec::with_capacity(0x100);
100 std::fs::File::open(path)?.read_to_end(&mut buf)?;
101 Self::new(&buf)
102 }
103
104 pub fn new(bytes: &[u8]) -> Result<Self, GhostError> {
116 if bytes.len() < 0x8E {
117 return Err(GhostError::DataLengthTooShort);
118 }
119
120 let header = Header::new(&bytes[..0x88])?;
121
122 let file_crc32 = u32::from_be_bytes(bytes[bytes.len() - 0x04..].try_into()?);
123 let mut base_crc32 = file_crc32;
124
125 let footer = if let Ok(ctgp_footer) = CTGPFooter::new(bytes) {
126 let input_data_end_offset = bytes.len() - ctgp_footer.len() - 0x08;
127 base_crc32 = u32::from_be_bytes(
128 bytes[input_data_end_offset..input_data_end_offset + 0x04].try_into()?,
129 );
130 Some(FooterType::CTGPFooter(ctgp_footer))
131 } else if let Ok(sp_footer) = SPFooter::new(bytes) {
132 let input_data_end_offset = bytes.len() - sp_footer.len() - 0x08;
133 base_crc32 = u32::from_be_bytes(
134 bytes[input_data_end_offset..input_data_end_offset + 0x04].try_into()?,
135 );
136 Some(FooterType::SPFooter(sp_footer))
137 } else {
138 None
139 };
140
141 let input_data_len = if bytes[0x8C..0x90] == *b"Yaz1" {
142 u32::from_be_bytes(bytes[0x88..0x8C].try_into().unwrap()) as usize + 0x04
143 } else {
144 header.decompressed_input_data_length() as usize
145 };
146
147 let input_data = InputData::new(&bytes[0x88..0x88 + input_data_len])?;
148
149 Ok(Self {
150 raw_data: bytes.to_vec(),
151 header,
152 input_data,
153 base_crc32,
154 footer,
155 file_crc32,
156 should_preserve_external_footer: true,
157 })
158 }
159
160 pub fn update_raw_data(&mut self) -> Result<(), GhostError> {
173 let mii_bytes = self.header().mii().raw_data().to_vec();
174 self.header_mut().set_mii(Mii::new(mii_bytes)?);
175 self.header_mut().fix_mii_crc16();
176
177 let mut buf = Vec::from(self.header().raw_data());
178
179 buf.extend_from_slice(self.input_data().raw_data());
180
181 let header_len = 0x88;
182 let new_input_data_end = header_len + self.input_data().raw_data().len();
183
184 let old_input_data_end = if self.raw_data[0x8C..0x90] == *b"Yaz1" {
186 u32::from_be_bytes(self.raw_data[0x88..0x8C].try_into().unwrap()) as usize
187 } else {
188 header_len + 0x2774
189 };
190
191 if new_input_data_end > old_input_data_end {
192 let diff = new_input_data_end - old_input_data_end;
193 let insert_pos = old_input_data_end;
194 self.raw_data
195 .splice(insert_pos..insert_pos, vec![0u8; diff]);
196 } else if new_input_data_end < old_input_data_end {
197 let diff = old_input_data_end - new_input_data_end;
198 let remove_end = old_input_data_end;
199 self.raw_data.drain(remove_end - diff..remove_end);
200 }
201
202 self.raw_data[..new_input_data_end].copy_from_slice(&buf[..new_input_data_end]);
203 let base_crc32 = crc32(&buf);
204
205 match (self.footer(), self.should_preserve_external_footer()) {
206 (Some(FooterType::CTGPFooter(ctgp_footer)), true) => {
207 buf.extend_from_slice(&base_crc32.to_be_bytes());
208 buf.extend_from_slice(ctgp_footer.raw_data());
209
210 let footer_len = ctgp_footer.len();
211 self.raw_data.drain(new_input_data_end..);
212 self.raw_data
213 .extend_from_slice(&buf[buf.len() - footer_len - 0x04..]);
214 self.raw_data.extend_from_slice(&[0u8; 4]);
215 }
216 (Some(FooterType::SPFooter(sp_footer)), true) => {
217 buf.extend_from_slice(&base_crc32.to_be_bytes());
218 buf.extend_from_slice(sp_footer.raw_data());
219
220 let footer_len = sp_footer.len();
221 self.raw_data.drain(new_input_data_end..);
222 self.raw_data
223 .extend_from_slice(&buf[buf.len() - footer_len - 0x04..]);
224 self.raw_data.extend_from_slice(&[0u8; 4]);
225 }
226 (_, true) if self.raw_data.len() >= new_input_data_end + 0x08 => {
227 self.raw_data.drain(new_input_data_end + 0x04..);
228 }
229 (_, false) if self.raw_data.len() >= new_input_data_end + 0x08 => self.raw_data
230 [new_input_data_end..new_input_data_end + 0x04]
231 .copy_from_slice(&base_crc32.to_be_bytes()),
232 _ => (),
233 }
234
235 let len = self.raw_data.len();
236 let crc32 = crc32(&self.raw_data[..len - 0x04]);
237 self.raw_data[len - 0x04..].copy_from_slice(&crc32.to_be_bytes());
238
239 let sha1 = compute_sha1_hex(&self.raw_data);
240 if let Some(FooterType::CTGPFooter(ctgp_footer)) = self.footer_mut() {
241 ctgp_footer.set_ghost_sha1(&sha1)?;
242 }
243
244 Ok(())
245 }
246
247 pub fn save_to_file<T: AsRef<std::path::Path>>(&mut self, path: T) -> Result<(), GhostError> {
254 self.update_raw_data()?;
255 let mut file = std::fs::File::create(path)?;
256 file.write_all(&self.raw_data)?;
257
258 Ok(())
259 }
260
261 pub fn compress_input_data(&mut self) {
265 if self.input_data().is_compressed() {
266 return;
267 }
268
269 self.input_data_mut().compress();
270 self.header_mut().set_compressed(true);
271 }
272
273 pub fn decompress_input_data(&mut self) {
277 if !self.input_data().is_compressed() {
278 return;
279 }
280
281 self.input_data_mut().decompress();
282 self.header_mut().set_compressed(false);
283 }
284
285 pub fn raw_data(&self) -> &[u8] {
289 &self.raw_data
290 }
291
292 pub fn raw_data_mut(&mut self) -> &mut [u8] {
294 &mut self.raw_data
295 }
296
297 pub fn header(&self) -> &Header {
299 &self.header
300 }
301
302 pub fn header_mut(&mut self) -> &mut Header {
304 &mut self.header
305 }
306
307 pub fn input_data(&self) -> &InputData {
309 &self.input_data
310 }
311
312 pub fn input_data_mut(&mut self) -> &mut InputData {
314 &mut self.input_data
315 }
316
317 pub fn footer(&self) -> Option<&FooterType> {
319 self.footer.as_ref()
320 }
321
322 pub fn footer_mut(&mut self) -> Option<&mut FooterType> {
324 self.footer.as_mut()
325 }
326
327 pub fn base_crc32(&self) -> u32 {
329 self.base_crc32
330 }
331
332 pub fn verify_base_crc32(&self) -> bool {
335 let mut data = Vec::from(self.header().raw_data());
336 data.extend_from_slice(self.input_data().raw_data());
337 self.base_crc32 == crc32(&data)
338 }
339
340 pub fn file_crc32(&self) -> u32 {
342 self.file_crc32
343 }
344
345 pub fn verify_file_crc32(&self) -> bool {
348 let len = self.raw_data().len();
349 self.file_crc32 == crc32(&self.raw_data()[..len - 0x04])
350 }
351
352 pub fn should_preserve_external_footer(&self) -> bool {
354 self.should_preserve_external_footer
355 }
356
357 pub fn set_should_preserve_external_footer(&mut self, b: bool) {
359 self.should_preserve_external_footer = b;
360 }
361}
362
363pub(crate) fn write_bits(
365 buf: &mut [u8],
366 byte_offset: usize,
367 bit_offset: usize,
368 bit_width: usize,
369 value: u64,
370) {
371 let bytes_needed = (bit_offset + bit_width).div_ceil(8);
372 let mut chunk: u64 = 0;
373
374 for i in 0..bytes_needed {
375 chunk = (chunk << 8) | buf[byte_offset + i] as u64;
376 }
377
378 let shift = bytes_needed * 8 - bit_offset - bit_width;
379 let mask = ((1u64 << bit_width) - 1) << shift;
380
381 chunk = (chunk & !mask) | ((value << shift) & mask);
382
383 for i in (0..bytes_needed).rev() {
384 buf[byte_offset + i] = (chunk & 0xFF) as u8;
385 chunk >>= 8;
386 }
387}
388
389pub(crate) fn compute_sha1_hex(input: &[u8]) -> [u8; 0x14] {
391 let mut hasher = Sha1::new();
392 hasher.update(input);
393 hasher.finalize().into()
394}
395
396pub(crate) fn datetime_from_timestamp(tick_count: u64) -> NaiveDateTime {
400 let clock_rate = 60_750_000.0; let epoch_shift = 946_684_800; let total_seconds = tick_count as f64 / clock_rate;
403 let total_nanoseconds = (total_seconds * 1_000_000_000.0) as i64;
404
405 let duration = Duration::nanoseconds(total_nanoseconds);
406 let epoch = DateTime::from_timestamp(epoch_shift, 0).unwrap();
407
408 epoch.naive_utc() + duration
409}
410
411pub(crate) fn duration_from_ticks(tick_count: u64) -> TimeDelta {
415 let clock_rate = 60_750_000.0; let total_seconds = tick_count as f64 / clock_rate;
417 let total_milliseconds = (total_seconds * 1_000.0) as i64;
418
419 Duration::milliseconds(total_milliseconds)
420}