1pub use super::locator::MipmapLocator;
2pub use super::version::BlpVersion;
3use std::fmt;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub enum BlpContentTag {
12 Jpeg,
13 Direct,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct UnknownContent(u32);
18
19impl fmt::Display for UnknownContent {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 write!(f, "Unknown content field value: {}", self.0)
22 }
23}
24
25impl TryFrom<u32> for BlpContentTag {
26 type Error = UnknownContent;
27
28 fn try_from(val: u32) -> Result<BlpContentTag, Self::Error> {
29 match val {
30 0 => Ok(BlpContentTag::Jpeg),
31 1 => Ok(BlpContentTag::Direct),
32 _ => Err(UnknownContent(val)),
33 }
34 }
35}
36
37impl From<BlpContentTag> for u32 {
38 fn from(val: BlpContentTag) -> u32 {
39 match val {
40 BlpContentTag::Jpeg => 0,
41 BlpContentTag::Direct => 1,
42 }
43 }
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
47pub struct BlpHeader {
48 pub version: BlpVersion,
49 pub content: BlpContentTag,
50 pub flags: BlpFlags,
51 pub width: u32,
52 pub height: u32,
53 pub mipmap_locator: MipmapLocator,
54}
55
56impl BlpHeader {
57 pub fn mipmaps_count(&self) -> usize {
59 if self.has_mipmaps() {
60 let width_n = (self.width as f32).log2() as usize;
61 let height_n = (self.height as f32).log2() as usize;
62 width_n.max(height_n)
63 } else {
64 0
65 }
66 }
67
68 pub fn has_mipmaps(&self) -> bool {
70 self.flags.has_mipmaps()
71 }
72
73 pub fn mipmap_size(&self, i: usize) -> (u32, u32) {
76 if i == 0 {
77 (self.width, self.height)
78 } else {
79 ((self.width >> i).max(1), (self.height >> i).max(1))
80 }
81 }
82
83 pub fn mipmap_pixels(&self, i: usize) -> u32 {
86 let (w, h) = self.mipmap_size(i);
87 w * h
88 }
89
90 pub fn alpha_bits(&self) -> u32 {
92 self.flags.alpha_bits()
93 }
94
95 pub fn internal_mipmaps(&self) -> Option<([u32; 16], [u32; 16])> {
97 match self.mipmap_locator {
98 MipmapLocator::Internal { offsets, sizes } => Some((offsets, sizes)),
99 MipmapLocator::External => None,
100 }
101 }
102
103 pub fn size(version: BlpVersion) -> usize {
105 4 + 4 + 4 + 4 + 4 + if version < BlpVersion::Blp2 {8} else {0} + if version > BlpVersion::Blp0 {16*4*2} else {0} }
113}
114
115impl Default for BlpHeader {
116 fn default() -> Self {
117 BlpHeader {
118 version: BlpVersion::Blp1,
119 content: BlpContentTag::Jpeg,
120 flags: Default::default(),
121 width: 1,
122 height: 1,
123 mipmap_locator: Default::default(),
124 }
125 }
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
129pub enum Compression {
130 Jpeg, Raw1,
132 Raw3,
133 Dxtc,
134}
135
136#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
137pub struct UnknownCompression(u8);
138
139impl fmt::Display for UnknownCompression {
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 write!(f, "Unknown compression field value: {}", self.0)
142 }
143}
144
145impl TryFrom<u8> for Compression {
146 type Error = UnknownCompression;
147
148 fn try_from(val: u8) -> Result<Compression, Self::Error> {
149 match val {
150 0 => Ok(Compression::Jpeg),
151 1 => Ok(Compression::Raw1),
152 2 => Ok(Compression::Dxtc),
153 3 => Ok(Compression::Raw3),
154 _ => Err(UnknownCompression(val)),
155 }
156 }
157}
158
159impl From<Compression> for u8 {
160 fn from(val: Compression) -> u8 {
161 match val {
162 Compression::Jpeg => 0,
163 Compression::Raw1 => 1,
164 Compression::Dxtc => 2,
165 Compression::Raw3 => 3,
166 }
167 }
168}
169
170#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
172pub enum BlpFlags {
173 Blp2 {
175 compression: Compression,
176 alpha_bits: u8, alpha_type: u8, has_mipmaps: u8,
179 },
180 Old {
182 alpha_bits: u32,
183 extra: u32, has_mipmaps: u32, },
186}
187
188impl Default for BlpFlags {
189 fn default() -> Self {
190 BlpFlags::Old {
191 alpha_bits: 8,
192 extra: 8,
193 has_mipmaps: 1,
194 }
195 }
196}
197
198impl BlpFlags {
199 pub fn has_mipmaps(&self) -> bool {
201 match self {
202 BlpFlags::Blp2 { has_mipmaps, .. } => *has_mipmaps != 0,
203 BlpFlags::Old { has_mipmaps, .. } => *has_mipmaps != 0,
204 }
205 }
206
207 pub fn alpha_bits(&self) -> u32 {
209 match self {
210 BlpFlags::Blp2 { compression, .. } if *compression == Compression::Raw3 => 4,
211 BlpFlags::Blp2 { alpha_bits, .. } => *alpha_bits as u32,
212 BlpFlags::Old { alpha_bits, .. } => *alpha_bits,
213 }
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn test_mipmap_count() {
223 let header = BlpHeader {
224 width: 512,
225 height: 256,
226 version: BlpVersion::Blp0,
227 ..Default::default()
228 };
229 assert_eq!(header.mipmaps_count(), 9);
230
231 let header = BlpHeader {
232 width: 512,
233 height: 256,
234 version: BlpVersion::Blp1,
235 ..Default::default()
236 };
237 assert_eq!(header.mipmaps_count(), 9);
238
239 let header = BlpHeader {
240 width: 1,
241 height: 4,
242 ..Default::default()
243 };
244 assert_eq!(header.mipmaps_count(), 2);
245
246 let header = BlpHeader {
247 width: 4,
248 height: 7,
249 ..Default::default()
250 };
251 assert_eq!(header.mipmaps_count(), 2);
252
253 let header = BlpHeader {
254 width: 768,
255 height: 128,
256 ..Default::default()
257 };
258 assert_eq!(header.mipmaps_count(), 9);
259 }
260}