1use hadris_fixed::FixedBytes;
2
3use super::io::{self, Error, LogicalSector, Read, Write};
4use crate::types::EndianType;
5
6sync_only! {
7#[cfg(feature = "alloc")]
8use core::ops::DerefMut;
9#[cfg(feature = "alloc")]
10use spin::Mutex;
11#[cfg(feature = "alloc")]
12use super::io::{Seek, SeekFrom};
13#[cfg(feature = "alloc")]
14use super::read::IsoImage;
15}
16
17#[repr(C)]
24#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
25pub struct PathTableEntryHeader {
26 pub len: u8,
28 pub extended_attr_record: u8,
30 pub parent_lba: [u8; 4],
32 pub parent_directory_number: [u8; 2],
34}
35
36impl PathTableEntryHeader {
37 pub fn from_bytes(bytes: &[u8]) -> Self {
39 *bytemuck::from_bytes(bytes)
40 }
41}
42
43#[derive(Debug, Clone)]
44pub struct PathTableEntry<const N: usize = 256> {
46 pub length: u8,
48 pub extended_attr_record: u8,
50 pub parent_lba: u32,
52 pub parent_index: u16,
54 pub name: FixedBytes<N>,
56}
57
58impl PathTableEntry {
59 pub fn size(&self) -> usize {
61 (size_of::<PathTableEntryHeader>() + self.name.len() + 1) & !1
62 }
63}
64
65io_transform! {
66impl PathTableEntry {
67 pub async fn parse<T: Read>(reader: &mut T, endian: EndianType) -> Result<Self, Error> {
69 let mut buf = [0; size_of::<PathTableEntryHeader>()];
70 reader.read_exact(&mut buf).await?;
71 let header = PathTableEntryHeader::from_bytes(&buf);
72 let mut name = FixedBytes::with_size(header.len as usize);
73 reader.read_exact(name.as_bytes_mut()).await?;
74 if header.len % 2 == 1 {
75 reader.read_exact(&mut [0]).await?;
77 }
78
79 Ok(Self {
80 length: header.len,
81 extended_attr_record: header.extended_attr_record,
82 parent_lba: endian.read_u32(header.parent_lba),
83 parent_index: endian.read_u16(header.parent_directory_number),
84 name,
85 })
86 }
87
88 pub async fn write<W: Write>(&self, writer: &mut W, endian: EndianType) -> io::Result<()> {
90 let header = PathTableEntryHeader {
91 len: self.name.len() as u8,
92 extended_attr_record: 0,
93 parent_lba: endian.u32_bytes(self.parent_lba),
94 parent_directory_number: endian.u16_bytes(self.parent_index),
95 };
96 writer.write_all(bytemuck::bytes_of(&header)).await?;
97 writer.write_all(self.name.as_bytes()).await?;
98 assert_eq!(header.len as usize, self.name.len());
99 if header.len % 2 == 1 {
100 writer.write_all(&[0]).await?;
101 }
102 Ok(())
103 }
104}
105} #[derive(Debug, Clone, Copy)]
108pub struct PathTableRef {
110 pub(crate) lpt: LogicalSector,
111 pub(crate) mpt: LogicalSector,
112 pub(crate) size: u64,
113}
114
115#[cfg(feature = "alloc")]
117pub struct PathTableInfo {
118 pub(crate) path_table: PathTableRef,
119}
120
121#[cfg(feature = "alloc")]
122impl PathTableInfo {
123 pub fn reference(&self) -> &PathTableRef {
125 &self.path_table
126 }
127}
128
129impl PathTableRef {
130 pub fn little_endian_sector(&self) -> LogicalSector {
132 self.lpt
133 }
134
135 pub fn big_endian_sector(&self) -> LogicalSector {
137 self.mpt
138 }
139
140 pub fn len(&self) -> u64 {
142 self.size
143 }
144
145 pub fn is_empty(&self) -> bool {
147 self.size == 0
148 }
149}
150
151sync_only! {
152#[cfg(feature = "alloc")]
153impl PathTableInfo {
154 pub fn entries<'a, DATA: Read + Seek>(
156 &self,
157 image: &'a IsoImage<DATA>,
158 ) -> PathTableEntryIter<'a, DATA> {
159 let start = if cfg!(target_endian = "little") {
160 self.path_table.lpt
161 } else {
162 self.path_table.mpt
163 };
164 let start_byte = (start.0 as u64) * 2048;
166 let end_byte = start_byte + self.path_table.size;
167 PathTableEntryIter {
168 data: &image.data,
169 current: start_byte,
170 end: end_byte,
171 }
172 }
173}
174
175#[cfg(feature = "alloc")]
176pub struct PathTableEntryIter<'a, DATA: Read + Seek> {
178 data: &'a Mutex<super::io::IsoCursor<DATA>>,
179 current: u64,
180 end: u64,
181}
182
183#[cfg(feature = "alloc")]
184impl<DATA: Read + Seek> Iterator for PathTableEntryIter<'_, DATA> {
185 type Item = io::Result<PathTableEntry>;
186
187 fn next(&mut self) -> Option<Self::Item> {
189 use super::io::try_io_result_option as try_io;
190 if self.current >= self.end {
191 return None;
192 }
193 let mut data = self.data.lock();
194 try_io!(data
195 .seek(SeekFrom::Start(self.current))
196 .map_err(Error::erase));
197 let entry = try_io!(PathTableEntry::parse(
198 data.deref_mut(),
199 EndianType::NativeEndian,
200 ));
201 self.current += entry.size() as u64;
202
203 Some(Ok(entry))
204 }
205}
206}