ds_rom/rom/raw/
overlay.rs

1use std::{
2    fmt::Display,
3    mem::{align_of, size_of},
4};
5
6use bitfield_struct::bitfield;
7use bytemuck::{Pod, PodCastError, Zeroable};
8use snafu::{Backtrace, Snafu};
9
10use crate::rom::Arm9OverlaySignaturesError;
11
12use super::{RawArm9Error, RawHeaderError};
13
14/// An entry in an overlay table. This is the raw struct, see the plain one [here](super::super::Overlay).
15#[repr(C)]
16#[derive(Clone, Copy, Zeroable, Pod)]
17pub struct Overlay {
18    /// Overlay ID.
19    pub id: u32,
20    /// Base address.
21    pub base_addr: u32,
22    /// Initialized size.
23    pub code_size: u32,
24    /// Uninitialized size.
25    pub bss_size: u32,
26    /// Offset to start of .ctor section.
27    pub ctor_start: u32,
28    /// Offset to end of .ctor section.
29    pub ctor_end: u32,
30    /// File ID for the FAT.
31    pub file_id: u32,
32    /// Flags and compressed size.
33    pub flags: OverlayFlags,
34}
35
36/// Errors related to [`Overlay`].
37#[derive(Snafu, Debug)]
38pub enum RawOverlayError {
39    /// See [`RawHeaderError`].
40    #[snafu(transparent)]
41    RawHeader {
42        /// Source error.
43        source: RawHeaderError,
44    },
45    /// See [`RawHeaderError`].
46    #[snafu(transparent)]
47    RawArm9 {
48        /// Source error.
49        source: RawArm9Error,
50    },
51    /// See [`Arm9OverlaySignaturesError`].
52    #[snafu(transparent)]
53    Arm9OverlaySignatures {
54        /// Source error.
55        source: Arm9OverlaySignaturesError,
56    },
57    /// Occurs when the input is not evenly divisible into a slice of [`Overlay`].
58    #[snafu(display("the overlay table must be a multiple of {} bytes:\n{backtrace}", size_of::<Overlay>()))]
59    InvalidSize {
60        /// Backtrace to the source of the error.
61        backtrace: Backtrace,
62    },
63    /// Occurs when the input is less aligned than [`Overlay`].
64    #[snafu(display("expected {expected}-alignment for overlay table but got {actual}-alignment:\n{backtrace}"))]
65    Misaligned {
66        /// Expected alignment.
67        expected: usize,
68        /// Actual input alignment.
69        actual: usize,
70        /// Backtrace to the source of the error.
71        backtrace: Backtrace,
72    },
73}
74
75impl Overlay {
76    fn check_size(data: &[u8]) -> Result<(), RawOverlayError> {
77        let size = size_of::<Self>();
78        if data.len() % size != 0 {
79            InvalidSizeSnafu {}.fail()
80        } else {
81            Ok(())
82        }
83    }
84
85    fn handle_pod_cast<T>(result: Result<T, PodCastError>, addr: usize) -> Result<T, RawOverlayError> {
86        match result {
87            Ok(build_info) => Ok(build_info),
88            Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) => {
89                MisalignedSnafu { expected: align_of::<Self>(), actual: 1usize << addr.trailing_zeros() }.fail()
90            }
91            Err(PodCastError::AlignmentMismatch) => panic!(),
92            Err(PodCastError::OutputSliceWouldHaveSlop) => panic!(),
93            Err(PodCastError::SizeMismatch) => unreachable!(),
94        }
95    }
96
97    /// Reinterprets a `&[u8]` as a reference to [`Overlay`].
98    ///
99    /// # Errors
100    ///
101    /// This function will return an error if the input is the wrong size, or not aligned enough.
102    pub fn borrow_from_slice(data: &'_ [u8]) -> Result<&'_ [Self], RawOverlayError> {
103        Self::check_size(data)?;
104        let addr = data as *const [u8] as *const () as usize;
105        Self::handle_pod_cast(bytemuck::try_cast_slice(data), addr)
106    }
107
108    /// Creates a [`DisplayOverlay`] which implements [`Display`].
109    pub fn display(&self, indent: usize) -> DisplayOverlay {
110        DisplayOverlay { overlay: self, indent }
111    }
112}
113
114/// Can be used to display values in [`Overlay`].
115pub struct DisplayOverlay<'a> {
116    overlay: &'a Overlay,
117    indent: usize,
118}
119
120impl Display for DisplayOverlay<'_> {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        let i = " ".repeat(self.indent);
123        let overlay = &self.overlay;
124        writeln!(f, "{i}ID ............... : {}", overlay.id)?;
125        writeln!(f, "{i}File ID .......... : {}", overlay.file_id)?;
126        writeln!(f, "{i}Base address ..... : {:#x}", overlay.base_addr)?;
127        writeln!(f, "{i}Code size ........ : {:#x}", overlay.code_size)?;
128        writeln!(f, "{i}.bss size ........ : {:#x}", overlay.bss_size)?;
129        writeln!(f, "{i}.ctor start ...... : {:#x}", overlay.ctor_start)?;
130        writeln!(f, "{i}.ctor end ........ : {:#x}", overlay.ctor_end)?;
131        writeln!(f, "{i}Compressed size .. : {:#x}", overlay.flags.size())?;
132        writeln!(f, "{i}Is compressed .... : {}", overlay.flags.is_compressed())?;
133        writeln!(f, "{i}Is signed ........ : {}", overlay.flags.is_signed())?;
134        writeln!(f, "{i}Reserved flags ... : {:#x}", overlay.flags.reserved())?;
135        Ok(())
136    }
137}
138
139/// Overlay flags and compressed size.
140#[bitfield(u32)]
141pub struct OverlayFlags {
142    /// Compressed size, zero if not compressed.
143    #[bits(24)]
144    pub size: usize,
145    pub is_compressed: bool,
146    pub is_signed: bool,
147    #[bits(6)]
148    pub reserved: u8,
149}
150
151unsafe impl Zeroable for OverlayFlags {}
152unsafe impl Pod for OverlayFlags {}