/* MEDPlayer load.c by Teijo Kinnunen 1990 */
#include <workbench/workbench.h>
#include "med.h"
extern struct Kappale far tamakappale;
extern UWORD lohkoja;
extern struct Lohko *blocks[];
extern struct CIA far ciaa;
extern struct Soitin *samples[];
#define M0F_LINEMSK0F	0x1
#define M0F_LINEMSK1F	0x2
#define M0F_FXMSK0F	0x4
#define M0F_FXMSK1F	0x8
#define M0F_LINEMSK00	0x10
#define M0F_LINEMSK10	0x20
#define M0F_FXMSK00	0x40
#define M0F_FXMSK10	0x80
extern void __asm SetTempo(register __d0 UWORD);
typedef LONG Fixed;
typedef LONG IFFP;
typedef IFFP ClientProc();
typedef struct _ClientFrame {
	ClientProc *getList,*getProp,*getForm,*getCat; } ClientFrame;
typedef struct { ULONG oneShotHiSamples, repeatHiSamples, samplesPerHiCycle;
	UWORD samplesPerSec; UBYTE ctOctave, sCompression; Fixed volume; }
	Voice8Header;
typedef struct {
   ClientFrame clientFrame;
   UBYTE foundVHDR;
   UBYTE pad1;
   Voice8Header sampHdr;
   } SVXFrame;
extern SVXFrame smusFrame;
extern BYTE *buf;
extern int szBuf;
extern struct SoundTrackerKappale far ladattu;
static UBYTE *soitinlista = NULL;
static ULONG slistanpituus = NULL;
BOOL LataaKappale();
void LataaSoitinLista(),VapautaSoitinLista();
static BOOL LataaLohkot(),LataaIFFSoitin(),diskerr(),LoadInstrument();
static BOOL NewInstrument();
static BPTR fh = NULL;
static void ConvertTo120(),EtsiHakemisto();
UWORD soittimennum = 1;
UWORD periodit[] = {	856,808,762,720,678,640,604,570,538,508,480,453,
			428,404,381,360,339,320,302,285,269,254,240,226,
			214,202,190,180,170,160,151,143,135,127,120,113,
			214,202,190,180,170,160,151,143,135,127,120,113,
			214,202,190,180,170,160,151,143,135,127,120,113,
			214,202,190,180,170,160,151,143,135,127,120,113 };

static BOOL FRead(BPTR fh,char *ptr,long len)
{
	return((BOOL)(Read(fh,ptr,len) != len));
}

static UBYTE GetNibble(UBYTE *mem,UWORD *nbnum)
{
	UBYTE *mloc = mem + (*nbnum / 2),res;
	if(*nbnum & 0x1) res = *mloc & 0x0f;
	else res = *mloc >> 4;
	(*nbnum)++;
	return(res);
}

static UWORD GetNibbles(UBYTE *mem,UWORD *nbnum,UBYTE nbs)
{
	UWORD res = 0;
	while(nbs--) { res <<= 4; res |= GetNibble(mem,nbnum); }
	return(res);
}

