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")]
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> {
117 if bytes.len() < 0x90 {
118 return Err(GhostError::DataLengthTooShort);
119 }
120
121 let header = Header::new(&bytes[..0x88])?;
122
123 let file_crc32 = u32::from_be_bytes(bytes[bytes.len() - 0x04..].try_into()?);
124 let mut base_crc32 = file_crc32;
125
126 let footer = if let Ok(ctgp_footer) = CTGPFooter::new(bytes) {
127 let input_data_end_offset = bytes.len() - ctgp_footer.len() - 0x08;
128 base_crc32 = u32::from_be_bytes(
129 bytes[input_data_end_offset..input_data_end_offset + 0x04].try_into()?,
130 );
131 Some(FooterType::CTGPFooter(ctgp_footer))
132 } else if let Ok(sp_footer) = SPFooter::new(bytes) {
133 let input_data_end_offset = bytes.len() - sp_footer.len() - 0x08;
134 base_crc32 = u32::from_be_bytes(
135 bytes[input_data_end_offset..input_data_end_offset + 0x04].try_into()?,
136 );
137 Some(FooterType::SPFooter(sp_footer))
138 } else {
139 None
140 };
141
142 let input_data_len = if bytes[0x8C..0x90] == *b"Yaz1" {
143 u32::from_be_bytes(bytes[0x88..0x8C].try_into().unwrap()) as usize + 0x04
144 } else {
145 header.decompressed_input_data_length() as usize
146 };
147
148 if bytes.len() < 0x88 + input_data_len + 0x04 {
149 return Err(GhostError::DataLengthTooShort);
150 }
151
152 let input_data = InputData::new(&bytes[0x88..0x88 + input_data_len])?;
153
154 Ok(Self {
155 raw_data: bytes.to_vec(),
156 header,
157 input_data,
158 base_crc32,
159 footer,
160 file_crc32,
161 should_preserve_external_footer: true,
162 })
163 }
164
165 pub fn update_raw_data(&mut self) -> Result<(), GhostError> {
178 let mii_bytes = self.header().mii().raw_data().to_vec();
179 self.header_mut().set_mii(Mii::new(mii_bytes)?);
180 self.header_mut().fix_mii_crc16();
181
182 let mut buf = Vec::from(self.header().raw_data());
183
184 buf.extend_from_slice(self.input_data().raw_data());
185
186 let header_len = 0x88;
187 let new_input_data_end = header_len + self.input_data().raw_data().len();
188
189 let old_input_data_end = if self.raw_data[0x8C..0x90] == *b"Yaz1" {
191 u32::from_be_bytes(self.raw_data[0x88..0x8C].try_into().unwrap()) as usize
192 } else {
193 header_len + 0x2774
194 };
195
196 if new_input_data_end > old_input_data_end {
197 let diff = new_input_data_end - old_input_data_end;
198 let insert_pos = old_input_data_end;
199 self.raw_data
200 .splice(insert_pos..insert_pos, vec![0u8; diff]);
201 } else if new_input_data_end < old_input_data_end {
202 let diff = old_input_data_end - new_input_data_end;
203 let remove_end = old_input_data_end;
204 self.raw_data.drain(remove_end - diff..remove_end);
205 }
206
207 self.raw_data[..new_input_data_end].copy_from_slice(&buf[..new_input_data_end]);
208 let base_crc32 = crc32(&buf);
209
210 match (self.footer(), self.should_preserve_external_footer()) {
211 (Some(FooterType::CTGPFooter(ctgp_footer)), true) => {
212 buf.extend_from_slice(&base_crc32.to_be_bytes());
213 buf.extend_from_slice(ctgp_footer.raw_data());
214
215 let footer_len = ctgp_footer.len();
216 self.raw_data.drain(new_input_data_end..);
217 self.raw_data
218 .extend_from_slice(&buf[buf.len() - footer_len - 0x04..]);
219 self.raw_data.extend_from_slice(&[0u8; 4]);
220 }
221 (Some(FooterType::SPFooter(sp_footer)), true) => {
222 buf.extend_from_slice(&base_crc32.to_be_bytes());
223 buf.extend_from_slice(sp_footer.raw_data());
224
225 let footer_len = sp_footer.len();
226 self.raw_data.drain(new_input_data_end..);
227 self.raw_data
228 .extend_from_slice(&buf[buf.len() - footer_len - 0x04..]);
229 self.raw_data.extend_from_slice(&[0u8; 4]);
230 }
231 (_, true) if self.raw_data.len() >= new_input_data_end + 0x08 => {
232 self.raw_data.drain(new_input_data_end + 0x04..);
233 }
234 (_, false) if self.raw_data.len() >= new_input_data_end + 0x08 => self.raw_data
235 [new_input_data_end..new_input_data_end + 0x04]
236 .copy_from_slice(&base_crc32.to_be_bytes()),
237 _ => (),
238 }
239
240 let len = self.raw_data.len();
241 let crc32 = crc32(&self.raw_data[..len - 0x04]);
242 self.raw_data[len - 0x04..].copy_from_slice(&crc32.to_be_bytes());
243
244 let sha1 = compute_sha1_hex(&self.raw_data);
245 if let Some(FooterType::CTGPFooter(ctgp_footer)) = self.footer_mut() {
246 ctgp_footer.set_ghost_sha1(&sha1)?;
247 }
248
249 Ok(())
250 }
251
252 pub fn save_to_file<T: AsRef<std::path::Path>>(&mut self, path: T) -> Result<(), GhostError> {
259 self.update_raw_data()?;
260 let mut file = std::fs::File::create(path)?;
261 file.write_all(&self.raw_data)?;
262
263 Ok(())
264 }
265
266 pub fn compress_input_data(&mut self) {
270 if self.input_data().is_compressed() {
271 return;
272 }
273
274 self.input_data_mut().compress();
275 self.header_mut().set_compressed(true);
276 }
277
278 pub fn decompress_input_data(&mut self) {
282 if !self.input_data().is_compressed() {
283 return;
284 }
285
286 self.input_data_mut().decompress();
287 self.header_mut().set_compressed(false);
288 }
289
290 pub fn raw_data(&self) -> &[u8] {
294 &self.raw_data
295 }
296
297 pub fn raw_data_mut(&mut self) -> &mut [u8] {
299 &mut self.raw_data
300 }
301
302 pub fn header(&self) -> &Header {
304 &self.header
305 }
306
307 pub fn header_mut(&mut self) -> &mut Header {
309 &mut self.header
310 }
311
312 pub fn input_data(&self) -> &InputData {
314 &self.input_data
315 }
316
317 pub fn input_data_mut(&mut self) -> &mut InputData {
319 &mut self.input_data
320 }
321
322 pub fn footer(&self) -> Option<&FooterType> {
324 self.footer.as_ref()
325 }
326
327 pub fn footer_mut(&mut self) -> Option<&mut FooterType> {
329 self.footer.as_mut()
330 }
331
332 pub fn base_crc32(&self) -> u32 {
334 self.base_crc32
335 }
336
337 pub fn verify_base_crc32(&self) -> bool {
340 let mut data = Vec::from(self.header().raw_data());
341 data.extend_from_slice(self.input_data().raw_data());
342 self.base_crc32 == crc32(&data)
343 }
344
345 pub fn file_crc32(&self) -> u32 {
347 self.file_crc32
348 }
349
350 pub fn verify_file_crc32(&self) -> bool {
353 let len = self.raw_data().len();
354 self.file_crc32 == crc32(&self.raw_data()[..len - 0x04])
355 }
356
357 pub fn should_preserve_external_footer(&self) -> bool {
359 self.should_preserve_external_footer
360 }
361
362 pub fn set_should_preserve_external_footer(&mut self, b: bool) {
364 self.should_preserve_external_footer = b;
365 }
366}
367
368pub(crate) fn write_bits(
370 buf: &mut [u8],
371 byte_offset: usize,
372 bit_offset: usize,
373 bit_width: usize,
374 value: u64,
375) {
376 let bytes_needed = (bit_offset + bit_width).div_ceil(8);
377 let mut chunk: u64 = 0;
378
379 for i in 0..bytes_needed {
380 chunk = (chunk << 8) | buf[byte_offset + i] as u64;
381 }
382
383 let shift = bytes_needed * 8 - bit_offset - bit_width;
384 let mask = ((1u64 << bit_width) - 1) << shift;
385
386 chunk = (chunk & !mask) | ((value << shift) & mask);
387
388 for i in (0..bytes_needed).rev() {
389 buf[byte_offset + i] = (chunk & 0xFF) as u8;
390 chunk >>= 8;
391 }
392}
393
394pub(crate) fn compute_sha1_hex(input: &[u8]) -> [u8; 0x14] {
396 let mut hasher = Sha1::new();
397 hasher.update(input);
398 hasher.finalize().into()
399}
400
401pub(crate) fn datetime_from_timestamp(tick_count: u64) -> NaiveDateTime {
405 let clock_rate = 60_750_000.0; let epoch_shift = 946_684_800; let total_seconds = tick_count as f64 / clock_rate;
408 let total_nanoseconds = (total_seconds * 1_000_000_000.0) as i64;
409
410 let duration = Duration::nanoseconds(total_nanoseconds);
411 let epoch = DateTime::from_timestamp(epoch_shift, 0).unwrap();
412
413 epoch.naive_utc() + duration
414}
415
416pub(crate) fn duration_from_ticks(tick_count: u64) -> TimeDelta {
420 let clock_rate = 60_750_000.0; let total_seconds = tick_count as f64 / clock_rate;
422 let total_milliseconds = (total_seconds * 1_000.0) as i64;
423
424 Duration::milliseconds(total_milliseconds)
425}