bmp: Rewrote the RLE decompressor
[deark.git] / modules / gemmeta.c
blob99a68328d1b1c96e04c187ded14a5d856b0e8cf7
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // GEM VDI Metafile (.gem)
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_gemmeta);
11 typedef struct localctx_struct {
12 int reserved;
13 } lctx;
15 struct opcode_data {
16 i64 pos;
17 i64 ptsin_pos;
18 i64 intin_pos;
19 i64 opcode;
21 // "Function sub-ID". This is also confusingly called "sub-opcode", but
22 // "sub-opcode" means something entirely different with respect to opcode 5.
23 i64 func_id;
25 i64 ptsin_count, intin_count;
28 typedef void (*record_decoder_fn)(deark *c, lctx *d, struct opcode_data *op);
30 struct opcode_info {
31 u16 opcode;
32 const char *name;
33 record_decoder_fn fn;
36 static void do_opcode_5(deark *c, lctx *d, struct opcode_data *op);
37 static void do_opcode_11(deark *c, lctx *d, struct opcode_data *op);
39 static const struct opcode_info opcode_info_arr[] = {
40 // This list is not intended to be complete.
41 { 5, "Escape", do_opcode_5 },
42 { 6, "Polyline", NULL },
43 { 9, "Filled area", NULL },
44 { 11, "GDP", do_opcode_11 },
45 { 13, "Set character baseline vector", NULL },
46 { 15, "Set polyline linetype", NULL },
47 { 16, "Set polyline line width", NULL },
48 { 17, "Set polyline color index", NULL },
49 { 18, "Set polymarker type", NULL },
50 { 19, "Set polymarker height", NULL },
51 { 20, "Set polymarker color index", NULL },
52 { 21, "Set text face", NULL },
53 { 22, "Set graphic text color index", NULL },
54 { 23, "Set fill interior style", NULL },
55 { 24, "Set fill style index", NULL },
56 { 25, "Set fill color index", NULL },
57 { 32, "Set writing mode", NULL },
58 { 39, "Set graphic text alignment", NULL },
59 { 104, "Set fill perimeter visibility", NULL },
60 { 106, "Set graphic text special effects", NULL },
61 { 107, "Set character cell height, points mode", NULL },
62 { 108, "Set polyline end styles", NULL },
63 { 112, "Set user defined fill pattern", NULL },
64 { 0xffff, "EOF", NULL },
65 { 0x0000, NULL, NULL }
68 static void do_opcode_5(deark *c, lctx *d, struct opcode_data *op)
70 i64 sub_opcode_id;
71 const char *name;
73 if(op->func_id!=99) return;
74 if(op->intin_count<1) return;
75 sub_opcode_id = de_getu16le(op->intin_pos);
77 switch(sub_opcode_id) {
78 case 10: name="Start Group"; break;
79 case 11: name="End Group"; break;
80 case 49: name="Set No Line Style"; break;
81 case 50: name="Set Attribute Shadow On"; break;
82 case 51: name="Set Attribute Shadow Off"; break;
83 case 80: name="Start Draw Area Type Primitive"; break;
84 case 81: name="End Draw Area Type Primitive"; break;
85 default:
86 if(sub_opcode_id>100) {
87 name="for developer use";
89 else {
90 name="?";
94 de_dbg(c, "sub-opcode id: %d (%s)", (int)sub_opcode_id, name);
97 static void do_opcode_11(deark *c, lctx *d, struct opcode_data *op)
99 const char *name;
101 switch(op->func_id) {
102 case 1: name="Bar"; break;
103 case 2: name="Arc"; break;
104 case 3: name="Pie"; break;
105 case 4: name="Circle"; break;
106 case 5: name="Ellipse"; break;
107 case 6: name="Elliptical arc"; break;
108 case 7: name="Elliptical Pie"; break;
109 case 8: name="Rounded rectangle"; break;
110 case 9: name="Filled rounded rectangle"; break;
111 case 10: name="Jutified graphic text"; break;
112 default: name="?"; break;
115 de_dbg(c, "function: %s", name);
118 static const struct opcode_info *find_opcode_info(i64 opcode)
120 i64 i;
122 for(i=0; opcode_info_arr[i].name!=NULL; i++) {
123 if(opcode_info_arr[i].opcode == opcode) {
124 return &opcode_info_arr[i];
127 return NULL;
130 // Returns 0 if we should stop reading the file.
131 static int do_record(deark *c, lctx *d, i64 pos, i64 *bytesused)
133 int retval = 0;
134 struct opcode_data op;
135 i64 ptsin_size_bytes;
136 i64 intin_size_bytes;
137 i64 data_size_bytes;
138 const struct opcode_info *opinfo;
139 const char *opcode_name;
141 *bytesused = 0;
142 de_zeromem(&op, sizeof(struct opcode_data));
144 de_dbg(c, "record at %d", (int)pos);
145 de_dbg_indent(c, 1);
147 op.opcode = de_getu16le(pos);
148 op.ptsin_count = de_getu16le(pos+2);
149 op.intin_count = de_getu16le(pos+4);
150 op.func_id = de_getu16le(pos+6);
152 ptsin_size_bytes = 4*op.ptsin_count;
153 intin_size_bytes = 2*op.intin_count;
154 data_size_bytes = ptsin_size_bytes + intin_size_bytes;
156 op.ptsin_pos = pos + 8;
157 op.intin_pos = pos + 8 + ptsin_size_bytes;
159 opinfo = find_opcode_info(op.opcode);
160 if(opinfo && opinfo->name)
161 opcode_name = opinfo->name;
162 else
163 opcode_name = "?";
165 de_dbg(c, "opcode=%d (%s), func_id=%d, #pts=%d, #int=%d (dlen=%d)",
166 (int)op.opcode, opcode_name, (int)op.func_id,
167 (int)op.ptsin_count, (int)op.intin_count,
168 (int)data_size_bytes);
170 *bytesused = 8 + data_size_bytes;
172 if(opinfo && opinfo->fn) {
173 opinfo->fn(c, d, &op);
176 if(op.opcode==65535) {
177 goto done;
180 retval = 1;
181 done:
182 de_dbg_indent(c, -1);
183 return retval;
186 static void de_run_gemmeta(deark *c, de_module_params *mparams)
188 lctx *d = NULL;
189 i64 pos;
190 i64 hdrlen_words;
191 i64 version;
192 i64 imgflag;
193 i64 bytesused;
195 d = de_malloc(c, sizeof(lctx));
197 pos = 0;
198 hdrlen_words = de_getu16le(pos+2);
199 de_dbg(c, "header length: %d words", (int)hdrlen_words);
200 version = de_getu16le(pos+4);
201 de_dbg(c, "version number: %d", (int)version);
202 // TODO: Read more header fields.
203 imgflag = de_getu16le(pos+28);
204 de_dbg(c, "image flag: %d", (int)imgflag);
206 pos += hdrlen_words*2;
208 while(1) {
209 if(pos >= c->infile->len) break;
210 if(!do_record(c, d, pos, &bytesused)) break;
211 if(bytesused<=0) break;
212 pos += bytesused;
214 de_free(c, d);
217 static int de_identify_gemmeta(deark *c)
219 // FIXME: This will not identify all files.
220 if(!dbuf_memcmp(c->infile, 0, "\xff\xff\x18\x00", 4))
221 return 100;
222 return 0;
225 void de_module_gemmeta(deark *c, struct deark_module_info *mi)
227 mi->id = "gemmeta";
228 mi->desc = "GEM VDI Metafile";
229 mi->run_fn = de_run_gemmeta;
230 mi->identify_fn = de_identify_gemmeta;