static void UnpackBlock(UWORD bnum,UBYTE *from)
{
	UBYTE *to = blocks[bnum]->music,lcnt,trkn = blocks[bnum]->numtracks;
	ULONG linemsk0 = *((ULONG *)from),linemsk1 = *((ULONG *)from + 1);
	ULONG fxmsk0 = *((ULONG *)from + 2),fxmsk1 = *((ULONG *)from + 3);
	ULONG *lmptr = &linemsk0,*fxptr = &fxmsk0;
	UWORD fromn = 0,lmsk;
	UBYTE *fromst = from + 16,bcnt,*tmpto;
	from += 16;
	for(lcnt = 0; lcnt < 64; lcnt++) {
		if(lcnt == 32) { lmptr = &linemsk1; fxptr = &fxmsk1; }
		if(*lmptr & 0x80000000) {
			lmsk = GetNibbles(fromst,&fromn,(UBYTE)(trkn / 4));
			lmsk <<= (16 - trkn);
			tmpto = to;
			for(bcnt = 0; bcnt < trkn; bcnt++) {
				if(lmsk & 0x8000) {
					*tmpto = (UBYTE)GetNibbles(fromst,
						&fromn,2);
					*(tmpto+1) = (GetNibble(fromst,
							&fromn) << 4);
				}
				lmsk <<= 1; tmpto += 3;
			}
		}
		if(*fxptr & 0x80000000) {
			lmsk = GetNibbles(fromst,&fromn,(UBYTE)(trkn / 4));
			lmsk <<= (16 - trkn);
			tmpto = to;
			for(bcnt = 0; bcnt < trkn; bcnt++) {
				if(lmsk & 0x8000) {
					*(tmpto+1) |= GetNibble(fromst,
							&fromn);
					*(tmpto+2) = (UBYTE)GetNibbles(
						fromst,&fromn,2);
				}
				lmsk <<= 1; tmpto += 3;
			}
		}
		to += 3 * trkn;
		*lmptr <<= 1; *fxptr <<= 1;
	}
}

static BOOL Load120Song(BPTR fh)
{
	ULONG zmsk,*conv = 0;
	UWORD convsz;
	UBYTE scnt,*nameptr,blkcnt;
	Ilmoita("Loading MED2.00-song...");
	if(FRead(fh,(char *)(&zmsk),4)) return(diskerr(fh,0)); /* MED\x03 */
	for(scnt = 0; scnt < 32; scnt++) {
		nameptr = &tamakappale.soitin[scnt][0];
		for(;;) {
			if(FRead(fh,nameptr,1)) return(diskerr(fh,0));
			if(*nameptr++ == '\0') break;
		}
	}
	if(FRead(fh,(char *)(&zmsk),4)) return(diskerr(fh,0));
	for(scnt = 0; scnt < 32; scnt++) {
		if(zmsk & 0x80000000) {
			if(FRead(fh,&tamakappale.soittimenvoimakkuus[scnt],1)) return(diskerr(fh,0));
		} else tamakappale.soittimenvoimakkuus[scnt] = 0;
		zmsk <<= 1;
	}
	if(FRead(fh,(char *)(&zmsk),4)) return(diskerr(fh,0));
	for(scnt = 0; scnt < 32; scnt++) {
		if(zmsk & 0x80000000) {
			if(FRead(fh,(char *)(&tamakappale.stoisto[scnt]),2)) return(diskerr(fh,0));
		} else tamakappale.stoisto[scnt] = 0;
		zmsk <<= 1;
	}
	if(FRead(fh,(char *)(&zmsk),4)) return(diskerr(fh,0));
	for(scnt = 0; scnt < 32; scnt++) {
		if(zmsk & 0x80000000) {
			if(FRead(fh,(char *)(&tamakappale.stoistonpit[scnt]),2)) return(diskerr(fh,0));
		} else tamakappale.stoistonpit[scnt] = 0;
		zmsk <<= 1;
	}
	if(FRead(fh,(char *)(&tamakappale.lohkoja),2)) return(diskerr(fh,0));
	if(FRead(fh,(char *)(&tamakappale.kappaleen_pituus),2)) return(diskerr(fh,0));
	if(FRead(fh,tamakappale.soittojarjestys,tamakappale.kappaleen_pituus)) return(diskerr(fh,0));
	if(FRead(fh,(char *)(&tamakappale.tempo),26)) return(diskerr(fh,0));
	if(FRead(fh,(char *)(&zmsk),4)) return(diskerr(fh,0));
	for(scnt = 0; scnt < 32; scnt++) {
		if(zmsk & 0x80000000) {
			if(FRead(fh,&tamakappale.midikanava[scnt],1)) return(diskerr(fh,0));
		} else tamakappale.midikanava[scnt] = 0;
		zmsk <<= 1;
	}
	if(FRead(fh,(char *)(&zmsk),4)) return(diskerr(fh,0));
	for(scnt = 0; scnt < 32; scnt++) {
		if(zmsk & 0x80000000) {
			if(FRead(fh,&tamakappale.midipreset[scnt],1)) return(diskerr(fh,0));
		} else tamakappale.midipreset[scnt] = 0;
		zmsk <<= 1;
	}
	lohkoja = 0;
	for(blkcnt = 0; blkcnt < tamakappale.lohkoja; blkcnt++) {
		if(FRead(fh,&scnt,1)) return(diskerr(fh,0));
		if(AllocBlock(blkcnt,scnt)) return(diskerr(fh,"No memory."));
		lohkoja++;
		if(FRead(fh,&scnt,1)) return(diskerr(fh,0));
		if(FRead(fh,(char *)(&convsz),2)) return(diskerr(fh,0));
		if(!(conv = AllocMem(convsz+16,MEMF_PUBLIC|MEMF_CLEAR))) return(diskerr(fh,"Memory.."));
		if(scnt & M0F_LINEMSK00) *conv = 0L;
		else if(scnt & M0F_LINEMSK0F) *conv = 0xffffffff;
		else if(FRead(fh,(UBYTE *)conv,4)) return(diskerr(fh,0));
		if(scnt & M0F_LINEMSK10) *(conv+1) = 0L;
		else if(scnt & M0F_LINEMSK1F) *(conv+1) = 0xffffffff;
		else if(FRead(fh,(UBYTE *)(conv + 1),4)) return(diskerr(fh,0));
		if(scnt & M0F_FXMSK00) *(conv+2) = 0L;
		else if(scnt & M0F_FXMSK0F) *(conv+2) = 0xffffffff;
		else if(FRead(fh,(UBYTE *)(conv + 2),4)) return(diskerr(fh,0));
		if(scnt & M0F_FXMSK10) *(conv+3) = 0L;
		else if(scnt & M0F_FXMSK1F) *(conv+3) = 0xffffffff;
		else if(FRead(fh,(UBYTE *)(conv + 3),4)) return(diskerr(fh,0));
		if(FRead(fh,(UBYTE *)(conv + 4),convsz)) return(diskerr(fh,0));
		UnpackBlock((UWORD)blkcnt,(UBYTE *)conv);
		FreeMem(conv,convsz+16); conv = 0;
	}
	return(FALSE);
}

