Made Unix builds more likely to be Y2038-compliant
[deark.git] / modules / psionpic.c
blobfb22c1b77e50215e261da2294e5004eda0355410
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // Psion PIC
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_psionpic);
11 struct plane_info_struct {
12 i64 width, height;
13 i64 image_pos; // absolute position in file
14 i64 image_size_in_bytes;
15 i64 rowspan;
16 u32 crc_reported;
19 typedef struct localctx_struct {
20 i64 num_planes;
21 int bw;
22 struct plane_info_struct *plane_info; // Array of plane_info_structs
23 struct de_crcobj *crco;
24 } lctx;
26 static void check_crc(deark *c, lctx *d, struct plane_info_struct *pi)
28 u32 crc_calc;
30 de_crcobj_reset(d->crco);
31 de_crcobj_addslice(d->crco, c->infile, pi->image_pos, pi->image_size_in_bytes);
32 crc_calc = de_crcobj_getval(d->crco);
33 de_dbg(c, "crc (calculated): 0x%04x", (UI)crc_calc);
34 if(crc_calc != pi->crc_reported) {
35 de_warn(c, "CRC check failed: Expected 0x%04x, got 0x%04x",
36 (UI)pi->crc_reported, (UI)crc_calc);
40 static void do_read_plane_info(deark *c, lctx *d, struct plane_info_struct *pi, i64 pos1)
42 i64 image_relative_pos;
43 i64 pos = pos1;
45 de_zeromem(pi, sizeof(struct plane_info_struct));
46 pi->crc_reported = (u32)de_getu16le_p(&pos);
47 pi->width = de_getu16le_p(&pos);
48 pi->height = de_getu16le_p(&pos);
49 pi->image_size_in_bytes = de_getu16le_p(&pos);
50 image_relative_pos = de_getu32le_p(&pos);
52 pi->image_pos = pos1 + 12 + image_relative_pos;
53 pi->rowspan = ((pi->width+15)/16)*2; // 2-byte alignment
55 de_dbg(c, "bitmap descriptor at %"I64_FMT, pos1);
56 de_dbg_indent(c, 1);
57 de_dbg(c, "image pos: %"I64_FMT, pi->image_pos);
58 de_dbg(c, "image len: %"I64_FMT, pi->image_size_in_bytes);
59 de_dbg_dimensions(c, pi->width, pi->height);
60 de_dbg(c, "crc (reported): 0x%04x", (UI)pi->crc_reported);
61 check_crc(c, d, pi);
62 de_dbg_indent(c, -1);
65 static void do_bitmap_1plane(deark *c, lctx *d, i64 plane_num)
67 struct plane_info_struct *pi = &d->plane_info[plane_num];
69 de_dbg(c, "making a bilevel image from plane %d", (int)plane_num);
71 de_convert_and_write_image_bilevel2(c->infile, pi->image_pos, pi->width, pi->height,
72 pi->rowspan, DE_CVTF_WHITEISZERO|DE_CVTF_LSBFIRST, NULL, 0);
75 static void do_bitmap_2planes(deark *c, lctx *d, i64 pn1, i64 pn2)
77 de_bitmap *img = NULL;
78 i64 planespan;
79 // AFAIK, all relevant devices support only 3 colors. Two of the four codes
80 // map to black. We'll make the two blacks slightly different, just to avoid
81 // losing information.
82 static const de_color pal[4] = { 0xffffffff, 0xff808080U, 0xff010101U, 0xff000000U };
84 de_dbg(c, "making a grayscale image from planes %d and %d", (int)pn1, (int)pn2);
86 // TODO: Support -padpix (need samples with width not a multiple of 16)
87 img = de_bitmap_create(c, d->plane_info[pn1].width, d->plane_info[pn1].height, 1);
89 planespan = d->plane_info[1].image_pos - d->plane_info[0].image_pos;
90 de_convert_image_paletted_planar(c->infile, d->plane_info[0].image_pos, 2,
91 d->plane_info[0].rowspan, planespan, pal, img, 0x1);
92 de_bitmap_write_to_file(img, NULL, 0);
94 de_bitmap_destroy(img);
97 static int could_be_2bit(lctx *d, int startpos)
99 i64 i;
100 if( (d->num_planes - startpos)%2 != 0) {
101 // Not an even number of bitmaps.
102 return 0;
104 for(i=startpos; i<d->num_planes; i+=2) {
105 if(d->plane_info[i].width!=d->plane_info[i+1].width ||
106 d->plane_info[i].height!=d->plane_info[i+1].height)
108 // Bitmaps aren't the same size.
109 return 0;
112 return 1;
115 #define PPIC_FMT_1_1 1
116 #define PPIC_FMT_1_2 2
117 #define PPIC_FMT_2_2 3
119 // This detection logic is just a wild guess. Copy it at your own risk.
120 static int detect_format(deark *c, lctx *d)
122 if(d->bw)
123 return PPIC_FMT_1_1;
125 if(d->num_planes>=3 &&
126 d->plane_info[0].width==24 && d->plane_info[0].height==24 &&
127 d->plane_info[1].width==48 && d->plane_info[1].height==48 &&
128 could_be_2bit(d, 1))
130 return PPIC_FMT_1_2;
133 if(d->num_planes>=1 &&
134 d->plane_info[0].width==24 && d->plane_info[0].height==24)
136 return PPIC_FMT_1_1;
139 if(d->num_planes>=2 && could_be_2bit(d, 0)) {
140 return PPIC_FMT_2_2;
143 return PPIC_FMT_1_1;
146 static void de_run_psionpic(deark *c, de_module_params *mparams)
148 lctx *d = NULL;
149 i64 i;
150 int format;
151 const char *s;
153 d = de_malloc(c, sizeof(lctx));
155 s = de_get_ext_option(c, "psionpic:bw");
156 if(s) {
157 d->bw = 1;
160 d->crco = de_crcobj_create(c, DE_CRCOBJ_CRC16_XMODEM);
162 d->num_planes = de_getu16le(6);
163 de_dbg(c, "number of planes/bitmaps: %d", (int)d->num_planes);
165 // After the 8-byte header are [num_images] 12-byte bitmap descriptors.
166 d->plane_info = de_mallocarray(c, d->num_planes, sizeof(struct plane_info_struct));
167 for(i=0; i<d->num_planes; i++) {
168 do_read_plane_info(c, d, &d->plane_info[i], 8+12*i);
171 // The PIC format seems like it was intended to store an arbitrary
172 // number of bilevel images, but some of them are clearly planes that
173 // are intended to be combined to form an image. I don't know for sure
174 // how I'm supposed to do that.
176 format = detect_format(c, d);
178 switch(format) {
179 case PPIC_FMT_2_2:
180 for(i=0; i+1<d->num_planes; i+=2) {
181 do_bitmap_2planes(c, d, i, i+1);
183 break;
184 case PPIC_FMT_1_2:
185 do_bitmap_1plane(c, d, 0);
186 for(i=1; i+1<d->num_planes; i+=2) {
187 do_bitmap_2planes(c, d, i, i+1);
189 break;
190 default:
191 for(i=0; i<d->num_planes; i++) {
192 do_bitmap_1plane(c, d, i);
196 de_crcobj_destroy(d->crco);
197 de_free(c, d->plane_info);
198 de_free(c, d);
201 static int de_identify_psionpic(deark *c)
203 if(!dbuf_memcmp(c->infile, 0, "PIC\xdc\x30\x30", 6))
204 return 100;
205 return 0;
208 static void de_help_psionpic(deark *c)
210 de_msg(c, "-opt psionpic:bw : Do not try to detect grayscale images");
213 void de_module_psionpic(deark *c, struct deark_module_info *mi)
215 mi->id = "psionpic";
216 mi->desc = "Psion PIC, a.k.a. EPOC PIC";
217 mi->run_fn = de_run_psionpic;
218 mi->identify_fn = de_identify_psionpic;
219 mi->help_fn = de_help_psionpic;