/*

    ascii2map - Simple ASCII map converter aimed at retro code

    Copyright (C) 2003-2015  Ian Cowburn (ianc@noddybox.co.uk)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    -------------------------------------------------------------------------

    Main

*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/* ---------------------------------------- VERSION INFO
*/

static const char *usage =
"Version 0.4 development\n"
"\n"
"This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
"GNU General Public License (Version 3) for more details.\n"
"\n"
"usage: ascii2map [-a|-b|-c] [-d directive] [input-file [output-file]]\n";


/* ---------------------------------------- TYPES
*/
typedef unsigned char uchar;

typedef enum
{
    Assembly,
    Binary,
    CSource
} Mode;


/* ---------------------------------------- GLOBALS
*/
static int              code_point = 'A';
static uchar            point[256];
static uchar            used[256];
static const char       *directive = "byte";
static Mode             mode = Assembly;

/* ---------------------------------------- PRIVATE FUNCTIONS
*/
static void Chomp(char *p)
{
    size_t l = strlen(p);

    while(l && (p[l-1] == '\n' || p[l-1] == '\r'))
    {
        p[--l] = 0;
    }
}


static void StartOutput(FILE *fp)
{
    switch(mode)
    {
        case CSource:
            fprintf(fp, "int map[] =\n{\n");
            break;

        default:
            break;
    }
}


static void EndOutput(FILE *fp)
{
    switch(mode)
    {
        case Assembly:
            fprintf(fp, "\n");
            break;

        case CSource:
            fprintf(fp, "\n};\n");
            break;

        default:
            break;
    }
}


static unsigned CodePoint(char c)
{
    int offset = c - code_point;

    if (offset < 0)
    {
        offset = 0;
    }

    if (offset > 255)
    {
        offset = 255;
    }

    return (unsigned)offset;
}


static void Output(FILE *fp, char c)
{
    static int column;
    static int first = 1;

    if (c < 0 || c > 255)
    {
        return;
    }

    switch(mode)
    {
        case Assembly:
            if (column == 0)
            {
                if (!first)
                {
                    fprintf(fp, "\n");
                }

                fprintf(fp, "\t%s\t", directive);
            }
            else
            {
                fprintf(fp, ", ");
            }

            if (used[c])
            {
                fprintf(fp, "%u", point[c]);
            }
            else
            {
                fprintf(fp, "%u", CodePoint(c));
            }

            break;

        case Binary:
            if (used[c])
            {
                putc(point[c], fp);
            }
            else
            {
                putc(CodePoint(c), fp);
            }

            break;

        case CSource:
            if (!first)
            {
                fprintf(fp, ", ");
            }

            if (column == 0)
            {
                if (!first)
                {
                    fprintf(fp, "\n");
                }

                fprintf(fp, "\t");
            }

            if (used[c])
            {
                fprintf(fp, "%u", point[c]);
            }
            else
            {
                fprintf(fp, "%u", CodePoint(c));
            }

            break;
    }

    column = (column + 1) % 8;

    if (first)
    {
        first = 0;
    }
}


/* ---------------------------------------- MAIN
*/
int main(int argc, char *argv[])
{
    FILE *in;
    FILE *out;
    char line[1024];
    int f;
    int in_map = 0;
    size_t len = 0;
    int warned = 0;

    f = 1;

    /* Process switches
    */
    while(argv[f] && argv[f][0] == '-')
    {
        int handled = 1;

        switch(argv[f][1])
        {
            case 'a':
                mode = Assembly;
                break;

            case 'b':
                mode = Binary;
                break;

            case 'c':
                mode = CSource;
                break;

            case 'd':
                if (argv[f+1])
                {
                    directive = argv[++f];
                }
                else
                {
                    handled = 0;
                }
                break;

            default:
                handled = 0;
                break;
        }

        if (!handled)
        {
            fprintf(stderr,"%s\n", usage);
            exit(EXIT_FAILURE);
        }

        f++;
    }

    /* Handle file args
    */
    if (argv[f])
    {
        in = fopen(argv[f], "r");

        if (!in)
        {
            fprintf(stderr,"Failed to open %s\n", argv[f]);
            exit(EXIT_FAILURE);
        }

        f++;
    }
    else
    {
        in = stdin;
    }

    if (argv[f])
    {
        if (mode == Binary)
        {
            out = fopen(argv[f], "wb");
        }
        else
        {
            out = fopen(argv[f], "w");
        }

        if (!in)
        {
            fprintf(stderr,"Failed to open %s\n", argv[f]);
            exit(EXIT_FAILURE);
        }
    }
    else
    {
        out = stdout;
    }

    /* Process input
    */
    used[' '] = 1;

    while(fgets(line, sizeof line, in))
    {
        Chomp(line);

        if (in_map)
        {
            char *p;

            if (len == 0)
            {
                len = strlen(line);
            }
            else
            {
                if (!warned && len != strlen(line))
                {
                    fprintf(stderr, "WARNING: Lines of different length\n");
                    warned = 1;
                }
            }

            p = line;

            while(*p)
            {
                Output(out, *p++);
            }
        }
        else
        {
            if (strcmp(line, "~") == 0)
            {
                StartOutput(out);
                in_map = 1;
            }

            if (line[0] == '!')
            {
                code_point = atoi(line + 1);
            }

            if (line[0] && line[1] == ':' && line[2])
            {
                char c;
                int i;

                c = line[0];
                i = atoi(line + 2);

                if (c >= 0 && c <= 255)
                {
                    point[c] = i;
                    used[c] = 1;
                }
            }
        }
    }

    EndOutput(out);

    return EXIT_SUCCESS;
}


/*
vim: ai sw=4 ts=8 expandtab
*/