bmp: Rewrote the RLE decompressor
[deark.git] / modules / rodraw.c
blob7b6014f95cbdb4610f3a47eb42d3d03cdea39195
1 // This file is part of Deark.
2 // Copyright (C) 2018 Jason Summers
3 // See the file COPYING for terms of use.
5 // RISC OS Draw / Acorn Draw / ArcDraw
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_rodraw);
11 struct objinfo {
12 u32 objtype;
13 int hasbbox;
14 i64 objsize;
15 i64 objpos;
16 i64 dpos;
17 i64 dlen;
18 const char *tyname;
21 typedef struct localctx_struct {
22 unsigned int majver, minver;
23 int nesting_level;
24 } lctx;
26 static void do_object_sprite(deark *c, lctx *d, struct objinfo *oi)
28 i64 dpos, dlen;
29 dbuf *outf = NULL;
30 de_finfo *fi = NULL;
32 fi = de_finfo_create(c);
34 dpos = oi->dpos;
35 dlen = oi->dlen;
37 if(oi->objtype==13) {
38 // Skip transformation matrix
39 dpos += 24;
40 dlen -= 24;
43 if(dlen>=16) {
44 // Peek at the sprite name, to use in the output filename.
45 de_ucstring *s = NULL;
46 s = ucstring_create(c);
47 dbuf_read_to_ucstring(c->infile, dpos+4, 12, s,
48 DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_RISCOS);
49 de_finfo_set_name_from_ucstring(c, fi, s, 0);
50 ucstring_destroy(s);
53 // An .acorn extension may be needed to open these files with XnView.
54 outf = dbuf_create_output_file(c, "acorn", fi, 0x0);
56 // Manufacture a sprite file, by adding a 12-byte header.
57 dbuf_writeu32le(outf, 1); // number of sprites
58 dbuf_writeu32le(outf, 12+4); // offset of sprite data (+4)
59 dbuf_writeu32le(outf, 12+dlen+4); // file size (+4)
60 dbuf_copy(c->infile, dpos, dlen, outf);
62 dbuf_close(outf);
63 de_finfo_destroy(c, fi);
66 // Caller sets oi->objtype.
67 // We set oi->tyname, oi->hasbbox.
68 static void get_objtype_info(struct objinfo *oi)
70 switch(oi->objtype) {
71 case 0: oi->tyname="font table"; break;
72 case 1: oi->tyname="text"; oi->hasbbox=1; break;
73 case 2: oi->tyname="path"; oi->hasbbox=1; break;
74 case 5: oi->tyname="sprite"; oi->hasbbox=1; break;
75 case 6: oi->tyname="group"; oi->hasbbox=1; break;
76 case 7: oi->tyname="tagged object"; oi->hasbbox=1; break;
77 case 9: oi->tyname="text area"; oi->hasbbox=1; break;
78 case 10: oi->tyname="text column"; oi->hasbbox=1; break;
79 case 11: oi->tyname="options"; oi->hasbbox=1; break;
80 case 12: oi->tyname="transformed text"; oi->hasbbox=1; break;
81 case 13: oi->tyname="transformed sprite"; oi->hasbbox=1; break;
82 case 65637: oi->tyname="DrawPlus internal data"; break;
84 if(!oi->tyname) oi->tyname="?";
87 static int do_object_sequence(deark *c, lctx *d, i64 pos1, i64 len);
89 static int do_object(deark *c, lctx *d, struct objinfo *oi)
91 if(oi->objtype==6) { // group
92 // TODO: group name, 12 bytes
93 do_object_sequence(c, d, oi->dpos+12, oi->dlen-12);
95 else if(oi->objtype==5) { // sprite
96 do_object_sprite(c, d, oi);
98 else if(oi->objtype==13) { // transformed sprite
99 do_object_sprite(c, d, oi);
101 // TODO: objtype 7 (tagged object)
103 return 1;
106 static int do_object_sequence(deark *c, lctx *d, i64 pos1, i64 len)
108 i64 pos = pos1;
110 d->nesting_level++;
111 if(d->nesting_level>16) goto done;
113 while(1) {
114 struct objinfo oi;
116 de_zeromem(&oi, sizeof(struct objinfo));
117 oi.objpos = pos;
118 if((oi.objpos+24) > (pos1+len)) break;
119 oi.objtype = (u32)de_getu32le_p(&pos);
120 oi.objsize = de_getu32le_p(&pos);
121 if(oi.objsize<8 || (oi.objpos+oi.objsize)>(pos1+len)) {
122 de_err(c, "Bad object size (%u) at %"I64_FMT, (unsigned int)oi.objsize, oi.objpos);
123 goto done;
126 get_objtype_info(&oi);
128 de_dbg(c, "object at %"I64_FMT", type=%u (%s), len=%"I64_FMT, oi.objpos,
129 oi.objtype, oi.tyname, oi.objsize);
131 if(oi.hasbbox) {
132 pos += 16;
134 oi.dpos = pos;
135 oi.dlen = oi.objpos+oi.objsize - oi.dpos;
136 if(oi.dlen<0) goto done_obj;
138 de_dbg_indent(c, 1);
139 do_object(c, d, &oi);
140 de_dbg_indent(c, -1);
142 done_obj:
143 pos = oi.objpos + oi.objsize;
146 done:
147 d->nesting_level--;
148 return 1;
151 static int do_header(deark *c, lctx *d, i64 pos1)
153 i64 pos = pos1;
155 de_dbg(c, "header at %d", (int)pos1);
156 de_dbg_indent(c, 1);
157 pos += 4; // file signature
158 d->majver = (unsigned int)de_getu32le_p(&pos);
159 d->minver = (unsigned int)de_getu32le_p(&pos);
160 de_dbg(c, "format version: %u,%u", d->majver, d->minver);
161 pos += 12; // app name
162 pos += 16; // bounding box
163 de_dbg_indent(c, -1);
164 return 1;
167 static void de_run_rodraw(deark *c, de_module_params *mparams)
169 lctx *d = NULL;
170 i64 pos;
172 d = de_malloc(c, sizeof(lctx));
174 pos = 0;
175 if(!do_header(c, d, pos)) goto done;
176 pos += 40;
178 if(!do_object_sequence(c, d, pos, c->infile->len-pos)) goto done;
180 done:
181 de_free(c, d);
184 static int de_identify_rodraw(deark *c)
186 if(!dbuf_memcmp(c->infile, 0, "Draw", 4)) {
187 if(!dbuf_memcmp(c->infile, 4, "\xc9\0\0\0\0\0\0\0", 8)) {
188 return 100;
190 return 49;
192 return 0;
195 void de_module_rodraw(deark *c, struct deark_module_info *mi)
197 mi->id = "rodraw";
198 mi->desc = "RISC OS Draw, Acorn Draw";
199 mi->run_fn = de_run_rodraw;
200 mi->identify_fn = de_identify_rodraw;