aboutsummaryrefslogtreecommitdiff
path: root/src/unityfs/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/unityfs/mod.rs')
-rw-r--r--src/unityfs/mod.rs95
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)
+ }
+}