use std::mem::ManuallyDrop;
use crate::DLMap;
use shared::OwnedPtr;
#[repr(transparent)]
pub struct EventFlag(u32);
impl From<u32> for EventFlag {
fn from(value: u32) -> Self {
EventFlag(value)
}
}
impl EventFlag {
pub fn group(&self) -> u32 {
self.0 / 1000
}
pub fn byte(&self) -> u32 {
(self.0 % 1000) / 8
}
pub fn bit(&self) -> u32 {
7 - ((self.0 % 1000) % 8)
}
}
#[repr(C)]
#[shared::singleton("CSEventFlagMan")]
pub struct CSEventFlagMan {
pub virtual_memory_flag: CSFD4VirtualMemoryFlag,
pub world_type: u32,
unk7c: [u8; 0x1f4],
}
#[repr(C)]
pub struct CSFD4VirtualMemoryFlag {
vftable: usize,
allocator: usize,
unk10: u32,
unk14: u32,
unk18: u32,
pub event_flag_divisor: u32,
pub event_flag_holder_size: u32,
pub event_flag_holder_count: u32,
pub flag_blocks: *mut FlagBlock,
pub flag_block_descriptors: DLMap<u32, FlagBlockDescriptor>,
unk38: [u8; 0x30],
}
impl CSFD4VirtualMemoryFlag {
pub fn set_flag(&mut self, flag: impl Into<EventFlag>, state: bool) {
let flag: EventFlag = flag.into();
let base = self.flag_blocks;
let ptr = self
.flag_block_descriptors
.find(&flag.group())
.and_then(|d| unsafe { d.resolve(base) });
if let Some(block) = ptr {
unsafe { (*block).set(flag, state) };
}
}
pub fn get_flag(&self, flag: impl Into<EventFlag>) -> bool {
let flag: EventFlag = flag.into();
let base = self.flag_blocks;
self.flag_block_descriptors
.find(&flag.group())
.and_then(|d| unsafe { d.resolve(base) })
.map(|block| unsafe { (*block).get(flag) })
.unwrap_or(false)
}
}
#[repr(C)]
pub struct FlagBlockDescriptor {
pub location_mode: u32,
location: FlagBlockLocationUnion,
}
impl FlagBlockDescriptor {
unsafe fn resolve(&self, base: *mut FlagBlock) -> Option<*mut FlagBlock> {
match self.location_mode {
1 => Some(unsafe { base.add(self.location.holder_offset as usize) }),
2 => Some(unsafe { self.location.external_location.as_ptr() }),
_ => None,
}
}
}
union FlagBlockLocationUnion {
holder_offset: u32,
external_location: ManuallyDrop<OwnedPtr<FlagBlock>>,
}
#[repr(C)]
pub struct FlagBlock {
bytes: [u8; 125],
}
impl FlagBlock {
pub fn set(&mut self, flag: EventFlag, state: bool) {
let byte = &mut self.bytes[flag.byte() as usize];
let mask = 0b00000001 << flag.bit();
*byte = match state {
true => *byte | mask,
false => *byte & !mask,
}
}
pub fn get(&self, flag: EventFlag) -> bool {
let byte = &self.bytes[flag.byte() as usize];
let mask = 0b00000001 << flag.bit();
(*byte & mask) != 0
}
}