/* mkfont
 *
 * read files of the following format and outputs a bunch of DECLE
 * directives for the resulting font.  Outputs a complete GRAM image 
 * (512 decles == 64 characters * 8 decles/character).  A grom image may
 * be generated by specifying a '-' as the first argument to the program.
 *
 * CHAR 'X'
 * ##...##.
 * ##...##.
 * .##.##..
 * ..###...
 * ..###...
 * .##.##..
 * ##...##.
 * ........
 *
 * or 
 *
 * CHAR # 88
 * ##...##.
 * ##...##.
 * .##.##..
 * ..###...
 * ..###...
 * .##.##..
 * ##...##.
 * ........
 *
 * Exactly 8 lines must follow a CHAR directive.  Dots/spaces/0
 * become zero-bits, everything else becomes 1-bits.  Short lines are
 * padded with zero-bits.  Characters beyond the 8th position are ignored.
 * Leading whitespace IS NOT.  Nothing intelligent is done with tabs.
 *
 * An offset 32 is subtracted from the ASCII value for the character by 
 * default.  This can be changed with an "OFFSET" directive.  All character 
 * numbers are taken modulo 64.  (Note: The offset is reset to 32 for each
 * file.)
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int offset = 32;
int numchars = 64;

#define NA (-10000)

int  font[256 * 8];
int  name[256];
char buf1[1024];
char buf2[1024];

int handle_char(FILE *f, int chnum, int numeric)
{
    int i, j, idx, eof = 0;
    unsigned row;
    char cbuf[9], *s;

    idx = (((unsigned)chnum - offset) % numchars) << 3;
    name[idx >> 3] = numeric ? ~chnum : chnum;

    for (i = 0; i < 8; i++, idx++)
    {
        if (fgets(buf1, 1024, f) == NULL)
        {
            eof = -1;
            buf1[0] = '\0';
        }
        buf1[8] = 0;

        /* trim trailing newline */
        s = buf1;
        while (*s)
        {
            if (*s == '\n' || *s == '\r') *s = 0;
            else s++;
        }

        sprintf(cbuf, "%-.8s", buf1);

        for (j = row = 0; j < 8; j++)
        {
            row <<= 1;
            if (cbuf[j] != '.' && 
                            cbuf[j] != ' ' && 
                            cbuf[j] != '0') row++;
        }
        font[idx] = row;
    }
        
    return eof;
}

void trim_whitespace(void)
{
    char *s1;

    s1 = buf1;

    while (isspace(*s1)) s1++;
    strncpy(buf2, s1, sizeof(buf2));
    s1 = buf2 + strlen(buf2) - 1;
    while (s1 > buf2 && isspace(*s1)) s1--;
    *++s1 = 0;

}

