use crate::{ classes::pptr::PPtr, serialized_file::SerializedFile, unityfs::{NodeReader, UnityFS, block_reader::BlockReader, multi_reader::MultiReader}, }; use anyhow::{Context, Result, anyhow}; use log::debug; use std::{ io::{Read, Seek}, marker::PhantomData, }; /// High-level wrapper around UnityFS, SerializedFile and all the classes. pub struct AssetBundle { pub fs: UnityFS, pub(crate) main: SerializedFile>>>, pub(crate) shared_assets: Option>>>>, } impl AssetBundle { pub fn open(inner: T) -> Result { let fs = UnityFS::open(inner).context("opening UnityFS")?; let main_ni = fs .find_main_file() .ok_or(anyhow!("AssetBundle seems to lack main file"))?; debug!("detected {:?} as main file", main_ni.name); let main = SerializedFile::read(fs.read(main_ni)?)?; let shared_assets = if let Some(n) = main.find_fs_shared_assets(&fs) { debug!("detect {:?} as shared assets", n.name); Some(SerializedFile::read(fs.read(&n)?)?) } else { None }; Ok(Self { fs, main, shared_assets, }) } pub fn all_toplevel(&self) -> impl Iterator { self.main .objects .iter() .map(|o| (0, o)) .chain( self.shared_assets .as_ref() .map(|e| e.objects.iter().map(|o| (1, o))) .into_iter() .flatten(), ) .map(|(fi, o)| PPtr { class: "".to_string(), file_id: fi, path_id: o.path_id, _class: PhantomData, }) } pub fn all_toplevel_of_class(&self, class_name: &str) -> impl Iterator { self.main .all_objects_of_class(class_name) .map(|o| (0, o)) .chain( self.shared_assets .as_ref() .map(|e| e.all_objects_of_class(class_name).map(|o| (1, o))) .into_iter() .flatten(), ) .map(|(fi, o)| PPtr { class: class_name.to_owned(), file_id: fi, path_id: o.path_id, _class: PhantomData, }) } }