1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // AMOS sprite/icon bank
7 #include <deark-private.h>
8 #include <deark-fmtutil.h>
9 DE_DECLARE_MODULE(de_module_abk
);
10 DE_DECLARE_MODULE(de_module_amos_source
);
12 #define CODE_AmBk 0x416d426bU
13 #define CODE_AmBs 0x416d4273U
14 #define CODE_AmIc 0x416d4963U
15 #define CODE_AmSp 0x416d5370U
17 // Data related to the whole file.
18 typedef struct localctx_struct
{
22 // Data related to a "bank". Most files consist of one bank, but some have
25 struct de_fourcc banktype4cc
;
36 i64 xsize
; // 16-bit words per row per plane
41 // Picture Bank settings
42 i64 pic_rledata_offset
;
43 i64 pic_points_offset
;
44 i64 pic_picdata_offset
;
45 i64 picdata_expected_unc_bytes
;
49 static void do_read_sprite_image(deark
*c
, lctx
*d
, struct amosbank
*bk
, i64 pos
)
52 i64 rowspan
, planespan
;
53 de_bitmap
*img
= NULL
;
55 width
= bk
->xsize
* 16;
58 de_dbg_dimensions(c
, width
, height
);
59 de_dbg(c
, "planes: %d", (int)bk
->nplanes
);
60 if(!de_good_image_dimensions(c
, width
, height
)) goto done
;
61 if(bk
->nplanes
<1 || bk
->nplanes
>6) {
62 de_err(c
, "Unsupported number of planes: %d", (int)bk
->nplanes
);
66 img
= de_bitmap_create(c
, width
, height
, 4);
68 rowspan
= bk
->xsize
*2;
69 planespan
= rowspan
*bk
->ysize
;
70 de_convert_image_paletted_planar(bk
->f
, pos
, bk
->nplanes
,
71 rowspan
, planespan
, bk
->pal
, img
, 0x2);
73 de_bitmap_write_to_file(img
, NULL
, 0);
76 de_bitmap_destroy(img
);
79 static int do_sprite_object(deark
*c
, lctx
*d
, struct amosbank
*bk
, i64 obj_idx
,
80 i64 pos
, int pass
, i64
*bytes_consumed
)
84 de_dbg(c
, "object #%d at %d", (int)obj_idx
, (int)pos
);
88 bk
->xsize
= dbuf_getu16be(bk
->f
, pos
);
89 bk
->ysize
= dbuf_getu16be(bk
->f
, pos
+2);
90 bk
->nplanes
= dbuf_getu16be(bk
->f
, pos
+4);
93 if(bk
->nplanes
> bk
->max_planes
) {
94 bk
->max_planes
= bk
->nplanes
;
99 do_read_sprite_image(c
, d
, bk
, pos
+10);
102 *bytes_consumed
= 10 + (bk
->xsize
*bk
->ysize
*bk
->nplanes
*2);
104 de_dbg_indent(c
, -1);
108 // pass 1 is just to find the location of the palette/
109 // pass 2 decodes the images.
110 static void do_read_sprite_objects(deark
*c
, lctx
*d
, struct amosbank
*bk
, i64 pos
, int pass
)
116 de_dbg(c
, "pass %d", pass
);
120 if(pos
>= bk
->f
->len
) break;
121 if(obj_idx
>= bk
->num_objects
) break;
123 ret
= do_sprite_object(c
, d
, bk
, obj_idx
, pos
, pass
, &bytes_consumed
);
124 if(!ret
|| bytes_consumed
<1) break;
125 pos
+= bytes_consumed
;
131 bk
->bank_len
= bk
->pal_pos
+ 64;
132 de_dbg(c
, "palette offset: %d", (int)bk
->pal_pos
);
133 de_dbg(c
, "bank len: %d", (int)bk
->bank_len
);
137 static void do_read_sprite_palette(deark
*c
, lctx
*d
, struct amosbank
*bk
)
148 de_dbg(c
, "palette at %d", (int)pos
);
151 colors_used
= de_pow2(bk
->max_planes
);
153 for(k
=0; k
<32; k
++) {
154 n
= (unsigned int)dbuf_getu16be(bk
->f
, pos
+k
*2);
155 cr1
= (u8
)((n
>>8)&0xf);
156 cg1
= (u8
)((n
>>4)&0xf);
161 bk
->pal
[k
] = DE_MAKE_RGB(cr
, cg
, cb
);
162 de_snprintf(tmps
, sizeof(tmps
), "0x%04x (%2d,%2d,%2d) "DE_CHAR_RIGHTARROW
" ",
163 n
, (int)cr1
, (int)cg1
, (int)cb1
);
164 de_dbg_pal_entry2(c
, k
, bk
->pal
[k
], tmps
, NULL
,
165 (k
>=colors_used
)?" [unused]":"");
167 // Set up colors #32-63 for 6-plane "Extra Half-Brite" mode.
168 // For normal images (<=5 planes), these colors won't be used.
169 bk
->pal
[k
+32] = DE_MAKE_RGB(cr
/2, cg
/2, cb
/2);
172 bk
->pal
[0] = DE_SET_ALPHA(bk
->pal
[0], 0); // First color is transparent.
173 // (Don't know if pal[32] should be transparent also.)
175 de_dbg_indent(c
, -1);
179 static int do_read_sprite(deark
*c
, lctx
*d
, struct amosbank
*bk
)
181 bk
->num_objects
= dbuf_getu16be(bk
->f
, 4);
182 de_dbg(c
, "number of objects: %d", (int)bk
->num_objects
);
184 do_read_sprite_objects(c
, d
, bk
, 6, 1);
186 if(d
->fmt
==CODE_AmBs
) {
187 dbuf_create_file_from_slice(bk
->f
, 0, bk
->bank_len
, bk
->file_ext
, NULL
, 0);
190 do_read_sprite_palette(c
, d
, bk
);
192 do_read_sprite_objects(c
, d
, bk
, 6, 2);
198 #define MEMBANKTYPE_DATAS 1
199 #define MEMBANKTYPE_MUSIC 2
200 #define MEMBANKTYPE_PICTURE 3
201 #define MEMBANKTYPE_ASM 4
202 #define MEMBANKTYPE_AMAL 5
203 #define MEMBANKTYPE_SAMPLES 6
208 const char *file_ext
;
210 static const struct membankinfo membankinfo_arr
[] = {
211 { MEMBANKTYPE_DATAS
, {'D','a','t','a','s',' ',' ',' '}, "data.abk" },
212 { MEMBANKTYPE_MUSIC
, {'M','u','s','i','c',' ',' ',' '}, "music.abk" },
213 { MEMBANKTYPE_PICTURE
, {'P','a','c','.','P','i','c','.'}, "pic.abk" },
214 { MEMBANKTYPE_ASM
, {'A','s','m',' ',' ',' ',' ',' '}, "asm.abk" },
215 { MEMBANKTYPE_AMAL
, {'A','m','a','l',' ',' ',' ',' '}, "amal.abk" },
216 { MEMBANKTYPE_SAMPLES
, {'S','a','m','p','l','e','s',' '}, "samples.abk" },
217 { 0, {0,0,0,0,0,0,0,0}, NULL
}
220 // 90-byte "Screen header"
221 // Has information about the intended display device. Not much of this
222 // is useful, other than the palette.
223 static void picture_bank_screen_header(deark
*c
, lctx
*d
, struct amosbank
*bk
, i64 pos
)
225 i64 screen_width
, screen_height
;
229 de_dbg(c
, "screen header at %d", (int)pos
);
232 screen_width
= dbuf_getu16be(bk
->f
, pos
+4);
233 screen_height
= dbuf_getu16be(bk
->f
, pos
+6);
234 de_dbg(c
, "screen dimensions: %d"DE_CHAR_TIMES
"%d", (int)screen_width
, (int)screen_height
);
236 bk
->amiga_mode
= (u32
)dbuf_getu16be(bk
->f
, pos
+20);
237 ncolors
= dbuf_getu16be(bk
->f
, pos
+22);
238 nplanes
= dbuf_getu16be(bk
->f
, pos
+24);
240 de_dbg(c
, "screen mode: 0x%04x, colors: %d, planes: %d",
241 (unsigned int)bk
->amiga_mode
, (int)ncolors
, (int)nplanes
);
243 bk
->pal_pos
= pos
+ 26;
245 // Set bk->max_planes, so that do_read_sprite_palette doesn't print
247 // TODO: We could look ahead at the picture header to figure out how many
248 // palette entries are used. Or we could just guess that it's the same as
249 // 'nplanes' in the screen header.
251 do_read_sprite_palette(c
, d
, bk
);
254 de_dbg_indent(c
, -1);
257 struct pictbank_params
{
270 // TODO: Consolidate this with the similar function in mbk.c.
271 // (Deferred for now. Should probably at least investigate HAM picture banks,
273 static void render_stos_pictbank(deark
*c
, struct pictbank_params
*pb
)
279 if((size_t)pb
->num_planes
> sizeof(xbuf
)) goto done
;
280 if(pb
->bits_per_pixel
!= pb
->num_planes
) goto done
;
281 de_zeromem(xbuf
, sizeof(xbuf
));
282 planesize
= pb
->width_in_bytes
* pb
->pseudoheight
;
284 for(lump
=0; lump
<pb
->height_in_lumps
; lump
++) {
286 i64 lump_start_srcpos_in_plane
;
289 lump_start_srcpos_in_plane
= pb
->width_in_bytes
* pb
->lines_per_lump
* lump
;
290 lump_start_ypos
= pb
->lines_per_lump
* lump
;
292 for(col_idx
=0; col_idx
<pb
->width_in_bytes
; col_idx
++) {
293 i64 col_start_srcpos_in_plane
;
296 col_start_srcpos_in_plane
= lump_start_srcpos_in_plane
+
297 pb
->lines_per_lump
*col_idx
;
299 for(ypos_in_lump
=0; ypos_in_lump
<pb
->lines_per_lump
; ypos_in_lump
++) {
304 ypos
= lump_start_ypos
+ ypos_in_lump
;
306 for(pn
=0; pn
<pb
->num_planes
; pn
++) {
307 xbuf
[pn
] = dbuf_getbyte(pb
->unc_pixels
, planesize
*pn
+
308 col_start_srcpos_in_plane
+ ypos_in_lump
);
315 for(pn
=0; pn
<pb
->bits_per_pixel
; pn
++) {
316 if(xbuf
[pn
] & (1<<(7-i
))) {
321 xpos
= col_idx
*8 + i
;
322 de_bitmap_setpixel_rgb(pb
->img
, xpos
, ypos
, pb
->pal
[palent
]);
334 static void picture_bank_read_picture(deark
*c
, lctx
*d
, struct amosbank
*bk
, i64 pos
)
336 i64 bytes_per_row_per_plane
;
340 de_bitmap
*img
= NULL
;
341 dbuf
*unc_pixels
= NULL
;
342 struct pictbank_params
*pb
= NULL
;
343 int saved_indent_level
;
345 de_dbg_indent_save(c
, &saved_indent_level
);
346 de_dbg(c
, "picture header at %d", (int)pos
);
349 // 24-byte "Picture header"
351 bytes_per_row_per_plane
= dbuf_getu16be(bk
->f
, pos
+8);
352 de_dbg(c
, "bytes per row per plane: %d", (int)bytes_per_row_per_plane
);
353 width
= bytes_per_row_per_plane
* 8;
355 height_in_lumps
= dbuf_getu16be(bk
->f
, pos
+10);
356 de_dbg(c
, "height in lumps: %d", (int)height_in_lumps
);
357 lines_per_lump
= dbuf_getu16be(bk
->f
, pos
+12);
358 de_dbg(c
, "lines per lump: %d", (int)lines_per_lump
);
359 height
= height_in_lumps
* lines_per_lump
;
361 de_dbg(c
, "calculated dimensions: %d"DE_CHAR_TIMES
"%d", (int)width
, (int)height
);
363 bk
->nplanes
= dbuf_getu16be(bk
->f
, pos
+14);
364 de_dbg(c
, "number of bitplanes: %d", (int)bk
->nplanes
);
366 bk
->pic_rledata_offset
= dbuf_getu32be(bk
->f
, pos
+16);
367 de_dbg(c
, "rledata offset: %d (file offset: %d)", (int)bk
->pic_rledata_offset
,
368 (int)(pos
+bk
->pic_rledata_offset
));
369 bk
->pic_rledata_offset
+= pos
; // Convert to absolute offset
371 bk
->pic_points_offset
= dbuf_getu32be(bk
->f
, pos
+20);
372 de_dbg(c
, "points offset: %d (file offset: %d)", (int)bk
->pic_points_offset
,
373 (int)(pos
+bk
->pic_points_offset
));
374 bk
->pic_points_offset
+= pos
; // Convert to absolute offset
376 if(!de_good_image_dimensions(c
, width
, height
)) goto done
;
377 if(bk
->nplanes
<1 || bk
->nplanes
>6) {
378 de_err(c
, "Unsupported number of planes: %d", (int)bk
->nplanes
);
382 de_dbg_indent(c
, -1);
384 bk
->pic_picdata_offset
= pos
+ 24;
385 de_dbg(c
, "picdata at %d", (int)bk
->pic_picdata_offset
);
387 bk
->picdata_expected_unc_bytes
= bytes_per_row_per_plane
* bk
->nplanes
* height
;
388 unc_pixels
= dbuf_create_membuf(c
, bk
->picdata_expected_unc_bytes
, 0);
389 fmtutil_decompress_stos_pictbank(c
, c
->infile
, bk
->pic_picdata_offset
,
390 bk
->pic_rledata_offset
, bk
->pic_points_offset
,
391 unc_pixels
, bk
->picdata_expected_unc_bytes
);
392 img
= de_bitmap_create(c
, width
, height
, 3);
394 pb
= de_malloc(c
, sizeof(struct pictbank_params
));
395 pb
->num_planes
= (UI
)bk
->nplanes
;
396 pb
->bits_per_pixel
= pb
->num_planes
;
397 pb
->width_in_bytes
= bytes_per_row_per_plane
;
398 pb
->height_in_lumps
= height_in_lumps
;
399 pb
->lines_per_lump
= lines_per_lump
;
400 pb
->pseudoheight
= height
;
401 pb
->unc_pixels
= unc_pixels
;
405 render_stos_pictbank(c
, pb
);
407 de_bitmap_write_to_file(img
, NULL
, 0);
409 dbuf_close(unc_pixels
);
410 de_bitmap_destroy(img
);
412 de_dbg_indent_restore(c
, saved_indent_level
);
415 static void picture_bank_make_palette(deark
*c
, lctx
*d
, struct amosbank
*bk
)
420 de_warn(c
, "No palette found. Using grayscale palette.");
421 for(k
=0; k
<32; k
++) {
422 v
= (u8
)(0.5+ ((double)k
)*(255.0/31.0));
423 bk
->pal
[k
] = DE_MAKE_GRAY(v
);
424 bk
->pal
[k
+32] = DE_MAKE_GRAY(v
/2);
428 static void do_picture_bank(deark
*c
, lctx
*d
, struct amosbank
*bk
)
432 int found_screen_header
= 0;
434 de_dbg(c
, "picture bank");
436 pos
+= 20; // Advance past AmBk header
438 segtype
= (u32
)de_getu32be(pos
);
439 if(segtype
==0x12031990) {
440 found_screen_header
= 1;
441 picture_bank_screen_header(c
, d
, bk
, pos
);
444 if(bk
->amiga_mode
& 0x0800) {
445 de_err(c
, "HAM Picture Bank images are not supported.");
449 segtype
= (u32
)de_getu32be(pos
);
452 if(segtype
!=0x06071963) {
453 de_err(c
, "Missing Picture Header");
457 if(!found_screen_header
) {
458 picture_bank_make_palette(c
, d
, bk
);
461 picture_bank_read_picture(c
, d
, bk
, pos
);
467 static int do_read_AmBk(deark
*c
, lctx
*d
, struct amosbank
*bk
)
473 const struct membankinfo
*mbi
= NULL
;
474 struct de_stringreaderdata
*srd
= NULL
;
478 if(bk
->f
->len
< 20) goto done
;
480 banknum
= dbuf_getu16be(bk
->f
, 4);
481 de_dbg(c
, "bank number (1-15): %d", (int)banknum
);
483 bank_len_code
= dbuf_getu32be(bk
->f
, 8);
484 bank_len_raw
= bank_len_code
& 0x0fffffff;
485 bk
->bank_len
= bank_len_raw
+12;
486 bk
->bank_data_len
= bank_len_raw
-8;
487 de_dbg(c
, "bank length: %d (dlen=%d, tlen=%d)", (int)bank_len_raw
,
488 (int)bk
->bank_data_len
, (int)bk
->bank_len
);
490 srd
= dbuf_read_string(bk
->f
, 12, 8, 8, 0, DE_ENCODING_ASCII
);
491 de_dbg(c
, "bank name: \"%s\"", ucstring_getpsz(srd
->str
));
493 if(bk
->bank_data_len
<0) goto done
;
495 for(i
=0; membankinfo_arr
[i
].type
!=0; i
++) {
496 if(!de_memcmp(srd
->sz
, membankinfo_arr
[i
].name
, 8)) {
497 mbi
= &membankinfo_arr
[i
];
503 membanktype
= mbi
->type
;
504 bk
->file_ext
= mbi
->file_ext
;
507 if(d
->fmt
==CODE_AmBs
) {
508 // If original file is in AmBs format, just extract the AmBk file.
509 dbuf_create_file_from_slice(bk
->f
, 0, bk
->bank_len
, bk
->file_ext
, NULL
, 0);
514 switch(membanktype
) {
515 case MEMBANKTYPE_PICTURE
:
516 do_picture_bank(c
, d
, bk
);
521 if(c
->extract_level
>=2) {
522 // Extracting the raw memory-bank data can be useful sometimes.
523 dbuf_create_file_from_slice(bk
->f
, 20, bk
->bank_data_len
, "bin", NULL
, 0);
528 de_destroy_stringreaderdata(c
, srd
);
532 static int do_read_bank(deark
*c
, lctx
*d
, i64 pos
, i64
*bytesused
)
534 struct amosbank
*bk
= NULL
;
538 bk
= de_malloc(c
, sizeof(struct amosbank
));
539 bk
->f
= dbuf_open_input_subfile(c
->infile
, pos
, c
->infile
->len
- pos
);
541 dbuf_read_fourcc(bk
->f
, 0, &bk
->banktype4cc
, 4, 0x0);
542 de_dbg(c
, "bank type '%s'", bk
->banktype4cc
.id_dbgstr
);
544 switch(bk
->banktype4cc
.id
) {
545 case CODE_AmIc
: bk
->file_ext
= "icon.abk"; break;
546 case CODE_AmSp
: bk
->file_ext
= "sprite.abk"; break;
547 case CODE_AmBk
: bk
->file_ext
= "AmBk.abk"; break;
548 default: bk
->file_ext
= "abk";
551 if(bk
->banktype4cc
.id
==CODE_AmIc
|| bk
->banktype4cc
.id
==CODE_AmSp
) {
552 ret
= do_read_sprite(c
, d
, bk
);
554 *bytesused
= bk
->bank_len
;
556 else if(bk
->banktype4cc
.id
==CODE_AmBk
) {
557 ret
= do_read_AmBk(c
, d
, bk
);
559 *bytesused
= bk
->bank_len
;
562 de_err(c
, "Unsupported bank type: '%s'", bk
->banktype4cc
.id_sanitized_sz
);
572 static void do_read_AmBs(deark
*c
, lctx
*d
)
580 nbanks
= de_getu16be(4);
581 de_dbg(c
, "number of banks: %d", (int)nbanks
);
584 for(i
=0; i
<nbanks
; i
++) {
585 if(pos
>= c
->infile
->len
) break;
586 de_dbg(c
, "bank #%d at %d", (int)i
, (int)pos
);
589 ret
= do_read_bank(c
, d
, pos
, &bytesused
);
590 de_dbg_indent(c
, -1);
591 if(!ret
|| bytesused
<1) break;
596 static void de_run_abk(deark
*c
, de_module_params
*mparams
)
601 d
= de_malloc(c
, sizeof(lctx
));
603 d
->fmt
= (u32
)de_getu32be(0);
605 if(d
->fmt
==CODE_AmBk
) {
606 de_declare_fmt(c
, "AMOS Memory Bank");
608 else if(d
->fmt
==CODE_AmSp
) {
609 de_declare_fmt(c
, "AMOS Sprite Bank");
611 else if(d
->fmt
==CODE_AmIc
) {
612 de_declare_fmt(c
, "AMOS Icon Bank");
614 else if(d
->fmt
==CODE_AmBs
) {
615 de_declare_fmt(c
, "AMOS AmBs format");
618 de_err(c
, "Unsupported format");
622 if(d
->fmt
==CODE_AmBk
||d
->fmt
==CODE_AmSp
|| d
->fmt
==CODE_AmIc
) {
623 do_read_bank(c
, d
, 0, &bytesused
);
625 else if(d
->fmt
==CODE_AmBs
) {
633 static int de_identify_abk(deark
*c
)
638 if(de_input_file_has_ext(c
, "abk")) ext_bonus
=40;
641 if(!de_memcmp(b
, "AmBk", 4))
643 if(!de_memcmp(b
, "AmSp", 4))
645 if(!de_memcmp(b
, "AmIc", 4))
647 if(!de_memcmp(b
, "AmBs", 4))
652 void de_module_abk(deark
*c
, struct deark_module_info
*mi
)
655 mi
->desc
= "AMOS resource (AmBk, sprite, icon, AmBs)";
656 mi
->run_fn
= de_run_abk
;
657 mi
->identify_fn
= de_identify_abk
;
660 static void de_run_amos_source(deark
*c
, de_module_params
*mparams
)
667 d
= de_malloc(c
, sizeof(lctx
));
670 basic_len
= de_getu32be(pos
);
672 de_dbg(c
, "BASIC code at %d, len=%d", (int)pos
, (int)basic_len
);
674 if(pos
>= c
->infile
->len
) goto done
;
675 if(dbuf_memcmp(c
->infile
, pos
, "AmBs", 4)) {
676 de_err(c
, "AmBs segment not found, expected at offset %d", (int)pos
);
680 de_dbg(c
, "AmBs segment at %d", (int)pos
);
681 nbanks
= de_getu16be(pos
+4);
683 de_dbg(c
, "number of banks: %d", (int)nbanks
);
684 if(nbanks
>0 || c
->extract_level
>=2) {
685 dbuf_create_file_from_slice(c
->infile
, pos
, c
->infile
->len
-pos
, "AmBs.abk", NULL
, 0);
688 de_dbg(c
, "not extracting empty AmBs segment");
690 de_dbg_indent(c
, -1);
697 static int de_identify_amos_source(deark
*c
)
702 if(de_input_file_has_ext(c
, "amos")) ext_bonus
=20;
705 if(!de_memcmp(b
, "AMOS Basic", 10))
707 if(!de_memcmp(b
, "AMOS Pro", 8))
712 void de_module_amos_source(deark
*c
, struct deark_module_info
*mi
)
714 mi
->id
= "amos_source";
715 mi
->desc
= "AMOS source code";
716 mi
->run_fn
= de_run_amos_source
;
717 mi
->identify_fn
= de_identify_amos_source
;