static void do_glyphs(deark *c, lctx *d) { struct de_bitmap_font *font = NULL; de_byte *font_data = NULL; de_int64 i; de_int64 glyph_rowspan; font = de_create_bitmap_font(c); font->has_nonunicode_codepoints = 1; font->nominal_width = (int)d->glyph_width; font->nominal_height = (int)d->glyph_height; font->num_chars = d->num_glyphs; // This may increase later glyph_rowspan = (d->glyph_width+7)/8; d->num_chars_alloc = d->num_glyphs; if(d->read_extra_codepoints) d->num_chars_alloc += MAX_EXTRA_CODEPOINTS; d->index_of_first_extra_codepoint = d->num_glyphs; d->num_extra_codepoints = 0; font->char_array = de_malloc(c, d->num_chars_alloc * sizeof(struct de_bitmap_font_char)); font_data = de_malloc(c, d->font_data_size); de_read(font_data, d->headersize, d->font_data_size); for(i=0; i<d->num_chars_alloc; i++) { font->char_array[i].width = font->nominal_width; font->char_array[i].height = font->nominal_height; font->char_array[i].rowspan = glyph_rowspan; if(i<d->num_glyphs) font->char_array[i].codepoint_nonunicode = (de_int32)i; else font->char_array[i].codepoint_nonunicode = DE_INVALID_CODEPOINT; font->char_array[i].codepoint_unicode = DE_INVALID_CODEPOINT; if(i<d->num_glyphs) font->char_array[i].bitmap = &font_data[i*d->bytes_per_glyph]; } if(d->has_unicode_table) { if(d->version==2) do_psf2_unicode_table(c, d, font); else do_psf1_unicode_table(c, d, font); } if(d->num_extra_codepoints>0) { font->num_chars = d->index_of_first_extra_codepoint + d->num_extra_codepoints; de_dbg(c, "codepoints aliases: %d\n", (int)d->num_extra_codepoints); de_dbg(c, "total characters: %d\n", (int)font->num_chars); } de_font_bitmap_font_to_image(c, font, NULL, 0); if(font) { de_free(c, font->char_array); de_destroy_bitmap_font(c, font); } de_free(c, font_data); }
static int do_decode_rle(deark *c, lctx *d, i64 pos1, dbuf *unc_pixels) { u8 b; i64 count; i64 k; u8 buf[8]; i64 pos = pos1; while(1) { if(pos >= c->infile->len) break; if(unc_pixels->len >= d->main_image.img_size_in_bytes) break; b = de_getbyte(pos); pos++; if(b & 0x80) { // RLE block count = (i64)(b - 0x80) + 1; de_read(buf, pos, d->bytes_per_pixel); pos += d->bytes_per_pixel; for(k=0; k<count; k++) { dbuf_write(unc_pixels, buf, d->bytes_per_pixel); } } else { // uncompressed block count = (i64)(b) + 1; dbuf_copy(c->infile, pos, count * d->bytes_per_pixel, unc_pixels); pos += count * d->bytes_per_pixel; } } de_dbg(c, "decompressed %d bytes to %d bytes", (int)(pos-pos1), (int)unc_pixels->len); return 1; }
static void do_extract_png_or_jp2(deark *c, lctx *d, struct page_ctx *pg) { de_byte buf[8]; de_finfo *fi = NULL; de_dbg(c, "Trying to extract file at %d\n", (int)pg->image_pos); // Detect the format de_read(buf, pg->image_pos, sizeof(buf)); fi = de_finfo_create(c); de_finfo_set_name_from_sz(c, fi, pg->filename_token, DE_ENCODING_ASCII); if(buf[4]=='j' && buf[5]=='P') { dbuf_create_file_from_slice(c->infile, pg->image_pos, pg->image_len, "jp2", fi, 0); } else if(buf[0]==0x89 && buf[1]==0x50) { dbuf_create_file_from_slice(c->infile, pg->image_pos, pg->image_len, "png", fi, 0); } else { de_err(c, "(Image #%d) Unidentified file format\n", pg->image_num); } de_finfo_destroy(c, fi); }
static int do_identify_png_internal(deark *c) { u8 buf[8]; de_read(buf, 0, sizeof(buf)); if(!de_memcmp(buf, "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", 8)) return DE_PNGFMT_PNG; if(!de_memcmp(buf, "\x8b\x4a\x4e\x47\x0d\x0a\x1a\x0a", 8)) return DE_PNGFMT_JNG; if(!de_memcmp(buf, "\x8a\x4d\x4e\x47\x0d\x0a\x1a\x0a", 8)) return DE_PNGFMT_MNG; return 0; }
static int de_identify_cardfile(deark *c) { de_byte buf[4]; de_read(buf, 0, 4); if(!de_memcmp(buf, "MGC", 3)) return 80; if(!de_memcmp(buf, "RRG", 3)) return 80; return 0; }
static int de_identify_psionapp(deark *c) { de_byte b[16]; de_read(b, 0, 16); if(!de_memcmp(b, "ImageFileType**\0", 16)) return 100; if(!de_memcmp(b, "OPLObjectFile**\0", 16)) return 100; return 0; }
static int de_identify_gzip(deark *c) { u8 buf[3]; de_read(buf, 0, 3); if(buf[0]==0x1f && buf[1]==0x8b) { if(buf[2]==0x08) return 100; return 10; } return 0; }
static int identify_fmt(deark *c, de_int64 pos) { de_byte buf[3]; de_read(buf, pos, 3); if(buf[0]!='P') return 0; if(buf[1]=='7' && buf[2]==0x0a) return FMT_PAM; if(buf[1]>='1' && buf[1]<='6') return buf[1] - '0'; return 0; }
static void read_compression_type(deark *c, lctx *d, i64 pos) { u8 buf[16]; de_dbg(c, "compression type at %d", (int)pos); de_read(buf, pos, sizeof(buf)); if(!de_memcmp(buf, "lzma\0", 5)) { d->cmpr_type = DE_RPM_CMPR_LZMA; } // Other valid compression types are "gzip", "bzip2", and "xz". // We'll autodetect most of them, but lzma is hard to detect. }
static int de_identify_tga(deark *c) { u8 b[18]; u8 x; int has_tga_ext; if(has_signature(c)) { return 100; } // TGA v1 format has no signature, but there are only a few common types of // it. We'll at least try to identify anything that we support. de_read(b, 0, 18); if(b[1]>1) return 0; // Color map type should be 0 or 1. // bits/pixel: if(b[16]!=1 && b[16]!=8 && b[16]!=15 && b[16]!=16 && b[16]!=24 && b[16]!=32) return 0; if(b[2]!=0 && b[2]!=1 && b[2]!=2 && b[2]!=3 && b[2]!=9 && b[2]!=10 && b[2]!=11 && b[2]!=32 && b[2]!=33) { return 0; // Unknown image type } if(b[12]==0 && b[13]==0) return 0; // Width can't be 0. if(b[14]==0 && b[15]==0) return 0; // Height can't be 0. // Bits per palette entry. Supposed to be 0 if there is no palette, but // in practice it may be 24 instead. if((b[1]==0 && b[7]==0) || b[7]==15 || b[7]==16 || b[7]==24 || b[7]==32) { ; } else { return 0; } has_tga_ext = de_input_file_has_ext(c, "tga"); x = b[17]&0x0f; // Number of attribute bits if(x!=0 && x!=1 && x!=8 && !has_tga_ext) return 0; if(has_tga_ext) { return 100; } if(de_input_file_has_ext(c, "vst")) { return 40; } return 8; }
static int de_identify_wri(deark *c) { u8 buf[6]; de_read(buf, 0, 6); if((buf[0]==0x31 || buf[0]==0x32) && !de_memcmp(&buf[1], "\xbe\x00\x00\x00\xab", 5)) { i64 pnMac; pnMac = de_getu16le(48*2); if(pnMac==0) return 0; // Apparently MSWord, not Write return 100; } return 0; }
static int de_identify_macpaint(deark *c) { de_byte buf[8]; de_read(buf, 65, 8); // Not all MacPaint files can be easily identified, but this will work // for some of them. if(!de_memcmp(buf, "PNTGMPNT", 8)) return 80; if(!de_memcmp(buf, "PNTG", 4)) return 70; if(de_input_file_has_ext(c, "mac")) return 10; if(de_input_file_has_ext(c, "macp")) return 15; if(de_input_file_has_ext(c, "pntg")) return 15; return 0; }
static void wri_convert_image_pal4planar(deark *c, i64 fpos, i64 bytes_per_row_per_plane, de_bitmap *img) { const i64 nplanes = 4; i64 i, j, plane; i64 rowspan; u8 *rowbuf = NULL; static const u32 pal16[16] = { 0x000000,0x800000,0x008000,0x808000,0x000080,0x800080,0x008080,0x808080, 0xc0c0c0,0xff0000,0x00ff00,0xffff00,0x0000ff,0xff00ff,0x00ffff,0xffffff }; rowspan = bytes_per_row_per_plane * nplanes; rowbuf = de_malloc(c, rowspan); // The usual order seems to be // row0_plane0x1, row0_plane0x2, row0_plane0x4, row0_plane0x8, // row1_plane0x1, row1_plane0x2, row1_plane0x4, row1_plane0x8, // ... // But I have seen another, and I see no way to detect/support it. for(j=0; j<img->height; j++) { de_read(rowbuf, fpos+j*rowspan, rowspan); for(i=0; i<img->width; i++) { unsigned int palent = 0; u32 clr; for(plane=0; plane<nplanes; plane++) { unsigned int n = 0; i64 idx; idx = bytes_per_row_per_plane*plane + i/8; if(idx<rowspan) n = rowbuf[idx]; if(n & (1<<(7-i%8))) { palent |= (1<<plane); } } clr = DE_MAKE_OPAQUE(pal16[palent]); de_bitmap_setpixel_rgb(img, i, j, clr); } } de_free(c, rowbuf); }
static void handle_embedded_file(deark *c, lctx *d, de_int64 offset, de_int64 len) { de_byte buf[16]; const char *ext; int extract_this_file; int is_pic; de_dbg(c, "embedded file at %d, len=%d\n", (int)offset, (int)len); is_pic = 0; ext = "bin"; extract_this_file = 0; if(len>0 && c->extract_level>=2) extract_this_file = 1; // As far as I can tell, there's no way to tell the type of an // embedded file, except by sniffing it. de_read(buf, offset, 16); if(len>=8) { if(!de_memcmp(buf, "PIC\xdc\x30\x30", 6)) { // Looks like a PIC file is_pic = 1; ext = "pic"; extract_this_file = 1; } } if(extract_this_file) { if(is_pic && d->convert_images) { // Convert PIC to PNG. // For consistency, this option shouldn't exist. But I'm not sure that // PIC files embedded in APP files are really the same as PIC files on // their own. They might need special handling. Until I'm sure they don't, // I'll leave this option here. de_run_module_by_id_on_slice(c, "psionpic", NULL, c->infile, offset, len); } else { // Just extract the file dbuf_create_file_from_slice(c->infile, offset, len, ext, NULL, is_pic?0:DE_CREATEFLAG_IS_AUX); } } else { de_dbg(c, "(not extracting this file)\n"); } }
static void do_identify_bitstream(deark *c, lctx *d, struct stream_info *si, i64 pos, i64 len) { u8 idbuf[16]; size_t bytes_to_scan; size_t k; bytes_to_scan = (size_t)len; if(bytes_to_scan > sizeof(idbuf)) { bytes_to_scan = sizeof(idbuf); } de_read(idbuf, pos, bytes_to_scan); for(k=0; k<DE_ITEMS_IN_ARRAY(stream_type_info_arr); k++) { if(!de_memcmp(idbuf, stream_type_info_arr[k].magic, stream_type_info_arr[k].magic_len)) { si->sti = &stream_type_info_arr[k]; si->stream_type = si->sti->stream_type; break; } } if(si->stream_type==STREAMTYPE_VORBIS) { d->found_vorbis = 1; } else if(si->stream_type==STREAMTYPE_THEORA) { d->found_theora = 1; } else if(si->stream_type==STREAMTYPE_SKELETON) { d->found_skeleton = 1; } else if(si->sti && (si->sti->flags&0x1)) { d->found_ogm = 1; } if(si->stream_type!=STREAMTYPE_VORBIS && si->stream_type!=STREAMTYPE_THEORA) { d->has_non_vorbis_non_theora_stream = 1; } de_dbg(c, "bitstream type: %s", si->sti?si->sti->name:"unknown"); }
static int read_pam_header_line(deark *c, lctx *d, struct page_ctx *pg, de_int64 pos, de_int64 *content_len, de_int64 *total_len, char *linebuf, size_t linebuf_len) { int ret; de_int64 amt_to_read; linebuf[0]='\0'; ret = dbuf_find_line(c->infile, pos, content_len, total_len); if(!ret) return 0; amt_to_read = *content_len; if(amt_to_read > (de_int64)(linebuf_len-1)) amt_to_read = (de_int64)(linebuf_len-1); de_read((de_byte*)linebuf, pos, amt_to_read); *content_len = amt_to_read; linebuf[amt_to_read] = '\0'; return 1; }
static void de_run_rpm(deark *c, de_module_params *mparams) { lctx *d = NULL; i64 pos; u8 buf[8]; const char *ext; i64 section_size = 0; de_finfo *fi = NULL; char filename[128]; d = de_malloc(c, sizeof(lctx)); if(!do_lead_section(c, d)) { goto done; } pos = 96; if(!do_header_structure(c, d, 1, pos, §ion_size)) { goto done; } pos += section_size; // Header structures are 8-byte aligned. The first one always starts at // offset 96, so we don't have to worry about it. But we need to make // sure the second one is aligned. pos = ((pos + 7)/8)*8; if(!do_header_structure(c, d, 0, pos, §ion_size)) { goto done; } pos += section_size; de_dbg(c, "data pos: %d", (int)pos); if(pos > c->infile->len) goto done; // There is usually a tag that indicates the compression format, but we // primarily figure out the format by sniffing its magic number, on the // theory that that's more reliable. // TODO: I think it's also theoretically possible that it could use an archive // format other than cpio. de_read(buf, pos, 8); if(buf[0]==0x1f && buf[1]==0x8b) { ext = "cpio.gz"; } else if(buf[0]==0x42 && buf[1]==0x5a && buf[2]==0x68) { ext = "cpio.bz2"; } else if(buf[0]==0xfd && buf[1]==0x37 && buf[2]==0x7a) { ext = "cpio.xz"; } else if(d->cmpr_type==DE_RPM_CMPR_LZMA || buf[0]==0x5d) { ext = "cpio.lzma"; } else { de_warn(c, "Unidentified compression or archive format"); ext = "cpio.bin"; } if(d->name_srd && c->filenames_from_file) { const char *version2 = "x"; const char *release2 = "x"; if(d->version_srd) version2 = d->version_srd->sz; if(d->release_srd) release2 = d->release_srd->sz; fi = de_finfo_create(c); de_snprintf(filename, sizeof(filename), "%s-%s.%s", d->name_srd->sz, version2, release2); de_finfo_set_name_from_sz(c, fi, filename, 0, DE_ENCODING_ASCII); } dbuf_create_file_from_slice(c->infile, pos, c->infile->len - pos, ext, fi, 0); done: de_finfo_destroy(c, fi); if(d) { de_destroy_stringreaderdata(c, d->name_srd); de_destroy_stringreaderdata(c, d->release_srd); de_destroy_stringreaderdata(c, d->version_srd); de_free(c, d); } }
// Note that a header *structure* is distinct from the header *section*. // Both the signature section and the header section use a header structure. static int do_header_structure(deark *c, lctx *d, int is_sig, i64 pos1, i64 *section_size) { i64 pos; i64 indexcount; i64 storesize; u8 buf[4]; u8 header_ver; i64 i; i64 tag_id, tag_type, tag_offset, tag_count; i64 data_store_pos; const char *hdrname; int retval = 0; hdrname = is_sig?"sig":"hdr"; pos = pos1; de_dbg(c, "%s section at %d", hdrname, (int)pos1); de_dbg_indent(c, 1); de_read(buf, pos, 4); if(buf[0]!=0x8e || buf[1]!=0xad || buf[2]!=0xe8) { de_err(c, "Bad header signature at %d", (int)pos); goto done; } header_ver = buf[3]; if(header_ver != 1) { de_err(c, "Unsupported header version"); goto done; } pos += 8; indexcount = de_getu32be(pos); storesize = de_getu32be(pos+4); de_dbg(c, "%s: pos=%d indexcount=%d storesize=%d", hdrname, (int)pos, (int)indexcount, (int)storesize); pos += 8; if(indexcount>1000) goto done; data_store_pos = pos + 16*indexcount; de_dbg(c, "%s: tag table at %d", hdrname, (int)pos); de_dbg_indent(c, 1); for(i=0; i<indexcount; i++) { tag_id = de_getu32be(pos); tag_type = de_getu32be(pos+4); tag_offset = de_getu32be(pos+8); tag_count = de_getu32be(pos+12); de_dbg2(c, "tag #%d type=%d offset=%d count=%d", (int)tag_id, (int)tag_type, (int)tag_offset, (int)tag_count); if(is_sig==0 && tag_id==DE_RPMTAG_PAYLOADCOMPRESSOR && tag_type==DE_RPM_STRING_TYPE) { read_compression_type(c, d, data_store_pos+tag_offset); } else if(is_sig==0 && tag_id==DE_RPMTAG_NAME && tag_type==DE_RPM_STRING_TYPE) { if(!d->name_srd) { d->name_srd = dbuf_read_string(c->infile, data_store_pos+tag_offset, DE_DBG_MAX_STRLEN, DE_DBG_MAX_STRLEN, DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_ASCII); de_dbg(c, "name: \"%s\"", ucstring_getpsz(d->name_srd->str)); } } else if(is_sig==0 && tag_id==DE_RPMTAG_VERSION && tag_type==DE_RPM_STRING_TYPE) { if(!d->version_srd) { d->version_srd = dbuf_read_string(c->infile, data_store_pos+tag_offset, DE_DBG_MAX_STRLEN, DE_DBG_MAX_STRLEN, DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_ASCII); de_dbg(c, "version: \"%s\"", ucstring_getpsz(d->version_srd->str)); } } else if(is_sig==0 && tag_id==DE_RPMTAG_RELEASE && tag_type==DE_RPM_STRING_TYPE) { if(!d->release_srd) { d->release_srd = dbuf_read_string(c->infile, data_store_pos+tag_offset, DE_DBG_MAX_STRLEN, DE_DBG_MAX_STRLEN, DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_ASCII); de_dbg(c, "release: \"%s\"", ucstring_getpsz(d->release_srd->str)); } } pos += 16; } de_dbg_indent(c, -1); pos = data_store_pos; de_dbg(c, "%s: data store at %d", hdrname, (int)pos); pos += storesize; *section_size = pos - pos1; retval = 1; done: de_dbg_indent(c, -1); return retval; }
// pos1 points to the ole_id field (should be 0x00000501). // Caller must have looked ahead to check the type. static int do_picture_ole_embedded_rendition(deark *c, lctx *d, struct para_info *pinfo, int rendition_idx, i64 pos1, i64 *bytes_consumed) { i64 pos = pos1; i64 stringlen; i64 data_len; u8 buf[16]; struct de_stringreaderdata *srd_typename = NULL; struct de_stringreaderdata *srd_filename = NULL; struct de_stringreaderdata *srd_params = NULL; pos += 4; // 0x00000501 pos += 4; // "type" (probably already read by caller) stringlen = de_getu32le_p(&pos); srd_typename = dbuf_read_string(c->infile, pos, stringlen, 260, DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_ASCII); de_dbg(c, "typename: \"%s\"", ucstring_getpsz(srd_typename->str)); pos += stringlen; stringlen = de_getu32le_p(&pos); srd_filename = dbuf_read_string(c->infile, pos, stringlen, 260, DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_ASCII); de_dbg(c, "filename: \"%s\"", ucstring_getpsz(srd_filename->str)); pos += stringlen; stringlen = de_getu32le_p(&pos); srd_params = dbuf_read_string(c->infile, pos, stringlen, 260, DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_ASCII); de_dbg(c, "params: \"%s\"", ucstring_getpsz(srd_params->str)); pos += stringlen; data_len = de_getu32le_p(&pos); de_dbg(c, "embedded ole rendition data: pos=%d, len=%d", (int)pos, (int)data_len); // TODO: I don't know the extent to which it's better to sniff the data, or // rely on the typename. de_read(buf, pos, sizeof(buf)); if(!de_strcmp(srd_typename->sz, "CDraw") && !de_memcmp(&buf[0], (const void*)"RIFF", 4) && !de_memcmp(&buf[8], (const void*)"CDR", 3) ) { // Looks like CorelDRAW dbuf_create_file_from_slice(c->infile, pos, data_len, "cdr", NULL, 0); } else if(buf[0]=='B' && buf[1]=='M') { // TODO: Detect true length of data dbuf_create_file_from_slice(c->infile, pos, data_len, "bmp", NULL, 0); } else { if(d->extract_ole) { extract_unknown_ole_obj(c, d, pos, data_len, srd_typename); } else { de_warn(c, "Unknown/unsupported type of OLE object (\"%s\") at %d", ucstring_getpsz(srd_typename->str), (int)pos1); } } pos += data_len; *bytes_consumed = pos - pos1; de_destroy_stringreaderdata(c, srd_typename); de_destroy_stringreaderdata(c, srd_filename); de_destroy_stringreaderdata(c, srd_params); return 1; }
// Caller must initialize *repeat_count. static void uncompress_line(deark *c, lctx *d, dbuf *unc_line, i64 pos1, i64 rownum, i64 *bytes_consumed, i64 *repeat_count) { i64 pos; u8 b0, b1; u8 val; i64 count; i64 k; i64 tmp_repeat_count; i64 unc_line_len_orig; *bytes_consumed = 0; pos = pos1; unc_line_len_orig = unc_line->len; while(1) { if(pos >= c->infile->len) break; if(unc_line->len - unc_line_len_orig >= d->rowspan_per_plane) break; b0 = de_getbyte(pos++); if(b0==0) { // Pattern run or scanline run b1 = de_getbyte(pos++); if(b1>0) { // pattern run de_read(d->pattern_buf, pos, d->patlen); pos += d->patlen; count = (i64)b1; for(k=0; k<count; k++) { dbuf_write(unc_line, d->pattern_buf, d->patlen); } } else { // (b1==0) scanline run u8 flagbyte; flagbyte = de_getbyte(pos); if(flagbyte==0xff) { pos++; tmp_repeat_count = (i64)de_getbyte(pos++); if(tmp_repeat_count == 0) { de_dbg(c, "row %d: bad repeat count", (int)rownum); } else { *repeat_count = tmp_repeat_count; } } else { de_dbg(c, "row %d: bad scanline run marker: 0x%02x", (int)rownum, (unsigned int)flagbyte); } } } else if(b0==0x80) { // "Uncompressed bit string" count = (i64)de_getbyte(pos++); dbuf_copy(c->infile, pos, count, unc_line); pos += count; } else { // "solid run" val = (b0&0x80) ? 0xff : 0x00; count = (i64)(b0 & 0x7f); dbuf_write_run(unc_line, val, count); } } *bytes_consumed = pos - pos1; }
static void do_psf2_unicode_table(deark *c, lctx *d, struct de_bitmap_font *font) { de_int64 cur_idx; de_int64 pos; int ret; de_int64 foundpos; de_int64 char_data_len; de_byte char_data_buf[200]; de_int32 ch; de_int64 utf8len; de_dbg(c, "Unicode table at %d\n", (int)d->unicode_table_pos); de_dbg_indent(c, 1); pos = d->unicode_table_pos; cur_idx = 0; while(1) { de_int64 pos_in_char_data; de_int64 cp_idx; if(cur_idx >= d->num_glyphs) break; if(pos >= c->infile->len) break; // Figure out the size of the data for this glyph ret = dbuf_search_byte(c->infile, 0xff, pos, c->infile->len - pos, &foundpos); if(!ret) break; char_data_len = foundpos - pos; if(char_data_len<0) char_data_len=0; else if(char_data_len>(de_int64)sizeof(char_data_buf)) char_data_len=(de_int64)sizeof(char_data_buf); // Read all the data for this glyph de_read(char_data_buf, pos, char_data_len); // Read the codepoints for this glyph cp_idx = 0; pos_in_char_data = 0; while(1) { if(pos_in_char_data >= char_data_len) break; ret = de_utf8_to_uchar(&char_data_buf[pos_in_char_data], char_data_len-pos_in_char_data, &ch, &utf8len); if(!ret) { // If there are any multi-codepoint aliases for this glyph, we // expect de_utf8_to_uchar() to fail when it hits the 0xfe byte. // So, this is not necessarily an error. break; } if(cp_idx==0) { // This is the primary Unicode codepoint for this glyph de_dbg2(c, "char[%d] = U+%04x\n", (int)cur_idx, (unsigned int)ch); font->char_array[cur_idx].codepoint_unicode = ch; } else { do_extra_codepoint(c, d, font, cur_idx, ch); } cp_idx++; pos_in_char_data += utf8len; } if(cp_idx==0) { de_warn(c, "Missing codepoint for char #%d\n", (int)cur_idx); } // Advance to the next glyph pos = foundpos+1; cur_idx++; } font->has_unicode_codepoints = 1; font->prefer_unicode = 1; de_dbg_indent(c, -1); }