rgbds_obj/
section.rs

1use crate::util::{opt_u32, read_str, read_u32le, read_u8};
2use crate::Patch;
3use std::convert::TryInto;
4use std::fmt::{self, Display, Formatter};
5use std::io::{self, Read};
6
7/// A section definition.
8#[derive(Debug)]
9pub struct Section {
10    name: Vec<u8>,
11    source_file_id: Option<u32>,
12    line_no: Option<u32>,
13    size: u32,
14    sect_type: SectionType,
15    modifier: SectionMod,
16    org: Option<u32>,
17    bank: Option<u32>,
18    align: u8,
19    ofs: u32,
20}
21impl Section {
22    pub(crate) fn read_from(mut input: impl Read, has_src: bool) -> Result<Self, io::Error> {
23        let name = read_str(&mut input)?;
24        let source_file_id = if has_src {
25            Some(read_u32le(&mut input)?)
26        } else {
27            None
28        };
29        let line_no = if has_src {
30            Some(read_u32le(&mut input)?)
31        } else {
32            None
33        };
34        let size = read_u32le(&mut input)?;
35        let sect_type = read_u8(&mut input)?;
36        let modifier = SectionMod::from(sect_type)?;
37        let org = opt_u32(read_u32le(&mut input)?);
38        let bank = opt_u32(read_u32le(&mut input)?);
39        let align = read_u8(&mut input)?;
40        let ofs = read_u32le(&mut input)?;
41
42        let sect_type = SectionType::read_from(sect_type, input, size.try_into().unwrap())?;
43
44        Ok(Self {
45            name,
46            source_file_id,
47            line_no,
48            size,
49            sect_type,
50            modifier,
51            org,
52            bank,
53            align,
54            ofs,
55        })
56    }
57
58    /// The section's name.
59    /// As with all names pulled from object files, this is not guaranteed to be valid UTF-8.
60    pub fn name(&self) -> &[u8] {
61        &self.name
62    }
63
64    /// Where the section has been defined.
65    /// That is, the [file stack node][crate::Node] ID, and the line number.
66    /// This is `None` for object files prior to v9 r11.
67    pub fn source(&self) -> Option<(u32, u32)> {
68        match (self.source_file_id, self.line_no) {
69            (Some(source_file_id), Some(line_no)) => Some((source_file_id, line_no)),
70            _ => None,
71        }
72    }
73
74    /// The section's size.
75    pub fn size(&self) -> u32 {
76        self.size
77    }
78
79    /// The section's memory type, including data, if any.
80    pub fn type_data(&self) -> &SectionType {
81        &self.sect_type
82    }
83
84    /// The section's modifier (regular, union, etc.).
85    pub fn modifier(&self) -> SectionMod {
86        self.modifier
87    }
88
89    /// The address at which the section was fixed, or `None` if left floating.
90    pub fn org(&self) -> Option<u32> {
91        self.org
92    }
93
94    /// The bank the section was assigned, or `None` if left floating.
95    pub fn bank(&self) -> Option<u32> {
96        self.bank
97    }
98
99    /// The section's alignment, in bits. 0 if not specified.
100    pub fn align(&self) -> u8 {
101        self.align
102    }
103
104    /// The section's alignment offset.
105    pub fn align_ofs(&self) -> u32 {
106        self.ofs
107    }
108}
109
110/// A section memory type, and associated data if applicable.
111#[derive(Debug)]
112pub enum SectionType {
113    Wram0,
114    Vram,
115    Romx(SectionData),
116    Rom0(SectionData),
117    Hram,
118    Wramx,
119    Sram,
120    Oam,
121}
122impl SectionType {
123    fn read_from(byte: u8, input: impl Read, size: usize) -> Result<Self, io::Error> {
124        use SectionType::*;
125
126        Ok(match byte & 0x3F {
127            0 => Wram0,
128            1 => Vram,
129            2 => Romx(SectionData::read_from(input, size)?),
130            3 => Rom0(SectionData::read_from(input, size)?),
131            4 => Hram,
132            5 => Wramx,
133            6 => Sram,
134            7 => Oam,
135            _ => {
136                return Err(io::Error::new(
137                    io::ErrorKind::InvalidData,
138                    "Invalid section type",
139                ))
140            }
141        })
142    }
143
144    /// The [section][Section]'s ROM data, if any.
145    pub fn data(&self) -> Option<&SectionData> {
146        use SectionType::*;
147
148        match self {
149            Rom0(data) | Romx(data) => Some(data),
150            _ => None,
151        }
152    }
153
154    /// Returns whether the section type may be banked.
155    /// Note that it's possible to configure this in RGBLINK (disabling VRAM banking with `-d`, for
156    /// example), so this may return `true` but have RGBLINK say otherwise.
157    pub fn is_banked(&self) -> bool {
158        use SectionType::*;
159
160        matches!(self, Romx(..) | Vram | Sram | Wramx)
161    }
162
163    /// The section type's name.
164    pub fn name(&self) -> &'static str {
165        use SectionType::*;
166
167        match self {
168            Wram0 => "WRAM0",
169            Vram => "VRAM",
170            Romx(..) => "ROMX",
171            Rom0(..) => "ROM0",
172            Hram => "HRAM",
173            Wramx => "WRAMX",
174            Sram => "SRAM",
175            Oam => "OAM",
176        }
177    }
178}
179impl Display for SectionType {
180    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
181        write!(fmt, "{}", self.name())
182    }
183}
184
185/// A section modifier.
186#[derive(Debug, PartialEq, Eq, Clone, Copy)]
187pub enum SectionMod {
188    Normal,
189    Union,
190    Fragment,
191}
192impl SectionMod {
193    fn from(byte: u8) -> Result<Self, io::Error> {
194        use SectionMod::*;
195
196        Ok(match byte & 0xC0 {
197            0x00 => Normal,
198            0x80 => Union,
199            0x40 => Fragment,
200            _ => {
201                return Err(io::Error::new(
202                    io::ErrorKind::InvalidData,
203                    "Invalid section modifier",
204                ))
205            }
206        })
207    }
208
209    /// The modifier's name, except that there is no such name for "regular" sections.
210    pub fn name(&self) -> Option<&'static str> {
211        use SectionMod::*;
212
213        match self {
214            Normal => None,
215            Union => Some("UNION"),
216            Fragment => Some("FRAGMENT"),
217        }
218    }
219}
220
221/// A ROM section's data.
222#[derive(Debug)]
223pub struct SectionData {
224    data: Vec<u8>,
225    patches: Vec<Patch>,
226}
227impl SectionData {
228    fn read_from(mut input: impl Read, size: usize) -> Result<Self, io::Error> {
229        let mut data = vec![0; size];
230        input.read_exact(&mut data)?;
231
232        let nb_patches = read_u32le(&mut input)?.try_into().unwrap();
233        let mut patches = Vec::with_capacity(nb_patches);
234        for _ in 0..nb_patches {
235            patches.push(Patch::read_from(&mut input)?);
236        }
237
238        Ok(Self { data, patches })
239    }
240
241    /// The section's data.
242    pub fn data(&self) -> &[u8] {
243        &self.data
244    }
245
246    /// The section's patches.
247    pub fn patches(&self) -> &[Patch] {
248        &self.patches
249    }
250}