ds_rom/rom/raw/
arm9_footer.rs

1use std::mem::{align_of, size_of};
2
3use bytemuck::{Pod, PodCastError, Zeroable};
4use snafu::{Backtrace, Snafu};
5
6use super::{RawHeaderError, NITROCODE};
7
8/// Footer of the ARM9 program.
9#[repr(C)]
10#[derive(Clone, Copy, Zeroable, Pod)]
11pub struct Arm9Footer {
12    nitrocode: u32,
13    /// Offset to [super::BuildInfo].
14    pub build_info_offset: u32,
15    reserved: u32,
16}
17
18/// Errors related to [`Arm9Footer`].
19#[derive(Debug, Snafu)]
20pub enum Arm9FooterError {
21    /// See [RawHeaderError].
22    #[snafu(transparent)]
23    RawHeader {
24        /// Source error.
25        source: RawHeaderError,
26    },
27    /// Occurs when the given input is not the expected size for an ARM9 footer.
28    #[snafu(display("ARM9 footer must be {expected} bytes but got {actual} bytes:\n{backtrace}"))]
29    WrongSize {
30        /// Expected ARM9 footer size.
31        expected: usize,
32        /// Actual input size.
33        actual: usize,
34        /// Backtrace to the source of the error.
35        backtrace: Backtrace,
36    },
37    /// Occurs when the given input less aligned than [Arm9Footer].
38    #[snafu(display("expected {expected}-alignment for ARM9 footer but got {actual}-alignment:\n{backtrace}"))]
39    Misaligned {
40        /// Expected alignment.
41        expected: usize,
42        /// Actual input alignment.
43        actual: usize,
44        /// Backtrace to the source of the error.
45        backtrace: Backtrace,
46    },
47    /// Occurs when nitrocode was not found in the input.
48    #[snafu(display("expected nitrocode {expected:#x} in ARM9 footer but got {actual:#x}:\n{backtrace}"))]
49    NoNitrocode {
50        /// Expected value.
51        expected: u32,
52        /// Actual input value.
53        actual: u32,
54        /// Backtrace to the source of the error.
55        backtrace: Backtrace,
56    },
57}
58
59impl Arm9Footer {
60    /// Creates a new [`Arm9Footer`].
61    pub fn new(build_info_offset: u32) -> Self {
62        Self { nitrocode: NITROCODE, build_info_offset, reserved: 0 }
63    }
64
65    fn check_size(data: &'_ [u8]) -> Result<(), Arm9FooterError> {
66        if data.len() != size_of::<Self>() {
67            WrongSizeSnafu { expected: size_of::<Self>(), actual: data.len() }.fail()
68        } else {
69            Ok(())
70        }
71    }
72
73    fn handle_pod_cast<T>(result: Result<T, PodCastError>, addr: usize) -> Result<T, Arm9FooterError> {
74        match result {
75            Ok(build_info) => Ok(build_info),
76            Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) => {
77                MisalignedSnafu { expected: align_of::<Self>(), actual: 1usize << addr.trailing_zeros() }.fail()
78            }
79            Err(PodCastError::AlignmentMismatch) => panic!(),
80            Err(PodCastError::OutputSliceWouldHaveSlop) => panic!(),
81            Err(PodCastError::SizeMismatch) => unreachable!(),
82        }
83    }
84
85    fn check_nitrocode(&self) -> Result<(), Arm9FooterError> {
86        if self.nitrocode != NITROCODE {
87            NoNitrocodeSnafu { expected: NITROCODE, actual: self.nitrocode }.fail()
88        } else {
89            Ok(())
90        }
91    }
92
93    /// Reinterprets a `&[u8]` as a reference to [`Arm9Footer`].
94    ///
95    /// # Errors
96    ///
97    /// This function will return an error if the input has the wrong size or alignment, or doesn't contain the nitrocode.
98    pub fn borrow_from_slice(data: &'_ [u8]) -> Result<&'_ Self, Arm9FooterError> {
99        Self::check_size(data)?;
100        let addr = data as *const [u8] as *const () as usize;
101        let footer: &Self = Self::handle_pod_cast(bytemuck::try_from_bytes(data), addr)?;
102        footer.check_nitrocode()?;
103        Ok(footer)
104    }
105
106    /// Reinterprets a `&mut [u8]` as a mutable reference to [`Arm9Footer`].
107    ///
108    /// # Errors
109    ///
110    /// This function will return an error if the input has the wrong size or alignment, or doesn't contain the nitrocode.
111    pub fn borrow_from_slice_mut(data: &'_ mut [u8]) -> Result<&'_ mut Self, Arm9FooterError> {
112        Self::check_size(data)?;
113        let addr = data as *const [u8] as *const () as usize;
114        let footer: &mut Self = Self::handle_pod_cast(bytemuck::try_from_bytes_mut(data), addr)?;
115        footer.check_nitrocode()?;
116        Ok(footer)
117    }
118}