1 // This file is part of Deark.
2 // Copyright (C) 2018 Jason Summers
3 // See the file COPYING for terms of use.
5 // Zoner BMI bitmap image
7 // Warning: This code is not based on any written specifications, so it may be
8 // wrong or misleading. Read it at your own risk.
10 #include <deark-config.h>
11 #include <deark-private.h>
12 #include <deark-fmtutil.h>
13 DE_DECLARE_MODULE(de_module_bmi
);
28 typedef struct localctx_struct
{
30 i64 fixed_header_size
;
32 struct table_item
*table
;
33 struct imageinfo globalimg
;
36 static void read_palette(deark
*c
, lctx
*d
, struct imageinfo
*ii
, i64 pos1
)
38 if(ii
->num_pal_entries
<1) return;
39 de_dbg(c
, "palette at %"I64_FMT
", %d entries", pos1
, (int)ii
->num_pal_entries
);
41 de_read_palette_rgb(c
->infile
, pos1
, ii
->num_pal_entries
, 4,
42 ii
->pal
, 256, DE_GETRGBFLAG_BGR
);
46 // Read the fixed part of the header
47 static int do_header(deark
*c
, lctx
*d
, i64 pos1
)
52 de_dbg(c
, "header at %d", (int)pos1
);
55 pos
+= 9; // signature
57 d
->globalimg
.w
= de_getu16le_p(&pos
);
58 d
->globalimg
.h
= de_getu16le_p(&pos
);
59 de_dbg_dimensions(c
, d
->globalimg
.w
, d
->globalimg
.h
);
61 d
->globalimg
.palmode
= (unsigned int)de_getu16le_p(&pos
);
62 de_dbg(c
, "palette mode: %u", d
->globalimg
.palmode
);
64 d
->globalimg
.bpp
= de_getu16le_p(&pos
);
65 de_dbg(c
, "bits/pixel: %d", (int)d
->globalimg
.bpp
);
67 if(d
->globalimg
.palmode
&& d
->globalimg
.bpp
>=1 && d
->globalimg
.bpp
<=8) {
68 d
->globalimg
.num_pal_entries
= de_pow2(d
->globalimg
.bpp
);
73 d
->num_table_items
= de_getu16le_p(&pos
);
74 if(d
->num_table_items
>100) goto done
;
76 d
->fixed_header_size
= pos
- pos1
;
81 de_err(c
, "Error reading header");
87 static int do_read_table(deark
*c
, lctx
*d
, i64 pos1
)
92 de_dbg(c
, "table at %d, %d items", (int)pos1
, (int)d
->num_table_items
);
93 d
->table
= de_mallocarray(c
, d
->num_table_items
, sizeof(struct table_item
));
97 for(k
=0; k
<d
->num_table_items
; k
++) {
98 d
->table
[k
].tag_num
= (unsigned int)de_getu16le_p(&pos
);
99 d
->table
[k
].tag_offs
= de_getu32le_p(&pos
);
100 de_dbg(c
, "item[%d]: tag=0x%x, offset=%"I64_FMT
, (int)k
,
101 d
->table
[k
].tag_num
, d
->table
[k
].tag_offs
);
104 de_dbg_indent(c
, -1);
108 static void do_bitmap(deark
*c
, lctx
*d
, i64 pos1
)
110 int saved_indent_level
;
112 i64 unc_data_size_reported
;
113 i64 unc_data_size_calc
;
114 i64 max_uncmpr_block_size
;
116 de_bitmap
*img
= NULL
;
117 dbuf
*unc_pixels
= NULL
;
119 const u32
*pal_to_use
= d
->globalimg
.pal
;
121 de_zeromem(&ii
, sizeof(struct imageinfo
));
123 de_dbg_indent_save(c
, &saved_indent_level
);
125 de_dbg(c
, "bitmap at %"I64_FMT
, pos1
);
127 ii
.w
= de_getu16le_p(&pos
);
128 ii
.h
= de_getu16le_p(&pos
);
129 de_dbg_dimensions(c
, ii
.w
, ii
.h
);
131 ii
.bpp
= de_getu16le_p(&pos
);
132 de_dbg(c
, "bits/pixel: %d", (int)ii
.bpp
);
134 ii
.palmode
= (unsigned int)de_getu16le_p(&pos
);
135 de_dbg(c
, "palette mode: %u", ii
.palmode
);
141 if(ii
.palmode
&& ii
.bpp
>=1 && ii
.bpp
<=8) {
142 ii
.num_pal_entries
= de_pow2(ii
.bpp
);
147 unc_data_size_reported
= de_getu32le_p(&pos
);
148 de_dbg(c
, "uncmpr data size (reported): %"I64_FMT
, unc_data_size_reported
);
150 rowspan
= de_pad_to_n(ii
.w
*ii
.bpp
, 32)/8;
151 unc_data_size_calc
= rowspan
* ii
.h
;
152 de_dbg(c
, "uncmpr data size (calculated): %"I64_FMT
, unc_data_size_calc
);
154 if(unc_data_size_reported
>DE_MAX_SANE_OBJECT_SIZE
) goto done
;
156 max_uncmpr_block_size
= de_getu16le_p(&pos
);
157 de_dbg(c
, "max uncmpr block size: %d", (int)max_uncmpr_block_size
);
158 if(max_uncmpr_block_size
> unc_data_size_calc
) {
159 max_uncmpr_block_size
= unc_data_size_calc
;
162 if(ii
.num_pal_entries
>0) {
163 read_palette(c
, d
, &ii
, pos
);
164 pos
+= 4*ii
.num_pal_entries
;
167 if(!de_good_image_dimensions(c
, ii
.w
, ii
.h
)) goto done
;
168 if(ii
.bpp
!=24 && ii
.bpp
!=8 && ii
.bpp
!=4 && ii
.bpp
!=1) {
169 de_err(c
, "Unsupported image type");
173 unc_pixels
= dbuf_create_membuf(c
, unc_data_size_calc
, 1);
178 if(unc_pixels
->len
>= unc_data_size_reported
) break;
179 if(pos
>= c
->infile
->len
) goto done
;
181 de_dbg(c
, "block at %d", (int)pos
);
183 blen
= de_getu16le_p(&pos
);
184 de_dbg(c
, "block len: %d", (int)blen
);
186 if(pos
+blen
> c
->infile
->len
) goto done
;
187 if(blen
>max_uncmpr_block_size
) goto done
;
189 if(unc_pixels
->len
< unc_data_size_calc
) {
190 i64 len_before
= unc_pixels
->len
;
192 if(!fmtutil_decompress_deflate(c
->infile
, pos
, blen
, unc_pixels
,
193 max_uncmpr_block_size
, NULL
,
194 DE_DEFLATEFLAG_ISZLIB
|DE_DEFLATEFLAG_USEMAXUNCMPRSIZE
))
199 de_dbg(c
, "decompressed to: %"I64_FMT
" (total=%"I64_FMT
")",
200 unc_pixels
->len
- len_before
, unc_pixels
->len
);
202 de_dbg_indent(c
, -1);
207 img
= de_bitmap_create(c
, ii
.w
, ii
.h
, 3);
210 de_convert_image_rgb(unc_pixels
, 0, rowspan
, 3, img
, DE_GETRGBFLAG_BGR
);
213 de_convert_image_paletted(unc_pixels
, 0, ii
.bpp
, rowspan
, pal_to_use
, img
, 0);
216 de_bitmap_write_to_file(img
, NULL
, DE_CREATEFLAG_OPT_IMAGE
);
219 de_bitmap_destroy(img
);
220 dbuf_close(unc_pixels
);
221 de_dbg_indent_restore(c
, saved_indent_level
);
224 static void do_bitmaps(deark
*c
, lctx
*d
)
228 for(k
=0; k
<d
->num_table_items
; k
++) {
229 if(d
->table
[k
].tag_num
==0x0001) {
230 do_bitmap(c
, d
, d
->table
[k
].tag_offs
);
235 static void do_comment(deark
*c
, lctx
*d
, i64 idx
, i64 pos1
)
237 de_ucstring
*s
= NULL
;
242 cmt_len
= de_getu32le_p(&pos
);
245 s
= ucstring_create(c
);
246 dbuf_read_to_ucstring_n(c
->infile
, pos
, cmt_len
, DE_DBG_MAX_STRLEN
, s
,
247 DE_CONVFLAG_STOP_AT_NUL
, d
->input_encoding
);
248 de_dbg(c
, "comment (item[%d]): \"%s\"", (int)idx
, ucstring_getpsz_d(s
));
252 static void do_comments(deark
*c
, lctx
*d
)
256 for(k
=0; k
<d
->num_table_items
; k
++) {
257 if(d
->table
[k
].tag_num
==0x0003) {
258 do_comment(c
, d
, k
, d
->table
[k
].tag_offs
);
263 static void de_run_bmi(deark
*c
, de_module_params
*mparams
)
268 d
= de_malloc(c
, sizeof(lctx
));
270 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_WINDOWS1252
);
272 if(!do_header(c
, d
, pos
)) goto done
;
273 pos
+= d
->fixed_header_size
;
275 if(d
->globalimg
.num_pal_entries
>0) {
276 read_palette(c
, d
, &d
->globalimg
, pos
);
277 pos
+= 4*d
->globalimg
.num_pal_entries
;
280 if(!do_read_table(c
, d
, pos
)) goto done
;
286 de_free(c
, d
->table
);
291 static int de_identify_bmi(deark
*c
)
293 if(!dbuf_memcmp(c
->infile
, 0, "ZonerBMIa", 9))
298 void de_module_bmi(deark
*c
, struct deark_module_info
*mi
)
301 mi
->desc
= "Zoner BMI bitmap";
302 mi
->run_fn
= de_run_bmi
;
303 mi
->identify_fn
= de_identify_bmi
;