New "videomaster" module
[deark.git] / modules / dlmaker.c
blobdc4407db1b98366b858f0d8ec576ca950ca2a2cb
1 // This file is part of Deark.
2 // Copyright (C) 2021 Jason Summers
3 // See the file COPYING for terms of use.
5 // DL animation format, used by DL MAKER / DL VIEWER
6 // by Davide Tome' & Luca De Gregorio
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_dlmaker);
11 #define V12_SCREEN_WIDTH 320
12 #define V12_SCREEN_HEIGHT 200
13 #define V12_SCREEN_SIZE_IN_BYTES 64000
15 struct v12_fields {
16 int opt_montage;
17 u8 screen_format;
18 i64 img_xsize, img_ysize;
19 // A "screen" is a 320x200 aggregate image containing 1 or more real images.
20 i64 imgs_per_screen;
21 i64 num_screens;
22 de_bitmap *screen_img;
23 de_bitmap *img;
26 typedef struct localctx_struct {
27 de_ext_encoding input_encoding;
28 u8 ver;
29 struct v12_fields v12; // fields only used in v1 and v2
30 i64 hdr_size;
31 i64 num_anim_code_units;
32 i64 anim_code_unit_size;
33 i64 num_audio_components;
34 i64 num_images;
35 de_finfo *fi;
36 u32 pal[256];
37 } lctx;
39 static void read_name(deark *c, lctx *d, i64 pos, de_ucstring *s, size_t nsize)
41 size_t i;
42 u8 buf[40];
44 if(nsize>40) return;
45 de_read(buf, pos, nsize);
46 for(i=0; i<nsize; i++) {
47 if(buf[i]) buf[i] ^= 0xff;
49 ucstring_append_bytes(s, buf, nsize, DE_CONVFLAG_STOP_AT_NUL, d->input_encoding);
52 static void do_v12screen(deark *c, lctx *d, i64 pos)
54 if(!d->v12.screen_img) {
55 d->v12.screen_img = de_bitmap_create(c, V12_SCREEN_WIDTH, V12_SCREEN_HEIGHT, 3);
57 de_convert_image_paletted(c->infile, pos, 8, V12_SCREEN_WIDTH, d->pal, d->v12.screen_img, 0);
58 if(d->v12.imgs_per_screen<=1 || d->v12.opt_montage) {
59 de_bitmap_write_to_file_finfo(d->v12.screen_img, d->fi, 0);
61 else {
62 i64 i;
63 i64 xpos = 0;
64 i64 ypos = 0;
66 if(!d->v12.img) {
67 d->v12.img = de_bitmap_create(c, d->v12.img_xsize, d->v12.img_ysize, 3);
70 for(i=0; i<d->v12.imgs_per_screen; i++) {
71 de_bitmap_copy_rect(d->v12.screen_img, d->v12.img, xpos, ypos, d->v12.img_xsize, d->v12.img_ysize,
72 0, 0, 0);
73 de_bitmap_write_to_file_finfo(d->v12.img, d->fi, 0);
75 xpos += d->v12.img_xsize;
76 if(xpos >= V12_SCREEN_WIDTH) {
77 xpos = 0;
78 ypos += d->v12.img_ysize;
84 static void do_extract_v3_image(deark *c, lctx *d, i64 pos, i64 xsize, i64 ysize)
86 de_bitmap *img = NULL;
88 if(!de_good_image_dimensions(c, xsize, ysize)) goto done;
90 img = de_bitmap_create(c, xsize, ysize, 3);
91 de_convert_image_paletted(c->infile, pos, 8, xsize, d->pal, img, 0);
92 de_bitmap_write_to_file_finfo(img, d->fi, 0);
94 done:
95 de_bitmap_destroy(img);
98 static void do_extract_audio_component(deark *c, lctx *d, i64 pos, i64 len)
100 dbuf *outf = NULL;
102 if(pos+len > c->infile->len) goto done;
103 if(len <= 26) goto done;
104 outf = dbuf_create_output_file(c, "voc", NULL, 0);
105 dbuf_write(outf, (const u8*)"Creative Voice File\x1a", 20);
106 dbuf_copy(c->infile, pos+20, len-20, outf);
107 done:
108 dbuf_close(outf);
111 static void do_audio(deark *c, lctx *d, i64 pos1)
113 i64 i;
114 i64 pos = pos1;
116 for(i=0; i<d->num_audio_components; i++) {
117 i64 dlen;
119 if(pos+5 > c->infile->len) goto done;
120 de_dbg(c, "audio component at %"I64_FMT, pos);
121 de_dbg_indent(c, 1);
122 dlen = de_getu32le_p(&pos);
123 de_dbg(c, "len: %"I64_FMT, dlen);
124 pos++; // unused?
125 do_extract_audio_component(c, d, pos, dlen);
126 de_dbg_indent(c, -1);
127 pos += dlen;
130 done:
134 static void do_read_palette(deark *c, lctx *d, i64 pos1)
136 i64 k;
137 i64 pos = pos1;
139 de_dbg(c, "palette at %"I64_FMT, pos1);
140 de_dbg_indent(c, 1);
141 for(k=0; k<256; k++) {
142 u8 cr1, cg1, cb1;
143 u8 cr2, cg2, cb2;
144 char tmps[64];
146 cr1 = de_getbyte_p(&pos);
147 cg1 = de_getbyte_p(&pos);
148 cb1 = de_getbyte_p(&pos);
149 cr2 = de_scale_63_to_255(cr1 & 0x3f);
150 cg2 = de_scale_63_to_255(cg1 & 0x3f);
151 cb2 = de_scale_63_to_255(cb1 & 0x3f);
152 d->pal[k] = DE_MAKE_RGB(cr2, cg2, cb2);
153 de_snprintf(tmps, sizeof(tmps), "(%2d,%2d,%2d) "DE_CHAR_RIGHTARROW" ",
154 (int)cr1, (int)cg1, (int)cb1);
155 de_dbg_pal_entry2(c, k, d->pal[k], tmps, NULL, NULL);
157 de_dbg_indent(c, -1);
160 static int do_read_header(deark *c, lctx *d)
162 int retval = 0;
163 i64 pos = 0;
164 size_t nsize;
165 de_ucstring *s = NULL;
167 d->ver = de_getbyte_p(&pos);
168 de_dbg(c, "version: %u", (UI)d->ver);
169 if(d->ver<1 || d->ver>3) {
170 de_err(c, "Not a DL file");
171 goto done;
174 if(d->ver==1) {
175 d->v12.screen_format = 1;
177 else if(d->ver==2) {
178 d->v12.screen_format = de_getbyte_p(&pos);
179 de_dbg(c, "screen format: %u", (UI)d->v12.screen_format);
181 else {
182 pos++;
185 if(d->ver<=2) {
186 switch(d->v12.screen_format) {
187 case 0:
188 d->v12.img_xsize = 320;
189 d->v12.img_ysize = 200;
190 break;
191 case 1:
192 d->v12.img_xsize = 160;
193 d->v12.img_ysize = 100;
194 break;
195 case 2:
196 d->v12.img_xsize = 80;
197 d->v12.img_ysize = 50;
198 break;
199 default:
200 de_err(c, "Invalid/unsupported DL format");
201 goto done;
204 d->v12.imgs_per_screen = (V12_SCREEN_WIDTH/d->v12.img_xsize) *
205 (V12_SCREEN_HEIGHT/d->v12.img_ysize);
206 if(d->ver==2) {
207 de_dbg_indent(c, 1);
208 de_dbg_dimensions(c, d->v12.img_xsize, d->v12.img_ysize);
209 de_dbg(c, "images/screen: %u", (UI)d->v12.imgs_per_screen);
210 de_dbg_indent(c, -1);
214 if(d->ver==3) pos += 50;
216 s = ucstring_create(c);
217 nsize = (d->ver==3) ? 40 : 20;
218 read_name(c, d, pos, s, nsize);
219 de_dbg(c, "title: \"%s\"", ucstring_getpsz_d(s));
220 pos += nsize;
222 if(d->ver!=1) {
223 ucstring_empty(s);
224 read_name(c, d, pos, s, nsize);
225 de_dbg(c, "author: \"%s\"", ucstring_getpsz_d(s));
226 pos += nsize;
229 if(d->ver==3) {
230 d->num_images = de_getu16le_p(&pos);
231 de_dbg(c, "num images: %u", (UI)d->num_images);
233 else {
234 d->v12.num_screens = (i64)de_getbyte_p(&pos);
235 de_dbg(c, "num screens: %u", (UI)d->v12.num_screens);
236 d->num_images = d->v12.num_screens * d->v12.imgs_per_screen;
237 de_dbg(c, "num images (calculated): %"I64_FMT, d->num_images);
240 if(d->ver==1) {
241 d->num_anim_code_units = de_getu16le_p(&pos);
242 d->anim_code_unit_size = 1;
244 else if(d->ver==3) {
245 d->num_anim_code_units = de_getu16le_p(&pos);
246 d->anim_code_unit_size = 2;
248 else {
249 d->num_anim_code_units = de_getu32le_p(&pos);
250 d->anim_code_unit_size = 2;
252 de_dbg(c, "num frames: %"I64_FMT, d->num_anim_code_units);
254 if(d->ver==3) {
255 d->num_audio_components = de_getu16le_p(&pos);
256 de_dbg(c, "num audio components: %"I64_FMT, d->num_audio_components);
259 do_read_palette(c, d, pos);
260 pos += 256*3;
261 d->hdr_size = pos;
262 retval = 1;
264 done:
265 ucstring_destroy(s);
266 return retval;
269 static void de_run_dlmaker(deark *c, de_module_params *mparams)
271 lctx *d = NULL;
272 i64 pos = 0;
273 i64 k;
274 i64 anim_cmds_size;
275 int saved_indent_level;
277 de_dbg_indent_save(c, &saved_indent_level);
278 d = de_malloc(c, sizeof(lctx));
279 d->input_encoding = de_get_input_encoding(c, NULL, DE_ENCODING_CP437);
280 d->input_encoding = DE_EXTENC_MAKE(d->input_encoding, DE_ENCSUBTYPE_HYBRID);
281 d->v12.opt_montage = de_get_ext_option_bool(c, "dlmaker:montage", 0);
283 d->fi = de_finfo_create(c);
284 d->fi->density.code = DE_DENSITY_UNK_UNITS;
285 d->fi->density.xdens = 6.0;
286 d->fi->density.ydens = 5.0;
288 if(!do_read_header(c, d)) goto done;
289 pos = d->hdr_size;
291 if(d->ver==3) {
292 for(k=0; k<d->num_images; k++) {
293 i64 xsize, ysize;
295 if(pos >= c->infile->len) goto done;
296 de_dbg(c, "image #%u at %"I64_FMT, (UI)k, pos);
297 de_dbg_indent(c, 1);
298 xsize = de_getu16le_p(&pos);
299 ysize = de_getu16le_p(&pos);
300 de_dbg_dimensions(c, xsize, ysize);
301 do_extract_v3_image(c, d, pos, xsize, ysize);
302 pos += xsize*ysize;
303 de_dbg_indent(c, -1);
306 else {
307 for(k=0; k<d->v12.num_screens; k++) {
308 if(pos >= c->infile->len) goto done;
309 de_dbg(c, "screen #%u at %"I64_FMT, (UI)k, pos);
310 de_dbg_indent(c, 1);
311 if(pos+V12_SCREEN_SIZE_IN_BYTES > c->infile->len) goto done;
312 do_v12screen(c, d, pos);
313 pos += V12_SCREEN_SIZE_IN_BYTES;
314 de_dbg_indent(c, -1);
318 anim_cmds_size = d->num_anim_code_units * d->anim_code_unit_size;
319 de_dbg(c, "anim commands at %"I64_FMT", len=%"I64_FMT, pos, anim_cmds_size);
320 pos += anim_cmds_size;
321 if(d->ver==3) {
322 do_audio(c, d, pos);
325 done:
326 de_dbg_indent_restore(c, saved_indent_level);
327 if(d) {
328 de_bitmap_destroy(d->v12.screen_img);
329 de_bitmap_destroy(d->v12.img);
330 de_finfo_destroy(c, d->fi);
331 de_free(c, d);
335 static int de_identify_dlmaker(deark *c)
337 u8 v;
338 i64 nscrn;
339 i64 hsize;
340 i64 ctlsize;
341 i64 expected_filesize;
343 if(!de_input_file_has_ext(c, "dl")) return 0;
344 v = de_getbyte(0);
345 if(v==1) {
346 nscrn = (i64)de_getbyte(21);
347 ctlsize = de_getu16le(22); // num frames * 1 byte
348 hsize = 792;
350 else if(v==2) {
351 if(de_getbyte(1)>2) return 0;
352 nscrn = (i64)de_getbyte(42);
353 ctlsize = de_getu32le(43) * 2;
354 hsize = 815;
356 else if(v==3) {
357 nscrn = (i64)de_getbyte(132);
358 ctlsize = de_getu16le(134) * 2;
359 hsize = 906;
361 else {
362 return 0;
365 if(nscrn==0 || ctlsize==0) return 0;
366 if(v==3) {
367 // This is just a minimum file size. v3 is hard to identify.
368 expected_filesize = hsize + nscrn*5 + ctlsize;
369 if(c->infile->len < expected_filesize) return 0;
370 return 10;
372 expected_filesize = hsize + V12_SCREEN_SIZE_IN_BYTES*nscrn + ctlsize;
373 if(c->infile->len == expected_filesize) return 90;
374 if(c->infile->len < expected_filesize) return 0;
375 // Allow for some padding or other unknown data at EOF.
376 if(c->infile->len > expected_filesize+511) return 0;
377 return 10;
380 static void de_help_dlmaker(deark *c)
382 de_msg(c, "-opt dlmaker:montage : Output the \"screens\", instead of the "
383 "individual images");
386 void de_module_dlmaker(deark *c, struct deark_module_info *mi)
388 mi->id = "dlmaker";
389 mi->desc = "DL animation (DL MAKER)";
390 mi->run_fn = de_run_dlmaker;
391 mi->identify_fn = de_identify_dlmaker;
392 mi->help_fn = de_help_dlmaker;