New "ea_data" module
[deark.git] / modules / grasp.c
blobe904065a2c30b59dfb5f4880f9be4eff512a6071
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // GRASP GL animation format
6 // GRASP font format
8 #include <deark-config.h>
9 #include <deark-private.h>
10 DE_DECLARE_MODULE(de_module_graspgl);
11 DE_DECLARE_MODULE(de_module_graspfont);
13 typedef struct localctx_struct {
14 i64 dir_header_nbytes;
15 } lctx;
17 // Returns 0 if there are no more files.
18 static int do_extract_file(deark *c, lctx *d, i64 fnum)
20 i64 pos;
21 i64 file_info_offset;
22 i64 file_data_offset;
23 i64 file_size;
24 de_finfo *fi = NULL;
25 de_ucstring *fname = NULL;
26 int saved_indent_level;
27 int retval = 1;
29 de_dbg_indent_save(c, &saved_indent_level);
30 pos = 2+17*fnum;
31 file_info_offset = de_getu32le(pos);
33 // The last "file" is usually not a file, but a "NULL terminator" with
34 // an offset of 0. This is worse than useless, since we already know
35 // how long the list is.
36 if(file_info_offset==0) {
37 de_dbg(c, "end-of-file-list marker found");
38 retval = 0;
39 goto done;
42 de_dbg(c, "file #%d offset: %d", (int)fnum, (int)file_info_offset);
43 de_dbg_indent(c, 1);
45 if(file_info_offset < d->dir_header_nbytes) {
46 de_warn(c, "Bad file offset (%d)", (int)file_info_offset);
47 goto done;
50 if(de_getbyte(pos+4)==0x00) {
51 de_warn(c, "Missing file name");
52 goto done;
55 fi = de_finfo_create(c);
57 fname = ucstring_create(c);
59 // In a Grasp GL file, filenames are 13 bytes, NUL-padded.
60 dbuf_read_to_ucstring(c->infile, pos+4, 13, fname, DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_ASCII);
61 de_finfo_set_name_from_ucstring(c, fi, fname, 0);
62 fi->original_filename_flag = 1;
63 de_dbg(c, "file name: %s", ucstring_getpsz(fname));
65 file_size = de_getu32le(file_info_offset);
66 de_dbg(c, "file size: %d", (int)file_size);
68 file_data_offset = file_info_offset+4;
69 if(file_data_offset > c->infile->len) goto done;
70 if(file_size > DE_MAX_SANE_OBJECT_SIZE) goto done;
72 dbuf_create_file_from_slice(c->infile, file_data_offset, file_size, NULL, fi, 0);
74 done:
75 de_finfo_destroy(c, fi);
76 ucstring_destroy(fname);
77 de_dbg_indent_restore(c, saved_indent_level);
78 return retval;
82 static void de_run_graspgl(deark *c, de_module_params *mparams)
84 lctx *d = NULL;
85 i64 num_files;
86 i64 pos;
87 i64 i;
89 d = de_malloc(c, sizeof(lctx));
91 pos = 0;
92 d->dir_header_nbytes = de_getu16le(pos);
93 de_dbg(c, "header bytes: %d", (int)d->dir_header_nbytes);
95 // 17 bytes per file entry
96 num_files = (d->dir_header_nbytes+16)/17;
97 de_dbg(c, "number of files: %d", (int)num_files);
99 for(i=0; i<num_files; i++) {
100 if(!do_extract_file(c, d, i))
101 break;
104 de_free(c, d);
107 static int de_identify_graspgl(deark *c)
109 i64 dir_header_nbytes;
110 i64 first_offset;
111 int gl_ext;
113 dir_header_nbytes = de_getu16le(0);
115 // Header should be a nonzero multiple of 17 bytes.
116 if(dir_header_nbytes==0 || (dir_header_nbytes%17 != 0)) return 0;
118 gl_ext = de_input_file_has_ext(c, "gl");
120 // Most likely, the first embedded file immediately follows
121 // the header. If so, it's pretty good evidence this is a
122 // grasp_gl file.
123 first_offset = de_getu32le(2);
124 if(first_offset == dir_header_nbytes + 2)
125 return gl_ext ? 100 : 70;
127 if(gl_ext) return 5;
129 return 0;
132 void de_module_graspgl(deark *c, struct deark_module_info *mi)
134 mi->id = "graspgl";
135 mi->desc = "GRASP GL animation";
136 mi->run_fn = de_run_graspgl;
137 mi->identify_fn = de_identify_graspgl;
140 // **************************************************************************
141 // GRASP font (.set/.fnt)
142 // **************************************************************************
144 static void de_run_graspfont_oldfmt(deark *c)
146 i64 reported_filesize;
147 i32 first_codepoint;
148 struct de_bitmap_font *font = NULL;
149 i64 bytes_per_glyph;
150 i64 i;
151 i64 font_data_size;
152 u8 *font_data = NULL;
153 i64 glyph_rowspan;
154 struct de_encconv_state es;
156 font = de_create_bitmap_font(c);
158 reported_filesize = de_getu16le(0);
159 de_dbg(c, "reported file size: %d", (int)reported_filesize);
161 font->has_nonunicode_codepoints = 1;
162 font->has_unicode_codepoints = 1;
163 font->num_chars = (i64)de_getbyte(2);
164 if(font->num_chars==0) font->num_chars=256;
165 first_codepoint = (i32)de_getbyte(3);
166 font->nominal_width = (int)de_getbyte(4);
167 font->nominal_height = (int)de_getbyte(5);
168 bytes_per_glyph = (i64)de_getbyte(6);
170 de_dbg(c, "number of glyphs: %d, first codepoint: %d", (int)font->num_chars, (int)first_codepoint);
171 de_dbg(c, "glyph dimensions: %d"DE_CHAR_TIMES"%d, size in bytes: %d", font->nominal_width,
172 font->nominal_height, (int)bytes_per_glyph);
174 glyph_rowspan = (font->nominal_width+7)/8;
175 if(bytes_per_glyph < glyph_rowspan*font->nominal_height ||
176 font->nominal_width<1 || font->nominal_height<1)
178 de_err(c, "Bad font metrics");
179 goto done;
182 font->char_array = de_mallocarray(c, font->num_chars, sizeof(struct de_bitmap_font_char));
183 font_data_size = bytes_per_glyph * font->num_chars;
184 font_data = de_malloc(c, font_data_size);
186 // There's no way to tell what encoding a GRASP font uses, but CP437 is
187 // a reasonable guess.
188 de_encconv_init(&es, DE_ENCODING_CP437_G);
190 de_read(font_data, 7, font_data_size);
192 for(i=0; i<font->num_chars; i++) {
193 font->char_array[i].width = font->nominal_width;
194 font->char_array[i].height = font->nominal_height;
195 font->char_array[i].rowspan = glyph_rowspan;
197 font->char_array[i].codepoint_nonunicode = first_codepoint + (i32)i;
199 font->char_array[i].codepoint_unicode =
200 de_char_to_unicode_ex(first_codepoint + (i32)i, &es);
202 font->char_array[i].bitmap = &font_data[i*bytes_per_glyph];
205 de_font_bitmap_font_to_image(c, font, NULL, 0);
207 done:
208 if(font) {
209 if(font->char_array) {
210 de_free(c, font->char_array);
212 de_destroy_bitmap_font(c, font);
214 de_free(c, font_data);
217 // Caution: This code is not based on any official specifications.
218 static void de_run_graspfont_newfmt(deark *c)
220 struct de_bitmap_font *font = NULL;
221 de_ucstring *fontname = NULL;
222 i64 k;
223 i64 glyph_offsets_table_pos;
224 i64 widths_table_pos;
225 i64 glyph_rowspan;
226 int tmp_width;
227 int ch_max_width = 0;
229 de_dbg(c, "header at %d", 0);
230 de_dbg_indent(c, 1);
232 font = de_create_bitmap_font(c);
233 font->has_nonunicode_codepoints = 1;
235 fontname = ucstring_create(c);
236 dbuf_read_to_ucstring(c->infile, 1, 13, fontname, DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_ASCII);
237 de_dbg(c, "name: \"%s\"", ucstring_getpsz(fontname));
239 font->num_chars = (i64)de_getbyte(16);
240 de_dbg(c, "number of glyphs: %d", (int)font->num_chars);
242 tmp_width = (int)de_getbyte(19);
243 de_dbg(c, "font width: %d", tmp_width);
244 font->nominal_height = (int)de_getbyte(20);
245 de_dbg(c, "font height: %d", font->nominal_height);
247 glyph_rowspan = (i64)de_getbyte(21);
249 de_dbg_indent(c, -1);
251 glyph_offsets_table_pos = 59;
252 widths_table_pos = glyph_offsets_table_pos +2*font->num_chars;
253 if(widths_table_pos<249)
254 widths_table_pos = 249;
255 de_dbg(c, "glyph offsets table at %d, width table at %d", (int)glyph_offsets_table_pos,
256 (int)widths_table_pos);
257 de_dbg_indent(c, 1);
259 font->char_array = de_mallocarray(c, font->num_chars, sizeof(struct de_bitmap_font_char));
261 for(k=0; k<font->num_chars; k++) {
262 i64 ch_offset;
263 i64 bitmapsize;
264 struct de_bitmap_font_char *ch = &font->char_array[k];
266 ch->codepoint_nonunicode = (i32)(33 + k);
268 ch_offset = de_getu16le(glyph_offsets_table_pos + 2 + 2*k);
270 ch->width = (int)de_getbyte(widths_table_pos + 1 + k);
271 de_dbg2(c, "ch[%d]: codepoint=%d, width=%d, glyph_offs=%d", (int)k,
272 (int)ch->codepoint_nonunicode,
273 (int)ch->width, (int)ch_offset);
275 if(ch->width<1) continue;
277 if(ch->width > ch_max_width) ch_max_width = ch->width;
278 ch->height = font->nominal_height;
279 ch->rowspan = glyph_rowspan;
280 bitmapsize = ch->rowspan * ch->height;
281 ch->bitmap = de_malloc(c, bitmapsize);
282 de_read(ch->bitmap, ch_offset, bitmapsize);
285 de_dbg_indent(c, -1);
287 de_dbg(c, "calculated maximum width: %d", (int)ch_max_width);
288 font->nominal_width = ch_max_width;
290 de_font_bitmap_font_to_image(c, font, NULL, 0);
292 if(font) {
293 if(font->char_array) {
294 for(k=0; k<font->num_chars; k++) {
295 de_free(c, font->char_array[k].bitmap);
297 de_free(c, font->char_array);
299 de_destroy_bitmap_font(c, font);
302 ucstring_destroy(fontname);
305 static int gfont_is_new_format(deark *c)
307 i64 reported_filesize;
309 if(de_getbyte(0)==0x10) {
310 reported_filesize = de_getu16le(25);
311 if(reported_filesize == c->infile->len) {
312 return 1;
315 return 0;
318 static void de_run_graspfont(deark *c, de_module_params *mparams)
320 if(gfont_is_new_format(c)) {
321 de_declare_fmt(c, "GRASP font (new)");
322 de_run_graspfont_newfmt(c);
324 else {
325 de_declare_fmt(c, "GRASP font (old)");
326 de_run_graspfont_oldfmt(c);
330 static int de_identify_graspfont(deark *c)
332 i64 reported_filesize;
333 i64 num_chars;
334 i64 bytes_per_glyph;
336 if(!de_input_file_has_ext(c, "set") && !de_input_file_has_ext(c, "fnt"))
337 return 0;
339 if(gfont_is_new_format(c)) {
340 return 30;
343 reported_filesize = de_getu16le(0);
344 if(reported_filesize != c->infile->len) return 0;
345 num_chars = (i64)de_getbyte(2);
346 if(num_chars==0) num_chars=256;
347 bytes_per_glyph = (i64)de_getbyte(6);
348 if(7+num_chars*bytes_per_glyph == reported_filesize)
349 return 100;
350 return 0;
353 void de_module_graspfont(deark *c, struct deark_module_info *mi)
355 mi->id = "graspfont";
356 mi->desc = "GRASP font";
357 mi->run_fn = de_run_graspfont;
358 mi->identify_fn = de_identify_graspfont;