1
1
// based on https://crates.io/crates/mini_fat
2
2
3
- use crate :: disk:: Read ;
3
+ use crate :: {
4
+ disk:: { Read , Seek , SeekFrom } ,
5
+ screen,
6
+ } ;
7
+ use core:: { char:: DecodeUtf16Error , fmt:: Write as _} ;
4
8
5
9
const DIRECTORY_ENTRY_BYTES : usize = 32 ;
6
10
const UNUSED_ENTRY_PREFIX : u8 = 0xE5 ;
7
11
const END_OF_DIRECTORY_PREFIX : u8 = 0 ;
8
12
13
+ static mut BUFFER : [ u8 ; 0x4000 ] = [ 0 ; 0x4000 ] ;
14
+
9
15
pub struct File {
10
16
first_cluster : u32 ,
11
17
file_size : u32 ,
@@ -31,7 +37,7 @@ struct Bpb {
31
37
}
32
38
33
39
impl Bpb {
34
- fn parse < D : Read > ( disk : & mut D ) -> Self {
40
+ fn parse < D : Read + Seek > ( disk : & mut D ) -> Self {
35
41
let mut raw = [ 0u8 ; 512 ] ;
36
42
disk. read_exact ( & mut raw) ;
37
43
@@ -128,7 +134,7 @@ pub struct FileSystem<D> {
128
134
bpb : Bpb ,
129
135
}
130
136
131
- impl < D : Read > FileSystem < D > {
137
+ impl < D : Read + Seek > FileSystem < D > {
132
138
pub fn parse ( mut disk : D ) -> Self {
133
139
Self {
134
140
bpb : Bpb :: parse ( & mut disk) ,
@@ -137,17 +143,48 @@ impl<D: Read> FileSystem<D> {
137
143
}
138
144
139
145
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
+ }
140
165
todo ! ( ) ;
141
166
}
142
167
143
- fn read_root_dir ( & mut self ) {
168
+ fn read_root_dir < ' a > ( & ' a mut self ) -> impl Iterator < Item = Result < RawDirectoryEntry , ( ) > > + ' a {
144
169
match self . bpb . fat_type ( ) {
145
170
FatType :: Fat32 => {
146
171
self . bpb . root_cluster ;
172
+ unimplemented ! ( ) ;
147
173
}
148
174
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)
151
188
}
152
189
}
153
190
}
@@ -159,3 +196,95 @@ enum FatType {
159
196
Fat16 ,
160
197
Fat32 ,
161
198
}
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