Skip to main content

ds_rom/rom/raw/
fat.rs

1use std::{
2    mem::{align_of, size_of},
3    ops::Range,
4};
5
6use bytemuck::{Pod, PodCastError, Zeroable};
7use snafu::{Backtrace, Snafu};
8
9use super::RawHeaderError;
10
11/// A file allocation which tells where a file starts and ends in the ROM.
12#[repr(C)]
13#[derive(Clone, Copy, Zeroable, Pod, Default)]
14pub struct FileAlloc {
15    /// Start offset.
16    pub start: u32,
17    /// End offset.
18    pub end: u32,
19}
20
21/// Errors related to [`FileAlloc`].
22#[derive(Debug, Snafu)]
23pub enum RawFatError {
24    /// See [`RawHeaderError`].
25    #[snafu(transparent)]
26    RawHeader {
27        /// Source error.
28        source: RawHeaderError,
29    },
30    /// Occurs when the input is not evenly divisible into a slice of [`FileAlloc`].
31    #[snafu(display("file allocation table must be a multiple of {} bytes", size_of::<FileAlloc>()))]
32    InvalidSize {
33        /// Backtrace to the source of the error.
34        backtrace: Backtrace,
35    },
36    /// Occurs when the input is less aligned than [`FileAlloc`].
37    #[snafu(display("expected {expected}-alignment for autoload infos but got {actual}-alignment:\n{backtrace}"))]
38    Misaligned {
39        /// Expected alignment.
40        expected: usize,
41        /// Actual input alignment.
42        actual: usize,
43        /// Backtrace to the source of the error.
44        backtrace: Backtrace,
45    },
46}
47
48impl FileAlloc {
49    fn check_size(data: &'_ [u8]) -> Result<(), RawFatError> {
50        let size = size_of::<Self>();
51        if !data.len().is_multiple_of(size) {
52            InvalidSizeSnafu {}.fail()
53        } else {
54            Ok(())
55        }
56    }
57
58    fn handle_pod_cast<T>(result: Result<T, PodCastError>, addr: usize) -> Result<T, RawFatError> {
59        match result {
60            Ok(build_info) => Ok(build_info),
61            Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) => {
62                MisalignedSnafu { expected: align_of::<Self>(), actual: 1usize << addr.trailing_zeros() }.fail()
63            }
64            Err(PodCastError::AlignmentMismatch) => panic!(),
65            Err(PodCastError::OutputSliceWouldHaveSlop) => panic!(),
66            Err(PodCastError::SizeMismatch) => unreachable!(),
67        }
68    }
69
70    /// Reinterprets a `&[u8]` as a reference to [`FileAlloc`].
71    ///
72    /// # Errors
73    ///
74    /// This function will return an error if the input the wrong size or not aligned enough.
75    pub fn borrow_from_slice(data: &'_ [u8]) -> Result<&'_ [Self], RawFatError> {
76        Self::check_size(data)?;
77        let addr = data as *const [u8] as *const () as usize;
78        Self::handle_pod_cast(bytemuck::try_cast_slice(data), addr)
79    }
80
81    /// Returns the contents of this file taken directly from the ROM.
82    pub fn into_file(self, rom: &[u8]) -> &[u8] {
83        &rom[self.start as usize..self.end as usize]
84    }
85
86    /// Returns a ROM offset [`Range`] for this file.
87    pub fn range(self) -> Range<usize> {
88        self.start as usize..self.end as usize
89    }
90}