1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_psionpic
);
11 struct plane_info_struct
{
13 i64 image_pos
; // absolute position in file
14 i64 image_size_in_bytes
;
19 typedef struct localctx_struct
{
22 struct plane_info_struct
*plane_info
; // Array of plane_info_structs
23 struct de_crcobj
*crco
;
26 static void check_crc(deark
*c
, lctx
*d
, struct plane_info_struct
*pi
)
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
;
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
);
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
);
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
;
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
)
100 if( (d
->num_planes
- startpos
)%2 != 0) {
101 // Not an even number of bitmaps.
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.
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
)
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 &&
133 if(d
->num_planes
>=1 &&
134 d
->plane_info
[0].width
==24 && d
->plane_info
[0].height
==24)
139 if(d
->num_planes
>=2 && could_be_2bit(d
, 0)) {
146 static void de_run_psionpic(deark
*c
, de_module_params
*mparams
)
153 d
= de_malloc(c
, sizeof(lctx
));
155 s
= de_get_ext_option(c
, "psionpic:bw");
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
);
180 for(i
=0; i
+1<d
->num_planes
; i
+=2) {
181 do_bitmap_2planes(c
, d
, i
, i
+1);
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);
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
);
201 static int de_identify_psionpic(deark
*c
)
203 if(!dbuf_memcmp(c
->infile
, 0, "PIC\xdc\x30\x30", 6))
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
)
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
;