dmg_oxide/
blkx.rs

1use crate::koly::UdifChecksum;
2use anyhow::Result;
3use byteorder::{ReadBytesExt, WriteBytesExt, BE};
4use std::io::{Read, Write};
5
6#[derive(Clone, Debug, Eq, PartialEq)]
7pub struct BlkxTable {
8    /// currently 1
9    pub version: u32,
10    /// starting sector
11    pub sector_number: u64,
12    /// number of sectors
13    pub sector_count: u64,
14    /// seems to always be 0
15    pub data_offset: u64,
16    /// seems to be a magic constant for zlib describing the buffer size
17    /// required for decompressing a chunk. number was taken from hdiutil
18    pub buffers_needed: u32,
19    /// not sure what this is, setting it to the partition index
20    pub block_descriptors: u32,
21    pub reserved: [u8; 24],
22    pub checksum: UdifChecksum,
23    /// chunk table
24    pub chunks: Vec<BlkxChunk>,
25}
26
27impl Default for BlkxTable {
28    fn default() -> Self {
29        Self {
30            version: 1,
31            sector_number: 0,
32            sector_count: 0,
33            data_offset: 0,
34            buffers_needed: 2056,
35            block_descriptors: 0,
36            reserved: [0; 24],
37            checksum: UdifChecksum::default(),
38            chunks: vec![],
39        }
40    }
41}
42
43impl BlkxTable {
44    pub fn new(index: u32, sector: u64, checksum: u32) -> Self {
45        Self {
46            block_descriptors: index,
47            sector_number: sector,
48            checksum: UdifChecksum::new(checksum),
49            ..Default::default()
50        }
51    }
52
53    pub fn add_chunk(&mut self, mut chunk: BlkxChunk) {
54        chunk.sector_number = self.sector_count;
55        self.sector_count += chunk.sector_count;
56        self.chunks.push(chunk);
57    }
58
59    pub fn read_from<R: Read>(r: &mut R) -> Result<Self> {
60        let mut signature = [0; 4];
61        r.read_exact(&mut signature)?;
62        anyhow::ensure!(&signature == b"mish");
63        let version = r.read_u32::<BE>()?;
64        let sector_number = r.read_u64::<BE>()?;
65        let sector_count = r.read_u64::<BE>()?;
66        let data_offset = r.read_u64::<BE>()?;
67        let buffers_needed = r.read_u32::<BE>()?;
68        let block_descriptors = r.read_u32::<BE>()?;
69        let mut reserved = [0; 24];
70        r.read_exact(&mut reserved)?;
71        let checksum = UdifChecksum::read_from(r)?;
72        let num_chunks = r.read_u32::<BE>()?;
73        let mut chunks = Vec::with_capacity(num_chunks as _);
74        for _ in 0..num_chunks {
75            chunks.push(BlkxChunk::read_from(r)?);
76        }
77        Ok(Self {
78            version,
79            sector_number,
80            sector_count,
81            data_offset,
82            buffers_needed,
83            block_descriptors,
84            reserved,
85            checksum,
86            chunks,
87        })
88    }
89
90    pub fn write_to<W: Write>(&self, w: &mut W) -> Result<()> {
91        w.write_all(b"mish")?;
92        w.write_u32::<BE>(self.version)?;
93        w.write_u64::<BE>(self.sector_number)?;
94        w.write_u64::<BE>(self.sector_count)?;
95        w.write_u64::<BE>(self.data_offset)?;
96        w.write_u32::<BE>(self.buffers_needed)?;
97        w.write_u32::<BE>(self.block_descriptors)?;
98        w.write_all(&self.reserved)?;
99        self.checksum.write_to(w)?;
100        w.write_u32::<BE>(self.chunks.len() as u32)?;
101        for chunk in &self.chunks {
102            chunk.write_to(w)?;
103        }
104        Ok(())
105    }
106}
107
108#[derive(Clone, Copy, Debug, Eq, PartialEq)]
109pub struct BlkxChunk {
110    /// compression type used for this chunk
111    pub r#type: u32,
112    pub comment: u32,
113    pub sector_number: u64,
114    pub sector_count: u64,
115    pub compressed_offset: u64,
116    pub compressed_length: u64,
117}
118
119impl Default for BlkxChunk {
120    fn default() -> Self {
121        Self {
122            r#type: ChunkType::Raw as _,
123            comment: 0,
124            sector_number: 0,
125            sector_count: 0,
126            compressed_offset: 0,
127            compressed_length: 0,
128        }
129    }
130}
131
132impl BlkxChunk {
133    pub fn new(
134        ty: ChunkType,
135        sector_number: u64,
136        sector_count: u64,
137        compressed_offset: u64,
138        compressed_length: u64,
139    ) -> Self {
140        Self {
141            r#type: ty as _,
142            sector_number,
143            sector_count,
144            compressed_offset,
145            compressed_length,
146            ..Default::default()
147        }
148    }
149
150    pub fn term(sector_number: u64, compressed_offset: u64) -> Self {
151        Self::new(ChunkType::Term, sector_number, 0, compressed_offset, 0)
152    }
153
154    pub fn read_from<R: Read>(r: &mut R) -> Result<Self> {
155        let r#type = r.read_u32::<BE>()?;
156        let comment = r.read_u32::<BE>()?;
157        let sector_number = r.read_u64::<BE>()?;
158        let sector_count = r.read_u64::<BE>()?;
159        let compressed_offset = r.read_u64::<BE>()?;
160        let compressed_length = r.read_u64::<BE>()?;
161        Ok(Self {
162            r#type,
163            comment,
164            sector_number,
165            sector_count,
166            compressed_offset,
167            compressed_length,
168        })
169    }
170
171    pub fn write_to<W: Write>(&self, w: &mut W) -> Result<()> {
172        w.write_u32::<BE>(self.r#type)?;
173        w.write_u32::<BE>(self.comment)?;
174        w.write_u64::<BE>(self.sector_number)?;
175        w.write_u64::<BE>(self.sector_count)?;
176        w.write_u64::<BE>(self.compressed_offset)?;
177        w.write_u64::<BE>(self.compressed_length)?;
178        Ok(())
179    }
180
181    pub fn ty(self) -> Option<ChunkType> {
182        ChunkType::from_u32(self.r#type)
183    }
184}
185
186/// Possible compression types of the BlkxChunk.
187#[repr(u32)]
188#[derive(Clone, Copy, Debug, Eq, PartialEq)]
189pub enum ChunkType {
190    Zero = 0x0000_0000,
191    Raw = 0x0000_0001,
192    Ignore = 0x0000_0002,
193    Comment = 0x7fff_fffe,
194    Adc = 0x8000_0004,
195    Zlib = 0x8000_0005,
196    Bzlib = 0x8000_0006,
197    Lzfse = 0x8000_0007,
198    Term = 0xffff_ffff,
199}
200
201impl ChunkType {
202    pub fn from_u32(ty: u32) -> Option<Self> {
203        Some(match ty {
204            x if x == ChunkType::Zero as u32 => ChunkType::Zero,
205            x if x == ChunkType::Raw as u32 => ChunkType::Raw,
206            x if x == ChunkType::Ignore as u32 => ChunkType::Ignore,
207            x if x == ChunkType::Comment as u32 => ChunkType::Comment,
208            x if x == ChunkType::Adc as u32 => ChunkType::Adc,
209            x if x == ChunkType::Zlib as u32 => ChunkType::Zlib,
210            x if x == ChunkType::Bzlib as u32 => ChunkType::Bzlib,
211            x if x == ChunkType::Lzfse as u32 => ChunkType::Lzfse,
212            x if x == ChunkType::Term as u32 => ChunkType::Term,
213            _ => return None,
214        })
215    }
216}