1#![allow(dead_code, non_upper_case_globals)]
2use crate::classes::FromObject;
3use crate::env::Object;
4use crate::error::{UnityError, UnityResult};
5use crate::object::ObjectInfo;
6use crate::reader::{ByteOrder, Reader};
7use dashmap::mapref::one::Ref;
8use dashmap::DashMap;
9use image::{DynamicImage, ImageBuffer, Rgba};
10use num_enum::FromPrimitive;
11use std::sync::Arc;
12
13use super::decode::{decode_etc1, decode_etc2, decode_etc2a8};
14
15#[allow(non_camel_case_types, non_upper_case_globals)]
16#[derive(Debug, Eq, PartialEq, FromPrimitive, Clone, Copy)]
17#[repr(i32)]
18pub enum TextureFormat {
19 #[num_enum(default)]
20 UnknownType = -1,
21 Alpha8 = 1,
22 ARGB4444,
23 RGB24,
24 RGBA32,
25 ARGB32,
26 RGB565 = 7,
27 R16 = 9,
28 DXT1,
29 DXT5 = 12,
30 RGBA4444,
31 BGRA32,
32 RHalf,
33 RGHalf,
34 RGBAHalf,
35 RFloat,
36 RGFloat,
37 RGBAFloat,
38 YUY2,
39 RGB9e5Float,
40 BC4 = 26,
41 BC5,
42 BC6H = 24,
43 BC7,
44 DXT1Crunched = 28,
45 DXT5Crunched,
46 PVRTC_RGB2,
47 PVRTC_RGBA2,
48 PVRTC_RGB4,
49 PVRTC_RGBA4,
50 ETC_RGB4,
51 ATC_RGB4,
52 ATC_RGBA8,
53 EAC_R = 41,
54 EAC_R_SIGNED,
55 EAC_RG,
56 EAC_RG_SIGNED,
57 ETC2_RGB,
58 ETC2_RGBA1,
59 ETC2_RGBA8,
60 ASTC_RGB_4x4,
61 ASTC_RGB_5x5,
62 ASTC_RGB_6x6,
63 ASTC_RGB_8x8,
64 ASTC_RGB_10x10,
65 ASTC_RGB_12x12,
66 ASTC_RGBA_4x4,
67 ASTC_RGBA_5x5,
68 ASTC_RGBA_6x6,
69 ASTC_RGBA_8x8,
70 ASTC_RGBA_10x10,
71 ASTC_RGBA_12x12,
72 ETC_RGB4_3DS,
73 ETC_RGBA8_3DS,
74 RG16,
75 R8,
76 ETC_RGB4Crunched,
77 ETC2_RGBA8Crunched,
78 ASTC_HDR_4x4,
79 ASTC_HDR_5x5,
80 ASTC_HDR_6x6,
81 ASTC_HDR_8x8,
82 ASTC_HDR_10x10,
83 ASTC_HDR_12x12,
84}
85
86impl Default for TextureFormat {
87 fn default() -> Self {
88 Self::UnknownType
89 }
90}
91#[derive(Default)]
92pub struct GLTextureSettings {
93 filter_mode: i32,
94 aniso: i32,
95 mip_bias: f32,
96 wrap_mode: i32,
97}
98
99impl GLTextureSettings {
100 pub fn load(object_info: &ObjectInfo, r: &mut Reader) -> UnityResult<Self> {
101 let mut result = Self::default();
102 result.filter_mode = r.read_i32()?;
103 result.aniso = r.read_i32()?;
104 result.mip_bias = r.read_f32()?;
105 if object_info.version[0] >= 2017 {
106 result.wrap_mode = r.read_i32()?;
107 let _wrap_w = r.read_i32()?;
108 let _wrap_h = r.read_i32()?;
109 } else {
110 result.wrap_mode = r.read_i32()?;
111 }
112 Ok(result)
113 }
114}
115
116#[derive(Default)]
117pub struct StreamingInfo {
118 offset: u64,
119 size: u32,
120 path: String,
121}
122
123impl StreamingInfo {
124 pub fn load(object_info: &ObjectInfo, r: &mut Reader) -> UnityResult<Self> {
125 let mut result = Self::default();
126 if object_info.version[0] >= 2020 {
127 result.offset = r.read_u64()?;
128 } else {
129 result.offset = r.read_u32()? as u64;
130 }
131 result.size = r.read_u32()?;
132 result.path = r.read_aligned_string()?;
133 Ok(result)
134 }
135}
136
137#[derive(Default)]
138pub struct Texture2D {
139 cache: Arc<DashMap<i64, DynamicImage>>,
140 pub path_id: i64,
141 pub name: String,
142 pub forced_fallback_format: i32,
143 pub downscale_fallback: bool,
144 pub width: i32,
145 pub height: i32,
146 pub complete_image_size: i32,
147 pub format: TextureFormat,
148 pub mip_map: bool,
149 pub mip_count: i32,
150 pub is_read_able: bool,
151 pub image_count: i32,
152 pub texture_dimension: i32,
153 pub light_map_format: i32,
154 pub color_space: i32,
155 pub size: i32,
156 pub stream_info: StreamingInfo,
157 pub texture_setting: GLTextureSettings,
158 pub data: Vec<u8>,
159}
160
161impl<'a> FromObject<'a> for Texture2D {
162 fn load(object: &Object) -> UnityResult<Self> {
163 let mut r = object.info.get_reader();
164 let mut result = Self::default();
165 result.cache = object.cache.clone();
166 result.path_id = object.info.path_id;
167 result.name = r.read_aligned_string()?;
168 let version = &object.info.version;
169 if version[0] > 2017 || (version[0] == 2017 && version[1] >= 3) {
170 result.forced_fallback_format = r.read_i32()?;
171 result.downscale_fallback = r.read_bool()?;
172 if version[0] > 2020 || (version[0] == 2020 && version[1] >= 2) {
173 let _is_alpha_channel_optional = r.read_bool()?;
174 }
175 r.align(4)?;
176 }
177 result.width = r.read_i32()?;
178 result.height = r.read_i32()?;
179 result.complete_image_size = r.read_i32()?;
180 if object.info.version[0] >= 2020 {
181 let _mips_stripped = r.read_i32()?;
182 }
183 result.format = TextureFormat::from(r.read_i32()?);
184 let mut _mip_map = false;
185 if object.info.version[0] < 5 {
186 _mip_map = r.read_bool()?;
187 } else if object.info.version[0] == 5 && object.info.version[1] < 2 {
188 _mip_map = r.read_bool()?;
189 } else {
190 result.mip_count = r.read_i32()?;
191 }
192 if version[0] > 2 || (version[0] == 2 && version[1] >= 6) {
193 result.is_read_able = r.read_bool()?;
194 }
195 if version[0] >= 2020 {
196 let _is_pre_processed = r.read_bool()?;
197 }
198 if version[0] > 2019 || (version[0] == 2019 && version[1] >= 3) {
199 let _is_ignore_master_texture_limit = r.read_bool()?;
200 }
201 if version[0] >= 3 {
202 if version[0] < 5 || (version[0] == 5 && version[1] <= 4) {
203 let _read_allowed = r.read_bool()?;
204 }
205 }
206 if version[0] > 2018 || (version[0] == 2018 && version[1] >= 2) {
207 let _streaming_mip_maps = r.read_bool()?;
208 }
209 r.align(4)?;
210 if version[0] > 2018 || (version[0] == 2018 && version[1] >= 2) {
211 let _streaming_mip_maps_priority = r.read_i32()?;
212 }
213 result.image_count = r.read_i32()?;
214 result.texture_dimension = r.read_i32()?;
215 result.texture_setting = GLTextureSettings::load(&object.info, &mut r)?;
216 if version[0] >= 3 {
217 result.light_map_format = r.read_i32()?;
218 }
219 if version[0] > 3 || (version[0] == 3 && version[1] >= 5) {
220 result.color_space = r.read_i32()?;
221 }
222 if version[0] > 2020 || (version[0] == 2020 && version[1] >= 2) {
223 let length = r.read_i32()?;
224 let _platform_blob = r.read_u8_slice(length as usize)?;
225 r.align(4)?;
226 }
227 result.size = r.read_i32()?;
228 if result.size == 0 && ((version[0] == 5 && version[1] >= 3) || version[0] > 5) {
229 result.stream_info = StreamingInfo::load(&object.info, &mut r)?;
230 }
231 if result.stream_info.path.is_empty() {
232 result.data = r.read_u8_list(result.size as usize)?;
233 } else {
234 let path = result.stream_info.path.split("/").last().ok_or(UnityError::InvalidValue)?;
235 for i in 0..object.bundle.nodes.len() {
236 let node = &object.bundle.nodes[i];
237 if node.path != path {
238 continue;
239 }
240 let file = &object.bundle.files[i];
241 let mut r = Reader::new(file.as_slice(), ByteOrder::Big);
242 r.set_offset(result.stream_info.offset as usize)?;
243 result.data = r.read_u8_list(result.stream_info.size as usize)?;
244 }
245 }
246 Ok(result)
247 }
248}
249impl Texture2D {
250 pub fn decode_image(&self) -> UnityResult<Ref<i64, DynamicImage>> {
251 if let Some(img) = self.cache.get(&self.path_id) {
252 return Ok(img);
253 }
254 let img = self.decode_image_without_cache()?;
255 self.cache.insert(self.path_id, img);
256 return Ok(self.cache.get(&self.path_id).unwrap());
257 }
258
259 pub fn decode_image_without_cache(&self) -> UnityResult<DynamicImage> {
260 let width = self.width;
261 let height = self.height;
262 let format = self.format;
263 return match format {
264 TextureFormat::ETC2_RGBA8 => {
265 let mut result: ImageBuffer<Rgba<u8>, Vec<u8>> = ImageBuffer::new(width as u32, height as u32);
266 decode_etc2a8(&self.data, width as usize, height as usize, result.as_mut());
267 Ok(result.into())
268 }
269 TextureFormat::ETC2_RGB => {
270 let mut result: ImageBuffer<Rgba<u8>, Vec<u8>> = ImageBuffer::new(width as u32, height as u32);
271 decode_etc2(&self.data, width as usize, height as usize, result.as_mut());
272 Ok(result.into())
273 }
274 TextureFormat::ETC_RGB4 => {
275 let mut result: ImageBuffer<Rgba<u8>, Vec<u8>> = ImageBuffer::new(width as u32, height as u32);
276 decode_etc1(&self.data, width as usize, height as usize, result.as_mut());
277 Ok(result.into())
278 }
279 _ => Err(UnityError::Unimplemented),
280 };
281 }
282}