ds_rom/rom/raw/
arm9_footer.rs

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