/*  modload.c - Tiny MOD Player V2.11 for Watcom C/C++ and DOS/4GW

		Module loader routines.

    Copyright 1993,94 Carlos Hasan
*/
#include <stdio.h>
#include <conio.h>
#include <malloc.h>
#include <io.h>
#include <fcntl.h>

#include "modplay.h"
#include "support.h"
#include "mixer.h"

/* MOD FileFormat */

#define ID_MK   0x2E4B2E4D
#define ID_FLT4 0x34544C46
#define ID_6CHN 0x4E484336
#define ID_8CHN 0x4E484338

typedef struct
{
	byte	SampleName[22];
	word	Length;
	byte	FineTune;
	byte	Volume;
	word	LoopStart;
	word	LoopLength;
} MODSample;

typedef struct
{
	byte				SongName[20];
	MODSample 	Samples[31];
	byte				OrderLength;
	byte				ReStart;
	byte				Orders[128];
	dword 			Sign;
} MODHeader;

/* MOD PeriodTable */
static word PeriodTable[12*7] =
{
	3424,3232,3048,2880,2712,2560,2416,2280,2152,2032,1920,1812,
	1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,906,
	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,
	107,101,95,90,85,80,75,71,67,63,60,56,
	53,50,47,45,42,40,37,35,33,31,30,28
};

/* MOD Loader Routine */

#define Swap(w) (((dword)(w&0xff))<<8|((dword)(w>>8)))

static char *lowbuff; // low memory buffer.
static short channel;
static long dsize;

Module *MODLoadModule(char *Path)
{
	int handle,NumTracks,NumPatts,i,j;
	word Note,Period,Inst,Effect;
	dword Length,LoopStart,LoopLength;
	MODHeader Header;
	MODSample *Sample;
	Module *Song;
	byte *Patt;
	long bsize;

	bsize = MODGetSetBuff(0);
	lowbuff = memalloc(bsize);
	MODGetSetBuff(lowbuff);
	dsize = MODSizeDMA(); // size of dma transfer buffer.

	if ((handle = open(Path,O_RDONLY | O_BINARY)) == -1)
        return NULL;

	if (read(handle,&Header,sizeof(Header)) != sizeof(Header))
	{
		close(handle);
		return NULL;
	}

	if ((Header.Sign == ID_MK) || (Header.Sign == ID_FLT4))
			NumTracks = 4;
	else if (Header.Sign == ID_6CHN)
			NumTracks = 6;
	else if (Header.Sign == ID_8CHN)
			NumTracks = 8;
	else
	{
		close(handle);
		return NULL;
	}

	if ((Song = calloc(1,sizeof(Module))) == NULL)
	{
		close(handle);
		return NULL;
	}

	Song->NumTracks = NumTracks;
	Song->OrderLength = Header.OrderLength;
	for (NumPatts = i = 0; i < 128; i++)
	{
		Song->Orders[i] = Header.Orders[i];
		if (NumPatts < Header.Orders[i]) NumPatts = Header.Orders[i];
	}
	NumPatts++;

	for (i = 0; i < NumPatts; i++)
	{
		if ((Song->Patterns[i] = memalloc(256*NumTracks)) == NULL)
		{
			MODFreeModule(Song);
			close(handle);
			return NULL;
		}
		if (read(handle,Song->Patterns[i],256*NumTracks) != 256*NumTracks)
		{
			MODFreeModule(Song);
			close(handle);
			return NULL;
		}

		for (Patt = Song->Patterns[i], j = 0; j < 64*NumTracks; Patt += 4, j++)
		{
			Period = ((word)(Patt[0] & 0x0F) << 8) | (Patt[1]);
			Inst = (Patt[0] & 0xF0) | (Patt[2] >> 4);
			Effect = ((word)(Patt[2] & 0x0F) << 8) | (Patt[3]);
			for (Note = 0; Note < 12*7; Note++) if (Period >= PeriodTable[Note]) break;
			if (Note == 12*7)
				Note = 0;
			else
				Note++;
			Patt[0] = Note;
			Patt[1] = Inst;
			Patt[2] = Effect & 0xFF;
			Patt[3] = Effect >> 8;
		}
	}

	for (i = 1; i <= 31; i++)
	{
		Sample = &Header.Samples[i-1];
		if (Sample->Length)
		{
			Length = Swap(Sample->Length) << 1;
			LoopStart = Swap(Sample->LoopStart) << 1;
			LoopLength = Swap(Sample->LoopLength) << 1;
			if ((Song->SampPtr[i] = memalloc(Length)) == NULL)
			{
				MODFreeModule(Song);
				close(handle);
				return NULL;
			}
			if ((unsigned)read(handle,Song->SampPtr[i],Length) != Length)
			{
				MODFreeModule(Song);
				close(handle);
				return NULL;
			}
			if (LoopLength <= 2)
			{
				Song->SampLoop[i] = Song->SampPtr[i] + Length;
				Song->SampEnd[i] = Song->SampLoop[i];
			}
			else
			{
				Song->SampLoop[i] = Song->SampPtr[i] + LoopStart;
				Song->SampEnd[i] = Song->SampLoop[i] + LoopLength;
			}
			Song->SampVolume[i] = Sample->Volume;
		}
	}

	close(handle);


	return Song;
}

void MODFreeModule(Module *Song)
{
	int i;

	if (Song)
	{

		TriggerStop(channel);

		for (i = 0; i < 128; i++) if (Song->Patterns[i]) memfree(Song->Patterns[i]);
		for (i = 1; i <= 31; i++) if (Song->SampPtr[i]) memfree(Song->SampPtr[i]);
		memfree(Song);
	}
	memfree(lowbuff); // Free up mixing buffers.
}



long MODPlay(Module *Modulefile,word Chans,word Rate)
{
	long ret;
	char *buff;
	FILE *fph;

	ret = MODPlayModule(Modulefile,Chans,Rate,&buff);
	if ( !ret ) // If ok, then start up the mixing channel.
	{
		channel = TriggerSound(buff,dsize,LOOPING,FULLVOLUME,FLATFREQ);
	}
	return(ret);
}

void MODTimeSlice(void)
{
	char *poll;

	poll = TriggerLoc(channel); // get current location.
	MODPoll(poll);
}
