//
//  stripttc.c
//  DfontSplitter
//
// Copyright © 2000 – 2015, George Williams and the FontForge Project contributors
//
// This file is originally from FontForge. <https://github.com/fontforge/fontforge> and is licensed
// under the GNU General Public License, version 3.
//

#include "stripttc.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libgen.h>

/* This program takes a ttc file and turns it into its component ttf files */
/* This makes two changes to the data:                                     */
/*    * The tables are placed at different offsets, therefore            */
/*      the offset fields in the table header are also different.        */
/*    * the 'head' table checksumAdjustment field is set correctly       */

#define CHR(ch1,ch2,ch3,ch4) (((ch1)<<24)|((ch2)<<16)|((ch3)<<8)|(ch4))

static void putshort(FILE *file,int sval) {
    putc((sval>>8)&0xff,file);
    putc(sval&0xff,file);
}

static void putlong(FILE *file,int val) {
    putc((val>>24)&0xff,file);
    putc((val>>16)&0xff,file);
    putc((val>>8)&0xff,file);
    putc(val&0xff,file);
}

static int getushort(FILE *ttf) {
    int ch1 = getc(ttf);
    int ch2 = getc(ttf);
    if ( ch2==EOF )
    return( EOF );
    return( (ch1<<8)|ch2 );
}

static int getlong(FILE *ttf) {
    int ch1 = getc(ttf);
    int ch2 = getc(ttf);
    int ch3 = getc(ttf);
    int ch4 = getc(ttf);
    if ( ch4==EOF )
    return( EOF );
    return( (ch1<<24)|(ch2<<16)|(ch3<<8)|ch4 );
}

static unsigned int filecheck(FILE *file) {
    unsigned int sum = 0, chunk;
    
    rewind(file);
    while ( 1 ) {
        chunk = getlong(file);
        if ( feof(file) || ferror(file))
        break;
        sum += chunk;
    }
    return( sum );
}

static void copytable(FILE *ttf,FILE *ttc,int offset,int length) {
    int i, ch;
    
    fseek(ttc,offset,SEEK_SET);
    for ( i=0; i<length && (ch=getc(ttc))!=EOF ; ++i )
    putc(ch,ttf);
    if ( ch==EOF )
    fprintf( stderr, "File ended before table\n" );
    if ( length&1 ) {
        putc('\0',ttf);
        ++length;
    }
    if ( length & 2 ) {
        putshort(ttf,0);
    }
}

static int handlefont(char *filename,int which,FILE *ttc,int offset) {
    char outfile[2000], *pt;
    FILE *ttf;
    int i, cnt, *offsets, *lengths, head, tag, pos, headpos;
    
    char cwd[2000];
    getcwd(&cwd, sizeof(cwd));
    
    snprintf(outfile, sizeof(outfile), "%s/%s", cwd, basename(filename));
    
    pt = strrchr(outfile,'.');
    if ( pt==NULL )
    pt = outfile + strlen(outfile);
    sprintf( pt, "_%02d.ttf", which );
    
    ttf = fopen( outfile,"w+b");
    if ( ttf==NULL ) {
        fprintf( stderr, "Failed to open %s for output.\n", outfile );
        return( -3 );
    }
    printf ( "%s ", outfile );
    
    fseek(ttc,offset,SEEK_SET);
    putlong(ttf,getlong(ttc));        /* sfnt version */
    putshort(ttf,cnt = getushort(ttc));    /* table cnt */
    putshort(ttf,getushort(ttc));    /* binary search header */
    putshort(ttf,getushort(ttc));
    putshort(ttf,getushort(ttc));
    
    offsets = malloc(cnt*sizeof(int));
    lengths = malloc(cnt*sizeof(int));
    head = -1;
    for ( i=0; i<cnt; ++i ) {
        tag = getlong(ttc);
        if ( tag==CHR('h','e','a','d'))
        head = i;
        putlong(ttf,tag);
        putlong(ttf,getlong(ttc));    /* checksum */
        putlong(ttf,offsets[i] = getlong(ttc));        /* Reserve space for offset, will fix later */
        putlong(ttf,lengths[i] = getlong(ttc));
    }
    headpos = -1;
    for ( i=0; i<cnt; ++i ) {
        pos = ftell(ttf);
        copytable(ttf,ttc,offsets[i],lengths[i]);
        if ( i==head ) {
            fseek(ttf,pos+8,SEEK_SET);
            putlong(ttf,0);
            headpos = pos;
        }
        fseek(ttf,12+i*16+8,SEEK_SET);
        putlong(ttf,pos);                /* Fix offset here */
        fseek(ttf,0,SEEK_END);
    }
    if ( headpos!=-1 ) {
        unsigned int checksum;
        checksum = filecheck(ttf);
        checksum = 0xb1b0afba-checksum;
        fseek(ttf,headpos+2*sizeof(int),SEEK_SET);
        putlong(ttf,checksum);
    }
    fclose(ttf);
    free(offsets); free(lengths);
    return( 0 );
}

int handlefile(char *filename) {
    FILE *ttc = fopen(filename,"rb");
    int version, cnt, e, i;
    int *offsets;
    
    if ( ttc==NULL ) {
        fprintf( stderr, "Could not open %s\n", filename );
        return( -1 );
    }
    
    version = getlong(ttc);
    if ( version!=CHR('t','t','c','f')) {
        fprintf( stderr, "%s does not look like a ttc file, bad version.\n", filename );
        fclose(ttc);
        return( -2 );
    }
    
    version = getlong(ttc);
    if ( version!=0x10000 && version != 0x20000 )
    fprintf( stderr, "Unexpected ttc version number: %08x\n", (unsigned int)(version) );
    cnt = getlong(ttc);
    offsets = malloc(cnt*sizeof(int));
    for ( i=0; i<cnt; ++i )
    offsets[i] = getlong(ttc);
    printf( "%s => ", filename );
    for ( i=0; i<cnt; ++i )
    if ( (e = handlefont(filename,i,ttc,offsets[i])) ) {
        fclose(ttc);
        free(offsets);
        return( e );
    };
    printf( "\n" );
    fclose(ttc);
    free(offsets);
    return( 0 );
}

int stripttc_main(int argc, char *argv[]) {
    int e, i;
    
    for ( i=1; i<argc; ++i )
    if ( (e = handlefile(argv[i])) )
    return( e );
    return( 0 );
}
