1 // This file is part of Deark.
2 // Copyright (C) 2017 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_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
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
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
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
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:
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:
64 typedef struct localctx_struct
{
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.
70 i64 image_width
, image_height
;
75 struct de_timestamp timestamp
;
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
{
88 // 0x02: Print decoded value, even if decoder_fn exists
94 obj_decoder_fn decoder_fn
;
97 static const char *get_fulltype_name(u8 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;
110 // Variable length integer/quantity (VLQ)
111 // fpos will be read and updated.
112 static i64
read_vlq(deark
*c
, i64
*fpos
)
119 nlen
= (i64
)de_getbyte(pos
++);
126 for(k
=0; k
<nlen
; k
++) {
127 val
= (val
<<8) | (i64
)de_getbyte(pos
++);
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
)
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
];
219 static int do_primitive_object(deark
*c
, lctx
*d
, i64 pos1
,
220 u8 obj_fulltype
, i64
*bytes_consumed
)
225 const struct obj_type_info_struct
*oti
;
228 i64 value_as_vlq
= 0;
230 de_dbg(c
, "primitive object at %d", (int)pos1
);
232 obj_type
= de_getbyte(pos
++);
233 oti
= find_obj_type_info(obj_fulltype
, obj_type
);
234 if(oti
&& oti
->name
) name
= oti
->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) {
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))) {
250 de_dbg(c
, "%s: (field is present)", name
);
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
);
263 de_dbg_indent(c
, -1);
264 *bytes_consumed
= pos
-pos1
;
268 static int do_object_list(deark
*c
, lctx
*d
, i64 pos1
, i64 len
,
269 u8 object_fulltype
, i64
*bytes_consumed
)
272 int saved_indent_level
;
275 de_dbg_indent_save(c
, &saved_indent_level
);
276 de_dbg(c
, "object list at %d, len=%d", (int)pos
, (int)len
);
279 if(pos
>=pos1
+len
) break;
280 if(pos
>=c
->infile
->len
) goto done
;
283 if(!do_primitive_object(c
, d
, pos
, object_fulltype
, &objsize
)) goto done
;
285 de_dbg_indent(c
, -1);
289 de_dbg_indent_restore(c
, saved_indent_level
);
293 // Process one "full object", given its address
294 static int do_full_object(deark
*c
, lctx
*d
, i64 pos1
,
300 i64 bytes_consumed2
= 0;
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
);
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
;
323 de_dbg_indent(c
, -1);
324 *bytes_consumed
= pos
-pos1
;
328 static void do_colormap(deark
*c
, lctx
*d
)
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
);
336 for(k
=0; k
<d
->colormap_size
&& k
<256; k
++) {
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
)
357 if(pos
>c
->infile
->len
) break;
358 if(pixel_count
>=num_pixels
) break;
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
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
;
387 dbuf
*unc_pixels
= NULL
;
391 if(d
->image_data_pos
==0) return;
393 de_dbg(c
, "image data at %d", (int)d
->image_data_pos
);
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
);
404 bytes_per_pixel
= d
->image_depth
/8;
406 if(d
->image_depth
==8 && d
->colormap_pos
==0) {
407 de_err(c
, "Missing colormap");
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
);
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) {
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) {
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);
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
)
452 i64 bytes_consumed
= 0;
455 d
= de_malloc(c
, sizeof(lctx
));
457 de_dbg(c
, "header at %d", (int)pos
);
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);
465 if(!do_full_object(c
, d
, root_obj_offs
, &bytes_consumed
)) goto done
;
473 static int de_identify_vort(deark
*c
)
475 if(!dbuf_memcmp(c
->infile
, 0, "VORT01", 6))
480 void de_module_vort(deark
*c
, struct deark_module_info
*mi
)
483 mi
->desc
= "VORT ray tracer PIX image";
484 mi
->run_fn
= de_run_vort
;
485 mi
->identify_fn
= de_identify_vort
;