use std::{
fmt::Display,
mem::{align_of, size_of},
};
use bitfield_struct::bitfield;
use bytemuck::{Pod, PodCastError, Zeroable};
use snafu::{Backtrace, Snafu};
use super::{RawArm9Error, RawHeaderError};
use crate::rom::Arm9OverlaySignaturesError;
#[repr(C)]
#[derive(Clone, Copy, Zeroable, Pod)]
pub struct Overlay {
pub id: u32,
pub base_addr: u32,
pub code_size: u32,
pub bss_size: u32,
pub ctor_start: u32,
pub ctor_end: u32,
pub file_id: u32,
pub flags: OverlayFlags,
}
#[derive(Snafu, Debug)]
pub enum RawOverlayError {
#[snafu(transparent)]
RawHeader {
source: RawHeaderError,
},
#[snafu(transparent)]
RawArm9 {
source: RawArm9Error,
},
#[snafu(transparent)]
Arm9OverlaySignatures {
source: Arm9OverlaySignaturesError,
},
#[snafu(display("the overlay table must be a multiple of {} bytes:\n{backtrace}", size_of::<Overlay>()))]
InvalidSize {
backtrace: Backtrace,
},
#[snafu(display("expected {expected}-alignment for overlay table but got {actual}-alignment:\n{backtrace}"))]
Misaligned {
expected: usize,
actual: usize,
backtrace: Backtrace,
},
}
impl Overlay {
fn check_size(data: &[u8]) -> Result<(), RawOverlayError> {
let size = size_of::<Self>();
if !data.len().is_multiple_of(size) {
InvalidSizeSnafu {}.fail()
} else {
Ok(())
}
}
fn handle_pod_cast<T>(result: Result<T, PodCastError>, addr: usize) -> Result<T, RawOverlayError> {
match result {
Ok(build_info) => Ok(build_info),
Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) => {
MisalignedSnafu { expected: align_of::<Self>(), actual: 1usize << addr.trailing_zeros() }.fail()
}
Err(PodCastError::AlignmentMismatch) => panic!(),
Err(PodCastError::OutputSliceWouldHaveSlop) => panic!(),
Err(PodCastError::SizeMismatch) => unreachable!(),
}
}
pub fn borrow_from_slice(data: &'_ [u8]) -> Result<&'_ [Self], RawOverlayError> {
Self::check_size(data)?;
let addr = data as *const [u8] as *const () as usize;
Self::handle_pod_cast(bytemuck::try_cast_slice(data), addr)
}
pub fn display(&self, indent: usize) -> DisplayOverlay<'_> {
DisplayOverlay { overlay: self, indent }
}
}
pub struct DisplayOverlay<'a> {
overlay: &'a Overlay,
indent: usize,
}
impl Display for DisplayOverlay<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let i = " ".repeat(self.indent);
let overlay = &self.overlay;
writeln!(f, "{i}ID ............... : {}", overlay.id)?;
writeln!(f, "{i}File ID .......... : {}", overlay.file_id)?;
writeln!(f, "{i}Base address ..... : {:#x}", overlay.base_addr)?;
writeln!(f, "{i}Code size ........ : {:#x}", overlay.code_size)?;
writeln!(f, "{i}.bss size ........ : {:#x}", overlay.bss_size)?;
writeln!(f, "{i}.ctor start ...... : {:#x}", overlay.ctor_start)?;
writeln!(f, "{i}.ctor end ........ : {:#x}", overlay.ctor_end)?;
writeln!(f, "{i}Compressed size .. : {:#x}", overlay.flags.size())?;
writeln!(f, "{i}Is compressed .... : {}", overlay.flags.is_compressed())?;
writeln!(f, "{i}Is signed ........ : {}", overlay.flags.is_signed())?;
writeln!(f, "{i}Reserved flags ... : {:#x}", overlay.flags.reserved())?;
Ok(())
}
}
#[bitfield(u32)]
pub struct OverlayFlags {
#[bits(24)]
pub size: usize,
pub is_compressed: bool,
pub is_signed: bool,
#[bits(6)]
pub reserved: u8,
}
unsafe impl Zeroable for OverlayFlags {}
unsafe impl Pod for OverlayFlags {}