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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
use crate::{
classes::pptr::PPtr,
serialized_file::SerializedFile,
unityfs::{UnityFS, header::NodeInfo},
};
use anyhow::{Context, Result, anyhow};
use log::debug;
use std::{
collections::HashMap,
fs::File,
io::{BufReader, Cursor, Read, Seek},
marker::PhantomData,
path::Path,
sync::{Arc, Mutex},
};
/// High-level wrapper around UnityFS, SerializedFile and all the classes.
pub struct AssetBundle<T> {
pub fs: UnityFS<T>,
pub ser_files: HashMap<String, Arc<Mutex<SerializedFile<Box<dyn ReadSeek>>>>>,
pub main_file: NodeInfo,
pub default_resources: SerializedFile<BufReader<File>>,
pub full_decomp: bool,
}
pub trait ReadSeek: Read + Seek + 'static {}
impl<T: Read + Seek + 'static> ReadSeek for T {}
impl<T: Read + Seek + 'static> AssetBundle<T> {
pub fn open(inner: T, support_dir: impl AsRef<Path>) -> Result<Self> {
let fs = UnityFS::open(inner).context("opening UnityFS")?;
let main_file = fs
.find_main_file()
.ok_or(anyhow!("AssetBundle seems to lack main file"))?
.clone();
debug!("opening default resource file");
let default_resources = SerializedFile::read(
BufReader::new(File::open(
support_dir.as_ref().join("unity default resources"),
)?),
"Library/unity default resources".to_owned(),
)?;
debug!("detected {:?} as main file", main_file.name);
Ok(Self {
fs,
main_file,
default_resources,
full_decomp: false,
ser_files: HashMap::new(),
})
}
pub fn get_fs_file(
&mut self,
nodeinfo: &NodeInfo,
) -> Result<Arc<Mutex<SerializedFile<Box<dyn ReadSeek>>>>> {
if !self.ser_files.contains_key(&nodeinfo.name) {
let mut node = self.fs.read(nodeinfo)?;
let node_box = if self.full_decomp {
let mut buf = Vec::new();
node.read_to_end(&mut buf)?;
Box::new(Cursor::new(buf)) as Box<dyn ReadSeek>
} else {
Box::new(node) as Box<dyn ReadSeek>
};
let file = SerializedFile::read(node_box, format!("archive:/{}", nodeinfo.name))?;
self.ser_files
.insert(nodeinfo.name.clone(), Arc::new(Mutex::new(file)));
}
Ok(self.ser_files.get(&nodeinfo.name).unwrap().clone())
}
pub fn all_toplevel(&mut self) -> Vec<PPtr> {
let main = self.get_fs_file(&self.main_file.clone()).unwrap();
let main = main.lock().unwrap();
let shared_assets = main.find_fs_shared_assets(&self.fs);
let main_obs = main
.objects
.iter()
.map(|o| {
(
main.ecx.clone(),
main.get_object_type_tree(o).unwrap().type_string.to_owned(),
o.to_owned(),
)
})
.collect::<Vec<_>>();
let shared_obs = if let Some(shared_assets) = shared_assets {
let shared = self.get_fs_file(&shared_assets).unwrap();
let shared = shared.lock().unwrap();
shared
.objects
.iter()
.map(|o| {
(
shared.ecx.clone(),
shared
.get_object_type_tree(o)
.unwrap()
.type_string
.to_owned(),
o.to_owned(),
)
})
.collect()
} else {
Vec::new()
};
main_obs
.into_iter()
.chain(shared_obs)
.map(|(ecx, class, o)| PPtr {
class,
ecx,
file_id: 0,
path_id: o.path_id,
_class: PhantomData,
})
.collect()
}
pub fn all_toplevel_of_class(&mut self, class_name: &str) -> Vec<PPtr> {
let main = self.get_fs_file(&self.main_file.clone()).unwrap();
let main = main.lock().unwrap();
let shared_assets = main.find_fs_shared_assets(&self.fs);
let main_obs = main
.all_objects_of_class(class_name)
.map(|o| (main.ecx.clone(), o.to_owned()))
.collect::<Vec<_>>();
let shared_obs = if let Some(shared_assets) = shared_assets {
let shared = self.get_fs_file(&shared_assets).unwrap();
let shared = shared.lock().unwrap();
shared
.all_objects_of_class(class_name)
.map(|o| (shared.ecx.clone(), o.to_owned()))
.collect()
} else {
Vec::new()
};
main_obs
.into_iter()
.chain(shared_obs)
.map(|(ecx, o)| PPtr {
class: class_name.to_owned(),
ecx,
file_id: 0,
path_id: o.path_id,
_class: PhantomData,
})
.collect()
}
}
|