exe: Support PAK v1.6 self-extracting archives
[deark.git] / modules / grasp.c
blob71408766489906f7d180a71a40b16dc9fd55d73f
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 de_encoding input_encoding;
15 int is_le;
16 u8 is_amiga;
17 i64 index_pos;
18 i64 index_size;
19 } lctx;
21 // Returns 0 if there are no more files.
22 static int do_extract_file(deark *c, lctx *d, i64 fnum)
24 i64 index_entry_pos;
25 i64 data_block_pos;
26 i64 dpos;
27 i64 dlen;
28 de_finfo *fi = NULL;
29 de_ucstring *fname = NULL;
30 int saved_indent_level;
31 int need_errmsg = 0;
32 int retval = 0;
34 de_dbg_indent_save(c, &saved_indent_level);
35 index_entry_pos = d->index_pos+17*fnum;
36 data_block_pos = dbuf_getu32x(c->infile, index_entry_pos, d->is_le);
38 // The last "file" is usually not a file, but a "NULL terminator" with an
39 // offset of 0. Not very useful, since we already know how long the list is.
40 if(data_block_pos==0) {
41 de_dbg(c, "end-of-file-list marker found");
42 goto done;
45 de_dbg(c, "file #%d", (int)fnum);
46 de_dbg_indent(c, 1);
47 de_dbg(c, "index entry pos: %"I64_FMT, index_entry_pos);
48 de_dbg(c, "data block pos: %"I64_FMT, data_block_pos);
50 if(data_block_pos < d->index_size) {
51 need_errmsg = 1;
52 goto done;
55 if(de_getbyte(index_entry_pos+4)==0x00) {
56 need_errmsg = 1; // missing file name?
57 goto done;
60 fi = de_finfo_create(c);
61 fname = ucstring_create(c);
63 // Filenames are 13 bytes, NUL-padded.
64 dbuf_read_to_ucstring(c->infile, index_entry_pos+4, 13, fname, DE_CONVFLAG_STOP_AT_NUL,
65 d->input_encoding);
66 de_finfo_set_name_from_ucstring(c, fi, fname, 0);
67 fi->original_filename_flag = 1;
68 de_dbg(c, "file name: \"%s\"", ucstring_getpsz_d(fname));
70 dlen = dbuf_getu32x(c->infile, data_block_pos, d->is_le);
71 dpos = data_block_pos+4;
72 de_dbg(c, "file data at %"I64_FMT", len=%"I64_FMT, dpos, dlen);
73 if(dpos+dlen > c->infile->len) {
74 need_errmsg = 1;
75 goto done;
78 dbuf_create_file_from_slice(c->infile, dpos, dlen, NULL, fi, 0);
79 retval = 1;
81 done:
82 if(need_errmsg) {
83 de_err(c, "Bad file entry (#%d)", (int)fnum);
85 de_finfo_destroy(c, fi);
86 ucstring_destroy(fname);
87 de_dbg_indent_restore(c, saved_indent_level);
88 return retval;
91 static void de_run_graspgl(deark *c, de_module_params *mparams)
93 lctx *d = NULL;
94 i64 num_files;
95 i64 pos = 0;
96 i64 i;
98 d = de_malloc(c, sizeof(lctx));
100 if((UI)de_getu32be(0)==0x41470100U) {
101 // Ref: Aminet : gl2p1.lzh
102 d->is_amiga = 1;
105 if(d->is_amiga) {
106 de_declare_fmt(c, "Amiga GRASP GL");
107 pos += 4;
109 else {
110 de_declare_fmt(c, "GRASP GL");
111 d->is_le = 1;
114 d->input_encoding = de_get_input_encoding(c, NULL,
115 (d->is_amiga ? DE_ENCODING_LATIN1 : DE_ENCODING_CP437));
117 d->index_size = dbuf_getu16x(c->infile, pos, d->is_le);
118 pos += 2;
120 d->index_pos = pos;
121 de_dbg(c, "index size: %"I64_FMT, d->index_size);
123 // 17 bytes per file entry
124 num_files = (d->index_size+16)/17;
125 de_dbg(c, "max number of files: %d", (int)num_files);
127 for(i=0; i<num_files; i++) {
128 if(!do_extract_file(c, d, i))
129 break;
132 de_free(c, d);
135 static int de_identify_graspgl(deark *c)
137 i64 index_size;
138 i64 index_pos;
139 i64 first_offset;
140 int is_le = 1;
141 int gl_ext;
143 index_size = de_getu16le(0);
144 if(index_size==0x4741) {
145 if((UI)de_getu16be(2)==0x0100U) {
146 is_le = 0; // Amiga GL?
147 index_size = de_getu16be(4);
151 // Header should be a nonzero multiple of 17 bytes.
152 if(index_size==0 || (index_size%17 != 0)) return 0;
153 index_pos = is_le ? 2 : 6;
154 if(index_pos+index_size>c->infile->len) return 0;
156 gl_ext = de_input_file_has_ext(c, "gl");
158 // Most likely, the first embedded file immediately follows
159 // the header. If so, it's pretty good evidence this is a
160 // grasp_gl file.
161 first_offset = dbuf_getu32x(c->infile, index_pos, is_le);
163 if(first_offset>c->infile->len || first_offset<index_pos+index_size) return 0;
164 if(first_offset == index_pos+index_size)
165 return gl_ext ? 100 : 70;
167 if(gl_ext) return 5;
169 return 0;
172 void de_module_graspgl(deark *c, struct deark_module_info *mi)
174 mi->id = "graspgl";
175 mi->desc = "GRASP GL animation";
176 mi->run_fn = de_run_graspgl;
177 mi->identify_fn = de_identify_graspgl;
180 // **************************************************************************
181 // GRASP font (.set/.fnt)
182 // **************************************************************************
184 static void de_run_graspfont_oldfmt(deark *c)
186 i64 reported_filesize;
187 i32 first_codepoint;
188 struct de_bitmap_font *font = NULL;
189 i64 bytes_per_glyph;
190 i64 i;
191 i64 font_data_size;
192 u8 *font_data = NULL;
193 i64 glyph_rowspan;
194 struct de_encconv_state es;
196 font = de_create_bitmap_font(c);
198 reported_filesize = de_getu16le(0);
199 de_dbg(c, "reported file size: %d", (int)reported_filesize);
201 font->has_nonunicode_codepoints = 1;
202 font->has_unicode_codepoints = 1;
203 font->num_chars = (i64)de_getbyte(2);
204 if(font->num_chars==0) font->num_chars=256;
205 first_codepoint = (i32)de_getbyte(3);
206 font->nominal_width = (int)de_getbyte(4);
207 font->nominal_height = (int)de_getbyte(5);
208 bytes_per_glyph = (i64)de_getbyte(6);
210 de_dbg(c, "number of glyphs: %d, first codepoint: %d", (int)font->num_chars, (int)first_codepoint);
211 de_dbg(c, "glyph dimensions: %d"DE_CHAR_TIMES"%d, size in bytes: %d", font->nominal_width,
212 font->nominal_height, (int)bytes_per_glyph);
214 glyph_rowspan = (font->nominal_width+7)/8;
215 if(bytes_per_glyph < glyph_rowspan*font->nominal_height ||
216 font->nominal_width<1 || font->nominal_height<1)
218 de_err(c, "Bad font metrics");
219 goto done;
222 font->char_array = de_mallocarray(c, font->num_chars, sizeof(struct de_bitmap_font_char));
223 font_data_size = bytes_per_glyph * font->num_chars;
224 font_data = de_malloc(c, font_data_size);
226 // There's no way to tell what encoding a GRASP font uses, but CP437 is
227 // a reasonable guess.
228 de_encconv_init(&es, DE_ENCODING_CP437_G);
230 de_read(font_data, 7, font_data_size);
232 for(i=0; i<font->num_chars; i++) {
233 font->char_array[i].width = font->nominal_width;
234 font->char_array[i].height = font->nominal_height;
235 font->char_array[i].rowspan = glyph_rowspan;
237 font->char_array[i].codepoint_nonunicode = first_codepoint + (i32)i;
239 font->char_array[i].codepoint_unicode =
240 de_char_to_unicode_ex(first_codepoint + (i32)i, &es);
242 font->char_array[i].bitmap = &font_data[i*bytes_per_glyph];
245 de_font_bitmap_font_to_image(c, font, NULL, 0);
247 done:
248 if(font) {
249 if(font->char_array) {
250 de_free(c, font->char_array);
252 de_destroy_bitmap_font(c, font);
254 de_free(c, font_data);
257 // Caution: This code is not based on any official specifications.
258 static void de_run_graspfont_newfmt(deark *c)
260 struct de_bitmap_font *font = NULL;
261 de_ucstring *fontname = NULL;
262 i64 k;
263 i64 glyph_offsets_table_pos;
264 i64 widths_table_pos;
265 i64 glyph_rowspan;
266 int tmp_width;
267 int ch_max_width = 0;
269 de_dbg(c, "header at %d", 0);
270 de_dbg_indent(c, 1);
272 font = de_create_bitmap_font(c);
273 font->has_nonunicode_codepoints = 1;
275 fontname = ucstring_create(c);
276 dbuf_read_to_ucstring(c->infile, 1, 13, fontname, DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_ASCII);
277 de_dbg(c, "name: \"%s\"", ucstring_getpsz(fontname));
279 font->num_chars = (i64)de_getbyte(16);
280 de_dbg(c, "number of glyphs: %d", (int)font->num_chars);
282 tmp_width = (int)de_getbyte(19);
283 de_dbg(c, "font width: %d", tmp_width);
284 font->nominal_height = (int)de_getbyte(20);
285 de_dbg(c, "font height: %d", font->nominal_height);
287 glyph_rowspan = (i64)de_getbyte(21);
289 de_dbg_indent(c, -1);
291 glyph_offsets_table_pos = 59;
292 widths_table_pos = glyph_offsets_table_pos +2*font->num_chars;
293 if(widths_table_pos<249)
294 widths_table_pos = 249;
295 de_dbg(c, "glyph offsets table at %d, width table at %d", (int)glyph_offsets_table_pos,
296 (int)widths_table_pos);
297 de_dbg_indent(c, 1);
299 font->char_array = de_mallocarray(c, font->num_chars, sizeof(struct de_bitmap_font_char));
301 for(k=0; k<font->num_chars; k++) {
302 i64 ch_offset;
303 i64 bitmapsize;
304 struct de_bitmap_font_char *ch = &font->char_array[k];
306 ch->codepoint_nonunicode = (i32)(33 + k);
308 ch_offset = de_getu16le(glyph_offsets_table_pos + 2 + 2*k);
310 ch->width = (int)de_getbyte(widths_table_pos + 1 + k);
311 de_dbg2(c, "ch[%d]: codepoint=%d, width=%d, glyph_offs=%d", (int)k,
312 (int)ch->codepoint_nonunicode,
313 (int)ch->width, (int)ch_offset);
315 if(ch->width<1) continue;
317 if(ch->width > ch_max_width) ch_max_width = ch->width;
318 ch->height = font->nominal_height;
319 ch->rowspan = glyph_rowspan;
320 bitmapsize = ch->rowspan * ch->height;
321 ch->bitmap = de_malloc(c, bitmapsize);
322 de_read(ch->bitmap, ch_offset, bitmapsize);
325 de_dbg_indent(c, -1);
327 de_dbg(c, "calculated maximum width: %d", (int)ch_max_width);
328 font->nominal_width = ch_max_width;
330 de_font_bitmap_font_to_image(c, font, NULL, 0);
332 if(font) {
333 if(font->char_array) {
334 for(k=0; k<font->num_chars; k++) {
335 de_free(c, font->char_array[k].bitmap);
337 de_free(c, font->char_array);
339 de_destroy_bitmap_font(c, font);
342 ucstring_destroy(fontname);
345 static int gfont_is_new_format(deark *c)
347 i64 reported_filesize;
349 if(de_getbyte(0)==0x10) {
350 reported_filesize = de_getu16le(25);
351 if(reported_filesize == c->infile->len) {
352 return 1;
355 return 0;
358 static void de_run_graspfont(deark *c, de_module_params *mparams)
360 if(gfont_is_new_format(c)) {
361 de_declare_fmt(c, "GRASP font (new)");
362 de_run_graspfont_newfmt(c);
364 else {
365 de_declare_fmt(c, "GRASP font (old)");
366 de_run_graspfont_oldfmt(c);
370 static int de_identify_graspfont(deark *c)
372 i64 reported_filesize;
373 i64 num_chars;
374 i64 bytes_per_glyph;
376 if(!de_input_file_has_ext(c, "set") && !de_input_file_has_ext(c, "fnt"))
377 return 0;
379 if(gfont_is_new_format(c)) {
380 return 30;
383 reported_filesize = de_getu16le(0);
384 if(reported_filesize != c->infile->len) return 0;
385 num_chars = (i64)de_getbyte(2);
386 if(num_chars==0) num_chars=256;
387 bytes_per_glyph = (i64)de_getbyte(6);
388 if(7+num_chars*bytes_per_glyph == reported_filesize)
389 return 100;
390 return 0;
393 void de_module_graspfont(deark *c, struct deark_module_info *mi)
395 mi->id = "graspfont";
396 mi->desc = "GRASP font";
397 mi->run_fn = de_run_graspfont;
398 mi->identify_fn = de_identify_graspfont;