1use std::{borrow::Cow, io::Read, mem::size_of, path::Path};
2
3use snafu::Snafu;
4
5use super::{
6 Arm9Footer, Arm9FooterError, Banner, FileAlloc, Fnt, Header, Overlay, RawBannerError, RawBuildInfoError, RawFatError,
7 RawFntError, RawHeaderError, RawOverlayError,
8};
9use crate::{
10 io::{open_file, write_file, FileError},
11 rom::{Arm7, Arm7Offsets, Arm9, Arm9Offsets},
12};
13
14pub struct Rom<'a> {
16 data: Cow<'a, [u8]>,
17}
18
19#[derive(Debug, Snafu)]
21pub enum RawArm9Error {
22 #[snafu(transparent)]
24 RawHeader {
25 source: RawHeaderError,
27 },
28 #[snafu(transparent)]
30 Arm9Footer {
31 source: Arm9FooterError,
33 },
34 #[snafu(transparent)]
36 RawBuildInfo {
37 source: RawBuildInfoError,
39 },
40}
41
42impl<'a> Rom<'a> {
43 pub fn new<T: Into<Cow<'a, [u8]>>>(data: T) -> Self {
45 Self { data: data.into() }
46 }
47
48 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, FileError> {
54 let mut file = open_file(path)?;
55 let size = file.metadata()?.len();
56 let mut buf = vec![0; size as usize];
57 file.read_exact(&mut buf)?;
58 let data: Cow<[u8]> = buf.into();
59 Ok(Self::new(data))
60 }
61
62 pub fn header(&self) -> Result<&Header, RawHeaderError> {
68 Header::borrow_from_slice(self.data.as_ref())
69 }
70
71 pub fn arm9(&self) -> Result<Arm9, RawArm9Error> {
77 let header = self.header()?;
78 let start = header.arm9.offset as usize;
79 let end = start + header.arm9.size as usize;
80 let data = &self.data[start..end];
81
82 let build_info_offset = if header.arm9_build_info_offset == 0 {
83 let footer = self.arm9_footer()?;
84 footer.build_info_offset
85 } else if header.arm9_build_info_offset > header.arm9.offset {
86 header.arm9_build_info_offset - header.arm9.offset
87 } else {
88 header.arm9_build_info_offset
90 };
91
92 Ok(Arm9::new(Cow::Borrowed(data), Arm9Offsets {
93 base_address: header.arm9.base_addr,
94 entry_function: header.arm9.entry,
95 build_info: build_info_offset,
96 autoload_callback: header.arm9_autoload_callback,
97 })?)
98 }
99
100 pub fn arm9_footer(&self) -> Result<&Arm9Footer, Arm9FooterError> {
106 let header = self.header()?;
107 let start = (header.arm9.offset + header.arm9.size) as usize;
108 let end = start + size_of::<Arm9Footer>();
109 let data = &self.data[start..end];
110 Arm9Footer::borrow_from_slice(data)
111 }
112
113 pub fn arm9_footer_mut(&mut self) -> Result<&mut Arm9Footer, Arm9FooterError> {
119 let header = self.header()?;
120 let start = (header.arm9.offset + header.arm9.size) as usize;
121 let end = start + size_of::<Arm9Footer>();
122 let data = &mut self.data.to_mut()[start..end];
123 Arm9Footer::borrow_from_slice_mut(data)
124 }
125
126 pub fn arm9_overlay_table(&self) -> Result<&[Overlay], RawOverlayError> {
132 let header = self.header()?;
133 let start = header.arm9_overlays.offset as usize;
134 let end = start + header.arm9_overlays.size as usize;
135 if start == 0 && end == 0 {
136 Ok(&[])
137 } else {
138 let data = &self.data[start..end];
139 Overlay::borrow_from_slice(data)
140 }
141 }
142
143 pub fn num_arm9_overlays(&self) -> Result<usize, RawHeaderError> {
149 let header = self.header()?;
150 let start = header.arm9_overlays.offset as usize;
151 let end = start + header.arm9_overlays.size as usize;
152 Ok((end - start) / size_of::<Overlay>())
153 }
154
155 pub fn arm7(&self) -> Result<Arm7, RawHeaderError> {
161 let header = self.header()?;
162 let start = header.arm7.offset as usize;
163 let end = start + header.arm7.size as usize;
164 let data = &self.data[start..end];
165
166 let build_info_offset =
167 if header.arm7_build_info_offset == 0 { 0 } else { header.arm7_build_info_offset - header.arm7.offset };
168
169 Ok(Arm7::new(Cow::Borrowed(data), Arm7Offsets {
170 base_address: header.arm7.base_addr,
171 entry_function: header.arm7.entry,
172 build_info: build_info_offset,
173 autoload_callback: header.arm7_autoload_callback,
174 }))
175 }
176
177 pub fn arm7_overlay_table(&self) -> Result<&[Overlay], RawOverlayError> {
183 let header = self.header()?;
184 let start = header.arm7_overlays.offset as usize;
185 let end = start + header.arm7_overlays.size as usize;
186 if start == 0 && end == 0 {
187 Ok(&[])
188 } else {
189 let data = &self.data[start..end];
190 Overlay::borrow_from_slice(data)
191 }
192 }
193
194 pub fn num_arm7_overlays(&self) -> Result<usize, RawHeaderError> {
200 let header = self.header()?;
201 let start = header.arm7_overlays.offset as usize;
202 let end = start + header.arm7_overlays.size as usize;
203 Ok((end - start) / size_of::<Overlay>())
204 }
205
206 pub fn fnt(&self) -> Result<Fnt, RawFntError> {
212 let header = self.header()?;
213 let start = header.file_names.offset as usize;
214 let end = start + header.file_names.size as usize;
215 let data = &self.data[start..end];
216 Fnt::borrow_from_slice(data)
217 }
218
219 pub fn fat(&self) -> Result<&[FileAlloc], RawFatError> {
225 let header = self.header()?;
226 let start = header.file_allocs.offset as usize;
227 let end = start + header.file_allocs.size as usize;
228 let data = &self.data[start..end];
229 let allocs = FileAlloc::borrow_from_slice(data)?;
230 Ok(allocs)
231 }
232
233 pub fn banner(&self) -> Result<Banner, RawBannerError> {
239 let header = self.header()?;
240 let start = header.banner_offset as usize;
241 let data = &self.data[start..];
242 Banner::borrow_from_slice(data)
243 }
244
245 pub fn padding_value(&self) -> Result<u8, RawBannerError> {
251 let header = self.header()?;
252 let banner = self.banner()?;
253
254 let end = header.banner_offset as usize + banner.version().banner_size();
261 Ok(self.data[end])
262 }
263
264 pub fn data(&self) -> &[u8] {
266 &self.data
267 }
268
269 pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<(), FileError> {
275 write_file(path, self.data())
276 }
277}