Skip to main content

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