rgbds_obj/
patch.rs

1use crate::util::{read_u32le, read_u8};
2use crate::RpnExpr;
3use std::convert::TryInto;
4use std::fmt::{self, Display, Formatter};
5use std::io::{self, Read};
6
7/// A section patch.
8/// Patches are bytes (or groups of bytes) whose computation was deferred to the linker.
9#[derive(Debug)]
10pub struct Patch {
11    source_file_id: u32,
12    line_no: u32,
13    offset: u32,
14    pc_section_id: u32,
15    pc_offset: u32,
16    patch_type: PatchType,
17    expr: RpnExpr,
18}
19impl Patch {
20    pub(crate) fn read_from(mut input: impl Read) -> Result<Self, io::Error> {
21        let source_file_id = read_u32le(&mut input)?;
22        let line_no = read_u32le(&mut input)?;
23        let offset = read_u32le(&mut input)?;
24        let pc_section_id = read_u32le(&mut input)?;
25        let pc_offset = read_u32le(&mut input)?;
26        let patch_type = PatchType::try_from(read_u8(&mut input)?)?;
27
28        let expr_size = read_u32le(&mut input)?.try_into().unwrap();
29        let mut expr = vec![0; expr_size];
30        input.read_exact(&mut expr)?;
31        let expr = RpnExpr::from_bytes(expr);
32
33        Ok(Self {
34            source_file_id,
35            line_no,
36            offset,
37            pc_section_id,
38            pc_offset,
39            patch_type,
40            expr,
41        })
42    }
43
44    /// Where the patch was defined.
45    /// That is, the [file stack node][crate::Node] ID, and the line number.
46    pub fn source(&self) -> (u32, u32) {
47        (self.source_file_id, self.line_no)
48    }
49
50    /// The offset within the [section][crate::Section]'s data where the patch will be written.
51    pub fn offset(&self) -> u32 {
52        self.offset
53    }
54
55    /// The patch's type, which notably determines its size.
56    pub fn patch_type(&self) -> PatchType {
57        self.patch_type
58    }
59
60    /// The ID of the expression's PC section. See [`pc_offset`][Self::pc_offset] for more info.
61    pub fn pc_section_id(&self) -> u32 {
62        self.pc_section_id
63    }
64
65    /// The expression's PC offset.
66    ///
67    /// This is separate from the [offset][Self::offset], because PC (`@`) may not point to the
68    /// byte being written, and, in the case of [`LOAD` blocks], may be in a different section
69    /// entirely!
70    ///
71    /// [`LOAD` blocks]: https://rgbds.gbdev.io/docs/v0.5.1/rgbasm.5.html#RAM_Code
72    pub fn pc_offset(&self) -> u32 {
73        self.pc_offset
74    }
75
76    /// The patch's RPN expression.
77    pub fn expr(&self) -> &RpnExpr {
78        &self.expr
79    }
80}
81
82/// A patch's type.
83#[derive(Debug, PartialEq, Eq, Clone, Copy)]
84pub enum PatchType {
85    Byte,
86    Word,
87    Long,
88    JrOfs,
89}
90impl PatchType {
91    fn try_from(byte: u8) -> Result<Self, io::Error> {
92        use PatchType::*;
93
94        Ok(match byte {
95            0 => Byte,
96            1 => Word,
97            2 => Long,
98            3 => JrOfs,
99            _ => {
100                return Err(io::Error::new(
101                    io::ErrorKind::InvalidData,
102                    "Invalid patch type",
103                ))
104            }
105        })
106    }
107
108    /// The patch type's name.
109    pub fn name(&self) -> &'static str {
110        use PatchType::*;
111
112        match self {
113            Byte => "byte",
114            Word => "word",
115            Long => "long",
116            JrOfs => "jr",
117        }
118    }
119
120    /// How many bytes this patch type modifies.
121    pub fn size(&self) -> u8 {
122        use PatchType::*;
123
124        match self {
125            Byte => 1,
126            Word => 2,
127            Long => 4,
128            JrOfs => 1,
129        }
130    }
131}
132impl Display for PatchType {
133    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
134        write!(fmt, "{}", self.name())
135    }
136}