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