ds_rom/rom/
overlay.rs

1use std::{borrow::Cow, io};
2
3use serde::{Deserialize, Serialize};
4
5use super::raw::{self, FileAlloc, OverlayCompressedSize, RawHeaderError};
6use crate::compress::lz77::{Lz77, Lz77DecompressError};
7
8/// An overlay module for ARM9/ARM7.
9#[derive(Clone)]
10pub struct Overlay<'a> {
11    originally_compressed: bool,
12    info: OverlayInfo,
13    data: Cow<'a, [u8]>,
14}
15
16const LZ77: Lz77 = Lz77 {};
17
18impl<'a> Overlay<'a> {
19    /// Creates a new [`Overlay`] from plain data.
20    pub fn new<T: Into<Cow<'a, [u8]>>>(data: T, info: OverlayInfo, originally_compressed: bool) -> Self {
21        Self { originally_compressed, info, data: data.into() }
22    }
23
24    /// Parses an [`Overlay`] from a FAT and ROM.
25    pub fn parse(overlay: &raw::Overlay, fat: &[FileAlloc], rom: &'a raw::Rom) -> Result<Self, RawHeaderError> {
26        let alloc = fat[overlay.file_id as usize];
27        let data = &rom.data()[alloc.range()];
28        Ok(Self {
29            originally_compressed: overlay.compressed.is_compressed() != 0,
30            info: OverlayInfo::new(overlay),
31            data: Cow::Borrowed(data),
32        })
33    }
34
35    /// Builds a raw overlay table entry.
36    pub fn build(&self) -> raw::Overlay {
37        raw::Overlay {
38            id: self.id() as u32,
39            base_addr: self.base_address(),
40            code_size: self.code_size(),
41            bss_size: self.bss_size(),
42            ctor_start: self.ctor_start(),
43            ctor_end: self.ctor_end(),
44            file_id: self.file_id(),
45            compressed: if self.is_compressed() {
46                OverlayCompressedSize::new().with_size(self.data.len()).with_is_compressed(1)
47            } else {
48                OverlayCompressedSize::new().with_size(0).with_is_compressed(0)
49            },
50        }
51    }
52
53    /// Returns the ID of this [`Overlay`].
54    pub fn id(&self) -> u16 {
55        self.info.id as u16
56    }
57
58    /// Returns the base address of this [`Overlay`].
59    pub fn base_address(&self) -> u32 {
60        self.info.base_address
61    }
62
63    /// Returns the end address of this [`Overlay`].
64    pub fn end_address(&self) -> u32 {
65        self.info.base_address + self.info.code_size + self.info.bss_size
66    }
67
68    /// Returns the size of initialized data in this [`Overlay`].
69    pub fn code_size(&self) -> u32 {
70        self.info.code_size
71    }
72
73    /// Returns the size of uninitialized data in this [`Overlay`].
74    pub fn bss_size(&self) -> u32 {
75        self.info.bss_size
76    }
77
78    /// Returns the offset to the start of the .ctor section.
79    pub fn ctor_start(&self) -> u32 {
80        self.info.ctor_start
81    }
82
83    /// Returns the offset to the end of the .ctor section.
84    pub fn ctor_end(&self) -> u32 {
85        self.info.ctor_end
86    }
87
88    /// Returns the file ID of this [`Overlay`].
89    pub fn file_id(&self) -> u32 {
90        self.info.file_id
91    }
92
93    /// Returns whether this [`Overlay`] is compressed. See [`Self::originally_compressed`] for whether this overlay was
94    /// compressed originally.
95    pub fn is_compressed(&self) -> bool {
96        self.info.compressed
97    }
98
99    /// Decompresses this [`Overlay`], but does nothing if already decompressed.
100    pub fn decompress(&mut self) -> Result<(), Lz77DecompressError> {
101        if !self.is_compressed() {
102            return Ok(());
103        }
104        self.data = LZ77.decompress(&self.data)?.into_vec().into();
105        self.info.compressed = false;
106        Ok(())
107    }
108
109    /// Compresses this [`Overlay`], but does nothing if already compressed.
110    ///
111    /// # Errors
112    ///
113    /// This function will return an error if an I/O operation fails.
114    pub fn compress(&mut self) -> Result<(), io::Error> {
115        if self.is_compressed() {
116            return Ok(());
117        }
118        self.data = LZ77.compress(&self.data, 0)?.into_vec().into();
119        self.info.compressed = true;
120        Ok(())
121    }
122
123    /// Returns a reference to the code of this [`Overlay`].
124    pub fn code(&self) -> &[u8] {
125        &self.data[..self.code_size() as usize]
126    }
127
128    /// Returns a reference to the full data of this [`Overlay`].
129    pub fn full_data(&self) -> &[u8] {
130        &self.data
131    }
132
133    /// Returns a reference to the info of this [`Overlay`].
134    pub fn info(&self) -> &OverlayInfo {
135        &self.info
136    }
137
138    /// Returns whether this [`Overlay`] was compressed originally. See [`Self::is_compressed`] for the current state.
139    pub fn originally_compressed(&self) -> bool {
140        self.originally_compressed
141    }
142}
143
144/// Info of an [`Overlay`], similar to an entry in the overlay table.
145#[derive(Serialize, Deserialize, Clone)]
146pub struct OverlayInfo {
147    /// Overlay ID.
148    pub id: u32,
149    /// Base address.
150    pub base_address: u32,
151    /// Initialized size.
152    pub code_size: u32,
153    /// Uninitialized size.
154    pub bss_size: u32,
155    /// Offset to start of .ctor section.
156    pub ctor_start: u32,
157    /// Offset to end of .ctor section.
158    pub ctor_end: u32,
159    /// File ID for the FAT.
160    pub file_id: u32,
161    /// Whether the overlay is compressed.
162    pub compressed: bool,
163}
164
165impl OverlayInfo {
166    /// Creates a new [`OverlayInfo`] from raw data.
167    pub fn new(overlay: &raw::Overlay) -> Self {
168        Self {
169            id: overlay.id,
170            base_address: overlay.base_addr,
171            code_size: overlay.code_size,
172            bss_size: overlay.bss_size,
173            ctor_start: overlay.ctor_start,
174            ctor_end: overlay.ctor_end,
175            file_id: overlay.file_id,
176            compressed: overlay.compressed.is_compressed() != 0,
177        }
178    }
179}