zoo: Various improvements
[deark.git] / modules / vort.c
blob01131e2416635e8331622c14e233e252e9ab6b3d
1 // This file is part of Deark.
2 // Copyright (C) 2017 Jason Summers
3 // See the file COPYING for terms of use.
5 // VORT pix image
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_vort);
11 // The VORT software includes documentation about this file format, but to me
12 // it seems unclear and incomplete. Here are some of my notes about the format,
13 // using my own terminology.
15 // VLQ := a LENGTH byte, followed by an unsigned #LENGTH-byte integer.
16 // (Longer-than-optimal LENGTHs are fine. A LENGTH of 0 is allowed, at least
17 // in some contexts.)
19 // FULL_OBJECT := a FULL_TYPE byte, then a VLQ, then a #VLQ-byte OBJECT_LIST
21 // OBJECT_LIST := a sequence of PRIMITIVE_OBJECTs
22 // (Note that, to parse an OBJECT_LIST, you need to know its size in bytes.
23 // To do anything useful with it, you also need its parent's FULL_TYPE.)
25 // PRIMITIVE_OBJECT := a PRIMITIVE_TYPE byte, then a LENGTH byte, then #LENGTH
26 // bytes of data
27 // (Note that, to do anything useful with a PRIMITIVE_OBJECT, you need to
28 // know its parent's FULL_TYPE.)
29 // (Note that the data following the PRIMITIVE_TYPE byte is often, but not
30 // always, in the form of a VLQ.)
32 // FULL_TYPE V_DIRECTORY w/ PRIMITIVE_TYPE D_OBJECT is a pointer to a
33 // FULL_OBJECT.
35 // The file can also contain unstructured data at arbitrary offsets, pointed to
36 // by certain PRIMITIVE_OBJECTs.
38 // At offset 6 is a VLQ giving the offset of the "root object" (a FULL_OBJECT).
39 // This is expected to be a directory object; otherwise I guess it has to be
40 // the only FULL_OBJECT in the file.
42 // All file offsets are measured from the beginning of the file.
44 // "Full object" types:
45 #define VORT_V_DIRECTORY 0
46 #define VORT_V_IMAGE 1
47 #define VORT_V_TEXT 2
48 #define VORT_V_COLORMAP 3
50 // Primitive object types for V_DIRECTORY:
51 #define VORT_D_OBJECT 2
53 // Primitive object types for V_IMAGE:
54 #define VORT_I_ADDR 0
55 #define VORT_I_IMWIDTH 1
56 #define VORT_I_IMHEIGHT 2
57 #define VORT_I_IMDEPTH 3
58 #define VORT_I_RLE_CODED 11
60 // Primitive object types for V_COLORMAP:
61 #define VORT_C_ADDR 0
62 #define VORT_C_SIZE 1
64 typedef struct localctx_struct {
65 int nesting_level;
67 // A file can potentially contain multiple images and/or multiple colormaps.
68 // But I'm just going to cross my fingers and hope it doesn't.
69 i64 image_data_pos;
70 i64 image_width, image_height;
71 i64 image_depth;
72 i64 colormap_pos;
73 i64 colormap_size;
74 u8 rle_flag;
75 struct de_timestamp timestamp;
77 u32 pal[256];
78 } lctx;
80 struct obj_type_info_struct;
82 typedef void (*obj_decoder_fn)(deark *c, lctx *d,
83 const struct obj_type_info_struct *oti,
84 i64 pos, i64 dlen, i64 value_as_vlq);
86 struct obj_type_info_struct {
87 // 0x01: is a VLQ
88 // 0x02: Print decoded value, even if decoder_fn exists
89 u32 flags;
91 u8 full_type;
92 u8 primitive_type;
93 const char *name;
94 obj_decoder_fn decoder_fn;
97 static const char *get_fulltype_name(u8 t)
99 const char *name;
100 switch(t) {
101 case VORT_V_DIRECTORY: name="directory"; break;
102 case VORT_V_IMAGE: name="image"; break;
103 case VORT_V_TEXT: name="text"; break;
104 case VORT_V_COLORMAP: name="colormap"; break;
105 default: name="?";
107 return name;
110 // Variable length integer/quantity (VLQ)
111 // fpos will be read and updated.
112 static i64 read_vlq(deark *c, i64 *fpos)
114 i64 nlen;
115 i64 k;
116 i64 val;
117 i64 pos = *fpos;
119 nlen = (i64)de_getbyte(pos++);
120 *fpos += 1+nlen;
121 if(nlen>7) {
122 return 0;
125 val = 0;
126 for(k=0; k<nlen; k++) {
127 val = (val<<8) | (i64)de_getbyte(pos++);
129 if(val<0) return 0;
130 return val;
133 static int do_full_object(deark *c, lctx *d, i64 pos1,
134 i64 *bytes_consumed);
136 static void decode_object_addr(deark *c, lctx *d,
137 const struct obj_type_info_struct *oti,
138 i64 pos, i64 dlen, i64 value_as_vlq)
140 i64 bytes_consumed = 0;
141 do_full_object(c, d, value_as_vlq, &bytes_consumed);
144 static void decode_date(deark *c, lctx *d,
145 const struct obj_type_info_struct *oti,
146 i64 pos, i64 dlen, i64 value_as_vlq)
148 char timestamp_buf[64];
150 de_unix_time_to_timestamp(value_as_vlq, &d->timestamp, 0x1);
151 de_timestamp_to_string(&d->timestamp, timestamp_buf, sizeof(timestamp_buf), 0);
152 de_dbg(c, "%s: %"I64_FMT" (%s)", oti->name, value_as_vlq, timestamp_buf);
155 static void decode_simple_item(deark *c, lctx *d,
156 const struct obj_type_info_struct *oti,
157 i64 pos, i64 dlen, i64 val)
159 if(oti->full_type==VORT_V_IMAGE) {
160 switch(oti->primitive_type) {
161 case VORT_I_ADDR: d->image_data_pos = val; break;
162 case VORT_I_IMWIDTH: d->image_width = val; break;
163 case VORT_I_IMHEIGHT: d->image_height = val; break;
164 case VORT_I_IMDEPTH: d->image_depth = val; break;
165 case VORT_I_RLE_CODED: d->rle_flag = 1; break;
168 else if(oti->full_type==VORT_V_COLORMAP) {
169 switch(oti->primitive_type) {
170 case VORT_C_ADDR: d->colormap_pos = val; break;
171 case VORT_C_SIZE: d->colormap_size = val; break;
176 static const struct obj_type_info_struct obj_type_info_arr[] = {
177 { 0x03, VORT_V_DIRECTORY, 0 /* VORT_D_PARENT */, "address of parent dir", NULL },
178 { 0x00, VORT_V_DIRECTORY, 1 /* VORT_D_NULL */, "empty", NULL },
179 { 0x03, VORT_V_DIRECTORY, VORT_D_OBJECT, "address of child object", decode_object_addr },
180 { 0x03, VORT_V_IMAGE, VORT_I_ADDR, "address of image data", decode_simple_item },
181 { 0x03, VORT_V_IMAGE, VORT_I_IMWIDTH, "image width", decode_simple_item },
182 { 0x03, VORT_V_IMAGE, VORT_I_IMHEIGHT, "image height", decode_simple_item },
183 { 0x03, VORT_V_IMAGE, VORT_I_IMDEPTH, "bits per pixel", decode_simple_item },
184 { 0x03, VORT_V_IMAGE, 4 /* I_RED */, "red channel flag", NULL },
185 { 0x03, VORT_V_IMAGE, 5 /* I_GREEN */, "green channel flag", NULL },
186 { 0x03, VORT_V_IMAGE, 6 /* I_BLUE */, "blue channel flag", NULL },
187 { 0x03, VORT_V_IMAGE, 7 /* I_ALPHA */, "alpha channel flag", NULL },
188 { 0x03, VORT_V_IMAGE, 8 /* I_BACKGND */, "background color", NULL },
189 { 0x01, VORT_V_IMAGE, 9 /* I_DATE */, "creation date", decode_date },
190 { 0x03, VORT_V_IMAGE, 10 /* I_COLORMAP */, "address of colormap object", NULL },
191 { 0x03, VORT_V_IMAGE, VORT_I_RLE_CODED, "run length encoded flag", decode_simple_item },
192 { 0x03, VORT_V_IMAGE, 12 /* I_XADDR */, "x coord if fragment", NULL },
193 { 0x03, VORT_V_IMAGE, 13 /* I_YADDR */, "y coord if fragment", NULL },
194 { 0x03, VORT_V_IMAGE, 14 /* I_ORIGWIDTH */, "whole width if fragment", NULL },
195 { 0x03, VORT_V_IMAGE, 15 /* I_ORIGHEIGHT */, "whole height if fragment", NULL },
196 { 0x03, VORT_V_TEXT, 0 /* T_ADDR */, "address of text data", NULL },
197 { 0x03, VORT_V_TEXT, 1 /* T_LENGTH */, "size of text data", NULL },
198 { 0x03, VORT_V_COLORMAP, VORT_C_ADDR, "address of colormap data", decode_simple_item },
199 { 0x03, VORT_V_COLORMAP, VORT_C_SIZE, "size of colormap data", decode_simple_item },
200 { 0x03, VORT_V_COLORMAP, 2 /* C_RED */, "red channel flag", NULL },
201 { 0x03, VORT_V_COLORMAP, 3 /* C_GREEN */, "green channel flag", NULL },
202 { 0x03, VORT_V_COLORMAP, 4 /* C_BLUE */, "blue channel flag", NULL }
205 static const struct obj_type_info_struct *find_obj_type_info(u8 full_type, u8 primitive_type)
207 size_t i;
209 for(i=0; i<DE_ARRAYCOUNT(obj_type_info_arr); i++) {
210 if(obj_type_info_arr[i].primitive_type==primitive_type &&
211 obj_type_info_arr[i].full_type==full_type)
213 return &obj_type_info_arr[i];
216 return NULL;
219 static int do_primitive_object(deark *c, lctx *d, i64 pos1,
220 u8 obj_fulltype, i64 *bytes_consumed)
222 u8 obj_type;
223 i64 obj_dlen;
224 i64 pos = pos1;
225 const struct obj_type_info_struct *oti;
226 const char *name;
228 i64 value_as_vlq = 0;
230 de_dbg(c, "primitive object at %d", (int)pos1);
231 de_dbg_indent(c, 1);
232 obj_type = de_getbyte(pos++);
233 oti = find_obj_type_info(obj_fulltype, obj_type);
234 if(oti && oti->name) name = oti->name;
235 else name = "?";
236 de_dbg(c, "primitive type: %u (%s)", (unsigned int)obj_type, name);
238 // The data is usually a VLQ, but sometimes it's not. For convenience,
239 // we'll read the length byte, then go back and read the whole thing as a VLQ.
240 obj_dlen = (i64)de_getbyte(pos);
242 if(obj_dlen>=1 && obj_dlen<=8) {
243 i64 tmppos = pos;
244 value_as_vlq = read_vlq(c, &tmppos);
246 pos++; // For the length byte
248 if(oti && oti->flags&0x01 && (!oti->decoder_fn || (oti->flags&0x2))) {
249 if(obj_dlen==0) {
250 de_dbg(c, "%s: (field is present)", name);
252 else {
253 de_dbg(c, "%s: %"I64_FMT, name, value_as_vlq);
257 if(oti && oti->decoder_fn) {
258 oti->decoder_fn(c, d, oti, pos, obj_dlen, value_as_vlq);
261 pos += obj_dlen;
263 de_dbg_indent(c, -1);
264 *bytes_consumed = pos-pos1;
265 return 1;
268 static int do_object_list(deark *c, lctx *d, i64 pos1, i64 len,
269 u8 object_fulltype, i64 *bytes_consumed)
271 i64 pos = pos1;
272 int saved_indent_level;
273 int retval = 0;
275 de_dbg_indent_save(c, &saved_indent_level);
276 de_dbg(c, "object list at %d, len=%d", (int)pos, (int)len);
277 while(1) {
278 i64 objsize;
279 if(pos>=pos1+len) break;
280 if(pos>=c->infile->len) goto done;
282 de_dbg_indent(c, 1);
283 if(!do_primitive_object(c, d, pos, object_fulltype, &objsize)) goto done;
284 pos += objsize;
285 de_dbg_indent(c, -1);
287 retval = 1;
288 done:
289 de_dbg_indent_restore(c, saved_indent_level);
290 return retval;
293 // Process one "full object", given its address
294 static int do_full_object(deark *c, lctx *d, i64 pos1,
295 i64 *bytes_consumed)
297 u8 obj_type;
298 i64 obj_dlen;
299 i64 pos = pos1;
300 i64 bytes_consumed2 = 0;
301 int retval = 0;
303 d->nesting_level++;
304 // Objects can't be nested without going through this code path, so doing
305 // this check only here should be sufficient.
306 if(d->nesting_level>8) goto done;
308 de_dbg(c, "full object at %d", (int)pos);
309 de_dbg_indent(c, 1);
310 obj_type = de_getbyte(pos++);
311 de_dbg(c, "full type: %u (%s)", (unsigned int)obj_type, get_fulltype_name(obj_type));
313 obj_dlen = read_vlq(c, &pos);
314 de_dbg(c, "data len: %"I64_FMT, obj_dlen);
316 if(!do_object_list(c, d, pos, obj_dlen, obj_type, &bytes_consumed2)) goto done;
318 pos += obj_dlen;
319 retval = 1;
321 done:
322 d->nesting_level--;
323 de_dbg_indent(c, -1);
324 *bytes_consumed = pos-pos1;
325 return retval;
328 static void do_colormap(deark *c, lctx *d)
330 i64 k;
332 if(d->colormap_pos==0 || d->colormap_size==0) return;
333 de_dbg(c, "colormap at %d, %d entries", (int)d->colormap_pos, (int)d->colormap_size);
335 de_dbg_indent(c, 1);
336 for(k=0; k<d->colormap_size && k<256; k++) {
337 u8 cr, cg, cb;
338 cr = de_getbyte(d->colormap_pos + k);
339 cg = de_getbyte(d->colormap_pos + d->colormap_size + k);
340 cb = de_getbyte(d->colormap_pos + d->colormap_size*2 + k);
341 d->pal[k] = DE_MAKE_RGB(cr, cg, cb);
342 de_dbg_pal_entry(c, k, d->pal[k]);
344 de_dbg_indent(c, -1);
347 static void do_decompress(deark *c, lctx *d, i64 pos1, dbuf *unc_pixels,
348 i64 num_pixels, i64 bytes_per_pixel)
350 i64 pos = pos1;
351 i64 pixel_count = 0;
353 while(1) {
354 u8 b;
355 i64 count;
357 if(pos>c->infile->len) break;
358 if(pixel_count>=num_pixels) break;
360 b = de_getbyte(pos);
361 pos++;
362 if(b>=128) { // uncompressed run
363 count = (i64)(b-128);
364 dbuf_copy(c->infile, pos, count*bytes_per_pixel, unc_pixels);
365 pos += count*bytes_per_pixel;
366 pixel_count += count;
368 else { // compressed run
369 i64 k;
370 u8 pixel_buf[4];
372 count = 1+(i64)b;
373 de_read(pixel_buf, pos, bytes_per_pixel);
374 pos += bytes_per_pixel;
375 for(k=0; k<count; k++) {
376 dbuf_write(unc_pixels, pixel_buf, bytes_per_pixel);
378 pixel_count += count;
383 static void do_image(deark *c, lctx *d)
385 de_bitmap *img = NULL;
386 de_finfo *fi = NULL;
387 dbuf *unc_pixels = NULL;
388 i64 i, j;
389 i64 bytes_per_pixel;
391 if(d->image_data_pos==0) return;
393 de_dbg(c, "image data at %d", (int)d->image_data_pos);
394 de_dbg_indent(c, 1);
396 if(!de_good_image_dimensions(c, d->image_width, d->image_height)) goto done;
398 img = de_bitmap_create(c, d->image_width, d->image_height, 3);
400 if(d->image_depth!=8 && d->image_depth!=24) {
401 de_err(c, "Unsupported bits/pixel: %d", (int)d->image_depth);
402 goto done;
404 bytes_per_pixel = d->image_depth/8;
406 if(d->image_depth==8 && d->colormap_pos==0) {
407 de_err(c, "Missing colormap");
408 goto done;
411 if(d->rle_flag) {
412 unc_pixels = dbuf_create_membuf(c, 0, 0);
413 do_decompress(c, d, d->image_data_pos, unc_pixels,
414 d->image_width*d->image_height, bytes_per_pixel);
416 else {
417 unc_pixels = dbuf_open_input_subfile(c->infile,
418 d->image_data_pos, c->infile->len-d->image_data_pos);
421 for(j=0; j<d->image_height; j++) {
422 for(i=0; i<d->image_width; i++) {
423 if(d->image_depth==8) {
424 u8 b;
425 b = dbuf_getbyte(unc_pixels, j*d->image_width + i);
426 de_bitmap_setpixel_rgb(img, i, j, d->pal[(unsigned int)b]);
428 else if(d->image_depth==24) {
429 u32 clr;
430 clr = dbuf_getRGB(unc_pixels, (j*d->image_width + i)*bytes_per_pixel, 0);
431 de_bitmap_setpixel_rgb(img, i, j, clr);
436 fi = de_finfo_create(c);
437 fi->internal_mod_time = d->timestamp;
439 de_bitmap_write_to_file_finfo(img, fi, 0);
441 done:
442 de_dbg_indent(c, -1);
443 dbuf_close(unc_pixels);
444 de_bitmap_destroy(img);
445 de_finfo_destroy(c, fi);
448 static void de_run_vort(deark *c, de_module_params *mparams)
450 i64 pos;
451 i64 root_obj_offs;
452 i64 bytes_consumed = 0;
453 lctx *d = NULL;
455 d = de_malloc(c, sizeof(lctx));
456 pos = 0;
457 de_dbg(c, "header at %d", (int)pos);
458 de_dbg_indent(c, 1);
459 pos += 6; // signature
460 root_obj_offs = read_vlq(c, &pos);
461 de_dbg(c, "root object address: %d", (int)root_obj_offs);
462 de_dbg_indent(c, -1);
464 pos = root_obj_offs;
465 if(!do_full_object(c, d, root_obj_offs, &bytes_consumed)) goto done;
466 do_colormap(c, d);
467 do_image(c, d);
469 done:
470 de_free(c, d);
473 static int de_identify_vort(deark *c)
475 if(!dbuf_memcmp(c->infile, 0, "VORT01", 6))
476 return 100;
477 return 0;
480 void de_module_vort(deark *c, struct deark_module_info *mi)
482 mi->id = "vort";
483 mi->desc = "VORT ray tracer PIX image";
484 mi->run_fn = de_run_vort;
485 mi->identify_fn = de_identify_vort;