Made Unix builds more likely to be Y2038-compliant
[deark.git] / modules / comicchat.c
blobd20b3cd4752635eb384e2228abfc0b9229953283
1 // This file is part of Deark.
2 // Copyright (C) 2024 Jason Summers
3 // See the file COPYING for terms of use.
5 // MS Comic Chat .AVB, .BGB
7 #include <deark-private.h>
8 #include <deark-fmtutil.h>
9 DE_DECLARE_MODULE(de_module_comicchat);
11 typedef struct localctx_comicchat {
12 de_encoding input_encoding;
13 u8 stopflag;
14 u8 opt_applymasks;
15 UI fmt_code;
16 UI major_ver;
17 struct de_inthashtable *images_seen;
18 i64 last_chunklen;
19 i64 img_ptr_bias;
20 de_ucstring *char_name;
21 de_ucstring *tmpstr;
22 } lctx;
24 struct chunk_ctx {
25 UI ck_type;
26 i64 ck_pos; // everything; starting from start of type field
27 i64 ck_len; // total length from ck_pos
30 struct image_lowlevel_ctx {
31 de_bitmap *img;
32 de_finfo *fi;
33 UI createflags;
36 struct image_highlevel_ctx {
37 u8 special_2bpp_transparency;
38 u8 is_icon;
39 // [0] is the foreground image.
40 // [1] and [2] are optional transparency masks.
41 // Sometimes the fg image has transparency, in which case we wouldn't expect
42 // the masks to exist.
43 struct image_lowlevel_ctx llimg[3];
46 struct image_extract_ctx {
47 struct de_bmpinfo bi;
48 i64 ihpos;
49 dbuf *unc_pixels;
50 i64 pal_num_entries;
51 de_color pal[256];
54 static void fixup_2bpp_transparency(deark *c, lctx *d, struct image_highlevel_ctx *ih)
56 de_bitmap *oldimg;
57 de_bitmap *newimg;
58 i64 i, j;
60 oldimg = ih->llimg[0].img;
61 if(!oldimg) return;
62 newimg = de_bitmap_create(c, oldimg->width, oldimg->height, 2);
63 for(j=0; j<oldimg->height; j++) {
64 for(i=0; i<oldimg->width; i++) {
65 de_color clr;
67 clr = de_bitmap_getpixel(oldimg, i, j);
68 if(clr==DE_MAKE_GRAY(128)) clr = DE_MAKE_RGBA(128,128,128,0);
69 de_bitmap_setpixel_rgba(newimg, i, j, clr);
73 de_bitmap_destroy(ih->llimg[0].img);
74 ih->llimg[0].img = newimg;
77 static void read_bmp_to_img(deark *c, lctx *d, dbuf *inf, i64 pos1,
78 struct image_highlevel_ctx *ih, int itype)
80 de_module_params *mparams = NULL;
81 struct fmtutil_bmp_mparams_indata *idata = NULL;
82 i64 bmplen;
83 int saved_indent_level;
85 de_dbg_indent_save(c, &saved_indent_level);
87 bmplen = dbuf_getu32le(inf, pos1+2);
88 if(pos1+bmplen > inf->len) goto done;
89 if(bmplen < 54) goto done;
91 de_dbg(c, "decoding BMP");
92 de_dbg_indent(c, 1);
93 mparams = de_malloc(c, sizeof(de_module_params));
94 idata = de_malloc(c, sizeof(struct fmtutil_bmp_mparams_indata));
95 mparams->in_params.flags = 0x2;
96 mparams->in_params.obj1 = (void*)idata;
97 de_run_module_by_id_on_slice(c, "bmp", mparams, inf, pos1, bmplen);
98 if(idata->img) {
99 ih->llimg[itype].img = idata->img;
100 idata->img = NULL;
101 ih->llimg[itype].fi = idata->fi;
102 idata->fi = NULL;
103 ih->llimg[itype].createflags = idata->createflags;
105 else {
106 de_err(c, "Failed to decode BMP image");
107 goto done;
110 if(itype==0 && ih->special_2bpp_transparency) {
111 fixup_2bpp_transparency(c, d, ih);
114 done:
115 if(idata) {
116 de_bitmap_destroy(idata->img);
117 de_finfo_destroy(c, idata->fi);
118 de_free(c, idata);
120 if(mparams) {
121 de_free(c, mparams);
123 de_dbg_indent_restore(c, saved_indent_level);
126 static void write_palette(deark *c, lctx *d, dbuf *outf,
127 de_color *pal, i64 num_entries)
129 i64 i;
131 for(i=0; i<num_entries; i++) {
132 dbuf_writebyte(outf, DE_COLOR_B(pal[i]));
133 dbuf_writebyte(outf, DE_COLOR_G(pal[i]));
134 dbuf_writebyte(outf, DE_COLOR_R(pal[i]));
135 dbuf_writebyte(outf, 0);
139 static void reconstruct_bmp_and_cvt_to_img(deark *c, lctx *d,
140 struct image_extract_ctx *ic, struct image_highlevel_ctx *ih, int itype)
142 dbuf *tmpbmp = NULL;
144 tmpbmp = dbuf_create_membuf(c, 0, 0);
145 fmtutil_generate_bmpfileheader(c, tmpbmp, &ic->bi, 0);
146 dbuf_copy(c->infile, ic->ihpos, ic->bi.infohdrsize, tmpbmp);
147 write_palette(c, d, tmpbmp, ic->pal, ic->bi.pal_entries);
148 dbuf_copy(ic->unc_pixels, 0, ic->unc_pixels->len, tmpbmp);
149 dbuf_flush(tmpbmp);
150 read_bmp_to_img(c, d, tmpbmp, 0, ih, itype);
151 dbuf_close(tmpbmp);
154 static void read_dib_to_img(deark *c, lctx *d, i64 pos1, UI magic,
155 struct image_highlevel_ctx *ih, int itype)
157 int ret;
158 i64 pos;
159 i64 cmpr_pos;
160 i64 orig_len, cmpr_len;
161 UI magic2;
162 int saved_indent_level;
163 struct image_extract_ctx *ic = NULL;
164 struct de_dfilter_results dres;
165 struct de_dfilter_in_params dcmpri;
166 struct de_dfilter_out_params dcmpro;
167 struct de_deflate_params deflateparams;
169 de_dbg_indent_save(c, &saved_indent_level);
170 ic = de_malloc(c, sizeof(struct image_extract_ctx));
171 de_zeromem(&deflateparams, sizeof(struct de_deflate_params));
172 de_dfilter_init_objects(c, &dcmpri, &dcmpro, &dres);
174 pos = pos1;
175 if(magic==0x0101) {
176 i64 ck_len;
178 // The palette is formatted like a standard chunk.
179 // TODO: Ideally, we'd use shared chunk-reading code here.
180 de_dbg(c, "palette chunk at %"I64_FMT, pos1);
181 de_dbg_indent(c, 1);
182 pos += 2; // 0x0101 tag, already read
183 ck_len = de_getu16le_p(&pos);
184 ic->pal_num_entries = de_getu16le_p(&pos);
185 de_dbg(c, "num entries: %"I64_FMT, ic->pal_num_entries);
186 if(ic->pal_num_entries>256) goto done;
188 // Note that palette is RGB, not BGR as might be expected.
189 if(ic->pal_num_entries>0) {
190 de_read_simple_palette(c, c->infile, pos, ic->pal_num_entries, 3,
191 ic->pal, 256, DE_RDPALTYPE_24BIT, 0);
193 pos = pos1 + 4 + ck_len;
194 de_dbg_indent(c, -1);
197 ic->ihpos = pos;
198 magic2 = (UI)de_getu16be(ic->ihpos);
199 if(magic2 != 0x2800) {
200 de_err(c, "Image not found at %"I64_FMT, ic->ihpos);
201 goto done;
204 de_dbg(c, "infoheader at %"I64_FMT, ic->ihpos);
205 de_dbg_indent(c, 1);
206 ret = fmtutil_get_bmpinfo(c, c->infile, &ic->bi, ic->ihpos,
207 c->infile->len - ic->ihpos, 0);
208 de_dbg_indent(c, -1);
209 if(!ret) goto done;
210 if(ic->bi.infohdrsize != 40) goto done;
211 if(ic->bi.pal_entries > 256) goto done;
212 // We don't expect the zlib decompression to result in an image that is still
213 // (RLE) compressed. We could support it, but we probably need samples.
214 if(ic->bi.is_compressed) goto done;
215 if(!de_good_image_dimensions(c, ic->bi.width, ic->bi.height)) goto done;
217 if(ic->bi.bitcount==1 && itype>0) {
218 ic->pal_num_entries = 2;
219 // 0 is transparent, and the palette is undefined.
220 // It'd be logical to make 0 black, but we make it white, so that masks
221 // from all format versions are white-is-transparent.
222 de_make_grayscale_palette(ic->pal, ic->pal_num_entries, 0x1);
224 else if(ic->bi.bitcount==2 && itype==0 && ic->pal_num_entries==0) {
225 ih->special_2bpp_transparency = 1;
226 ic->pal_num_entries = 4;
227 ic->pal[0] = DE_MAKE_GRAY(128); // Arbitrary key color. Will be made transparent.
228 // Sometimes pal[1] is used for the halo around the character, so it might
229 // be interesting to make it partially transparent or something.
230 // But unfortunately, other times the halo is pal[2].
231 ic->pal[1] = DE_MAKE_GRAY(254); // alternate white foreground
232 ic->pal[2] = DE_STOCKCOLOR_WHITE; // normal white foregreound
233 ic->pal[3] = DE_STOCKCOLOR_BLACK; // normal black foreground
236 pos = ic->ihpos + ic->bi.infohdrsize;
237 orig_len = de_getu32le_p(&pos);
238 de_dbg(c, "orig len: %"I64_FMT, orig_len);
239 cmpr_len = de_getu32le_p(&pos);
240 de_dbg(c, "cmpr len: %"I64_FMT, cmpr_len);
241 cmpr_pos = pos;
242 de_dbg(c, "cmpr data at %"I64_FMT, cmpr_pos);
243 if(cmpr_pos + cmpr_len > c->infile->len) goto done;
245 ic->unc_pixels = dbuf_create_membuf(c, 0, 0);
246 dcmpri.f = c->infile;
247 dcmpri.pos = cmpr_pos;
248 dcmpri.len = cmpr_len;
249 dcmpro.f = ic->unc_pixels;
250 dcmpro.expected_len = de_min_int(orig_len, ic->bi.foreground_size);
251 dcmpro.len_known = 1;
252 deflateparams.flags = DE_DEFLATEFLAG_ISZLIB;
253 de_dbg(c, "[decompressing]");
254 de_dbg_indent(c, 1);
255 fmtutil_deflate_codectype1(c, &dcmpri, &dcmpro, &dres, (void*)&deflateparams);
256 de_dbg_indent(c, -1);
257 dbuf_flush(ic->unc_pixels);
258 if(dres.errcode != 0) {
259 de_err(c, "%s", de_dfilter_get_errmsg(c, &dres));
260 goto done;
263 // This will ultimately result in the BMP header & palette being read
264 // again (by the bmp module this time), dbg info printed again, etc.
265 // It's messy, but it will do.
266 reconstruct_bmp_and_cvt_to_img(c, d, ic, ih, itype);
268 done:
269 if(ic) {
270 dbuf_close(ic->unc_pixels);
271 de_free(c, ic);
273 de_dbg_indent_restore(c, saved_indent_level);
276 // Read an image component, decode it in whatever way required,
277 // and store it at ih->llimg[itype].
278 static void read_image_lowlevel(deark *c, lctx *d, i64 pos1,
279 struct image_highlevel_ctx *ih, int itype)
281 UI magic;
282 int saved_indent_level;
283 de_dbg_indent_save(c, &saved_indent_level);
285 if(pos1==0) goto done;
287 de_dbg(c, "[image component at %"I64_FMT"]", pos1);
288 de_dbg_indent(c, 1);
290 magic = (UI)de_getu16be(pos1);
291 de_dbg(c, "sig: 0x%04x", magic);
292 if(magic!=0x0101 && magic!=0x2800 && magic!=0x424d) {
293 de_err(c, "Image not found at %"I64_FMT, pos1);
294 goto done;
297 if(magic==0x424d) {
298 read_bmp_to_img(c, d, c->infile, pos1, ih, itype);
300 else {
301 read_dib_to_img(c, d, pos1, magic, ih, itype);
304 done:
305 de_dbg_indent_restore(c, saved_indent_level);
308 static void emit_images_highlevel_separate(deark *c, lctx *d,
309 struct image_highlevel_ctx *ih)
311 size_t k;
313 if(ih->llimg[0].img) {
314 if(ih->is_icon) {
315 de_finfo_set_name_from_sz(c, ih->llimg[0].fi,
316 "icon", 0, DE_ENCODING_LATIN1);
318 de_bitmap_write_to_file_finfo(ih->llimg[0].img, ih->llimg[0].fi,
319 ih->llimg[0].createflags);
321 for(k=1; k<=2; k++) {
322 if(ih->llimg[k].img) {
323 de_finfo_set_name_from_sz(c, ih->llimg[k].fi,
324 (k==1?"sm_mask":"lg_mask"), 0, DE_ENCODING_LATIN1);
325 de_bitmap_write_to_file_finfo(ih->llimg[k].img, ih->llimg[k].fi,
326 (ih->llimg[k].createflags | DE_CREATEFLAG_IS_AUX));
331 static void emit_images_highlevel_applymasks(deark *c, lctx *d,
332 struct image_highlevel_ctx *ih)
334 de_bitmap *tmpimg = NULL;
335 i64 w, h;
336 size_t k;
338 if(!ih->llimg[0].img) goto done; // should be impossible
339 w = ih->llimg[0].img->width;
340 h = ih->llimg[0].img->height;
342 if(!ih->llimg[1].img && !ih->llimg[2].img) { // should be impossible
343 emit_images_highlevel_separate(c, d, ih);
344 goto done;
347 for(k=1; k<=2; k++) {
348 if(!ih->llimg[k].img) continue;
349 if(tmpimg) {
350 de_bitmap_destroy(tmpimg);
352 tmpimg = de_bitmap_create(c, w, h, 4);
353 de_bitmap_copy_rect(ih->llimg[0].img, tmpimg, 0, 0, w, h, 0, 0, 0);
354 de_bitmap_apply_mask(tmpimg, ih->llimg[k].img, DE_BITMAPFLAG_WHITEISTRNS);
355 de_bitmap_write_to_file_finfo(tmpimg, ih->llimg[0].fi,
356 ih->llimg[0].createflags);
359 done:
360 de_bitmap_destroy(tmpimg);
363 // We can either extract the foreground and masks to individual files, or
364 // apply the masks. For us to apply the masks, certain conditions must be met.
365 static int should_apply_masks(deark *c, lctx *d, struct image_highlevel_ctx *ih)
367 size_t k;
369 if(!d->opt_applymasks) return 0;
370 if(ih->special_2bpp_transparency) return 0;
371 if(!ih->llimg[0].img) return 0;
372 if(!ih->llimg[1].img && !ih->llimg[2].img) return 0;
374 for(k=1; k<=2; k++) {
375 if(ih->llimg[k].img) {
376 if(ih->llimg[k].img->width != ih->llimg[0].img->width) return 0;
377 if(ih->llimg[k].img->height != ih->llimg[0].img->height) return 0;
380 return 1;
383 static void extract_image_highlevel(deark *c, lctx *d, i64 imgpos, i64 m1pos, i64 m2pos,
384 u8 is_icon)
386 struct image_highlevel_ctx *ih = NULL;
387 size_t k;
389 ih = de_malloc(c, sizeof(struct image_highlevel_ctx));
391 de_dbg(c, "[extracting image at %"I64_FMT" : %"I64_FMT" : %"I64_FMT"]",
392 imgpos, m1pos, m2pos);
393 de_dbg_indent(c, 1);
394 read_image_lowlevel(c, d, imgpos, ih, 0);
395 read_image_lowlevel(c, d, m1pos, ih, 1);
396 read_image_lowlevel(c, d, m2pos, ih, 2);
398 ih->is_icon = is_icon;
399 for(k=0; k<3; k++) {
400 if(!ih->llimg[k].fi) {
401 ih->llimg[k].fi = de_finfo_create(c);
403 ih->llimg[k].createflags |= DE_CREATEFLAG_OPT_IMAGE;
406 if(should_apply_masks(c, d, ih)) {
407 emit_images_highlevel_applymasks(c, d, ih);
409 else {
410 emit_images_highlevel_separate(c, d, ih);
412 de_dbg_indent(c, -1);
414 if(ih) {
415 for(k=0; k<3; k++) {
416 de_bitmap_destroy(ih->llimg[k].img);
417 de_finfo_destroy(c, ih->llimg[k].fi);
419 de_free(c, ih);
423 static int found_image(deark *c, lctx *d, struct chunk_ctx *cctx,
424 i64 imgpos, i64 m1pos, i64 m2pos)
426 int retval = 0;
427 int ret;
428 u8 is_icon;
430 if(imgpos) imgpos += d->img_ptr_bias;
431 if(m1pos) m1pos += d->img_ptr_bias;
432 if(m2pos) m2pos += d->img_ptr_bias;
433 de_dbg(c, "pointer: image at %"I64_FMT", mask1 at %"I64_FMT", mask2 at %"I64_FMT,
434 imgpos, m1pos, m2pos);
435 if(m1pos==m2pos) m1pos = 0;
436 if(imgpos==0 || imgpos>=c->infile->len || m1pos>=c->infile->len ||
437 m2pos>=c->infile->len)
439 goto done;
441 retval = 1;
443 ret = de_inthashtable_add_item(c, d->images_seen, imgpos, NULL);
444 if(!ret) {
445 de_dbg(c, "[already handled this image]");
446 goto done;
449 is_icon = (cctx->ck_type==0x0003 || cctx->ck_type==0x0100);
450 extract_image_highlevel(c, d, imgpos, m1pos, m2pos, is_icon);
452 done:
453 return retval;
456 static void handle_chunk_1imageptr(deark *c, lctx *d, struct chunk_ctx *cctx, i64 offset)
458 i64 n;
460 n = de_getu32le(cctx->ck_pos + offset);
461 found_image(c, d, cctx, n, 0, 0);
464 static void handle_chunk_multiimageptr(deark *c, lctx *d, struct chunk_ctx *cctx,
465 i64 offset, i64 item_size, i64 item_count)
467 i64 pos;
468 i64 i;
470 de_dbg(c, "item count: %"I64_FMT, item_count);
471 pos = cctx->ck_pos + offset;
473 for(i=0; i<item_count; i++) {
474 i64 n, m1, m2;
476 n = de_getu32le(pos);
477 m1 = de_getu32le(pos+4);
478 m2 = de_getu32le(pos+8);
479 if(!found_image(c, d, cctx, n, m1, m2)) goto done;
480 pos += item_size;
483 done:
487 static void handle_chunk_string(deark *c, lctx *d, i64 pos, i64 len,
488 de_ucstring *s, const char *name)
490 ucstring_empty(s);
491 dbuf_read_to_ucstring_n(c->infile, pos, len, 512, s,
492 DE_CONVFLAG_STOP_AT_NUL, d->input_encoding);
493 de_dbg(c, "%s: \"%s\"", name, ucstring_getpsz_d(s));
496 // sets d->last_chunklen (=total size)
497 // may set d->stopflag, ...
498 static void comicchat_read_chunk(deark *c, lctx *d, i64 pos1)
500 struct chunk_ctx *cctx = NULL;
501 int ret;
502 i64 foundpos = 0;
503 i64 n;
504 int saved_indent_level;
506 de_dbg_indent_save(c, &saved_indent_level);
507 cctx = de_malloc(c, sizeof(struct chunk_ctx));
508 cctx->ck_pos = pos1;
509 d->last_chunklen = 0;
510 if(cctx->ck_pos+2 > c->infile->len) goto done;
511 cctx->ck_type = (UI)de_getu16le(cctx->ck_pos);
512 de_dbg(c, "chunk at %"I64_FMT", type=0x%04x", cctx->ck_pos, (UI)cctx->ck_type);
513 de_dbg_indent(c, 1);
515 if(cctx->ck_type==0x0001) {
516 ret = dbuf_search_byte(c->infile, 0x00, cctx->ck_pos+2, 4096, &foundpos);
517 if(ret) {
518 cctx->ck_len = foundpos + 1 - cctx->ck_pos;
519 handle_chunk_string(c, d, cctx->ck_pos+2, cctx->ck_len-2,
520 d->char_name, "char name");
523 else if(cctx->ck_type==0x0002) {
524 cctx->ck_len = 4;
526 else if(cctx->ck_type==0x0003) {
527 cctx->ck_len = 6;
528 handle_chunk_1imageptr(c, d, cctx, 2);
530 else if(cctx->ck_type==0x0004) {
531 n = de_getu16le(cctx->ck_pos+2);
532 cctx->ck_len = 4 + 43*n;
533 handle_chunk_multiimageptr(c, d, cctx, 4, 43, n);
535 else if(cctx->ck_type==0x0005 || cctx->ck_type==0x0009) {
536 n = de_getu16le(cctx->ck_pos+2);
537 cctx->ck_len = 4 + 35*n;
538 handle_chunk_multiimageptr(c, d, cctx, 4, 35, n);
540 else if(cctx->ck_type==0x0006 || cctx->ck_type==0x0007) {
541 cctx->ck_len = 2;
542 d->stopflag = 1;
543 goto done;
545 else if(cctx->ck_type==0x0008) {
546 cctx->ck_len = 4;
548 else if(cctx->ck_type==0x000a) {
549 n = de_getu16le(cctx->ck_pos+2);
550 cctx->ck_len = 4 + 33*n;
551 handle_chunk_multiimageptr(c, d, cctx, 4, 33, n);
553 else if(cctx->ck_type==0x000b || cctx->ck_type==0x000c) {
554 n = de_getu16le(cctx->ck_pos+2);
555 cctx->ck_len = 4 + 25*n;
556 handle_chunk_multiimageptr(c, d, cctx, 4, 25, n);
558 else if(cctx->ck_type>=0x0100 && cctx->ck_type<=0x01ff) {
559 i64 dlen_field;
561 dlen_field = de_getu16le(cctx->ck_pos+2);
562 cctx->ck_len = dlen_field + 4;
564 if(cctx->ck_type==0x0100 || cctx->ck_type==0x0102) {
565 handle_chunk_1imageptr(c, d, cctx, 4);
567 else if(cctx->ck_type==0x0103) {
568 handle_chunk_string(c, d, cctx->ck_pos+4, cctx->ck_len-4, d->tmpstr,
569 "copyright/author");
571 else if(cctx->ck_type==0x0104) {
572 handle_chunk_string(c, d, cctx->ck_pos+4, cctx->ck_len-4, d->tmpstr, "url1");
574 else if(cctx->ck_type==0x0105) {
575 handle_chunk_string(c, d, cctx->ck_pos+4, cctx->ck_len-4, d->tmpstr, "url2");
577 else if(cctx->ck_type==0x0107) {
578 n = de_getu32le(cctx->ck_pos+4);
579 d->img_ptr_bias += n;
580 de_sanitize_offset(&d->img_ptr_bias);
581 de_dbg(c, "image pointer bias: %"I64_FMT, n);
585 if(cctx->ck_len<2) {
586 de_err(c, "Invalid file or unsupported chunk (0x%04x); can't continue",
587 cctx->ck_type);
588 d->stopflag = 1;
589 goto done;
592 de_dbg(c, "chunk len: %"I64_FMT, cctx->ck_len);
593 if(c->debug_level>=3) {
594 de_dbg_hexdump(c, c->infile, cctx->ck_pos, cctx->ck_len, 256, NULL, 0x1);
596 d->last_chunklen = cctx->ck_len;
598 done:
599 de_free(c, cctx);
600 de_dbg_indent_restore(c, saved_indent_level);
603 static void de_run_comicchat(deark *c, de_module_params *mparams)
605 i64 pos = 0;
606 lctx *d = NULL;
607 const char *tstr;
609 d = de_malloc(c, sizeof(lctx));
610 d->char_name = ucstring_create(c);
611 d->tmpstr = ucstring_create(c);
612 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_WINDOWS1252);
613 d->opt_applymasks = de_get_ext_option_bool(c, "comicchat:applymasks", 1);
615 de_dbg(c, "header at %"I64_FMT, pos);
616 de_dbg_indent(c, 1);
617 pos += 2;
618 d->fmt_code = (UI)de_getu16le_p(&pos);
619 d->major_ver = (UI)de_getu16le_p(&pos);
620 if(d->major_ver==1) tstr = "v1.0-2.1";
621 else if(d->major_ver==2) tstr = "v2.5";
622 else tstr = "?";
623 de_dbg(c, "fmt ver: %u (%s)", d->major_ver, tstr);
624 if(d->major_ver==2 && d->fmt_code==3) tstr = " (BGB)";
625 else tstr = "";
626 de_dbg(c, "fmt subtype: %u%s", d->fmt_code, tstr);
627 de_dbg_indent(c, -1);
629 d->images_seen = de_inthashtable_create(c);
630 while(1) {
631 comicchat_read_chunk(c, d, pos);
632 if(d->stopflag || d->last_chunklen==0) goto done;
633 pos += d->last_chunklen;
636 done:
637 if(d) {
638 ucstring_destroy(d->char_name);
639 ucstring_destroy(d->tmpstr);
640 de_inthashtable_destroy(c, d->images_seen);
641 de_free(c, d);
645 static int de_identify_comicchat(deark *c)
647 u8 has_ext, has_hdr, has_endtag;
648 UI n0, n2, n4;
650 n0 = (UI)de_getu16be(0);
651 if(n0!=0x8100 && n0!=0x8181) return 0;
652 n2 = (UI)de_getu16le(2);
653 n4 = (UI)de_getu16le(4);
655 has_hdr = 0;
656 if(n0==0x8100) {
657 if(n4==1 && (n2==1 || n2==2)) has_hdr = 1;
659 else { // 0x8181
660 if(n4==2 && (n2>=1 && n2<=3)) has_hdr = 1;
662 if(!has_hdr) return 0;
664 has_endtag = (de_getu16le(c->infile->len - 2) == 0x0007);
665 has_ext = (de_input_file_has_ext(c, "avb") ||
666 de_input_file_has_ext(c, "bgb"));
667 if(has_endtag && has_ext) return 100;
668 if(has_endtag || has_ext) return 55;
669 return 35;
672 static void de_help_comicchat(deark *c)
674 de_msg(c, "-opt comicchat:applymasks=0 : Write masks to separate files");
677 void de_module_comicchat(deark *c, struct deark_module_info *mi)
679 mi->id = "comicchat";
680 mi->desc = "Microsoft Comic Chat AVB or BGB";
681 mi->run_fn = de_run_comicchat;
682 mi->identify_fn = de_identify_comicchat;
683 mi->help_fn = de_help_comicchat;