use std::fmt::Display;
use bitfield::bitfield;
use thiserror::Error;
use crate::{
cs::{CSRandXorshift, OptionalItemId},
dlut::DLFixedVector,
};
use shared::{OwnedPtr, Subclass, Superclass};
#[repr(C)]
#[shared::singleton("CSGaitem")]
pub struct CSGaitemImp {
vftable: usize,
pub gaitems: [Option<OwnedPtr<CSGaitemIns>>; 5120],
gaitem_descriptors: [CSGaitemImpEntry; 5120],
indexes: [u32; 5120],
write_index: u32,
read_index: u32,
pub rand_xorshift: CSRandXorshift,
unk23028: [u8; 8],
pub is_being_serialized: bool,
unk23038: [u8; 7],
}
impl CSGaitemImp {
pub fn gaitem_ins_by_handle(&self, handle: &GaitemHandle) -> Option<&CSGaitemIns> {
if !handle.is_indexed() {
return None;
}
let index = handle.index() as usize;
if index > self.gaitems.len() {
return None;
}
Some(self.gaitems[index].as_ref()?.as_ref())
}
pub fn gaitem_ins_by_handle_mut(&mut self, handle: &GaitemHandle) -> Option<&mut CSGaitemIns> {
if !handle.is_indexed() {
return None;
}
let index = handle.index() as usize;
if index > self.gaitems.len() {
return None;
}
Some(self.gaitems[index].as_mut()?.as_mut())
}
}
#[repr(C)]
#[derive(Superclass)]
#[superclass(children(CSWepGaitemIns, CSGemGaitemIns))]
pub struct CSGaitemIns {
vftable: usize,
pub gaitem_handle: GaitemHandle,
pub item_id: OptionalItemId,
}
#[repr(C)]
pub struct CSGaitemImpEntry {
unindexed_gaitem_handle: u32,
ref_count: u32,
}
bitfield! {
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct GaitemHandle(u32);
impl Debug;
pub index, _: 15, 0;
_, set_index: 15, 0;
pub selector, _: 23, 0;
_, set_selector: 23, 0;
pub is_indexed, _: 23;
_, set_is_indexed: 23;
u8;
pub category_raw, _: 30, 28;
_, set_category_raw: 30, 28;
category_flag, set_category_flag: 31;
}
#[derive(Debug, Error)]
pub enum GaitemHandleError {
#[error("Not a valid Gaitem handle category {0}")]
InvalidCategory(u8),
}
impl GaitemHandle {
pub fn from_parts(selector: u32, category: GaitemCategory) -> Self {
let mut handle = GaitemHandle(0);
handle.set_selector(selector);
handle.set_category_raw(category as u8);
handle.set_category_flag(true);
handle
}
pub fn category(self) -> Result<GaitemCategory, GaitemHandleError> {
GaitemCategory::try_from(self.category_raw())
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum GaitemCategory {
Weapon = 0,
Protector = 1,
Accessory = 2,
Goods = 3,
Gem = 4,
}
impl TryFrom<u8> for GaitemCategory {
type Error = GaitemHandleError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(GaitemCategory::Weapon),
1 => Ok(GaitemCategory::Protector),
2 => Ok(GaitemCategory::Accessory),
3 => Ok(GaitemCategory::Goods),
4 => Ok(GaitemCategory::Gem),
_ => Err(GaitemHandleError::InvalidCategory(value)),
}
}
}
impl Display for GaitemHandle {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self.category() {
Ok(category) => match self.is_indexed() {
true => write!(
f,
"GaitemHandle({},0x{:x},{:?})",
self.index(),
self.selector(),
category
),
false => write!(f, "GaitemHandle(-1,{},{:?})", self.selector(), category),
},
Err(err) => write!(f, "GaitemHandle(0x{:x},{:?})", self.0, err),
}
}
}
#[repr(C)]
#[derive(Subclass)]
pub struct CSWepGaitemIns {
pub gaitem_ins: CSGaitemIns,
pub durability: u32,
pub reinforcement_param_id: u32,
pub gem_slot_table: CSGemSlotTable,
}
#[repr(C)]
pub struct CSGemSlotTable {
vtable: usize,
pub gem_slots: [CSGemSlot; 1],
}
#[repr(C)]
pub struct CSGemSlot {
vtable: usize,
pub gaitem_handle: GaitemHandle,
}
#[repr(C)]
#[derive(Subclass)]
pub struct CSGemGaitemIns {
pub gaitem_ins: CSGaitemIns,
pub weapon_handle: GaitemHandle,
}
#[repr(C)]
pub struct CSGaitemGameDataEntry {
pub item_id: OptionalItemId,
pub already_acquired: bool,
}
#[repr(C)]
pub struct CSGaitemGameData {
pub igame_data_elem_vftable: usize,
pub gaitem_entries: DLFixedVector<CSGaitemGameDataEntry, 14000>,
}
#[cfg(test)]
mod test {
use crate::cs::{
CSGaitemImp, CSGaitemIns, CSGemGaitemIns, CSGemSlot, CSGemSlotTable, CSWepGaitemIns,
};
#[test]
fn proper_sizes() {
assert_eq!(0x19038, size_of::<CSGaitemImp>());
assert_eq!(0x10, size_of::<CSGaitemIns>());
assert_eq!(0x30, size_of::<CSWepGaitemIns>());
assert_eq!(0x18, size_of::<CSGemSlotTable>());
assert_eq!(0x10, size_of::<CSGemSlot>());
assert_eq!(0x18, size_of::<CSGemGaitemIns>());
}
}