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
);
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
;
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
)
68 de_dbg(c
, "header at %d", (int)pos
);
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
);
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
);
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
);
90 if(!de_good_image_count(c
, d
->image_count
)) goto done
;
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;
117 static int read_filename(deark
*c
, lctx
*d
, struct page_ctx
*pg
, i64 pos1
, i64
*bytes_consumed
)
121 de_ucstring
*fname_orig
= NULL
;
123 fname_orig
= ucstring_create(c
);
125 if(d
->ver_combined
>=0x010001) { // v1.1+
127 fnlen
= de_getu32le(pos
);
128 de_dbg(c
, "original filename len: %d", (int)fnlen
);
131 de_err(c
, "Bad filename length");
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
);
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
);
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
);
153 ucstring_append_sz(pg
->fname
, "jpg", DE_ENCODING_LATIN1
);
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;
161 pg
->thumbnail_ext
= "jpg";
163 pg
->thumbnail_ext
= "bmp";
168 ucstring_destroy(fname_orig
);
169 *bytes_consumed
= pos
- pos1
;
173 static void read_FILETIME(deark
*c
, lctx
*d
, struct page_ctx
*pg
, i64 pos
)
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
)
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
;
205 de_dbg(c
, "bitmap at %d", (int)pos
);
208 if(!fmtutil_get_bmpinfo(c
, c
->infile
, &bi
, pos
, c
->infile
->len
-pos
, 0)) {
209 de_err(c
, "Invalid bitmap");
213 if(bi
.infohdrsize
!= 40) {
214 de_err(c
, "Unexpected BMP format");
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
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
);
254 else { // uncompressed run
256 dbuf_copy(c
->infile
, pos
, count
, outf
);
262 if(b0
>0xc0) { // a compressed run
263 count
= (i64
)(b0
-0xc0);
264 b1
= de_getbyte(pos
++);
265 dbuf_write_run(outf
, b1
, count
);
268 else { // literal byte
270 dbuf_writebyte(outf
, b0
);
279 *bytes_consumed
= pos
- pos1
;
280 de_dbg_indent(c
, -1);
284 static int do_one_thumbnail(deark
*c
, lctx
*d
, i64 pos1
, i64 imgidx
, i64
*bytes_consumed
)
289 unsigned int filetype_code
;
293 struct page_ctx
*pg
= NULL
;
294 i64 fn_field_size
= 0;
296 de_dbg(c
, "image #%d at %d", (int)imgidx
, (int)pos1
);
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
)) {
306 pos
+= fn_field_size
;
308 if(d
->ver_major
==2) {
309 read_FILETIME(c
, d
, pg
, pos
);
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
);
325 tn_h
= de_getu16le(pos
);
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
);
339 if(d
->ver_major
==1) {
340 read_unix_time(c
, d
, pg
, pos
);
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
);
350 if(x
==0) { // truncated entry
351 de_dbg(c
, "thumbnail not present");
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
);
362 if(pos
+ payload_len
> c
->infile
->len
) {
363 de_err(c
, "Bad payload length (%u) or unsupported format", (unsigned int)payload_len
);
367 dbuf_create_file_from_slice(c
->infile
, pos
, payload_len
, pg
->thumbnail_ext
, pg
->fi
, 0);
370 else { // ver_major==1
372 if(!read_bitmap_v1(c
, d
, pg
, pos
, &thumbnail_size
)) {
375 pos
+= thumbnail_size
;
380 *bytes_consumed
= pos
- pos1
;
381 de_finfo_destroy(c
, pg
->fi
);
382 ucstring_destroy(pg
->fname
);
384 de_dbg_indent(c
, -1);
388 static void de_run_jbf(deark
*c
, de_module_params
*mparams
)
395 d
= de_malloc(c
, sizeof(lctx
));
396 if(!do_read_header(c
, d
, pos
)) goto done
;
401 if(count
>=d
->image_count
) break;
402 if(pos
>=c
->infile
->len
) goto done
;
405 if(!do_one_thumbnail(c
, d
, pos
, count
, &bytes_consumed
)) {
408 if(bytes_consumed
<1) goto done
;
409 pos
+= bytes_consumed
;
417 static int de_identify_jbf(deark
*c
)
419 if(!dbuf_memcmp(c
->infile
, 0, "JASC BROWS FILE", 15))
424 void de_module_jbf(deark
*c
, struct deark_module_info
*mi
)
427 mi
->desc
= "PaintShop Pro Browser Cache (pspbrwse.jbf)";
428 mi
->run_fn
= de_run_jbf
;
429 mi
->identify_fn
= de_identify_jbf
;