//Структуры

struct ModSample
{
	byte msName[22];
	word msLength;
	byte msFinetune;
	byte msVolume;
	word msRepeat;
	word msRepLen;
};

struct ModHeader
{
	byte mhName[20];
	struct ModSample mhSamples[31];
	byte mhOrderLen;
	byte mhReStart;
	byte mhOrder[128];
	word mhSign[2];
}Header;


struct ModInfoRec
{
	byte OrderLen;
	byte ReStart;
	byte Order[128];
	dword Patterns;
	word SampOfs[31];
	word SampSeg[31];
	word SampLen[31];
	word SampRep[31];
	word SampRepLen[31];
	word SampVol[31];
}ModInfo;

//Resources and Data.
word FileHandle;
byte ErrorInfo;
byte EndModule;

//Объявления процедур
word FOPEN(DX,AL);
word FREAD(BX,DX,CX);
word FSEEK(BX,CX,DX,AL);

void LoadModule(word FileName)
{
	ErrorInfo=1;
	ClearModInfo();
	FileHandle=FOPEN(FileName,0);
	IF(AX==0)goto FAILED;
	FREAD(AX,#Header,sizeof(ModHeader));
	IF(AX!=sizeof(ModHeader))goto CLOSEFILE;
	IF(Header.mhSign=='.M')&&(Header.mhSign[2]=='.K')GOTO ISMODFILE;
	IF(Header.mhSign=='LF')&&(Header.mhSign[2]=='4T')GOTO ISMODFILE;
	SI=#Header.mhSamples[15].msName;
	DI=#Header.mhOrderLen;
	ES=DS;
	$cld
	CX=130;
	$rep $movsb
	DI=#Header.mhSamples[15].msName;
	AX=0;
	CX=16*sizeof(ModSample);
	$rep $stosb
	FSEEK(FileHandle,0,600,0);
ISMODFILE:
	ModInfo.OrderLen=Header.mhOrderLen;
	AL=Header.mhReStart;
	IF(AL>=Header.mhOrderLen)AL=0x7F;
	ModInfo.ReStart=AL;
	CX=128;
	AX=0;
	BX=0;
	loop(CX){
		AH=Header.mhOrder[BX];
		ModInfo.Order[BX]=AH;
		IF(AH>AL)AL=AH;
		BX++;
	}
	AH=0;
	AX++;
	$push AX
	GETMEM(AX*64);
	$pop CX
	IF(AX==0)goto CLOSEFILE;
	DSWORD[#ModInfo.Patterns]=0;
	DSWORD[#ModInfo.Patterns+2]=AX;
	READFAR(,FileHandle,CX<<10,AX);
	IF(CARRYFLAG)GOTO CLOSEFILE;
	FOR(DI=0,SI=0;DI<31*2;DI++,DI++,SI+=sizeof(ModSample)){
		AX=DSWORD[#Header.mhSamples[0].msLength+SI];
		AL><AH;
		AX<<=1;
		ModInfo.SampLen[DI]=AX;
		ModInfo.SampVol[DI]=DSBYTE[#Header.mhSamples[0].msVolume+SI];
		AX=DSWORD[#Header.mhSamples[0].msRepeat+SI];
		AL><AH;
		AX<<=1;
		ModInfo.SampRep[DI]=AX;
		AX=DSWORD[#Header.mhSamples[0].msRepLen+SI];
		AL><AH;
		AX<<=1;
		ModInfo.SampRepLen[DI]=AX;
	}
	FOR(DI=0;DI<31*2;DI++,DI++){
		AX=ModInfo.SampLen[DI]>>4;
		IF(NOTZEROFLAG){
			GETMEM(AX+1);
			IF(AX==0)GOTO CLOSEFILE;
			ModInfo.SampOfs[DI]=0;
			ModInfo.SampSeg[DI]=AX;
			READFAR(,FileHandle,ModInfo.SampLen[DI],AX);
			IF(CARRYFLAG)GOTO CLOSEFILE;
		}
	}
	ErrorInfo=0;
	EndModule=FALSE;
CLOSEFILE:
	FCLOSE(,FileHandle);
FAILED:
}

void FreeModule()
{
	$pusha
	AX=DSWORD[#ModInfo.Patterns+2];
	IF(AX!=0)FREEMEM();
	FOR(SI=0;SI<31*2;SI++,SI++){
		AX=ModInfo.SampSeg[SI];
		IF(ModInfo.SampLen[SI]!=0)&&(AX!=0)FREEMEM(AX);
	}
	ClearModInfo();
	$popa
}


void ClearModInfo()
{
	$pusha
	$push ES
	ModInfo=0;
	$pop ES
	$popa
}
