use std::{borrow::Cow, ptr::NonNull};
use from_singleton::*;
use pelite::pe64::{Pe, Rva};
use thiserror::Error;
use crate::Program;
#[derive(Error, Debug)]
pub enum InstanceError {
#[error("Static object not found: {0}")]
NotFound(Cow<'static, str>),
#[error("Static object not initialized: {0}")]
Null(Cow<'static, str>),
}
pub type InstanceResult<T> = Result<T, InstanceError>;
pub trait FromStatic {
fn name() -> Cow<'static, str>;
fn instance_ptr() -> InstanceResult<*mut Self>;
unsafe fn instance_mut() -> InstanceResult<&'static mut Self> {
Self::instance_ptr()
.and_then(|p| unsafe { p.as_mut() }.ok_or(InstanceError::Null(Self::name())))
}
unsafe fn instance() -> InstanceResult<&'static Self> {
Self::instance_ptr()
.and_then(|p| unsafe { p.as_ref() }.ok_or(InstanceError::Null(Self::name())))
}
}
impl<T: FromSingleton> FromStatic for T {
fn name() -> Cow<'static, str> {
<Self as FromSingleton>::name()
}
fn instance_ptr() -> InstanceResult<*mut T> {
address_of::<T>()
.map(|nn| nn.as_ptr())
.ok_or(InstanceError::NotFound(Self::name()))
}
}
pub fn load_static_direct<T: FromStatic>(rva: Rva) -> InstanceResult<*mut T> {
Program::current()
.rva_to_va(rva)
.map_err(|_| InstanceError::NotFound(T::name()))
.map(|a| a as *mut T)
}
pub unsafe fn load_static_indirect<T: FromStatic>(rva: Rva) -> InstanceResult<*mut T> {
let target = Program::current()
.rva_to_va(rva)
.map_err(|_| InstanceError::NotFound(T::name()))?
as *mut Option<NonNull<T>>;
unsafe {
target
.as_mut()
.and_then(|opt| opt.as_mut())
.map(|nn| nn.as_ptr())
.ok_or(InstanceError::Null(T::name()))
}
}