Skip to main content

macbinary/
structs.rs

1use binrw::binread;
2use bitflags::bitflags;
3use macintosh_utils::{
4    FourCC, Point,
5    chrono::{DateTime, Utc},
6    decode_string,
7};
8use std::cmp::min;
9
10bitflags! {
11    #[derive(Debug, Clone)]
12    pub struct Flags: u8 {
13        const LOCKED = 1<<0;
14    }
15}
16
17/// MacBinary file header
18#[binread]
19#[derive(Debug)]
20#[br(big)]
21pub struct Header {
22    /// File format version
23    pub version: u8,
24    #[br(temp,assert(name_len > 0 && name_len < 63))]
25    name_len: u8,
26    /// Original file name
27    #[br(map(|r: [u8; 63]| decode_string(r[0..min(name_len as usize, 63)].to_vec())))]
28    pub name: String,
29    /// Macintosh file type code
30    pub type_code: FourCC,
31    /// Macintosh creator code
32    pub creator_code: FourCC,
33    /// Upper 8-bit of the finder flags
34    pub finder_flags_upper: u8,
35    #[br(temp, assert(zero==0))]
36    zero: u8,
37    /// Position in parent window
38    pub position: Point,
39    /// Id of parent window
40    pub window_id: u16,
41    #[br(map(Flags::from_bits_retain))]
42    pub flags: Flags,
43    #[br(temp, assert(zero_again==0))]
44    zero_again: u8,
45    /// Number of bytes in data fork
46    pub data_fork_len: u32,
47    /// Number of bytes in resource fork
48    pub resource_fork_len: u32,
49    #[br(map(macintosh_utils::date))]
50    pub created_at: DateTime<Utc>,
51    #[br(map(macintosh_utils::date))]
52    pub modified_at: DateTime<Utc>,
53
54    /// Length of file comment
55    pub comment_len: u16,
56    /// Lower 8bits of finder flags
57    pub finder_flags_lower: u8,
58    //#[br(temp, assert(magic == fourcc!("mBIN")))]
59    pub magic: FourCC,
60    pub file_name_script: u8,
61    pub extended_finder_flags: u8,
62    #[br(temp)]
63    _reserved_2: [u8; 8],
64    pub unpacked_total_len: u32,
65    pub extended_header_len: u16,
66    pub uploader_version: u8,
67    pub downloader_min_version: u8,
68    /// xmodem crc 16
69    pub checksum: u16,
70
71    #[br(temp)]
72    _reserved_3: u16,
73}
74
75impl Header {
76    pub const FIXED_SIZE: usize = 128;
77
78    fn extended_header_location(&self) -> u64 {
79        Header::FIXED_SIZE as u64
80    }
81
82    pub fn data_fork_location(&self) -> u64 {
83        self.extended_header_location() + align_128(self.extended_header_len as u64)
84    }
85
86    pub fn resource_fork_location(&self) -> u64 {
87        self.data_fork_location() + align_128(self.data_fork_len as u64)
88    }
89
90    pub fn file_comment_location(&self) -> u64 {
91        self.resource_fork_location() + align_128(self.resource_fork_len as u64)
92    }
93}
94
95fn align_128(input: u64) -> u64 {
96    if (0x80 - 1) & input != 0 {
97        (input + 0x80) & !(0x80 - 1)
98    } else {
99        input
100    }
101}
102
103#[cfg(test)]
104mod test {
105    use super::align_128;
106
107    #[test]
108    fn align_int() {
109        assert_eq!(align_128(0), 0);
110        assert_eq!(align_128(1), 128);
111        assert_eq!(align_128(127), 128);
112        assert_eq!(align_128(128), 128);
113        assert_eq!(align_128(129), 256);
114    }
115}