static BOOL LoadAttachedInstrs(BPTR fh)
{
	ULONG imsk;
	struct Soitin loaded;
	if(FRead(fh,(char *)(&imsk),4)) return(diskerr(0,0));
	soittimennum = 0;
	while(soittimennum < 31) {
		imsk <<= 1;	/* There's no instrument #0 */
		soittimennum++;
		if(imsk & 0x80000000) {
			if(FRead(fh,(char *)(&loaded),sizeof(struct Soitin)))
				return(diskerr(0,0));
			if(NewInstrument(loaded.length,soittimennum))
				return(diskerr(0,"No memory left"));
			samples[soittimennum]->type = loaded.type;
			if(FRead(fh,((char *)samples[soittimennum]) +
				sizeof(struct Soitin),loaded.length))
				return(diskerr(0,0));
		}
	}
	return(FALSE);
}

static BOOL NewInstrument(ULONG size, UWORD num)
{
	if(samples[num]) {
		FreeMem((void *)samples[num],samples[num]->length +
		sizeof(struct Soitin));
		samples[num] = NULL;
	}
	samples[num] = AllocMem(size + sizeof(struct Soitin),
				MEMF_PUBLIC|MEMF_CHIP|MEMF_CLEAR);
	if(!samples[num]) return(TRUE);
	samples[num]->length = size;
	return(FALSE);
}

