// ** reescrito en C (antes Pascal) por CNGSOFT el Domingo 7 de Agosto de 2016 por la noche **
/*
AMSDOS - 128-BYTE HEADER
	16-BIT CHECKSUM [0..$42] = W[$43]
	TYPE = B[$12]
	LOAD = W[$15]
	SIZE = W[$18], W[$40]
	BOOT = W[$1A]
PLUS3DOS - 128-BYTE HEADER
	8-BIT CHECKSUM [0..$7E] = [$7F]
	SIZE = W[$10], W[$14]

AMSTRAD - 28-BYTE HEADER (FLAG $2C)
	CHAR[16] : FILENAME PADDED WITH $00
	BYTE : BLOCK.ID (1,2,3...)
	BYTE : LASTBLOCK?$FF:$00
	BYTE : TYPE
 	WORD : BLOCK SIZE ($0800)
	WORD : START+PAST BLOCKS
	BYTE : FIRSTBLOCK?$FF:$00
	WORD : LENGTH
	WORD : BOOT
SPECTRUM - 17-BYTE HEADER (FLAG $00)
	BYTE : TYPE
	CHAR[10] : FILENAME PADDED WITH $20
	WORD : START
	WORD : LENGTH
	WORD : BOOT
*/
#include <stdio.h> // fopen...
#include <stdlib.h> // atoi...
#include <string.h> // strcpy...

unsigned char body[1<<16],head[1<<8],o[2<<20]; int oo=0;
FILE *fi,*fo; char *si=NULL,*so=NULL,*sn=NULL;

int flag_n=0,flag_b=1000,flag_i=255,flag_o=4096,flag_h=2560,flag_p=10240,flag_z=4,flag_v=0;
int filetype=0,filesize=0,fileload=0,fileboot=0,flag_bb,flag_m=0,flag_t=0;

int i,j,k,status=0;

int ucase(int i) { return i>='a'&&i<='z'?i-32:i; }

void create11()
{
	memset(head,0,16);
	if (sn)
	{
		if (strcmp(sn,"-"))
			strcpy(head,sn);
		else
			for (int i=0;i<16&&si[i];++i)
				head[i]=ucase(si[i]);
	}
	head[0x12]=filetype;
	head[0x18]=filesize;
	head[0x19]=filesize>>8;
	head[0x1A]=fileboot;
	head[0x1B]=fileboot>>8;
}

void update11(int n,int is1st,int islast,int l)
{
	head[0x10]=n;
	head[0x11]=islast?-1:0;
	head[0x13]=l;
	head[0x14]=l>>8;
	head[0x15]=fileload;
	head[0x16]=fileload>>8;
	head[0x17]=is1st?-1:0;
}

#define putc(x) (o[oo++]=x)
int putcc(int x) { putc(x); return putc(x>>8); }
int putccc(int x) { putc(x); putc(x>>8); return putc(x>>16); }
int putcccc(int x) { putc(x); putc(x>>8); putc(x>>16); return putc(x>>24); }
int wwrite(char *s,int l) { if (l<1) return 0; memcpy(&o[oo],s,l); oo+=l; return l; }

void record10(unsigned char *t,int first,int l,int p)
{
	putc(0x10);
	putcc(p);
	putcc(l+2);
	putc(first);
	wwrite(t,l);
	int i=first,j=0;
	while (j<l)
		i^=t[j++];
	putc(i);
}
void record11(unsigned char *t,int first,int l,int p)
{
	putc(0x11);
	putcc(flag_bb);
	putcc(flag_b);
	putcc(flag_b);
	putcc(flag_b);
	putcc(flag_bb);
	putcc(flag_o);
	putc(8);
	putcc(p);
	p=1+(((l+255)/256)*258)+flag_z;
	putccc(p);
	putc(first);
	p=0;
	while (l>0)
	{
		wwrite(t+p,256);
		int crc16=-1;
		for (int c,f=0;f<256;++f) // early CRC-16-CCITT as used by Amstrad
		{
			c=t[p++];
			crc16=(crc16<<4)^(4129*(((c>>4)^(crc16>>12))&15));
			crc16=(crc16<<4)^(4129*((c^(crc16>>12))&15));
		}
		crc16=~crc16;
		putc(crc16>>8); // HI FIRST,
		putc(crc16); // AND LO NEXT!
		l-=256;
	}
	l=flag_z;
	while (l--)
		putc(255);
}
void record4B(unsigned char *t,int mul,int l,int p)
{
	putc(0x4B);
	putcccc(l+12);
	putcc(p);
	putcc(729*1);
	putcc(16000*mul);
	putcc(729*2);
	putcc(729*1);
	putc(0x12*mul);
	putc(0x54);
	wwrite(t,l);
}

