use crate::util::{read_u32le, read_u8};
use crate::RpnExpr;
use std::convert::TryInto;
use std::fmt::{self, Display, Formatter};
use std::io::{self, Read};
#[derive(Debug)]
pub struct Patch {
source_file_id: u32,
line_no: u32,
offset: u32,
pc_section_id: u32,
pc_offset: u32,
patch_type: PatchType,
expr: RpnExpr,
}
impl Patch {
pub(crate) fn read_from(mut input: impl Read) -> Result<Self, io::Error> {
let source_file_id = read_u32le(&mut input)?;
let line_no = read_u32le(&mut input)?;
let offset = read_u32le(&mut input)?;
let pc_section_id = read_u32le(&mut input)?;
let pc_offset = read_u32le(&mut input)?;
let patch_type = PatchType::try_from(read_u8(&mut input)?)?;
let expr_size = read_u32le(&mut input)?.try_into().unwrap();
let mut expr = vec![0; expr_size];
input.read_exact(&mut expr)?;
let expr = RpnExpr::from_bytes(expr);
Ok(Self {
source_file_id,
line_no,
offset,
pc_section_id,
pc_offset,
patch_type,
expr,
})
}
pub fn source(&self) -> (u32, u32) {
(self.source_file_id, self.line_no)
}
pub fn offset(&self) -> u32 {
self.offset
}
pub fn patch_type(&self) -> PatchType {
self.patch_type
}
pub fn pc_section_id(&self) -> u32 {
self.pc_section_id
}
pub fn pc_offset(&self) -> u32 {
self.pc_offset
}
pub fn expr(&self) -> &RpnExpr {
&self.expr
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum PatchType {
Byte,
Word,
Long,
JrOfs,
}
impl PatchType {
fn try_from(byte: u8) -> Result<Self, io::Error> {
use PatchType::*;
Ok(match byte {
0 => Byte,
1 => Word,
2 => Long,
3 => JrOfs,
_ => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Invalid patch type",
))
}
})
}
pub fn name(&self) -> &'static str {
use PatchType::*;
match self {
Byte => "byte",
Word => "word",
Long => "long",
JrOfs => "jr",
}
}
pub fn size(&self) -> u8 {
use PatchType::*;
match self {
Byte => 1,
Word => 2,
Long => 4,
JrOfs => 1,
}
}
}
impl Display for PatchType {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", self.name())
}
}