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)]
25#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
26pub struct PathTableEntryHeader {
27 pub len: u8,
29 pub extended_attr_record: u8,
31 pub parent_lba: [u8; 4],
33 pub parent_directory_number: [u8; 2],
35}
36
37impl PathTableEntryHeader {
38 pub fn from_bytes(bytes: &[u8]) -> Self {
40 *bytemuck::from_bytes(bytes)
41 }
42}
43
44#[derive(Debug, Clone)]
45pub struct PathTableEntry<const N: usize = 256> {
47 pub length: u8,
49 pub extended_attr_record: u8,
51 pub parent_lba: u32,
53 pub parent_index: u16,
55 pub name: FixedBytes<N>,
57}
58
59impl PathTableEntry {
60 pub fn size(&self) -> usize {
62 (size_of::<PathTableEntryHeader>() + self.name.len() + 1) & !1
63 }
64}
65
66io_transform! {
67impl PathTableEntry {
68 pub async fn parse<T: Read>(reader: &mut T, endian: EndianType) -> Result<Self, Error> {
70 let mut buf = [0; size_of::<PathTableEntryHeader>()];
71 reader.read_exact(&mut buf).await?;
72 let header = PathTableEntryHeader::from_bytes(&buf);
73 let mut name = FixedBytes::with_size(header.len as usize);
74 reader.read_exact(name.as_bytes_mut()).await?;
75 if header.len % 2 == 1 {
76 reader.read_exact(&mut [0]).await?;
78 }
79
80 Ok(Self {
81 length: header.len,
82 extended_attr_record: header.extended_attr_record,
83 parent_lba: endian.read_u32(header.parent_lba),
84 parent_index: endian.read_u16(header.parent_directory_number),
85 name,
86 })
87 }
88
89 pub async fn write<W: Write>(&self, writer: &mut W, endian: EndianType) -> io::Result<()> {
91 let header = PathTableEntryHeader {
92 len: self.name.len() as u8,
93 extended_attr_record: 0,
94 parent_lba: endian.u32_bytes(self.parent_lba),
95 parent_directory_number: endian.u16_bytes(self.parent_index),
96 };
97 writer.write_all(bytemuck::bytes_of(&header)).await?;
98 writer.write_all(self.name.as_bytes()).await?;
99 assert_eq!(header.len as usize, self.name.len());
100 if header.len % 2 == 1 {
101 writer.write_all(&[0]).await?;
102 }
103 Ok(())
104 }
105}
106} #[derive(Debug, Clone, Copy)]
109pub struct PathTableRef {
111 pub(crate) lpt: LogicalSector,
112 pub(crate) mpt: LogicalSector,
113 pub(crate) size: u64,
114}
115
116#[cfg(feature = "alloc")]
118pub struct PathTableInfo {
119 pub(crate) path_table: PathTableRef,
120}
121
122#[cfg(feature = "alloc")]
123impl PathTableInfo {
124 pub fn reference(&self) -> &PathTableRef {
126 &self.path_table
127 }
128}
129
130impl PathTableRef {
131 pub fn little_endian_sector(&self) -> LogicalSector {
133 self.lpt
134 }
135
136 pub fn big_endian_sector(&self) -> LogicalSector {
138 self.mpt
139 }
140
141 pub fn len(&self) -> u64 {
143 self.size
144 }
145
146 pub fn is_empty(&self) -> bool {
148 self.size == 0
149 }
150}
151
152sync_only! {
153#[cfg(feature = "alloc")]
154impl PathTableInfo {
155 pub fn entries<'a, DATA: Read + Seek>(
157 &self,
158 image: &'a IsoImage<DATA>,
159 ) -> PathTableEntryIter<'a, DATA> {
160 let start = if cfg!(target_endian = "little") {
161 self.path_table.lpt
162 } else {
163 self.path_table.mpt
164 };
165 let start_byte = (start.0 as u64) * 2048;
167 let end_byte = start_byte + self.path_table.size;
168 PathTableEntryIter {
169 data: &image.data,
170 current: start_byte,
171 end: end_byte,
172 }
173 }
174}
175
176#[cfg(feature = "alloc")]
177pub struct PathTableEntryIter<'a, DATA: Read + Seek> {
179 data: &'a Mutex<super::io::IsoCursor<DATA>>,
180 current: u64,
181 end: u64,
182}
183
184#[cfg(feature = "alloc")]
185impl<DATA: Read + Seek> Iterator for PathTableEntryIter<'_, DATA> {
186 type Item = io::Result<PathTableEntry>;
187
188 fn next(&mut self) -> Option<Self::Item> {
190 macro_rules! try_or_end {
191 ($expr:expr) => {
192 match $expr {
193 Ok(val) => val,
194 Err(err) => {
195 self.current = self.end;
196 return Some(Err(err));
197 }
198 }
199 };
200 }
201 if self.current >= self.end {
202 return None;
203 }
204 let mut data = self.data.lock();
205 try_or_end!(data
206 .seek(SeekFrom::Start(self.current))
207 .map_err(Error::erase));
208 let entry = try_or_end!(PathTableEntry::parse(
209 data.deref_mut(),
210 EndianType::NativeEndian,
211 ));
212 self.current += entry.size() as u64;
213
214 Some(Ok(entry))
215 }
216}
217}