unity_rs/classes/
texture2d.rs

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}