fnt: Improved error handling, etc.
[deark.git] / modules / rosprite.c
blob0d7d8e393a28ac95160222068b1ce393157f45bc
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // Acorn Sprite / RISC OS Sprite
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_rosprite);
11 struct old_mode_info {
12 u8 mode;
13 u8 fgbpp;
14 u8 xdpi;
15 u8 ydpi;
17 // Screen mode list at: http://www.riscos.com/support/users/userguide3/book3b/book3_17.html
18 // TODO: Find reliable information about DPI fields.
19 static const struct old_mode_info old_mode_info_arr[] = {
20 {0, 1, 90, 45},
21 {1, 2, 45, 45},
22 {2, 4, 0, 0},
23 {4, 1, 45, 45},
24 {5, 2, 0, 0},
25 {8, 2, 90, 45},
26 {9, 4, 45, 45},
27 {10, 8, 0, 0},
28 {11, 2, 0, 0},
29 {12, 4, 90, 45},
30 {13, 8, 45, 45},
31 {14, 4, 0, 0},
32 {15, 8, 90, 45},
33 {16, 4, 0, 0},
34 {17, 4, 0, 0},
35 {18, 1, 90, 90},
36 {19, 2, 90, 90},
37 {20, 4, 90, 90},
38 {21, 8, 90, 90},
39 {22, 4, 0, 0},
40 {23, 1, 0, 0},
41 {24, 8, 0, 0},
42 {25, 1, 0, 0},
43 {26, 2, 0, 0},
44 {27, 4, 90, 90},
45 {28, 8, 90, 90},
46 {29, 1, 0, 0},
47 {30, 2, 0, 0},
48 {31, 4, 90, 90},
49 {32, 8, 90, 90},
50 {33, 1, 0, 0},
51 {34, 2, 0, 0},
52 {35, 4, 0, 0},
53 {36, 8, 90, 45},
54 {37, 1, 0, 0},
55 {38, 2, 0, 0},
56 {39, 4, 0, 0},
57 {40, 8, 0, 0},
58 {41, 1, 0, 0},
59 {42, 2, 0, 0},
60 {43, 4, 0, 0},
61 {44, 1, 0, 0},
62 {45, 2, 0, 0},
63 {46, 4, 0, 0},
64 {47, 8, 0, 0},
65 {48, 4, 0, 0},
66 {49, 8, 0, 0},
68 // I have some mode-107 files, but I don't know how standard this is.
69 {107, 16, 0, 0}
72 struct page_ctx {
73 i64 fgbpp;
74 i64 maskbpp;
75 i64 width_in_words;
76 i64 first_bit, last_bit;
77 i64 npwidth, height;
78 i64 width_1; // Width of intermediate bitmap. Includes left-padding, but not right-padding.
79 i64 pdwidth;
80 i64 mask_width_1;
81 i64 num_padding_pixels_at_start_of_row;
82 i64 num_padding_pixels_at_end_of_row;
83 i64 xdpi, ydpi;
84 u32 mode;
85 UI new_img_type; // 0 if old format
86 u8 has_mask;
87 u8 use_mask;
88 #define MASK_TYPE_OLD 1 // Binary transparency, fgbpp bits/pixel
89 #define MASK_TYPE_NEW_1 2 // Binary transparency, 8 bits/pixel
90 #define MASK_TYPE_NEW_8 3 // Alpha transparency, 8 bits/pixel
91 int mask_type;
92 i64 mask_rowspan;
93 i64 image_offset;
94 i64 mask_offset;
96 de_bitmap *img;
97 de_bitmap *mask;
99 int has_custom_palette;
100 i64 custom_palette_pos;
101 i64 custom_palette_ncolors;
102 de_color pal[256];
103 de_color maskpal[256];
106 typedef struct localctx_struct {
107 i64 num_images;
108 } lctx;
110 static const de_color pal4[4] = {
111 0xffffffffU,0xffbbbbbbU,0xff777777U,0xff000000U
114 static de_color getpal4(int k)
116 if(k<0 || k>3) return 0;
117 return pal4[k];
120 static const de_color pal16[16] = {
121 0xffffffffU,0xffddddddU,0xffbbbbbbU,0xff999999U,0xff777777U,0xff555555U,0xff333333U,0xff000000U,
122 0xff4499ffU,0xffeeee00U,0xff00cc00U,0xffdd0000U,0xffeeeebbU,0xff558800U,0xffffbb00U,0xff00bbffU
125 static de_color getpal16(int k)
127 if(k<0 || k>15) return 0;
128 return pal16[k];
131 static de_color getpal256(int k)
133 u8 r, g, b;
134 if(k<0 || k>255) return 0;
135 r = k%8 + ((k%32)/16)*8;
136 g = k%4 + ((k%128)/32)*4;
137 b = (u8)(k%4 + ((k%16)/8)*4 + (k/128)*8);
138 r = (r<<4)|r;
139 g = (g<<4)|g;
140 b = (b<<4)|b;
141 return DE_MAKE_RGB(r,g,b);
144 static void remove_left_padding(deark *c, struct page_ctx *pg)
146 de_bitmap *imgtmp;
148 if(pg->num_padding_pixels_at_start_of_row<1) return;
150 // Create a replacement bitmap, and copy the important pixels from the old
151 // one to it.
152 imgtmp = de_bitmap_create(c, pg->npwidth, pg->height, pg->img->bytes_per_pixel);
153 de_bitmap_copy_rect(pg->img, imgtmp, pg->num_padding_pixels_at_start_of_row, 0,
154 pg->npwidth, pg->height, 0, 0, 0);
156 de_bitmap_destroy(pg->img);
157 pg->img = imgtmp;
158 imgtmp = NULL;
161 static void convert_image_16bit(deark *c, lctx *d, struct page_ctx *pg, de_bitmap *img)
163 i64 i, j;
165 for(j=0; j<pg->height; j++) {
166 for(i=0; i<pg->width_1; i++) {
167 de_color clr;
169 clr = (de_color)de_getu16le(pg->image_offset + 4*pg->width_in_words*j + i*2);
170 clr = de_bgr555_to_888(clr);
171 de_bitmap_setpixel_rgba(pg->img, i, j, clr);
176 static void do_image(deark *c, lctx *d, struct page_ctx *pg, de_finfo *fi)
178 int is_grayscale;
179 int bypp;
181 if(pg->fgbpp<=8) {
182 is_grayscale = de_is_grayscale_palette(pg->pal, ((i64)1)<<pg->fgbpp);
184 else {
185 is_grayscale = 0;
188 bypp = is_grayscale?1:3;
189 if(pg->has_mask) bypp++;
191 if(pg->mask_type==MASK_TYPE_OLD) {
192 pg->mask_width_1 = pg->width_1;
194 else if(pg->mask_type==MASK_TYPE_NEW_1 || pg->mask_type==MASK_TYPE_NEW_8) {
195 pg->mask_width_1 = pg->npwidth;
198 pg->img = de_bitmap_create(c, pg->width_1, pg->height, bypp);
200 if(pg->xdpi>0) {
201 fi->density.code = DE_DENSITY_DPI;
202 fi->density.xdens = (double)pg->xdpi;
203 fi->density.ydens = (double)pg->ydpi;
206 de_dbg(c, "image data at %"I64_FMT, pg->image_offset);
208 if(pg->fgbpp==32) {
209 de_convert_image_rgb(c->infile, pg->image_offset, 4*pg->width_in_words, 4, pg->img, 0);
211 else if(pg->fgbpp==16) {
212 convert_image_16bit(c, d, pg, pg->img);
214 else {
215 de_convert_image_paletted(c->infile, pg->image_offset, pg->fgbpp, 4*pg->width_in_words,
216 pg->pal, pg->img, 0x1);
219 if(pg->has_mask && pg->use_mask) {
220 de_dbg(c, "transparency mask at %"I64_FMT, pg->mask_offset);
222 if(pg->maskbpp<1 || pg->maskbpp>8) {
223 de_warn(c, "This type of transparency mask is not supported");
224 goto after_mask;
227 pg->mask = de_bitmap_create(c, pg->mask_width_1, pg->height, 1);
229 // Make a palette to use with de_convert_image_paletted().
230 if(pg->mask_type==MASK_TYPE_NEW_8) {
231 de_make_grayscale_palette(pg->maskpal, 256, 0);
233 else {
234 // Supposedly, anything nonzero is opaque.
235 de_memset(pg->maskpal, 0xff, sizeof(pg->maskpal));
236 pg->maskpal[0] = DE_STOCKCOLOR_BLACK;
239 // Start with an opaque mask.
240 de_bitmap_rect(pg->mask, 0, 0, pg->mask->width, pg->mask->height, DE_STOCKCOLOR_WHITE, 0);
242 de_convert_image_paletted(c->infile, pg->mask_offset, pg->maskbpp, pg->mask_rowspan,
243 pg->maskpal, pg->mask, 0x1);
245 de_bitmap_apply_mask(pg->img, pg->mask, 0);
246 de_bitmap_destroy(pg->mask);
247 pg->mask = NULL;
249 after_mask:
251 if(pg->num_padding_pixels_at_start_of_row>0 && !c->padpix) {
252 remove_left_padding(c, pg);
255 de_bitmap_write_to_file_finfo(pg->img, fi, DE_CREATEFLAG_OPT_IMAGE);
258 static de_color average_color(de_color c1, de_color c2)
260 u8 a, r, g, b;
261 a = ((de_color)DE_COLOR_A(c1) + DE_COLOR_A(c2))/2;
262 r = ((de_color)DE_COLOR_R(c1) + DE_COLOR_R(c2))/2;
263 g = ((de_color)DE_COLOR_G(c1) + DE_COLOR_G(c2))/2;
264 b = ((de_color)DE_COLOR_B(c1) + DE_COLOR_B(c2))/2;
265 return DE_MAKE_RGBA(r,g,b,a);
268 static void do_setup_palette(deark *c, lctx *d, struct page_ctx *pg)
270 i64 k;
271 de_color clr1, clr2, clr3;
273 if(pg->fgbpp>8) {
274 return;
277 if(pg->has_custom_palette) {
278 de_dbg(c, "custom palette at %d, %d entries", (int)pg->custom_palette_pos,
279 (int)pg->custom_palette_ncolors);
281 de_dbg_indent(c, 1);
283 for(k=0; k<256; k++) {
284 if(pg->has_custom_palette) {
285 if(k<pg->custom_palette_ncolors) {
286 // Each palette entry has two colors, which are usually but not always
287 // the same.
288 // TODO: Figure out what to do if they are different. For now, we'll
289 // average them.
290 clr1 = dbuf_getRGB(c->infile, pg->custom_palette_pos + 8*k + 1, 0);
291 clr2 = dbuf_getRGB(c->infile, pg->custom_palette_pos + 8*k + 4 + 1, 0);
292 if(clr1==clr2) {
293 pg->pal[k] = clr1;
294 de_dbg_pal_entry(c, k, clr1);
296 else {
297 char tmps[64];
299 clr3 = average_color(clr1, clr2);
300 pg->pal[k] = clr3;
301 de_snprintf(tmps, sizeof(tmps), "(%3d,%3d,%3d),(%3d,%3d,%3d) "DE_CHAR_RIGHTARROW" ",
302 (int)DE_COLOR_R(clr1), (int)DE_COLOR_G(clr1), (int)DE_COLOR_B(clr1),
303 (int)DE_COLOR_R(clr2), (int)DE_COLOR_G(clr2), (int)DE_COLOR_B(clr2));
304 de_dbg_pal_entry2(c, k, clr3, tmps, NULL, NULL);
307 else {
308 pg->pal[k] = getpal256((int)k);
311 else if(pg->fgbpp==4 && k<16) {
312 pg->pal[k] = getpal16((int)k);
314 else if(pg->fgbpp==2 && k<4) {
315 pg->pal[k] = getpal4((int)k);
317 else if(pg->fgbpp==1 && k<2) {
318 pg->pal[k] = (k==0)?DE_STOCKCOLOR_WHITE:DE_STOCKCOLOR_BLACK;
320 else {
321 pg->pal[k] = getpal256((int)k);
325 de_dbg_indent(c, -1);
328 static void read_sprite_name(deark *c, lctx *d, de_finfo *fi, i64 pos)
330 de_ucstring *s = NULL;
331 if(c->debug_level<1 && !c->filenames_from_file) return;
333 s = ucstring_create(c);
334 dbuf_read_to_ucstring(c->infile, pos, 12, s, DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_RISCOS);
335 de_dbg(c, "sprite name: \"%s\"", ucstring_getpsz(s));
337 if(c->filenames_from_file) {
338 de_finfo_set_name_from_ucstring(c, fi, s, 0);
341 ucstring_destroy(s);
344 static void do_sprite(deark *c, lctx *d, i64 index,
345 i64 pos1, i64 len)
347 de_finfo *fi = NULL;
348 struct page_ctx *pg = NULL;
349 i64 image_offset_raw, mask_offset_raw;
350 i64 pos;
351 int saved_indent_level;
352 char descr[32];
354 de_dbg_indent_save(c, &saved_indent_level);
355 pg = de_malloc(c, sizeof(struct page_ctx));
357 de_dbg(c, "image header at %"I64_FMT, pos1);
358 de_dbg_indent(c, 1);
360 fi = de_finfo_create(c);
362 pos = pos1+4;
363 read_sprite_name(c, d, fi, pos);
364 pos += 12;
366 pg->width_in_words = de_getu32le_p(&pos) +1;
367 pg->height = de_getu32le_p(&pos) +1;
368 de_dbg(c, "width in words: %"I64_FMT, pg->width_in_words);
369 de_dbg(c, "height: %"I64_FMT, pg->height);
371 pg->first_bit = de_getu32le_p(&pos);
372 if(pg->first_bit>31) pg->first_bit=31;
373 pg->last_bit = de_getu32le_p(&pos);
374 if(pg->last_bit>31) pg->last_bit=31;
375 de_dbg(c, "first bit: %u", (UI)pg->first_bit);
376 de_dbg(c, "last bit: %u", (UI)pg->last_bit);
378 image_offset_raw = de_getu32le_p(&pos);
379 pg->image_offset = pos1 + image_offset_raw;
380 de_dbg(c, "image offset: %"I64_FMT" ("DE_CHAR_RIGHTARROW"%"I64_FMT")",
381 image_offset_raw, pg->image_offset);
383 mask_offset_raw = de_getu32le_p(&pos);
384 pg->mask_offset = pos1 + mask_offset_raw;
385 if(mask_offset_raw && (mask_offset_raw!=image_offset_raw)) {
386 pg->has_mask = 1;
387 pg->use_mask = 1; // Default
389 if(pg->has_mask) {
390 de_snprintf(descr, sizeof(descr), DE_CHAR_RIGHTARROW"%"I64_FMT,
391 pg->mask_offset);
393 else {
394 de_strlcpy(descr, "no mask", sizeof(descr));
396 de_dbg(c, "mask offset: %"I64_FMT" (%s)", mask_offset_raw, descr);
398 pg->mode = (u32)de_getu32le_p(&pos);
399 de_dbg(c, "mode: 0x%08x", (unsigned int)pg->mode);
401 de_dbg_indent(c, 1);
403 pg->new_img_type = (pg->mode&0x78000000U)>>27;
404 de_dbg(c, "format version: %s", (pg->new_img_type?"new":"old"));
405 if(pg->new_img_type==0)
406 de_dbg(c, "old format screen mode: %u", (UI)pg->mode);
407 else
408 de_dbg(c, "new format image type: %u", (UI)pg->new_img_type);
410 if(pg->new_img_type!=0 && pg->first_bit!=0) {
411 de_warn(c, "Invalid \"first bit\" value for new image format");
412 pg->first_bit = 0;
415 if(pg->new_img_type==0) {
416 // old format
417 size_t x;
419 for(x=0; x<DE_ARRAYCOUNT(old_mode_info_arr); x++) {
420 if(pg->mode == (u32)old_mode_info_arr[x].mode) {
421 pg->fgbpp = (i64)old_mode_info_arr[x].fgbpp;
422 pg->xdpi = (i64)old_mode_info_arr[x].xdpi;
423 pg->ydpi = (i64)old_mode_info_arr[x].ydpi;
424 break;
428 if(pg->fgbpp==0) {
429 de_err(c, "Screen mode %u not supported", (UI)pg->mode);
430 goto done;
433 if(pg->has_mask) {
434 pg->mask_type = MASK_TYPE_OLD;
435 pg->mask_rowspan = 4*pg->width_in_words;
436 pg->maskbpp = pg->fgbpp;
437 de_dbg(c, "mask type: old");
440 if(pg->fgbpp>8 && pg->has_mask) {
441 de_warn(c, "Transparency not supported for this image type");
442 pg->use_mask = 0;
446 else {
447 // new format
448 pg->xdpi = (pg->mode&0x07ffc000)>>14;
449 pg->ydpi = (pg->mode&0x00003ffe)>>1;
450 de_dbg(c, "dpi: %d"DE_CHAR_TIMES"%d", (int)pg->xdpi, (int)pg->ydpi);
451 switch(pg->new_img_type) {
452 case 1: // ->1
453 case 2: // ->2
454 case 3: // ->4
455 case 4: // ->8
456 case 5: // ->16
457 case 6: // ->32
458 pg->fgbpp = 1LL<<(pg->new_img_type-1);
459 break;
460 //case 7: 32bpp CMYK (TODO)
461 //case 8: 24bpp (TODO)
462 default:
463 de_err(c, "New format type %u not supported", (UI)pg->new_img_type);
464 goto done;
467 if(pg->has_mask) {
468 if(pg->mode&0x80000000U) {
469 pg->mask_type = MASK_TYPE_NEW_8;
470 pg->maskbpp = 8;
472 else {
473 pg->mask_type = MASK_TYPE_NEW_1;
474 pg->maskbpp = 1;
476 de_dbg(c, "mask type: new - %s", pg->mask_type==MASK_TYPE_NEW_8 ? "alpha" : "binary");
480 de_dbg(c, "foreground bits/pixel: %d", (int)pg->fgbpp);
482 de_dbg_indent(c, -1);
484 pg->pdwidth = (pg->width_in_words * 32)/pg->fgbpp;
485 pg->num_padding_pixels_at_start_of_row = pg->first_bit / pg->fgbpp;
486 pg->num_padding_pixels_at_end_of_row = (32 - (pg->last_bit+1)) / pg->fgbpp;
487 pg->npwidth = pg->pdwidth - pg->num_padding_pixels_at_start_of_row -
488 pg->num_padding_pixels_at_end_of_row;
489 if(c->padpix) {
490 pg->width_1 = pg->pdwidth;
492 else {
493 pg->width_1 = pg->pdwidth - pg->num_padding_pixels_at_end_of_row;
495 de_dbg(c, "width (calculated): %"I64_FMT, pg->npwidth);
497 if(!de_good_image_dimensions(c, pg->npwidth, pg->height)) goto done;
498 if(pg->width_1<1) goto done;
500 if(pg->mask_type==MASK_TYPE_NEW_1 || pg->mask_type==MASK_TYPE_NEW_8) {
501 pg->mask_rowspan = de_pad_to_n(pg->maskbpp*pg->npwidth, 32) / 8;
504 de_dbg_indent(c, -1);
506 pg->custom_palette_pos = pos1 + 44;
507 if(pg->image_offset >= pg->custom_palette_pos+8 && pg->fgbpp<=8) {
508 pg->has_custom_palette = 1;
509 pg->custom_palette_ncolors = (pg->image_offset - (pos1+44))/8;
510 if(pg->custom_palette_ncolors>256) pg->custom_palette_ncolors=256;
513 do_setup_palette(c, d, pg);
515 do_image(c, d, pg, fi);
516 done:
517 de_dbg_indent_restore(c, saved_indent_level);
518 de_finfo_destroy(c, fi);
519 if(pg) {
520 if(pg->img) de_bitmap_destroy(pg->img);
521 if(pg->mask) de_bitmap_destroy(pg->mask);
522 de_free(c, pg);
526 static void de_run_rosprite(deark *c, de_module_params *mparams)
528 lctx *d = NULL;
529 i64 pos;
530 i64 sprite_size;
531 i64 first_sprite_offset_raw;
532 i64 first_sprite_offset;
533 i64 implied_file_size_raw;
534 i64 implied_file_size;
535 i64 k;
536 i64 sprite_count = 0;
538 d = de_malloc(c, sizeof(lctx));
540 pos = 0;
541 d->num_images = de_getu32le_p(&pos);
542 de_dbg(c, "number of images: %"I64_FMT, d->num_images);
543 first_sprite_offset_raw = de_getu32le_p(&pos);
544 first_sprite_offset = first_sprite_offset_raw - 4;
545 de_dbg(c, "first sprite offset: %"I64_FMT" ("DE_CHAR_RIGHTARROW"%"I64_FMT")",
546 first_sprite_offset_raw, first_sprite_offset);
547 implied_file_size_raw = de_getu32le_p(&pos);
548 implied_file_size = implied_file_size_raw - 4;
549 de_dbg(c, "reported file size: %"I64_FMT" ("DE_CHAR_RIGHTARROW"%"I64_FMT")",
550 implied_file_size_raw, implied_file_size);
551 if(implied_file_size != c->infile->len) {
552 de_warn(c, "Reported and actual file sizes differ "
553 "(%"I64_FMT", %"I64_FMT")", implied_file_size, c->infile->len);
556 pos = first_sprite_offset;
557 for(k=0; k<d->num_images; k++) {
558 if(pos>=c->infile->len) goto done;
559 sprite_size = de_getu32le(pos);
560 de_dbg(c, "image #%d at %"I64_FMT", size=%"I64_FMT, (int)k, pos, sprite_size);
561 if(sprite_size<1) goto done;
562 // We intentionally allow sprite_size to be set wrong, because
563 // such files exist, and we don't need it for *this* sprite.
564 de_dbg_indent(c, 1);
565 do_sprite(c, d, k, pos, sprite_size);
566 sprite_count++;
567 de_dbg_indent(c, -1);
568 if(sprite_size<40) goto done;
569 pos += sprite_size;
572 done:
573 if(sprite_count < d->num_images) {
574 de_warn(c, "Expected %"I64_FMT" images, only found %"I64_FMT,
575 d->num_images, sprite_count);
577 de_free(c, d);
580 static int de_identify_rosprite(deark *c)
582 i64 num_images, first_sprite_offset, implied_file_size;
583 i64 offset2;
585 num_images = de_getu32le(0);
586 if(num_images<1 || num_images>1000) return 0;
588 first_sprite_offset = de_getu32le(4) - 4;
589 // I've only ever seen this be 12 or (rarely) 28, though it's legal
590 // for it to have other values
591 if(first_sprite_offset!=12 && first_sprite_offset!=28) return 0;
592 if(first_sprite_offset+48 > c->infile->len) return 0;
594 implied_file_size = de_getu32le(8) - 4;
595 if(implied_file_size > c->infile->len) return 0;
597 if(num_images!=1) {
598 // TODO?: A multi-sprite file with extra junk at EOF will not be detected.
599 // To better detect multi-sprite files, we'd probably have to walk through
600 // the file.
601 if(implied_file_size != c->infile->len) return 0;
602 return 55;
605 offset2 = de_getu32le(first_sprite_offset);
606 if(offset2==implied_file_size && implied_file_size==c->infile->len) {
607 // Found some bad files where this pointer is absolute when it should be
608 // relative.
609 return 15;
611 offset2 += first_sprite_offset;
612 if(offset2 != implied_file_size) return 0;
613 if(implied_file_size == c->infile->len) return 80;
614 return 30;
617 void de_module_rosprite(deark *c, struct deark_module_info *mi)
619 mi->id = "rosprite";
620 mi->desc = "RISC OS Sprite, a.k.a. Acorn Sprite";
621 mi->run_fn = de_run_rosprite;
622 mi->identify_fn = de_identify_rosprite;