1use anyhow::{anyhow, bail, Result};
2use image::ImageFormat;
3
4use crate::lzss::{lzss_unpack, lzss_unpack32};
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum G00Type {
8 Type0,
9 Type1,
10 Type2,
11 Type3,
12}
13
14impl G00Type {
15 fn from_u8(v: u8) -> Result<Self> {
16 match v {
17 0 => Ok(Self::Type0),
18 1 => Ok(Self::Type1),
19 2 => Ok(Self::Type2),
20 3 => Ok(Self::Type3),
21 _ => bail!("g00: unknown type {}", v),
22 }
23 }
24}
25
26#[derive(Clone, Debug)]
27pub struct G00 {
28 pub ty: G00Type,
29 pub cuts: Vec<G00Cut>,
30}
31
32#[derive(Clone, Debug)]
33pub struct G00Cut {
34 pub width: u32,
35 pub height: u32,
36 pub center_x: i32,
37 pub center_y: i32,
38 pub disp_left: i32,
39 pub disp_top: i32,
40 pub disp_right: i32,
41 pub disp_bottom: i32,
42 pub chips: Vec<G00Chip>,
43}
44
45#[derive(Clone, Debug)]
46pub struct G00Chip {
47 pub x: u32,
48 pub y: u32,
49 pub width: u32,
50 pub height: u32,
51 pub sprite: bool,
52 pub data: G00ChipData,
53}
54
55#[derive(Clone, Debug)]
56pub enum G00ChipData {
57 Type0Lzss32(Vec<u8>),
59 Type1LzssIndexed(Vec<u8>),
61 RawBgra(Vec<u8>),
63 Jpeg(Vec<u8>),
65}
66
67struct Cur<'a> {
68 buf: &'a [u8],
69 pos: usize,
70}
71
72impl<'a> Cur<'a> {
73 fn new(buf: &'a [u8]) -> Self {
74 Self { buf, pos: 0 }
75 }
76
77 fn remaining(&self) -> usize {
78 self.buf.len().saturating_sub(self.pos)
79 }
80
81 fn ensure(&self, n: usize) -> Result<()> {
82 if self.pos + n > self.buf.len() {
83 bail!(
84 "g00: unexpected EOF (need {}, have {})",
85 n,
86 self.remaining()
87 );
88 }
89 Ok(())
90 }
91
92 fn take(&mut self, n: usize) -> Result<&'a [u8]> {
93 self.ensure(n)?;
94 let out = &self.buf[self.pos..self.pos + n];
95 self.pos += n;
96 Ok(out)
97 }
98
99 fn skip(&mut self, n: usize) -> Result<()> {
100 self.ensure(n)?;
101 self.pos += n;
102 Ok(())
103 }
104
105 fn read_u8(&mut self) -> Result<u8> {
106 Ok(self.take(1)?[0])
107 }
108
109 fn read_u16_le(&mut self) -> Result<u16> {
110 let b = self.take(2)?;
111 Ok(u16::from_le_bytes([b[0], b[1]]))
112 }
113
114 fn read_u32_le(&mut self) -> Result<u32> {
115 let b = self.take(4)?;
116 Ok(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
117 }
118
119 fn read_i32_le(&mut self) -> Result<i32> {
120 Ok(self.read_u32_le()? as i32)
121 }
122}
123
124impl G00 {
125 pub fn parse(bytes: &[u8]) -> Result<Self> {
127 let mut cur = Cur::new(bytes);
128 let ty = G00Type::from_u8(cur.read_u8()?)?;
129
130 match ty {
131 G00Type::Type0 | G00Type::Type1 | G00Type::Type3 => {
132 let w = cur.read_u16_le()? as u32;
133 let h = cur.read_u16_le()? as u32;
134 let rest = cur.take(cur.remaining())?.to_vec();
135
136 let chip_data = match ty {
137 G00Type::Type0 => G00ChipData::Type0Lzss32(rest),
138 G00Type::Type1 => G00ChipData::Type1LzssIndexed(rest),
139 G00Type::Type3 => G00ChipData::Jpeg(rest),
140 _ => unreachable!(),
141 };
142
143 let chip = G00Chip {
144 x: 0,
145 y: 0,
146 width: w,
147 height: h,
148 sprite: false,
149 data: chip_data,
150 };
151
152 let cut = G00Cut {
153 width: w,
154 height: h,
155 center_x: 0,
156 center_y: 0,
157 disp_left: 0,
158 disp_top: 0,
159 disp_right: w as i32,
160 disp_bottom: h as i32,
161 chips: vec![chip],
162 };
163
164 Ok(Self {
165 ty,
166 cuts: vec![cut],
167 })
168 }
169 G00Type::Type2 => {
170 let _w = cur.read_u16_le()? as u32;
173 let _h = cur.read_u16_le()? as u32;
174 let cut_cnt = cur.read_i32_le()?;
175 if cut_cnt < 0 {
176 bail!("g00: negative cut_cnt {}", cut_cnt);
177 }
178
179 let db_bytes = (cut_cnt as usize)
181 .checked_mul(24)
182 .ok_or_else(|| anyhow!("g00: cut database size overflow"))?;
183 cur.skip(db_bytes)?;
184
185 let compressed = cur.take(cur.remaining())?;
186 let decompressed = lzss_unpack(compressed)?;
187
188 let mut dcur = Cur::new(&decompressed);
189 let table_cut_cnt = dcur.read_u32_le()? as usize;
190
191 let mut pairs: Vec<(usize, i32)> = Vec::with_capacity(table_cut_cnt);
193 for _ in 0..table_cut_cnt {
194 let off = dcur.read_u32_le()? as usize;
195 let size = dcur.read_u32_le()? as i32;
196 pairs.push((off, size));
197 }
198
199 let mut cuts = Vec::new();
200 for (off, size) in pairs {
201 if off == 0 || size <= 0 {
202 continue;
203 }
204 let size_u = size as usize;
205 let end = off
206 .checked_add(size_u)
207 .ok_or_else(|| anyhow!("g00: cut slice overflow"))?;
208 if end > decompressed.len() {
209 bail!(
210 "g00: cut slice out of bounds (off={}, size={}, len={})",
211 off,
212 size_u,
213 decompressed.len()
214 );
215 }
216 let cut_data = &decompressed[off..end];
217 cuts.push(parse_type2_cut(cut_data)?);
218 }
219
220 if cuts.is_empty() {
221 bail!("g00: type2 has no cuts");
222 }
223
224 Ok(Self { ty, cuts })
225 }
226 }
227 }
228}
229
230fn parse_type2_cut(cut_data: &[u8]) -> Result<G00Cut> {
231 let mut cur = Cur::new(cut_data);
239
240 let _cut_type = cur.read_u8()?;
241 cur.skip(1)?; let chip_cnt = cur.read_u16_le()? as usize;
243
244 let x = cur.read_i32_le()?;
245 let y = cur.read_i32_le()?;
246 let disp_xl = cur.read_i32_le()?;
247 let disp_yl = cur.read_i32_le()?;
248 let xc = cur.read_i32_le()?;
249 let yc = cur.read_i32_le()?;
250 let cut_xl = cur.read_i32_le()?;
251 let cut_yl = cur.read_i32_le()?;
252
253 cur.skip(20 * 4)?; if cut_xl <= 0 || cut_yl <= 0 {
256 bail!("g00: invalid cut size {}x{}", cut_xl, cut_yl);
257 }
258
259 let mut chips = Vec::with_capacity(chip_cnt);
260 for _ in 0..chip_cnt {
261 let cx = cur.read_u16_le()? as u32;
272 let cy = cur.read_u16_le()? as u32;
273 let ctype = cur.read_u8()?;
274 cur.skip(1)?; let w = cur.read_u16_le()? as u32;
276 let h = cur.read_u16_le()? as u32;
277 cur.skip(2)?; cur.skip(20 * 4)?; let pix_len = (w as usize)
281 .checked_mul(h as usize)
282 .and_then(|v| v.checked_mul(4))
283 .ok_or_else(|| anyhow!("g00: chip pixel size overflow"))?;
284 let pix = cur.take(pix_len)?.to_vec();
285
286 chips.push(G00Chip {
287 x: cx,
288 y: cy,
289 width: w,
290 height: h,
291 sprite: ctype == 1,
292 data: G00ChipData::RawBgra(pix),
293 });
294 }
295
296 Ok(G00Cut {
297 width: cut_xl as u32,
298 height: cut_yl as u32,
299 center_x: xc,
300 center_y: yc,
301 disp_left: x,
302 disp_top: y,
303 disp_right: x.saturating_add(disp_xl),
304 disp_bottom: y.saturating_add(disp_yl),
305 chips,
306 })
307}
308
309impl G00Chip {
310 pub fn decode_bgra(&self) -> Result<Vec<u8>> {
312 match &self.data {
313 G00ChipData::Type0Lzss32(blob) => {
314 let out = lzss_unpack32(blob)?;
315 let expected = (self.width as usize)
316 .checked_mul(self.height as usize)
317 .and_then(|v| v.checked_mul(4))
318 .ok_or_else(|| anyhow!("g00: expected size overflow"))?;
319 if out.len() != expected {
320 bail!(
321 "g00: type0 size mismatch (got={}, expected={})",
322 out.len(),
323 expected
324 );
325 }
326 Ok(out)
327 }
328 G00ChipData::Type1LzssIndexed(blob) => {
329 let dec = lzss_unpack(blob)?;
330 let mut cur = Cur::new(&dec);
331
332 let pal_cnt = cur.read_u16_le()? as usize;
333 if pal_cnt == 0 {
334 bail!("g00: type1 pal_cnt=0");
335 }
336
337 let pal_bytes = pal_cnt
338 .checked_mul(4)
339 .ok_or_else(|| anyhow!("g00: palette size overflow"))?;
340 let pal_raw = cur.take(pal_bytes)?;
341
342 let expected_px = (self.width as usize)
343 .checked_mul(self.height as usize)
344 .ok_or_else(|| anyhow!("g00: index size overflow"))?;
345
346 if cur.remaining() < expected_px {
347 bail!(
348 "g00: type1 truncated indices (need={}, have={})",
349 expected_px,
350 cur.remaining()
351 );
352 }
353
354 let indices = cur.take(expected_px)?;
355
356 let mut out: Vec<u8> = Vec::with_capacity(expected_px * 4);
359 for &ix in indices {
360 let ix = ix as usize;
361 if ix >= pal_cnt {
362 bail!(
363 "g00: type1 palette index out of range (ix={}, pal_cnt={})",
364 ix,
365 pal_cnt
366 );
367 }
368 let base = ix * 4;
369 out.extend_from_slice(&pal_raw[base..base + 4]);
370 }
371
372 Ok(out)
373 }
374 G00ChipData::RawBgra(pix) => {
375 let expected = (self.width as usize)
376 .checked_mul(self.height as usize)
377 .and_then(|v| v.checked_mul(4))
378 .ok_or_else(|| anyhow!("g00: expected size overflow"))?;
379 if pix.len() != expected {
380 bail!(
381 "g00: raw size mismatch (got={}, expected={})",
382 pix.len(),
383 expected
384 );
385 }
386 Ok(pix.clone())
387 }
388 G00ChipData::Jpeg(bytes) => {
389 let img = image::load_from_memory_with_format(bytes, ImageFormat::Jpeg)
390 .or_else(|_| image::load_from_memory(bytes))
391 .map_err(|e| anyhow!("g00: JPEG decode failed: {e}"))?;
392 let rgba = img.to_rgba8();
393 let (w, h) = rgba.dimensions();
394 if w != self.width || h != self.height {
395 bail!(
396 "g00: JPEG size mismatch (got={}x{}, expected={}x{})",
397 w,
398 h,
399 self.width,
400 self.height
401 );
402 }
403 let mut out = rgba.into_raw();
404 bgra_to_rgba_in_place(&mut out);
405 Ok(out)
407 }
408 }
409 }
410
411 pub fn decode_rgba(&self) -> Result<Vec<u8>> {
413 let mut bgra = self.decode_bgra()?;
414 bgra_to_rgba_in_place(&mut bgra);
415 Ok(bgra)
416 }
417
418 pub fn jpeg_bytes(&self) -> Option<&[u8]> {
419 match &self.data {
420 G00ChipData::Jpeg(b) => Some(b),
421 _ => None,
422 }
423 }
424}
425
426fn bgra_to_rgba_in_place(buf: &mut [u8]) {
427 for px in buf.chunks_exact_mut(4) {
428 px.swap(0, 2);
430 }
431}