/* bin2exl sourceFile targetFile [-t:DOS|K7|TASM] [-H:0xHeaderSize] [-r:0xRecordSize] [-n:4LettersK7Filename] 
*/

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>

void erreur(int);
void init();
void end();
int ManageArguments();
int DOSConv();
int K7Conv();
int TASMConv();
int load();
int save();

struct header_DCExel
{
	unsigned char Check;
	unsigned char Flag;
	unsigned char NbRecordH;
	unsigned char NbRecordL;	
	unsigned char MaxRecordLengthH;
	unsigned char MaxRecordLengthL;
	char Reserved1[7];
	unsigned char FirstAuH;
	unsigned char FirstAuL;
	unsigned char NbAu;
	char Reserved2[496];
};

char *source=NULL;
char *target=NULL;
int TypeDest=0;
unsigned char K7Name[4]={0x20,0x20,0x20,0x20};
unsigned char *bufIN;
unsigned char *bufOUT;
int LengthIN;
int LengthOUT;
int headerSize=0;
int recordSize=0;

int main (int argc, char *argv[])
{
	int result;

	printf("Bin2Exl 2010.01 (c)Jester\n");
	if (ManageArguments(argc,argv))	exit(1);
	
	init();
	LengthIN=load();
	if (LengthIN) {
		switch (TypeDest)
		{
			case 1: result=DOSConv();break;
			case 2: result=K7Conv();break;
			case 3: result=TASMConv();break;			
		}
		if (!result) save();
	}
	end();
	exit(0);
}

void erreur(int err)
{
	fprintf(stderr,"ERROR: ");
	switch (err)
	{
		case 1: fprintf(stderr,"Same filename for source and target files.\n");break;
		case 2: fprintf(stderr,"Cannot open source file.\n");break;
		case 3: fprintf(stderr,"Cannot save target file.\n");break;
		case 4: fprintf(stderr,"When loading source file.\n");break;
		case 5: fprintf(stderr,"When saving target file.\n");break;
		case 6: fprintf(stderr,"Memory problem.\n");break;
		case 7: fprintf(stderr,"Incorrect Header size.\n");break;
		default: fprintf(stderr,"Erreur %i\n",err);
	}
}

int ManageArguments(int argc, char *argv[])
{	int c,i;
	
	while ((c = getopt (argc, argv, "hit:H:r:n:")) != -1)
		switch (c)
		{
			case 'h':
				fprintf(stdout,"Usage: bin2exl sourceFile targetFile [-t:DOS|K7|TASM] [-H:0xHeaderSize] [-r:0xRecordSize] [-n:4LettersK7Filename]\n");	
				return 1;
			case 't':
				if (!strcmp(":DOS",optarg)) TypeDest=1;
				else
					if (!strcmp(":K7",optarg)) TypeDest=2;				
				else
					if (!strcmp(":TASM",optarg)) TypeDest=3;
				break;
			case 'H':
				headerSize=strtol(optarg+1,NULL,16);
				break;
			case 'r':
				recordSize=strtol(optarg+1,NULL,16);
				break;
			case 'n':
				if (strlen(optarg+1)) {
					if (strlen(optarg+1)>4) {
						fprintf (stderr, "Incorrect K7 Filename.\n");
						return 1;}
					i=0;
					while ((optarg+1)[i]) K7Name[i++]=(optarg+1)[i];
				}
				break;
			case '?':
				if (optopt == 'H' || optopt == 'r' || optopt == 'n' || optopt=='t')
					fprintf (stderr, "Option -%c requires an argument.\n", optopt);
				else if (isprint (optopt))
					fprintf (stderr, "Unknown option `-%c'.\n", optopt);
				else
					fprintf (stderr,"Unknown option character `\\x%x'.\n",optopt);
				return 1;
			default: return 1;
		}
	if (!TypeDest) TypeDest=1;
		
	if (argc-optind<2 || argc-optind>2)
		{
		fprintf (stderr,"Two arguments required: sourceFile TargetFile.\n");
		return 1; }
		
	source=argv[optind];
	target=argv[optind+1];
	if (!strcmp(source,target)) {erreur(1); return 1;}
	return 0;
}
 
 void init()
 {
	bufIN=malloc(640000);
	if (!bufIN) erreur(6);
	bufOUT=malloc(640000);
	if (!bufOUT) erreur(6);	
}
 
