use std::fmt;
use std::io;
use binrw::{BinRead, BinReaderExt};
use fourcc::FourCC;
use macintosh_utils::Fork;
use v5::EntryFlags;
pub mod v1;
pub mod v5;
#[derive(BinRead, Debug)]
#[br(big)]
pub enum ArchiveHeader {
V1(v1::ArchiveHeader),
V5(v5::ArchiveHeader),
}
impl ArchiveHeader {
pub fn entry_count(&self) -> usize {
match self {
ArchiveHeader::V1(header) => header.entry_count as usize,
ArchiveHeader::V5(header) => header.entry_count as usize,
}
}
pub fn version(&self) -> Version {
match self {
ArchiveHeader::V1(header) => header.version,
ArchiveHeader::V5(header) => header.version,
}
}
pub fn checksum_valid(&self) -> bool {
match self {
ArchiveHeader::V1(_) => true,
ArchiveHeader::V5(header) => header.checksum_valid,
}
}
}
#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Copy, Clone)]
pub enum Algorithm {
None,
RLE,
LZW,
Huffman,
LZAH,
HuffmanFixed,
LZMW,
LzHuffman,
Installer,
Arsenic,
Unknown(u8),
}
impl BinRead for Algorithm {
type Args<'a> = ();
fn read_options<R: io::Read + io::Seek>(
reader: &mut R,
_endian: binrw::Endian,
_args: Self::Args<'_>,
) -> binrw::BinResult<Self> {
Ok(match reader.read_be::<u8>()? & !(128) {
0u8 => Self::None,
1u8 => Self::RLE,
2u8 => Self::LZW,
3u8 => Self::Huffman,
5u8 => Self::LZAH,
6u8 => Self::HuffmanFixed,
8u8 => Self::LZMW,
13u8 => Self::LzHuffman,
14u8 => Self::Installer,
15u8 => Self::Arsenic,
val => Self::Unknown(val),
})
}
}
impl fmt::Display for Algorithm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Algorithm::RLE => "RLE".fmt(f),
Algorithm::None => "None".fmt(f),
Algorithm::LZW => "LZW".fmt(f),
Algorithm::Huffman => "Huff".fmt(f),
Algorithm::LZAH => "LZAH".fmt(f),
Algorithm::HuffmanFixed => "HuFi".fmt(f),
Algorithm::LZMW => "LZMW".fmt(f),
Algorithm::LzHuffman => "LzHu".fmt(f),
Algorithm::Installer => "Inst".fmt(f),
Algorithm::Arsenic => "Ars".fmt(f),
Algorithm::Unknown(m) => format!("Unknown {m}").fmt(f),
}
}
}
#[derive(Debug, Clone)]
pub enum Entry {
File(File),
Directory(Directory),
EndOfDirectory,
}
#[derive(Debug, Clone)]
pub enum File {
V1(v1::File),
V5(v5::File),
}
impl File {
pub fn name(&self) -> &str {
match self {
File::V1(file) => file.file_name.as_str(),
File::V5(file) => file.file_name.as_str(),
}
}
pub fn creator(&self) -> FourCC {
match self {
File::V1(file) => file.creator_code,
File::V5(file) => file.creator_code,
}
}
pub fn file_code(&self) -> FourCC {
match self {
File::V1(file) => file.file_code,
File::V5(file) => file.file_code,
}
}
pub fn comment(&self) -> &str {
match self {
File::V1(_) => "",
File::V5(file) => file.comment.as_str(),
}
}
pub fn uses_encryption(&self) -> bool {
match self {
File::V1(file) => file.uses_encryption(),
File::V5(file) => file.uses_encryption(),
}
}
#[inline]
pub fn has(&self, fork: Fork) -> bool {
match self {
File::V1(file) => file.compressed_size(fork) != 0,
File::V5(file) => file.compressed_size(fork) != 0,
}
}
#[inline]
pub fn uncompressed_size(&self, fork: Fork) -> usize {
match self {
File::V1(file) => file.uncompressed_size(fork),
File::V5(file) => file.uncompressed_size(fork),
}
}
#[inline]
pub fn compressed_size(&self, fork: Fork) -> usize {
match self {
File::V1(file) => file.compressed_size(fork),
File::V5(file) => file.compressed_size(fork),
}
}
#[inline]
pub fn compression_method(&self, fork: Fork) -> Algorithm {
match self {
File::V1(file) => file.compression_method(fork),
File::V5(file) => file.compression_method(fork),
}
}
#[inline]
pub fn encrypted(&self, fork: Fork) -> bool {
match self {
File::V1(file) => file.encrypted(fork),
File::V5(file) => file.encrypted(fork),
}
}
#[inline]
pub fn checksum(&self, fork: Fork) -> u16 {
match self {
File::V1(file) => file.checksum(fork),
File::V5(file) => file.checksum(fork),
}
}
#[inline]
pub fn offset(&self, fork: Fork) -> u64 {
match self {
File::V1(file) => file.offset(fork),
File::V5(file) => file.offset(fork),
}
}
pub fn created_at(&self) -> chrono::DateTime<chrono::Utc> {
match self {
File::V1(file) => file.created_at,
File::V5(file) => file.creation_date,
}
}
pub fn modified_at(&self) -> chrono::DateTime<chrono::Utc> {
match self {
File::V1(file) => file.modified_at,
File::V5(file) => file.modification_date,
}
}
pub fn index(&self) -> usize {
match self {
File::V1(file) => file.index,
File::V5(file) => file.index,
}
}
}
#[derive(Debug, Clone)]
pub enum Directory {
V1(v1::Directory),
V5(v5::Directory),
}
impl Directory {
#[inline]
pub fn name(&self) -> &str {
match self {
Directory::V1(dir) => &dir.file_name,
Directory::V5(dir) => dir.file_name(),
}
}
#[inline]
pub fn encrypted(&self, _: Fork) -> bool {
match self {
Directory::V1(_) => false,
Directory::V5(dir) => dir.flags.contains(EntryFlags::ENCRYPTED),
}
}
#[inline]
pub fn algorithm(&self, fork: Fork) -> Algorithm {
match self {
Directory::V1(dir) => dir.algorithm(fork),
Directory::V5(dir) => dir.algorithm(fork),
}
}
#[inline]
pub fn uncompressed_size(&self, fork: Fork) -> usize {
match self {
Directory::V1(dir) => dir.uncompressed_size(fork),
Directory::V5(dir) => dir.uncompressed_size(fork),
}
}
#[inline]
pub fn compressed_size(&self, fork: Fork) -> usize {
match self {
Directory::V1(dir) => dir.compressed_size(fork),
Directory::V5(dir) => dir.compressed_size(fork),
}
}
#[inline]
pub fn offset(&self, fork: Fork) -> u64 {
match self {
Directory::V1(dir) => dir.offset(fork),
Directory::V5(dir) => dir.offset(fork),
}
}
#[inline]
pub fn checksum(&self, fork: Fork) -> u16 {
match self {
Directory::V1(dir) => dir.checksum(fork),
Directory::V5(dir) => dir.checksum(fork),
}
}
#[inline]
pub fn has(&self, fork: Fork) -> bool {
match self {
Directory::V1(dir) => dir.compressed_size(fork) != 0,
Directory::V5(dir) => dir.compressed_size(fork) != 0,
}
}
pub fn comment(&self) -> &str {
match self {
Directory::V1(_) => "", Directory::V5(dir) => dir.comment(),
}
}
pub fn uses_encryption(&self) -> bool {
match self {
Directory::V1(dir) => dir.uses_encryption(),
Directory::V5(dir) => dir.uses_encryption(),
}
}
}
#[derive(BinRead, Debug, PartialEq, PartialOrd, Copy, Clone)]
#[br(big)]
pub enum Version {
#[br(magic(1u8))]
Early,
#[br(magic(2u8))]
Later,
#[br(magic(5u8))]
Five,
Unknown(u8),
}
impl Version {
pub fn short_str(&self) -> &'static str {
match self {
Version::Early => "1",
Version::Later => "2",
Version::Five => "5",
Version::Unknown(_) => "x",
}
}
}
impl fmt::Display for Version {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Version::Early => f.write_str("1.5.x and earlier"),
Version::Later => f.write_str("1.6 to 4.5"),
Version::Five => f.write_str("5.x"),
Version::Unknown(v) => f.write_fmt(format_args!("Unknown {v}")),
}
}
}