ds_rom/rom/raw/
overlay.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use std::{
    fmt::Display,
    mem::{align_of, size_of},
    usize,
};

use bitfield_struct::bitfield;
use bytemuck::{Pod, PodCastError, Zeroable};
use snafu::{Backtrace, Snafu};

use super::RawHeaderError;

/// An entry in an overlay table. This is the raw struct, see the plain one [here](super::super::Overlay).
#[repr(C)]
#[derive(Clone, Copy, Zeroable, Pod)]
pub struct Overlay {
    /// Overlay ID.
    pub id: u32,
    /// Base address.
    pub base_addr: u32,
    /// Initialized size.
    pub code_size: u32,
    /// Uninitialized size.
    pub bss_size: u32,
    /// Offset to start of .ctor section.
    pub ctor_start: u32,
    /// Offset to end of .ctor section.
    pub ctor_end: u32,
    /// File ID for the FAT.
    pub file_id: u32,
    /// Compressed size.
    pub compressed: OverlayCompressedSize,
}

/// Errors related to [`Overlay`].
#[derive(Snafu, Debug)]
pub enum RawOverlayError {
    /// See [`RawHeaderError`].
    #[snafu(transparent)]
    RawHeader {
        /// Source error.
        source: RawHeaderError,
    },
    /// Occurs when the input is not evenly divisible into a slice of [`Overlay`].
    #[snafu(display("the overlay table must be a multiple of {} bytes:\n{backtrace}", size_of::<Overlay>()))]
    InvalidSize {
        /// Backtrace to the source of the error.
        backtrace: Backtrace,
    },
    /// Occurs when the input is less aligned than [`Overlay`].
    #[snafu(display("expected {expected}-alignment for overlay table but got {actual}-alignment:\n{backtrace}"))]
    Misaligned {
        /// Expected alignment.
        expected: usize,
        /// Actual input alignment.
        actual: usize,
        /// Backtrace to the source of the error.
        backtrace: Backtrace,
    },
}

impl Overlay {
    fn check_size(data: &[u8]) -> Result<(), RawOverlayError> {
        let size = size_of::<Self>();
        if data.len() % size != 0 {
            InvalidSizeSnafu {}.fail()
        } else {
            Ok(())
        }
    }

    fn handle_pod_cast<T>(result: Result<T, PodCastError>, addr: usize) -> Result<T, RawOverlayError> {
        match result {
            Ok(build_info) => Ok(build_info),
            Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) => {
                MisalignedSnafu { expected: align_of::<Self>(), actual: 1usize << addr.trailing_zeros() }.fail()
            }
            Err(PodCastError::AlignmentMismatch) => panic!(),
            Err(PodCastError::OutputSliceWouldHaveSlop) => panic!(),
            Err(PodCastError::SizeMismatch) => unreachable!(),
        }
    }

    /// Reinterprets a `&[u8]` as a reference to [`Overlay`].
    ///
    /// # Errors
    ///
    /// This function will return an error if the input is the wrong size, or not aligned enough.
    pub fn borrow_from_slice(data: &'_ [u8]) -> Result<&'_ [Self], RawOverlayError> {
        Self::check_size(data)?;
        let addr = data as *const [u8] as *const () as usize;
        Self::handle_pod_cast(bytemuck::try_cast_slice(&data), addr)
    }

    /// Creates a [`DisplayOverlay`] which implements [`Display`].
    pub fn display(&self, indent: usize) -> DisplayOverlay {
        DisplayOverlay { overlay: self, indent }
    }
}

/// Can be used to display values in [`Overlay`].
pub struct DisplayOverlay<'a> {
    overlay: &'a Overlay,
    indent: usize,
}

impl<'a> Display for DisplayOverlay<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let i = format!("{:indent$}", "", indent = self.indent);
        let overlay = &self.overlay;
        writeln!(f, "{i}ID ............... : {}", overlay.id)?;
        writeln!(f, "{i}File ID .......... : {}", overlay.file_id)?;
        writeln!(f, "{i}Base address ..... : {:#x}", overlay.base_addr)?;
        writeln!(f, "{i}Code size ........ : {:#x}", overlay.code_size)?;
        writeln!(f, "{i}.bss size ........ : {:#x}", overlay.bss_size)?;
        writeln!(f, "{i}.ctor start ...... : {:#x}", overlay.ctor_start)?;
        writeln!(f, "{i}.ctor end ........ : {:#x}", overlay.ctor_end)?;
        writeln!(f, "{i}Compressed size .. : {:#x}", overlay.compressed.size())?;
        writeln!(f, "{i}Is compressed .... : {}", overlay.compressed.is_compressed() != 0)?;
        Ok(())
    }
}

/// Overlay compressed size bitfield.
#[bitfield(u32)]
pub struct OverlayCompressedSize {
    /// Compressed size, zero if not compressed.
    #[bits(24)]
    pub size: usize,
    pub is_compressed: u8,
}

unsafe impl Zeroable for OverlayCompressedSize {}
unsafe impl Pod for OverlayCompressedSize {}