ds_rom/rom/
arm9.rs

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