use crate::{ assetbundle::AssetBundle, object::{Value, parser::FromValue}, }; use anyhow::{Result, anyhow, bail}; use log::debug; use serde::Serialize; use std::{ io::{Read, Seek}, marker::PhantomData, }; #[derive(Debug, Serialize)] pub struct PPtr { #[serde(skip, default)] pub(crate) _class: PhantomData, pub class: String, pub file_id: i32, pub path_id: i64, } impl FromValue for PPtr { fn from_value(v: Value) -> Result { let Value::Object { class, fields } = v else { bail!("PPtr expected but not an object") }; let inner = class .strip_prefix("PPtr<") .ok_or(anyhow!("not a PPtr"))? .strip_suffix(">") .ok_or(anyhow!("PPtr '>' missing"))?; Ok(PPtr { class: inner.to_owned(), _class: PhantomData, file_id: fields["m_FileID"] .as_i32() .ok_or(anyhow!("PPtr m_FileID is not i32"))?, path_id: fields["m_PathID"] .as_i64() .ok_or(anyhow!("PPtr m_FileID is not i64"))?, }) } } impl PPtr { pub fn cast(self) -> PPtr { PPtr { _class: PhantomData, class: self.class, file_id: self.file_id, path_id: self.path_id, } } pub fn is_null(&self) -> bool { self.path_id == 0 && self.file_id == 0 } pub fn load(&self, bundle: &mut AssetBundle) -> Result { debug!( "loading PPtr<{}> file_id={} path_id={}", self.class, self.file_id, self.path_id ); match self.file_id { 0 => { let ob = bundle .main .objects .iter() .find(|o| o.path_id == self.path_id) .ok_or(anyhow!("object with path id {} not found", self.path_id))? .to_owned(); bundle.main.read_object(ob)?.parse() } 1 => { let file = bundle.shared_assets.as_mut().ok_or(anyhow!( "shared assets referenced but not included in bundle" ))?; let ob = file .objects .iter() .find(|o| o.path_id == self.path_id) .ok_or(anyhow!("object with path id {} not found", self.path_id))? .to_owned(); file.read_object(ob)?.parse() } _ => unimplemented!(), } } }