ds_rom/rom/raw/
rom.rs

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
14/// A raw DS ROM, see the plain struct [here](super::super::Rom).
15pub struct Rom<'a> {
16    data: Cow<'a, [u8]>,
17}
18
19/// Errors related to [`Rom::arm9`].
20#[derive(Debug, Snafu)]
21pub enum RawArm9Error {
22    /// See [`RawHeaderError`].
23    #[snafu(transparent)]
24    RawHeader {
25        /// Source error.
26        source: RawHeaderError,
27    },
28    /// See [`Arm9FooterError`].
29    #[snafu(transparent)]
30    Arm9Footer {
31        /// Source error.
32        source: Arm9FooterError,
33    },
34    /// See [`RawBuildInfoError`].
35    #[snafu(transparent)]
36    RawBuildInfo {
37        /// Source error.
38        source: RawBuildInfoError,
39    },
40}
41
42impl<'a> Rom<'a> {
43    /// Creates a new ROM from raw data.
44    pub fn new<T: Into<Cow<'a, [u8]>>>(data: T) -> Self {
45        Self { data: data.into() }
46    }
47
48    /// Loads from a ROM file.
49    ///
50    /// # Errors
51    ///
52    /// This function will return an error if an I/O operation fails.
53    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    /// Returns the header of this [`Rom`].
63    ///
64    /// # Errors
65    ///
66    /// See [`Header::borrow_from_slice`].
67    pub fn header(&self) -> Result<&Header, RawHeaderError> {
68        Header::borrow_from_slice(self.data.as_ref())
69    }
70
71    /// Returns the ARM9 program of this [`Rom`].
72    ///
73    /// # Errors
74    ///
75    /// See [`Self::header`].
76    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            // `arm9_build_info_offset` is not an absolute ROM offset in DSi titles
89            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    /// Returns a reference to the ARM9 footer of this [`Rom`].
101    ///
102    /// # Errors
103    ///
104    /// See [`Self::header`] and [`Arm9Footer::borrow_from_slice`].
105    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    /// Returns a mutable reference to the ARM9 footer of this [`Rom`].
114    ///
115    /// # Errors
116    ///
117    /// See [`Self::header`] and [`Arm9Footer::borrow_from_slice_mut`].
118    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    /// Returns the ARM9 overlay table of this [`Rom`].
127    ///
128    /// # Errors
129    ///
130    /// See [`Self::header`] and [`Overlay::borrow_from_slice`].
131    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    /// Returns the number of ARM9 overlays in this [`Rom`].
144    ///
145    /// # Errors
146    ///
147    /// See [`Self::header`].
148    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    /// Returns the ARM7 program of this [`Rom`].
156    ///
157    /// # Errors
158    ///
159    /// See [`Self::header`].
160    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    /// Returns the ARM7 overlay table of this [`Rom`].
178    ///
179    /// # Errors
180    ///
181    /// See [`Self::header`] and [`Overlay::borrow_from_slice`].
182    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    /// Returns the number of ARM7 overlays in this [`Rom`].
195    ///
196    /// # Errors
197    ///
198    /// See [`Self::header`].
199    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    /// Returns the FNT of this [`Rom`].
207    ///
208    /// # Errors
209    ///
210    /// See [`Self::header`] and [`Fnt::borrow_from_slice`].
211    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    /// Returns the FAT of this [`Rom`].
220    ///
221    /// # Errors
222    ///
223    /// See [`Self::header`] and [`FileAlloc::borrow_from_slice`].
224    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    /// Returns the banner of this [`Rom`].
234    ///
235    /// # Errors
236    ///
237    /// See [`Self::header`] and [`Banner::borrow_from_slice`].
238    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    /// Returns the padding value between sections of this [`Rom`].
246    ///
247    /// # Errors
248    ///
249    /// See [`Self::header`] and [`Self::banner`].
250    pub fn padding_value(&self) -> Result<u8, RawBannerError> {
251        let header = self.header()?;
252        let banner = self.banner()?;
253
254        // The banner has a known size which is never a multiple of 512,
255        // so it can't coincide with the start of another section.
256        //
257        // Therefore, we can use the first byte after the banner to determine
258        // the padding value.
259
260        let end = header.banner_offset as usize + banner.version().banner_size();
261        Ok(self.data[end])
262    }
263
264    /// Returns a reference to the data of this [`Rom`].
265    pub fn data(&self) -> &[u8] {
266        &self.data
267    }
268
269    /// Saves this ROM to a new file.
270    ///
271    /// # Errors
272    ///
273    /// This function will return an error if an I/O operation fails.
274    pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<(), FileError> {
275        write_file(path, self.data())
276    }
277}