ds_rom/rom/raw/
build_info.rs1use 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#[repr(C)]
13#[derive(Clone, Copy, Zeroable, Pod)]
14pub struct BuildInfo {
15 pub autoload_infos_start: u32,
17 pub autoload_infos_end: u32,
19 pub autoload_blocks: u32,
21 pub bss_start: u32,
23 pub bss_end: u32,
25 pub compressed_code_end: u32,
27 pub sdk_version: u32,
29 nitrocode: u32,
30 nitrocode_rev: u32,
31}
32
33#[derive(Debug, Snafu)]
35pub enum RawBuildInfoError {
36 #[snafu(transparent)]
38 RawHeader {
39 source: RawHeaderError,
41 },
42 #[snafu(display("expected {expected:#x} bytes for build info but had only {actual:#x}:\n{backtrace}"))]
44 DataTooSmall {
45 expected: usize,
47 actual: usize,
49 backtrace: Backtrace,
51 },
52 #[snafu(display("expected {expected}-alignment for build info but got {actual}-alignment:\n{backtrace}"))]
54 Misaligned {
55 expected: usize,
57 actual: usize,
59 backtrace: Backtrace,
61 },
62 #[snafu(display("expected nitrocode {expected:#x} at the end of build info but got {actual:#x}:\n{backtrace}"))]
64 NoNitrocode {
65 expected: u32,
67 actual: u32,
69 backtrace: Backtrace,
71 },
72}
73
74impl BuildInfo {
75 fn check_size(data: &'_ [u8]) -> Result<(), RawBuildInfoError> {
76 let size = size_of::<Self>();
77 if data.len() < size {
78 DataTooSmallSnafu { expected: size, actual: data.len() }.fail()
79 } else {
80 Ok(())
81 }
82 }
83
84 fn handle_pod_cast<T>(result: Result<T, PodCastError>, addr: usize) -> Result<T, RawBuildInfoError> {
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 fn check_nitrocode(&self) -> Result<(), RawBuildInfoError> {
97 if self.nitrocode != NITROCODE {
98 NoNitrocodeSnafu { expected: NITROCODE, actual: self.nitrocode }.fail()
99 } else if self.nitrocode_rev != NITROCODE.swap_bytes() {
100 NoNitrocodeSnafu { expected: NITROCODE.swap_bytes(), actual: self.nitrocode_rev }.fail()
101 } else {
102 Ok(())
103 }
104 }
105
106 pub fn borrow_from_slice(data: &'_ [u8]) -> Result<&'_ Self, RawBuildInfoError> {
112 let size = size_of::<Self>();
113 Self::check_size(data)?;
114 let addr = data as *const [u8] as *const () as usize;
115 let build_info: &Self = Self::handle_pod_cast(bytemuck::try_from_bytes(&data[..size]), addr)?;
116 build_info.check_nitrocode()?;
117 Ok(build_info)
118 }
119
120 pub fn borrow_from_slice_mut(data: &'_ mut [u8]) -> Result<&'_ mut Self, RawBuildInfoError> {
126 let size = size_of::<Self>();
127 Self::check_size(data)?;
128 let addr = data as *const [u8] as *const () as usize;
129 let build_info: &mut Self = Self::handle_pod_cast(bytemuck::try_from_bytes_mut(&mut data[..size]), addr)?;
130 build_info.check_nitrocode()?;
131 Ok(build_info)
132 }
133
134 pub fn is_compressed(&self) -> bool {
136 self.compressed_code_end != 0
137 }
138
139 pub fn display(&self, indent: usize) -> DisplayBuildInfo<'_> {
141 DisplayBuildInfo { build_info: self, indent }
142 }
143}
144
145pub struct DisplayBuildInfo<'a> {
147 build_info: &'a BuildInfo,
148 indent: usize,
149}
150
151impl Display for DisplayBuildInfo<'_> {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 let i = " ".repeat(self.indent);
154 let build_info = &self.build_info;
155 writeln!(f, "{i}Autoload infos start .. : {:#x}", build_info.autoload_infos_start)?;
156 writeln!(f, "{i}Autoload infos end .... : {:#x}", build_info.autoload_infos_end)?;
157 writeln!(f, "{i}Autoload blocks ....... : {:#x}", build_info.autoload_blocks)?;
158 writeln!(f, "{i}.bss start ............ : {:#x}", build_info.bss_start)?;
159 writeln!(f, "{i}.bss end .............. : {:#x}", build_info.bss_end)?;
160 writeln!(f, "{i}Compressed code end ... : {:#x}", build_info.compressed_code_end)?;
161 writeln!(f, "{i}SDK version ........... : {:#x}", build_info.sdk_version)?;
162 Ok(())
163 }
164}