diff options
Diffstat (limited to 'src/unityfs/mod.rs')
-rw-r--r-- | src/unityfs/mod.rs | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/src/unityfs/mod.rs b/src/unityfs/mod.rs new file mode 100644 index 0000000..bc7e3ec --- /dev/null +++ b/src/unityfs/mod.rs @@ -0,0 +1,95 @@ +pub mod block_reader; +pub mod header; +pub mod multi_reader; + +use anyhow::Result; +use block_reader::BlockReader; +use header::{BlockInfo, NodeInfo, UnityFSHeader}; +use log::debug; +use multi_reader::MultiReader; +use std::{ + io::{Error, ErrorKind, Read, Seek, SeekFrom}, + sync::Arc, +}; + +pub struct UnityFS<T> { + reader: MultiReader<T>, + blocks: Arc<Vec<BlockInfo>>, + inner_seek_offset: u64, + pub header: UnityFSHeader, +} + +pub struct NodeReader<T> { + inner: T, + position: u64, + offset: u64, + size: u64, +} + +impl<T: Read + Seek> UnityFS<T> { + pub fn open(mut file: T) -> Result<Self> { + let (header, blocks) = UnityFSHeader::read(&mut file)?; + let inner_seek_offset = file.stream_position()?; + + Ok(Self { + blocks: Arc::new(blocks), + header, + inner_seek_offset, + reader: MultiReader::new(file)?, + }) + } + + pub fn find_main_file(&self) -> Option<&NodeInfo> { + self.header.nodes().iter().find(|n| { + !n.name.ends_with(".resource") + && !n.name.ends_with(".resS") + && !n.name.ends_with(".sharedAssets") + }) + } + + pub fn read<'a>(&'a self, node: &NodeInfo) -> Result<NodeReader<BlockReader<MultiReader<T>>>> { + let mut inner = self.reader.clone(); + inner.seek(SeekFrom::Start(self.inner_seek_offset))?; + let br = BlockReader::new(self.blocks.clone(), inner, self.inner_seek_offset); + Ok(NodeReader { + size: node.size, + offset: node.offset, + position: 0, + inner: br, + }) + } +} + +impl<T: Read> Read for NodeReader<T> { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { + let bytes_left = self.size - self.position; + let end = buf.len().min(bytes_left as usize); + let size = self.inner.read(&mut buf[..end])?; + self.position += size as u64; + Ok(size) + } +} +impl<T: Seek + Read> Seek for NodeReader<T> { + fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> { + match pos { + SeekFrom::Current(n) if n >= 0 => { + for _ in 0..n { + self.read_exact(&mut [0u8])?; + } + Ok(self.stream_position()?) + } + SeekFrom::Start(n) => { + debug!("seek node to {n} (off={})", self.offset); + if n > self.size { + return Err(Error::new(ErrorKind::NotSeekable, "seek out of bounds")); + } + self.position = n; + self.inner.seek(SeekFrom::Start(self.offset + n)) + } + _ => unimplemented!(), + } + } + fn stream_position(&mut self) -> std::io::Result<u64> { + Ok(self.position) + } +} |