bmp: Rewrote the RLE decompressor
[deark.git] / modules / tivariable.c
blob15b8786d3e8e3b4fd7896c4e231d2771b59e4bb7
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 #include <deark-config.h>
6 #include <deark-private.h>
7 DE_DECLARE_MODULE(de_module_tivariable);
9 typedef struct localctx_struct {
10 i64 w, h;
11 int fmt;
12 } lctx;
14 typedef void (*ti_decoder_fn)(deark *c, lctx *d);
16 static void do_ti83(deark *c, lctx *d);
17 static void do_ti85(deark *c, lctx *d);
18 static void do_ti92(deark *c, lctx *d);
20 struct ti_ver_info {
21 const char *sig;
22 const char *description;
23 ti_decoder_fn decoder_fn;
25 // The format IDs are the indices of the items in this array.
26 static const struct ti_ver_info ti_ver_info_arr[] = {
27 #define DE_FMT_NOT_TI 0
28 { NULL, NULL, NULL },
29 #define DE_FMT_UNKNOWN_TI 1
30 { NULL, NULL, NULL },
31 #define DE_FMT_TI73 2
32 { "**TI73**", "TI73 variable file", do_ti83 },
33 #define DE_FMT_TI82 3
34 { "**TI82**", "TI82 variable file", do_ti83 },
35 #define DE_FMT_TI83 4
36 { "**TI83**", "TI83 variable file", do_ti83 },
37 #define DE_FMT_TI83F 5
38 { "**TI83F*", "TI83F variable file", do_ti83 },
39 #define DE_FMT_TI85 6
40 { "**TI85**", "TI85 variable file", do_ti85 },
41 #define DE_FMT_TI86 7
42 { "**TI86**", "TI86 variable file", do_ti85 },
43 #define DE_FMT_TI89 8
44 { "**TI89**", "TI89 variable file", do_ti92 },
45 #define DE_FMT_TI92 9
46 { "**TI92**", "TI92 variable file", do_ti92 },
47 #define DE_FMT_TI92P 10
48 { "**TI92P*", "TI92P variable file", do_ti92 }
49 #define DE_FMT_COUNT 11
52 static int identify_internal(deark *c)
54 u8 buf[8];
55 int i;
57 de_read(buf, 0, 8);
59 if(de_memcmp(buf, "**TI", 4)) return DE_FMT_NOT_TI;
61 for(i=2; i<DE_FMT_COUNT; i++) {
62 if(!de_memcmp(buf, ti_ver_info_arr[i].sig, 8)) {
63 return i;
66 return DE_FMT_UNKNOWN_TI;
69 static int do_bitmap(deark *c, lctx *d, i64 pos)
71 i64 rowspan;
72 int retval = 0;
74 de_dbg_dimensions(c, d->w, d->h);
75 rowspan = (d->w+7)/8;
77 if(pos+rowspan*d->h > c->infile->len) {
78 de_err(c, "Unexpected end of file");
79 goto done;
82 de_convert_and_write_image_bilevel(c->infile, pos, d->w, d->h, rowspan,
83 DE_CVTF_WHITEISZERO, NULL, 0);
84 retval = 1;
85 done:
86 return retval;
89 static int do_bitmap_8ca(deark *c, lctx *d, i64 pos)
91 de_bitmap *img = NULL;
92 i64 i;
93 i64 j;
94 i64 rowspan;
95 int retval = 0;
96 u8 b0, b1;
97 u32 clr;
99 de_dbg_dimensions(c, d->w, d->h);
101 rowspan = (((d->w * 16)+31)/32)*4; // Uses 4-byte alignment, apparently
103 if(pos+rowspan*d->h > c->infile->len) {
104 de_err(c, "Unexpected end of file");
105 goto done;
107 if(!de_good_image_dimensions(c, d->w, d->h)) goto done;
109 img = de_bitmap_create(c, d->w, d->h, 3);
111 for(j=0; j<d->h; j++) {
112 for(i=0; i<d->w; i++) {
113 b0 = de_getbyte(pos + j*rowspan + i*2);
114 b1 = de_getbyte(pos + j*rowspan + i*2 + 1);
115 clr = (((u32)b1)<<8) | b0;
116 clr = de_rgb565_to_888(clr);
117 de_bitmap_setpixel_rgb(img, i, j, clr);
122 de_bitmap_write_to_file(img, NULL, DE_CREATEFLAG_FLIP_IMAGE);
123 retval = 1;
124 done:
125 de_bitmap_destroy(img);
126 return retval;
129 static int do_bitmap_8ci(deark *c, lctx *d, i64 pos)
131 de_bitmap *img = NULL;
132 i64 i;
133 i64 j;
134 i64 rowspan;
135 int retval = 0;
136 u8 b0;
138 de_dbg_dimensions(c, d->w, d->h);
140 rowspan = (d->w + 1) / 2;
142 if(pos+rowspan*d->h > c->infile->len) {
143 de_err(c, "Unexpected end of file");
144 goto done;
146 if(!de_good_image_dimensions(c, d->w, d->h)) goto done;
148 // This a 4 bits/pixel format, but I don't even know if it's grayscale or color.
149 img = de_bitmap_create(c, d->w, d->h, 1);
151 for(j=0; j<d->h; j++) {
152 for(i=0; i<d->w; i++) {
153 b0 = de_getbyte(pos + j*rowspan + i/2);
154 if(i%2)
155 b0 &= 0x0f;
156 else
157 b0 >>= 4;
159 de_bitmap_setpixel_gray(img, i, j, b0 * 17);
164 de_bitmap_write_to_file(img, NULL, 0);
165 retval = 1;
166 done:
167 de_bitmap_destroy(img);
168 return retval;
171 static int do_ti83_picture_var(deark *c, lctx *d, i64 pos)
173 i64 picture_size;
175 de_dbg(c, "picture at %d", (int)pos);
176 picture_size = de_getu16le(pos);
177 de_dbg(c, "picture size: %d", (int)picture_size);
179 if(picture_size==21945) {
180 d->w = 265;
181 d->h = 165;
182 return do_bitmap_8ci(c, d, pos+2);
185 d->w = 95;
186 d->h = 63;
187 return do_bitmap(c, d, pos+2);
190 static int do_ti83c_picture_var(deark *c, lctx *d, i64 pos)
192 // Try to support a type of color image (.8ca).
193 // This code may not be correct.
194 de_dbg(c, "picture at %d", (int)pos);
195 d->w = 133;
196 d->h = 83;
197 return do_bitmap_8ca(c, d, pos+3);
200 static int do_ti85_picture_var(deark *c, lctx *d, i64 pos)
202 i64 x;
204 de_dbg(c, "picture at %d", (int)pos);
205 x = de_getu16le(pos);
206 de_dbg(c, "picture size: %d", (int)x);
207 d->w = 128;
208 d->h = 63;
209 return do_bitmap(c, d, pos+2);
212 static int do_ti92_picture_var(deark *c, lctx *d, i64 pos)
214 i64 x;
216 de_dbg(c, "picture at %d", (int)pos);
217 pos+=4;
219 x = de_getu16be(pos);
220 de_dbg(c, "picture size: %d", (int)x);
221 d->h = de_getu16be(pos+2);
222 d->w = de_getu16be(pos+4);
223 return do_bitmap(c, d, pos+6);
226 static void do_ti92_var_table_entry(deark *c, lctx *d, i64 pos)
228 i64 data_offset;
229 u8 type_id;
231 de_dbg(c, "var table entry at %d", (int)pos);
232 data_offset = de_getu32le(pos);
234 type_id = de_getbyte(pos+12);
235 de_dbg(c, "var type: 0x%02x", (unsigned int)type_id);
236 if(type_id!=0x10) {
237 // Not a picture
238 return;
240 de_dbg(c, "data offset: %d", (int)data_offset);
241 do_ti92_picture_var(c, d, data_offset);
244 static void do_ti83(deark *c, lctx *d)
246 i64 pos;
247 i64 data_section_size;
248 i64 data_section_end;
249 i64 var_data_size;
250 u8 type_id;
252 // 0-7: signature
253 // 8-10: 0x1a 0x0a 0x00
254 // 11-52: comment
256 data_section_size = de_getu16le(53);
257 de_dbg(c, "data section size: %d", (int)data_section_size);
258 data_section_end = 55+data_section_size;
259 if(data_section_end > c->infile->len) {
260 de_err(c, "Data section goes beyond end of file");
261 goto done;
264 // Read the variables
265 pos = 55;
266 while(pos < data_section_end) {
267 var_data_size = de_getu16le(pos+2);
268 type_id = de_getbyte(pos+4);
269 pos += 15;
270 if(d->fmt==DE_FMT_TI83F)
271 pos += 2;
272 de_dbg(c, "var type=0x%02x pos=%d len=%d", (unsigned int)type_id,
273 (int)pos, (int)var_data_size);
275 if(type_id==0x07) { // guess
276 do_ti83_picture_var(c, d, pos);
278 else if(type_id==0x1a) {
279 do_ti83c_picture_var(c, d, pos);
282 pos += var_data_size;
285 done:
289 static void do_ti85(deark *c, lctx *d)
291 i64 pos;
292 i64 data_section_size;
293 i64 data_section_end;
294 i64 var_data_size;
295 i64 name_len_reported;
296 i64 name_field_len;
297 u8 type_id;
298 i64 x1, x2;
299 int warned = 0;
301 // 0-7: signature
302 // 8-10: 0x1a 0x0a 0x00
303 // 11-52: comment
305 data_section_size = de_getu16le(53);
306 de_dbg(c, "data section size: %d", (int)data_section_size);
307 data_section_end = 55+data_section_size;
308 if(data_section_end > c->infile->len) {
309 de_err(c, "Data section goes beyond end of file");
310 goto done;
313 // Read the variables
314 pos = 55;
315 while(pos < data_section_end) {
316 if(data_section_end - pos < 8) {
317 de_warn(c, "Invalid variable entry size. This file may not have been processed correctly.");
318 break;
321 var_data_size = de_getu16le(pos+2);
322 type_id = de_getbyte(pos+4);
323 name_len_reported = (i64)de_getbyte(pos+5);
324 de_dbg(c, "reported var name length: %d", (int)name_len_reported);
325 if(d->fmt==DE_FMT_TI86) {
326 name_field_len = 8; // Initial default
328 // The TI86 name field length *should* always be 8, but some files do not
329 // do it that way.
330 if(name_len_reported!=8) {
331 // There are two "variable data length" fields that should contain the
332 // same value. Although this is bad design, we can exploit it to help
333 // guess the correct length of the variable name field.
335 x1 = de_getu16le(pos+14);
336 x2 = de_getu16le(pos+6+name_len_reported);
337 if(x1!=var_data_size && x2==var_data_size) {
338 if(!warned) {
339 de_warn(c, "This TI86 file appears to use TI85 variable name format "
340 "instead of TI86 format. Trying to continue.");
341 warned = 1;
343 name_field_len = name_len_reported;
347 else {
348 // TI85 format
349 name_field_len = name_len_reported;
352 pos += 6+name_field_len;
354 x1 = de_getu16le(pos);
355 if(x1!=var_data_size) {
356 de_warn(c, "Inconsistent variable-data-length fields. "
357 "This file may not be processed correctly.");
359 pos += 2;
361 de_dbg(c, "var type=0x%02x pos=%d len=%d", (unsigned int)type_id,
362 (int)pos, (int)var_data_size);
364 if(type_id==0x11) { // guess
365 do_ti85_picture_var(c, d, pos);
368 pos += var_data_size;
371 done:
375 static void do_ti92(deark *c, lctx *d)
377 i64 pos;
378 i64 numvars;
379 i64 x;
380 i64 i;
382 // 0-7: signature
383 // 8-9: 0x01 0x00
384 // 10-17: default folder name
385 // 18-57: comment
387 numvars = de_getu16le(58);
388 de_dbg(c, "number of variables/folders: %d", (int)numvars);
389 if(!de_good_image_count(c, numvars)) goto done;
391 pos = 60;
392 for(i=0; i<numvars; i++) {
393 do_ti92_var_table_entry(c, d, pos);
394 pos+=16;
397 // Data section
398 x = de_getu32le(pos);
399 de_dbg(c, "reported file size: %d", (int)x);
401 done:
405 static void de_run_tivariable(deark *c, de_module_params *mparams)
407 lctx *d = NULL;
409 d = de_malloc(c, sizeof(lctx));
410 d->fmt = identify_internal(c);
412 if(!ti_ver_info_arr[d->fmt].decoder_fn) {
413 de_err(c, "Unknown or unsupported TI variable file version");
414 goto done;
417 de_declare_fmt(c, ti_ver_info_arr[d->fmt].description);
418 ti_ver_info_arr[d->fmt].decoder_fn(c, d);
420 done:
421 de_free(c, d);
424 static int de_identify_tivariable(deark *c)
426 int fmt;
427 fmt = identify_internal(c);
428 if(fmt==DE_FMT_NOT_TI) return 0;
429 if(fmt==DE_FMT_UNKNOWN_TI) return 10;
430 return 100;
433 void de_module_tivariable(deark *c, struct deark_module_info *mi)
435 mi->id = "tivariable";
436 mi->desc = "TI calculator variable or picture file";
437 mi->run_fn = de_run_tivariable;
438 mi->identify_fn = de_identify_tivariable;