def annotate(data, header):
    cache = _optcache()
    if cache is None:
        return None
    for field in header.structs[0].fields:
        if field.label == 10003:
            rowtype = field.type.elem_type
            Structrows = gff4._structtype(rowtype.fourcc, rowtype.fields, rowtype.size)
            field10003 = gff4.Field(field.label, gff4._listtype(Structrows, False), False, field.offset)
        elif field.label == 10002:
            field10002 = gff4.Field(field.label, ListStructcolm, False, field.offset)
    Structgtop = gff4._structtype(header.structs[0].fourcc, (field10002, field10003), header.structs[0].size)
    header = header._replace(structs=(Structgtop, Structcolm, Structrows))
    
    gtop = Structgtop()
    colms = gtop[10002]
    existing = []
    for colm in data[10002]:
        colm = Structcolm(colm)
        if not colm[10000]:
            colname = lookupcolname(colm[10001], cache)
            if colname is not None:
                colm[10000] = colname
        else:
            existing.append(colm[10000])
        colms.append(colm)
    gtop[10003].extend(Structrows(row) for row in data[10003])
    addnames(existing)
    
    return gtop, header
def csv2gda(source, dest=None, platform='PC  '):
    f = source
    close = False
    if isinstance(source, basestring):
        f = open(source, 'rb')
        close = True

    csvr = csv.reader(f)
    names = csvr.next()
    colcount = len(names)
    types = csvr.next()
    if len(types) != colcount:
        raise RuntimeError, 'inconsistent number of columns'
    if names[0].lower() != 'id':
        raise RuntimeError, 'first column must be ID'
    if types[0] != 'int':
        raise RuntimeError, 'ID column must be int'

    goodnames = [s.decode('utf_8') for s in names if not CRC_NAME.match(s)]
    addnames(goodnames)

    column_list = ListStructcolm()
    row_fields = []
    field_offset = 0
    field_label = G2DA_COLUMN_1_LABEL
    for name, typename in zip(names, types):
        if typename == 'comment':
            continue
        colm = Structcolm()
        if CRC_NAME.match(name):
            colm[10001] = int(name[1:], 16)
        else:
            colm[10001] = checksum(name)
        coltypeid = coltypes.index(typename)
        colm[10999] = coltypeid
        column_list.append(colm)

        field = gff4.Field(field_label, gfftypes[coltypeid], False,
                           field_offset)
        field_label += 1
        field_offset += 4
        row_fields.append(field)

    Structrows = gff4._structtype('rows', row_fields, field_offset)
    ListStructrows = gff4._listtype(Structrows, False)
    G2DA_ROW_LIST = gff4.Field(10003, ListStructrows, False, 4)
    Structgtop = gff4._structtype('gtop', (G2DA_COLUMN_LIST, G2DA_ROW_LIST), 8)
    header = gff4.Header('V4.0', platform, 'G2DA', 'V0.2', 0, 0, 0,
                         (Structgtop, Structcolm, Structrows))

    rows_list = ListStructrows()
    for cells in csvr:
        if len(cells) != colcount:
            raise RuntimeError, 'inconsistent number of columns'
        field_label = G2DA_COLUMN_1_LABEL
        row = Structrows()
        for cell, typename in zip(cells, types):
            if typename == 'comment':
                continue
            if cell != '****':
                row[field_label] = cell
            field_label += 1
        rows_list.append(row)

    gtop = Structgtop()
    gtop[10002] = column_list
    gtop[10003] = rows_list

    if close:
        f.close()

    if dest is None:
        return gff4.GFF4Data(header, gtop)
    elif isinstance(dest, basestring):
        with open(dest, 'wb') as f:
            gff4.write_gff4(f, gtop, header, 4)
    else:
        gff4.write_gff4(dest, gtop, header, 4)
        csvw.writerow([coltypes[col[10999]] for col in gda[10002]])
        for row in gda[10003]:
            csvw.writerow([colvalue(v) for k, v in sorted(row.iteritems())])
    finally:
        if close:
            f.close()


