/* 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 "../../satcom_lib/sc_common.h"

/* Binary data of IP.BIN and loader. */
#include "data.h"
//#include "data.c"

/* Offset where to put the IP.BIN / loader / compressed data. */
#include "mf_address.h"

/* Endianness conversion. */
unsigned long bswap_ul(unsigned long in)
{
    unsigned long out;
    out  = ((in >> 24) & 0xFF) <<  0;
    out |= ((in >> 16) & 0xFF) <<  8;
    out |= ((in >>  8) & 0xFF) << 16;
    out |= ((in >>  0) & 0xFF) << 24;
    return out;
}


int main (int argc, char *argv[])
{
    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("--- Saturn FirmWare maker v0.2 by cafe-alpha.               ---\n");
            printf("--- This is a part of my entry in the S.A.T.U.R.N. contest. ---\n");
            printf("Usage : <in_file> <out_file> [<exec_address>] [<max_size_KB>]\n");
            printf("        Default values:\n");
            printf("         exec_address : 0x06004000\n");
            printf("         max_size_KB  : 256\n");
            return 0;
        }
    }

    char* infile = argv[1];
    char* outfile = argv[2];
    unsigned long exec_addr = 0x06004000;
    if(argc >= 4) exec_addr = strtoul(argv[3], NULL, 0);
    unsigned long max_size = 256;
    if(argc >= 5) max_size = strtoul(argv[4], NULL, 0);

    printf("Input file   : %s\n", infile);
    printf("Output file  : %s\n", outfile);
    printf("Exec address : 0x%08X\n", exec_addr);
    printf("Max size (KB): %d\n", max_size);
    max_size = max_size*1024;

    FILE* fp;

    /* Read target program file. */
    unsigned long inlen;
    fp = fopen(infile, "rb");
    if(!fp)
    {
        printf("Open input file error !\n");
        return 1;
    }
    fseek(fp, 0, SEEK_END);
    inlen = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    unsigned char* inbuffer = (unsigned char*)malloc(inlen);
    if(!inbuffer)
    {
        fclose(fp); fp = NULL;
        printf("Alloc error !\n");
        return 1;
    }
    fread(inbuffer, 1, inlen, fp);
    fclose(fp); fp = NULL;



    /* Compress target program. */
    printf("Compress data ...\n");
    unsigned char* compbuffer = (unsigned char*)malloc(inlen+SC_COMP_HEADER_MAXSIZE);
    if(!compbuffer)
    {
        free(inbuffer); inbuffer = NULL;
        printf("Alloc error !\n");
        return 1;
    }
    unsigned long complen;
    // Decompression stuff doesn't works, so disable compression.
    //if(datacomp)
    //{
    //    complen = comp_exec(inbuffer, inlen, compbuffer);
    //    printf("Compressed size = %d bytes (input size * %f %%)\n", complen, ((float)(100*complen))/inlen);
    //}
    //else
    {
        memcpy((void*)compbuffer, (void*)inbuffer, inlen);
        complen = inlen;
    }
    free(inbuffer); inbuffer = NULL;

    /* Compressed file size check */
    if((FC_DATA_OFFSET + complen) >= (max_size-256))
    {
        free(compbuffer); compbuffer = NULL;
        printf("Error : Data don't fit in given size !\n");
        return 1;
    }



    unsigned char* outbuffer = NULL;
    outbuffer = (unsigned char*)malloc(max_size);
    if(!outbuffer)
    {
        printf("Alloc error !\n");
        return 1;
    }
    memset((void*)outbuffer, 'P', max_size);

    /* Copy ip.bin */
    memcpy((void*)(outbuffer + FC_IPBIN_OFFSET), (void*)ip_bin_dat, IP_BIN_SIZ);
    /* Copy loader */
    memcpy((void*)(outbuffer + FC_LOADER_OFFSET), (void*)loader_bin_dat, LOADER_BIN_SIZ);



    /* Write loader parameters. */
    /* Pointer to the parameters (length, execution start address). */
    unsigned long* params = (unsigned long*)(outbuffer + FC_PARAMS_OFFSET);
    params[FC_PDATALEN_INDEX] = bswap_ul(complen);
    params[FC_EXEADDRS_INDEX] = bswap_ul(exec_addr);

    /* Copy target program. */
    memcpy((void*)(outbuffer + FC_DATA_OFFSET), (void*)compbuffer, complen);


    /* Write IP.BIN + loader + compressed data to disk. */
    fp = fopen(outfile, "wb");
    if(fp)
    {
        unsigned long writelen = FC_DATA_OFFSET + complen;
        writelen = writelen - (writelen%256) + 256;
        fwrite(outbuffer, 1, writelen, fp);
        printf("Wrote %d bytes to disk.\n", writelen);
        fclose(fp); fp = NULL;
    }
    free(outbuffer); outbuffer = NULL;
    free(compbuffer); compbuffer = NULL;

    return 0;
}



