nrg: Improved support for v2
[deark.git] / modules / grob.c
blobc852cd48f6cc92c2ce3a7638141f5b2960af5195
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 i, j;
22 i64 plane;
23 i64 rowspan;
24 i64 pdwidth;
25 u8 b;
26 unsigned int v;
27 u8 v2;
28 de_bitmap *img = NULL;
30 if(d->num_planes<=1) {
31 de_convert_and_write_image_bilevel2(inf, pos, d->w, d->h_phys, (d->w+7)/8,
32 DE_CVTF_WHITEISZERO|DE_CVTF_LSBFIRST, NULL, 0);
33 return;
36 if((d->h_phys % d->num_planes) != 0) {
37 de_warn(c, "Number of rows is not divisible by number of planes. The grob:planes "
38 "setting is probably not correct.");
40 h_logical = d->h_phys/d->num_planes;
42 if(!de_good_image_dimensions(c, d->w, h_logical))
43 goto done;
45 de_dbg(c, "logical dimensions: %d"DE_CHAR_TIMES"%d", (int)d->w, (int)h_logical);
47 rowspan = (d->w+7)/8;
48 pdwidth = rowspan * 8;
49 img = de_bitmap_create2(c, d->w, pdwidth, h_logical, 1);
51 for(j=0; j<h_logical; j++) {
52 for(i=0; i<pdwidth; i++) {
53 v = 0;
54 for(plane=0; plane<d->num_planes; plane++) {
55 b = de_get_bits_symbol_lsb(inf, 1,
56 pos+rowspan*(h_logical*(i64)plane+j), i);
57 if(d->grayscale_lsb)
58 v |= b<<(unsigned int)plane;
59 else
60 v = (v<<1)|b;
62 v2 = 255-de_sample_nbit_to_8bit(d->num_planes, v);
63 de_bitmap_setpixel_gray(img, i, j, v2);
67 de_bitmap_write_to_file_finfo(img, NULL, 0);
68 done:
69 de_bitmap_destroy(img);
72 static void de_run_grob_binary(deark *c, lctx *d)
74 u8 hdr[18];
75 i64 obj_id;
76 i64 length;
78 de_declare_fmt(c, "HP GROB, binary encoded");
80 de_read(hdr, 0, 18);
82 // Next 4 fields are packed 20-bit integers, 2.5 bytes each.
84 obj_id = (hdr[10]&0x0f)<<16 | hdr[9]<<8 | hdr[8];
85 length = hdr[12]<<12 | hdr[11]<<4 | hdr[10]>>4;
86 de_dbg(c, "object id: 0x%05x", (unsigned int)obj_id);
87 if(obj_id != 0x02b1e) {
88 de_warn(c, "Unexpected object identifier (0x%05x, expected 0x02b1e)", (unsigned int)obj_id);
90 de_dbg(c, "object length in nibbles: %d", (int)length);
92 d->h_phys = (hdr[15]&0x0f)<<16 | hdr[14]<<8 | hdr[13];
93 d->w = hdr[17]<<12 | hdr[16]<<4 | hdr[15]>>4;
94 de_dbg(c, "%sdimensions: %d"DE_CHAR_TIMES"%d", (d->num_planes==1)?"":"physical ",
95 (int)d->w, (int)d->h_phys);
97 grob_read_binary_bitmap(c, d, c->infile, 18);
100 // On return, sets d->bytes_consumed
101 static void grob_text_1_image(deark *c, lctx *d, i64 pos1)
103 i64 data_start;
104 u8 x;
105 u8 b0, b1;
106 i64 pos;
107 dbuf *bin_bmp = NULL; // Binary version of the bitmap
109 pos = pos1;
111 d->w = 0;
112 d->h_phys = 0;
114 // We assume the GROB text format starts with
115 // "GROB" <zero or more spaces> <width> <one or more spaces>
116 // <height> <one or more spaces> <data>.
118 // TODO: This parser is pretty clumsy.
120 pos += 4; // Skip over "GROB"
122 while(de_getbyte(pos)==' ')
123 pos++;
124 while((x=de_getbyte(pos))!=' ') {
125 d->w = d->w*10 + (x-'0');
126 pos++;
129 while(de_getbyte(pos)==' ')
130 pos++;
131 while((x=de_getbyte(pos))!=' ') {
132 d->h_phys = d->h_phys*10 + (x-'0');
133 pos++;
136 while(de_getbyte(pos)==' ')
137 pos++;
138 data_start = pos;
140 de_dbg(c, "%sdimensions: %d"DE_CHAR_TIMES"%d", (d->num_planes==1)?"":"physical ",
141 (int)d->w, (int)d->h_phys);
143 // FIXME: This should really be testing the logical height, not the
144 // physical height.
145 if(!de_good_image_dimensions(c, d->w, d->h_phys))
146 goto done;
148 // Decode the quasi-hex-encoded data into a memory buffer, then use the
149 // same decoder as for binary format.
151 bin_bmp = dbuf_create_membuf(c, d->h_phys * (d->w+7)/8, 0);
153 pos = data_start;
154 while(pos < c->infile->len) {
155 b0 = de_getbyte(pos);
156 b1 = de_getbyte(pos+1);
157 if(b0<48 || b1<48) {
158 // Apparently, we've reached the end of the bitmap data.
159 break;
162 pos+=2;
164 x = de_decode_hex_digit(b0,NULL) | (de_decode_hex_digit(b1,NULL)<<4);
165 dbuf_writebyte(bin_bmp, x);
168 d->bytes_consumed = pos - pos1;
170 grob_read_binary_bitmap(c, d, bin_bmp, 0);
172 done:
173 dbuf_close(bin_bmp);
176 static void de_run_grob_text(deark *c, lctx *d)
178 i64 pos;
179 i64 img_pos = 0;
180 int ret;
181 i64 img_count = 0;
183 de_declare_fmt(c, "HP GROB, text encoded");
185 // Though some text GROB files begin with "GROB", we also want to support files
186 // that have "%%HP" headers, and other files that have one or more GROB data objects
187 // embedded in them.
189 pos = 0;
191 while(pos < c->infile->len) {
192 // TODO: Ideally, we should be more careful about what we search for.
193 // Maybe we should make sure "GROB" is the first nonwhitespace on the line,
194 // but even that isn't enough.
196 ret = dbuf_search(c->infile, (const u8*)"GROB", 4, pos, c->infile->len-pos, &img_pos);
197 if(!ret) {
198 // No more images in this file.
199 break;
202 de_dbg(c, "GROB format found at %d", (int)img_pos);
204 img_count++;
205 if(!de_good_image_count(c, img_count)) break;
206 grob_text_1_image(c, d, img_pos);
208 if(d->bytes_consumed<1) break;
209 pos = img_pos + d->bytes_consumed;
212 if(img_count==0) {
213 de_err(c, "Unknown or unsupported GROB format");
217 static void de_run_grob(deark *c, de_module_params *mparams)
219 lctx *d = NULL;
220 u8 buf[4];
221 const char *s;
223 d = de_malloc(c, sizeof(lctx));
225 s = de_get_ext_option(c, "grob:planes");
226 if(s) {
227 d->num_planes = de_atoi64(s);
229 if(d->num_planes<1) d->num_planes=1;
230 if(d->num_planes>8) {
231 de_err(c, "Unsupported grob:planes option");
232 goto done;
235 s = de_get_ext_option(c, "grob:planeorder");
236 if(s && s[0]=='l') {
237 d->grayscale_lsb = 1;
240 de_read(buf, 0, 4);
242 if(!de_memcmp(buf, "HPHP", 4)) {
243 de_run_grob_binary(c, d);
245 else {
246 de_run_grob_text(c, d);
249 done:
250 de_free(c, d);
253 static int de_identify_grob(deark *c)
255 u8 buf[10];
256 de_read(buf, 0, 10);
258 if(buf[0]=='H' && buf[1]=='P' && buf[2]=='H' && buf[3]=='P' &&
259 buf[4]=='4' && (buf[5]=='8' || buf[5]=='9') &&
260 buf[8]==0x1e && buf[9]==0x2b)
262 return 100;
265 if(buf[0]=='G' && buf[1]=='R' && buf[2]=='O' && buf[3]=='B') {
266 return 90;
269 return 0;
272 static void de_help_grob(deark *c)
274 de_msg(c, "-opt grob:planes=<n> : Treat image as grayscale");
275 de_msg(c, "-opt grob:planeorder=l : Least-significant plane comes first");
278 void de_module_grob(deark *c, struct deark_module_info *mi)
280 mi->id = "grob";
281 mi->desc = "GROB - HP48/49 calculator image";
282 mi->run_fn = de_run_grob;
283 mi->identify_fn = de_identify_grob;
284 mi->help_fn = de_help_grob;