#!/usr/bin/env python2 # build HEX font file from FreeDOS CPIDOS font import os import sys import zipfile import binascii import subprocess # tools def load_codepage(name): """ Load codepage to Unicode table. """ cp_to_unicode = {} f = open(name, 'rb') for line in f: # ignore empty lines and comment lines (first char is #) if (not line) or (line[0] == '#'): continue # split unicodepoint and hex string splitline = line.split(':') # ignore malformed lines if len(splitline) < 2: continue # extract codepage point cp_point = binascii.unhexlify(splitline[0].strip()) # extract unicode points ucs = u'' for cp_str in splitline[1].split()[0].strip().split(','): ucs += unichr(int('0x' + cp_str.strip(), 16)) cp_to_unicode[cp_point] = ucs # fill up any undefined 1-byte codepoints for c in range(256): if chr(c) not in cp_to_unicode: cp_to_unicode[chr(c)] = u'\0' return cp_to_unicode def chars_to_uint(c): return ord(c[0]) + ord(c[1])*0x100 def chars_to_ulong(c): return ord(c[0]) + ord(c[1])*0x100 + ord(c[2])*0x10000 + ord(c[3])*0x1000000 def read_codepage_header(cpi): size = chars_to_uint(cpi.read(2)) chars_to_ulong(cpi.read(4)) # offset to next header, ignore this and assume header - page - header - page etc. cpi.read(2) # device_type cpi.read(8) # device name codepage = chars_to_uint(cpi.read(2)) cpi.read(6) # reserved cpi.read(size-24) # pointer to CPIInfoHeader or 0 return codepage def read_font_header(cpi): # skip version number cpi.read(2) num_fonts = chars_to_uint(cpi.read(2)) chars_to_uint(cpi.read(2)) # size return num_fonts def load_cpi_font(cpi): height = ord(cpi.read(1)) width = ord(cpi.read(1)) cpi.read(2) num_chars = chars_to_uint(cpi.read(2)) font = [] for _ in range(num_chars): lines = cpi.read(height*(width//8)) # we assume width==8 font += [lines] return height, font def save_rom_font(font, out_name): out = open(out_name, 'wb') for glyph in font: out.write(glyph) def write_hex(outfile, font, unitbl): with open(outfile, 'w') as of: for i, f in enumerate(font): of.write(hexline(unitbl[chr(i)], f)) def hexline(ucp, glyph): s = binascii.hexlify(glyph).upper() tohex = s + '0'*(32-len(s)) ucp_str = ','.join(['%04X' % ord(c) for c in ucp]) return "%s:%s\n" % (ucp_str, tohex) def add_to_multidict(mdict, key, value): try: mdict[key].add(value) except KeyError: mdict[key] = set([value]) fdzip = 'cpidos30.zip' ucp_loc = 'codepage/' cpi_prefix = 'BIN/' cpi_names = ['ega.cpx'] + ['ega%d.cpx'% i for i in range(2, 19)] def main(): try: os.mkdir('work') except OSError: pass os.chdir('work') try: os.mkdir('hex') except OSError: pass try: os.mkdir('rom') except OSError: pass # unpack zipfile pack = zipfile.ZipFile('../' + fdzip, 'r') for name in cpi_names: pack.extract(cpi_prefix + name) subprocess.call(['upx', '-d', cpi_prefix + name]) # retrieve forced choices forced = {} multidict = {8: {}, 14: {}, 16: {}} dropped = {8: {}, 14: {}, 16: {}} with open('../choices', 'r') as f: for line in f: if line and line[0] in ('#', '\n'): continue codepoint, codepagestr = line.strip('\n').split(':', 1) ucs = unichr(int(codepoint, 16)) codepage = codepagestr.split(':') forced[ucs] = (int(codepage[0]), codepage[1] if codepage[1:] else None) # load CPIs and add to dictionary for cpi_name in cpi_names: print print cpi_name cpi = open(cpi_prefix + cpi_name, 'rb') # 23-byte header cpi.read(23) # get number codepages in this file num = chars_to_uint(cpi.read(2)) for _ in range(num): codepage = read_codepage_header(cpi) num_fonts = read_font_header(cpi) fonts = {} print '[', codepage, ']', for _ in range(num_fonts): height, font = load_cpi_font(cpi) print height, # save intermediate ROM font save_rom_font(font, 'rom/%s_%d_%02d.fnt' % (cpi_name, codepage, height)) try: unitbl = load_codepage(os.path.join('..', ucp_loc, str(codepage)+'.ucp')) except Exception as e: print codepage, height, e else: for key, value in unitbl.iteritems(): unikey = unitbl[key] glyph = font[ord(key)] if (unikey in forced and (codepage, cpi_name) != forced[unikey] and (codepage, None) != forced[unikey]): # do not add forced chars #print "dropping", hex(ord(unikey)), codepage, cpi_name, repr(forced[unikey]) add_to_multidict(dropped[height], unikey, glyph) else: add_to_multidict(multidict[height], unikey, glyph) # save intermediate HEX write_hex('hex/%s_%d_%02d.hex' % (cpi_name, codepage, height), font, unitbl) print pua = range(0xe000, 0xf900) for height in (8, 14, 16): keys = sorted(multidict[height].keys()) # write out all except pua with open('base_%02d.hex' % height, 'w') as f: # header with open ('../header.txt', 'r') as h: for line in h: f.write(line) for key in keys: if len(key) > 1 or ord(key) not in pua: for glyph in multidict[height][key]: f.write(hexline(key, glyph)) # write out pua with open('pua_%02d.hex' % height, 'w') as f: for unicp in pua: try: for glyph in multidict[height][unichr(unicp)]: f.write(hexline(unichr(unicp), glyph)) except KeyError: pass # write out dropped glyphs with open('dropped_%02d.hex' % height, 'w') as f: for unicp, glyphset in dropped[height].iteritems(): for glyph in glyphset: # only output what differs from base if unicp not in multidict[height] or glyph not in multidict[height][unicp]: f.write(hexline(unicp, glyph)) #print multidict[16].keys() main()