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-config.h>
8 #include <deark-private.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
)
56 i64 rowspan
, planespan
;
58 de_bitmap
*img
= NULL
;
60 width
= bk
->xsize
* 16;
63 de_dbg_dimensions(c
, width
, height
);
64 de_dbg(c
, "planes: %d", (int)bk
->nplanes
);
65 if(!de_good_image_dimensions(c
, width
, height
)) goto done
;
66 if(bk
->nplanes
<1 || bk
->nplanes
>6) {
67 de_err(c
, "Unsupported number of planes: %d", (int)bk
->nplanes
);
71 img
= de_bitmap_create(c
, width
, height
, 4);
73 rowspan
= bk
->xsize
*2;
74 planespan
= rowspan
*bk
->ysize
;
76 for(j
=0; j
<height
; j
++) {
77 for(i
=0; i
<width
; i
++) {
79 for(plane
=0; plane
<bk
->nplanes
; plane
++) {
80 b
= de_get_bits_symbol(bk
->f
, 1, pos
+ plane
*planespan
+ j
*rowspan
, i
);
81 if(b
) palent
|= (1<<plane
);
83 if(palent
<=255) clr
= bk
->pal
[palent
];
86 de_bitmap_setpixel_rgb(img
, i
, j
, clr
);
90 de_bitmap_write_to_file(img
, NULL
, 0);
93 de_bitmap_destroy(img
);
96 static int do_sprite_object(deark
*c
, lctx
*d
, struct amosbank
*bk
, i64 obj_idx
,
97 i64 pos
, int pass
, i64
*bytes_consumed
)
101 de_dbg(c
, "object #%d at %d", (int)obj_idx
, (int)pos
);
105 bk
->xsize
= dbuf_getu16be(bk
->f
, pos
);
106 bk
->ysize
= dbuf_getu16be(bk
->f
, pos
+2);
107 bk
->nplanes
= dbuf_getu16be(bk
->f
, pos
+4);
110 if(bk
->nplanes
> bk
->max_planes
) {
111 bk
->max_planes
= bk
->nplanes
;
116 do_read_sprite_image(c
, d
, bk
, pos
+10);
119 *bytes_consumed
= 10 + (bk
->xsize
*bk
->ysize
*bk
->nplanes
*2);
121 de_dbg_indent(c
, -1);
125 // pass 1 is just to find the location of the palette/
126 // pass 2 decodes the images.
127 static void do_read_sprite_objects(deark
*c
, lctx
*d
, struct amosbank
*bk
, i64 pos
, int pass
)
133 de_dbg(c
, "pass %d", pass
);
137 if(pos
>= bk
->f
->len
) break;
138 if(obj_idx
>= bk
->num_objects
) break;
140 ret
= do_sprite_object(c
, d
, bk
, obj_idx
, pos
, pass
, &bytes_consumed
);
141 if(!ret
|| bytes_consumed
<1) break;
142 pos
+= bytes_consumed
;
148 bk
->bank_len
= bk
->pal_pos
+ 64;
149 de_dbg(c
, "palette offset: %d", (int)bk
->pal_pos
);
150 de_dbg(c
, "bank len: %d", (int)bk
->bank_len
);
154 static void do_read_sprite_palette(deark
*c
, lctx
*d
, struct amosbank
*bk
)
165 de_dbg(c
, "palette at %d", (int)pos
);
168 colors_used
= de_pow2(bk
->max_planes
);
170 for(k
=0; k
<32; k
++) {
171 n
= (unsigned int)dbuf_getu16be(bk
->f
, pos
+k
*2);
172 cr1
= (u8
)((n
>>8)&0xf);
173 cg1
= (u8
)((n
>>4)&0xf);
178 bk
->pal
[k
] = DE_MAKE_RGB(cr
, cg
, cb
);
179 de_snprintf(tmps
, sizeof(tmps
), "0x%04x (%2d,%2d,%2d) "DE_CHAR_RIGHTARROW
" ",
180 n
, (int)cr1
, (int)cg1
, (int)cb1
);
181 de_dbg_pal_entry2(c
, k
, bk
->pal
[k
], tmps
, NULL
,
182 (k
>=colors_used
)?" [unused]":"");
184 // Set up colors #32-63 for 6-plane "Extra Half-Brite" mode.
185 // For normal images (<=5 planes), these colors won't be used.
186 bk
->pal
[k
+32] = DE_MAKE_RGB(cr
/2, cg
/2, cb
/2);
189 bk
->pal
[0] = DE_SET_ALPHA(bk
->pal
[0], 0); // First color is transparent.
190 // (Don't know if pal[32] should be transparent also.)
192 de_dbg_indent(c
, -1);
196 static int do_read_sprite(deark
*c
, lctx
*d
, struct amosbank
*bk
)
198 bk
->num_objects
= dbuf_getu16be(bk
->f
, 4);
199 de_dbg(c
, "number of objects: %d", (int)bk
->num_objects
);
201 do_read_sprite_objects(c
, d
, bk
, 6, 1);
203 if(d
->fmt
==CODE_AmBs
) {
204 dbuf_create_file_from_slice(bk
->f
, 0, bk
->bank_len
, bk
->file_ext
, NULL
, 0);
207 do_read_sprite_palette(c
, d
, bk
);
209 do_read_sprite_objects(c
, d
, bk
, 6, 2);
215 #define MEMBANKTYPE_DATAS 1
216 #define MEMBANKTYPE_MUSIC 2
217 #define MEMBANKTYPE_PICTURE 3
218 #define MEMBANKTYPE_ASM 4
219 #define MEMBANKTYPE_AMAL 5
220 #define MEMBANKTYPE_SAMPLES 6
225 const char *file_ext
;
227 static const struct membankinfo membankinfo_arr
[] = {
228 { MEMBANKTYPE_DATAS
, {'D','a','t','a','s',' ',' ',' '}, "data.abk" },
229 { MEMBANKTYPE_MUSIC
, {'M','u','s','i','c',' ',' ',' '}, "music.abk" },
230 { MEMBANKTYPE_PICTURE
, {'P','a','c','.','P','i','c','.'}, "pic.abk" },
231 { MEMBANKTYPE_ASM
, {'A','s','m',' ',' ',' ',' ',' '}, "asm.abk" },
232 { MEMBANKTYPE_AMAL
, {'A','m','a','l',' ',' ',' ',' '}, "amal.abk" },
233 { MEMBANKTYPE_SAMPLES
, {'S','a','m','p','l','e','s',' '}, "samples.abk" },
234 { 0, {0,0,0,0,0,0,0,0}, NULL
}
237 // 90-byte "Screen header"
238 // Has information about the intended display device. Not much of this
239 // is useful, other than the palette.
240 static void picture_bank_screen_header(deark
*c
, lctx
*d
, struct amosbank
*bk
, i64 pos
)
242 i64 screen_width
, screen_height
;
246 de_dbg(c
, "screen header at %d", (int)pos
);
249 screen_width
= dbuf_getu16be(bk
->f
, pos
+4);
250 screen_height
= dbuf_getu16be(bk
->f
, pos
+6);
251 de_dbg(c
, "screen dimensions: %d"DE_CHAR_TIMES
"%d", (int)screen_width
, (int)screen_height
);
253 bk
->amiga_mode
= (u32
)dbuf_getu16be(bk
->f
, pos
+20);
254 ncolors
= dbuf_getu16be(bk
->f
, pos
+22);
255 nplanes
= dbuf_getu16be(bk
->f
, pos
+24);
257 de_dbg(c
, "screen mode: 0x%04x, colors: %d, planes: %d",
258 (unsigned int)bk
->amiga_mode
, (int)ncolors
, (int)nplanes
);
260 bk
->pal_pos
= pos
+ 26;
262 // Set bk->max_planes, so that do_read_sprite_palette doesn't print
264 // TODO: We could look ahead at the picture header to figure out how many
265 // palette entries are used. Or we could just guess that it's the same as
266 // 'nplanes' in the screen header.
268 do_read_sprite_palette(c
, d
, bk
);
271 de_dbg_indent(c
, -1);
274 static void picture_bank_uncompress(deark
*c
, lctx
*d
, struct amosbank
*bk
,
277 i64 picdatapos
; // file offset of next unread byte
283 int rbitnum
, pbitnum
;
285 de_dbg(c
, "uncompressing picture");
288 picdatapos
= bk
->pic_picdata_offset
;
289 rledatapos
= bk
->pic_rledata_offset
;
290 pointspos
= bk
->pic_points_offset
;
292 picbyte
= dbuf_getbyte(bk
->f
, picdatapos
++);
293 rlebyte
= dbuf_getbyte(bk
->f
, rledatapos
++);
295 pointsbyte
= dbuf_getbyte(bk
->f
, pointspos
++);
298 if(pointsbyte
& (1 << pbitnum
--)) {
299 rlebyte
= dbuf_getbyte(bk
->f
, rledatapos
++);
303 if(unc_pixels
->len
>= bk
->picdata_expected_unc_bytes
) break;
304 if(rlebyte
& (1 << rbitnum
--)) {
305 picbyte
= dbuf_getbyte(bk
->f
, picdatapos
++);
308 dbuf_writebyte(unc_pixels
, picbyte
);
311 if(pointsbyte
& (1 << pbitnum
--)) {
312 rlebyte
= dbuf_getbyte(bk
->f
, rledatapos
++);
317 pointsbyte
= dbuf_getbyte(bk
->f
, pointspos
++);
324 i64 cmpr_pic_bytes
, cmpr_rle_bytes
, points_bytes
;
326 cmpr_pic_bytes
= picdatapos
- bk
->pic_picdata_offset
;
327 cmpr_rle_bytes
= rledatapos
- bk
->pic_rledata_offset
;
328 points_bytes
= pointspos
- bk
->pic_points_offset
;
329 de_dbg(c
, "compressed pic bytes: %d", (int)cmpr_pic_bytes
);
330 de_dbg(c
, "compressed rle bytes: %d", (int)cmpr_rle_bytes
);
331 de_dbg(c
, "points bytes: %d", (int)points_bytes
);
332 de_dbg(c
, "uncompressed %d bytes to %d bytes",
333 (int)(cmpr_pic_bytes
+ cmpr_rle_bytes
+ points_bytes
),
334 (int)unc_pixels
->len
);
337 de_dbg_indent(c
, -1);
340 static void picture_bank_read_picture(deark
*c
, lctx
*d
, struct amosbank
*bk
, i64 pos
)
342 i64 bytes_per_row_per_plane
;
346 de_bitmap
*img
= NULL
;
347 dbuf
*unc_pixels
= NULL
;
359 int saved_indent_level
;
361 de_dbg_indent_save(c
, &saved_indent_level
);
362 de_dbg(c
, "picture header at %d", (int)pos
);
365 // 24-byte "Picture header"
367 bytes_per_row_per_plane
= dbuf_getu16be(bk
->f
, pos
+8);
368 de_dbg(c
, "bytes per row per plane: %d", (int)bytes_per_row_per_plane
);
369 width
= bytes_per_row_per_plane
* 8;
371 height_in_lumps
= dbuf_getu16be(bk
->f
, pos
+10);
372 de_dbg(c
, "height in lumps: %d", (int)height_in_lumps
);
373 lines_per_lump
= dbuf_getu16be(bk
->f
, pos
+12);
374 de_dbg(c
, "lines per lump: %d", (int)lines_per_lump
);
375 height
= height_in_lumps
* lines_per_lump
;
377 de_dbg(c
, "calculated dimensions: %d"DE_CHAR_TIMES
"%d", (int)width
, (int)height
);
379 bk
->nplanes
= dbuf_getu16be(bk
->f
, pos
+14);
380 de_dbg(c
, "number of bitplanes: %d", (int)bk
->nplanes
);
382 bk
->pic_rledata_offset
= dbuf_getu32be(bk
->f
, pos
+16);
383 de_dbg(c
, "rledata offset: %d (file offset: %d)", (int)bk
->pic_rledata_offset
,
384 (int)(pos
+bk
->pic_rledata_offset
));
385 bk
->pic_rledata_offset
+= pos
; // Convert to absolute offset
387 bk
->pic_points_offset
= dbuf_getu32be(bk
->f
, pos
+20);
388 de_dbg(c
, "points offset: %d (file offset: %d)", (int)bk
->pic_points_offset
,
389 (int)(pos
+bk
->pic_points_offset
));
390 bk
->pic_points_offset
+= pos
; // Convert to absolute offset
392 if(!de_good_image_dimensions(c
, width
, height
)) goto done
;
393 if(bk
->nplanes
<1 || bk
->nplanes
>6) {
394 de_err(c
, "Unsupported number of planes: %d", (int)bk
->nplanes
);
398 de_dbg_indent(c
, -1);
400 bk
->pic_picdata_offset
= pos
+ 24;
401 de_dbg(c
, "picdata at %d", (int)bk
->pic_picdata_offset
);
403 bk
->picdata_expected_unc_bytes
= bytes_per_row_per_plane
* bk
->nplanes
* height
;
404 unc_pixels
= dbuf_create_membuf(c
, bk
->picdata_expected_unc_bytes
, 0);
405 picture_bank_uncompress(c
, d
, bk
, unc_pixels
);
407 img
= de_bitmap_create(c
, width
, height
, 3);
409 lumpspan
= bytes_per_row_per_plane
* lines_per_lump
;
410 planespan
= lumpspan
* height_in_lumps
;
413 for(lump
=0; lump
<height_in_lumps
; lump
++) {
415 for(strip
=0; strip
<bytes_per_row_per_plane
; strip
++) {
416 for(line_in_lump
=0; line_in_lump
<lines_per_lump
; line_in_lump
++) {
419 for(plane
=0; plane
<bk
->nplanes
; plane
++) {
420 x
= de_get_bits_symbol(unc_pixels
, 1, pos_in_picdata
+ plane
*planespan
, k
);
421 if(x
) palent
|= 1<<plane
;
424 de_bitmap_setpixel_rgb(img
, xpos
, ypos
, bk
->pal
[palent
]);
433 ypos
-= lines_per_lump
;
435 ypos
+= lines_per_lump
;
438 de_bitmap_write_to_file(img
, NULL
, 0);
440 dbuf_close(unc_pixels
);
441 de_bitmap_destroy(img
);
442 de_dbg_indent_restore(c
, saved_indent_level
);
445 static void picture_bank_make_palette(deark
*c
, lctx
*d
, struct amosbank
*bk
)
450 de_warn(c
, "No palette found. Using grayscale palette.");
451 for(k
=0; k
<32; k
++) {
452 v
= (u8
)(0.5+ ((double)k
)*(255.0/31.0));
453 bk
->pal
[k
] = DE_MAKE_GRAY(v
);
454 bk
->pal
[k
+32] = DE_MAKE_GRAY(v
/2);
458 static void do_picture_bank(deark
*c
, lctx
*d
, struct amosbank
*bk
)
462 int found_screen_header
= 0;
464 de_dbg(c
, "picture bank");
466 pos
+= 20; // Advance past AmBk header
468 segtype
= (u32
)de_getu32be(pos
);
469 if(segtype
==0x12031990) {
470 found_screen_header
= 1;
471 picture_bank_screen_header(c
, d
, bk
, pos
);
474 if(bk
->amiga_mode
& 0x0800) {
475 de_err(c
, "HAM Picture Bank images are not supported.");
479 segtype
= (u32
)de_getu32be(pos
);
482 if(segtype
!=0x06071963) {
483 de_err(c
, "Missing Picture Header");
487 if(!found_screen_header
) {
488 picture_bank_make_palette(c
, d
, bk
);
491 picture_bank_read_picture(c
, d
, bk
, pos
);
497 static int do_read_AmBk(deark
*c
, lctx
*d
, struct amosbank
*bk
)
503 const struct membankinfo
*mbi
= NULL
;
504 struct de_stringreaderdata
*srd
= NULL
;
508 if(bk
->f
->len
< 20) goto done
;
510 banknum
= dbuf_getu16be(bk
->f
, 4);
511 de_dbg(c
, "bank number (1-15): %d", (int)banknum
);
513 bank_len_code
= dbuf_getu32be(bk
->f
, 8);
514 bank_len_raw
= bank_len_code
& 0x0fffffff;
515 bk
->bank_len
= bank_len_raw
+12;
516 bk
->bank_data_len
= bank_len_raw
-8;
517 de_dbg(c
, "bank length: %d (dlen=%d, tlen=%d)", (int)bank_len_raw
,
518 (int)bk
->bank_data_len
, (int)bk
->bank_len
);
520 srd
= dbuf_read_string(bk
->f
, 12, 8, 8, 0, DE_ENCODING_ASCII
);
521 de_dbg(c
, "bank name: \"%s\"", ucstring_getpsz(srd
->str
));
523 if(bk
->bank_data_len
<0) goto done
;
525 for(i
=0; membankinfo_arr
[i
].type
!=0; i
++) {
526 if(!de_memcmp(srd
->sz
, membankinfo_arr
[i
].name
, 8)) {
527 mbi
= &membankinfo_arr
[i
];
533 membanktype
= mbi
->type
;
534 bk
->file_ext
= mbi
->file_ext
;
537 if(d
->fmt
==CODE_AmBs
) {
538 // If original file is in AmBs format, just extract the AmBk file.
539 dbuf_create_file_from_slice(bk
->f
, 0, bk
->bank_len
, bk
->file_ext
, NULL
, 0);
544 switch(membanktype
) {
545 case MEMBANKTYPE_PICTURE
:
546 do_picture_bank(c
, d
, bk
);
551 if(c
->extract_level
>=2) {
552 // Extracting the raw memory-bank data can be useful sometimes.
553 dbuf_create_file_from_slice(bk
->f
, 20, bk
->bank_data_len
, "bin", NULL
, 0);
558 de_destroy_stringreaderdata(c
, srd
);
562 static int do_read_bank(deark
*c
, lctx
*d
, i64 pos
, i64
*bytesused
)
564 struct amosbank
*bk
= NULL
;
568 bk
= de_malloc(c
, sizeof(struct amosbank
));
569 bk
->f
= dbuf_open_input_subfile(c
->infile
, pos
, c
->infile
->len
- pos
);
571 dbuf_read_fourcc(bk
->f
, 0, &bk
->banktype4cc
, 4, 0x0);
572 de_dbg(c
, "bank type '%s'", bk
->banktype4cc
.id_dbgstr
);
574 switch(bk
->banktype4cc
.id
) {
575 case CODE_AmIc
: bk
->file_ext
= "icon.abk"; break;
576 case CODE_AmSp
: bk
->file_ext
= "sprite.abk"; break;
577 case CODE_AmBk
: bk
->file_ext
= "AmBk.abk"; break;
578 default: bk
->file_ext
= "abk";
581 if(bk
->banktype4cc
.id
==CODE_AmIc
|| bk
->banktype4cc
.id
==CODE_AmSp
) {
582 ret
= do_read_sprite(c
, d
, bk
);
584 *bytesused
= bk
->bank_len
;
586 else if(bk
->banktype4cc
.id
==CODE_AmBk
) {
587 ret
= do_read_AmBk(c
, d
, bk
);
589 *bytesused
= bk
->bank_len
;
592 de_err(c
, "Unsupported bank type: '%s'", bk
->banktype4cc
.id_sanitized_sz
);
602 static void do_read_AmBs(deark
*c
, lctx
*d
)
610 nbanks
= de_getu16be(4);
611 de_dbg(c
, "number of banks: %d", (int)nbanks
);
614 for(i
=0; i
<nbanks
; i
++) {
615 if(pos
>= c
->infile
->len
) break;
616 de_dbg(c
, "bank #%d at %d", (int)i
, (int)pos
);
619 ret
= do_read_bank(c
, d
, pos
, &bytesused
);
620 de_dbg_indent(c
, -1);
621 if(!ret
|| bytesused
<1) break;
626 static void de_run_abk(deark
*c
, de_module_params
*mparams
)
631 d
= de_malloc(c
, sizeof(lctx
));
633 d
->fmt
= (u32
)de_getu32be(0);
635 if(d
->fmt
==CODE_AmBk
) {
636 de_declare_fmt(c
, "AMOS Memory Bank");
638 else if(d
->fmt
==CODE_AmSp
) {
639 de_declare_fmt(c
, "AMOS Sprite Bank");
641 else if(d
->fmt
==CODE_AmIc
) {
642 de_declare_fmt(c
, "AMOS Icon Bank");
644 else if(d
->fmt
==CODE_AmBs
) {
645 de_declare_fmt(c
, "AMOS AmBs format");
648 de_err(c
, "Unsupported format");
652 if(d
->fmt
==CODE_AmBk
||d
->fmt
==CODE_AmSp
|| d
->fmt
==CODE_AmIc
) {
653 do_read_bank(c
, d
, 0, &bytesused
);
655 else if(d
->fmt
==CODE_AmBs
) {
663 static int de_identify_abk(deark
*c
)
668 if(de_input_file_has_ext(c
, "abk")) ext_bonus
=40;
671 if(!de_memcmp(b
, "AmBk", 4))
673 if(!de_memcmp(b
, "AmSp", 4))
675 if(!de_memcmp(b
, "AmIc", 4))
677 if(!de_memcmp(b
, "AmBs", 4))
682 void de_module_abk(deark
*c
, struct deark_module_info
*mi
)
685 mi
->desc
= "AMOS resource (AmBk, sprite, icon, AmBs)";
686 mi
->run_fn
= de_run_abk
;
687 mi
->identify_fn
= de_identify_abk
;
690 static void de_run_amos_source(deark
*c
, de_module_params
*mparams
)
697 d
= de_malloc(c
, sizeof(lctx
));
700 basic_len
= de_getu32be(pos
);
702 de_dbg(c
, "BASIC code at %d, len=%d", (int)pos
, (int)basic_len
);
704 if(pos
>= c
->infile
->len
) goto done
;
705 if(dbuf_memcmp(c
->infile
, pos
, "AmBs", 4)) {
706 de_err(c
, "AmBs segment not found, expected at offset %d", (int)pos
);
710 de_dbg(c
, "AmBs segment at %d", (int)pos
);
711 nbanks
= de_getu16be(pos
+4);
713 de_dbg(c
, "number of banks: %d", (int)nbanks
);
714 if(nbanks
>0 || c
->extract_level
>=2) {
715 dbuf_create_file_from_slice(c
->infile
, pos
, c
->infile
->len
-pos
, "AmBs.abk", NULL
, 0);
718 de_dbg(c
, "not extracting empty AmBs segment");
720 de_dbg_indent(c
, -1);
727 static int de_identify_amos_source(deark
*c
)
732 if(de_input_file_has_ext(c
, "amos")) ext_bonus
=20;
735 if(!de_memcmp(b
, "AMOS Basic", 10))
737 if(!de_memcmp(b
, "AMOS Pro", 8))
742 void de_module_amos_source(deark
*c
, struct deark_module_info
*mi
)
744 mi
->id
= "amos_source";
745 mi
->desc
= "AMOS source code";
746 mi
->run_fn
= de_run_amos_source
;
747 mi
->identify_fn
= de_identify_amos_source
;