exe: Support PAK v1.6 self-extracting archives
[deark.git] / modules / mbk.c
blob4b2258b1a1a0355d7487180c11ffedf5a87c26ab
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);
11 DE_DECLARE_MODULE(de_module_stos_pp1);
12 DE_DECLARE_MODULE(de_module_stos_pp2);
13 DE_DECLARE_MODULE(de_module_stos_pp3);
14 DE_DECLARE_MODULE(de_module_stos_daj);
16 #define BANKID_PKSCREEN 0x06071963U
17 #define BANKID_SPRITE 0x19861987U
19 #define PKPIC_IMGTYPE_UNKNOWN 99
20 #define PKPIC_IMGTYPE_LOW 100
21 #define PKPIC_IMGTYPE_MED 101
22 #define PKPIC_IMGTYPE_HIGH 102
23 #define PKPIC_IMGTYPE_PP1 110
24 #define PKPIC_IMGTYPE_PP3 112
25 #define PKPIC_IMGTYPE_M4P 120
27 static const u8 *g_lion_sig = (const u8*)"Lionpoubnk";
29 struct pkpic_ctx {
30 UI res_code;
31 UI imgtype; // PKPIC_IMGTYPE_*
32 i64 width_in_words;
33 i64 height_in_lumps;
34 i64 lines_per_lump;
35 i64 pseudowidth, pseudoheight;
36 i64 w, h;
37 i64 picdata_rel, picdata_abs;
38 i64 rledata_rel, rledata_abs;
39 i64 pointdata_rel, pointdata_abs;
40 i64 unc_image_size;
43 typedef struct localctx_struct {
44 u8 is_container_fmt;
45 UI imgtype_of_res0; // PKPIC_IMGTYPE_*
46 UI imgtype_of_res1;
47 i64 banknum;
48 u8 banktype;
49 i64 banksize;
50 u32 data_bank_id;
51 u32 pal[256];
52 } lctx;
54 static const char* sprite_res_name[3] = { "low", "med", "high" };
55 static const u8 sprite_res_bpp[3] = { 4, 2, 1 };
57 // Decode one sprite
58 static void do_sprite_param_block(deark *c, lctx *d, i64 res,
59 i64 sprite_index, i64 param_blk_pos, i64 pos)
61 i64 sprite_data_offs_raw;
62 i64 width_raw; // = width_in_pixels/16
63 i64 mask_offs;
64 i64 mask_size;
65 i64 fg_offs;
66 i64 fg_size;
67 struct atari_img_decode_data *adata_fg = NULL;
68 struct atari_img_decode_data *adata_mask = NULL;
69 de_finfo *fi = NULL;
70 u32 mask_pal[2] = { DE_STOCKCOLOR_WHITE, DE_STOCKCOLOR_BLACK };
72 de_dbg(c, "%s-res sprite #%d param block at %d", sprite_res_name[res],
73 (int)sprite_index, (int)pos);
74 de_dbg_indent(c, 1);
75 adata_fg = de_malloc(c, sizeof(struct atari_img_decode_data));
76 adata_mask = de_malloc(c, sizeof(struct atari_img_decode_data));
78 adata_fg->bpp = (i64)sprite_res_bpp[res];
79 adata_fg->ncolors = ((i64)1)<<adata_fg->bpp;
80 adata_mask->bpp = 1;
81 adata_mask->ncolors = 2;
83 sprite_data_offs_raw = de_getu32be(pos);
85 //de_dbg(c, "sprite data offset: %d (->%d)", (int)sprite_data_offs_raw, (int)mask_offs);
86 width_raw = (i64)de_getbyte(pos+4);
87 adata_fg->w = width_raw*16;
88 adata_fg->h = (i64)de_getbyte(pos+5);
89 de_dbg_dimensions(c, adata_fg->w, adata_fg->h);
90 if(!de_good_image_dimensions(c, adata_fg->w, adata_fg->h)) goto done;
92 adata_mask->w = adata_fg->w;
93 adata_mask->h = adata_fg->h;
94 mask_offs = param_blk_pos + sprite_data_offs_raw;
95 mask_size = (width_raw * 2 * 1) * adata_mask->h;
96 de_dbg(c, "mask image at %d, len=%d", (int)mask_offs, (int)mask_size);
97 if(mask_offs>=c->infile->len) goto done;
99 fg_offs = mask_offs + mask_size;
100 fg_size = (width_raw * 2 * adata_fg->bpp) * adata_fg->h;
101 de_dbg(c, "foreground image at %d, len=%d", (int)fg_offs, (int)fg_size);
103 adata_mask->unc_pixels = dbuf_open_input_subfile(c->infile, mask_offs, mask_size);
104 adata_fg->unc_pixels = dbuf_open_input_subfile(c->infile, fg_offs, fg_size);
106 adata_mask->pal = mask_pal;
107 adata_fg->pal = d->pal;
109 adata_mask->img = de_bitmap_create(c, adata_fg->w, adata_fg->h, 1);
110 adata_fg->img = de_bitmap_create(c, adata_fg->w, adata_fg->h, 4);
112 fmtutil_atari_decode_image(c, adata_mask);
113 fmtutil_atari_decode_image(c, adata_fg);
114 de_bitmap_apply_mask(adata_fg->img, adata_mask->img, 0);
115 fi = de_finfo_create(c);
116 fmtutil_atari_set_standard_density(c, adata_fg, fi);
117 de_bitmap_write_to_file_finfo(adata_fg->img, fi, 0);
119 done:
120 if(adata_fg) {
121 dbuf_close(adata_fg->unc_pixels);
122 de_bitmap_destroy(adata_fg->img);
123 de_free(c, adata_fg);
125 if(adata_mask) {
126 dbuf_close(adata_mask->unc_pixels);
127 de_bitmap_destroy(adata_mask->img);
128 de_free(c, adata_mask);
130 de_finfo_destroy(c, fi);
131 de_dbg_indent(c, -1);
134 // A block of sprites for a particular resolution
135 static void do_sprite_param_blocks(deark *c, lctx *d, i64 res,
136 i64 nsprites, i64 pos)
138 i64 k;
139 de_dbg(c, "%s-res sprite param blocks at %d", sprite_res_name[res],
140 (int)pos);
142 de_dbg_indent(c, 1);
143 for(k=0; k<nsprites; k++) {
144 do_sprite_param_block(c, d, res, k, pos, pos + 8*k);
146 de_dbg_indent(c, -1);
149 static void read_sprite_palette(deark *c, lctx *d, i64 pos)
151 i64 n;
153 if(pos>=c->infile->len) return;
155 n = de_getu32be(pos);
156 if(n!=0x50414c54) {
157 de_warn(c, "Sprite palette not found (expected at %d)", (int)pos);
158 d->pal[0] = DE_STOCKCOLOR_WHITE;
159 return;
161 de_dbg(c, "sprite palette at %d", (int)pos);
162 de_dbg_indent(c, 1);
163 fmtutil_read_atari_palette(c, c->infile, pos+4, d->pal, 16, 16, 0);
164 de_dbg_indent(c, -1);
167 static void do_sprite_bank(deark *c, lctx *d, i64 pos)
169 i64 res;
170 i64 paramoffs_raw[3]; // One for each resolution: low, med, hi
171 i64 paramoffs[3];
172 i64 nsprites[3];
173 i64 nsprites_total = 0;
174 i64 pal_pos;
176 for(res=0; res<3; res++) {
177 paramoffs_raw[res] = de_getu32be(pos+4+4*res);
178 // paramoffs is relative to the first position after the ID.
179 paramoffs[res] = pos + 4 + paramoffs_raw[res];
180 nsprites[res] = de_getu16be(pos+16+2*res);
181 de_dbg(c, "%s-res sprites: %d, param blk offset: %d ("DE_CHAR_RIGHTARROW" %d)", sprite_res_name[res],
182 (int)nsprites[res], (int)paramoffs_raw[res], (int)paramoffs[res]);
183 nsprites_total += nsprites[res];
186 // TODO: What's the right way to calculate the position of the palette?
187 pal_pos = pos + 22 + (nsprites_total)*8;
188 read_sprite_palette(c, d, pal_pos);
190 for(res=0; res<3; res++) {
191 if(nsprites[res]<1) continue;
192 if(paramoffs[res]>(c->infile->len-8)) continue;
193 do_sprite_param_blocks(c, d, res, nsprites[res], paramoffs[res]);
197 static void do_icon(deark *c, lctx *d, i64 idx, i64 pos)
199 de_bitmap *fgimg = NULL;
200 de_bitmap *maskimg = NULL;
201 UI cvtflags = 0;
202 i64 format_flag;
203 i64 bgcol, fgcol;
204 i64 w, h;
205 i64 rowspan;
206 i64 bitsstart;
208 de_dbg(c, "icon #%d, at %d", (int)idx, (int)pos);
209 de_dbg_indent(c, 1);
211 format_flag = de_getu16be(pos+4);
212 de_dbg(c, "format flag: 0x%04x", (unsigned int)format_flag);
213 bgcol = de_getu16be(pos+6);
214 fgcol = de_getu16be(pos+8);
215 de_dbg(c, "bgcol: 0x%04x, fgcol: 0x%04x", (unsigned int)bgcol, (unsigned int)fgcol);
217 // TODO: I don't know how to figure out what colors to use.
218 if(fgcol==0 && bgcol!=0) {
221 else {
222 cvtflags |= DE_CVTF_WHITEISZERO;
225 w = 16;
226 h = 16;
227 rowspan = 4;
228 fgimg = de_bitmap_create(c, w, h, 2);
229 maskimg = de_bitmap_create(c, w, h, 1);
231 bitsstart = pos + 10;
233 de_convert_image_bilevel(c->infile, bitsstart, rowspan, maskimg, 0);
234 de_convert_image_bilevel(c->infile, bitsstart+2, rowspan, fgimg, cvtflags);
235 de_bitmap_apply_mask(fgimg, maskimg, 0);
237 de_bitmap_write_to_file(fgimg, NULL, DE_CREATEFLAG_OPT_IMAGE);
238 de_bitmap_destroy(fgimg);
239 de_bitmap_destroy(maskimg);
240 de_dbg_indent(c, -1);
243 struct pictbank_params {
244 u8 ok;
245 UI num_planes;
246 UI bits_per_pixel;
247 i64 width_in_bytes;
248 i64 height_in_lumps;
249 i64 lines_per_lump;
250 i64 pseudoheight;
251 dbuf *unc_pixels;
252 de_bitmap *img;
253 de_color *pal;
256 static void do_icon_bank(deark *c, lctx *d, i64 pos)
258 i64 num_icons;
259 i64 k;
261 num_icons = de_getu16be(pos+4);
262 de_dbg(c, "number of icons: %d", (int)num_icons);
263 for(k=0; k<num_icons; k++) {
264 do_icon(c, d, k, pos+6+84*k);
268 static void render_stos_pp3(deark *c, struct pictbank_params *pb, i64 width_in_words)
270 i64 planesize;
271 i64 lump;
273 planesize = pb->width_in_bytes * pb->pseudoheight;
275 for(lump=0; lump<pb->height_in_lumps; lump++) {
276 i64 col_idx;
277 i64 lump_start_srcpos_in_plane;
278 i64 lump_start_ypos;
280 lump_start_srcpos_in_plane = pb->width_in_bytes * pb->lines_per_lump * lump;
281 lump_start_ypos = pb->lines_per_lump * lump * 2;
283 // col_idx=0 = the first 32 pixels of the even numbered rows
284 // col_idx=width_in_words = the first 32 pixels of the odd numbered rows
285 // (TODO: What happens if width_in_words is odd?)
286 for(col_idx=0; col_idx<pb->width_in_bytes; col_idx++) {
287 i64 col_start_srcpos_in_plane;
288 i64 ypos_in_lump;
289 UI rowparity;
291 if(col_idx & 1) {
292 continue; // We process 2 columns at a time
295 if(col_idx >= width_in_words) {
296 rowparity = 1;
298 else {
299 rowparity = 0;
302 col_start_srcpos_in_plane = lump_start_srcpos_in_plane + col_idx*pb->lines_per_lump;
304 for(ypos_in_lump=0; ypos_in_lump<pb->lines_per_lump; ypos_in_lump++) {
305 UI i;
306 UI n;
307 i64 xpos, ypos;
309 ypos = lump_start_ypos + ypos_in_lump*2 + rowparity;
311 if(rowparity) {
312 xpos = (col_idx % width_in_words)*16;
314 else {
315 xpos = col_idx*16;
318 // n=0: the first 8 pixels of every 32 plane 0 lump+0
319 // n=1: the second 8 pixels of every 32 plane 0 lump+1
320 // n=2: the third 8 pixels of every 32 plane 1 lump+0
321 // n=3: the fourth 8 pixels of every 32 plane 1 lump+1
322 for(n=0; n<4; n++) {
323 u8 v;
325 v = dbuf_getbyte(pb->unc_pixels, planesize*(n>>1) +
326 col_start_srcpos_in_plane + pb->lines_per_lump*(n&1) + ypos_in_lump);
328 for(i=0; i<8; i++) {
329 UI palent;
331 palent = (v>>(7-i)) & 1;
332 de_bitmap_setpixel_rgb(pb->img, xpos, ypos, pb->pal[palent]);
333 xpos++;
341 static void render_stos_pp1(deark *c, struct pictbank_params *pb)
343 i64 planesize;
344 i64 lump;
345 UI num_planes = 2;
346 UI bits_per_pixel = 4;
347 u8 xbuf[4];
349 planesize = pb->width_in_bytes * pb->pseudoheight;
351 for(lump=0; lump<pb->height_in_lumps; lump++) {
352 i64 col_idx;
353 i64 lump_start_srcpos_in_plane;
354 i64 lump_start_ypos;
355 i64 num_skipped_cols = 0;
357 lump_start_srcpos_in_plane = pb->width_in_bytes * pb->lines_per_lump * lump;
358 lump_start_ypos = pb->lines_per_lump * lump;
360 for(col_idx=0; col_idx<pb->width_in_bytes; col_idx++) {
361 i64 col_start_srcpos_in_plane;
362 i64 ypos_in_lump;
364 // Skip the last 2 of every group of 4 columns. They contain bits
365 // associated with the first 2 columns.
366 if((col_idx&2)!=0) {
367 num_skipped_cols++;
368 continue;
371 col_start_srcpos_in_plane = lump_start_srcpos_in_plane +
372 pb->lines_per_lump*col_idx;
374 for(ypos_in_lump=0; ypos_in_lump<pb->lines_per_lump; ypos_in_lump++) {
375 UI i;
376 UI pn;
377 i64 xpos, ypos;
379 ypos = lump_start_ypos + ypos_in_lump;
381 for(pn=0; pn<num_planes; pn++) {
382 xbuf[pn] = dbuf_getbyte(pb->unc_pixels, planesize*pn +
383 col_start_srcpos_in_plane + ypos_in_lump);
384 xbuf[pn+2] = dbuf_getbyte(pb->unc_pixels, planesize*pn +
385 col_start_srcpos_in_plane + 2*pb->lines_per_lump + ypos_in_lump);
388 for(i=0; i<8; i++) {
389 UI palent;
391 palent = 0;
392 for(pn=0; pn<bits_per_pixel; pn++) {
393 if(xbuf[pn] & (1<<(7-i))) {
394 palent |= (1<<pn);
398 xpos = (col_idx-num_skipped_cols)*8 + i;
399 de_bitmap_setpixel_rgb(pb->img, xpos, ypos, pb->pal[palent]);
406 static void render_stos_med4plane(deark *c, struct pictbank_params *pb)
408 i64 planesize;
409 i64 lump;
410 UI num_planes = 4;
411 UI bits_per_pixel = 2;
412 u8 xbuf[4];
414 //width_in_bytes = pb->width_in_words*2;
415 planesize = pb->width_in_bytes * pb->pseudoheight;
417 for(lump=0; lump<pb->height_in_lumps; lump++) {
418 i64 col_idx;
419 i64 lump_start_srcpos_in_plane;
420 i64 lump_start_ypos;
422 lump_start_srcpos_in_plane = pb->width_in_bytes * pb->lines_per_lump * lump;
423 lump_start_ypos = pb->lines_per_lump * lump;
425 for(col_idx=0; col_idx<pb->width_in_bytes; col_idx++) {
426 i64 col_start_srcpos_in_plane;
427 i64 ypos_in_lump;
429 // Each column that we process sets 16 pixels:
430 // 8 pixels are set, then 8 pixels skipped, then 8 pixels set.
432 col_start_srcpos_in_plane = lump_start_srcpos_in_plane +
433 pb->lines_per_lump*col_idx;
435 for(ypos_in_lump=0; ypos_in_lump<pb->lines_per_lump; ypos_in_lump++) {
436 UI i;
437 UI pn;
438 i64 xpos1, ypos;
440 ypos = lump_start_ypos + ypos_in_lump;
442 // xbuf[0..1] are for the first set of 8 pixels.
443 // xbuf[2..3] are for the second set.
444 for(pn=0; pn<num_planes; pn++) {
445 xbuf[pn] = dbuf_getbyte(pb->unc_pixels, planesize*pn +
446 col_start_srcpos_in_plane + ypos_in_lump);
449 xpos1 = col_idx*16;
450 if(col_idx%2) xpos1 -= 8;
452 for(i=0; i<8; i++) {
453 i64 xpos;
454 UI palent;
455 UI pixset;
457 for(pixset=0; pixset<2; pixset++) {
458 palent = 0;
459 for(pn=0; pn<bits_per_pixel; pn++) {
460 if(xbuf[pixset*2+pn] & (1<<(7-i))) {
461 palent |= (1<<pn);
464 xpos = xpos1 + pixset*16 + i;
465 de_bitmap_setpixel_rgb(pb->img, xpos, ypos, pb->pal[palent]);
473 // TODO: Consolidate this with the similar function in abk.c.
474 static void render_stos_pictbank_std(deark *c, struct pictbank_params *pb)
476 i64 planesize;
477 i64 lump;
478 u8 xbuf[8];
480 if((size_t)pb->num_planes > sizeof(xbuf)) goto done;
481 if(pb->bits_per_pixel != pb->num_planes) goto done;
482 de_zeromem(xbuf, sizeof(xbuf));
483 planesize = pb->width_in_bytes * pb->pseudoheight;
485 for(lump=0; lump<pb->height_in_lumps; lump++) {
486 i64 col_idx;
487 i64 lump_start_srcpos_in_plane;
488 i64 lump_start_ypos;
490 lump_start_srcpos_in_plane = pb->width_in_bytes * pb->lines_per_lump * lump;
491 lump_start_ypos = pb->lines_per_lump * lump;
493 for(col_idx=0; col_idx<pb->width_in_bytes; col_idx++) {
494 i64 col_start_srcpos_in_plane;
495 i64 ypos_in_lump;
497 col_start_srcpos_in_plane = lump_start_srcpos_in_plane +
498 pb->lines_per_lump*col_idx;
500 for(ypos_in_lump=0; ypos_in_lump<pb->lines_per_lump; ypos_in_lump++) {
501 UI i;
502 UI pn;
503 i64 xpos, ypos;
505 ypos = lump_start_ypos + ypos_in_lump;
507 for(pn=0; pn<pb->num_planes; pn++) {
508 xbuf[pn] = dbuf_getbyte(pb->unc_pixels, planesize*pn +
509 col_start_srcpos_in_plane + ypos_in_lump);
512 for(i=0; i<8; i++) {
513 UI palent;
515 palent = 0;
516 for(pn=0; pn<pb->bits_per_pixel; pn++) {
517 if(xbuf[pn] & (1<<(7-i))) {
518 palent |= (1<<pn);
522 xpos = col_idx*8 + i;
523 de_bitmap_setpixel_rgb(pb->img, xpos, ypos, pb->pal[palent]);
529 pb->ok = 1;
531 done:
535 static void render_stos_pictbank1(deark *c, lctx *d, struct pkpic_ctx *pp,
536 dbuf *unc_pixels, de_bitmap *img, UI num_planes)
538 struct pictbank_params *pb;
540 pb = de_malloc(c, sizeof(struct pictbank_params));
541 pb->num_planes = num_planes;
542 pb->bits_per_pixel = pb->num_planes;
543 pb->width_in_bytes = pp->width_in_words * 2;
544 pb->height_in_lumps = pp->height_in_lumps;
545 pb->lines_per_lump = pp->lines_per_lump;
546 pb->pseudoheight = pp->pseudoheight;
547 pb->unc_pixels = unc_pixels;
548 pb->img = img;
549 pb->pal = d->pal;
551 if(pp->imgtype==PKPIC_IMGTYPE_PP1) {
552 render_stos_pp1(c, pb);
554 else if(pp->imgtype==PKPIC_IMGTYPE_PP3) {
555 render_stos_pp3(c, pb, pp->width_in_words);
557 else if(pp->imgtype==PKPIC_IMGTYPE_M4P) {
558 render_stos_med4plane(c, pb);
560 else {
561 render_stos_pictbank_std(c, pb);
564 de_free(c, pb);
567 static void do_pkscreen_bank(deark *c, lctx *d, i64 pos1)
569 struct pkpic_ctx *pp = NULL;
570 dbuf *unc_pixels = NULL;
571 de_bitmap *img = NULL;
572 struct atari_img_decode_data *adata = NULL;
573 de_finfo *fi = NULL;
574 const char *tname = NULL;
575 i64 pos;
576 UI num_planes;
577 UI bits_per_pixel;
578 int saved_indent_level;
580 de_dbg_indent_save(c, &saved_indent_level);
581 pp = de_malloc(c, sizeof(struct pkpic_ctx));
582 de_zeromem(d->pal, sizeof(d->pal));
584 pos = pos1+4;
585 pp->res_code = (UI)de_getu16be_p(&pos);
586 de_dbg(c, "res: %u", pp->res_code);
588 switch(pp->res_code) {
589 case 0:
590 if(d->imgtype_of_res0==PKPIC_IMGTYPE_UNKNOWN) {
591 pp->imgtype = PKPIC_IMGTYPE_LOW;
593 else {
594 pp->imgtype = d->imgtype_of_res0;
596 break;
597 case 1:
598 if(d->imgtype_of_res1==PKPIC_IMGTYPE_UNKNOWN) {
599 const char *tmps;
601 if(d->is_container_fmt) {
602 pp->imgtype = PKPIC_IMGTYPE_MED;
603 tmps = "l|h";
605 else {
606 // Unfortunately, a file with res=1 (that couldn't be identified by
607 // its extension) seems to be more likely to be the oddball PP1
608 // format than the nice standard STOS medium-resolution format.
609 pp->imgtype = PKPIC_IMGTYPE_PP1;
610 tmps = "m|h";
612 de_warn(c, "Ambiguous image. If it looks wrong, try \"-opt stos:res1=<%s>\".", tmps);
614 else {
615 pp->imgtype = d->imgtype_of_res1;
617 break;
618 case 2: pp->imgtype = PKPIC_IMGTYPE_HIGH; break;
619 default: pp->imgtype = PKPIC_IMGTYPE_UNKNOWN; break;
622 switch(pp->imgtype) {
623 case PKPIC_IMGTYPE_LOW:
624 num_planes = 4;
625 bits_per_pixel = 4;
626 tname = "4-plane low res";
627 break;
628 case PKPIC_IMGTYPE_M4P:
629 num_planes = 4;
630 bits_per_pixel = 2;
631 tname = "DAJ 4-plane med res";
632 break;
633 case PKPIC_IMGTYPE_MED:
634 num_planes = 2;
635 bits_per_pixel = 2;
636 tname = "2-plane med res";
637 break;
638 case PKPIC_IMGTYPE_HIGH:
639 num_planes = 1;
640 bits_per_pixel = 1;
641 tname = "1-plane high res";
642 break;
643 case PKPIC_IMGTYPE_PP1:
644 num_planes = 2;
645 bits_per_pixel = 4;
646 tname = "PP1 2-plane low res";
647 break;
648 case PKPIC_IMGTYPE_PP3:
649 num_planes = 2;
650 bits_per_pixel = 1;
651 tname = "PP3 2-plane high res";
652 break;
653 default:
654 de_err(c, "Unsupported picture resolution: %u", pp->res_code);
655 goto done;
657 if(tname) {
658 de_dbg(c, "interpreted image type: %s", tname);
661 pos += 4;
662 pp->width_in_words = de_getu16be_p(&pos);
663 pp->pseudowidth = pp->width_in_words * 16;
664 if(pp->imgtype==PKPIC_IMGTYPE_PP1) {
665 pp->w = pp->width_in_words * 8;
667 else if(pp->imgtype==PKPIC_IMGTYPE_M4P) {
668 pp->w = pp->width_in_words * 32;
670 else {
671 pp->w = pp->pseudowidth;
673 de_dbg(c, "width in words: %"I64_FMT, pp->width_in_words);
674 pp->height_in_lumps = de_getu16be_p(&pos);
675 de_dbg(c, "height in lumps: %"I64_FMT, pp->height_in_lumps);
676 pos += 2; // unknown
677 pp->lines_per_lump = de_getu16be_p(&pos);
678 de_dbg(c, "lines per lump: %"I64_FMT, pp->lines_per_lump);
679 pp->pseudoheight = pp->height_in_lumps * pp->lines_per_lump;
680 if(pp->imgtype==PKPIC_IMGTYPE_PP3) {
681 pp->h = pp->pseudoheight * 2;
683 else {
684 pp->h = pp->pseudoheight;
686 de_dbg_dimensions(c, pp->w, pp->h);
687 pos += 2; // flags
688 pp->picdata_rel = 70;
689 pp->rledata_rel = de_getu32be_p(&pos);
690 pp->pointdata_rel = de_getu32be_p(&pos);
691 pp->picdata_abs = pos1 + pp->picdata_rel;
692 pp->rledata_abs = pos1 + pp->rledata_rel;
693 pp->pointdata_abs = pos1 + pp->pointdata_rel;
694 de_dbg(c, "picdata: %"I64_FMT, pp->picdata_abs);
695 de_dbg(c, "rledata: %"I64_FMT, pp->rledata_abs);
696 de_dbg(c, "pointdata: %"I64_FMT, pp->pointdata_abs);
698 pos = pos1+38;
699 de_dbg(c, "palette at %"I64_FMT, pos);
700 de_dbg_indent(c, 1);
701 fmtutil_read_atari_palette(c, c->infile, pos, d->pal, 16, 16, 0);
702 de_dbg_indent(c, -1);
704 if(bits_per_pixel==1) {
705 if((d->pal[0] & 0xffffff)==0) {
706 d->pal[0] = DE_STOCKCOLOR_BLACK;
707 d->pal[1] = DE_STOCKCOLOR_WHITE;
709 else {
710 d->pal[0] = DE_STOCKCOLOR_WHITE;
711 d->pal[1] = DE_STOCKCOLOR_BLACK;
715 pp->unc_image_size = pp->width_in_words*2 * (i64)num_planes * pp->h;
717 unc_pixels = dbuf_create_membuf(c, 0, 0);
719 fmtutil_decompress_stos_pictbank(c, c->infile, pp->picdata_abs, pp->rledata_abs,
720 pp->pointdata_abs, unc_pixels, pp->unc_image_size);
722 img = de_bitmap_create(c, pp->w, pp->h, ((bits_per_pixel==1)?1:3));
723 render_stos_pictbank1(c, d, pp, unc_pixels, img, num_planes);
725 fi = de_finfo_create(c);
726 adata = de_malloc(c, sizeof(struct atari_img_decode_data));
727 adata->bpp = (i64)bits_per_pixel;
728 fmtutil_atari_set_standard_density(c, adata, fi);
729 de_bitmap_write_to_file_finfo(img, fi, DE_CREATEFLAG_OPT_IMAGE);
731 done:
732 de_bitmap_destroy(img);
733 dbuf_close(unc_pixels);
734 if(pp) {
735 de_free(c, pp);
737 de_free(c, adata);
738 de_finfo_destroy(c, fi);
739 de_dbg_indent_restore(c, saved_indent_level);
742 static void do_mbk_data_bank(deark *c, lctx *d, i64 pos)
744 const char *bn = "?";
746 de_dbg(c, "STOS data bank at %d", (int)pos);
747 de_dbg_indent(c, 1);
748 d->data_bank_id = (u32)de_getu32be(pos);
750 switch(d->data_bank_id) {
751 case BANKID_PKSCREEN: bn = "packed screen"; break;
752 case 0x13490157U: bn = "music bank"; break;
753 case 0x28091960U: bn = "icon bank"; break;
754 case BANKID_SPRITE: bn = "sprite bank"; break;
755 case 0x4d414553U: bn = "Maestro!"; break;
758 de_dbg(c, "data bank id: 0x%08x (%s)", (unsigned int)d->data_bank_id, bn);
760 switch(d->data_bank_id) {
761 case BANKID_SPRITE:
762 do_sprite_bank(c, d, pos);
763 break;
764 case 0x28091960U:
765 do_icon_bank(c, d, pos);
766 break;
767 case BANKID_PKSCREEN:
768 do_pkscreen_bank(c, d, pos);
769 break;
771 de_dbg_indent(c, -1);
774 static void do_mbk(deark *c, lctx *d)
776 i64 pos = 0;
777 const char *bt = "?";
779 de_dbg(c, "MBK header at %d", (int)pos);
780 de_dbg_indent(c, 1);
782 de_dbg(c, "bank number: %d", (int)d->banknum);
784 d->banksize = de_getu32be(14);
785 d->banktype = (u8)(d->banksize>>24);
786 d->banksize &= (i64)0x00ffffff;
788 switch(d->banktype) {
789 case 0x01: bt = "work"; break;
790 case 0x02: bt = "screen"; break;
791 case 0x81: bt = "data"; break;
792 case 0x82: bt = "datascreen"; break;
793 case 0x84: bt = "set"; break;
794 case 0x85: bt = "packed files"; break;
797 de_dbg(c, "bank type: 0x%02x (%s)", (unsigned int)d->banktype, bt);
798 de_dbg(c, "bank size: %d", (int)d->banksize);
800 de_dbg_indent(c, -1);
802 pos += 18;
804 if(d->banktype==0x81) {
805 do_mbk_data_bank(c, d, pos);
809 static void do_mbs(deark *c, lctx *d)
811 i64 pos = 0;
812 de_dbg(c, "MBS header at %d", (int)pos);
815 static void run_mbk_mbs_internal(deark *c, de_module_params *mparams, UI mode)
817 lctx *d = NULL;
818 u32 id;
819 const char *s;
820 char opt_res0_c = '\0';
821 char opt_res1_c = '\0';
822 u8 buf[10];
824 d = de_malloc(c, sizeof(lctx));
826 d->imgtype_of_res1 = PKPIC_IMGTYPE_UNKNOWN;
827 d->imgtype_of_res0 = PKPIC_IMGTYPE_UNKNOWN;
829 if(mode==1) { // .pp1 extension
830 d->imgtype_of_res1 = PKPIC_IMGTYPE_PP1;
832 else if(mode==2) {
833 d->imgtype_of_res1 = PKPIC_IMGTYPE_MED;
835 else if(mode==3) {
836 d->imgtype_of_res1 = PKPIC_IMGTYPE_PP3;
838 else if(mode==4) { // .daj extension
839 d->imgtype_of_res0 = PKPIC_IMGTYPE_M4P;
840 d->imgtype_of_res1 = PKPIC_IMGTYPE_MED;
843 s = de_get_ext_option(c, "stos:res0");
844 if(s) opt_res0_c = s[0];
845 s = de_get_ext_option(c, "stos:res1");
846 if(s) opt_res1_c = s[0];
848 if(opt_res1_c=='l') {
849 d->imgtype_of_res1 = PKPIC_IMGTYPE_PP1;
851 else if(opt_res1_c=='m') {
852 d->imgtype_of_res1 = PKPIC_IMGTYPE_MED;
854 else if(opt_res1_c=='h') {
855 d->imgtype_of_res1 = PKPIC_IMGTYPE_PP3;
858 if(opt_res0_c=='l') {
859 d->imgtype_of_res0 = PKPIC_IMGTYPE_LOW;
861 else if(opt_res0_c=='m') {
862 d->imgtype_of_res0 = PKPIC_IMGTYPE_M4P;
865 de_read(buf, 0, sizeof(buf));
866 if(!de_memcmp(buf, g_lion_sig, 10)) {
867 d->is_container_fmt = 1;
868 d->banknum = de_getu32be(10);
869 if(d->banknum==0) {
870 de_declare_fmt(c, "STOS MBS");
871 do_mbs(c, d);
873 else {
874 de_declare_fmt(c, "STOS MBK");
875 do_mbk(c, d);
878 else {
879 id = (u32)de_getu32be_direct(buf);
881 if(id==BANKID_SPRITE) {
882 de_declare_fmt(c, "STOS Sprite Bank");
883 do_sprite_bank(c, d, 0);
885 else if(id==BANKID_PKSCREEN) {
886 de_declare_fmt(c, "STOS Packed Screen / Picture Packer");
887 do_pkscreen_bank(c, d, 0);
889 else {
890 de_err(c, "Not a (supported) STOS/MBK format");
894 de_free(c, d);
897 static void de_run_mbk_mbs(deark *c, de_module_params *mparams)
899 run_mbk_mbs_internal(c, mparams, 0);
902 // Detects raw packed screen, and MBK containing packed screen.
903 // Returns the resolution in *res.
904 static int is_stos_packedscreen(deark *c, UI *res)
906 u8 buf[24];
907 u8 is_mbk = 0;
908 UI id = 0;
909 i64 bpos = 0;
911 *res = 0;
912 de_read(buf, 0, sizeof(buf));
914 if(!de_memcmp(buf, g_lion_sig, 10)) is_mbk = 1;
915 if(is_mbk) {
916 if((UI)de_getu32be_direct(&buf[10]) == 0) return 0; // screen out MBS
917 if(buf[14] != 0x81) return 0; // must be type 'data'
918 bpos = 18;
921 id = (UI)de_getu32be_direct(&buf[bpos]);
922 if(id!=BANKID_PKSCREEN) return 0;
923 *res = (UI)de_getu16be_direct(&buf[bpos+4]);
924 return 1;
927 // Note: Returned values must be kept consistent with other stos modules.
928 static int de_identify_mbk(deark *c)
930 u8 buf[10];
931 UI id;
933 de_read(buf, 0, sizeof(buf));
934 if(!de_memcmp(buf, g_lion_sig, 10))
935 return 99;
936 id = (UI)de_getu32be_direct(buf);
937 if(id==BANKID_SPRITE) {
938 return 100;
940 else if(id==BANKID_PKSCREEN) {
941 return 99;
943 return 0;
946 static void de_help_mbk(deark *c)
948 fmtutil_atari_help_palbits(c);
949 de_msg(c, "-opt stos:res0=<l|m> : Assume res 0 pics are low/med res");
950 de_msg(c, "-opt stos:res1=<l|m|h> : Assume res 1 pics are low/med/high res");
953 void de_module_mbk(deark *c, struct deark_module_info *mi)
955 mi->id = "stos";
956 mi->desc = "STOS Memory Bank (.MBK)";
957 mi->run_fn = de_run_mbk_mbs;
958 mi->identify_fn = de_identify_mbk;
959 mi->help_fn = de_help_mbk;
960 mi->id_alias[0] = "mbk";
963 // Note: Returned values must be kept consistent with other stos modules.
964 static int de_identify_stos_pp1(deark *c)
966 int has_ext;
967 int is_pkscr;
968 UI res;
970 has_ext = de_input_file_has_ext(c, "pp1");
971 if(!has_ext) return 0;
972 is_pkscr = is_stos_packedscreen(c, &res);
973 if(is_pkscr && res==1) return 100;
974 return 0;
977 static void de_run_stos_pp1(deark *c, de_module_params *mparams)
979 run_mbk_mbs_internal(c, mparams, 1);
982 void de_module_stos_pp1(deark *c, struct deark_module_info *mi)
984 mi->id = "stos_pp1";
985 mi->desc = "Picture Packer low res";
986 mi->run_fn = de_run_stos_pp1;
987 mi->identify_fn = de_identify_stos_pp1;
988 mi->help_fn = de_help_mbk;
991 static void de_run_stos_pp2(deark *c, de_module_params *mparams)
993 run_mbk_mbs_internal(c, mparams, 2);
996 // Note: Returned values must be kept consistent with other stos modules.
997 static int de_identify_stos_pp2(deark *c)
999 int has_ext;
1000 int is_pkscr;
1001 UI res;
1003 has_ext = de_input_file_has_ext(c, "pp2");
1004 if(!has_ext) return 0;
1005 is_pkscr = is_stos_packedscreen(c, &res);
1006 if(is_pkscr && res==1) return 100;
1007 return 0;
1010 void de_module_stos_pp2(deark *c, struct deark_module_info *mi)
1012 mi->id = "stos_pp2";
1013 mi->desc = "Picture Packer med res";
1014 mi->run_fn = de_run_stos_pp2;
1015 mi->identify_fn = de_identify_stos_pp2;
1016 mi->help_fn = de_help_mbk;
1019 static void de_run_stos_pp3(deark *c, de_module_params *mparams)
1021 run_mbk_mbs_internal(c, mparams, 3);
1024 // Note: Returned values must be kept consistent with other stos modules.
1025 static int de_identify_stos_pp3(deark *c)
1027 int has_ext;
1028 int is_pkscr;
1029 UI res;
1031 has_ext = de_input_file_has_ext(c, "pp3");
1032 if(!has_ext) return 0;
1033 is_pkscr = is_stos_packedscreen(c, &res);
1034 if(is_pkscr && res==1) return 100;
1035 return 0;
1038 void de_module_stos_pp3(deark *c, struct deark_module_info *mi)
1040 mi->id = "stos_pp3";
1041 mi->desc = "Picture Packer high res";
1042 mi->run_fn = de_run_stos_pp3;
1043 mi->identify_fn = de_identify_stos_pp3;
1044 mi->help_fn = de_help_mbk;
1047 static void de_run_stos_daj(deark *c, de_module_params *mparams)
1049 run_mbk_mbs_internal(c, mparams, 4);
1052 // Note: Returned values must be kept consistent with other stos modules.
1053 static int de_identify_stos_daj(deark *c)
1055 int has_ext;
1056 int is_pkscr;
1057 UI res;
1059 has_ext = de_input_file_has_ext(c, "daj");
1060 if(!has_ext) return 0;
1061 is_pkscr = is_stos_packedscreen(c, &res);
1062 if(is_pkscr && (res==0 || res==1)) return 100;
1063 return 0;
1066 // .DAJ, a 4-plane medium res format, seems to be used only by a presentation-like
1067 // viewer used by the ST+ (ST Plus) Atari diskmagazine.
1068 void de_module_stos_daj(deark *c, struct deark_module_info *mi)
1070 mi->id = "stos_daj";
1071 mi->desc = "Picture Packer DAJ";
1072 mi->run_fn = de_run_stos_daj;
1073 mi->identify_fn = de_identify_stos_daj;
1074 mi->help_fn = de_help_mbk;