/* This program is free software. It comes without any warranty, to
 * the extent permitted by applicable law. You can redistribute it
 * and/or modify it under the terms of the Do What The Fuck You Want
 * To Public License, Version 2, as published by Sam Hocevar. See
 * http://sam.zoy.org/wtfpl/COPYING for more details. */ 

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


// Put filename in buffer.
void get_filename(char* in, char* out)
{
    int inl = strlen(in);
    int i, j, pos = 0;
    for(i=0; i<inl; i++)
    {
        j = inl-1 - i;
        if((in[j] == '\\') || (in[j] == '/')) break;
        pos = j;
    }
    out[0] = '\0';
    for(i=pos; i<inl; i++)
    {
        out[i-pos] = in[i];
    }
    out[i-pos] = '\0';
}
// Return a pointer to filename extension.
// Return filename when no extension.
char* get_extension(char* in)
{
    int inl = strlen(in);
    int i, j;
    for(i=1; i<inl; i++)
    {
        j = inl-1 - i;
        if(in[j] == '.') return in+j+1;
    }
    return in;
}

// Remove illegal characters in a variable name.
void remove_illegal_chars(char* inout)
{
    int inl = strlen(inout);
    int i;
    for(i=0; i<inl; i++)
    {
        if(((inout[i] < 'a') || (inout[i] > 'z'))
        && ((inout[i] < 'A') || (inout[i] > 'Z'))
        && ((inout[i] < '0') || (inout[i] > '9')))
        {
            inout[i] = '_';
        }
    }
}

// Set all caps string.
void set_all_caps(char* inout)
{
    int inl = strlen(inout);
    int i;
    for(i=0; i<inl; i++)
    {
        if((inout[i] >= 'a') && (inout[i] <= 'z'))
        {
            inout[i] = inout[i] - 'a' + 'A';
        }
    }
}


// Print binary data (Taken from SaturnOrbit's bin2c source).
#define READBYTES   16
void print_bin_data(FILE* i, FILE* o)
{
    unsigned char buffer[READBYTES+10];
    int red, q, count=0;

    do
    {
        red = fread(buffer, 1, READBYTES, i);
        fprintf(o, "    ");
        for (q=0; q<red; q++)
        {
            fprintf(o, "0x%02X, ", buffer[q]);
            if ((count % 5000) == 0) printf(".");
            count++;
        }
        fprintf(o, "\n");
    } while (red == READBYTES);
    printf("\n");
}


#define STRSIZE 8192
int main (int argc, char *argv[])
{
    char prog_filename[8192];
    get_filename(argv[0], prog_filename);

    if(argc <= 1)
    {
        printf("Type %s -h for help.\n", argv[0]);
        return 0;
    }
    if(argc >= 2)
    {
        if((!strcmp(argv[1], "-h"))
        || (!strcmp(argv[1], "-help"))
        || (!strcmp(argv[1], "-?")))
        {
            printf("--- BIN2C v0.2 by SaturnOrbit bin2c author and cafe-alpha. ---\n");
            printf("--- This is a part of my S.A.T.U.R.N. contest entry.       ---\n");
            printf("Usage : %s <bin_file_1> [<bin_file_2>] [... <bin_file_n>] <out_file>\n", prog_filename);
            printf("           Where <bin_file_x> are binary the files to convert.\n");
            printf("           And   <out_file>   is the output C file.\n");
            printf("Example : %s test1.bin test2.bin output.c\n", prog_filename);
            printf(" Will create output.c and output.h containing data from test1.bin and test2.bin\n");
            printf("\n");

            return 0;
        }
    }


    /* Set output C file. */
    char out_cfile[STRSIZE];
    strcpy(out_cfile, argv[argc-1]);
    /* Set output header file. */
    char out_hfile[STRSIZE];
    strcpy(out_hfile, out_cfile);
    get_extension(out_cfile)[0] = 'c';
    get_extension(out_hfile)[0] = 'h';

    printf("Welcome in BIN2C\n");
    printf("Output C file: \"%s\"\n", out_cfile);
    printf("Output H file: \"%s\"\n", out_hfile);

    /* Open C and H files. */
    FILE* fc = fopen(out_cfile, "wb");
    if(fc == NULL)
    {
        printf("Error: cant open output C file.\n");
        return 1;
    }

    FILE* fh = fopen(out_hfile, "wb");
    if(fh == NULL)
    {
        printf("Error: cant open output H file.\n");
        fclose(fc); fc=NULL;
        return 1;
    }


    /* Write ifndef/define stuff in H file. */
    char out_hdefine[STRSIZE];
    srand(time(NULL));
    sprintf(out_hdefine, "_BIN2C_HEADER_%04X_%04X_%04X_%04X_", (unsigned short)rand(), (unsigned short)rand(), (unsigned short)rand(), (unsigned short)rand());
    fprintf(fh, "#ifndef %s\n", out_hdefine);
    fprintf(fh, "#define %s\n", out_hdefine);
    fprintf(fh, "\n");

    /* Write include stuff in C file. */
    fprintf(fc, "#include \"%s\"\n", out_hfile);
    fprintf(fc, "\n");


    /* Read all the input files. */
    int i;
    for(i=1; i<argc-1; i++)
    {
        char in_file[STRSIZE];
        strcpy(in_file, argv[i]);
        FILE* fi = fopen(in_file, "rb");
        if(fi != NULL)
        {
            printf("Open input file: \"%s\"\n", in_file);

            /* Set variable name, and data size define name. */
            char out_dat[STRSIZE];
            char out_siz[STRSIZE];
            get_filename(in_file, out_dat);
            remove_illegal_chars(out_dat);
            memcpy((void*)out_siz, (void*)out_dat, STRSIZE);
            strcat(out_dat, "_dat");
            strcat(out_siz, "_SIZ");
            set_all_caps(out_siz);


            /* Print length and extern stuff in H file. */
            fseek(fi, 0, SEEK_END);
            unsigned long length = ftell(fi);
            fseek(fi, 0, SEEK_SET);
            printf(" File size = %d bytes\n", length);
            fprintf(fh, "/* generated by bin2c from file %s */\n", in_file);
            fprintf(fh, "#define %s %d\n", out_siz, length);
            fprintf(fh, "extern const unsigned char %s[];\n", out_dat);
            fprintf(fh, "\n");



            /* Print stuff in C file. */
            fprintf(fc, "const unsigned char %s[] = \n", out_dat/*, out_siz*/);
            fprintf(fc, "{\n");
            printf(" Write binary data ..");
            print_bin_data(fi, fc);
            fprintf(fc, "};\n");
            fprintf(fc, "\n");

            fclose(fi); fi=NULL;
        }
        else
        {
            printf("Warning: cant open input file \"%s\" !\n", in_file);
        }
    }


    /* Don't forget endif in include file. */
    fprintf(fh, "#endif // %s\n", out_hdefine);

    /* END ! Close output files. */
    fclose(fh); fh=NULL;
    fclose(fc); fc=NULL;

    return 0;
}



