New "videomaster" module
[deark.git] / modules / bmi.c
blobfca065b46226a89c578dfd6bf65158d7cd9ce4f6
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);
15 struct table_item {
16 unsigned int tag_num;
17 i64 tag_offs;
20 struct imageinfo {
21 i64 w, h;
22 unsigned int palmode;
23 i64 bpp;
24 i64 num_pal_entries;
25 u32 pal[256];
28 typedef struct localctx_struct {
29 int input_encoding;
30 i64 fixed_header_size;
31 i64 num_table_items;
32 struct table_item *table;
33 struct imageinfo globalimg;
34 } lctx;
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);
40 de_dbg_indent(c, 1);
41 de_read_palette_rgb(c->infile, pos1, ii->num_pal_entries, 4,
42 ii->pal, 256, DE_GETRGBFLAG_BGR);
43 de_dbg_indent(c, -1);
46 // Read the fixed part of the header
47 static int do_header(deark *c, lctx *d, i64 pos1)
49 i64 pos = pos1;
50 int retval = 0;
52 de_dbg(c, "header at %d", (int)pos1);
53 de_dbg_indent(c, 1);
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);
71 pos += 2;
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;
77 retval = 1;
79 done:
80 if(!retval) {
81 de_err(c, "Error reading header");
83 de_dbg_indent(c, -1);
84 return retval;
87 static int do_read_table(deark *c, lctx *d, i64 pos1)
89 i64 pos = pos1;
90 i64 k;
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));
95 de_dbg_indent(c, 1);
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);
105 return 1;
108 static void do_bitmap(deark *c, lctx *d, i64 pos1)
110 int saved_indent_level;
111 i64 pos = pos1;
112 i64 unc_data_size_reported;
113 i64 unc_data_size_calc;
114 i64 max_uncmpr_block_size;
115 i64 i, j;
116 i64 rowspan;
117 de_bitmap *img = NULL;
118 dbuf *unc_pixels = NULL;
119 struct imageinfo ii;
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);
127 de_dbg_indent(c, 1);
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);
138 if(ii.palmode) {
139 pal_to_use = ii.pal;
142 if(ii.palmode && ii.bpp>=1 && ii.bpp<=8) {
143 ii.num_pal_entries = de_pow2(ii.bpp);
146 pos += 2;
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");
171 goto done;
174 unc_pixels = dbuf_create_membuf(c, unc_data_size_calc, 1);
176 while(1) {
177 i64 blen;
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);
183 de_dbg_indent(c, 1);
184 blen = de_getu16le_p(&pos);
185 de_dbg(c, "block len: %d", (int)blen);
186 pos++;
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))
197 goto done;
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);
205 pos += blen;
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++) {
212 if(ii.bpp==24) {
213 u32 clr;
214 clr = dbuf_getRGB(unc_pixels, j*rowspan+i*3, DE_GETRGBFLAG_BGR);
215 de_bitmap_setpixel_rgb(img, i, j, clr);
217 else {
218 u8 b;
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);
227 done:
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)
235 i64 k;
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;
247 i64 cmt_len;
248 i64 pos = pos1;
250 pos += 2;
251 cmt_len = de_getu32le_p(&pos);
252 pos += 2;
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));
258 ucstring_destroy(s);
261 static void do_comments(deark *c, lctx *d)
263 i64 k;
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)
274 lctx *d = NULL;
275 i64 pos = 0;
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;
290 do_comments(c, d);
291 do_bitmaps(c, d);
293 done:
294 if(d) {
295 de_free(c, d->table);
296 de_free(c, d);
300 static int de_identify_bmi(deark *c)
302 if(!dbuf_memcmp(c->infile, 0, "ZonerBMIa", 9))
303 return 100;
304 return 0;
307 void de_module_bmi(deark *c, struct deark_module_info *mi)
309 mi->id = "bmi";
310 mi->desc = "Zoner BMI bitmap";
311 mi->run_fn = de_run_bmi;
312 mi->identify_fn = de_identify_bmi;