//! Macintosh archived format header utilities.

use crate::durandal::bin::*;

/// Checks for an `AppleSingle` header. Returns offset to the resource fork.
pub fn check_apple_single(b: &[u8]) -> Option<usize>
{
   // check magic numbers
   if b.len() < 26 || *b.get(0..8)? != [0, 5, 22, 0, 0, 2, 0, 0] {
      return None;
   }

   let num = usize::from(u16b(&b[24..]));

   if b.len() < 26 + 12 * num {
      return None;
   }

   // get the resource fork (entity 1)
   for i in 0..num {
      let p   = 26 + 12 * i;
      let ent = u32b(&b[p..]);
      let ofs = usize_from_u32(u32b(&b[p + 4..]));
      let len = usize_from_u32(u32b(&b[p + 8..]));

      if ent == 1 {
         return if ofs + len > b.len() {None} else {Some(ofs)};
      }
   }

   // no resource fork
   None
}

/// Checks for a `MacBinary II` header. Returns offset to the resource fork.
pub fn check_macbin(b: &[u8]) -> Option<usize>
{
   // check legacy version, length, zero fill, and macbin2 version
   // I swear this isn't *completely* magic
   if b.len() < 128 || b[0] != 0 || b[1] > 63 || b[74] != 0 || b[123] > 129 {
      return None;
   }

   let mut crc = 0;

   // check header crc
   for &byte in b.iter().take(124) {
      for j in 8..16 {
         let d = u16::from(byte) << j;

         if (d ^ crc) & 0x8000 == 0 {
            crc <<= 1;
         } else {
            crc = crc << 1 ^ 0x1021;
         }
      }
   }

   // if ok, resource fork follows
   if crc == u16b(&b[124..]) {
      Some(128)
   } else {
      None
   }
}

/// Reads a `MacBin` or `AppleSingle` header if there is one and returns the
/// offset from the start of the header to the resource fork (if one is found.)
pub fn try_mac_header(b: &[u8]) -> usize
{
   if let Some(ofs) = check_macbin(b) {
      ofs
   } else {
      check_apple_single(b).unwrap_or(0)
   }
}

// EOF