void end()
{
	if (bufIN) free(bufIN);
	if (bufOUT) free(bufOUT);
}

int load()
{
	FILE *fd;
	int longueur;
	
	fd=fopen(source,"rb");
	if (!fd) {erreur(2); return 0;}
	longueur=fread(bufIN,1, 640000,fd);
	if (!longueur) erreur(4);
	fclose(fd);
	return longueur;
}

int save()
{
	FILE *fd;
	int longueur;
	
	fd=fopen(target,"wb");
	if (!fd) {erreur(3); return 0;}
	
	longueur=fwrite(bufOUT,1,LengthOUT,fd);
	if (LengthOUT!=longueur) erreur(5);
	fclose(fd);
	fprintf(stderr,"%s file written.\n",target);
	return longueur;
}

int DOSConv()
{	int i;
	int ptt=0;
	int pts=0;
	struct header_DCExel hddc;
	int maxRec,nbRecFull,nbRecTot,lastRec;
	
	if (headerSize>LengthIN) {erreur(7); return 1;}
	else LengthIN=LengthIN-headerSize;
	if (headerSize & recordSize) {
		maxRec=headerSize*(headerSize>recordSize) + recordSize*(headerSize<=recordSize);
		nbRecFull=LengthIN/recordSize;
		lastRec=(LengthIN%recordSize>0);
		nbRecTot=nbRecFull+lastRec+1; }
	else if (headerSize) {
		maxRec=headerSize*(headerSize>LengthIN) + LengthIN*(headerSize<=LengthIN);
		nbRecFull=1;
		lastRec=0;
		nbRecTot=nbRecFull+lastRec+1; 
		recordSize=LengthIN;}
	else if (recordSize) {
		maxRec=recordSize;
		nbRecFull=LengthIN/recordSize;
		lastRec=(LengthIN%recordSize>0);
		nbRecTot=nbRecFull+lastRec; }

	else { 
		maxRec=LengthIN;
		nbRecFull=1;
		lastRec=0;
		nbRecTot=nbRecFull+lastRec; 
		recordSize=LengthIN;}

	hddc.Check=0xFD;
	hddc.Flag=0x00;
	hddc.NbRecordH=nbRecTot/256;
	hddc.NbRecordL=nbRecTot & 0xFF;	
	for(i=0;i<7;i++) hddc.Reserved1[i]=0;
	hddc.FirstAuH=0x00;
	hddc.FirstAuL=0x01;
	for(i=0;i<496;i++) hddc.Reserved2[i]=0;
	hddc.MaxRecordLengthH=maxRec/256;
	hddc.MaxRecordLengthL=maxRec & 0xFF;	

	float NBAU=ceil(((headerSize>0)*(headerSize+2) + (recordSize+2)*nbRecFull + (recordSize+2)*lastRec - LengthIN%recordSize)/512);
	if (NBAU<((float)((headerSize>0)*(headerSize+2) + (recordSize+2)*nbRecFull + (recordSize+2)*lastRec - LengthIN%recordSize)/(float)512)) hddc.NbAu=NBAU+1;
	else hddc.NbAu=NBAU;
	
	memcpy(bufOUT+ptt,&hddc, sizeof(struct header_DCExel));
	ptt=ptt+sizeof(struct header_DCExel);
	if (headerSize) {
		bufOUT[ptt++]=headerSize / 256;
		bufOUT[ptt++]=headerSize & 0xFF;
		memcpy(bufOUT+ptt,bufIN+pts, headerSize);
		ptt=ptt+headerSize;
		pts=pts+headerSize;
	}

	for(i=0;i<nbRecFull;i++) {
		bufOUT[ptt++]=recordSize / 256;
		bufOUT[ptt++]=recordSize & 0xFF;
		memcpy(bufOUT+ptt,bufIN+pts, recordSize);
		ptt=ptt+recordSize;
		pts=pts+recordSize;
	}
	if (lastRec) {
		bufOUT[ptt++]=(LengthIN%recordSize) / 256;
		bufOUT[ptt++]=(LengthIN%recordSize) & 0xFF;
		memcpy(bufOUT+ptt,bufIN+pts, LengthIN%recordSize);
		ptt=ptt+LengthIN%recordSize;
		pts=pts+LengthIN%recordSize;
	}

	for(;ptt<(hddc.NbAu+1)*512;ptt++) bufOUT[ptt]=0;

	LengthOUT=ptt;
	
	return 0;
}

