/*
 *  load_mod.c - Loads standard Protracker modules.
 *
 *  (C) 1994 Mikael Nordqvist (d91mn@efd.lth.se, mech@df.lth.se)
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <sys/ultrasound.h>
#include <limits.h>
#include <string.h>
#include <ctype.h>

#include "mod.h"

/* External global variables */

SEQ_DECLAREBUF();
extern int seqfd, gus_dev;

extern struct mod_info M;
extern struct options opt;

extern char quit;

extern int periodtable[NR_OCTAVES*12];
extern char effect_used[NR_EFX];

/* Local functions */
static int process_header(char *);
static int read_patterndata(int);

/* Defines and variables used while loading module */

#define MOD31_NAME              0
#define MOD31_SAMPLEINFO       20
#define MOD31_SONGLENGTH      950
#define MOD31_RESTARTPOS      951
#define MOD31_PATTERNTABLE    952
#define MOD31_MAGIC          1080
#define MOD31_PATTERNDATA    1084

#define MOD31_SAMPLEINFO_SIZE         30

static int expected_length, OS15;


int load_mod(int fd)
{
    unsigned char buf[MOD31_PATTERNDATA];
    char tmpbuf[80];

    int i, diff, fatalerr, tmp;

    /* The following might fail if the total length of a 15 instrument
     * module is less than MOD31_PATTERNDATA, but that is very
     * unlikely.
     */
    if(read(fd, buf, MOD31_PATTERNDATA) != MOD31_PATTERNDATA) {
	info("Unable to read header from file.\n");
	return 0;
    }

    if(!process_header(buf))
	return 0;

    /* As a 15 instrument modules has no tag, a -4 offset is needed in
     * that case.
     */
    if(lseek(fd, MOD31_PATTERNDATA+OS15+(OS15 ? -4 : 0), SEEK_SET)
       == -1) {
	info("Unable to read patterndata.\n");
	return 0;
    }
    
    if(!read_patterndata(fd)) {
	info("Unable to read patterndata.\n");
	return 0;
    }

    ioctl(seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev);

    tmp=0;
    for(i=1; i <=M.nr_samples; ++i)
	if(M.sample[i].length > 4)
	    tmp++;

    info("\nUploading instruments to GUS...\n");
    
    fatalerr=0;
    for(i=1; i <= M.nr_samples; ++i) {
	if(M.sample[i].length > 4) { /* Only load non-silent samples */
	    if(!read_and_upload_sample(fd, i)) {
		fatalerr=1;
		break;
	    }
	}
	else { /* Skip sample */
	    if(lseek(fd, M.sample[i].length, SEEK_CUR) == -1){
		fatalerr=1;
		break;
	    }
	}
	if(quit == QUIT_SIGNAL) /* Bail out if we got QUIT_SIGNAL */
	    return 0;
    }
    
    if(fatalerr) {
	diff=expected_length-lseek(fd, 0, SEEK_CUR);
	sprintf(tmpbuf, "File %d bytes short", diff);
	print_status(tmpbuf);
	info("\nShort file (%d bytes missing).\n", diff);
	sleep(1);
	return 0;
    }
    
    /* Check if filelength was as expected. This is done with two 
     * statements to make sure we don't move the filepointer to the 
     * end before checking current position.
     */

    diff=-lseek(fd, 0, SEEK_CUR);
    diff+=lseek(fd, 0, SEEK_END);

    if(diff) {
	if(diff > 1024*64) {
	    print_status("Too many garbagebytes");
	    sleep(1);
	    return 0;
	}
	else {
	    sprintf(tmpbuf, "Found %d garbagebytes", diff);
	    print_status(tmpbuf);
	    sleep(1);
	}

	info("\nFound %d garbagebytes after samples. Ignoring.\n", diff);
    }

    print_songname(M.name);
    print_samples(1);
    print_songinfo(M.nr_voices, tmp, "MOD", expected_length,
		   M.songlength, M.nr_patterns);
    
    return 1;
}


