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
;
117 de_bitmap
*img
= NULL
;
118 dbuf
*unc_pixels
= NULL
;
120 const u32
*pal_to_use
= d
->globalimg
.pal
;
122 de_zeromem(&ii
, sizeof(struct imageinfo
));
124 de_dbg_indent_save(c
, &saved_indent_level
);
126 de_dbg(c
, "bitmap at %"I64_FMT
, pos1
);
128 ii
.w
= de_getu16le_p(&pos
);
129 ii
.h
= de_getu16le_p(&pos
);
130 de_dbg_dimensions(c
, ii
.w
, ii
.h
);
132 ii
.bpp
= de_getu16le_p(&pos
);
133 de_dbg(c
, "bits/pixel: %d", (int)ii
.bpp
);
135 ii
.palmode
= (unsigned int)de_getu16le_p(&pos
);
136 de_dbg(c
, "palette mode: %u", ii
.palmode
);
142 if(ii
.palmode
&& ii
.bpp
>=1 && ii
.bpp
<=8) {
143 ii
.num_pal_entries
= de_pow2(ii
.bpp
);
148 unc_data_size_reported
= de_getu32le_p(&pos
);
149 de_dbg(c
, "uncmpr data size (reported): %"I64_FMT
, unc_data_size_reported
);
151 rowspan
= de_pad_to_n(ii
.w
*ii
.bpp
, 32)/8;
152 unc_data_size_calc
= rowspan
* ii
.h
;
153 de_dbg(c
, "uncmpr data size (calculated): %"I64_FMT
, unc_data_size_calc
);
155 if(unc_data_size_reported
>DE_MAX_SANE_OBJECT_SIZE
) goto done
;
157 max_uncmpr_block_size
= de_getu16le_p(&pos
);
158 de_dbg(c
, "max uncmpr block size: %d", (int)max_uncmpr_block_size
);
159 if(max_uncmpr_block_size
> unc_data_size_calc
) {
160 max_uncmpr_block_size
= unc_data_size_calc
;
163 if(ii
.num_pal_entries
>0) {
164 read_palette(c
, d
, &ii
, pos
);
165 pos
+= 4*ii
.num_pal_entries
;
168 if(!de_good_image_dimensions(c
, ii
.w
, ii
.h
)) goto done
;
169 if(ii
.bpp
!=24 && ii
.bpp
!=8 && ii
.bpp
!=4 && ii
.bpp
!=1) {
170 de_err(c
, "Unsupported image type");
174 unc_pixels
= dbuf_create_membuf(c
, unc_data_size_calc
, 1);
179 if(unc_pixels
->len
>= unc_data_size_reported
) break;
180 if(pos
>= c
->infile
->len
) goto done
;
182 de_dbg(c
, "block at %d", (int)pos
);
184 blen
= de_getu16le_p(&pos
);
185 de_dbg(c
, "block len: %d", (int)blen
);
187 if(pos
+blen
> c
->infile
->len
) goto done
;
188 if(blen
>max_uncmpr_block_size
) goto done
;
190 if(unc_pixels
->len
< unc_data_size_calc
) {
191 i64 len_before
= unc_pixels
->len
;
193 if(!fmtutil_decompress_deflate(c
->infile
, pos
, blen
, unc_pixels
,
194 max_uncmpr_block_size
, NULL
,
195 DE_DEFLATEFLAG_ISZLIB
|DE_DEFLATEFLAG_USEMAXUNCMPRSIZE
))
200 de_dbg(c
, "decompressed to: %"I64_FMT
" (total=%"I64_FMT
")",
201 unc_pixels
->len
- len_before
, unc_pixels
->len
);
203 de_dbg_indent(c
, -1);
208 img
= de_bitmap_create(c
, ii
.w
, ii
.h
, 3);
210 for(j
=0; j
<ii
.h
; j
++) {
211 for(i
=0; i
<ii
.w
; i
++) {
214 clr
= dbuf_getRGB(unc_pixels
, j
*rowspan
+i
*3, DE_GETRGBFLAG_BGR
);
215 de_bitmap_setpixel_rgb(img
, i
, j
, clr
);
219 b
= de_get_bits_symbol(unc_pixels
, ii
.bpp
, j
*rowspan
, i
);
220 de_bitmap_setpixel_rgb(img
, i
, j
, pal_to_use
[(unsigned int)b
]);
225 de_bitmap_write_to_file(img
, NULL
, 0);
228 de_bitmap_destroy(img
);
229 dbuf_close(unc_pixels
);
230 de_dbg_indent_restore(c
, saved_indent_level
);
233 static void do_bitmaps(deark
*c
, lctx
*d
)
237 for(k
=0; k
<d
->num_table_items
; k
++) {
238 if(d
->table
[k
].tag_num
==0x0001) {
239 do_bitmap(c
, d
, d
->table
[k
].tag_offs
);
244 static void do_comment(deark
*c
, lctx
*d
, i64 idx
, i64 pos1
)
246 de_ucstring
*s
= NULL
;
251 cmt_len
= de_getu32le_p(&pos
);
254 s
= ucstring_create(c
);
255 dbuf_read_to_ucstring_n(c
->infile
, pos
, cmt_len
, DE_DBG_MAX_STRLEN
, s
,
256 DE_CONVFLAG_STOP_AT_NUL
, d
->input_encoding
);
257 de_dbg(c
, "comment (item[%d]): \"%s\"", (int)idx
, ucstring_getpsz_d(s
));
261 static void do_comments(deark
*c
, lctx
*d
)
265 for(k
=0; k
<d
->num_table_items
; k
++) {
266 if(d
->table
[k
].tag_num
==0x0003) {
267 do_comment(c
, d
, k
, d
->table
[k
].tag_offs
);
272 static void de_run_bmi(deark
*c
, de_module_params
*mparams
)
277 d
= de_malloc(c
, sizeof(lctx
));
279 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_WINDOWS1252
);
281 if(!do_header(c
, d
, pos
)) goto done
;
282 pos
+= d
->fixed_header_size
;
284 if(d
->globalimg
.num_pal_entries
>0) {
285 read_palette(c
, d
, &d
->globalimg
, pos
);
286 pos
+= 4*d
->globalimg
.num_pal_entries
;
289 if(!do_read_table(c
, d
, pos
)) goto done
;
295 de_free(c
, d
->table
);
300 static int de_identify_bmi(deark
*c
)
302 if(!dbuf_memcmp(c
->infile
, 0, "ZonerBMIa", 9))
307 void de_module_bmi(deark
*c
, struct deark_module_info
*mi
)
310 mi
->desc
= "Zoner BMI bitmap";
311 mi
->run_fn
= de_run_bmi
;
312 mi
->identify_fn
= de_identify_bmi
;