/*
Copyright (C) 2000-2002 José Roberto B. de A. Monteiro <jrm@sel.eesc.usp.br>
                        and Pedro Zorzenon Neto <pzn@vztech.com.br>

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 2
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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

Do not forget to visit Free Software Foundation site: http://fsf.org

$Id: intelhex.c,v 1.2 2003/06/30 17:20:25 pzn Exp $
*/

#include "common.h"
#include "intelhex.h"
#include <stdlib.h>
#include <string.h>
#ifdef __linux__
# include <sys/stat.h>
#endif

int intelhex_fileopen (inteldata *id, char *filename, int mode) {
    /*mode = 1 -> open file for reading
             0 -> creates a new file for writing */

    /* TODO: please, check if the file already exists before overwriting */
    if (mode==0) {
	id->file=fopen(filename,"w");
	if (id->file==NULL) {
	    fprintf(stderr,"File create error\n");
	    abort(); }
        id->eof_found=0;
        return 1;
    }
    id->file=fopen(filename,"r");
    if (id->file==NULL) {
	fprintf(stderr,"Error when opening the file '%s' for reading.\n",filename);
	abort(); }
    id->eof_found=0;
    return 1;
}

int intelhex_fileclose (inteldata * id) {
    return fclose(id->file);
}

int intelhex_readline (inteldata * id) {
    /* returns:
         0 EOF found
         1 line read ok
         >1 some error occoured
    */
    char line[526], conv[5];
    int rectype, chksum, linelen, reccount, i;

    /* verifies eof and eof_record */
    if (id->eof_found==1) return 0; /* eof_record found */
    if (feof(id->file)) {
	fprintf(stderr,"error in intelhex file - eof_record not found\n");
	return 2; }

    /* reads a line from stdin (hex file format) */
    fgets(line, 525, id->file);

    /* changes CR+LF to FL if needed and checks if LF is present */
    linelen=strlen(line);
    if (line[linelen-2] == 13)
    {
	line[linelen-2]=line[linelen-1];
	line[linelen-1]=0;
	linelen=strlen(line);
    }
    if (line[linelen-1] != 10) {
	fprintf(stderr,"error in intelhex file - linefeed not found at end of line\n");
	return 3; }

    /* checks for record mark */
    if (line[0] != 0x3a) {
	fprintf(stderr,"error in intelhex file - ':' missing\n"); return 4; }

    /* verifies the minimum linelength */
    if (linelen < 12) {
	fprintf(stderr,"error in intelhex file - less than 11 characters in a line\n");
	return 5; }

    /* verifies the checksum */
    conv[2]=0; chksum=0;
    for(i=1; i<(linelen-1); i+=2) {
	conv[0]=line[i]; conv[1]=line[i+1];
	chksum+=(int)strtoul(conv,(char **)NULL,16); }
    if (chksum % 256) {
	fprintf(stderr,"error in intelhex file - bad chksum\n"); return 6; }

    /* reads record length */
    conv[2]=0; i=1;
    conv[0]=line[i]; conv[1]=line[i+1]; i+=2;
    id->reclen=(int)strtoul(conv,(char **)NULL,16);

    /* checks if linelength is (reclength*2 + 12) */
    if (((id->reclen*2)+12) != linelen) {
	fprintf(stderr,"error in intelhex file - number of characters doesn't match reclength\n");
	return 7; }

    /* reads load offset */
    conv[4]=0; conv[0]=line[i]; conv[1]=line[i+1];
    conv[2]=line[i+2]; conv[3]=line[i+3]; i+=4;
    id->offset=(int)strtoul(conv,(char **)NULL,16);

    /* reads record type */
    conv[2]=0; conv[0]=line[i]; conv[1]=line[i+1]; i+=2;
    rectype=(int)strtoul(conv,(char **)NULL,16);

    /* returns if rectype is 01 (eof_record) */
    if(rectype==1) {
	id->eof_found=1;
	return 0; /* eof_record found */ }

    /* checks if rectype is 00 */
    if(rectype!=0) {
	fprintf(stderr,"error in intelhex file - this program only accepts rectype 00(data) or 01(eof)\n");
	return 8; }

    /* reads record data */
    for(reccount=0; reccount<id->reclen; reccount++) {
	conv[0]=line[i]; conv[1]=line[i+1]; i+=2;
	id->recdata[reccount]=(int)strtoul(conv,(char **)NULL,16); }
    return 1; /* successfull read - data available */
}

int intelhex_writeline (inteldata * id) {
    unsigned int chksum;
    int reccount;
    if (id->reclen==0 || id->reclen>255 || id->offset<0 || id->offset>0xffff) {
	fprintf(stderr,"error in intelhex file - offset/reclen invalid when writing file\n");
	return 0;
    }
    fprintf(id->file, ":%02X%04X00", id->reclen, id->offset);
    chksum = id->reclen; /* reclength */
    chksum += id->offset % 256; /* low 8 bits of load offset */
    chksum += id->offset / 256; /* high 8 bits of load offset */
    /* chksum += 0x00;*/ /* rectype */
    for (reccount=0; reccount<id->reclen; reccount++) {
	fprintf(id->file, "%02X", id->recdata[reccount]);
	chksum += id->recdata[reccount];
    }
    chksum = 256 - (chksum % 256);
    if (chksum==256) chksum=0;
    fprintf(id->file, "%02X", chksum);
    fprintf(id->file, "\n");
    return 1;
}

void intelhex_writeeof (inteldata * id) {
    fprintf(id->file, ":00000001FF\n");
}
