use std::alloc::{Layout, LayoutError, alloc_zeroed};
use std::{borrow::Cow, fmt, ops, ptr, sync::LazyLock};
use pelite::pe64::Pe;
use shared::{FromStatic, IncompleteArrayField, InstanceResult, OwnedPtr, Program};
use super::ItemId;
use crate::{dlkr::DLAllocatorRef, rva, stl::Vector};
#[repr(C)]
pub struct MapItemMan {
_vftable: usize,
_unk08: u32,
_unk0c: u32,
_unk10: u32,
_unk14: u32,
_unk18: u64,
_unk20: u64,
_unk28: u64,
_unk30: u64,
_unk38: u64,
_unk40: u64,
_unk48: u64,
_unk50: u64,
_unk58: u64,
_unk60: u64,
_unk68: u64,
_unk70: u64,
_unk78: Vector<u8>,
_unk98: Vector<u8>,
pub map_item_drop_changer: OwnedPtr<CSMapItemDropChanger>,
pub menu_handle: MenuHandle,
_unkd8: [u8; 0x50],
_unk128: u64,
_unk130: u8,
_unk134: u32,
_unk138: u32,
_unk140: DLAllocatorRef,
_unk148: u64,
_unk150: u64,
_unk158: u32,
_unk15c: u32,
_unk160: u32,
_unk164: u32,
_unk168: u64,
_unk170: u64,
_unk178: u64,
_unk180: u64,
_unk188: u64,
_unk190: u16,
}
impl FromStatic for MapItemMan {
fn name() -> Cow<'static, str> {
"MapItemMan".into()
}
unsafe fn instance() -> InstanceResult<&'static 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(&self, item: impl Into<ItemBufferEntry>) {
let array = ItemArray::new([item.into()]);
self.grant_items(&array);
}
pub fn grant_items(&self, items: impl AsRef<ItemBuffer>) {
let grant_items: extern "C" fn(&MapItemMan, &ItemBuffer, usize, usize, bool) =
unsafe { std::mem::transmute(*MAP_ITEM_MAN_GRANT_ITEM_VA) };
grant_items(self, items.as_ref(), 0, 0, false);
}
}
#[repr(C, packed(4))]
pub struct CSMapItemDropChanger {
_vftable: usize,
_unk08: Vector<u8>,
_unk28: Vector<u8>,
_unk48: u8,
_unk4c: u32,
_unk50: u32,
_unk54: u32,
_unk58: u64,
_unk60: u32,
}
#[repr(C)]
pub struct MenuHandle {
_vftable: usize,
_unk08: u64,
_unk10: u64,
}
#[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,
_legacy_durability: i32,
_unused: u32,
}
impl ItemBufferEntry {
fn new(item: ItemId, quantity: u32) -> Self {
Self {
id: item,
quantity,
_legacy_durability: -1,
_unused: 0,
}
}
}
impl From<ItemId> for ItemBufferEntry {
fn from(id: ItemId) -> Self {
Self::new(id, 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) }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bitfield() {
assert_eq!(0x64, size_of::<CSMapItemDropChanger>());
assert_eq!(0x18, size_of::<MenuHandle>());
assert_eq!(0x198, size_of::<MapItemMan>());
}
}