int TASMConv()
{	
	int i;
	int parlg=17;
	int ptt=0;
	
	for(i=0; i<LengthIN; i++,parlg++)
	{
		if (parlg>16)
		{
			ptt=ptt+sprintf(bufOUT+ptt,"\r\n\t.byte ");
			parlg=1;
		}
		ptt=ptt+sprintf(bufOUT+ptt,"$%02X",bufIN[i]);
		if (i<LengthIN-1 && parlg<16) ptt=ptt+sprintf(bufOUT+ptt,",");
	}
	ptt=ptt+sprintf(bufOUT+ptt, "\r\n");
	

	LengthOUT=ptt;
	
	return 0;
}

int K7Conv()
{
	char sync[256] = {
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
										0x55,0x55,0x55,0x55,0x55,0x70};
	
	int i,j;
	int ptt=0;
	int pts=0;
	int nbRecFull,lastRec;
	unsigned char checksum=0;
	
	if (headerSize>LengthIN) {erreur(7); return 1;}
	else LengthIN=LengthIN-headerSize;
	if (headerSize & recordSize) {
		nbRecFull=LengthIN/recordSize;
		lastRec=(LengthIN%recordSize>0); }
	else if (headerSize) {
		nbRecFull=1;
		lastRec=0;
		recordSize=LengthIN;}
	else if (recordSize) {
		nbRecFull=LengthIN/recordSize;
		lastRec=(LengthIN%recordSize>0); }
	else { 
		nbRecFull=1;
		lastRec=0;
		recordSize=LengthIN;}
	if (headerSize) {
		memcpy(bufOUT+ptt,sync,256);
		ptt=ptt+256;
		bufOUT[ptt++]=K7Name[3];
		bufOUT[ptt++]=K7Name[2];
		bufOUT[ptt++]=K7Name[1];
		bufOUT[ptt++]=K7Name[0];
		bufOUT[ptt++]=0x7A;	
		bufOUT[ptt++]=(headerSize-1)& 0xFF;
		bufOUT[ptt++]=(headerSize-1)/256;
		bufOUT[ptt++]=0x00;
		bufOUT[ptt++]=0x00;
		for(i=0;i<headerSize;i++) {
			bufOUT[ptt++]=bufIN[pts];
			checksum=checksum+bufIN[pts++];}
		bufOUT[ptt++]=checksum;
		bufOUT[ptt++]=checksum;	
	}
	for(i=0;i<nbRecFull;i++) {
		memcpy(bufOUT+ptt,sync,256);
		ptt=ptt+256;
		bufOUT[ptt++]=K7Name[3];
		bufOUT[ptt++]=K7Name[2];
		bufOUT[ptt++]=K7Name[1];
		bufOUT[ptt++]=K7Name[0];
		bufOUT[ptt++]=0x7A;	
		bufOUT[ptt++]=(recordSize-1)& 0xFF;
		bufOUT[ptt++]=(recordSize-1)/256;
		bufOUT[ptt++]=0x00;
		bufOUT[ptt++]=0x00;
	  checksum=0;
		for(j=0;j<recordSize;j++) {
			bufOUT[ptt++]=bufIN[pts];
			checksum=checksum+bufIN[pts++];}
		bufOUT[ptt++]=checksum;
		bufOUT[ptt++]=checksum;	
	}
	if (lastRec) {
		checksum=0;
		memcpy(bufOUT+ptt,sync,256);
		ptt=ptt+256;
		bufOUT[ptt++]=K7Name[3];
		bufOUT[ptt++]=K7Name[2];
		bufOUT[ptt++]=K7Name[1];
		bufOUT[ptt++]=K7Name[0];
		bufOUT[ptt++]=0x7A;	
		bufOUT[ptt++]=(LengthIN%recordSize-1)& 0xFF;
		bufOUT[ptt++]=(LengthIN%recordSize-1)/256;
		bufOUT[ptt++]=0x00;
		bufOUT[ptt++]=0x00;
		for(i=0;i<(LengthIN%recordSize);i++) {
			bufOUT[ptt++]=bufIN[pts];
			checksum=checksum+bufIN[pts++];}
		bufOUT[ptt++]=checksum;
		bufOUT[ptt++]=checksum;	
	}
	
	LengthOUT=ptt;

	return 0;
}