#G2DA_COLUMN_NAME = gff4.Field(10000, gff4.ECString, False, 0)
#G2DA_COLUMN_HASH = gff4.Field(10001, gff4.UINT32, False, 4)
#G2DA_COLUMN_TYPE = gff4.Field(10999, gff4.UINT8, False, 8)
#Structcolm = gff4._structtype('colm', (G2DA_COLUMN_NAME, G2DA_COLUMN_HASH, G2DA_COLUMN_TYPE), 12)

G2DA_COLUMN_HASH = gff4.Field(10001, gff4.UINT32, False, 0)
G2DA_COLUMN_TYPE = gff4.Field(10999, gff4.UINT8, False, 4)
Structcolm = gff4._structtype('colm', (G2DA_COLUMN_HASH, G2DA_COLUMN_TYPE), 8)
ListStructcolm = gff4._listtype(Structcolm, False)
G2DA_COLUMN_LIST = gff4.Field(10002, ListStructcolm, False, 0)

G2DA_COLUMN_1_LABEL = 10005


def csv2gda(source, dest=None, platform='PC  '):
    f = source
    close = False
    if isinstance(source, basestring):
        f = open(source, 'rb')
        close = True

    csvr = csv.reader(f)
    names = csvr.next()
from array import array
import gff4

field19002 = gff4.Field(19002, gff4.UINT32, False, 0)
field19003 = gff4.Field(19003, gff4.ECString, False, 4)
StructSTRN = gff4._structtype('STRN', [field19002, field19003], 8)
field19001 = gff4.Field(19001, gff4._listtype(StructSTRN, False), False, 0)
StructTLK_20 = gff4._structtype('TLK ', [field19001], 4)
tlkheader = gff4.Header(version='V4.0', platform='PC  ', file_type='TLK ', file_version='V0.2', string_count=0, string_offset=96, data_offset=96, structs=(StructTLK_20, StructSTRN))

def htlk2tlk(htlk):
    tlk = StructTLK_20()
    table = htlk[19007]
    data = htlk[19008]
    for hstr in htlk[19006]:
        id = hstr[19004]
        start = hstr[19005]
        tlk[19001].append(StructSTRN({19002: id, 19003: _decompress(start, table, data)}))
    return tlk

def getstring(htlk, id):
    for hstr in htlk[19006]:
        if id == hstr[19004]:
            return _decompress(hstr[19005], htlk=htlk)
    raise KeyError

def _decompress(start, table=None, data=None, htlk=None):
    if table is None:
        table = htlk[19007]
    if data is None:
        data = htlk[19008]
    cache.executemany("INSERT OR IGNORE INTO names (crc, string) VALUES (?, ?);", (_tuple(s) for s in ss))
    cache.commit()

def dumpnames(f):
    cache = _optcache()
    count = 0
    if cache is not None:
        for row in cache.execute("SELECT string FROM names ORDER BY string ASC;"):
            print >>f, row[0]
            count += 1
    return count

field10000 = gff4.Field(10000, gff4.ECString, False, 0)
field10001 = gff4.Field(10001, gff4.UINT32, False, 4)
field10999 = gff4.Field(10999, gff4.UINT8, False, 8)
Structcolm = gff4._structtype('colm', (field10000, field10001, field10999), 12)
ListStructcolm = gff4._listtype(Structcolm, False)

def annotate(data, header):
    cache = _optcache()
    if cache is None:
        return None
    for field in header.structs[0].fields:
        if field.label == 10003:
            rowtype = field.type.elem_type
            Structrows = gff4._structtype(rowtype.fourcc, rowtype.fields, rowtype.size)
            field10003 = gff4.Field(field.label, gff4._listtype(Structrows, False), False, field.offset)
        elif field.label == 10002:
            field10002 = gff4.Field(field.label, ListStructcolm, False, field.offset)
    Structgtop = gff4._structtype(header.structs[0].fourcc, (field10002, field10003), header.structs[0].size)
    header = header._replace(structs=(Structgtop, Structcolm, Structrows))