fnt: Improved error handling, etc.
[deark.git] / modules / adex.c
blobdaa3f1565155f92cdf6c1163b206d88ec80a759c
1 // This file is part of Deark.
2 // Copyright (C) 2022 Jason Summers
3 // See the file COPYING for terms of use.
5 // ADEX .img/.rle
7 #include <deark-private.h>
8 DE_DECLARE_MODULE(de_module_adex);
10 // We follow Image Alchemy's implementation of ADEX, which is different from
11 // XnView's.
12 // (I wouldn't want to bet that either of them get it exactly right.)
14 struct adexctx {
15 u8 cmpr_meth;
16 u8 bpp; // bits per pixel
17 i64 npwidth, pdwidth, h;
18 i64 rowspan;
19 i64 expected_imgsize; // uncompressed size in bytes
20 i64 palentries;
21 de_color pal[256];
24 // TODO: Make de_convert_image_paletted() support this.
25 static void convert_image_pal4_lsb(dbuf *f, i64 fpos,
26 i64 rowspan, const de_color *pal, de_bitmap *img)
28 i64 i, j;
30 for(j=0; j<img->height; j++) {
31 for(i=0; i<rowspan; i++) {
32 UI palent;
33 u8 x;
35 x = dbuf_getbyte(f, fpos+rowspan*j+i);
36 palent = x & 0x0f;
37 de_bitmap_setpixel_rgba(img, i*2, j, pal[palent]);
38 palent = x >> 4;
39 de_bitmap_setpixel_rgba(img, i*2+1, j, pal[palent]);
44 // I'm not sure how well I understand RLE format. It has a lot of extra "space"
45 // for more features to exist.
46 // So we go to some effort to fail if something unexpected happens -- otherwise
47 // this code could be much shorter.
48 // The format as I know it is very simple:
49 // Treat the format as byte-oriented (it decompressed to bytes, not pixels).
50 // Read 4 bytes at a time.
51 // First two bytes are a repeat-count.
52 // Last two bytes are the bytes to repeat.
53 // Note: Each row ends with an item with repeat-count=0.
54 static int adex_decompress_rle(deark *c, struct adexctx *d, i64 pos1, dbuf *outf)
56 i64 nbytes_written = 0;
57 int retval = 0;
58 i64 pos = pos1;
59 i64 nbytes_this_row = 0;
61 while(1) {
62 i64 count;
63 i64 k;
64 u8 buf[2];
66 if(nbytes_written >= d->expected_imgsize) break;
67 if(pos > c->infile->len) {
68 goto done;
70 if(nbytes_this_row > d->rowspan) {
71 goto done;
74 count = de_getu16le_p(&pos);
75 de_read(buf, pos, 2);
76 pos += 2;
78 if(count==0) { // end-of-row marker
79 if(nbytes_this_row != d->rowspan) {
80 goto done;
82 nbytes_this_row = 0;
84 else {
85 for(k=0; k<count; k++) {
86 dbuf_write(outf, buf, 2);
88 nbytes_written += 2*count;
89 nbytes_this_row += 2*count;
92 retval = 1;
94 done:
95 if(!retval) {
96 de_err(c, "RLE decompression failed");
98 return retval;
101 static void de_run_adex(deark *c, de_module_params *mparams)
103 struct adexctx *d = NULL;
104 i64 pos;
105 de_bitmap *img = NULL;
106 dbuf *unc_pixels = NULL;
107 int need_errmsg = 0;
109 d = de_malloc(c, sizeof(struct adexctx));
110 pos = 4;
111 d->cmpr_meth = de_getbyte_p(&pos);
112 de_dbg(c, "cmpr meth: %u", (UI)d->cmpr_meth);
113 d->bpp = de_getbyte_p(&pos);
114 de_dbg(c, "bits/pixel: %u", (UI)d->bpp);
115 d->npwidth = de_getu16le_p(&pos);
116 d->h = de_getu16le_p(&pos);
117 de_dbg_dimensions(c, d->npwidth, d->h);
118 d->palentries = de_getu16le_p(&pos);
119 de_dbg(c, "palette entries: %u", (UI)d->palentries);
121 if(d->palentries>256) { need_errmsg = 1; goto done; }
122 if(d->palentries==0) {
123 // This is likely legal, but we'd have to get the palette from a different file
124 need_errmsg = 1;
125 goto done;
127 de_read_simple_palette(c, c->infile, pos, d->palentries, 3, d->pal, 256,
128 DE_RDPALTYPE_24BIT, 0);
129 pos += 3*d->palentries;
130 pos += 2; // After the palette are two bytes of unknown purpose.
132 de_dbg(c, "image data at %"I64_FMT, pos);
134 if(d->cmpr_meth>1) { need_errmsg = 1; goto done; }
135 if(d->bpp!=4 && d->bpp!=8) { need_errmsg = 1; goto done; }
136 if(!de_good_image_dimensions(c, d->npwidth, d->h)) goto done;
138 d->rowspan = de_pad_to_n(d->npwidth*d->bpp, 16)/8;
139 d->pdwidth = (d->rowspan*d->bpp)/2;
140 d->expected_imgsize = d->h * d->rowspan;
142 if(d->cmpr_meth==1) {
143 unc_pixels = dbuf_create_membuf(c, d->expected_imgsize, 0x1);
144 dbuf_enable_wbuffer(unc_pixels);
145 if(!adex_decompress_rle(c, d, pos, unc_pixels)) goto done;
146 dbuf_flush(unc_pixels);
148 else {
149 unc_pixels = dbuf_open_input_subfile(c->infile, pos, c->infile->len - pos);
152 img = de_bitmap_create2(c, d->npwidth, d->pdwidth, d->h, 3);
153 if(d->bpp==4) {
154 convert_image_pal4_lsb(unc_pixels, 0, d->rowspan, d->pal, img);
156 else {
157 de_convert_image_paletted(unc_pixels, 0, d->bpp, d->rowspan, d->pal, img, 0);
160 de_bitmap_write_to_file(img, NULL, DE_CREATEFLAG_FLIP_IMAGE);
161 done:
162 de_bitmap_destroy(img);
163 dbuf_close(unc_pixels);
164 if(need_errmsg) {
165 de_err(c, "Bad or unsupported image type");
167 de_free(c, d);
170 static int de_identify_adex(deark *c)
172 u8 b;
174 if((u32)de_getu32be(0)!=0x50494354U) return 0; // "PICT"
175 b = de_getbyte(4);
176 if(b!=0 && b!=1) return 0;
177 b = de_getbyte(5);
178 if(b!=4 && b!=8) return 0;
179 return 65;
182 void de_module_adex(deark *c, struct deark_module_info *mi)
184 mi->id = "adex";
185 mi->desc = "ADEX IMG/RLE";
186 mi->run_fn = de_run_adex;
187 mi->identify_fn = de_identify_adex;