1use crate::player::{ObjRef, Player};
8use crate::resource::StreamingInfo;
9use crate::value::Value;
10use crate::{Error, Result, invalid};
11
12pub const USAGE_NORMAL_DXT5NM: i64 = 3;
14pub const USAGE_NORMAL_PLAIN: i64 = 4;
16
17#[derive(Clone, Debug, PartialEq)]
19pub struct UnityTexture {
20 pub name: String,
22 pub width: u32,
24 pub height: u32,
26 pub format: i64,
28 pub mips: u32,
30 pub srgb: bool,
32 pub usage: i64,
34 pub wrap: [i64; 2],
36 pub filter: i64,
38 pub mip_bias: f32,
40 pub aniso: i64,
42 pub data: Vec<u8>,
44}
45
46fn dimension(v: &Value, k: &str) -> Result<u32> {
48 let n = int(v, k)?;
49 u32::try_from(n).map_err(|_| invalid(format!("texture: {k} of {n}")))
50}
51
52fn int(v: &Value, k: &str) -> Result<i64> {
53 v.get(k)
54 .and_then(Value::as_i64)
55 .ok_or_else(|| invalid(format!("texture: no integer {k}")))
56}
57
58fn float(v: &Value, k: &str) -> Result<f32> {
59 v.get(k)
60 .and_then(Value::as_f32)
61 .ok_or_else(|| invalid(format!("texture: no float {k}")))
62}
63
64#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67pub enum TextureFormat {
68 Alpha8,
70 Argb4444,
72 Rgb24,
74 Rgba32,
76 Dxt1,
78 Dxt5,
80 Bc6h,
82 Bc7,
84 Bc4,
86 Bc5,
88 R8,
90}
91
92impl TextureFormat {
93 pub fn from_id(id: i64) -> Result<TextureFormat> {
95 Ok(match id {
96 1 => TextureFormat::Alpha8,
97 2 => TextureFormat::Argb4444,
98 3 => TextureFormat::Rgb24,
99 4 => TextureFormat::Rgba32,
100 10 => TextureFormat::Dxt1,
101 12 => TextureFormat::Dxt5,
102 24 => TextureFormat::Bc6h,
103 25 => TextureFormat::Bc7,
104 26 => TextureFormat::Bc4,
105 27 => TextureFormat::Bc5,
106 63 => TextureFormat::R8,
107 28 | 29 | 64 | 65 => {
108 return Err(Error::Unsupported(format!("crunched texture format {id}")));
109 }
110 id => return Err(Error::Unsupported(format!("texture format {id}"))),
111 })
112 }
113
114 fn block(self) -> (u32, usize) {
116 match self {
117 TextureFormat::Alpha8 | TextureFormat::R8 => (1, 1),
118 TextureFormat::Argb4444 => (1, 2),
119 TextureFormat::Rgb24 => (1, 3),
120 TextureFormat::Rgba32 => (1, 4),
121 TextureFormat::Dxt1 | TextureFormat::Bc4 => (4, 8),
122 TextureFormat::Dxt5 | TextureFormat::Bc6h | TextureFormat::Bc7 | TextureFormat::Bc5 => (4, 16),
123 }
124 }
125
126 pub fn level_size(self, width: u32, height: u32) -> Option<usize> {
129 let (side, bytes) = self.block();
130 let blocks_x = usize::try_from(width.div_ceil(side).max(1)).ok()?;
131 let blocks_y = usize::try_from(height.div_ceil(side).max(1)).ok()?;
132 blocks_x.checked_mul(blocks_y)?.checked_mul(bytes)
133 }
134}
135
136impl UnityTexture {
137 pub fn read(player: &Player, r: &ObjRef) -> Result<Self> {
139 let t = player.decode(r)?;
140 let settings = t
141 .get("m_TextureSettings")
142 .ok_or_else(|| invalid("texture: no m_TextureSettings"))?;
143 let stream = t
144 .get("m_StreamData")
145 .map(StreamingInfo::of)
146 .transpose()?
147 .flatten();
148 let data = match stream {
149 Some(s) => player.read_stream(&r.file, &s)?,
150 None => t
151 .get("image data")
152 .and_then(Value::as_bytes)
153 .ok_or_else(|| invalid("texture: no image data"))?
154 .to_vec(),
155 };
156 Ok(UnityTexture {
157 name: t.get("m_Name").and_then(Value::as_str).unwrap_or("").to_owned(),
158 width: dimension(&t, "m_Width")?,
159 height: dimension(&t, "m_Height")?,
160 format: int(&t, "m_TextureFormat")?,
161 mips: dimension(&t, "m_MipCount")?,
162 srgb: int(&t, "m_ColorSpace")? == 1,
163 usage: int(&t, "m_LightmapFormat")?,
164 wrap: [int(settings, "m_WrapU")?, int(settings, "m_WrapV")?],
165 filter: int(settings, "m_FilterMode")?,
166 mip_bias: float(settings, "m_MipBias")?,
167 aniso: int(settings, "m_Aniso")?,
168 data,
169 })
170 }
171
172 pub fn levels(&self) -> Result<(TextureFormat, Vec<&[u8]>)> {
176 if self.width == 0 || self.height == 0 || self.mips == 0 {
177 return Err(Error::Unsupported(format!(
178 "{}: an empty texture ({}x{}, {} levels)",
179 self.name, self.width, self.height, self.mips
180 )));
181 }
182 let format = TextureFormat::from_id(self.format)?;
183 if self.mips > 32 {
185 return Err(invalid(format!("{}: {} mip levels", self.name, self.mips)));
186 }
187 let mut levels = Vec::with_capacity(self.mips as usize);
188 let mut at = 0usize;
189 for i in 0..self.mips {
190 let (w, h) = ((self.width >> i).max(1), (self.height >> i).max(1));
191 let size = format.level_size(w, h).ok_or_else(|| {
192 invalid(format!(
193 "{}: level {i} of {w}x{h} does not fit in memory",
194 self.name
195 ))
196 })?;
197 let bytes = at
198 .checked_add(size)
199 .and_then(|end| self.data.get(at..end))
200 .ok_or_else(|| {
201 invalid(format!(
202 "{}: level {i} past the {} bytes of pixels",
203 self.name,
204 self.data.len()
205 ))
206 })?;
207 levels.push(bytes);
208 at = at.saturating_add(size);
209 }
210 if at != self.data.len() {
211 return Err(invalid(format!(
212 "{}: {} levels use {at} of {} bytes",
213 self.name,
214 self.mips,
215 self.data.len()
216 )));
217 }
218 Ok((format, levels))
219 }
220}