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
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
;
17 // Returns 0 if there are no more files.
18 static int do_extract_file(deark
*c
, lctx
*d
, i64 fnum
)
25 de_ucstring
*fname
= NULL
;
26 int saved_indent_level
;
29 de_dbg_indent_save(c
, &saved_indent_level
);
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");
42 de_dbg(c
, "file #%d offset: %d", (int)fnum
, (int)file_info_offset
);
45 if(file_info_offset
< d
->dir_header_nbytes
) {
46 de_warn(c
, "Bad file offset (%d)", (int)file_info_offset
);
50 if(de_getbyte(pos
+4)==0x00) {
51 de_warn(c
, "Missing file name");
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);
75 de_finfo_destroy(c
, fi
);
76 ucstring_destroy(fname
);
77 de_dbg_indent_restore(c
, saved_indent_level
);
82 static void de_run_graspgl(deark
*c
, de_module_params
*mparams
)
89 d
= de_malloc(c
, sizeof(lctx
));
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
))
107 static int de_identify_graspgl(deark
*c
)
109 i64 dir_header_nbytes
;
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
123 first_offset
= de_getu32le(2);
124 if(first_offset
== dir_header_nbytes
+ 2)
125 return gl_ext
? 100 : 70;
132 void de_module_graspgl(deark
*c
, struct deark_module_info
*mi
)
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
;
148 struct de_bitmap_font
*font
= NULL
;
152 u8
*font_data
= NULL
;
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");
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);
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
;
223 i64 glyph_offsets_table_pos
;
224 i64 widths_table_pos
;
227 int ch_max_width
= 0;
229 de_dbg(c
, "header at %d", 0);
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
);
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
++) {
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);
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
) {
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
);
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
;
336 if(!de_input_file_has_ext(c
, "set") && !de_input_file_has_ext(c
, "fnt"))
339 if(gfont_is_new_format(c
)) {
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
)
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
;