Skip to main content

ds_rom/rom/
arm9.rs

1use std::{borrow::Cow, io, mem::replace, ops::Range, str::Utf8Error};
2
3use serde::{Deserialize, Serialize};
4use snafu::{Backtrace, Snafu};
5
6use super::{
7    raw::{
8        AutoloadInfo, AutoloadInfoEntry, AutoloadKind, BuildInfo, HmacSha1Signature, HmacSha1SignatureError,
9        RawAutoloadInfoError, RawBuildInfoError, NITROCODE_BYTES,
10    },
11    Autoload, OverlayTable,
12};
13use crate::{
14    compress::lz77::{Lz77, Lz77DecompressError},
15    crc::CRC_16_MODBUS,
16    crypto::blowfish::{Blowfish, BlowfishError, BlowfishKey, BlowfishLevel},
17    rom::LibraryEntry,
18};
19
20/// ARM9 program.
21#[derive(Clone)]
22pub struct Arm9<'a> {
23    data: Cow<'a, [u8]>,
24    offsets: Arm9Offsets,
25    originally_compressed: bool,
26    originally_encrypted: bool,
27}
28
29/// Offsets in the ARM9 program.
30#[derive(Serialize, Deserialize, Clone, Copy)]
31pub struct Arm9Offsets {
32    /// Base address.
33    pub base_address: u32,
34    /// Entrypoint function address.
35    pub entry_function: u32,
36    /// Build info offset.
37    pub build_info: u32,
38    /// Autoload callback address.
39    pub autoload_callback: u32,
40    /// Offset to overlay HMAC-SHA1 signature table.
41    pub overlay_signatures: u32,
42}
43
44const SECURE_AREA_ID: [u8; 8] = [0xff, 0xde, 0xff, 0xe7, 0xff, 0xde, 0xff, 0xe7];
45const SECURE_AREA_ENCRY_OBJ: &[u8] = "encryObj".as_bytes();
46
47const LZ77: Lz77 = Lz77 {};
48
49const COMPRESSION_START: usize = 0x4000;
50
51/// Errors related to [`Arm9`].
52#[derive(Debug, Snafu)]
53pub enum Arm9Error {
54    /// Occurs when the program is too small to contain a secure area.
55    #[snafu(display("expected {expected:#x} bytes for secure area but had only {actual:#x}:\n{backtrace}"))]
56    DataTooSmall {
57        /// Expected minimum size.
58        expected: usize,
59        /// Actual size.
60        actual: usize,
61        /// Backtrace to the source of the error.
62        backtrace: Backtrace,
63    },
64    /// See [`BlowfishError`].
65    #[snafu(transparent)]
66    Blowfish {
67        /// Source error.
68        source: BlowfishError,
69    },
70    /// Occurs when the string "encryObj" is not found when de/encrypting the secure area.
71    #[snafu(display("invalid encryption, 'encryObj' not found"))]
72    NotEncryObj {
73        /// Backtrace to the source of the error.
74        backtrace: Backtrace,
75    },
76    /// See [`RawBuildInfoError`].
77    #[snafu(transparent)]
78    RawBuildInfo {
79        /// Source error.
80        source: RawBuildInfoError,
81    },
82    /// See [`Lz77DecompressError`].
83    #[snafu(transparent)]
84    Lz77Decompress {
85        /// Source error.
86        source: Lz77DecompressError,
87    },
88    /// See [`io::Error`].
89    #[snafu(transparent)]
90    Io {
91        /// Source error.
92        source: io::Error,
93    },
94}
95
96/// Errors related to ARM9 autoloads.
97#[derive(Debug, Snafu)]
98pub enum Arm9AutoloadError {
99    /// See [`RawBuildInfoError`].
100    #[snafu(transparent)]
101    RawBuildInfo {
102        /// Source error.
103        source: RawBuildInfoError,
104    },
105    /// See [`RawAutoloadInfoError`].
106    #[snafu(transparent)]
107    RawAutoloadInfo {
108        /// Source error.
109        source: RawAutoloadInfoError,
110    },
111    /// Occurs when trying to access autoload blocks while the ARM9 program is compressed.
112    #[snafu(display("ARM9 program must be decompressed before accessing autoload blocks:\n{backtrace}"))]
113    Compressed {
114        /// Backtrace to the source of the error.
115        backtrace: Backtrace,
116    },
117    /// Occurs when trying to access a kind of autoload block which doesn't exist in the ARM9 program.
118    #[snafu(display("autoload block {kind} could not be found:\n{backtrace}"))]
119    NotFound {
120        /// Kind of autoload.
121        kind: AutoloadKind,
122        /// Backtrace to the source of the error.
123        backtrace: Backtrace,
124    },
125}
126
127/// Errors related to [`Arm9::overlay_signatures`] and [`Arm9::overlay_signatures_mut`].
128#[derive(Debug, Snafu)]
129pub enum Arm9OverlaySignaturesError {
130    /// See [`OverlaySignatureError`].
131    #[snafu(transparent)]
132    HmacSha1Signature {
133        /// Source error.
134        source: HmacSha1SignatureError,
135    },
136    /// See [`RawBuildInfoError`].
137    #[snafu(transparent)]
138    RawBuildInfo {
139        /// Source error.
140        source: RawBuildInfoError,
141    },
142    /// Occurs when trying to access overlay signatures while the ARM9 program is compressed.
143    #[snafu(display("ARM9 program must be decompressed before accessing overlay signatures:\n{backtrace}"))]
144    OverlaySignaturesCompressed {
145        /// Backtrace to the source of the error.
146        backtrace: Backtrace,
147    },
148}
149
150/// Errors related to [`Arm9::hmac_sha1_key`].
151#[derive(Debug, Snafu)]
152pub enum Arm9HmacSha1KeyError {
153    /// See [`RawBuildInfoError`].
154    #[snafu(transparent)]
155    RawBuildInfo {
156        /// Source error.
157        source: RawBuildInfoError,
158    },
159    /// Occurs when trying to access the HMAC-SHA1 key while the ARM9 program is compressed.
160    #[snafu(display("ARM9 program must be decompressed before accessing HMAC-SHA1 key:\n{backtrace}"))]
161    HmacSha1KeyCompressed {
162        /// Backtrace to the source of the error.
163        backtrace: Backtrace,
164    },
165}
166
167/// Options for [`Arm9::with_two_tcms`].
168pub struct Arm9WithTcmsOptions {
169    /// Whether the program was compressed originally.
170    pub originally_compressed: bool,
171    /// Whether the program was encrypted originally.
172    pub originally_encrypted: bool,
173}
174
175impl<'a> Arm9<'a> {
176    /// Creates a new ARM9 program from raw data.
177    pub fn new<T: Into<Cow<'a, [u8]>>>(data: T, offsets: Arm9Offsets) -> Result<Self, RawBuildInfoError> {
178        let mut arm9 = Arm9 { data: data.into(), offsets, originally_compressed: false, originally_encrypted: false };
179        arm9.originally_compressed = arm9.is_compressed()?;
180        arm9.originally_encrypted = arm9.is_encrypted();
181        Ok(arm9)
182    }
183
184    /// Creates a new ARM9 program with raw data and two autoloads (ITCM and DTCM).
185    ///
186    /// # Errors
187    ///
188    /// See [`Self::build_info_mut`].
189    pub fn with_two_tcms(
190        mut data: Vec<u8>,
191        itcm: Autoload,
192        dtcm: Autoload,
193        offsets: Arm9Offsets,
194        options: Arm9WithTcmsOptions,
195    ) -> Result<Self, RawBuildInfoError> {
196        let autoload_infos = [*itcm.info().entry(), *dtcm.info().entry()];
197
198        let autoload_blocks = data.len() as u32 + offsets.base_address;
199        data.extend(itcm.into_data().iter());
200        data.extend(dtcm.into_data().iter());
201        let autoload_infos_start = data.len() as u32 + offsets.base_address;
202        data.extend(bytemuck::bytes_of(&autoload_infos));
203        let autoload_infos_end = data.len() as u32 + offsets.base_address;
204
205        let Arm9WithTcmsOptions { originally_compressed, originally_encrypted } = options;
206        let mut arm9 = Self { data: data.into(), offsets, originally_compressed, originally_encrypted };
207
208        let build_info = arm9.build_info_mut()?;
209        build_info.autoload_blocks = autoload_blocks;
210        build_info.autoload_infos_start = autoload_infos_start;
211        build_info.autoload_infos_end = autoload_infos_end;
212
213        Ok(arm9)
214    }
215
216    /// Creates a new ARM9 program with raw data and a list of autoloads.
217    ///
218    /// # Errors
219    ///
220    /// See [`Self::build_info_mut`].
221    pub fn with_autoloads(
222        mut data: Vec<u8>,
223        autoloads: &[Autoload],
224        offsets: Arm9Offsets,
225        options: Arm9WithTcmsOptions,
226    ) -> Result<Self, RawBuildInfoError> {
227        let autoload_blocks = data.len() as u32 + offsets.base_address;
228
229        for autoload in autoloads {
230            data.extend(autoload.full_data());
231        }
232
233        let autoload_infos_start = data.len() as u32 + offsets.base_address;
234        for autoload in autoloads {
235            data.extend(bytemuck::bytes_of(autoload.info().entry()));
236        }
237        let autoload_infos_end = data.len() as u32 + offsets.base_address;
238
239        let Arm9WithTcmsOptions { originally_compressed, originally_encrypted } = options;
240        let mut arm9 = Self { data: data.into(), offsets, originally_compressed, originally_encrypted };
241
242        let build_info = arm9.build_info_mut()?;
243        build_info.autoload_blocks = autoload_blocks;
244        build_info.autoload_infos_start = autoload_infos_start;
245        build_info.autoload_infos_end = autoload_infos_end;
246
247        Ok(arm9)
248    }
249
250    /// Returns whether the secure area is encrypted. See [`Self::originally_encrypted`] for whether the secure area was
251    /// encrypted originally.
252    pub fn is_encrypted(&self) -> bool {
253        self.data.len() < 8 || self.data[0..8] != SECURE_AREA_ID
254    }
255
256    /// Decrypts the secure area. Does nothing if already decrypted.
257    ///
258    /// # Errors
259    ///
260    /// This function will return an error if the program is too small to contain a secure area, [`Blowfish::decrypt`] fails or
261    /// "encryObj" was not found.
262    pub fn decrypt(&mut self, key: &BlowfishKey, gamecode: u32) -> Result<(), Arm9Error> {
263        if !self.is_encrypted() {
264            return Ok(());
265        }
266
267        if self.data.len() < 0x4000 {
268            DataTooSmallSnafu { expected: 0x4000usize, actual: self.data.len() }.fail()?;
269        }
270
271        let mut secure_area = [0u8; 0x4000];
272        secure_area.clone_from_slice(&self.data[0..0x4000]);
273
274        let blowfish = Blowfish::new(key, gamecode, BlowfishLevel::Level2);
275        blowfish.decrypt(&mut secure_area[0..8])?;
276
277        let blowfish = Blowfish::new(key, gamecode, BlowfishLevel::Level3);
278        blowfish.decrypt(&mut secure_area[0..0x800])?;
279
280        if &secure_area[0..8] != SECURE_AREA_ENCRY_OBJ {
281            NotEncryObjSnafu {}.fail()?;
282        }
283
284        secure_area[0..8].copy_from_slice(&SECURE_AREA_ID);
285        self.data.to_mut()[0..0x4000].copy_from_slice(&secure_area);
286        Ok(())
287    }
288
289    /// Encrypts the secure area. Does nothing if already encrypted.
290    ///
291    /// # Errors
292    ///
293    /// This function will return an error if the program is too small to contain a secure area, or the secure area ID was not
294    /// found.
295    pub fn encrypt(&mut self, key: &BlowfishKey, gamecode: u32) -> Result<(), Arm9Error> {
296        if self.is_encrypted() {
297            return Ok(());
298        }
299
300        if self.data.len() < 0x4000 {
301            DataTooSmallSnafu { expected: 0x4000usize, actual: self.data.len() }.fail()?;
302        }
303
304        if self.data[0..8] != SECURE_AREA_ID {
305            NotEncryObjSnafu {}.fail()?;
306        }
307
308        let secure_area = self.encrypted_secure_area(key, gamecode);
309        self.data.to_mut()[0..0x4000].copy_from_slice(&secure_area);
310        Ok(())
311    }
312
313    /// Returns an encrypted copy of the secure area.
314    pub fn encrypted_secure_area(&self, key: &BlowfishKey, gamecode: u32) -> [u8; 0x4000] {
315        let mut secure_area = [0u8; 0x4000];
316        secure_area.copy_from_slice(&self.data[0..0x4000]);
317        if self.is_encrypted() {
318            return secure_area;
319        }
320
321        secure_area[0..8].copy_from_slice(SECURE_AREA_ENCRY_OBJ);
322
323        let blowfish = Blowfish::new(key, gamecode, BlowfishLevel::Level3);
324        blowfish.encrypt(&mut secure_area[0..0x800]).unwrap();
325
326        let blowfish = Blowfish::new(key, gamecode, BlowfishLevel::Level2);
327        blowfish.encrypt(&mut secure_area[0..8]).unwrap();
328
329        secure_area
330    }
331
332    /// Returns a CRC checksum of the encrypted secure area.
333    pub fn secure_area_crc(&self, key: &BlowfishKey, gamecode: u32) -> u16 {
334        let secure_area = self.encrypted_secure_area(key, gamecode);
335        CRC_16_MODBUS.checksum(&secure_area)
336    }
337
338    /// Returns a reference to the build info.
339    ///
340    /// # Errors
341    ///
342    /// See [`BuildInfo::borrow_from_slice`].
343    pub fn build_info(&self) -> Result<&BuildInfo, RawBuildInfoError> {
344        BuildInfo::borrow_from_slice(&self.data[self.offsets.build_info as usize..])
345    }
346
347    /// Returns a mutable reference to the build info.
348    ///
349    /// # Errors
350    ///
351    /// See [`BuildInfo::borrow_from_slice_mut`].
352    pub fn build_info_mut(&mut self) -> Result<&mut BuildInfo, RawBuildInfoError> {
353        BuildInfo::borrow_from_slice_mut(&mut self.data.to_mut()[self.offsets.build_info as usize..])
354    }
355
356    /// Returns the library version strings in this [`Arm9`].
357    ///
358    /// # Errors
359    ///
360    /// This function will return an error if a library string is not valid UTF-8.
361    pub fn libraries(&self) -> Result<Box<[LibraryEntry<'_>]>, Utf8Error> {
362        let libraries_start = self.offsets.build_info as usize + size_of::<BuildInfo>();
363        let mut address = self.base_address() + libraries_start as u32;
364        let mut data = &self.data[libraries_start..];
365
366        let mut libraries = Vec::new();
367
368        'outer: while data[0] == b'[' {
369            let Some((end_pos, _)) = data.iter().enumerate().find(|(_, c)| **c == b']') else {
370                break;
371            };
372            let version_string = str::from_utf8(&data[..end_pos + 1])?;
373
374            libraries.push(LibraryEntry::new(address, version_string));
375
376            address += end_pos as u32 + 1;
377            data = &data[end_pos + 1..];
378            loop {
379                match data[0] {
380                    b'\0' => {
381                        address += 1;
382                        data = &data[1..]
383                    }
384                    b'[' => break,
385                    _ => break 'outer,
386                }
387            }
388        }
389
390        Ok(libraries.into_boxed_slice())
391    }
392
393    /// Returns whether this ARM9 program is compressed. See [`Self::originally_compressed`] for whether the program was
394    /// compressed originally.
395    ///
396    /// # Errors
397    ///
398    /// See [`Self::build_info`].
399    pub fn is_compressed(&self) -> Result<bool, RawBuildInfoError> {
400        Ok(self.build_info()?.is_compressed())
401    }
402
403    /// Decompresses this ARM9 program. Does nothing if already decompressed.
404    ///
405    /// # Errors
406    ///
407    /// See [`Self::is_compressed`] and [`Self::build_info_mut`].
408    pub fn decompress(&mut self) -> Result<(), Arm9Error> {
409        if !self.is_compressed()? {
410            return Ok(());
411        }
412
413        let data: Cow<[u8]> = LZ77.decompress(&self.data)?.into_vec().into();
414        let old_data = replace(&mut self.data, data);
415        let build_info = match self.build_info_mut() {
416            Ok(build_info) => build_info,
417            Err(e) => {
418                self.data = old_data;
419                return Err(e.into());
420            }
421        };
422        build_info.compressed_code_end = 0;
423        Ok(())
424    }
425
426    /// Compresses this ARM9 program. Does nothing if already compressed.
427    ///
428    /// # Errors
429    ///
430    /// See [`Self::is_compressed`], [`Lz77::compress`] and [`Self::build_info_mut`].
431    pub fn compress(&mut self) -> Result<(), Arm9Error> {
432        if self.is_compressed()? {
433            return Ok(());
434        }
435
436        let data: Cow<[u8]> = LZ77.compress(&self.data, COMPRESSION_START)?.into_vec().into();
437        let length = data.len();
438        let old_data = replace(&mut self.data, data);
439        let base_address = self.base_address();
440        let build_info = match self.build_info_mut() {
441            Ok(build_info) => build_info,
442            Err(e) => {
443                self.data = old_data;
444                return Err(e.into());
445            }
446        };
447        build_info.compressed_code_end = base_address + length as u32;
448        Ok(())
449    }
450
451    fn get_autoload_info_entries(&self, build_info: &BuildInfo) -> Result<&[AutoloadInfoEntry], Arm9AutoloadError> {
452        let start = (build_info.autoload_infos_start - self.base_address()) as usize;
453        let end = (build_info.autoload_infos_end - self.base_address()) as usize;
454        let autoload_info = AutoloadInfoEntry::borrow_from_slice(&self.data[start..end])?;
455        Ok(autoload_info)
456    }
457
458    /// Returns the autoload infos of this [`Arm9`].
459    ///
460    /// # Errors
461    ///
462    /// This function will return an error if [`Self::build_info`] or [`Self::get_autoload_infos`] fails or this ARM9 program
463    /// is compressed.
464    pub fn autoload_infos(&self) -> Result<Vec<AutoloadInfo>, Arm9AutoloadError> {
465        let build_info: &BuildInfo = self.build_info()?;
466        if build_info.is_compressed() {
467            CompressedSnafu {}.fail()?;
468        }
469        Ok(self
470            .get_autoload_info_entries(build_info)?
471            .iter()
472            .enumerate()
473            .map(|(index, entry)| AutoloadInfo::new(*entry, index as u32))
474            .collect())
475    }
476
477    /// Returns the autoloads of this [`Arm9`].
478    ///
479    /// # Errors
480    ///
481    /// This function will return an error if [`Self::build_info`] or [`Self::get_autoload_infos`] fails or this ARM9 program
482    /// is compressed.
483    pub fn autoloads(&self) -> Result<Box<[Autoload<'_>]>, Arm9AutoloadError> {
484        let build_info = self.build_info()?;
485        if build_info.is_compressed() {
486            CompressedSnafu {}.fail()?;
487        }
488        let autoload_infos = self.autoload_infos()?;
489
490        let mut autoloads = vec![];
491        let mut load_offset = build_info.autoload_blocks - self.base_address();
492        for autoload_info in autoload_infos {
493            let start = load_offset as usize;
494            let end = start + autoload_info.code_size() as usize;
495            let data = &self.data[start..end];
496            autoloads.push(Autoload::new(data, autoload_info));
497            load_offset += autoload_info.code_size();
498        }
499
500        Ok(autoloads.into_boxed_slice())
501    }
502
503    /// Returns the number of unknown autoloads of this [`Arm9`].
504    ///
505    /// # Errors
506    ///
507    /// See [`Self::autoloads`].
508    pub fn num_unknown_autoloads(&self) -> Result<usize, Arm9AutoloadError> {
509        Ok(self.autoloads()?.iter().filter(|a| matches!(a.kind(), AutoloadKind::Unknown(_))).count())
510    }
511
512    /// Returns the HMAC-SHA1 key in this ARM9 program.
513    pub fn hmac_sha1_key(&self) -> Result<Option<[u8; 64]>, Arm9HmacSha1KeyError> {
514        if self.is_compressed()? {
515            HmacSha1KeyCompressedSnafu {}.fail()?
516        }
517
518        // Credits to pleonex: https://scenegate.github.io/Ekona/docs/specs/cartridge/security.html#overlays
519        let Some((i, _)) = self.data.chunks(4).enumerate().filter(|(_, chunk)| *chunk == NITROCODE_BYTES).nth(1) else {
520            return Ok(None);
521        };
522        let start = i * 4;
523        let end = start + 64;
524        if end > self.data.len() {
525            return Ok(None);
526        }
527        let mut key = [0u8; 64];
528        key.copy_from_slice(&self.data[start..end]);
529        Ok(Some(key))
530    }
531
532    fn overlay_table_signature_range(&self) -> Result<Option<Range<usize>>, Arm9OverlaySignaturesError> {
533        let overlay_signatures_offset = self.overlay_signatures_offset() as usize;
534        if overlay_signatures_offset == 0 {
535            return Ok(None);
536        }
537
538        if self.is_compressed()? {
539            OverlaySignaturesCompressedSnafu {}.fail()?;
540        }
541
542        // The overlay table signature is located right before the overlay signatures
543        let start = overlay_signatures_offset - size_of::<HmacSha1Signature>();
544        let end = overlay_signatures_offset;
545        if end > self.data.len() {
546            return Ok(None);
547        }
548        Ok(Some(start..end))
549    }
550
551    /// Returns the ARM9 overlay table signature.
552    ///
553    /// # Errors
554    ///
555    /// This function will return an error if the ARM9 program is compressed or if [`HmacSha1Signature::borrow_from_slice`] fails.
556    pub fn overlay_table_signature(&self) -> Result<Option<&HmacSha1Signature>, Arm9OverlaySignaturesError> {
557        let Some(range) = self.overlay_table_signature_range()? else {
558            return Ok(None);
559        };
560        let data = &self.data[range];
561
562        let signature = HmacSha1Signature::borrow_from_slice(data)?;
563        Ok(Some(signature.first().unwrap()))
564    }
565
566    /// Returns a mutable reference to the ARM9 overlay table signature.
567    ///
568    /// # Errors
569    ///
570    /// This function will return an error if the ARM9 program is compressed or if [`HmacSha1Signature::borrow_from_slice_mut`]
571    /// fails.
572    pub fn overlay_table_signature_mut(&mut self) -> Result<Option<&mut HmacSha1Signature>, Arm9OverlaySignaturesError> {
573        let Some(range) = self.overlay_table_signature_range()? else {
574            return Ok(None);
575        };
576        let data = &mut self.data.to_mut()[range];
577
578        let signature = HmacSha1Signature::borrow_from_slice_mut(data)?;
579        Ok(Some(signature.first_mut().unwrap()))
580    }
581
582    fn overlay_signatures_range(&self, num_overlays: usize) -> Result<Option<Range<usize>>, Arm9OverlaySignaturesError> {
583        let start = self.overlay_signatures_offset() as usize;
584        if start == 0 {
585            return Ok(None);
586        }
587
588        if self.is_compressed()? {
589            OverlaySignaturesCompressedSnafu {}.fail()?;
590        }
591
592        let end = start + size_of::<HmacSha1Signature>() * num_overlays;
593        if end > self.data.len() {
594            return Ok(None);
595        }
596        Ok(Some(start..end))
597    }
598
599    /// Returns the ARM9 overlay signature table.
600    ///
601    /// # Errors
602    ///
603    /// This function will return an error if the ARM9 program is compressed or if [`HmacSha1Signature::borrow_from_slice`]
604    /// fails.
605    pub fn overlay_signatures(&self, num_overlays: usize) -> Result<Option<&[HmacSha1Signature]>, Arm9OverlaySignaturesError> {
606        let Some(range) = self.overlay_signatures_range(num_overlays)? else {
607            return Ok(None);
608        };
609        let data = &self.data[range];
610        Ok(Some(HmacSha1Signature::borrow_from_slice(data)?))
611    }
612
613    /// Returns a mutable reference to the ARM9 overlay signature table.
614    ///
615    /// # Errors
616    ///
617    /// This function will return an error if the ARM9 program is compressed or if [`HmacSha1Signature::borrow_from_slice_mut`]
618    /// fails.
619    pub fn overlay_signatures_mut(
620        &mut self,
621        num_overlays: usize,
622    ) -> Result<Option<&mut [HmacSha1Signature]>, Arm9OverlaySignaturesError> {
623        let Some(range) = self.overlay_signatures_range(num_overlays)? else {
624            return Ok(None);
625        };
626        let data = &mut self.data.to_mut()[range];
627        Ok(Some(HmacSha1Signature::borrow_from_slice_mut(data)?))
628    }
629
630    /// Returns the code of this ARM9 program.
631    ///
632    /// # Errors
633    ///
634    /// See [`Self::build_info`].
635    pub fn code(&self) -> Result<&[u8], RawBuildInfoError> {
636        let build_info = self.build_info()?;
637        Ok(&self.data[..(build_info.bss_start - self.base_address()) as usize])
638    }
639
640    /// Returns a reference to the full data.
641    pub fn full_data(&self) -> &[u8] {
642        &self.data
643    }
644
645    /// Returns the base address.
646    pub fn base_address(&self) -> u32 {
647        self.offsets.base_address
648    }
649
650    /// Returns the end address.
651    pub fn end_address(&self) -> Result<u32, RawBuildInfoError> {
652        let build_info = self.build_info()?;
653        Ok(build_info.bss_end)
654    }
655
656    /// Returns the entry function address.
657    pub fn entry_function(&self) -> u32 {
658        self.offsets.entry_function
659    }
660
661    /// Returns the build info offset.
662    pub fn build_info_offset(&self) -> u32 {
663        self.offsets.build_info
664    }
665
666    /// Returns the autoload callback address.
667    pub fn autoload_callback(&self) -> u32 {
668        self.offsets.autoload_callback
669    }
670
671    /// Returns the offset to the overlay HMAC-SHA1 signature table.
672    pub fn overlay_signatures_offset(&self) -> u32 {
673        self.offsets.overlay_signatures
674    }
675
676    /// Returns the [`Range`] of uninitialized data in this ARM9 program.
677    ///
678    /// # Errors
679    ///
680    /// See [`Self::build_info`].
681    pub fn bss(&self) -> Result<Range<u32>, RawBuildInfoError> {
682        let build_info = self.build_info()?;
683        Ok(build_info.bss_start..build_info.bss_end)
684    }
685
686    /// Returns a reference to the ARM9 offsets.
687    pub fn offsets(&self) -> &Arm9Offsets {
688        &self.offsets
689    }
690
691    /// Returns whether the ARM9 program was compressed originally. See [`Self::is_compressed`] for the current state.
692    pub fn originally_compressed(&self) -> bool {
693        self.originally_compressed
694    }
695
696    /// Returns whether the ARM9 program was encrypted originally. See [`Self::is_encrypted`] for the current state.
697    pub fn originally_encrypted(&self) -> bool {
698        self.originally_encrypted
699    }
700
701    pub(crate) fn update_overlay_signatures(
702        &mut self,
703        arm9_overlay_table: &OverlayTable,
704    ) -> Result<(), Arm9OverlaySignaturesError> {
705        let arm9_overlays = arm9_overlay_table.overlays();
706        let Some(signatures) = self.overlay_signatures_mut(arm9_overlays.len())? else {
707            return Ok(());
708        };
709        for overlay in arm9_overlays {
710            if let Some(signature) = overlay.signature() {
711                signatures[overlay.id() as usize] = signature;
712            }
713        }
714
715        if let Some(signature) = arm9_overlay_table.signature() {
716            let Some(table_signature) = self.overlay_table_signature_mut()? else {
717                return Ok(());
718            };
719            *table_signature = signature;
720        }
721
722        Ok(())
723    }
724}
725
726impl AsRef<[u8]> for Arm9<'_> {
727    fn as_ref(&self) -> &[u8] {
728        &self.data
729    }
730}