int main(int argc, char *argv[])
{
    FILE * f;
    char cval = 0;
    int val = 0;
    int eof = 0;
        int skip = 0;
    int decle = 0, chars = 0;
    int lineno;
    int i, j, k, idx;
    int suppress = 0;
    int rle_ok = 0;
    int gramfont = 1;
    int font16 = 1;
    char * fontname = "FONT";

    if (argc == 1 || (argc==2 && argv[1][0] == '-'))
    {
        fprintf(stderr, "usage: mkfont [-] in1 [in2 [...]]"
                                " > font.asm\n");
        exit(1);
    }


    if (argv[1][0] == '-') { numchars = 256; argv++; }
    gramfont = numchars == 64;

    
    if (gramfont)
    {
        for (i = 0; i < 256;     i++) name[i] = NA;
        for (i = 0; i < 256 * 8; i++) font[i] = NA;
    } else
    {
        for (i = 0; i < 256;     i++) name[i] = ~i;
        for (i = 0; i < 256 * 8; i++) font[i] = 0;
    }

    while (*++argv)
    {
        f = fopen(argv[0], "r");
        if (!f)
        {
            perror("fopen()");
            fprintf(stderr, "Could not open %s for reading.\n",
                argv[0]);
            exit(1);
        }

        offset = 32;
        lineno = 0;
        eof = 0;

        fprintf(stderr, "Processing file: %s\n", argv[0]);
        while (!eof && fgets(buf1, 1024, f) != NULL)
        {
            lineno++;
            trim_whitespace();

            if (buf2[0] == ';' || strlen(buf2) == 0) continue;
            if (sscanf(buf2, "CHAR '%c'", &cval) == 1)
            {
                eof = handle_char(f, cval, 0);
                lineno += 8;
            } else
            if (sscanf(buf2, "CHAR # %d", &val) == 1)
            {
                eof = handle_char(f, val, 1);
                lineno += 8;
            } else
            if (sscanf(buf2, "OFFSET %d", &val) == 1)
            {
                offset = val;
            } else
            if (!strncmp(buf2, "FONT ", 5))
            {
                fontname = strdup(buf2 + 5);
                fprintf(stderr, "Font name: '%s'\n", fontname);
            } else
            {
                fprintf(stderr, "Warning: Ignoring line %d "
                        "of file %s\n", 
                        lineno, argv[0]);
            }
        }
        fclose(f);
    }

    fprintf(stderr, "Generating font.\n");
    if (gramfont)
        printf("%s:   PROC\n", fontname);
    else
        printf("        ORG     $3000\nGROM:   PROC\n");
    for (i = 0; i < numchars; i++)
    {
        if (gramfont && name[i] == NA) { skip++; continue; }

        if (gramfont && (skip || !i))
        {
            int span;

            for (j = i + 1; j < numchars; j++)
                if (name[j] == NA) break;

            span = j - i;

            printf(";; Skipped %d indices.\n", skip);
            printf(";; Encoding span of %d %s\n", 
                span, span != 1 ? "entries" : "entry");

            /* We output skip * 8 since it makes the ASM easier */
            /* Likewise for span length.  This works because    */
            /* neither can get larger than 64.              */
            if (font16)
            {
                unsigned int x;
                x = (0xFF00 & (skip << 8)) | (0x00FF & span);
                x = (0xFFF8 & (x    << 3)) | (0x0007 & (x >> 13));
                printf("        DECLE   $%.4X\n\n", x);
                decle++;
            } else
            {
                printf("        DECLE   $%.3X, $%.3X\n\n",
                    skip*8, span*8);
                decle += 2;
            }
            skip = 0;
        }
        
        if (name[i] < 0)
            printf(";; Character #%d, GR%cM character index %d\n", 
                ~name[i], gramfont ?'A':'O', i);
        else
            printf(";; Character '%c', GR%cM character index %d\n", 
                name[i], gramfont ? 'A':'O', i);
        chars++;
        if (font16)
        {
            for (j = 0, idx = i << 3; j < 8; j += 2, idx += 2)
            {
                printf("        DECLE   $%.4X  ;", 
                       ((0xFF & font[idx + 1]) << 8) | (0xFF & font[idx]));

                for (k = 7; k >= 0; k--)
                    putchar((1 & (font[idx    ] >> k))?'#':'.');
                putchar('\n');

                printf(";       - - -          ;");

                for (k = 7; k >= 0; k--)
                    putchar((1 & (font[idx + 1] >> k))?'#':'.');
                putchar('\n');
                decle++;
            }
        } else
        {
            for (j = 0, idx = i << 3; j < 8; j++, idx++)
            {
                printf(suppress > 0 ?  ";       - - -   "
                                    :  "        DECLE   ");
                if (rle_ok && suppress <= 0)
                {
                    decle++;
                    for(suppress = 0; suppress < 3; suppress++)
                         if (font[idx] != font[1+idx+suppress])
                            break;

                    printf(" $%.3X    ; ",
                               (suppress << 8) | font[idx]);
                } else
                {
                    suppress--;
                    printf(" $%.3X    ; ", font[idx]);
                }

                for (k = 7; k >= 0; k--)
                    putchar((1 & (font[idx] >> k))?'#':'.');
                putchar('\n');
            }
        }
        putchar('\n');
    }

    printf("\n;; End of font.\n");
    if (gramfont) 
    {
        if (font16)
        {
            printf("        DECLE   $0000\n\n");
            decle++;
        } else
        {
            printf("        BYTE    $00, $00\n\n");
            decle += 2;
        }
    }
    printf(";; Total chars:  %10d characters\n", chars);
    printf(";; Total length: %10d decles\n", decle);
    printf(";; Decles/char:  %10.3f decles/character\n", 
        (double)decle/chars);
    printf("        ENDP\n");

    return 0;
}           
