zoo: Various improvements
[deark.git] / modules / mbk.c
blob02cde40fc1c88d1dfe6cd89af41476f7d5d015ef
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // STOS Memory Bank (MBK)
7 #include <deark-config.h>
8 #include <deark-private.h>
9 #include <deark-fmtutil.h>
10 DE_DECLARE_MODULE(de_module_mbk);
12 typedef struct localctx_struct {
13 i64 banknum;
14 u8 banktype;
15 i64 banksize;
16 u32 data_bank_id;
17 u32 pal[256];
18 } lctx;
20 static const char* sprite_res_name[3] = { "low", "med", "high" };
21 static const u8 sprite_res_bpp[3] = { 4, 2, 1 };
23 // Decode one sprite
24 static void do_sprite_param_block(deark *c, lctx *d, i64 res,
25 i64 sprite_index, i64 param_blk_pos, i64 pos)
27 i64 sprite_data_offs_raw;
28 i64 width_raw; // = width_in_pixels/16
29 i64 mask_offs;
30 i64 mask_size;
31 i64 fg_offs;
32 i64 fg_size;
33 struct atari_img_decode_data *adata_fg = NULL;
34 struct atari_img_decode_data *adata_mask = NULL;
35 de_finfo *fi = NULL;
36 u32 mask_pal[2] = { DE_STOCKCOLOR_WHITE, DE_STOCKCOLOR_BLACK };
38 de_dbg(c, "%s-res sprite #%d param block at %d", sprite_res_name[res],
39 (int)sprite_index, (int)pos);
40 de_dbg_indent(c, 1);
41 adata_fg = de_malloc(c, sizeof(struct atari_img_decode_data));
42 adata_mask = de_malloc(c, sizeof(struct atari_img_decode_data));
44 adata_fg->bpp = (i64)sprite_res_bpp[res];
45 adata_fg->ncolors = ((i64)1)<<adata_fg->bpp;
46 adata_mask->bpp = 1;
47 adata_mask->ncolors = 2;
49 sprite_data_offs_raw = de_getu32be(pos);
51 //de_dbg(c, "sprite data offset: %d (->%d)", (int)sprite_data_offs_raw, (int)mask_offs);
52 width_raw = (i64)de_getbyte(pos+4);
53 adata_fg->w = width_raw*16;
54 adata_fg->h = (i64)de_getbyte(pos+5);
55 de_dbg_dimensions(c, adata_fg->w, adata_fg->h);
56 if(!de_good_image_dimensions(c, adata_fg->w, adata_fg->h)) goto done;
58 adata_mask->w = adata_fg->w;
59 adata_mask->h = adata_fg->h;
60 mask_offs = param_blk_pos + sprite_data_offs_raw;
61 mask_size = (width_raw * 2 * 1) * adata_mask->h;
62 de_dbg(c, "mask image at %d, len=%d", (int)mask_offs, (int)mask_size);
63 if(mask_offs>=c->infile->len) goto done;
65 fg_offs = mask_offs + mask_size;
66 fg_size = (width_raw * 2 * adata_fg->bpp) * adata_fg->h;
67 de_dbg(c, "foreground image at %d, len=%d", (int)fg_offs, (int)fg_size);
69 adata_mask->unc_pixels = dbuf_open_input_subfile(c->infile, mask_offs, mask_size);
70 adata_fg->unc_pixels = dbuf_open_input_subfile(c->infile, fg_offs, fg_size);
72 adata_mask->pal = mask_pal;
73 adata_fg->pal = d->pal;
75 adata_mask->img = de_bitmap_create(c, adata_fg->w, adata_fg->h, 1);
76 adata_fg->img = de_bitmap_create(c, adata_fg->w, adata_fg->h, 4);
78 fmtutil_atari_decode_image(c, adata_mask);
79 fmtutil_atari_decode_image(c, adata_fg);
80 de_bitmap_apply_mask(adata_fg->img, adata_mask->img, 0);
81 fi = de_finfo_create(c);
82 fmtutil_atari_set_standard_density(c, adata_fg, fi);
83 de_bitmap_write_to_file_finfo(adata_fg->img, fi, 0);
85 done:
86 if(adata_fg) {
87 dbuf_close(adata_fg->unc_pixels);
88 de_bitmap_destroy(adata_fg->img);
89 de_free(c, adata_fg);
91 if(adata_mask) {
92 dbuf_close(adata_mask->unc_pixels);
93 de_bitmap_destroy(adata_mask->img);
94 de_free(c, adata_mask);
96 de_finfo_destroy(c, fi);
97 de_dbg_indent(c, -1);
100 // A block of sprites for a particular resolution
101 static void do_sprite_param_blocks(deark *c, lctx *d, i64 res,
102 i64 nsprites, i64 pos)
104 i64 k;
105 de_dbg(c, "%s-res sprite param blocks at %d", sprite_res_name[res],
106 (int)pos);
108 de_dbg_indent(c, 1);
109 for(k=0; k<nsprites; k++) {
110 do_sprite_param_block(c, d, res, k, pos, pos + 8*k);
112 de_dbg_indent(c, -1);
115 static void read_sprite_palette(deark *c, lctx *d, i64 pos)
117 i64 n;
119 if(pos>=c->infile->len) return;
121 n = de_getu32be(pos);
122 if(n!=0x50414c54) {
123 de_warn(c, "Sprite palette not found (expected at %d)", (int)pos);
124 d->pal[0] = DE_STOCKCOLOR_WHITE;
125 return;
127 de_dbg(c, "sprite palette at %d", (int)pos);
128 de_dbg_indent(c, 1);
129 fmtutil_read_atari_palette(c, c->infile, pos+4, d->pal, 16, 16, 0);
130 de_dbg_indent(c, -1);
133 static void do_sprite_bank(deark *c, lctx *d, i64 pos)
135 i64 res;
136 i64 paramoffs_raw[3]; // One for each resolution: low, med, hi
137 i64 paramoffs[3];
138 i64 nsprites[3];
139 i64 nsprites_total = 0;
140 i64 pal_pos;
142 for(res=0; res<3; res++) {
143 paramoffs_raw[res] = de_getu32be(pos+4+4*res);
144 // paramoffs is relative to the first position after the ID.
145 paramoffs[res] = pos + 4 + paramoffs_raw[res];
146 nsprites[res] = de_getu16be(pos+16+2*res);
147 de_dbg(c, "%s-res sprites: %d, param blk offset: %d ("DE_CHAR_RIGHTARROW" %d)", sprite_res_name[res],
148 (int)nsprites[res], (int)paramoffs_raw[res], (int)paramoffs[res]);
149 nsprites_total += nsprites[res];
152 // TODO: What's the right way to calculate the position of the palette?
153 pal_pos = pos + 22 + (nsprites_total)*8;
154 read_sprite_palette(c, d, pal_pos);
156 for(res=0; res<3; res++) {
157 if(nsprites[res]<1) continue;
158 if(paramoffs[res]>(c->infile->len-8)) continue;
159 do_sprite_param_blocks(c, d, res, nsprites[res], paramoffs[res]);
163 static void do_icon(deark *c, lctx *d, i64 idx, i64 pos)
165 de_bitmap *img = NULL;
166 i64 format_flag;
167 i64 bgcol, fgcol;
168 i64 i, j;
169 i64 w, h;
170 i64 rowspan;
171 u8 mskbit, fgbit;
172 i64 bitsstart;
173 u32 clr;
175 de_dbg(c, "icon #%d, at %d", (int)idx, (int)pos);
176 de_dbg_indent(c, 1);
178 format_flag = de_getu16be(pos+4);
179 de_dbg(c, "format flag: 0x%04x", (unsigned int)format_flag);
180 bgcol = de_getu16be(pos+6);
181 fgcol = de_getu16be(pos+8);
182 de_dbg(c, "bgcol: 0x%04x, fgcol: 0x%04x", (unsigned int)bgcol, (unsigned int)fgcol);
184 // TODO: I don't know how to figure out what colors to use.
185 if(fgcol==0 && bgcol!=0) {
186 d->pal[0] = DE_STOCKCOLOR_BLACK;
187 d->pal[1] = DE_STOCKCOLOR_WHITE;
189 else {
190 d->pal[0] = DE_STOCKCOLOR_WHITE;
191 d->pal[1] = DE_STOCKCOLOR_BLACK;
194 w = 16;
195 h = 16;
196 rowspan = 4;
197 img = de_bitmap_create(c, w, h, 2);
199 bitsstart = pos + 10;
200 for(j=0; j<h; j++) {
201 for(i=0; i<w; i++) {
202 mskbit = de_get_bits_symbol(c->infile, 1, bitsstart + j*rowspan, i);
203 fgbit = de_get_bits_symbol(c->infile, 1, bitsstart + j*rowspan + 2, i);
204 clr = d->pal[(unsigned int)fgbit];
205 if(!mskbit) {
206 clr = DE_SET_ALPHA(clr, 0);
208 de_bitmap_setpixel_rgba(img, i, j, clr);
212 de_bitmap_write_to_file(img, NULL, 0);
213 de_bitmap_destroy(img);
214 de_dbg_indent(c, -1);
217 static void do_icon_bank(deark *c, lctx *d, i64 pos)
219 i64 num_icons;
220 i64 k;
222 num_icons = de_getu16be(pos+4);
223 de_dbg(c, "number of icons: %d", (int)num_icons);
224 for(k=0; k<num_icons; k++) {
225 do_icon(c, d, k, pos+6+84*k);
229 static void do_mbk_data_bank(deark *c, lctx *d, i64 pos)
231 const char *bn = "?";
233 de_dbg(c, "STOS data bank at %d", (int)pos);
234 de_dbg_indent(c, 1);
235 d->data_bank_id = (u32)de_getu32be(pos);
237 switch(d->data_bank_id) {
238 case 0x06071963U: bn = "packed screen"; break;
239 case 0x13490157U: bn = "music bank"; break;
240 case 0x28091960U: bn = "icon bank"; break;
241 case 0x19861987U: bn = "sprite bank"; break;
242 case 0x4d414553U: bn = "Maestro!"; break;
245 de_dbg(c, "data bank id: 0x%08x (%s)", (unsigned int)d->data_bank_id, bn);
247 switch(d->data_bank_id) {
248 case 0x19861987U:
249 do_sprite_bank(c, d, pos);
250 break;
251 case 0x28091960U:
252 do_icon_bank(c, d, pos);
253 break;
255 de_dbg_indent(c, -1);
258 static void do_mbk(deark *c, lctx *d)
260 i64 pos = 0;
261 const char *bt = "?";
263 de_dbg(c, "MBK header at %d", (int)pos);
264 de_dbg_indent(c, 1);
266 de_dbg(c, "bank number: %d", (int)d->banknum);
268 d->banksize = de_getu32be(14);
269 d->banktype = (u8)(d->banksize>>24);
270 d->banksize &= (i64)0x00ffffff;
272 switch(d->banktype) {
273 case 0x01: bt = "work"; break;
274 case 0x02: bt = "screen"; break;
275 case 0x81: bt = "data"; break;
276 case 0x82: bt = "datascreen"; break;
277 case 0x84: bt = "set"; break;
278 case 0x85: bt = "packed files"; break;
281 de_dbg(c, "bank type: 0x%02x (%s)", (unsigned int)d->banktype, bt);
282 de_dbg(c, "bank size: %d", (int)d->banksize);
284 de_dbg_indent(c, -1);
286 pos += 18;
288 if(d->banktype==0x81) {
289 do_mbk_data_bank(c, d, pos);
293 static void do_mbs(deark *c, lctx *d)
295 i64 pos = 0;
296 de_dbg(c, "MBS header at %d", (int)pos);
299 static void de_run_mbk_mbs(deark *c, de_module_params *mparams)
301 lctx *d = NULL;
302 u32 id;
303 u8 buf[10];
305 d = de_malloc(c, sizeof(lctx));
307 de_read(buf, 0, sizeof(buf));
308 if(!de_memcmp(buf, "Lionpoubnk", 10)) {
309 d->banknum = de_getu32be(10);
310 if(d->banknum==0) {
311 de_declare_fmt(c, "STOS MBS");
312 do_mbs(c, d);
314 else {
315 de_declare_fmt(c, "STOS MBK");
316 do_mbk(c, d);
319 else {
320 id = (u32)de_getu32be_direct(buf);
322 if(id==0x19861987U) {
323 de_declare_fmt(c, "STOS Sprite Bank");
324 do_sprite_bank(c, d, 0);
326 else {
327 de_err(c, "Not a (supported) STOS/MBK format");
331 de_free(c, d);
334 static int de_identify_mbk(deark *c)
336 u8 buf[10];
338 de_read(buf, 0, sizeof(buf));
339 if(!de_memcmp(buf, "Lionpoubnk", 10))
340 return 100;
341 if(!de_memcmp(buf, "\x19\x86\x19\x87", 4)) { // Sprite bank
342 return 100;
344 return 0;
347 static void de_help_mbk(deark *c)
349 fmtutil_atari_help_palbits(c);
352 void de_module_mbk(deark *c, struct deark_module_info *mi)
354 mi->id = "stos";
355 mi->desc = "STOS Memory Bank (.MBK)";
356 mi->run_fn = de_run_mbk_mbs;
357 mi->identify_fn = de_identify_mbk;
358 mi->help_fn = de_help_mbk;