#![cfg_attr(not(feature = "std"), no_std)]
#![deny(unsafe_code)]
pub mod descriptors;
pub mod helpers;
pub mod extensions;
pub mod types;
pub mod iter;
use alloc::vec;
const DISK_SECTOR_SIZE: usize = 2048;
const PRIMARY_VOLUME_DESCRIPTOR_POSITION: usize = 0x8000;
const FLAG_DIRECTORY: u8 = 1 << 1;
extern crate alloc;
use alloc::{
borrow::ToOwned,
boxed::Box,
string::String,
vec::Vec,
};
use core::marker::{Send, Sync};
use bitflags::bitflags;
bitflags! {
pub struct ISOInternalFlags: u32 {
const HasJoliet = (1 << 0);
}
}
#[repr(C, packed(1))]
#[derive(Clone, Copy, Debug, Default, FromBytes, Immutable, IntoBytes)]
pub struct ISODateTime {
pub year: u8,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: u8,
pub gmt_offset: u8,
}
#[repr(C, packed(1))]
#[derive(Debug, Default, Clone, FromBytes, Immutable, IntoBytes)]
pub struct ISODirectoryRecord {
pub(crate) length: u8,
pub(crate) xar_length: u8,
pub lba: types::LSB_MSB<u32>,
pub data_length: types::LSB_MSB<u32>,
pub datetime: ISODateTime,
pub flags: u8,
pub(crate) unit_size: u8,
pub(crate) interleave_gap_size: u8,
pub(crate) volume_seq_number: types::LSB_MSB<u16>,
pub(crate) file_identifier_length: u8, }
#[derive(Debug, Default, Clone)]
pub struct ISODirectoryEntry {
pub record: ISODirectoryRecord,
pub name: String,
}
impl ISODirectoryEntry {
pub const fn is_folder(&self) -> bool {
(self.record.flags & FLAG_DIRECTORY) != 0
}
pub const fn is_file(&self) -> bool {
!self.is_folder()
}
pub fn lsb_position(&self) -> u32 {
self.record.lba.get()
}
pub fn file_size(&self) -> u32 {
self.record.data_length.get()
}
}
pub mod io;
pub use io::Read;
use zerocopy::{FromBytes, Immutable, IntoBytes};
use crate::{descriptors::DescriptorType, iter::{DescriptorIterator, DirectoryIter}};
pub struct ISO9660 {
root_directory: ISODirectoryRecord,
flags: ISOInternalFlags,
device: Box<dyn Read + Send + Sync>,
}
impl ISO9660 {
pub fn from_device(mut device: impl Read + Send + Sync + 'static) -> Option<ISO9660> {
let mut flags = ISOInternalFlags::empty();
let pvd_desc = DescriptorIterator::new(&mut device).find(|x| x.desc_type == DescriptorType::PrimaryVolume)?;
let mut main_descriptor = pvd_desc.try_as_pvd()?;
let svd: Option<descriptors::Descriptor> = DescriptorIterator::new(&mut device).find(|x| x.desc_type == DescriptorType::SupplementaryVolume);
if let Some(ref svd) = svd {
main_descriptor = svd.try_as_svd()?;
flags |= ISOInternalFlags::HasJoliet;
}
let root_dir = ISODirectoryRecord::read_from_prefix(&main_descriptor.directory_entry).ok()?.0;
Some(ISO9660 {
root_directory: root_dir,
flags: flags,
device: Box::new(device),
})
}
pub fn descriptors(&mut self) -> DescriptorIterator<'_> {
DescriptorIterator::new(self.device.as_mut())
}
fn read_rock_ridge_name(
&mut self,
byte_offset: usize,
main_part_size: usize,
extension_size: usize,
) -> Option<String> {
let mut address = byte_offset + main_part_size;
if address % 2 != 0 {
address += 1;
}
let mut extension_data: Vec<u8> = vec![0; extension_size];
self.device.read(address, &mut extension_data);
let rock_ridge_data = extensions::rock_ridge::parse(&extension_data);
for i in rock_ridge_data {
if let extensions::rock_ridge::Entity::Name { name } = i {
return Some(name.to_owned());
}
}
None
}
fn read_joliet_name(
&mut self,
byte_offset: usize,
len: usize,
) -> Option<String> {
let mut address = byte_offset;
if (address % 2 != 0) && len != 1 {
address += 1;
}
let mut ucs2_name: Vec<u8> = vec![0; len];
self.device.read(address, &mut ucs2_name);
extensions::joliet::parse_name(&ucs2_name)
}
pub fn read_directory(&mut self, start_lba: usize) -> DirectoryIter<'_> {
let byte_offset = start_lba * DISK_SECTOR_SIZE;
DirectoryIter::new(self, byte_offset)
}
pub fn read_file(
&mut self,
directory_entry: &ISODirectoryEntry,
offset: usize,
data: &mut [u8],
) -> Option<()> {
if (directory_entry.record.flags & FLAG_DIRECTORY) != 0 {
return None;
}
let position = directory_entry.lsb_position() as usize;
let data_length = directory_entry.file_size() as usize;
if offset + data.len() > data_length {
return None;
}
self.device
.read((position * DISK_SECTOR_SIZE) + offset, data);
Some(())
}
#[inline]
pub fn read_root(&mut self) -> DirectoryIter<'_> {
self.read_directory(self.root_directory.lba.get() as usize)
}
#[inline]
pub fn root(&self) -> &ISODirectoryRecord {
&self.root_directory
}
}