int main(int argc,char *argv[])
{
	for (i=1;i<argc;++i)
	{
		if (!strcmp("-v",argv[i]))
			flag_v=1;
		else if (!strcmp("-q",argv[i]))
			flag_v=0;
		else if (!strcmp("-n",argv[i]))
			flag_n=1;
		else if (!strcmp("-b",argv[i]))
			if (i+1<argc)
				flag_b=atoi(argv[++i]);
			else
				status=1;
		else if (!strcmp("-m",argv[i]))
			if (i+1<argc)
				flag_m=atoi(argv[++i]);
			else
				status=1;
		else if (!strcmp("-i",argv[i]))
			if (i+1<argc)
				flag_i=atoi(argv[++i]);
			else
				status=1;
		else if (!strcmp("-o",argv[i]))
			if (i+1<argc)
				flag_o=atoi(argv[++i]);
			else
				status=1;
		else if (!strcmp("-t",argv[i]))
			flag_t=1;
		else if (!strcmp("-r",argv[i]))
			if (i+1<argc)
				sn=argv[++i];
			else
				status=1;
		else if (!strcmp("-h",argv[i]))
			if (i+1<argc)
				flag_h=atoi(argv[++i]);
			else
				status=1;
		else if (!strcmp("-p",argv[i]))
			if (i+1<argc)
				flag_p=atoi(argv[++i]);
			else
				status=1;
		else if (!strcmp("-z",argv[i]))
			if (i+1<argc)
				flag_z=atoi(argv[++i]);
			else
				status=1;
		else if (!si)
			si=argv[i];
		else if (!so)
			so=argv[i];
		else
			status=1;
	}
	if (status||!so)
	{
		puts("Usage: CPC2CDT [option..] infile outfile"
			"\n\t-n\tcreate new file"
			"\n\t-r FILE\tname to record on tape, unnamed file if missing"
			"\n\t-t\trecord CPC file as a standard 2k block and a giant block"
			"\n\t-m N\tmode: 0 (CPC file), 1 (CPC raw), 2 (ZX file), 3 (ZX raw),\n\t\t 4 (MSX x1), 5 (MSX x2) (0)"
			"\n\t-b N\tbaud rate for CPC blocks (1000)"
			"\n\t-i N\tID byte for raw blocks (255)"
			"\n\t-o N\tnumber of pilot pulses for CPC blocks (4096)"
			"\n\t-z N\tlength of CPC block trailing tone in bytes (4)"
			"\n\t-h N\tpause between CPC file blocks, in milliseconds (2560)"
			"\n\t-p N\tpause after the final block, in milliseconds (10240)"
		);
		return 1;
	}
	if (flag_b>0)
		flag_b=(3500000/3+flag_b/2)/flag_b;
	else
		flag_b=-flag_b;
	flag_bb=flag_b*2;
	if (fi=fopen(si,"rb"))
	{
		// READ HEADER AND DETECT FORMAT
		memset(head,0,sizeof(head));
		memset(body,0,sizeof(body));
		filesize=fread(body,1,128,fi);
		if (filesize==128)
		{
			i=j=0;
			while (i<0x43)
				j+=body[i++];
			if (j&&(body[0x43]+256*body[0x44])==j) // AMSDOS!
			{
				if (flag_v) printf("AMSDOS!! ");
				filetype=body[0x12];
				filesize=body[0x40]+body[0x41]*256;
				fileload=body[0x15]+body[0x16]*256;
				fileboot=body[0x1A]+body[0x1B]*256;
				fread(body,1,filesize,fi);
			}
			else
			{
				while (i<0x7F)
					j+=body[i++];
				if (j&&(j&0xFF)==body[0x7F]) // PLUS3DOS!
				{
					if (flag_v) printf("PLUS3DOS ");
					filetype=body[0x0F];
					filesize=body[0x10]+body[0x11]*256;
					fileload=body[0x12]+body[0x13]*256;
					fileboot=body[0x14]+body[0x15]*256;
					fread(body,1,filesize,fi);
				}
				else // NEITHER :-(
				{
					if (flag_v) printf("NEITHER? ");
					filesize=128+fread(body+128,1,(1<<16)-128,fi);
				}
			}
		}
		fclose(fi);
		if (fo=fopen(so,flag_n?"wb":"ab"))
		{
			if (flag_n)
				wwrite("ZXTape!\032\001\000\040\000\012",13);
			switch (flag_m)
			{
				case 0:
					create11();
					if (filesize>0x800)
					{
						update11(j=1,1,0,0x800); // FIRST BLOCK
						record11(head,44,28,16);
						record11(body,22,0x800,flag_h);
						k=filesize-0x800;
						i=0x800;
						if (!flag_t)
							while (k>0x800)
							{
								fileload+=0x800;
								update11(++j,0,0,0x800); // MID BLOCK
								record11(head,44,28,16);
								record11(body+i,22,0x800,flag_h);
								k-=0x800;
								i+=0x800;
							}
						fileload+=0x800;
						update11(++j,0,1,k); // LAST BLOCK
						record11(head,44,28,16);
						record11(body+i,22,k,flag_p);
					}
					else
					{
						update11(1,1,1,filesize); // SINGLE BLOCK
						record11(head,44,28,16);
						record11(body,22,filesize,flag_p);
					}
					break;
				case 1:
					record11(body,flag_i,filesize,flag_p);
					break;
				case 2:
					head[0]=filetype;
					memset(head+1,32,10);
					if (sn)
					{
						if (strcmp(sn,"-"))
						{
							i=0;
							while (sn[i]&&i<10)
							{
								head[i+1]=sn[i];
								++i;
							}
						}
						else
						{
							i=0;
							while (si[i]&&i<10)
							{
								head[i+1]=ucase(si[i]);
								++i;
							}
						}
					}
					//strcpy(head,sn);
					head[11]=filesize;
					head[12]=filesize>>8;
					head[13]=fileload;
					head[14]=fileload>>8;
					head[15]=fileboot;
					head[16]=fileboot>>8;
					record10(head,0,17,1000);
					record10(body,255,filesize,flag_p);
					break;
				case 3:
					record10(body,flag_i,filesize,flag_p);
					break;
				case 4:
					record4B(body,2,filesize,flag_p);
					break;
				case 5:
					record4B(body,1,filesize,flag_p);
					break;
			}
			if (flag_v) puts("Ok");
			fwrite(o,1,oo,fo),fclose(fo);
		}
		else
		{
			puts("error: cannot handle target!");
			return 1;
		}
	}
	else
	{
		puts("error: cannot open source!");
		return 1;
	}
 	return 0;
}
