use std::ptr::NonNull;
use std::sync::LazyLock;
use std::thread;
use std::time::{Duration, Instant};
use pelite::pe64::Pe;
use vtable_rs::VPtr;
use windows::core::PCWSTR;
use crate::dlkr::DLPlainConditionSignal;
use crate::fd4::{FD4TaskBase, FD4TaskBaseVmt, FD4TaskData};
use crate::{
DLVector,
dlkr::DLPlainLightMutex,
fd4::{FD4BasicHashString, FD4Time},
rva,
util::system::{SystemInitError, wait_for_system_init},
};
use shared::{
FromStatic, InstanceError, OwnedPtr, RecurringTask, SharedTaskImp, Subclass, Superclass,
program::Program,
};
#[vtable_rs::vtable]
pub trait CSEzTaskVmt: FD4TaskBaseVmt {
fn eztask_execute(&mut self, data: &FD4TaskData);
fn register_task(&mut self);
fn free_task(&mut self);
}
#[vtable_rs::vtable]
pub trait CSEzTaskProxyVmt: FD4TaskBaseVmt {
fn get_task_group(&self) -> CSTaskGroupIndex;
}
#[repr(C)]
#[derive(Subclass, Superclass)]
#[subclass(base = FD4TaskBase)]
pub struct CSEzTask {
pub vftable: VPtr<dyn CSEzTaskVmt, Self>,
unk8: u32,
_padc: u32,
pub task_proxy: NonNull<CSEzTaskProxy>,
}
#[repr(C)]
#[derive(Subclass, Superclass)]
#[subclass(base = CSEzTask, base = FD4TaskBase)]
pub struct CSEzRabbitTaskBase {
pub ez_task: CSEzTask,
unk18: u32,
_pad1c: u32,
}
#[repr(C)]
#[derive(Subclass)]
#[subclass(base = CSEzRabbitTaskBase, base = CSEzTask, base = FD4TaskBase)]
pub struct CSEzRabbitNoUpdateTask {
pub ez_rabbit_task_base: CSEzRabbitTaskBase,
}
#[repr(C)]
pub struct CSEzUpdateTask<TEzTask, TSubject> {
pub base_task: TEzTask,
pub subject: NonNull<TSubject>,
pub executor: fn(&TSubject, &FD4Time),
}
#[repr(C)]
#[derive(Subclass)]
#[subclass(base = CSEzRabbitTaskBase, base = CSEzTask, base = FD4TaskBase)]
pub struct CSEzRabbitTask {
pub base: CSEzRabbitTaskBase,
}
#[repr(C)]
pub struct CSEzVoidTask<TEzTask, TSubject>
where
TEzTask: Subclass<CSEzTask>,
{
pub base_task: TEzTask,
pub subject: NonNull<TSubject>,
pub executor: fn(&TSubject, f32),
}
#[repr(C)]
#[derive(Subclass)]
#[subclass(base = FD4TaskBase)]
pub struct CSEzTaskProxy {
vftable: VPtr<dyn CSEzTaskProxyVmt, Self>,
unk8: u32,
_padc: u32,
pub task: Option<NonNull<CSEzTask>>,
}
#[repr(C)]
#[shared::singleton("CSTaskGroup")]
pub struct CSTaskGroup {
vftable: usize,
pub task_groups: [OwnedPtr<CSTimeLineTaskGroupIns>; 168],
}
#[repr(C)]
#[derive(Superclass)]
pub struct CSTaskGroupIns {
vftable: usize,
pub name: FD4BasicHashString,
unk40: [u8; 0x10],
}
#[repr(C)]
#[derive(Subclass)]
pub struct CSTimeLineTaskGroupIns {
pub base: CSTaskGroupIns,
pub step_impl: usize,
unk60: [u8; 0x20],
}
#[repr(C)]
#[shared::singleton("CSTask")]
pub struct CSTaskImp {
vftable: usize,
pub inner: OwnedPtr<CSTask>,
}
impl CSTaskImp {
pub fn wait_for_instance(timeout: Duration) -> Result<&'static Self, SystemInitError> {
let start = Instant::now();
wait_for_system_init(&Program::current(), timeout)?;
loop {
match unsafe { Self::instance() } {
Err(InstanceError::NotFound(_)) => return Err(SystemInitError::InvalidRva),
Err(InstanceError::Null(_)) => {
if start.elapsed() > timeout {
return Err(SystemInitError::Timeout);
}
thread::yield_now();
}
Ok(instance) => return Ok(instance),
}
}
}
}
static REGISTER_TASK_VA: LazyLock<u64> = LazyLock::new(|| {
Program::current()
.rva_to_va(rva::get().register_task)
.expect("Call target for REGISTER_TASK was not in exe")
});
impl SharedTaskImp<CSTaskGroupIndex, FD4TaskData> for CSTaskImp {
fn register_task_internal(&self, index: CSTaskGroupIndex, task: &RecurringTask<FD4TaskData>) {
let register_task: extern "C" fn(
&CSTaskImp,
CSTaskGroupIndex,
&RecurringTask<FD4TaskData>,
) = unsafe { std::mem::transmute(*REGISTER_TASK_VA) };
register_task(self, index, task);
}
}
#[repr(C)]
#[derive(Superclass)]
pub struct CSTaskBase {
vftable: usize,
allocator: usize,
pub task_groups: DLVector<TaskGroupEntry>,
pub task_group_index_max: u32,
_pad34: u32,
}
#[repr(C)]
pub struct TaskGroupEntry {
pub index: u32,
pub name: [u16; 64],
pub active: bool,
}
#[repr(C)]
#[derive(Subclass)]
pub struct CSTask {
pub task_base: CSTaskBase,
allocator: usize,
unk40: usize,
unk48: [usize; 3],
unk60: [usize; 3],
pub task_runner_manager: OwnedPtr<CSTaskRunnerManager>,
pub task_runners: [OwnedPtr<CSTaskRunner>; 6],
pub task_runners_ex: [OwnedPtr<CSTaskRunnerEx>; 6],
unke0: usize,
}
#[repr(C)]
pub struct CSTaskRunner {
vftable: usize,
task_queue: usize,
pub task_runner_manager: OwnedPtr<CSTaskRunnerManager>,
unk18: u32,
_pad1c: u32,
unk_string: PCWSTR,
}
#[repr(C)]
pub struct CSTaskRunnerEx {
}
#[repr(C)]
pub struct CSTaskRunnerManager {
allocator: usize,
pub concurrent_task_group_count: usize,
pub concurrent_task_group_policy: OwnedPtr<TaskGroupConcurrency>,
pub current_concurrent_task_group: u32,
unk1c: u32,
unk20: u32,
_pad24: u32,
pub mutex: DLPlainLightMutex,
pub signals: [DLPlainConditionSignal; 6],
unkb8: u32,
unkbc: u32,
unkc0: u32,
unkc4: u32,
unkc8: u32,
unkcc: u32,
unkd0: u32,
unkd4: u32,
}
#[repr(C)]
pub struct TaskGroupConcurrency {
pub slots: [TaskGroupConcurrencySlot; 6],
}
#[repr(C)]
pub struct TaskGroupConcurrencySlot {
pub task_group_index: u32,
pub task_group_concurrency_type: u32,
}
#[repr(u32)]
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy)]
pub enum CSTaskGroupIndex {
FrameBegin,
SteamThread0,
SteamThread1,
SteamThread2,
SteamThread3,
SteamThread4,
SteamThread5,
SystemStep,
ResStep,
PadStep,
GameFlowStep,
EndShiftWorldPosition,
GameMan,
TaskLineIdx_Sys,
TaskLineIdx_Test,
TaskLineIdx_NetworkFlowStep,
TaskLineIdx_InGame_InGameStep,
TaskLineIdx_InGame_InGameStayStep,
MovieStep,
RemoStep,
TaskLineIdx_InGame_MoveMapStep,
FieldArea_EndWorldAiManager,
EmkSystem_Pre,
EmkSystem_ConditionStatus,
EmkSystem_Post,
EventMan,
FlverResDelayDelectiionBegin,
TaskLineIdx_InGame_FieldAreaStep,
TaskLineIdx_InGame_TestNetStep,
TaskLineIdx_InGame_InGameMenuStep,
TaskLineIdx_InGame_TitleMenuStep,
TaskLineIdx_InGame_CommonMenuStep,
TaskLineIdx_FrpgNet_Sys,
TaskLineIdx_FrpgNet_Lobby,
TaskLineIdx_FrpgNet_ConnectMan,
TaskLineIdx_FrpgNet_Connect,
TaskLineIdx_FrpgNet_Other,
SfxMan,
FaceGenMan,
FrpgNetMan,
NetworkUserManager,
SessionManager,
BlockList,
LuaConsoleServer,
RmiMan,
ResMan,
SfxDebugger,
REMOTEMAN,
Geom_WaitActivateFade,
Geom_UpdateDraw,
Grass_BatchUpdate,
Grass_ResourceLoadKick,
Grass_ResourceLoad,
Grass_ResourceCleanup,
WorldChrMan_Respawn,
WorldChrMan_Prepare,
ChrIns_CalcUpdateInfo_PerfBegin,
ChrIns_CalcUpdateInfo,
ChrIns_CalcUpdateInfo_PerfEnd,
WorldChrMan_PrePhysics,
WorldChrMan_CalcOmissionLevel_Begin,
WorldChrMan_CalcOmissionLevel,
WorldChrMan_CalcOmissionLevel_End,
WorldChrMan_ConstructUpdateList,
WorldChrMan_ChrNetwork,
ChrIns_Prepare,
ChrIns_NaviCache,
ChrIns_AILogic_PerfBegin,
ChrIns_AILogic,
ChrIns_AILogic_PerfEnd,
AI_SimulationStep,
ChrIns_PreBehavior,
ChrIns_PreBehaviorSafe,
GeomModelInsCreatePartway_Begin,
HavokBehavior,
GeomModelInsCreatePartway_End,
ChrIns_BehaviorSafe,
ChrIns_PrePhysics_Begin,
ChrIns_PrePhysics,
ChrIns_PrePhysics_End,
NetFlushSendData,
ChrIns_PrePhysicsSafe,
ChrIns_RagdollSafe,
ChrIns_GarbageCollection,
GeomModelInsCreate,
AiBeginCollectGabage,
WorldChrMan_Update_RideCheck,
InGameDebugViewer,
LocationStep,
LocationUpdate_PrePhysics,
LocationUpdate_PrePhysics_Parallel,
LocationUpdate_PrePhysics_Post,
LocationUpdate_PostCloth,
LocationUpdate_PostCloth_Parallel,
LocationUpdate_PostCloth_Post,
LocationUpdate_DebugDraw,
EventCondition_BonfireNearEnemyCheck,
HavokWorldUpdate_Pre,
RenderingSystemUpdate,
HavokWorldUpdate_Post,
ChrIns_PreCloth,
ChrIns_PreClothSafe,
HavokClothUpdate_Pre_AddRemoveRigidBody,
HavokClothUpdate_Pre_ClothModelInsSafe,
HavokClothUpdate_Pre_ClothModelIns,
HavokClothUpdate_Pre_ClothManager,
CameraStep,
DrawParamUpdate,
GetNPAuthCode,
SoundStep,
HavokClothUpdate_Post_ClothManager,
HavokClothUpdate_Post_ClothModelIns,
HavokClothVertexUpdateFinishWait,
ChrIns_PostPhysics,
ChrIns_PostPhysicsSafe,
CSDistViewManager_Update,
HavokAi_SilhouetteGeneratorHelper_Begin,
WorldChrMan_PostPhysics,
GameFlowInGame_MoveMap_PostPhysics_0,
HavokAi_SilhouetteGeneratorHelper_End,
DmgMan_Pre,
DmgMan_ShapeCast,
DmgMan_Post,
GameFlowInGame_MoveMap_PostPhysics_1_Core0,
GameFlowInGame_MoveMap_PostPhysics_1_Core1,
GameFlowInGame_MoveMap_PostPhysics_1_Core2,
MenuMan,
WorldChrMan_Update_BackreadRequestPre,
ChrIns_Update_BackreadRequest,
WorldChrMan_Update_BackreadRequestPost,
HavokAi_World,
WorldAiManager_BeginUpdateFormation,
WorldAiManager_EndUpdateFormation,
GameFlowInGame_TestNet,
GameFlowInGame_InGameMenu,
GameFlowInGame_TitleMenu,
GameFlowInGame_CommonMenu,
GameFlowFrpgNet_Sys,
GameFlowFrpgNet_Lobby,
GameFlowFrpgNet_ConnectMan,
GameFlowFrpgNet_Connect,
GameFlowStep_Post,
ScaleformStep,
FlverResDelayDelectiionEnd,
Draw_Pre,
GraphicsStep,
DebugDrawMemoryBar,
DbgMenuStep,
DbgRemoteStep,
PlaylogSystemStep,
ReviewMan,
ReportSystemStep,
DbgDispStep,
DrawStep,
DrawBegin,
GameSceneDraw,
AdhocDraw,
DrawEnd,
Draw_Post,
SoundPlayLimitterUpdate,
BeginShiftWorldPosition,
FileStep,
FileStepUpdate_Begin,
FileStepUpdate_End,
Flip,
DelayDeleteStep,
AiEndCollectGabage,
RecordHeapStats,
FrameEnd,
}