use crate::util::{opt_u32, read_str, read_u32le, read_u8};
use crate::Patch;
use std::convert::TryInto;
use std::fmt::{self, Display, Formatter};
use std::io::{self, Read};
#[derive(Debug)]
pub struct Section {
name: Vec<u8>,
size: u32,
sect_type: SectionType,
modifier: SectionMod,
org: Option<u32>,
bank: Option<u32>,
align: u8,
ofs: u32,
}
impl Section {
pub(crate) fn read_from(mut input: impl Read) -> Result<Self, io::Error> {
let name = read_str(&mut input)?;
let size = read_u32le(&mut input)?;
let sect_type = read_u8(&mut input)?;
let modifier = SectionMod::from(sect_type)?;
let org = opt_u32(read_u32le(&mut input)?);
let bank = opt_u32(read_u32le(&mut input)?);
let align = read_u8(&mut input)?;
let ofs = read_u32le(&mut input)?;
let sect_type = SectionType::read_from(sect_type, input, size.try_into().unwrap())?;
Ok(Self {
name,
size,
sect_type,
modifier,
org,
bank,
align,
ofs,
})
}
pub fn name(&self) -> &[u8] {
&self.name
}
pub fn size(&self) -> u32 {
self.size
}
pub fn type_data(&self) -> &SectionType {
&self.sect_type
}
pub fn modifier(&self) -> SectionMod {
self.modifier
}
pub fn org(&self) -> Option<u32> {
self.org
}
pub fn bank(&self) -> Option<u32> {
self.bank
}
pub fn align(&self) -> u8 {
self.align
}
pub fn align_ofs(&self) -> u32 {
self.ofs
}
}
#[derive(Debug)]
pub enum SectionType {
Wram0,
Vram,
Romx(SectionData),
Rom0(SectionData),
Hram,
Wramx,
Sram,
Oam,
}
impl SectionType {
fn read_from(byte: u8, input: impl Read, size: usize) -> Result<Self, io::Error> {
use SectionType::*;
Ok(match byte & 0x3F {
0 => Wram0,
1 => Vram,
2 => Romx(SectionData::read_from(input, size)?),
3 => Rom0(SectionData::read_from(input, size)?),
4 => Hram,
5 => Wramx,
6 => Sram,
7 => Oam,
_ => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Invalid section type",
))
}
})
}
pub fn data(&self) -> Option<&SectionData> {
use SectionType::*;
match self {
Rom0(data) | Romx(data) => Some(data),
_ => None,
}
}
pub fn is_banked(&self) -> bool {
use SectionType::*;
matches!(self, Romx(..) | Vram | Sram | Wramx)
}
pub fn name(&self) -> &'static str {
use SectionType::*;
match self {
Wram0 => "WRAM0",
Vram => "VRAM",
Romx(..) => "ROMX",
Rom0(..) => "ROM0",
Hram => "HRAM",
Wramx => "WRAMX",
Sram => "SRAM",
Oam => "OAM",
}
}
}
impl Display for SectionType {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", self.name())
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SectionMod {
Normal,
Union,
Fragment,
}
impl SectionMod {
fn from(byte: u8) -> Result<Self, io::Error> {
use SectionMod::*;
Ok(match byte & 0xC0 {
0x00 => Normal,
0x80 => Union,
0x40 => Fragment,
_ => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Invalid section modifier",
))
}
})
}
pub fn name(&self) -> Option<&'static str> {
use SectionMod::*;
match self {
Normal => None,
Union => Some("UNION"),
Fragment => Some("FRAGMENT"),
}
}
}
#[derive(Debug)]
pub struct SectionData {
data: Vec<u8>,
patches: Vec<Patch>,
}
impl SectionData {
fn read_from(mut input: impl Read, size: usize) -> Result<Self, io::Error> {
let mut data = vec![0; size];
input.read_exact(&mut data)?;
let nb_patches = read_u32le(&mut input)?.try_into().unwrap();
let mut patches = Vec::with_capacity(nb_patches);
for _ in 0..nb_patches {
patches.push(Patch::read_from(&mut input)?);
}
Ok(Self { data, patches })
}
pub fn data(&self) -> &[u8] {
&self.data
}
pub fn patches(&self) -> &[Patch] {
&self.patches
}
}