fnt: Improved error handling, etc.
[deark.git] / modules / insetpix.c
blob0473cf3fdb82803a3dfe3b907158b5667489220f
1 // This file is part of Deark.
2 // Copyright (C) 2016-2024 Jason Summers
3 // See the file COPYING for terms of use.
5 // Inset .PIX
7 #include <deark-private.h>
8 #include <deark-fmtutil.h>
9 DE_DECLARE_MODULE(de_module_insetpix);
11 struct insetpix_item_data {
12 UI id;
13 UI tile_num;
14 u8 is_special;
15 u8 is_tile;
16 i64 len;
17 i64 loc;
20 typedef struct localctx_insetpix {
21 de_encoding input_encoding;
22 u8 hmode;
23 u8 htype;
24 u8 graphics_type; // 0=character, 1=bitmap
25 u8 board_type;
27 u8 have_image_info; // 1 = we've read the segment
28 u8 have_pal_data;
29 u8 have_tile_info;
31 i64 imginfo_pos, imginfo_len; // 0 = no info
32 i64 pal_pos, pal_len;
33 i64 tileinfo_pos, tileinfo_len;
35 i64 item_count;
36 i64 num_tiles_found;
37 i64 idx_of_1st_tile;
38 i64 npwidth, pdwidth, height;
39 i64 w_in_chars, h_in_chars;
40 i64 gfore; // Foreground color bits
41 i64 max_sample_value;
42 u8 pal_sample_descriptor[4]; // 0=intens, 1=red, 2=green, 3=blue
43 u32 descriptor_combined;
44 i64 haspect, vaspect;
46 i64 page_rows, page_cols;
47 i64 stp_rows, stp_cols;
49 i64 rowspan;
50 i64 compression_bytes_per_row;
51 int is_grayscale;
53 u8 max_pal_intensity, max_pal_sample;
55 de_bitmap *tile_img; // Re-used for each tile
57 i64 pal_entries_used;
58 de_color pal[256];
59 } lctx;
61 // Sets d->have_pal_data if successful
62 static void do_palette(deark *c, lctx *d)
64 i64 pos1;
65 i64 pos;
66 i64 pal_entries_in_file;
67 i64 i;
68 size_t k;
69 u8 sm1[4]; // Original I, R, G, B
70 u8 sm2[4]; // Intermediate
71 u8 sm3[4]; // Post-processed samples
72 u8 uses_intens = 0; // Special handling if we have RGB and 'I' values.
73 double max_color_sample;
74 double pal_sample_scalefactor[4];
75 int saved_indent_level;
77 de_dbg_indent_save(c, &saved_indent_level);
78 pos1 = d->pal_pos;
79 if(pos1==0 || d->pal_len<4) goto done;
80 pos = pos1;
81 de_dbg(c, "palette at %"I64_FMT, pos1);
82 de_dbg_indent(c, 1);
84 pal_entries_in_file = d->pal_len/4;
85 de_dbg(c, "number of palette colors: %d", (int)pal_entries_in_file);
87 d->pal_entries_used = pal_entries_in_file; // default
88 if(d->graphics_type==1) {
89 d->pal_entries_used = d->max_sample_value+1;
91 if(d->pal_entries_used > pal_entries_in_file) d->pal_entries_used = pal_entries_in_file;
94 // If intensity bits are used, make the initial colors darker, so that the
95 // intensity bits can lighten them.
96 uses_intens = (d->pal_sample_descriptor[0]!=0) && !d->is_grayscale;
97 max_color_sample = uses_intens ? 170.0 : 255.0;
99 for(k=0; k<4; k++) {
100 if(d->pal_sample_descriptor[k]>=2)
101 pal_sample_scalefactor[k] = max_color_sample / (double)(d->pal_sample_descriptor[k]-1);
102 else
103 pal_sample_scalefactor[k] = 0.0;
106 for(i=0; i<pal_entries_in_file; i++) {
107 char tmps[64];
109 if(i>255) break;
111 for(k=0; k<4; k++) {
112 sm1[k] = de_getbyte_p(&pos);
113 sm2[k] = sm1[k];
116 for(k=1; k<4; k++) {
117 // Best I can figure is that, in the palette definition, when there
118 // are exactly 4 sample intensities, intensity 1 is brighter than
119 // intensity 2. I don't know why I have to swap them like this.
120 if(d->pal_sample_descriptor[k]==4) {
121 if(sm2[k]==1) sm2[k] = 2;
122 else if(sm2[k]==2) sm2[k] = 1;
126 if(d->is_grayscale) {
127 sm3[0] = (u8)(0.5+ pal_sample_scalefactor[0] * (double)sm2[0]);
128 d->pal[i] = DE_MAKE_GRAY(sm3[0]);
130 else {
131 sm3[1] = (u8)(0.5+ pal_sample_scalefactor[1] * (double)sm2[1]);
132 sm3[2] = (u8)(0.5+ pal_sample_scalefactor[2] * (double)sm2[2]);
133 sm3[3] = (u8)(0.5+ pal_sample_scalefactor[3] * (double)sm2[3]);
134 if(uses_intens && sm2[0]) {
135 // This is just a guess. The spec doesn't say what intensity bits do.
136 // This is pretty much what old PC graphics cards do when the
137 // intensity bit is set.
138 sm3[1] += 85;
139 sm3[2] += 85;
140 sm3[3] += 85;
142 d->pal[i] = DE_MAKE_RGB(sm3[1],sm3[2],sm3[3]);
145 if(uses_intens) {
146 de_snprintf(tmps, sizeof(tmps), "(%3u,%3u,%3u,intens=%u) "DE_CHAR_RIGHTARROW" ",
147 (UI)sm1[1], (UI)sm1[2], (UI)sm1[3], (UI)sm1[0]);
149 else {
150 de_snprintf(tmps, sizeof(tmps), "(%3u,%3u,%3u) "DE_CHAR_RIGHTARROW" ",
151 (UI)sm1[1], (UI)sm1[2], (UI)sm1[3]);
153 de_dbg_pal_entry2(c, i, d->pal[i], tmps, NULL,
154 i<d->pal_entries_used ? "":" [unused]");
157 d->have_pal_data = 1;
158 done:
159 de_dbg_indent_restore(c, saved_indent_level);
162 static const char *get_board_type_name(u8 bt)
164 const char *name = NULL;
166 switch(bt & 0x7e) {
167 case 0: name="none"; break;
168 case 8: name="CGA"; break;
169 case 16: name="Hercules"; break;
170 case 24: name="EGA"; break;
172 return name?name:"?";
175 // Sets d->have_image_info if successful
176 static void do_image_info(deark *c, lctx *d)
178 i64 pos1;
179 i64 pos;
180 int saved_indent_level;
182 de_dbg_indent_save(c, &saved_indent_level);
183 pos1 = d->imginfo_pos;
184 if(!pos1 || d->imginfo_len<32) goto done;
186 de_dbg(c, "image information at %"I64_FMT, pos1);
187 de_dbg_indent(c, 1);
188 pos = pos1;
190 d->hmode = de_getbyte_p(&pos);
191 de_dbg(c, "hardware mode: %u", (UI)d->hmode);
193 d->htype = de_getbyte_p(&pos);
194 d->graphics_type = d->htype & 0x01;
195 d->board_type = d->htype & 0xfe;
196 de_dbg(c, "htype: 0x%02x", (UI)d->htype);
197 de_dbg_indent(c, 1);
198 de_dbg(c, "board type: %u (%s)", (UI)d->board_type,
199 get_board_type_name(d->board_type));
200 de_dbg(c, "graphics type: %u (%s)", (UI)d->graphics_type,
201 d->graphics_type?"bitmap":"character");
202 de_dbg_indent(c, -1);
204 if(d->graphics_type==0) {
205 pos = pos1 + 5;
206 d->w_in_chars = (i64)de_getbyte_p(&pos);
207 d->h_in_chars = (i64)de_getbyte_p(&pos);
208 de_dbg(c, "dimensions: %u"DE_CHAR_TIMES"%u characters",
209 (UI)d->w_in_chars, (UI)d->h_in_chars);
212 if(d->graphics_type==1) {
213 pos = pos1 + 18;
214 d->npwidth = de_getu16le_p(&pos);
215 d->height = de_getu16le_p(&pos);
216 de_dbg_dimensions(c, d->npwidth, d->height);
218 d->gfore = (i64)de_getbyte_p(&pos);
219 de_dbg(c, "foreground color bits: %d", (int)d->gfore);
220 d->max_sample_value = de_pow2(d->gfore) -1;
223 pos = pos1 + 25;
224 d->descriptor_combined = (u32)de_getu32be_p(&pos);
225 d->pal_sample_descriptor[0] = (u8)(d->descriptor_combined>>24);
226 d->pal_sample_descriptor[1] = (u8)((d->descriptor_combined>>16) & 0xff);
227 d->pal_sample_descriptor[2] = (u8)((d->descriptor_combined>>8) & 0xff);
228 d->pal_sample_descriptor[3] = (u8)(d->descriptor_combined & 0xff);
229 de_dbg(c, "palette descriptor (IRGB): %u,%u,%u,%u",
230 (UI)d->pal_sample_descriptor[0], (UI)d->pal_sample_descriptor[1],
231 (UI)d->pal_sample_descriptor[2], (UI)d->pal_sample_descriptor[3]);
233 pos++; // "pages"
234 d->haspect = de_getbyte_p(&pos);
235 d->vaspect = de_getbyte_p(&pos);
236 de_dbg(c, "aspect ratio: %d"DE_CHAR_TIMES"%d", (int)d->haspect, (int)d->vaspect);
238 d->have_image_info = 1;
239 done:
240 de_dbg_indent_restore(c, saved_indent_level);
243 // Sets d->have_tile_info if successful
244 static void do_tileinfo(deark *c, lctx *d)
246 i64 pos1;
247 i64 pos;
248 int saved_indent_level;
250 de_dbg_indent_save(c, &saved_indent_level);
251 pos1 = d->tileinfo_pos;
252 if(!pos1 || d->tileinfo_len<8) goto done;
254 de_dbg(c, "tile information at %"I64_FMT, pos1);
255 de_dbg_indent(c, 1);
256 pos = pos1;
258 d->page_rows = de_getu16le_p(&pos);
259 d->page_cols = de_getu16le_p(&pos);
260 d->stp_rows = de_getu16le_p(&pos);
261 d->stp_cols = de_getu16le_p(&pos);
262 de_dbg(c, "dimensions of a tile: %"I64_FMT DE_CHAR_TIMES "%"I64_FMT,
263 d->page_cols, d->page_rows);
264 de_dbg(c, "dimensions in tiles: %"I64_FMT DE_CHAR_TIMES "%"I64_FMT,
265 d->stp_cols, d->stp_rows);
267 if(d->page_cols%8 != 0) {
268 de_err(c, "page_cols must be a multiple of 8 (is %d)", (int)d->page_cols);
269 goto done;
272 if(d->num_tiles_found==0) goto done;
274 d->have_tile_info = 1;
275 done:
276 de_dbg_indent_restore(c, saved_indent_level);
279 static u8 getbit(const u8 *m, i64 bitnum)
281 u8 b;
283 b = m[bitnum/8];
284 b = (b>>(7-bitnum%8)) & 0x1;
285 return b;
288 static void do_decompress_tile(deark *c, lctx *d, struct insetpix_item_data *itd,
289 dbuf *unc_pixels, i64 num_rows)
291 u8 *rowbuf1 = NULL;
292 u8 *compression_bytes = NULL;
293 i64 pos;
294 i64 i, j;
295 i64 plane;
296 i64 endpos = itd->loc + itd->len;
298 // There are d->gfore planes (1-bpp images). The first row of each plane is
299 // uncompressed. The rest are compressed with a delta compression algorithm.
300 // There are d->page_rows rows in each plane.
302 rowbuf1 = de_malloc(c, d->rowspan);
303 compression_bytes = de_malloc(c, d->compression_bytes_per_row);
305 pos = itd->loc;
307 for(plane=0; plane<d->gfore; plane++) {
308 for(j=0; j<num_rows; j++) {
309 if(pos >= endpos) {
310 de_warn(c, "Not enough data in tile %u", itd->tile_num);
311 goto done;
314 if(j==0) {
315 // First row is stored uncompressed
316 dbuf_copy(c->infile, pos, d->rowspan, unc_pixels);
317 pos += d->rowspan;
319 else {
320 de_read(compression_bytes, pos, d->compression_bytes_per_row);
321 pos += d->compression_bytes_per_row;
323 // Read back a copy of the previous row
324 dbuf_read(unc_pixels, rowbuf1, unc_pixels->len - d->rowspan, d->rowspan);
326 // For every 1 bit in the compression_bytes array, read a byte from the file.
327 // For every 0 bit, copy the byte from the previous row.
328 for(i=0; i<d->rowspan; i++) {
329 u8 b;
331 if(getbit(compression_bytes, i)) {
332 b = de_getbyte_p(&pos);
334 else {
335 b = rowbuf1[i];
337 dbuf_writebyte(unc_pixels, b);
343 de_dbg(c, "decompressed %"I64_FMT" bytes to %"I64_FMT, pos-itd->loc, unc_pixels->len);
345 done:
346 de_free(c, compression_bytes);
347 de_free(c, rowbuf1);
350 static void do_render_tile(deark *c, lctx *d, de_bitmap *img,
351 struct insetpix_item_data *itd)
353 i64 x_pos_in_tiles, y_pos_in_tiles;
354 i64 x_origin_in_pixels, y_origin_in_pixels;
355 dbuf *unc_pixels = NULL;
356 i64 nrows_expected;
357 i64 planespan;
359 x_pos_in_tiles = (i64)itd->tile_num % d->stp_cols;
360 y_pos_in_tiles = (i64)itd->tile_num / d->stp_cols;
362 x_origin_in_pixels = x_pos_in_tiles * d->page_cols;
363 y_origin_in_pixels = y_pos_in_tiles * d->page_rows;
365 // "If the actual row bound of the tile exceeds the image, the extra
366 // rows are not present."
367 nrows_expected = d->height - y_origin_in_pixels;
368 if(nrows_expected > d->page_rows) nrows_expected = d->page_rows;
369 planespan = nrows_expected * d->rowspan;
371 de_dbg(c, "tile (%d,%d), pixel position (%d,%d), size %d"DE_CHAR_TIMES"%d",
372 (int)x_pos_in_tiles, (int)y_pos_in_tiles,
373 (int)x_origin_in_pixels, (int)y_origin_in_pixels,
374 (int)d->page_cols, (int)nrows_expected);
376 unc_pixels = dbuf_create_membuf(c, 4096, 0);
378 do_decompress_tile(c, d, itd, unc_pixels, nrows_expected);
380 if(d->tile_img) {
381 // Clear the previous image
382 de_bitmap_rect(d->tile_img, 0, 0, d->page_cols, d->page_rows, 0, 0);
384 else {
385 d->tile_img = de_bitmap_create(c, d->page_cols, d->page_rows, d->is_grayscale?1:3);
388 // This will try to convert 'd->page_rows' rows, when there might only be
389 // 'nrows_expected' rows, but that won't cause a problem.
390 de_convert_image_paletted_planar(unc_pixels, 0, d->gfore, d->rowspan, planespan,
391 d->pal, d->tile_img, 0x2);
393 de_bitmap_copy_rect(d->tile_img, img, 0, 0, d->page_cols, nrows_expected, x_origin_in_pixels,
394 y_origin_in_pixels, 0);
396 dbuf_close(unc_pixels);
399 static void insetpix_read_item(deark *c, i64 pos, struct insetpix_item_data *itd)
401 itd->id = (UI)de_getu16le_p(&pos);
402 itd->len = de_getu16le_p(&pos);
403 itd->loc = de_getu32le(pos);
404 itd->is_special = (u8)(itd->id>=0x4000);
405 itd->is_tile = (u8)(itd->id>=0x8000 && itd->id<0xffff);
406 if(itd->is_tile)
407 itd->tile_num = itd->id-0x8000;
408 else
409 itd->tile_num = 0;
412 static void get_item_name(struct insetpix_item_data *itd, char *nbuf, size_t nbuf_len)
414 const char *n1;
416 if(itd->is_tile) {
417 de_snprintf(nbuf, nbuf_len, "tile #%u", itd->tile_num);
418 return;
421 switch(itd->id) {
422 case 0: n1 = "image info"; break;
423 case 1: n1 = "palette"; break;
424 case 2: n1 = "tile info"; break;
425 case 17: n1 = "printing options"; break;
426 case 0xff: n1 = "empty"; break;
427 default: n1 = "?";
429 de_strlcpy(nbuf, n1, nbuf_len);
432 static void insetpix_dbg_item(deark *c, struct insetpix_item_data *itd, i64 idx)
434 char nbuf[40];
436 get_item_name(itd, nbuf, sizeof(nbuf));
437 de_dbg(c, "item #%d: id=%u (%s), loc=%"I64_FMT", len=%"I64_FMT, (int)idx,
438 itd->id, nbuf, itd->loc, itd->len);
441 static void do_bitmap(deark *c, lctx *d)
443 i64 item;
444 de_bitmap *img = NULL;
445 de_finfo *fi = NULL;
447 de_dbg(c, "reading image data");
448 de_dbg_indent(c, 1);
450 if(!de_good_image_dimensions(c, d->npwidth, d->height)) goto done;
452 d->rowspan = d->page_cols/8;
453 d->compression_bytes_per_row = (d->rowspan+7)/8; // Just a guess. Spec doesn't say.
455 if(c->padpix) {
456 d->pdwidth = d->page_cols * d->stp_cols;
458 else {
459 d->pdwidth = d->npwidth;
462 img = de_bitmap_create2(c, d->npwidth, d->pdwidth, d->height, d->is_grayscale?1:3);
464 // Read through the items again, this time looking only at the image tiles.
465 for(item=0; item<d->item_count; item++) {
466 struct insetpix_item_data itd;
467 i64 pos;
469 pos = 4 + 8*item;
470 if(pos+8 > c->infile->len) break;
472 insetpix_read_item(c, pos, &itd);
473 if(!itd.is_tile) continue;
474 insetpix_dbg_item(c, &itd, item);
476 de_dbg_indent(c, 1);
477 do_render_tile(c, d, img, &itd);
478 de_dbg_indent(c, -1);
481 fi = de_finfo_create(c);
482 fi->density.code = DE_DENSITY_UNK_UNITS;
483 fi->density.xdens = (double)d->haspect;
484 fi->density.ydens = (double)d->vaspect;
485 de_bitmap_write_to_file_finfo(img, fi, DE_CREATEFLAG_OPT_IMAGE);
487 done:
488 de_bitmap_destroy(img);
489 de_finfo_destroy(c, fi);
490 de_dbg_indent(c, -1);
493 static void do_char_graphics(deark *c, lctx *d)
495 struct insetpix_item_data itd;
496 dbuf *unc_pixels = NULL;
497 struct de_char_context *charctx = NULL;
498 struct fmtutil_char_simplectx *csctx = NULL;
499 int saved_indent_level;
501 de_dbg_indent_save(c, &saved_indent_level);
502 de_dbg(c, "reading char graphics");
503 de_dbg_indent(c, 1);
504 if(d->num_tiles_found!=1) {
505 de_err(c, "Multi-tile char. graphics not supported");
506 goto done;
509 insetpix_read_item(c, 4 + 8*d->idx_of_1st_tile, &itd);
510 insetpix_dbg_item(c, &itd, d->idx_of_1st_tile);
512 unc_pixels = dbuf_create_membuf(c, 4096, 0);
513 csctx = de_malloc(c, sizeof(struct fmtutil_char_simplectx));
514 charctx = de_create_charctx(c, 0);
516 csctx->width_in_chars = d->page_cols;
517 csctx->height_in_chars = d->page_rows;
519 // Unless I'm missing something, this format is just wacky.
520 // Assume the screen is 80x25. The original data is then 80x25 bytes of
521 // character data, followed immediately by 80x25 bytes of attribute data.
522 // So its sort of 80x50. Good so far. But then it's compressed as if it were
523 // 160x25. Which is strange.
524 // The first 12 rows are character data.
525 // The 13th row is half character data, half attribute data.
526 // The last 12 rows are attribute data.
527 // The compression is only effective when a character is the same as the one
528 // *two* rows above it.
529 // (The format documentation does hint at this, but doesn't adequately
530 // explain it.)
532 d->gfore = 1; // hack (gfore is used as # of planes)
533 d->rowspan = csctx->width_in_chars*2;
534 d->compression_bytes_per_row = (d->rowspan+7)/8;
535 do_decompress_tile(c, d, &itd, unc_pixels, csctx->height_in_chars);
537 csctx->input_encoding = d->input_encoding;
538 csctx->inf = unc_pixels;
539 csctx->inf_pos = 0;
540 csctx->inf_len = csctx->width_in_chars * csctx->height_in_chars * 2;
541 csctx->fg_stride = 1;
542 csctx->attr_offset = csctx->width_in_chars*csctx->height_in_chars;
543 de_memcpy(&charctx->pal, &d->pal, sizeof(de_color)*16);
545 fmtutil_char_simple_run(c, csctx, charctx);
547 done:
548 de_free_charctx(c, charctx);
549 de_free(c, csctx);
550 dbuf_close(unc_pixels);
551 de_dbg_indent_restore(c, saved_indent_level);
554 static void de_run_insetpix(deark *c, de_module_params *mparams)
556 lctx *d = NULL;
557 UI pix_version;
558 i64 item;
559 i64 pos;
560 i64 num_skipped_items = 0;
561 int saved_indent_level;
563 de_dbg_indent_save(c, &saved_indent_level);
564 d = de_malloc(c, sizeof(lctx));
565 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_CP437);
567 pix_version = (UI)de_getu16le(0);
568 d->item_count = de_getu16le(2);
569 de_dbg(c, "version: %u", pix_version);
570 de_dbg(c, "index at 4, %d items", (int)d->item_count);
572 // Scan the index, and record the location of items we care about.
573 // (The index will be read again when converting the image bitmap.)
574 de_dbg_indent(c, 1);
575 for(item=0; item<d->item_count; item++) {
576 struct insetpix_item_data itd;
577 u8 skip_flag = 0;
579 pos = 4 + 8*item;
580 if(pos+8 > c->infile->len) goto done;
582 insetpix_read_item(c, pos, &itd);
583 if(itd.is_tile) {
584 if(d->num_tiles_found==0) {
585 d->idx_of_1st_tile = item;
587 d->num_tiles_found++;
589 skip_flag = itd.is_special;
590 if(skip_flag) {
591 num_skipped_items++;
593 if(skip_flag && c->debug_level<2) { // Skip "tile" items for now
594 continue;
597 insetpix_dbg_item(c, &itd, item);
599 if(skip_flag) {
600 continue;
603 if(itd.loc + itd.len > c->infile->len) {
604 de_err(c, "Item #%d (ID %u) goes beyond end of file",
605 (int)item, itd.id);
606 goto done;
609 switch(itd.id) {
610 case 0:
611 d->imginfo_pos = itd.loc;
612 d->imginfo_len = itd.len;
613 break;
614 case 1:
615 if(!d->pal_pos) {
616 d->pal_pos = itd.loc;
617 d->pal_len = itd.len;
619 break;
620 case 2:
621 d->tileinfo_pos = itd.loc;
622 d->tileinfo_len = itd.len;
623 break;
626 if(c->debug_level<2) {
627 de_dbg(c, "other items not listed: %"I64_FMT, num_skipped_items);
629 de_dbg(c, "number of tiles: %"I64_FMT, d->num_tiles_found);
630 de_dbg_indent(c, -1);
632 do_image_info(c, d);
633 if(!d->have_image_info) {
634 de_err(c, "Bad or missing Image Info");
635 goto done;
638 if(d->pal_sample_descriptor[0]!=0 && d->pal_sample_descriptor[1]==0 &&
639 d->pal_sample_descriptor[2]==0 && d->pal_sample_descriptor[3]==0)
641 d->is_grayscale = 1;
644 do_palette(c, d);
645 if(!d->have_pal_data) {
646 de_err(c, "Bad or missing palette");
647 goto done;
650 do_tileinfo(c, d);
651 if(!d->have_tile_info) {
652 de_err(c, "Bad or missing Tile Info");
653 goto done;
656 if(d->graphics_type==0) {
657 do_char_graphics(c, d);
659 else {
660 if(d->gfore<1 || d->gfore>8) {
661 de_err(c, "Inset PIX with %d bits/pixel are not supported", (int)d->gfore);
662 goto done;
665 switch(d->descriptor_combined) {
666 case 0x00040404U:
667 case 0x00404040U:
668 case 0x02000000U:
669 case 0x02020202U:
670 break;
671 default:
672 de_warn(c, "Not a known image type. This image might not be handled correctly.");
675 do_bitmap(c, d);
678 done:
679 if(d) {
680 de_bitmap_destroy(d->tile_img);
681 de_free(c, d);
683 de_dbg_indent_restore(c, saved_indent_level);
686 // Inset PIX is hard to identify.
687 static int de_identify_insetpix(deark *c)
689 UI pix_version;
690 i64 item_count;
691 i64 item;
692 i64 item_loc, item_len;
693 u8 has_ext;
694 u8 has_typical_1st_item;
696 if(c->detection_data->best_confidence_so_far>20) return 0;
698 pix_version = (UI)de_getu16le(0);
699 // Other versions exist, but I don't know anything about them.
700 if(pix_version!=3) return 0;
702 has_ext = (u8)de_input_file_has_ext(c, "pix");
703 has_typical_1st_item = (u8)((u32)de_getu32le(4)==(u32)0x00200000);
704 if(!has_ext && !has_typical_1st_item) return 0;
706 item_count = de_getu16le(2);
707 // Need at least 4 items (image info, palette info, tile info, and 1 tile).
708 if(item_count<4 || item_count>500) return 0;
710 if(4 + 8*item_count >= c->infile->len) return 0;
712 for(item=0; item<item_count && item<16; item++) {
713 item_len = de_getu16le(4+8*item+2);
714 item_loc = de_getu32le(4+8*item+4);
715 if(item_loc < 4 + 8*item_count) return 0;
716 if(item_loc+item_len > c->infile->len) return 0;
719 return 20;
722 void de_module_insetpix(deark *c, struct deark_module_info *mi)
724 mi->id = "insetpix";
725 mi->desc = "Inset PIX image";
726 mi->run_fn = de_run_insetpix;
727 mi->identify_fn = de_identify_insetpix;