use crate::*;
use std::io;
use flate2::read::DeflateDecoder;
#[derive(BinRead, Debug)]
#[br(map = Self::from_u64)]
pub struct L1Entry {
pub l2_offset: u64,
pub is_used: bool,
}
impl L1Entry {
fn from_u64(x: u64) -> Self {
L1Entry {
l2_offset: x & 0x00ff_ffff_ffff_fe00,
is_used: x & 0x8000_0000_0000_0000 != 0,
}
}
pub fn read_l2(
&self,
reader: &mut (impl Read + Seek),
cluster_bits: u32
) -> Option<Vec<L2Entry>> {
reader.seek(SeekFrom::Start(self.l2_offset)).ok()?;
let L2Entries(entries) = reader.read_be_args((cluster_bits,)).ok()?;
Some(
entries
.into_iter()
.map(|x| L2Entry::from_u64(x, cluster_bits))
.collect()
)
}
}
#[derive(BinRead)]
#[br(import(cluster_bits: u32))]
struct L2Entries(
#[br(count = (1 << cluster_bits) / 8)]
Vec<u64>,
);
#[derive(Debug, Clone)]
pub struct L2Entry {
pub cluster_descriptor: ClusterDescriptor,
pub is_compressed: bool,
pub is_used: bool,
}
impl L2Entry {
fn from_u64(x: u64, cluster_bits: u32) -> Self {
let is_compressed = x & 0x4000_0000_0000_0000 != 0;
L2Entry {
cluster_descriptor: ClusterDescriptor::from_u64(
is_compressed,
x & 0x3fffffffffffffff,
cluster_bits
),
is_used: x & 0x8000_0000_0000_0000 != 0,
is_compressed,
}
}
pub fn read_contents(
&self,
reader: &mut (impl Read + Seek),
buf: &mut [u8],
comp_type: CompressionType,
) -> io::Result<()> {
match &self.cluster_descriptor {
ClusterDescriptor::Standard(cluster) => {
if cluster.all_zeroes || cluster.host_cluster_offset == 0 {
buf.fill(0);
} else {
reader.seek(SeekFrom::Start(cluster.host_cluster_offset))
.map_err(|_| io::Error::new(
io::ErrorKind::UnexpectedEof,
"Seeked past the end of the file attempting to read the current \
cluster"
))?;
io::copy(
&mut reader.take(buf.len() as u64),
&mut io::Cursor::new(buf)
)?;
}
},
ClusterDescriptor::Compressed(cluster) => {
match comp_type {
CompressionType::Zlib => {
reader.seek(SeekFrom::Start(cluster.host_cluster_offset))
.map_err(|_| io::Error::new(
io::ErrorKind::UnexpectedEof,
"Seeked past the end of the file attempting to read the current \
cluster"
))?;
let cluster_size = buf.len() as u64;
let mut cluster = io::Cursor::new(buf);
io::copy(
&mut DeflateDecoder::new(reader).take(cluster_size),
&mut cluster
)?;
}
CompressionType::Zstd => {
reader.seek(SeekFrom::Start(cluster.host_cluster_offset))
.map_err(|_| io::Error::new(
io::ErrorKind::UnexpectedEof,
"Seeked past the end of the file attempting to read the current \
cluster"
))?;
let cluster_size = buf.len() as u64;
let mut cluster = io::Cursor::new(buf);
io::copy(
&mut zstd::Decoder::new(reader)?.take(cluster_size),
&mut cluster
)?;
},
}
},
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub enum ClusterDescriptor {
Standard(StandardClusterDescriptor),
Compressed(CompressedClusterDescriptor),
}
#[derive(Debug, Clone)]
pub struct StandardClusterDescriptor {
pub all_zeroes: bool,
pub host_cluster_offset: u64,
}
impl StandardClusterDescriptor {
fn from_u64(x: u64) -> Self {
Self {
all_zeroes: x & 1 != 0,
host_cluster_offset: (x & 0x00ff_ffff_ffff_fe00)
}
}
}
#[derive(Debug, Clone)]
pub struct CompressedClusterDescriptor {
pub host_cluster_offset: u64,
pub additional_sector_count: u64,
}
fn mask(bits: u32) -> u64 {
(1 << bits) - 1
}
impl CompressedClusterDescriptor {
fn from_u64(x: u64, cluster_bits: u32) -> Self {
let host_cluster_bits = 62 - (cluster_bits - 8);
Self {
host_cluster_offset: x & mask(host_cluster_bits),
additional_sector_count: (x & !mask(host_cluster_bits)) >> host_cluster_bits,
}
}
}
impl ClusterDescriptor {
fn from_u64(is_compressed: bool, x: u64, cluster_bits: u32) -> Self {
if is_compressed {
Self::Compressed(CompressedClusterDescriptor::from_u64(x, cluster_bits))
} else {
Self::Standard(StandardClusterDescriptor::from_u64(x))
}
}
}