static BOOL LoadInstrument(char *name,BOOL src)
{
	ULONG length;
	char latausnimi[100],tunnus[5];
	if(src) EtsiHakemisto(tamakappale.soitin[soittimennum],latausnimi);
	else {
		strcpy(latausnimi,name);
		strcpy(tamakappale.soitin[soittimennum],name);
	}
	if(!(fh = Open(latausnimi,MODE_OLDFILE))) {
		Ilmoita2("Can't find: ",name);
		return(TRUE);
	}
	if(Read(fh,tunnus,4) != 4) return(diskerr(fh,"Too short."));
	tunnus[4] = '\0';
	Ilmoita2("Loading sample: ",name);
	if(!strcmp(tunnus,"FORM")) return(LataaIFFSoitin(fh));
	Seek(fh,0,OFFSET_END);
	length = Seek(fh,0,OFFSET_BEGINNING);
	if(NewInstrument(length,soittimennum)) return(diskerr(0,"No memory."));
	if(Read(fh,(char *)(samples[soittimennum]) + sizeof(struct Soitin),
		length) != length) {
		FreeMem((void *)samples[soittimennum],
    			samples[soittimennum]->length + sizeof(struct Soitin));
		samples[soittimennum] = NULL;
		return(diskerr(0,0));
	}
	Close(fh); fh = NULL;
	return(FALSE);
}

BOOL LoadSong(char *nimi)
{
	UWORD scnt;
	char tunnus[5];
	if(!(fh = Open(nimi,MODE_OLDFILE))) return(diskerr(0,0));
	if(Read(fh,tunnus,4) != 4) return(diskerr(fh,"Not a song!"));
	tunnus[4] = '\0';
	Seek(fh,0,OFFSET_BEGINNING);
	if(AllocBlock(0,4)) return(diskerr(fh,"No memory!"));
	lohkoja = 0;
	if(!strcmp(tunnus,"MED\x02")) {
		Ilmoita("Loading MED2.00-song...");
		if(Read(fh,(char *)&tamakappale,sizeof(struct Kappale111)) <
			sizeof(struct Kappale111)) return(diskerr(fh,0));
		if(LataaLohkot(FALSE)) return(diskerr(fh,0));
	} else if(!strcmp(tunnus,"MED\x03")) {
		if(Load120Song(fh)) return(diskerr(fh,0));
	} else {
		if(!strcmp(tunnus,"SONG")) Ilmoita("Loading SoundFX-song...");
		else Ilmoita("Loading SoundTracker-song...");
		if(Read(fh,(char *)&ladattu,sizeof(struct SoundTrackerKappale)) <
			sizeof(struct SoundTrackerKappale)) return(diskerr(fh,"Ei ole kappale"));
		if(LataaLohkot(TRUE)) return(diskerr(fh,0));
		tamakappale.lohkoja = lohkoja;
		tamakappale.tempo = 6;
		tamakappale.kappaleen_pituus = ladattu.st_kappaleen_pit;
		tamakappale.vaihtoja = 5;
		memcpy((char *)(tamakappale.soittojarjestys),
			(char *)(ladattu.st_lohkojarj),100);
		for(scnt = 0; scnt < 15; scnt++) {
			tamakappale.soitin[scnt+1][0] = '\0';
			tamakappale.soittimenvoimakkuus[scnt+1] = ladattu.st_ins[scnt].sti_vol;
			tamakappale.stoisto[scnt+1] = ladattu.st_ins[scnt].sti_repeat;
			tamakappale.stoistonpit[scnt+1] = ladattu.st_ins[scnt].sti_replen * 2;
			strcpy(tamakappale.soitin[scnt+1],ladattu.st_ins[scnt].sti_nimi);
		}
	}
	if(tamakappale.liput & LIPPU_SUODATINPAALLA) ciaa.ciapra &= ~CIAF_LED;
	else ciaa.ciapra |= CIAF_LED;
	soittimennum = 1;
	if(tamakappale.liput & LIPPU_SOITTIMETMUKANA) LoadAttachedInstrs(fh);
	Close(fh); fh = NULL;
	if(!(tamakappale.liput & LIPPU_SOITTIMETMUKANA)) {
		for(scnt = 0; scnt < 31; scnt++) {
			if(tamakappale.soitin[scnt+1][0] && !tamakappale.midikanava[scnt+1])
			  LoadInstrument(tamakappale.soitin[scnt+1],TRUE);
			soittimennum++;
		}
	}
	soittimennum = 1;
	Ilmoita("Song is now loaded.");
	return(FALSE);
}