static int process_header(char *buf)
{
    char tmpch, *p, typebuf[16], tmpbuf[80];
    int i, tmp;
    struct sample_info *s;

    M.nr_tracks=0;
    M.nr_voices=4;     /* Default to 4-channel 31 sample module */
    M.nr_samples=31;
    OS15=0;


    if(!strncmp((char *)&buf[MOD31_MAGIC], "M.K.", 4)) {
	sprintf(typebuf, "M.K.");
    }
    else if(!strncmp((char *)&buf[MOD31_MAGIC], "M!K!", 4)) {
	sprintf(typebuf, "M!K!");
    }
    else if(!strncmp((char *)&buf[MOD31_MAGIC], "FLT4", 4)) {
	sprintf(typebuf, "FLT4");
    }
    else if(!strncmp((char *)&buf[MOD31_MAGIC], "4CHN", 4)) {
	sprintf(typebuf, "4CHN");
    }
    else if(!strncmp((char *)&buf[MOD31_MAGIC], "6CHN", 4)) {
	sprintf(typebuf, "6CHN");
	M.nr_voices=6;
    }
    else if(!strncmp((char *)&buf[MOD31_MAGIC], "8CHN", 4)) {
	sprintf(typebuf, "8CHN");
	M.nr_voices=8;
    }
    else if(!strncmp((char *)&buf[MOD31_MAGIC], "16CH", 4)) {
	sprintf(typebuf, "16CH"); /* Take Tracker */
	M.nr_voices=16;
    }
    else if(!strncmp((char *)&buf[MOD31_MAGIC], "32CH", 4)) {
	sprintf(typebuf, "32CH"); /* Take Tracker */
	M.nr_voices=32;
    }
    else {
	sprintf(typebuf, "15 sample");
	info("No known tag found, trying 15 instrument module.\n");
	    M.nr_samples=15;
	OS15=-16*MOD31_SAMPLEINFO_SIZE; /* Modifier for 15 instrument module */
    }
    
    sprintf(tmpbuf, "Loading MOD (%s)", typebuf);
    print_status(tmpbuf);
    info("Type: '%s'\n", typebuf);
    
    M.sample=(struct sample_info *)malloc((1+M.nr_samples)*
					  sizeof(struct sample_info));

    strncpy(M.name, &buf[MOD31_NAME], 20);
    M.name[20]=0;
    fix_string(M.name);

    M.songlength=buf[MOD31_SONGLENGTH+OS15];
    M.restartpos=buf[MOD31_RESTARTPOS+OS15];

    if(!M.songlength || M.songlength > 127)
	return 0;
    
    memcpy(M.patterntable, &buf[MOD31_PATTERNTABLE+OS15], 128);
    
    print_sample_info_header();

    expected_length=0; /* Start counting expected filesize */
    p=&buf[MOD31_SAMPLEINFO];
    
    
    /* Here we make extra checks for 15 instrument modules, as this is where
     * we get if we try to load something not supported, or something not
     * a module.
     */
    
    for(i=1; i <= M.nr_samples; ++i) {
	s=&M.sample[i];
	s->valid=0;              /* Valid isn't set to TRUE until it's
				  * sampledata has been read.
				  */
	strncpy(s->name, p, 22);
	s->name[22]=0;
	p+=22;
	fix_string(s->name);
	
	s->length=SWAPSHORT(p)*2;
	p+=2;
	
	tmpch=(*p++&0x0f);
	s->finetune=(tmpch > 7 ? tmpch|0xf0 : tmpch);
	
	s->volume=*(unsigned char *)p++;

	if(s->volume > 64 && M.nr_samples == 15 && !opt.tolerant)
	    return 0;  /* Bail */
	
	tmp=SWAPSHORT(p)*2; /* Repeat start */
	p+=2;
	if(s->length && (tmp < 0 || tmp >= s->length)) {
	    tmp=0;
	}
	s->repeat_start=tmp;
	
	/* -1 as the sample at repeat_end is played */
	tmp+=SWAPSHORT(p)*2-1;
	p+=2;

	if(tmp < s->repeat_start+2)
	    tmp=s->repeat_start+1; /* turn off looping */
	else if(tmp >= s->length)
	    tmp=s->length-1;       /* truncate loop-end */

	s->repeat_end=tmp;
	
	if(s->repeat_end > s->repeat_start+1)
	    s->looped=LOOP_FORWARD;
	else
	    s->looped=0;

	s->bits_16=0;
	s->unsigned_data=0;
	
	expected_length+=s->length;
	
	s->c2freq=-1; /* Use PAL/NTSC */

	print_sample_info(i);
    }
    
    p=&buf[MOD31_PATTERNTABLE+OS15];
    for(i=0, tmp=0; i <= 127; ++i) {
	M.patterntable[i]=p[i];
	if(p[i] > tmp)
	    tmp=p[i];
    }
    M.nr_patterns=++tmp;

    if(M.nr_patterns > 127)
	return 0;

    if(M.restartpos >= M.songlength)
	M.restartpos=0;
    
    info("\nVoices: %d  Patterns: %d  Songlength: %d",
	 M.nr_voices, M.nr_patterns, M.songlength);
    if(M.restartpos)
	info("  Restartpos: %d\n", M.restartpos);
    else
	info("\n");
    
    for(i=0; i < M.nr_voices; ++i)
	M.panning[i]=get_voice_balance(i);
    
    expected_length+=MOD31_PATTERNDATA+OS15+(OS15 ? -4 : 0)+
	M.nr_patterns*1024;
    
    M.volrange=64;

    if(expected_length > 2*1024*1024) /* Sanity check */
	return 0;

    return 1;
}


