exe: Support PAK v1.6 self-extracting archives
[deark.git] / modules / grob.c
blob2ca4ef0fa6ee3d71b4cfb230ab3e9437cbaa6674
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // "GROB" image format for HP48/49 calculators.
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_grob);
11 typedef struct localctx_struct {
12 i64 w, h_phys;
13 i64 bytes_consumed;
14 i64 num_planes;
15 int grayscale_lsb; // Does the plane of least-significant bits come first?
16 } lctx;
18 static void grob_read_binary_bitmap(deark *c, lctx *d, dbuf *inf, i64 pos)
20 i64 h_logical;
21 i64 rowspan;
22 i64 pdwidth;
23 de_bitmap *img = NULL;
24 de_color pal[256];
26 if(d->num_planes<=1) {
27 de_convert_and_write_image_bilevel2(inf, pos, d->w, d->h_phys, (d->w+7)/8,
28 DE_CVTF_WHITEISZERO|DE_CVTF_LSBFIRST, NULL, 0);
29 return;
31 if(d->num_planes>8) goto done;
33 if((d->h_phys % d->num_planes) != 0) {
34 de_warn(c, "Number of rows is not divisible by number of planes. The grob:planes "
35 "setting is probably not correct.");
37 h_logical = d->h_phys/d->num_planes;
39 if(!de_good_image_dimensions(c, d->w, h_logical))
40 goto done;
42 de_dbg(c, "logical dimensions: %d"DE_CHAR_TIMES"%d", (int)d->w, (int)h_logical);
44 rowspan = (d->w+7)/8;
45 pdwidth = rowspan * 8;
46 img = de_bitmap_create2(c, d->w, pdwidth, h_logical, 1);
48 de_zeromem(pal, sizeof(pal));
49 de_make_grayscale_palette(pal, 1ULL<<d->num_planes, 0x1);
51 de_convert_image_paletted_planar(inf, pos, d->num_planes,
52 rowspan, h_logical*rowspan, pal, img, 0x1 | (d->grayscale_lsb?0x02:0));
54 de_bitmap_write_to_file_finfo(img, NULL, 0);
55 done:
56 de_bitmap_destroy(img);
59 static void de_run_grob_binary(deark *c, lctx *d)
61 u8 hdr[18];
62 i64 obj_id;
63 i64 length;
65 de_declare_fmt(c, "HP GROB, binary encoded");
67 de_read(hdr, 0, 18);
69 // Next 4 fields are packed 20-bit integers, 2.5 bytes each.
71 obj_id = (hdr[10]&0x0f)<<16 | hdr[9]<<8 | hdr[8];
72 length = hdr[12]<<12 | hdr[11]<<4 | hdr[10]>>4;
73 de_dbg(c, "object id: 0x%05x", (unsigned int)obj_id);
74 if(obj_id != 0x02b1e) {
75 de_warn(c, "Unexpected object identifier (0x%05x, expected 0x02b1e)", (unsigned int)obj_id);
77 de_dbg(c, "object length in nibbles: %d", (int)length);
79 d->h_phys = (hdr[15]&0x0f)<<16 | hdr[14]<<8 | hdr[13];
80 d->w = hdr[17]<<12 | hdr[16]<<4 | hdr[15]>>4;
81 de_dbg(c, "%sdimensions: %d"DE_CHAR_TIMES"%d", (d->num_planes==1)?"":"physical ",
82 (int)d->w, (int)d->h_phys);
84 grob_read_binary_bitmap(c, d, c->infile, 18);
87 // On return, sets d->bytes_consumed
88 static void grob_text_1_image(deark *c, lctx *d, i64 pos1)
90 i64 data_start;
91 u8 x;
92 u8 b0, b1;
93 i64 pos;
94 dbuf *bin_bmp = NULL; // Binary version of the bitmap
96 pos = pos1;
98 d->w = 0;
99 d->h_phys = 0;
101 // We assume the GROB text format starts with
102 // "GROB" <zero or more spaces> <width> <one or more spaces>
103 // <height> <one or more spaces> <data>.
105 // TODO: This parser is pretty clumsy.
107 pos += 4; // Skip over "GROB"
109 while(de_getbyte(pos)==' ')
110 pos++;
111 while((x=de_getbyte(pos))!=' ') {
112 d->w = d->w*10 + (x-'0');
113 pos++;
116 while(de_getbyte(pos)==' ')
117 pos++;
118 while((x=de_getbyte(pos))!=' ') {
119 d->h_phys = d->h_phys*10 + (x-'0');
120 pos++;
123 while(de_getbyte(pos)==' ')
124 pos++;
125 data_start = pos;
127 de_dbg(c, "%sdimensions: %d"DE_CHAR_TIMES"%d", (d->num_planes==1)?"":"physical ",
128 (int)d->w, (int)d->h_phys);
130 // FIXME: This should really be testing the logical height, not the
131 // physical height.
132 if(!de_good_image_dimensions(c, d->w, d->h_phys))
133 goto done;
135 // Decode the quasi-hex-encoded data into a memory buffer, then use the
136 // same decoder as for binary format.
138 bin_bmp = dbuf_create_membuf(c, d->h_phys * (d->w+7)/8, 0);
140 pos = data_start;
141 while(pos < c->infile->len) {
142 b0 = de_getbyte(pos);
143 b1 = de_getbyte(pos+1);
144 if(b0<48 || b1<48) {
145 // Apparently, we've reached the end of the bitmap data.
146 break;
149 pos+=2;
151 x = de_decode_hex_digit(b0,NULL) | (de_decode_hex_digit(b1,NULL)<<4);
152 dbuf_writebyte(bin_bmp, x);
155 d->bytes_consumed = pos - pos1;
157 grob_read_binary_bitmap(c, d, bin_bmp, 0);
159 done:
160 dbuf_close(bin_bmp);
163 static void de_run_grob_text(deark *c, lctx *d)
165 i64 pos;
166 i64 img_pos = 0;
167 int ret;
168 i64 img_count = 0;
170 de_declare_fmt(c, "HP GROB, text encoded");
172 // Though some text GROB files begin with "GROB", we also want to support files
173 // that have "%%HP" headers, and other files that have one or more GROB data objects
174 // embedded in them.
176 pos = 0;
178 while(pos < c->infile->len) {
179 // TODO: Ideally, we should be more careful about what we search for.
180 // Maybe we should make sure "GROB" is the first nonwhitespace on the line,
181 // but even that isn't enough.
183 ret = dbuf_search(c->infile, (const u8*)"GROB", 4, pos, c->infile->len-pos, &img_pos);
184 if(!ret) {
185 // No more images in this file.
186 break;
189 de_dbg(c, "GROB format found at %d", (int)img_pos);
191 img_count++;
192 if(!de_good_image_count(c, img_count)) break;
193 grob_text_1_image(c, d, img_pos);
195 if(d->bytes_consumed<1) break;
196 pos = img_pos + d->bytes_consumed;
199 if(img_count==0) {
200 de_err(c, "Unknown or unsupported GROB format");
204 static void de_run_grob(deark *c, de_module_params *mparams)
206 lctx *d = NULL;
207 u8 buf[4];
208 const char *s;
210 d = de_malloc(c, sizeof(lctx));
212 s = de_get_ext_option(c, "grob:planes");
213 if(s) {
214 d->num_planes = de_atoi64(s);
216 if(d->num_planes<1) d->num_planes=1;
217 if(d->num_planes>8) {
218 de_err(c, "Unsupported grob:planes option");
219 goto done;
222 s = de_get_ext_option(c, "grob:planeorder");
223 if(s && s[0]=='l') {
224 d->grayscale_lsb = 1;
227 de_read(buf, 0, 4);
229 if(!de_memcmp(buf, "HPHP", 4)) {
230 de_run_grob_binary(c, d);
232 else {
233 de_run_grob_text(c, d);
236 done:
237 de_free(c, d);
240 static int de_identify_grob(deark *c)
242 u8 buf[10];
243 de_read(buf, 0, 10);
245 if(buf[0]=='H' && buf[1]=='P' && buf[2]=='H' && buf[3]=='P' &&
246 buf[4]=='4' && (buf[5]=='8' || buf[5]=='9') &&
247 buf[8]==0x1e && buf[9]==0x2b)
249 return 100;
252 if(buf[0]=='G' && buf[1]=='R' && buf[2]=='O' && buf[3]=='B') {
253 return 90;
256 return 0;
259 static void de_help_grob(deark *c)
261 de_msg(c, "-opt grob:planes=<n> : Treat image as grayscale");
262 de_msg(c, "-opt grob:planeorder=l : Least-significant plane comes first");
265 void de_module_grob(deark *c, struct deark_module_info *mi)
267 mi->id = "grob";
268 mi->desc = "GROB - HP48/49 calculator image";
269 mi->run_fn = de_run_grob;
270 mi->identify_fn = de_identify_grob;
271 mi->help_fn = de_help_grob;