inky_frame/fs/volume/
name.rs

1// Permission is hereby granted, free of charge, to any person obtaining a copy
2// of this software and associated documentation files (the "Software"), to deal
3// in the Software without restriction, including without limitation the rights
4// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
5// copies of the Software, and to permit persons to whom the Software is
6// furnished to do so, subject to the following conditions:
7//
8// The above copyright notice and this permission notice shall be included in
9// all copies or substantial portions of the Software.
10//
11// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17// SOFTWARE.
18//
19
20#![no_implicit_prelude]
21
22extern crate core;
23extern crate rpsp;
24
25use core::cell::UnsafeCell;
26use core::clone::Clone;
27use core::cmp::{self, PartialEq};
28use core::convert::AsRef;
29use core::default::Default;
30use core::iter::Iterator;
31use core::marker::Sync;
32use core::mem::forget;
33use core::ops::{Deref, DerefMut, Drop};
34use core::option::Option::{None, Some};
35use core::ptr::NonNull;
36use core::result::Result::{self, Err, Ok};
37use core::str::from_utf8_unchecked;
38use core::unreachable;
39
40use rpsp::locks::Spinlock30;
41
42use crate::fs::{DeviceError, FatVersion};
43
44// Shared References are annoying, but this is the best way to handle this as
45// the Pico does not like this struct when it's 255 bytes in size, so we'll
46// just share it and pray there's never any concurrent access lol.
47//
48// Actually, we'll use Spinlock30 to sync this. So well 'own' the Spinlock when
49// we do 'LongNamePtr::new' and release it when the 'LongNamePtr' object is
50// dropped.
51static CACHE: Cache = Cache::new();
52
53pub struct LongName(pub(super) [u8; LongName::SIZE]);
54pub struct ShortName(pub(super) [u8; ShortName::SIZE]);
55pub struct VolumeName(pub(super) [u8; VolumeName::SIZE]);
56
57pub(super) struct LongNamePtr(NonNull<LongName>);
58
59struct Cache(UnsafeCell<LongName>);
60
61impl Cache {
62    #[inline(always)]
63    const fn new() -> Cache {
64        Cache(UnsafeCell::new(LongName::empty()))
65    }
66}
67impl LongName {
68    pub const SIZE: usize = 0xFFusize;
69
70    #[inline(always)]
71    pub const fn empty() -> LongName {
72        LongName([0u8; LongName::SIZE])
73    }
74
75    #[inline]
76    pub fn from_slice_truncate(v: &[u8]) -> LongName {
77        let mut n = LongName([0x20u8; LongName::SIZE]);
78        n.fill_inner(v);
79        n
80    }
81    #[inline]
82    pub fn from_str_truncate(v: impl AsRef<str>) -> LongName {
83        LongName::from_slice_truncate(v.as_ref().as_bytes())
84    }
85    #[inline]
86    pub fn from_slice(v: &[u8]) -> Result<LongName, DeviceError> {
87        if v.len() > LongName::SIZE {
88            Err(DeviceError::NameTooLong)
89        } else {
90            Ok(LongName::from_slice_truncate(v))
91        }
92    }
93    #[inline]
94    pub fn from_str(v: impl AsRef<str>) -> Result<LongName, DeviceError> {
95        if v.as_ref().len() > LongName::SIZE {
96            Err(DeviceError::NameTooLong)
97        } else {
98            Ok(LongName::from_str_truncate(v))
99        }
100    }
101
102    #[inline(always)]
103    pub fn len(&self) -> usize {
104        self.0.iter().position(|v| *v == 0).unwrap_or(LongName::SIZE)
105    }
106    #[inline(always)]
107    pub fn as_str(&self) -> &str {
108        unsafe { from_utf8_unchecked(&self.0[0..self.0.iter().position(|v| *v == 0).unwrap_or(LongName::SIZE)]) }
109    }
110    pub fn lfn_size(&self) -> u8 {
111        let mut r = self.len();
112        if r <= 13 {
113            return 1;
114        }
115        r += 1; // NULL CHAR
116        let c = r / 0xC;
117        if (c * 0xC) == r {
118            return c as u8;
119        }
120        if r > 0xC {
121            r += 1; // ADD PAD
122        }
123        (r / 0xC) as u8 + 1
124    }
125    #[inline(always)]
126    pub fn is_self(&self) -> bool {
127        self.0[0] == b'.' && self.0[1] == 0
128    }
129    #[inline(always)]
130    pub fn is_empty(&self) -> bool {
131        self.0[0] == 0
132    }
133    #[inline(always)]
134    pub fn as_bytes(&self) -> &[u8] {
135        &self.0[0..self.0.iter().position(|v| *v == 0).unwrap_or(LongName::SIZE)]
136    }
137    #[inline(always)]
138    pub fn is_parent(&self) -> bool {
139        self.0[0] == b'.' && self.0[1] == b'.' && self.0[2] == 0
140    }
141    #[inline]
142    pub fn fill(&mut self, v: &[u8]) -> Result<(), DeviceError> {
143        if v.len() > LongName::SIZE {
144            return Err(DeviceError::NameTooLong);
145        }
146        self.fill_inner(v);
147        Ok(())
148    }
149    #[inline]
150    pub fn fill_str(&mut self, v: impl AsRef<str>) -> Result<(), DeviceError> {
151        self.fill(v.as_ref().as_bytes())
152    }
153
154    #[inline(always)]
155    pub(super) fn reset(&mut self) {
156        self.0.fill(0)
157    }
158    pub(super) fn fill_lfn(&mut self, b: &[u8]) -> u8 {
159        if b.len() < 0x20 {
160            return 0u8;
161        }
162        let v = ((b[0] & 0x1F) as usize - 1) * 0xD;
163        for i in 0..0xD {
164            if i + v >= LongName::SIZE {
165                break;
166            }
167            let c = b[LongName::pos_to_lfn(i)];
168            if c == 0 {
169                break;
170            }
171            self.0[v + i] = c;
172        }
173        b[0xD]
174    }
175
176    #[inline(always)]
177    pub(super) fn pos_to_lfn(v: usize) -> usize {
178        match v {
179            0 => 1,
180            1 => 3,
181            2 => 5,
182            3 => 7,
183            4 => 9,
184            5 => 14,
185            6 => 16,
186            7 => 18,
187            8 => 20,
188            9 => 22,
189            10 => 24,
190            11 => 28,
191            12 => 30,
192            _ => unreachable!(),
193        }
194    }
195
196    #[inline]
197    fn fill_inner(&mut self, v: &[u8]) {
198        if v.len() < LongName::SIZE {
199            self.0[0..v.len()].copy_from_slice(v)
200        } else {
201            self.0.copy_from_slice(&v[0..LongName::SIZE]);
202        }
203    }
204}
205impl ShortName {
206    pub const SIZE: usize = 0xBusize;
207    pub const SIZE_EXT: usize = 0x3usize;
208    pub const SIZE_NAME: usize = 0x8usize;
209
210    pub const SELF: ShortName = ShortName([0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20]);
211    pub const PARENT: ShortName = ShortName([0x2E, 0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20]);
212
213    #[inline]
214    pub fn empty() -> ShortName {
215        ShortName([0x20u8; ShortName::SIZE])
216    }
217    #[inline]
218    pub fn from_slice(v: &[u8]) -> ShortName {
219        let mut n = ShortName([0x20u8; ShortName::SIZE]);
220        n.fill(v);
221        n
222    }
223    #[inline]
224    pub fn from_str(v: impl AsRef<str>) -> ShortName {
225        ShortName::from_slice(v.as_ref().as_bytes())
226    }
227
228    #[inline]
229    pub fn name(&self) -> &str {
230        unsafe {
231            from_utf8_unchecked(
232                &self.0[0..self.0[0..ShortName::SIZE_NAME]
233                    .iter()
234                    .position(|v| *v == 0x20)
235                    .unwrap_or(ShortName::SIZE_NAME)],
236            )
237        }
238    }
239    #[inline]
240    pub fn as_str(&self) -> &str {
241        let i = self.extension_len();
242        if i == 0 {
243            self.name()
244        } else {
245            unsafe { from_utf8_unchecked(&self.0[0..ShortName::SIZE_NAME + i]) }
246        }
247    }
248    #[inline]
249    pub fn checksum(&self) -> u8 {
250        let mut s = 0u8;
251        for i in self.0.iter() {
252            s = ((s & 1) << 7).wrapping_add((s >> 1) + *i);
253        }
254        s
255    }
256    #[inline(always)]
257    pub fn is_self(&self) -> bool {
258        self.0.eq(&ShortName::SELF.0)
259    }
260    #[inline(always)]
261    pub fn is_parent(&self) -> bool {
262        self.0.eq(&ShortName::PARENT.0)
263    }
264    #[inline(always)]
265    pub fn as_bytes(&self) -> &[u8] {
266        &self.0
267    }
268    #[inline(always)]
269    pub fn extension(&self) -> &str {
270        unsafe { from_utf8_unchecked(&self.0[ShortName::SIZE_NAME..ShortName::SIZE_NAME + self.extension_len()]) }
271    }
272    #[inline(always)]
273    pub fn fill(&mut self, v: &[u8]) {
274        ShortName::to_sfn(v, self);
275    }
276    #[inline]
277    pub fn fill_str(&mut self, v: impl AsRef<str>) {
278        self.fill(v.as_ref().as_bytes());
279    }
280
281    #[inline(always)]
282    pub(super) fn fill_inner(&mut self, v: &[u8]) {
283        self.0.copy_from_slice(&v[0..ShortName::SIZE])
284    }
285
286    #[inline(always)]
287    fn transform_char(v: u8) -> u8 {
288        match v {
289            0x00..=0x1F | 0x20 | 0x22 | 0x2A | 0x2B | 0x2C | 0x2F | 0x3A | 0x3B | 0x3C | 0x3D | 0x3E | 0x3F | 0x5B | 0x5C | 0x5D | 0x7C => b'+',
290            v if v >= b'a' && v <= b'z' => v - 0x20,
291            v => v,
292        }
293    }
294    fn to_sfn(b: &[u8], sfn: &mut ShortName) {
295        // Look for SELF and PARENT names
296        match b.len() {
297            0 => return,
298            1 if b[0] == b'.' => {
299                sfn.0.copy_from_slice(&ShortName::SELF.0);
300                return;
301            },
302            2 if b[0] == b'.' && b[1] == b'.' => {
303                sfn.0.copy_from_slice(&ShortName::PARENT.0);
304                return;
305            },
306            _ => (),
307        }
308        // Extract the extension first, if there is one.
309        // 'n' is the end of the 'name'.
310        let n = match b.iter().rposition(|v| *v == b'.') {
311            Some(i) => {
312                // We drop any chars more than 3 for the extension.
313                match b.len().saturating_sub(i + 1) {
314                    0 => (),
315                    1 => sfn.0[ShortName::SIZE_NAME] = b[i + 1],
316                    2 => {
317                        sfn.0[ShortName::SIZE_NAME] = b[i + 1];
318                        sfn.0[ShortName::SIZE_NAME + 1] = b[i + 2];
319                    },
320                    _ => sfn.0[ShortName::SIZE_NAME..ShortName::SIZE].copy_from_slice(&b[i + 1..i + 4]),
321                }
322                i
323            },
324            None => b.len(),
325        };
326        if n < ShortName::SIZE_NAME {
327            sfn.0[0..n].copy_from_slice(&b[0..n]);
328            sfn.0[n..ShortName::SIZE_NAME].fill(0x20)
329        } else if n == ShortName::SIZE_NAME {
330            sfn.0[0..ShortName::SIZE_NAME].copy_from_slice(&b[0..ShortName::SIZE_NAME])
331        } else {
332            sfn.0[0..ShortName::SIZE_NAME - 2].copy_from_slice(&b[0..ShortName::SIZE_NAME - 2]);
333            // NOTE(sf): We don't really know the "number" of files contained, like
334            //           we could read the dir first before setting this last bit
335            //           (or after setting it).
336            // NOTE(sf): ^ Would be a lot of work tbh.
337            sfn.0[ShortName::SIZE_NAME - 2] = b'~';
338            sfn.0[ShortName::SIZE_NAME - 1] = b'1';
339        }
340        // Flatten any spaces to '_'. We don't flatten spaces in extensions.
341        // This only goes the length of the original string, so the empty
342        // bytes stay as spaces.
343        for i in 0..cmp::min(n, ShortName::SIZE_NAME) {
344            if sfn.0[i] == 0x20 {
345                sfn.0[i] = b'_'
346            }
347        }
348        // Last, make sure every a-z is capitalized.
349        for i in sfn.0[0..ShortName::SIZE_NAME].iter_mut() {
350            *i = ShortName::transform_char(*i)
351        }
352    }
353
354    #[inline(always)]
355    fn extension_len(&self) -> usize {
356        match (
357            self.0[ShortName::SIZE_NAME],
358            self.0[ShortName::SIZE_NAME + 1],
359            self.0[ShortName::SIZE_NAME + 2],
360        ) {
361            (0x20, 0x20, 0x20) => 0,
362            (_, 0x20, 0x20) => 1,
363            (_, _, 0x20) => 2,
364            (..) => 3,
365        }
366    }
367}
368impl VolumeName {
369    pub const SIZE: usize = 0xBusize;
370
371    #[inline]
372    pub fn empty() -> VolumeName {
373        VolumeName([0x20u8; VolumeName::SIZE])
374    }
375
376    #[inline]
377    pub(super) fn from_slice(f: &FatVersion, v: &[u8]) -> VolumeName {
378        let i = match f {
379            FatVersion::Fat16(_) => 0x2B,
380            FatVersion::Fat32(_) => 0x47,
381        };
382        let mut n = VolumeName([0x20u8; VolumeName::SIZE]);
383        if (v.len().saturating_sub(i)) < VolumeName::SIZE {
384            n.0[0..(v.len().saturating_sub(i))].copy_from_slice(v)
385        } else {
386            n.0.copy_from_slice(&v[i..VolumeName::SIZE + i]);
387        }
388        n
389    }
390
391    #[inline(always)]
392    pub fn as_str(&self) -> &str {
393        unsafe { from_utf8_unchecked(&self.0[0..self.0.iter().position(|v| *v == 0x20).unwrap_or(VolumeName::SIZE)]) }
394    }
395    #[inline(always)]
396    pub fn as_bytes(&self) -> &[u8] {
397        &self.0
398    }
399}
400impl LongNamePtr {
401    #[inline(always)]
402    pub(super) fn new() -> LongNamePtr {
403        let c = Spinlock30::claim();
404        // Claim and 'forget' the lock.
405        forget(c);
406        // Now nobody else can create this struct until we drop it.
407        LongNamePtr(unsafe { NonNull::new_unchecked(CACHE.0.get()) })
408    }
409}
410
411impl PartialEq for ShortName {
412    #[inline(always)]
413    fn eq(&self, other: &ShortName) -> bool {
414        self.0.eq(&other.0)
415    }
416}
417impl PartialEq<str> for ShortName {
418    #[inline(always)]
419    fn eq(&self, other: &str) -> bool {
420        self.eq(other.as_bytes())
421    }
422}
423impl PartialEq<[u8]> for ShortName {
424    #[inline]
425    fn eq(&self, other: &[u8]) -> bool {
426        let mut v = ShortName::empty();
427        v.fill(other);
428        v.0.eq(&self.0)
429    }
430}
431
432impl PartialEq for LongName {
433    #[inline(always)]
434    fn eq(&self, other: &LongName) -> bool {
435        self.0.eq(&other.0)
436    }
437}
438impl PartialEq<str> for LongName {
439    #[inline(always)]
440    fn eq(&self, other: &str) -> bool {
441        self.eq(other.as_bytes())
442    }
443}
444impl PartialEq<[u8]> for LongName {
445    #[inline(always)]
446    fn eq(&self, other: &[u8]) -> bool {
447        self.as_bytes().eq(other)
448    }
449}
450
451impl Deref for LongName {
452    type Target = [u8];
453
454    #[inline(always)]
455    fn deref(&self) -> &[u8] {
456        self.as_bytes()
457    }
458}
459impl AsRef<str> for LongName {
460    #[inline(always)]
461    fn as_ref(&self) -> &str {
462        self.as_str()
463    }
464}
465
466impl Clone for ShortName {
467    #[inline(always)]
468    fn clone(&self) -> ShortName {
469        ShortName(self.0.clone())
470    }
471}
472impl Deref for ShortName {
473    type Target = [u8];
474
475    #[inline(always)]
476    fn deref(&self) -> &[u8] {
477        &self.0
478    }
479}
480impl AsRef<str> for ShortName {
481    #[inline(always)]
482    fn as_ref(&self) -> &str {
483        self.as_str()
484    }
485}
486
487impl Clone for VolumeName {
488    #[inline(always)]
489    fn clone(&self) -> VolumeName {
490        VolumeName(self.0.clone())
491    }
492}
493impl Deref for VolumeName {
494    type Target = [u8];
495
496    #[inline(always)]
497    fn deref(&self) -> &[u8] {
498        self.as_bytes()
499    }
500}
501impl Default for VolumeName {
502    #[inline(always)]
503    fn default() -> VolumeName {
504        VolumeName::empty()
505    }
506}
507impl AsRef<str> for VolumeName {
508    #[inline(always)]
509    fn as_ref(&self) -> &str {
510        self.as_str()
511    }
512}
513
514impl Drop for LongNamePtr {
515    #[inline(always)]
516    fn drop(&mut self) {
517        unsafe { Spinlock30::free() }
518    }
519}
520impl Deref for LongNamePtr {
521    type Target = LongName;
522
523    #[inline(always)]
524    fn deref(&self) -> &LongName {
525        unsafe { self.0.as_ref() }
526    }
527}
528impl DerefMut for LongNamePtr {
529    #[inline(always)]
530    fn deref_mut(&mut self) -> &mut LongName {
531        unsafe { &mut *self.0.as_ptr() }
532    }
533}
534
535unsafe impl Sync for Cache {}
536
537#[cfg(feature = "debug")]
538mod display {
539    extern crate core;
540
541    use core::fmt::{Debug, Display, Formatter, Result, Write};
542    use core::iter::Iterator;
543    use core::result::Result::Ok;
544
545    use crate::fs::{LongName, ShortName, VolumeName};
546
547    impl Debug for LongName {
548        #[inline(always)]
549        fn fmt(&self, f: &mut Formatter<'_>) -> Result {
550            f.write_str(self.as_str())
551        }
552    }
553    impl Display for LongName {
554        #[inline(always)]
555        fn fmt(&self, f: &mut Formatter<'_>) -> Result {
556            f.write_str(self.as_str())
557        }
558    }
559
560    impl Debug for ShortName {
561        fn fmt(&self, f: &mut Formatter<'_>) -> Result {
562            for (i, v) in self.0.iter().enumerate() {
563                if i == 8 {
564                    f.write_char('.')?;
565                }
566                if *v == 0x20 {
567                    f.write_char('.')?;
568                } else {
569                    f.write_char(*v as _)?;
570                }
571            }
572            Ok(())
573        }
574    }
575    impl Display for ShortName {
576        #[inline]
577        fn fmt(&self, f: &mut Formatter<'_>) -> Result {
578            f.write_str(self.name())?;
579            f.write_char('.')?;
580            f.write_str(self.extension())
581        }
582    }
583
584    impl Debug for VolumeName {
585        #[inline(always)]
586        fn fmt(&self, f: &mut Formatter<'_>) -> Result {
587            f.write_str(self.as_str())
588        }
589    }
590    impl Display for VolumeName {
591        #[inline(always)]
592        fn fmt(&self, f: &mut Formatter<'_>) -> Result {
593            f.write_str(self.as_str())
594        }
595    }
596}