static int read_patterndata(int fd)
{
    unsigned char buf[4*64*32]; /* 4 bytes, 64 lines, MAX 32 channels */
    unsigned char tmp;
    unsigned int period;
    struct event *e;
    int p, l, v, pos, hinote, lonote;
    struct event events[64*32]; /* 64 lines, 32 channels = 1 pattern */
    struct event *track;
    
    hinote=0;
    lonote=255;
    
    for(v=0; v < NR_EFX; ++v)
	effect_used[v]=0;

    for(v=0; v < M.nr_voices; ++v)
	M.track_idx[v]=(int *)malloc(M.nr_patterns*sizeof(int));

    /* Allocate for worst case (no identical tracks) */
    M.tracks=(struct event **)malloc(M.nr_patterns*M.nr_voices*
				    sizeof(struct event *));

    for(p=0; p < M.nr_patterns; ++p) {
	if(read(fd, buf, 4*64*M.nr_voices) != 4*64*M.nr_voices)
	    return 0;
	pos=0;
	track=events;
	for(l=0; l < 64; ++l, track++) {
	    for(v=0; v < M.nr_voices; ++v) {
		e=&track[v*64];
		
		e->sample=
		    (buf[pos]&0xf0)|((buf[pos+2]>>4)&0x0f);
		tmp=buf[pos+2]&0x0f;
		if(tmp != 0x0e) {
		    e->arg=buf[pos+3];
		}
		else {
		    tmp=0x10|((buf[pos+3]>>4)&0x0f);
		    e->arg=buf[pos+3]&0x0f;
		}
		e->effect=tmp;

		if(tmp || e->arg)
		    effect_used[tmp]=1;
		
		period=((buf[pos]&0x0f)<<8)|buf[pos+1];
		/* period*4 as we convert X-2 >= X-4 */
		e->note=
		    (period ? period2note(period*4) : 0);
		
		if(e->note < BASE_NOTE+2*12 || e->note >= BASE_NOTE+7*12)
		    e->note=0; /* Catch any garbage notes */
		
		/* Clear the second effect */
		e->effect2=e->arg2=0;

		if(e->note) {
		    hinote=MAX(hinote, e->note);
		    lonote=MIN(lonote, e->note);
		}
		pos+=4;
	    }
	}
	/* Convert pattern to tracks */
	for(v=0; v < M.nr_voices; ++v)
	    M.track_idx[v][p]=get_track_idx(&events[v*64]);
    }

    if(!opt.low_note) { /* Determine if we should allow octave 0 and 4 */
	if(lonote < BASE_NOTE+3*12 || hinote > BASE_NOTE+6*12-1) {
	    opt.low_note=BASE_NOTE+2*12;
	    opt.high_note=BASE_NOTE+7*12-1;
	}
	else {
	    opt.low_note=BASE_NOTE+3*12;
	    opt.high_note=BASE_NOTE+6*12-1;
	}
    }
    else { /* Warn user if he made an invalid choice */
	if(hinote > opt.high_note || lonote < opt.low_note)
	    info("Warning: Module contains notes in other octaves than"
		 " the allowed.\n         It might be played"
		 " incorrectly.\n");
    }
    
    print_used_effects();

    return 1;
}


/*  Balance: 0 is left, 15 is right.
 */

int get_voice_balance(int v)
{
    static char bal[]={1, 14, 14, 1};

    if(M.format == MODFORMAT_ULT)
	return 7;
    else if(M.format == MODFORMAT_MTM)
	return (v%2 ? 12 : 4);
    else if(opt.mono || (M.nr_voices%4))
	return 7; /* Default to centered */
    else
	return bal[v%4];
}
