use std::alloc::{Layout, LayoutError, alloc_zeroed};
use std::{borrow::Cow, fmt, ops, ptr, sync::LazyLock};
use pelite::pe64::Pe;
use shared::{FromStatic, InstanceResult, Program, util::IncompleteArrayField};
use super::ItemId;
use crate::rva;
#[repr(C)]
pub struct MapItemMan {
}
impl FromStatic for MapItemMan {
fn name() -> Cow<'static, str> {
"MapItemMan".into()
}
fn instance_ptr() -> InstanceResult<*mut Self> {
unsafe { shared::load_static_indirect(rva::get().map_item_man_ptr) }
}
}
pub static MAP_ITEM_MAN_GRANT_ITEM_VA: LazyLock<u64> = LazyLock::new(|| {
Program::current()
.rva_to_va(rva::get().map_item_man_grant_item)
.expect("Call target for MAP_ITEM_MAN_GRANT_ITEM_VA was not in exe")
});
impl MapItemMan {
pub fn grant_item(&mut self, item: impl Into<ItemBufferEntry>) {
let array = ItemArray::new([item.into()]);
self.grant_items(&array);
}
pub fn grant_items(&mut self, items: impl AsRef<ItemBuffer>) {
let grant_items: extern "C" fn(&MapItemMan, &ItemBuffer, &i32) =
unsafe { std::mem::transmute(*MAP_ITEM_MAN_GRANT_ITEM_VA) };
let unknown = 1;
grant_items(self, items.as_ref(), &unknown);
}
}
#[repr(C)]
pub struct ItemBuffer {
length: u32,
items: IncompleteArrayField<ItemBufferEntry>,
}
impl ItemBuffer {
pub fn new(length: u32) -> Box<Self> {
let layout = Self::layout(length.try_into().unwrap()).unwrap();
let mut buffer = unsafe { Box::from_raw(alloc_zeroed(layout) as *mut ItemBuffer) };
buffer.length = length;
buffer
}
fn layout(length: usize) -> Result<Layout, LayoutError> {
let layout = Layout::new::<ItemBuffer>();
let (layout, _) = layout.extend(Layout::array::<ItemBufferEntry>(length)?)?;
Ok(layout.pad_to_align())
}
pub fn as_slice(&self) -> &[ItemBufferEntry] {
unsafe { self.items.as_slice(self.length.try_into().unwrap()) }
}
pub fn as_mut_slice(&mut self) -> &mut [ItemBufferEntry] {
unsafe { self.items.as_mut_slice(self.length.try_into().unwrap()) }
}
pub fn remove(&mut self, index: usize) -> ItemBufferEntry {
let len = self.length as usize;
if index >= len {
panic!("removal index (is {index}) should be < len (is {len})");
}
unsafe {
let ret;
{
let ptr = self.as_mut_ptr().add(index);
ret = ptr::read(ptr);
ptr::copy(ptr.add(1), ptr, len - index - 1);
}
self.length = (len - 1) as u32;
ret
}
}
}
impl From<&[ItemBufferEntry]> for Box<ItemBuffer> {
fn from(items: &[ItemBufferEntry]) -> Box<ItemBuffer> {
let mut buffer = ItemBuffer::new(items.len().try_into().unwrap());
buffer.as_mut_slice().clone_from_slice(items);
buffer
}
}
impl ops::Deref for ItemBuffer {
type Target = [ItemBufferEntry];
fn deref(&self) -> &[ItemBufferEntry] {
self.as_slice()
}
}
impl ops::DerefMut for ItemBuffer {
fn deref_mut(&mut self) -> &mut [ItemBufferEntry] {
self.as_mut_slice()
}
}
impl fmt::Debug for ItemBuffer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
self.as_slice().fmt(f)
}
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct ItemBufferEntry {
pub id: ItemId,
pub quantity: u32,
pub durability: i32,
}
impl From<ItemId> for ItemBufferEntry {
fn from(id: ItemId) -> ItemBufferEntry {
ItemBufferEntry {
id,
quantity: 1,
durability: -1,
}
}
}
#[repr(C)]
pub struct ItemArray<const N: usize> {
length: u32,
pub items: [ItemBufferEntry; N],
}
impl<const N: usize> ItemArray<N> {
#[inline]
pub fn new(items: [ItemBufferEntry; N]) -> Self {
ItemArray {
length: N.try_into().unwrap(),
items,
}
}
}
impl<const N: usize> fmt::Debug for ItemArray<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
self.items.fmt(f)
}
}
impl<const N: usize> AsRef<ItemBuffer> for ItemArray<N> {
fn as_ref(&self) -> &ItemBuffer {
let pointer: *const ItemArray<N> = ptr::from_ref(self);
unsafe { &*(pointer as *const ItemBuffer) }
}
}