unity_asset/environment/imp/
container.rs1use super::*;
2
3impl Environment {
4 fn scan_pptr(value: &UnityValue) -> Option<(i32, i64)> {
5 match value {
6 UnityValue::Object(obj) => {
7 let file_id = obj
8 .get("fileID")
9 .or_else(|| obj.get("m_FileID"))
10 .and_then(|v| v.as_i64())
11 .and_then(|v| i32::try_from(v).ok());
12 let path_id = obj
13 .get("pathID")
14 .or_else(|| obj.get("m_PathID"))
15 .and_then(|v| v.as_i64());
16
17 if let (Some(file_id), Some(path_id)) = (file_id, path_id) {
18 return Some((file_id, path_id));
19 }
20
21 for (_, v) in obj.iter() {
22 if let Some(pptr) = Self::scan_pptr(v) {
23 return Some(pptr);
24 }
25 }
26
27 None
28 }
29 UnityValue::Array(items) => {
30 for item in items {
31 if let Some(pptr) = Self::scan_pptr(item) {
32 return Some(pptr);
33 }
34 }
35 None
36 }
37 _ => None,
38 }
39 }
40
41 fn extract_assetbundle_container_from_typetree(
42 &self,
43 context: &BinaryObjectRef<'_>,
44 parsed: &UnityObject,
45 ) -> Vec<BundleContainerEntry> {
46 let mut out = Vec::new();
47
48 let Some(UnityValue::Array(items)) = parsed.class.get("m_Container") else {
49 return out;
50 };
51
52 for item in items {
53 let (asset_path, second) = match item {
54 UnityValue::Array(pair) if pair.len() == 2 => {
56 let Some(asset_path) = pair[0].as_str() else {
57 continue;
58 };
59 (asset_path.to_string(), &pair[1])
60 }
61 UnityValue::Object(obj) => {
63 let first = obj.get("first").and_then(|v| v.as_str());
64 let second = obj.get("second").or_else(|| obj.get("value"));
65 let (Some(first), Some(second)) = (first, second) else {
66 continue;
67 };
68 (first.to_string(), second)
69 }
70 _ => continue,
71 };
72
73 let Some((file_id, path_id)) = Self::scan_pptr(second) else {
74 continue;
75 };
76 if path_id == 0 {
77 continue;
78 }
79
80 let key = self.resolve_binary_pptr(context, file_id, path_id);
81 out.push(BundleContainerEntry {
82 bundle_source: context.source.clone(),
83 asset_index: context.asset_index.unwrap_or(0),
84 asset_path,
85 file_id,
86 path_id,
87 key,
88 });
89 }
90
91 out
92 }
93
94 pub fn bundle_container_entries<P: AsRef<Path>>(
99 &self,
100 bundle_path: P,
101 ) -> Result<Vec<BundleContainerEntry>> {
102 let bundle_source = BinarySource::path(bundle_path.as_ref());
103 self.bundle_container_entries_source(&bundle_source)
104 }
105
106 pub fn bundle_container_entries_source(
107 &self,
108 bundle_source: &BinarySource,
109 ) -> Result<Vec<BundleContainerEntry>> {
110 match self.bundle_container_cache.read() {
111 Ok(cache) => {
112 if let Some(cached) = cache.get(bundle_source) {
113 return Ok(cached.clone());
114 }
115 }
116 Err(e) => {
117 let cache = e.into_inner();
118 if let Some(cached) = cache.get(bundle_source) {
119 return Ok(cached.clone());
120 }
121 }
122 }
123
124 let (key, bundle) = self.bundles.get_key_value(bundle_source).ok_or_else(|| {
125 UnityAssetError::format(format!(
126 "AssetBundle source not loaded: {}",
127 bundle_source.describe()
128 ))
129 })?;
130
131 let mut out: Vec<BundleContainerEntry> = Vec::new();
132 let typetree_options = self.options.typetree;
133 let reporter = self.reporter.clone();
134
135 for (asset_index, file) in bundle.assets.iter().enumerate() {
136 for object in file.object_handles() {
137 if object.class_id() != 142 {
138 continue;
139 }
140 let obj_ref = BinaryObjectRef {
141 source: key,
142 source_kind: BinarySourceKind::AssetBundle,
143 asset_index: Some(asset_index),
144 object,
145 typetree_options,
146 reporter: reporter.clone(),
147 };
148
149 if object.file().enable_type_tree
151 && let Ok(parsed) = obj_ref.read()
152 {
153 let extracted =
154 self.extract_assetbundle_container_from_typetree(&obj_ref, &parsed);
155 if !extracted.is_empty() {
156 out.extend(extracted);
157 continue;
158 }
159 }
160
161 if let Ok(raw_entries) = object.file().assetbundle_container_raw(object.info()) {
163 for (asset_path, file_id, path_id) in raw_entries {
164 if path_id == 0 {
165 continue;
166 }
167 let key = self
168 .resolve_binary_pptr(&obj_ref, file_id, path_id)
169 .or_else(|| {
170 let matches =
174 self.find_binary_objects_in_source_id(obj_ref.source, path_id);
175 if matches.len() == 1 {
176 Some(matches[0].key())
177 } else {
178 None
179 }
180 });
181 out.push(BundleContainerEntry {
182 bundle_source: obj_ref.source.clone(),
183 asset_index,
184 asset_path,
185 file_id,
186 path_id,
187 key,
188 });
189 }
190 }
191 }
192 }
193
194 match self.bundle_container_cache.write() {
195 Ok(mut cache) => {
196 cache.insert(bundle_source.clone(), out.clone());
197 }
198 Err(e) => {
199 e.into_inner().insert(bundle_source.clone(), out.clone());
200 }
201 }
202 Ok(out)
203 }
204
205 pub fn find_bundle_container_entries(&self, pattern: &str) -> Vec<BundleContainerEntry> {
207 let mut bundle_sources: Vec<&BinarySource> = self.bundles.keys().collect();
208 bundle_sources.sort();
209
210 let mut out = Vec::new();
211 for bundle_source in bundle_sources {
212 if let Ok(entries) = self.bundle_container_entries_source(bundle_source) {
213 out.extend(
214 entries
215 .into_iter()
216 .filter(|e| e.asset_path.contains(pattern)),
217 );
218 }
219 }
220 out
221 }
222
223 pub fn find_binary_object_keys_in_bundle_container(
225 &self,
226 pattern: &str,
227 ) -> Vec<(String, BinaryObjectKey)> {
228 self.find_bundle_container_entries(pattern)
229 .into_iter()
230 .filter_map(|e| e.key.map(|k| (e.asset_path, k)))
231 .collect()
232 }
233}