11// based on https://crates.io/crates/mini_fat
22
3- use crate :: disk:: Read ;
3+ use crate :: {
4+ disk:: { Read , Seek , SeekFrom } ,
5+ screen,
6+ } ;
7+ use core:: { char:: DecodeUtf16Error , fmt:: Write as _} ;
48
59const DIRECTORY_ENTRY_BYTES : usize = 32 ;
610const UNUSED_ENTRY_PREFIX : u8 = 0xE5 ;
711const END_OF_DIRECTORY_PREFIX : u8 = 0 ;
812
13+ static mut BUFFER : [ u8 ; 0x4000 ] = [ 0 ; 0x4000 ] ;
14+
915pub struct File {
1016 first_cluster : u32 ,
1117 file_size : u32 ,
@@ -31,7 +37,7 @@ struct Bpb {
3137}
3238
3339impl Bpb {
34- fn parse < D : Read > ( disk : & mut D ) -> Self {
40+ fn parse < D : Read + Seek > ( disk : & mut D ) -> Self {
3541 let mut raw = [ 0u8 ; 512 ] ;
3642 disk. read_exact ( & mut raw) ;
3743
@@ -128,7 +134,7 @@ pub struct FileSystem<D> {
128134 bpb : Bpb ,
129135}
130136
131- impl < D : Read > FileSystem < D > {
137+ impl < D : Read + Seek > FileSystem < D > {
132138 pub fn parse ( mut disk : D ) -> Self {
133139 Self {
134140 bpb : Bpb :: parse ( & mut disk) ,
@@ -137,17 +143,48 @@ impl<D: Read> FileSystem<D> {
137143 }
138144
139145 pub fn lookup_file ( & mut self , path : & str ) -> Option < File > {
146+ let root = self . read_root_dir ( ) ;
147+ for entry in root {
148+ write ! ( screen:: Writer , "entry: " ) . unwrap ( ) ;
149+ match entry {
150+ Ok ( RawDirectoryEntry :: Normal ( entry) ) => {
151+ writeln ! ( screen:: Writer , "{}" , entry. short_filename_main) . unwrap ( ) ;
152+ }
153+ Ok ( RawDirectoryEntry :: LongName ( entry) ) => {
154+ for c in entry. name ( ) {
155+ match c {
156+ Ok ( c) => write ! ( screen:: Writer , "{c}" ) . unwrap ( ) ,
157+ Err ( _) => write ! ( screen:: Writer , "X" ) . unwrap ( ) ,
158+ }
159+ }
160+ writeln ! ( screen:: Writer ) . unwrap ( ) ;
161+ }
162+ Err ( ( ) ) => writeln ! ( screen:: Writer , "<failed to read>" ) . unwrap ( ) ,
163+ }
164+ }
140165 todo ! ( ) ;
141166 }
142167
143- fn read_root_dir ( & mut self ) {
168+ fn read_root_dir < ' a > ( & ' a mut self ) -> impl Iterator < Item = Result < RawDirectoryEntry , ( ) > > + ' a {
144169 match self . bpb . fat_type ( ) {
145170 FatType :: Fat32 => {
146171 self . bpb . root_cluster ;
172+ unimplemented ! ( ) ;
147173 }
148174 FatType :: Fat12 | FatType :: Fat16 => {
149- self . bpb . root_directory_offset ( ) ;
150- self . bpb . root_directory_size ( ) ;
175+ let root_directory_size = self . bpb . root_directory_size ( ) ;
176+ let buffer = unsafe { & mut BUFFER [ ..] } ;
177+ assert ! ( root_directory_size <= buffer. len( ) ) ;
178+ let raw = & mut buffer[ ..root_directory_size] ;
179+
180+ self . disk
181+ . seek ( SeekFrom :: Start ( self . bpb . root_directory_offset ( ) ) ) ;
182+ self . disk . read_exact ( raw) ;
183+
184+ raw. chunks ( DIRECTORY_ENTRY_BYTES )
185+ . take_while ( |raw_entry| raw_entry[ 0 ] != END_OF_DIRECTORY_PREFIX )
186+ . filter ( |raw_entry| raw_entry[ 0 ] != UNUSED_ENTRY_PREFIX )
187+ . map ( RawDirectoryEntry :: parse)
151188 }
152189 }
153190 }
@@ -159,3 +196,95 @@ enum FatType {
159196 Fat16 ,
160197 Fat32 ,
161198}
199+
200+ #[ derive( Debug ) ]
201+ struct RawDirectoryEntryNormal < ' a > {
202+ short_filename_main : & ' a str ,
203+ short_filename_extension : & ' a str ,
204+ attributes : u8 ,
205+ first_cluster : u32 ,
206+ file_size : u32 ,
207+ }
208+
209+ #[ derive( Debug ) ]
210+ struct RawDirectoryEntryLongName < ' a > {
211+ order : u8 ,
212+ name_1 : & ' a [ u8 ] ,
213+ name_2 : & ' a [ u8 ] ,
214+ name_3 : & ' a [ u8 ] ,
215+ attributes : u8 ,
216+ checksum : u8 ,
217+ }
218+
219+ impl < ' a > RawDirectoryEntryLongName < ' a > {
220+ pub fn name ( & self ) -> impl Iterator < Item = Result < char , DecodeUtf16Error > > + ' a {
221+ let iter = self
222+ . name_1
223+ . chunks ( 2 )
224+ . chain ( self . name_2 . chunks ( 2 ) )
225+ . chain ( self . name_3 . chunks ( 2 ) )
226+ . map ( |c| u16:: from_le_bytes ( c. try_into ( ) . unwrap ( ) ) )
227+ . take_while ( |& c| c != 0 ) ;
228+ char:: decode_utf16 ( iter)
229+ }
230+ }
231+
232+ #[ derive( Debug ) ]
233+ enum RawDirectoryEntry < ' a > {
234+ Normal ( RawDirectoryEntryNormal < ' a > ) ,
235+ LongName ( RawDirectoryEntryLongName < ' a > ) ,
236+ }
237+
238+ impl < ' a > RawDirectoryEntry < ' a > {
239+ fn parse ( raw : & ' a [ u8 ] ) -> Result < Self , ( ) > {
240+ let attributes = raw[ 11 ] ;
241+ if attributes == directory_attributes:: LONG_NAME {
242+ let order = raw[ 0 ] ;
243+ let name_1 = & raw [ 1 ..11 ] ;
244+ let checksum = raw[ 13 ] ;
245+ let name_2 = & raw [ 14 ..26 ] ;
246+ let name_3 = & raw [ 28 ..32 ] ;
247+
248+ Ok ( Self :: LongName ( RawDirectoryEntryLongName {
249+ order,
250+ name_1,
251+ name_2,
252+ name_3,
253+ attributes,
254+ checksum,
255+ } ) )
256+ } else {
257+ fn slice_to_string ( slice : & [ u8 ] ) -> Result < & str , ( ) > {
258+ const SKIP_SPACE : u8 = 0x20 ;
259+ let mut iter = slice. into_iter ( ) . copied ( ) ;
260+ let start_idx = iter. position ( |c| c != SKIP_SPACE ) . ok_or ( ( ) ) ?;
261+ let end_idx = start_idx + iter. position ( |c| c == SKIP_SPACE ) . unwrap_or ( slice. len ( ) ) ;
262+
263+ core:: str:: from_utf8 ( & slice[ start_idx..end_idx] ) . map_err ( |_| ( ) )
264+ }
265+ let short_filename_main = slice_to_string ( & raw [ 0 ..8 ] ) ?;
266+ let short_filename_extension = slice_to_string ( & raw [ 8 ..11 ] ) ?;
267+ let first_cluster_hi = u16:: from_le_bytes ( raw[ 20 ..22 ] . try_into ( ) . unwrap ( ) ) ;
268+ let first_cluster_lo = u16:: from_le_bytes ( raw[ 26 ..28 ] . try_into ( ) . unwrap ( ) ) ;
269+ let first_cluster = ( ( first_cluster_hi as u32 ) << 16 ) | ( first_cluster_lo as u32 ) ;
270+ let file_size = u32:: from_le_bytes ( raw[ 28 ..32 ] . try_into ( ) . unwrap ( ) ) ;
271+ Ok ( Self :: Normal ( RawDirectoryEntryNormal {
272+ short_filename_main,
273+ short_filename_extension,
274+ attributes,
275+ first_cluster,
276+ file_size,
277+ } ) )
278+ }
279+ }
280+ }
281+
282+ mod directory_attributes {
283+ pub const READ_ONLY : u8 = 0x01 ;
284+ pub const HIDDEN : u8 = 0x02 ;
285+ pub const SYSTEM : u8 = 0x04 ;
286+ pub const VOLUME_ID : u8 = 0x08 ;
287+ pub const DIRECTORY : u8 = 0x10 ;
288+
289+ pub const LONG_NAME : u8 = READ_ONLY | HIDDEN | SYSTEM | VOLUME_ID ;
290+ }
0 commit comments