aboutsummaryrefslogtreecommitdiff
path: root/src/unityfs/mod.rs
blob: bc7e3ec55f975e8886cbcc39b0c469369e14e427 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
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)
    }
}