#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

int main(int argc, char** argv)
{
	int i;

    uint32_t bin_offset = 0;
    uint32_t hex_offset = 0;
    uint32_t length     = (1<<30); // 2G max
    int      eof        = 1;
    int      segment    = -1;
    int      append     = 0;
    int      help       = 0;
    int      unnamed    = 0;
    char     name_in[1024];
    char     name_out[1024];
    unsigned char buffer[64], b1, b2, ch;
    
    FILE     *fi, *fo;
    
    if(argc == 1)
        help = 1;
        
    // Parse options
    for(i=1;i<argc;i++) {
        if(argv[i][0] == '-') { // option
            switch(argv[i][1]) {
            case 'i':
                if(i != (argc-1)) {
                    bin_offset = strtoul(argv[i+1], 0, 0);
                    i++;
                } else {
                    printf("-i option: no offset given.\n");
                    exit(1);
                }
                break;

            case 'h':
                help = 1;
                break;

            case 'o':
                if(i != (argc-1)) {
                    hex_offset = strtoul(argv[i+1], 0, 0);
                    i++;
                } else {
                    printf("-o option: no offset given.\n");
                    exit(1);
                }
                break;

            case 'l':
                if(i != (argc-1)) {
                    length = strtoul(argv[i+1], 0, 0);
                    i++;
                } else {
                    printf("-l option: no length given.\n");
                    exit(1);
                }
                break;

            case 't':
                eof = 0;
                break;

            case 'a':
                append = 1;
                break;
                
            default:
                printf("Unknown option %s given.\n", argv[i]);
                exit(2);
            }
        } else {
            switch(unnamed) {
            case 0:
                strncpy(name_in, argv[i], 1024);
                break;

            case 1:
                strncpy(name_out, argv[i], 1024);
                break;
            
            default:
                printf("Superfluous argument %s.\n", argv[i]);
                exit(3);
            }
            unnamed++;
        }
    }

    if(help) {
        fprintf(stderr, "Usage: %s [-i <bin offset>] [-o <hex offset>] [-l <length>] \n\t\t[-h] [-t] [-a] <infile> <outfile>\n", argv[0]);
        fprintf(stderr, "\t-h shows this help.\n");
        fprintf(stderr, "\t-t omits the end record.\n");
        fprintf(stderr, "\t-a appends to the existing hex file.\n");
        return 1;
    }

    fi = fopen(name_in, "rb");
    if(!fi) {
        fprintf(stderr, "Can't open '%s' as input.\n", name_in);
        exit(-1);
    }
    fo = fopen(name_out, (append)?"at":"wt");
    if(!fo) {
        fprintf(stderr, "Can't open '%s' as output.\n", name_out);
        exit(-1);
    }

    fseek(fi, SEEK_SET, bin_offset);

    int bytes_in_segment;
    int bytes_read;
    int bytes_to_read;
    int seg_flag = 0;
    
    while(length && !feof(fi)) {
        bytes_in_segment = 65536 - (hex_offset & 0xFFFF);
        if((bytes_in_segment == 65536)||(segment == -1)) {
            //:020000040000FA
            segment = (hex_offset >> 16);
            b1 = (segment & 0xFF);
            b2 = (segment >> 8) & 0xFF;
            ch = 0xFA - b1 - b2;
            seg_flag = 1;
        }
        bytes_to_read = (bytes_in_segment > 32)?32:bytes_in_segment;
        bytes_read = fread((char *)buffer, 1, bytes_to_read, fi);
        if(bytes_read) {
            if(seg_flag) {
                fprintf(fo, ":02000004%04X%02X\n", segment, ch);
                seg_flag = 0;            
            }
            fprintf(fo, ":%02X%04X00", bytes_read, hex_offset & 0xFFFF);
            b1 = (hex_offset & 0xFF);
            b2 = (hex_offset >> 8) & 0xFF;
            ch = 0 - bytes_read - b1 - b2;
            for(i=0;i<bytes_read;i++) {
                fprintf(fo, "%02X", buffer[i]);
                ch -= buffer[i];
            }
            fprintf(fo, "%02X\n", ch);
        }        
        hex_offset += bytes_read;
        length     -= bytes_read;
    }

    if(eof) {
        fprintf(fo, ":00000001FF\n");
    }
    fclose(fo);
    fclose(fi);

    return 0;
}