static BOOL LataaLohkot(st)
BOOL st; /* SoundTracker ?? */
{
	long jaljella = Seek(fh,0,OFFSET_END),lpt1 = 1024,lpt2 = 1028;
	BOOL error = FALSE;
	ULONG *convblk = AllocMem(1024 + 4,MEMF_PUBLIC|MEMF_CLEAR);
	if(!convblk) return(TRUE);	/* no memory */
	jaljella = Seek(fh,jaljella,OFFSET_BEGINNING) - jaljella;
	for(;;) {
		if(lohkoja >= 100) return(TRUE); /* Apua...ei enää!!!! */
		memset((void *)convblk,0,1028);
		if(st) {
			*convblk = 0L;
			if(lpt1 > jaljella) lpt1 = jaljella;
			if(Read(fh,(char *)(convblk + 1),lpt1)
				< lpt1) { error = TRUE; break; }
			if((jaljella -= 1024) <= 0) break; /* 0 jäljellä */
		} else {
			if(lpt2 > jaljella) lpt2 = jaljella;
			if(Read(fh,(char *)convblk,lpt2) < lpt2)
				{ error = TRUE; break; }
			if((jaljella -= 1028) <= 0) break;
		}
		ConvertTo120((UBYTE *)convblk,(UBYTE *)blocks[lohkoja]);
		if(AllocBlock(++lohkoja,4)) {
			error = TRUE; /* Out of memory!!*/
			break;
		}
	}
	ConvertTo120((UBYTE *)convblk,(UBYTE *)blocks[lohkoja]);
	lohkoja++;
	FreeMem((void *)convblk,1028);
	return(error);
}

static BOOL diskerr(BPTR fh,char *txt)
{
	char virheteksti[45],*virheptr;
	DisplayBeep(0);
	strcpy(virheteksti,"Error: ");
	switch(IoErr()) {
		case 103:	virheptr = "Insufficient free store."; break;
		case 202:	virheptr = "Object in use."; break;
		case 203:	virheptr = "Object already exists."; break;
		case 204:	virheptr = "Directory not found."; break;
		case 205:	virheptr = "Object not found."; break;
		case 210:	virheptr = "Invalid name."; break;
		case 212:	virheptr = "Not of required type."; break;
		case 213:	virheptr = "Disk not validated."; break;
		case 214:	virheptr = "Disk write-protected."; break;
		case 218:	virheptr = "Device not mounted."; break;
		case 221:	virheptr = "Disk full."; break;
		case 222:	virheptr = "File is delete-protected."; break;
		case 223:	virheptr = "File is write-protected."; break;
		case 224:	virheptr = "File is read-protected."; break;
		case 225:	virheptr = "Not a DOS disk."; break;
		case 226:	virheptr = "No disk in drive."; break;
		default:	if(txt) virheptr = txt;
				else virheptr = "Something is wrong.";
	}
	if(fh) Close(fh);
	strcpy(virheteksti+7,virheptr);
	Ilmoita(virheteksti);
	return(TRUE);
}

static BOOL LataaIFFSoitin()
{
	Seek(fh,0,OFFSET_BEGINNING);
	buf = NULL; szBuf = NULL;
	(void)ReadSample(fh);
	Close(fh); fh = NULL;
	if(!buf)    return(diskerr(0,"Failed to load!"));
	if(NewInstrument(szBuf,soittimennum)) Ilmoita("Out of memory!!");
	else {
		memcpy((char *)(samples[soittimennum]) +
			sizeof(struct Soitin),(void *)buf,szBuf);
		switch(smusFrame.sampHdr.ctOctave) {
			case 5:	samples[soittimennum]->type = IFF5OCT;	break;
			case 3:	samples[soittimennum]->type = IFF3OCT;	break;
		}
	}
	FreeMem((void *)buf,szBuf);
	return(FALSE);
}

