New "ea_data" module
[deark.git] / modules / jbf.c
blob1af4c1d8e401c9dc54022258330f782dde81edf5
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // PaintShop Pro Browser Cache (JBF) (pspbrwse.jbf)
7 // This module was developed with the help of information from
8 // jbfinspect.c (https://github.com/0x09/jbfinspect), which says:
9 // "The author disclaims all copyright on this code."
11 #include <deark-config.h>
12 #include <deark-private.h>
13 #include <deark-fmtutil.h>
14 DE_DECLARE_MODULE(de_module_jbf);
16 struct page_ctx {
17 de_finfo *fi;
18 de_ucstring *fname;
19 const char *thumbnail_ext;
22 typedef struct localctx_struct {
23 unsigned int ver_major;
24 unsigned int ver_minor;
25 unsigned int ver_combined;
26 i64 image_count;
27 } lctx;
29 static const u32 v1pal[256] = {
30 0x000000,0xffffff,0xff0000,0x00fe00,0x0000fe,0xffff00,0xff00ff,0x00ffff,
31 0x0f0f0f,0x171717,0x1f1f1f,0x272727,0x383838,0x404040,0x484848,0x4f4f4f,
32 0x606060,0x686868,0x707070,0x808080,0x979797,0xa0a0a0,0xb0b0b0,0xb8b8b8,
33 0xbfbfbf,0xc8c8c8,0xd9d9d9,0xe0e0e0,0xe8e8e8,0xf0f0f0,0xc00000,0x170606,
34 0x270506,0x400b0b,0x500f0f,0x681717,0x801717,0x981b1b,0xa01f20,0xb82324,
35 0xc82728,0xe02b2b,0xf12f2f,0xff4040,0xfe5050,0xff6060,0xff706f,0xff807f,
36 0xff9898,0xffa0a0,0xffb0b0,0xfec0c0,0xffd0d0,0xffe0e0,0xfff0f0,0x00c000,
37 0x061705,0x062705,0x0a400b,0x0f500f,0x176717,0x177f17,0x1b971b,0x1fa020,
38 0x24b724,0x27c827,0x2ce02b,0x30f02f,0x40ff40,0x50ff50,0x60ff60,0x70ff70,
39 0x80ff80,0x98ff98,0x9fffa0,0xb0ffb0,0xc0fec0,0xd0fed1,0xe0ffe1,0xf0fff0,
40 0x0000c0,0x060617,0x050628,0x0a0b3f,0x0f0e4f,0x171768,0x171780,0x1c1b98,
41 0x2020a0,0x2324b8,0x2728c8,0x2b2be0,0x2f2ff0,0x4040ff,0x5050ff,0x605fff,
42 0x6f70ff,0x8080ff,0x9797fe,0x9fa0ff,0xb0afff,0xc0c0ff,0xd0d0ff,0xe0e0fe,
43 0xf0f0ff,0xc0c100,0x171706,0x282705,0x40400b,0x4f500f,0x686717,0x808017,
44 0x97981b,0xa0a01f,0xb8b824,0xc8c827,0xe1e02b,0xf0f030,0xffff3f,0xffff50,
45 0xfeff60,0xffff6f,0xffff80,0xffff98,0xfffea0,0xfefeb1,0xffffc0,0xffffd0,
46 0xfeffe0,0xfffff0,0xc000c0,0x170517,0x270527,0x400a3f,0x500f50,0x681768,
47 0x80177f,0x981b98,0xa01f9f,0xb823b8,0xc927c8,0xe02be1,0xf02ff1,0xff40fe,
48 0xff50fe,0xff5fff,0xff70ff,0xff7fff,0xfe98ff,0xfea0ff,0xffb0ff,0xffc0ff,
49 0xffcffe,0xffdfff,0xfff0ff,0x00c0c0,0x062727,0x0a4040,0x0f4f50,0x186868,
50 0x178080,0x1b9898,0x1fa0a0,0x23b8b8,0x27c8c8,0x2ce0df,0x30f0f0,0x40ffff,
51 0x4fffff,0x60ffff,0x70fffe,0x80fffe,0x97fffe,0xa0ffff,0xafffff,0xc1ffff,
52 0xcfffff,0xf1ffff,0x170f05,0x271705,0x401f0a,0x50270f,0x673817,0x804017,
53 0x98481b,0xa0501f,0xb86023,0xc86828,0xe0702b,0xf0802f,0xf88840,0xf49850,
54 0xf49760,0xf8a070,0xf8b080,0xf8b898,0xf9bea0,0xfac8b1,0xffd9c0,0xffe0d0,
55 0xffe8e0,0xfff0f0,0x28170f,0x402317,0x4f2f1f,0x674028,0x7f4830,0x985438,
56 0x9f6040,0xb86c48,0xc88050,0xd9845c,0xe09768,0xdf9c73,0xe4a880,0xe8b797,
57 0xe9c098,0xefcca4,0xefd8b0,0xf8e4bc,0xf9f0c8,0xf8f8d4,0xfff4e1,0xfff7f0,
58 0x50507f,0x5f5f88,0x686897,0x706f98,0x8080a0,0x8888b0,0x9798be,0x9898c8,
59 0xa0a0d9,0xb0b0e0,0xb8b8e8,0x50804f,0x5f8760,0x679868,0x6f9870,0x7fa080,
60 0x88b087,0x98be98,0x98c898,0xa0d89f,0xb0e1b0,0xb8e8b8,0x007ff0,0x00f080,
61 0x7f00f0,0x78f000,0xf18000,0xf00080,0xc10037,0xa89080,0x606848,0x887860
64 static int do_read_header(deark *c, lctx *d, i64 pos)
66 int retval = 0;
68 de_dbg(c, "header at %d", (int)pos);
69 de_dbg_indent(c, 1);
71 pos += 15;
72 d->ver_major = (unsigned int)de_getu16be(pos);
73 d->ver_minor = (unsigned int)de_getu16be(pos+2);
74 d->ver_combined = (d->ver_major<<16) | d->ver_minor;
75 de_dbg(c, "format version: %u.%u", d->ver_major, d->ver_minor);
76 pos+=4;
78 if(d->ver_major<1 || d->ver_major>2) {
79 de_err(c, "Unsupported JBF format version: %u.%u", d->ver_major, d->ver_minor);
80 goto done;
82 if(d->ver_major==1 && (d->ver_minor==2 || d->ver_minor>3)) {
83 de_warn(c, "Unrecognized JBF format version (%u.%u). File may not be "
84 "decoded correctly.", d->ver_major, d->ver_minor);
87 d->image_count = de_getu32le(pos);
88 de_dbg(c, "image count: %d", (int)d->image_count);
89 //pos+=4;
90 if(!de_good_image_count(c, d->image_count)) goto done;
92 retval = 1;
93 done:
94 de_dbg_indent(c, -1);
95 return retval;
98 static const char *get_type_name(unsigned int filetype_code)
100 const char *nm = "unknown";
102 switch(filetype_code) {
103 // There are many more PSP file types. These are just some common ones.
104 case 0x00: nm="none"; break;
105 case 0x01: nm="BMP"; break;
106 case 0x0a: nm="GIF"; break;
107 case 0x11: nm="JPEG"; break;
108 case 0x18: nm="PCX"; break;
109 case 0x1c: nm="PNG"; break;
110 case 0x1f: nm="PSP"; break;
111 case 0x23: nm="TGA"; break;
112 case 0x24: nm="TIFF"; break;
114 return nm;
117 static int read_filename(deark *c, lctx *d, struct page_ctx *pg, i64 pos1, i64 *bytes_consumed)
119 int retval = 0;
120 i64 pos = pos1;
121 de_ucstring *fname_orig = NULL;
123 fname_orig = ucstring_create(c);
125 if(d->ver_combined>=0x010001) { // v1.1+
126 i64 fnlen;
127 fnlen = de_getu32le(pos);
128 de_dbg(c, "original filename len: %d", (int)fnlen);
129 pos += 4;
130 if(fnlen>1000) {
131 de_err(c, "Bad filename length");
132 goto done;
135 // I don't think there's any way to know the encoding of the filename.
136 // WINDOWS1252 is just a guess.
137 dbuf_read_to_ucstring(c->infile, pos, fnlen, fname_orig, 0, DE_ENCODING_WINDOWS1252);
138 pos += fnlen;
140 else { // v1.0
141 // File always has 13 bytes reserved for the filename.
142 // The name is up to 12 bytes long, terminated by 0x00.
143 dbuf_read_to_ucstring(c->infile, pos, 12, fname_orig, DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_WINDOWS1252);
144 pos += 13;
147 de_dbg(c, "original filename: \"%s\"", ucstring_getpsz(fname_orig));
149 if(c->filenames_from_file) {
150 pg->fname = ucstring_clone(fname_orig);
151 ucstring_append_sz(pg->fname, ".thumb.", DE_ENCODING_LATIN1);
152 if(d->ver_major>=2)
153 ucstring_append_sz(pg->fname, "jpg", DE_ENCODING_LATIN1);
154 else
155 ucstring_append_sz(pg->fname, "bmp", DE_ENCODING_LATIN1);
156 de_finfo_set_name_from_ucstring(c, pg->fi, pg->fname, 0);
157 pg->fi->original_filename_flag = 1;
159 else {
160 if(d->ver_major>=2)
161 pg->thumbnail_ext = "jpg";
162 else
163 pg->thumbnail_ext = "bmp";
166 retval = 1;
167 done:
168 ucstring_destroy(fname_orig);
169 *bytes_consumed = pos - pos1;
170 return retval;
173 static void read_FILETIME(deark *c, lctx *d, struct page_ctx *pg, i64 pos)
175 i64 ft;
176 char timestamp_buf[64];
178 ft = de_geti64le(pos);
179 de_FILETIME_to_timestamp(ft, &pg->fi->timestamp[DE_TIMESTAMPIDX_MODIFY], 0x1);
180 de_timestamp_to_string(&pg->fi->timestamp[DE_TIMESTAMPIDX_MODIFY], timestamp_buf, sizeof(timestamp_buf), 0);
181 de_dbg(c, "mod time: %s", timestamp_buf);
184 static void read_unix_time(deark *c, lctx *d, struct page_ctx *pg, i64 pos)
186 i64 ut;
187 char timestamp_buf[64];
189 ut = de_geti32le(pos);
190 de_unix_time_to_timestamp(ut, &pg->fi->timestamp[DE_TIMESTAMPIDX_MODIFY], 0x1);
191 de_timestamp_to_string(&pg->fi->timestamp[DE_TIMESTAMPIDX_MODIFY], timestamp_buf, sizeof(timestamp_buf), 0);
192 de_dbg(c, "mod time: %s", timestamp_buf);
195 static int read_bitmap_v1(deark *c, lctx *d, struct page_ctx *pg, i64 pos1, i64 *bytes_consumed)
197 struct de_bmpinfo bi;
198 int retval = 0;
199 dbuf *outf = NULL;
200 i64 pos = pos1;
201 i64 k;
202 i64 count;
203 i64 dec_bytes = 0;
205 de_dbg(c, "bitmap at %d", (int)pos);
206 de_dbg_indent(c, 1);
208 if(!fmtutil_get_bmpinfo(c, c->infile, &bi, pos, c->infile->len-pos, 0)) {
209 de_err(c, "Invalid bitmap");
210 goto done;
213 if(bi.infohdrsize != 40) {
214 de_err(c, "Unexpected BMP format");
215 goto done;
218 outf = dbuf_create_output_file(c, pg->thumbnail_ext, pg->fi, 0);
219 // Manufacture a BMP fileheader
220 fmtutil_generate_bmpfileheader(c, outf, &bi, 0);
222 // Copy the BITMAPINFOHEADER
223 dbuf_copy(c->infile, pos, bi.infohdrsize, outf);
225 // Write the standard palette
226 for(k=0; k<256; k++) {
227 dbuf_writebyte(outf, (u8)DE_COLOR_B(v1pal[k]));
228 dbuf_writebyte(outf, (u8)DE_COLOR_G(v1pal[k]));
229 dbuf_writebyte(outf, (u8)DE_COLOR_R(v1pal[k]));
230 dbuf_writebyte(outf, 0);
233 pos += bi.infohdrsize;
235 // Decompress the image
236 while(1) {
237 u8 b0, b1;
239 // Stop if we reach the end of the input file.
240 if(pos >= c->infile->len) break;
242 // Stop if we decompressed the expected number of bytes
243 if(dec_bytes >= bi.foreground_size) break;
245 b0 = de_getbyte(pos++);
247 if(d->ver_minor>=3) {
248 if(b0>0x80) { // a compressed run
249 count = (i64)(b0-0x80);
250 b1 = de_getbyte(pos++);
251 dbuf_write_run(outf, b1, count);
252 dec_bytes += count;
254 else { // uncompressed run
255 count = (i64)b0;
256 dbuf_copy(c->infile, pos, count, outf);
257 pos += count;
258 dec_bytes += count;
261 else {
262 if(b0>0xc0) { // a compressed run
263 count = (i64)(b0-0xc0);
264 b1 = de_getbyte(pos++);
265 dbuf_write_run(outf, b1, count);
266 dec_bytes += count;
268 else { // literal byte
269 count = 1;
270 dbuf_writebyte(outf, b0);
271 dec_bytes += count;
276 retval = 1;
277 done:
278 dbuf_close(outf);
279 *bytes_consumed = pos - pos1;
280 de_dbg_indent(c, -1);
281 return retval;
284 static int do_one_thumbnail(deark *c, lctx *d, i64 pos1, i64 imgidx, i64 *bytes_consumed)
286 i64 payload_len;
287 int retval = 0;
288 i64 pos = pos1;
289 unsigned int filetype_code;
290 i64 file_size;
291 i64 x;
292 i64 tn_w, tn_h;
293 struct page_ctx *pg = NULL;
294 i64 fn_field_size = 0;
296 de_dbg(c, "image #%d at %d", (int)imgidx, (int)pos1);
297 de_dbg_indent(c, 1);
299 pg = de_malloc(c, sizeof(struct page_ctx));
301 pg->fi = de_finfo_create(c);
303 if(!read_filename(c, d, pg, pos, &fn_field_size)) {
304 goto done;
306 pos += fn_field_size;
308 if(d->ver_major==2) {
309 read_FILETIME(c, d, pg, pos);
310 pos += 8;
313 if(d->ver_major==2) {
314 // The original file type (not the format of the thumbnail)
315 filetype_code = (unsigned int)de_getu32le(pos);
316 de_dbg(c, "original file type: 0x%02x (%s)", filetype_code, get_type_name(filetype_code));
317 pos += 4; // filetype code
319 else if(d->ver_major==1 && d->ver_minor<3) {
320 pos += 4; // TODO: FOURCC
323 tn_w = de_getu16le(pos);
324 pos += 4;
325 tn_h = de_getu16le(pos);
326 pos += 4;
327 de_dbg(c, "original dimensions: %d"DE_CHAR_TIMES"%d", (int)tn_w, (int)tn_h);
329 pos += 4; // color depth
331 if(d->ver_major==2) {
332 pos += 4; // (uncompressed size?)
335 file_size = de_getu32le(pos);
336 de_dbg(c, "original file size: %u", (unsigned int)file_size);
337 pos += 4;
339 if(d->ver_major==1) {
340 read_unix_time(c, d, pg, pos);
341 pos += 4;
343 pos += 4; // TODO: image index
346 if(d->ver_major==2) {
347 // first 4 bytes of 12-byte "thumbnail signature"
348 x = de_getu32le(pos);
349 pos += 4;
350 if(x==0) { // truncated entry
351 de_dbg(c, "thumbnail not present");
352 retval = 1;
353 goto done;
356 pos += 8; // remaining 8 byte of signature
358 payload_len = de_getu32le(pos);
359 de_dbg(c, "payload len: %u", (unsigned int)payload_len);
360 pos += 4;
362 if(pos + payload_len > c->infile->len) {
363 de_err(c, "Bad payload length (%u) or unsupported format", (unsigned int)payload_len);
364 goto done;
367 dbuf_create_file_from_slice(c->infile, pos, payload_len, pg->thumbnail_ext, pg->fi, 0);
368 pos += payload_len;
370 else { // ver_major==1
371 i64 thumbnail_size;
372 if(!read_bitmap_v1(c, d, pg, pos, &thumbnail_size)) {
373 goto done;
375 pos += thumbnail_size;
378 retval = 1;
379 done:
380 *bytes_consumed = pos - pos1;
381 de_finfo_destroy(c, pg->fi);
382 ucstring_destroy(pg->fname);
383 de_free(c, pg);
384 de_dbg_indent(c, -1);
385 return retval;
388 static void de_run_jbf(deark *c, de_module_params *mparams)
390 lctx *d = NULL;
391 i64 pos = 0;
392 i64 bytes_consumed;
393 i64 count = 0;
395 d = de_malloc(c, sizeof(lctx));
396 if(!do_read_header(c, d, pos)) goto done;
397 pos += 1024;
399 count = 0;
400 while(1) {
401 if(count>=d->image_count) break;
402 if(pos>=c->infile->len) goto done;
404 bytes_consumed = 0;
405 if(!do_one_thumbnail(c, d, pos, count, &bytes_consumed)) {
406 goto done;
408 if(bytes_consumed<1) goto done;
409 pos += bytes_consumed;
410 count++;
413 done:
414 de_free(c, d);
417 static int de_identify_jbf(deark *c)
419 if(!dbuf_memcmp(c->infile, 0, "JASC BROWS FILE", 15))
420 return 100;
421 return 0;
424 void de_module_jbf(deark *c, struct deark_module_info *mi)
426 mi->id = "jbf";
427 mi->desc = "PaintShop Pro Browser Cache (pspbrwse.jbf)";
428 mi->run_fn = de_run_jbf;
429 mi->identify_fn = de_identify_jbf;