1use anyhow::{Result, anyhow, bail};
2use image::ImageFormat;
3
4use crate::lzss::{lzss_unpack, lzss_unpack32};
5
6pub const G00_TYPE3_XOR_TABLE: [u8; 256] = [
11 0x45, 0x0C, 0x85, 0xC0, 0x75, 0x14, 0xE5, 0x5D, 0x8B, 0x55, 0xEC, 0xC0, 0x5B, 0x8B, 0xC3, 0x8B,
12 0x81, 0xFF, 0x00, 0x00, 0x04, 0x00, 0x85, 0xFF, 0x6A, 0x00, 0x76, 0xB0, 0x43, 0x00, 0x76, 0x49,
13 0x00, 0x8B, 0x7D, 0xE8, 0x8B, 0x75, 0xA1, 0xE0, 0x0C, 0x85, 0xC0, 0xC0, 0x75, 0x78, 0x30, 0x44,
14 0x00, 0x85, 0xFF, 0x76, 0x37, 0x81, 0x1D, 0xD0, 0xFF, 0x00, 0x00, 0x75, 0x44, 0x8B, 0xB0, 0x43,
15 0x45, 0xF8, 0x8D, 0x55, 0xFC, 0x52, 0x00, 0x76, 0x68, 0x00, 0x00, 0x04, 0x00, 0x6A, 0x43, 0x8B,
16 0xB1, 0x43, 0x00, 0x6A, 0x05, 0xFF, 0x50, 0xFF, 0xD3, 0xA1, 0xE0, 0x04, 0x00, 0x56, 0x15, 0x2C,
17 0x44, 0x00, 0x85, 0xC0, 0x74, 0x09, 0xC3, 0xA1, 0x5F, 0x5E, 0x33, 0x8B, 0xE5, 0x5D, 0xE0, 0x30,
18 0x04, 0x00, 0x81, 0xC6, 0x00, 0x00, 0x81, 0xEF, 0x04, 0x00, 0x85, 0x30, 0x44, 0x00, 0x00, 0x00,
19 0x5D, 0xC3, 0x8B, 0x55, 0xF8, 0x8D, 0x5E, 0x5B, 0x4D, 0xFC, 0x51, 0xC4, 0x04, 0x5F, 0x8B, 0xE5,
20 0x43, 0x00, 0xEB, 0xD8, 0x8B, 0x45, 0xFF, 0x15, 0xE8, 0x83, 0xC0, 0x57, 0x56, 0x52, 0x2C, 0xB1,
21 0x01, 0x00, 0x8B, 0x7D, 0xE8, 0x89, 0x00, 0xE8, 0x45, 0xF4, 0x8B, 0x20, 0x50, 0x6A, 0x47, 0x28,
22 0x00, 0x50, 0x53, 0xFF, 0x15, 0x34, 0xE4, 0x6A, 0xB1, 0x43, 0x00, 0x0C, 0x8B, 0x45, 0x00, 0x6A,
23 0x8B, 0x4D, 0xEC, 0x89, 0x08, 0x8A, 0x85, 0xC0, 0x45, 0xF0, 0x84, 0x8B, 0x45, 0x10, 0x74, 0x05,
24 0xF5, 0x28, 0x01, 0x00, 0x83, 0xC4, 0x52, 0x6A, 0x08, 0x89, 0x45, 0x83, 0xC2, 0x20, 0x00, 0xE8,
25 0xE8, 0xF4, 0xFB, 0xFF, 0xFF, 0x8B, 0x8B, 0x5D, 0x45, 0x0C, 0x83, 0xC0, 0x74, 0xC5, 0xF8, 0x53,
26 0xC4, 0x08, 0x85, 0xC0, 0x75, 0x56, 0x30, 0x44, 0x8B, 0x1D, 0xD0, 0xF0, 0xA1, 0xE0, 0x00, 0x83,
27];
28
29pub fn xor_type3_jpeg_in_place(buf: &mut [u8]) {
33 for (i, byte) in buf.iter_mut().enumerate() {
34 *byte ^= G00_TYPE3_XOR_TABLE[i & 0xFF];
35 }
36}
37
38pub fn decode_type3_jpeg_payload(payload: &[u8]) -> Vec<u8> {
40 let mut out = payload.to_vec();
41 xor_type3_jpeg_in_place(&mut out);
42 out
43}
44
45#[derive(Clone, Copy, Debug, PartialEq, Eq)]
46pub enum G00Type {
47 Type0,
48 Type1,
49 Type2,
50 Type3,
51}
52
53impl G00Type {
54 fn from_u8(v: u8) -> Result<Self> {
55 match v {
56 0 => Ok(Self::Type0),
57 1 => Ok(Self::Type1),
58 2 => Ok(Self::Type2),
59 3 => Ok(Self::Type3),
60 _ => bail!("g00: unknown type {}", v),
61 }
62 }
63}
64
65#[derive(Clone, Debug)]
66pub struct G00 {
67 pub ty: G00Type,
68 pub cuts: Vec<G00Cut>,
69}
70
71#[derive(Clone, Debug)]
72pub struct G00Cut {
73 pub width: u32,
74 pub height: u32,
75 pub center_x: i32,
76 pub center_y: i32,
77 pub disp_left: i32,
78 pub disp_top: i32,
79 pub disp_right: i32,
80 pub disp_bottom: i32,
81 pub chips: Vec<G00Chip>,
82}
83
84#[derive(Clone, Debug)]
85pub struct G00Chip {
86 pub x: u32,
87 pub y: u32,
88 pub width: u32,
89 pub height: u32,
90 pub sprite: bool,
91 pub data: G00ChipData,
92}
93
94#[derive(Clone, Debug)]
95pub enum G00ChipData {
96 Type0Lzss32(Vec<u8>),
98 Type1LzssIndexed(Vec<u8>),
100 RawBgra(Vec<u8>),
102 Jpeg(Vec<u8>),
104}
105
106struct Cur<'a> {
107 buf: &'a [u8],
108 pos: usize,
109}
110
111impl<'a> Cur<'a> {
112 fn new(buf: &'a [u8]) -> Self {
113 Self { buf, pos: 0 }
114 }
115
116 fn remaining(&self) -> usize {
117 self.buf.len().saturating_sub(self.pos)
118 }
119
120 fn ensure(&self, n: usize) -> Result<()> {
121 if self.pos + n > self.buf.len() {
122 bail!(
123 "g00: unexpected EOF (need {}, have {})",
124 n,
125 self.remaining()
126 );
127 }
128 Ok(())
129 }
130
131 fn take(&mut self, n: usize) -> Result<&'a [u8]> {
132 self.ensure(n)?;
133 let out = &self.buf[self.pos..self.pos + n];
134 self.pos += n;
135 Ok(out)
136 }
137
138 fn skip(&mut self, n: usize) -> Result<()> {
139 self.ensure(n)?;
140 self.pos += n;
141 Ok(())
142 }
143
144 fn read_u8(&mut self) -> Result<u8> {
145 Ok(self.take(1)?[0])
146 }
147
148 fn read_u16_le(&mut self) -> Result<u16> {
149 let b = self.take(2)?;
150 Ok(u16::from_le_bytes([b[0], b[1]]))
151 }
152
153 fn read_u32_le(&mut self) -> Result<u32> {
154 let b = self.take(4)?;
155 Ok(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
156 }
157
158 fn read_i32_le(&mut self) -> Result<i32> {
159 Ok(self.read_u32_le()? as i32)
160 }
161}
162
163impl G00 {
164 pub fn parse(bytes: &[u8]) -> Result<Self> {
166 let mut cur = Cur::new(bytes);
167 let ty = G00Type::from_u8(cur.read_u8()?)?;
168
169 match ty {
170 G00Type::Type0 | G00Type::Type1 | G00Type::Type3 => {
171 let w = cur.read_u16_le()? as u32;
172 let h = cur.read_u16_le()? as u32;
173 let rest = cur.take(cur.remaining())?.to_vec();
174
175 let chip_data = match ty {
176 G00Type::Type0 => G00ChipData::Type0Lzss32(rest),
177 G00Type::Type1 => G00ChipData::Type1LzssIndexed(rest),
178 G00Type::Type3 => G00ChipData::Jpeg(rest),
179 _ => unreachable!(),
180 };
181
182 let chip = G00Chip {
183 x: 0,
184 y: 0,
185 width: w,
186 height: h,
187 sprite: false,
188 data: chip_data,
189 };
190
191 let cut = G00Cut {
192 width: w,
193 height: h,
194 center_x: 0,
195 center_y: 0,
196 disp_left: 0,
197 disp_top: 0,
198 disp_right: w as i32,
199 disp_bottom: h as i32,
200 chips: vec![chip],
201 };
202
203 Ok(Self {
204 ty,
205 cuts: vec![cut],
206 })
207 }
208 G00Type::Type2 => {
209 let _w = cur.read_u16_le()? as u32;
212 let _h = cur.read_u16_le()? as u32;
213 let cut_cnt = cur.read_i32_le()?;
214 if cut_cnt < 0 {
215 bail!("g00: negative cut_cnt {}", cut_cnt);
216 }
217
218 let db_bytes = (cut_cnt as usize)
220 .checked_mul(24)
221 .ok_or_else(|| anyhow!("g00: cut database size overflow"))?;
222 cur.skip(db_bytes)?;
223
224 let compressed = cur.take(cur.remaining())?;
225 let decompressed = lzss_unpack(compressed)?;
226
227 let mut dcur = Cur::new(&decompressed);
228 let table_cut_cnt = dcur.read_u32_le()? as usize;
229
230 let mut pairs: Vec<(usize, i32)> = Vec::with_capacity(table_cut_cnt);
232 for _ in 0..table_cut_cnt {
233 let off = dcur.read_u32_le()? as usize;
234 let size = dcur.read_u32_le()? as i32;
235 pairs.push((off, size));
236 }
237
238 let mut cuts = Vec::new();
239 for (off, size) in pairs {
240 if off == 0 || size <= 0 {
241 continue;
242 }
243 let size_u = size as usize;
244 let end = off
245 .checked_add(size_u)
246 .ok_or_else(|| anyhow!("g00: cut slice overflow"))?;
247 if end > decompressed.len() {
248 bail!(
249 "g00: cut slice out of bounds (off={}, size={}, len={})",
250 off,
251 size_u,
252 decompressed.len()
253 );
254 }
255 let cut_data = &decompressed[off..end];
256 cuts.push(parse_type2_cut(cut_data)?);
257 }
258
259 if cuts.is_empty() {
260 bail!("g00: type2 has no cuts");
261 }
262
263 Ok(Self { ty, cuts })
264 }
265 }
266 }
267}
268
269fn parse_type2_cut(cut_data: &[u8]) -> Result<G00Cut> {
270 let mut cur = Cur::new(cut_data);
278
279 let _cut_type = cur.read_u8()?;
280 cur.skip(1)?; let chip_cnt = cur.read_u16_le()? as usize;
282
283 let x = cur.read_i32_le()?;
284 let y = cur.read_i32_le()?;
285 let disp_xl = cur.read_i32_le()?;
286 let disp_yl = cur.read_i32_le()?;
287 let xc = cur.read_i32_le()?;
288 let yc = cur.read_i32_le()?;
289 let cut_xl = cur.read_i32_le()?;
290 let cut_yl = cur.read_i32_le()?;
291
292 cur.skip(20 * 4)?; if cut_xl <= 0 || cut_yl <= 0 {
295 bail!("g00: invalid cut size {}x{}", cut_xl, cut_yl);
296 }
297
298 let mut chips = Vec::with_capacity(chip_cnt);
299 for _ in 0..chip_cnt {
300 let cx = cur.read_u16_le()? as u32;
311 let cy = cur.read_u16_le()? as u32;
312 let ctype = cur.read_u8()?;
313 cur.skip(1)?; let w = cur.read_u16_le()? as u32;
315 let h = cur.read_u16_le()? as u32;
316 cur.skip(2)?; cur.skip(20 * 4)?; let pix_len = (w as usize)
320 .checked_mul(h as usize)
321 .and_then(|v| v.checked_mul(4))
322 .ok_or_else(|| anyhow!("g00: chip pixel size overflow"))?;
323 let pix = cur.take(pix_len)?.to_vec();
324
325 chips.push(G00Chip {
326 x: cx,
327 y: cy,
328 width: w,
329 height: h,
330 sprite: ctype == 1,
331 data: G00ChipData::RawBgra(pix),
332 });
333 }
334
335 Ok(G00Cut {
336 width: cut_xl as u32,
337 height: cut_yl as u32,
338 center_x: xc,
339 center_y: yc,
340 disp_left: x,
341 disp_top: y,
342 disp_right: x.saturating_add(disp_xl),
343 disp_bottom: y.saturating_add(disp_yl),
344 chips,
345 })
346}
347
348impl G00Chip {
349 pub fn decode_bgra(&self) -> Result<Vec<u8>> {
351 match &self.data {
352 G00ChipData::Type0Lzss32(blob) => {
353 let out = lzss_unpack32(blob)?;
354 let expected = (self.width as usize)
355 .checked_mul(self.height as usize)
356 .and_then(|v| v.checked_mul(4))
357 .ok_or_else(|| anyhow!("g00: expected size overflow"))?;
358 if out.len() != expected {
359 bail!(
360 "g00: type0 size mismatch (got={}, expected={})",
361 out.len(),
362 expected
363 );
364 }
365 Ok(out)
366 }
367 G00ChipData::Type1LzssIndexed(blob) => {
368 let dec = lzss_unpack(blob)?;
369 let mut cur = Cur::new(&dec);
370
371 let pal_cnt = cur.read_u16_le()? as usize;
372 if pal_cnt == 0 {
373 bail!("g00: type1 pal_cnt=0");
374 }
375
376 let pal_bytes = pal_cnt
377 .checked_mul(4)
378 .ok_or_else(|| anyhow!("g00: palette size overflow"))?;
379 let pal_raw = cur.take(pal_bytes)?;
380
381 let expected_px = (self.width as usize)
382 .checked_mul(self.height as usize)
383 .ok_or_else(|| anyhow!("g00: index size overflow"))?;
384
385 if cur.remaining() < expected_px {
386 bail!(
387 "g00: type1 truncated indices (need={}, have={})",
388 expected_px,
389 cur.remaining()
390 );
391 }
392
393 let indices = cur.take(expected_px)?;
394
395 let mut out: Vec<u8> = Vec::with_capacity(expected_px * 4);
398 for &ix in indices {
399 let ix = ix as usize;
400 if ix >= pal_cnt {
401 bail!(
402 "g00: type1 palette index out of range (ix={}, pal_cnt={})",
403 ix,
404 pal_cnt
405 );
406 }
407 let base = ix * 4;
408 out.extend_from_slice(&pal_raw[base..base + 4]);
409 }
410
411 Ok(out)
412 }
413 G00ChipData::RawBgra(pix) => {
414 let expected = (self.width as usize)
415 .checked_mul(self.height as usize)
416 .and_then(|v| v.checked_mul(4))
417 .ok_or_else(|| anyhow!("g00: expected size overflow"))?;
418 if pix.len() != expected {
419 bail!(
420 "g00: raw size mismatch (got={}, expected={})",
421 pix.len(),
422 expected
423 );
424 }
425 Ok(pix.clone())
426 }
427 G00ChipData::Jpeg(bytes) => {
428 let jpeg = decode_type3_jpeg_payload(bytes);
429 if !jpeg.starts_with(&[0xFF, 0xD8]) {
430 bail!(
431 "g00: type3 XOR decode did not produce JPEG SOI (got={:02X?})",
432 &jpeg[..jpeg.len().min(2)]
433 );
434 }
435 let img = image::load_from_memory_with_format(&jpeg, ImageFormat::Jpeg)
436 .or_else(|_| image::load_from_memory(&jpeg))
437 .map_err(|e| anyhow!("g00: JPEG decode failed after type3 XOR: {e}"))?;
438 let rgba = img.to_rgba8();
439 let (w, h) = rgba.dimensions();
440 if w != self.width || h != self.height {
441 bail!(
442 "g00: JPEG size mismatch (got={}x{}, expected={}x{})",
443 w,
444 h,
445 self.width,
446 self.height
447 );
448 }
449 let mut out = rgba.into_raw();
450 bgra_to_rgba_in_place(&mut out);
451 Ok(out)
453 }
454 }
455 }
456
457 pub fn decode_rgba(&self) -> Result<Vec<u8>> {
459 let mut bgra = self.decode_bgra()?;
460 bgra_to_rgba_in_place(&mut bgra);
461 Ok(bgra)
462 }
463
464 pub fn jpeg_bytes(&self) -> Option<&[u8]> {
466 match &self.data {
467 G00ChipData::Jpeg(b) => Some(b),
468 _ => None,
469 }
470 }
471}
472
473fn bgra_to_rgba_in_place(buf: &mut [u8]) {
474 for px in buf.as_chunks_mut::<4>().0 {
475 px.swap(0, 2);
477 }
478}