void LataaSoitinLista(char *fname)
{
	UBYTE *scanptr;
	if(fname) {
		Ilmoita2("Searching path file: ",fname);
		fh = Open(fname,MODE_OLDFILE);
	}
	if(!fh) {
		Ilmoita("Searching path file: MED_paths");
		fh = Open("MED_paths",MODE_OLDFILE);
	}
	if(!fh) {
		Ilmoita("Searching path file: S:MED_paths");
		fh = Open("S:MED_paths",MODE_OLDFILE);
	}
	if(!fh) {
		Ilmoita("Couldn't find path file.");
		return; /* ei löytynyt */
	} else Ilmoita("Loading path file...");
	Seek(fh,0,OFFSET_END);
	slistanpituus = Seek(fh,0,OFFSET_BEGINNING);
	if(!(soitinlista = AllocMem(slistanpituus,MEMF_PUBLIC))) {
		Close(fh);
		fh = NULL;
		return;
	}
	Read(fh,soitinlista,slistanpituus);
	Close(fh);
	fh = NULL;
	for(scanptr=soitinlista;scanptr<soitinlista+slistanpituus;scanptr++) {
		if(*scanptr == '\n') *scanptr = '\0';
		if(*scanptr == ':' && *(scanptr+1) == ':') *scanptr = '\0';
		if(*scanptr >= 'a' && *scanptr <= 'z')
			*scanptr = *scanptr - 'a' + 'A'; /* isoiksi */
		/* rivinvaihdot 0:ksi */
	}
}

void VapautaSoitinLista()
{
	if(soitinlista) FreeMem((void *)soitinlista,slistanpituus);
}

static void EtsiHakemisto(nimi,mihin)
UBYTE *nimi,*mihin;
{
	UBYTE *scanptr = soitinlista,*hakemisto = NULL,isonimi[40],*isoksi = &isonimi[0];
	BOOL loytyi = FALSE;
	if(!scanptr) { /* soitinlistaa ei ole ladattu */
		strcpy(mihin,nimi);
		return;
	}
	if(*nimi == '*' || *nimi == ':') {
		*mihin = '\0'; /* ei hyväksytä! */
		return;
	}
	strcpy(isonimi,nimi);
	while(*isoksi) {
		if(*isoksi >= 'a' && *isoksi <= 'z')
			*isoksi = *isoksi - 'a' + 'A'; /*muutetaan isoksi*/
		isoksi++;
	}
	while(scanptr < soitinlista + slistanpituus) {
		if(*scanptr == '*') hakemisto = scanptr;
		else if(*scanptr == isonimi[0]) { /* tutkitaan 1.merkki */
			if(!strcmp(scanptr,isonimi)) {
				loytyi = TRUE; /* löytyi !! */
				break;
			}
		}
		while(*scanptr++ != '\0'); /* etsitään seuraava */
	}
	if(!loytyi) {
		strcpy(mihin,nimi); /* ei löytynyt, kokeillaan sellaisenaan */
		return;
	}
	/* löytyi!! */
	if(hakemisto) strcpy(mihin,hakemisto+1);
		/* kopioidaan hakemiston nimi (alussa '*') */
	strcat(mihin,nimi); /* ja lisätään nimi */
}

static void ConvertTo120(from,to)
UBYTE *from,*to;
{
	register UWORD testper,count;
	register UBYTE newnotenum = 0,instrG_V = 0;
	from += 4;	/* skip 4 bytes */
	to += 4;
	for(count = 0; count < (64 * 4); count++) {
		testper = *((UWORD *)from);
		from += 2;
		newnotenum = instrG_V = 0;
		if(testper & 0x9000) { testper &= ~0x9000; instrG_V = 1; }
		if(testper)	while(testper < periodit[newnotenum++]);
		if(instrG_V) newnotenum |= 0x80;
		*to++ = newnotenum;
		*to++ = *from++;
		*to++ = *from++;
	}
}
