ds_rom/rom/raw/
arm9_footer.rs1use std::mem::{align_of, size_of};
2
3use bytemuck::{Pod, PodCastError, Zeroable};
4use snafu::{Backtrace, Snafu};
5
6use super::{RawHeaderError, NITROCODE};
7
8#[repr(C)]
10#[derive(Clone, Copy, Zeroable, Pod)]
11pub struct Arm9Footer {
12 nitrocode: u32,
13 pub build_info_offset: u32,
15 reserved: u32,
16}
17
18#[derive(Debug, Snafu)]
20pub enum Arm9FooterError {
21 #[snafu(transparent)]
23 RawHeader {
24 source: RawHeaderError,
26 },
27 #[snafu(display("ARM9 footer must be {expected} bytes but got {actual} bytes:\n{backtrace}"))]
29 WrongSize {
30 expected: usize,
32 actual: usize,
34 backtrace: Backtrace,
36 },
37 #[snafu(display("expected {expected}-alignment for ARM9 footer but got {actual}-alignment:\n{backtrace}"))]
39 Misaligned {
40 expected: usize,
42 actual: usize,
44 backtrace: Backtrace,
46 },
47 #[snafu(display("expected nitrocode {expected:#x} in ARM9 footer but got {actual:#x}:\n{backtrace}"))]
49 NoNitrocode {
50 expected: u32,
52 actual: u32,
54 backtrace: Backtrace,
56 },
57}
58
59impl Arm9Footer {
60 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 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 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}