/*
 *  load_ult.c - Loads Ultratracker 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 char effect_used[NR_EFX];

/* Local functions */
static int process_header(int);
static int read_patterndata(int);
static void fix_effect(unsigned char *, unsigned char *);

/* Defines and variables used while loading module */

#define ULT_OFFSET_MAGIC         0
#define ULT_OFFSET_NAME         15
#define ULT_OFFSET_SONGTEXTLEN  47
#define ULT_OFFSET_SONGTEXT     48

#define ULT_SAMPLEINFO_SIZE     64
#define ULT_SAMPLEINFO_SIZE_NEW 66

#define ULT_FLAG_BITS_16         4
#define ULT_FLAG_LOOP            8
#define ULT_FLAG_LOOPBACKWARD   16

#define ULT_REPEATBLOCK_NOTE 0xfc

static int expected_length, version;

int load_ult(int fd)
{
    char tmpbuf[80];

    int i, diff, fatalerr;

    print_status("Loading ULT");
    M.nr_tracks=0;
    if(!process_header(fd))
	return 0;

    if(!read_patterndata(fd)) {
	info("Unable to read patterndata.\n");
	return 0;
    }
    
    ioctl(seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev);
    
    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",0);
	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 && opt.verbose) {
	warning("\nFound %d garbagebytes after samples. Ignoring.\n", diff);
    }
    
    print_songname(M.name);
    print_samples(1);
    print_songinfo(M.nr_voices, M.nr_samples, "ULT", expected_length,
		   M.songlength, M.nr_patterns);
    return 1;
}


static int process_header(int fd)
{
    unsigned char buf[32*255+1]; /* Maximum size of SONGTEXT */
    char *p;
    int tmp, i, current_si_size;
    struct sample_info *s;

    if(read(fd, buf, ULT_OFFSET_SONGTEXT) != ULT_OFFSET_SONGTEXT) {
	info("Unable to read header from file.\n");
	return 0;
    }
    
    expected_length=ULT_OFFSET_SONGTEXT; /* Start counting expected filesize */
    
    if(strncmp("MAS_UTrack_V", &buf[ULT_OFFSET_MAGIC], 12)) {
	info("Not an Ultratracker module.\n");
	return 0;
    }
    
    /* Copy songname and strip trailing spaces */
    strncpy(M.name, &buf[ULT_OFFSET_NAME], 32);
    M.name[32]=0;
    fix_string(M.name);
    
    buf[ULT_OFFSET_MAGIC+15]=0;
    version=atoi(&buf[ULT_OFFSET_MAGIC+12]);
    if(version < 1 || version >4) {
	print_status("Format not supported");
	info("Format version '%s' not supported.\n", &buf[ULT_OFFSET_MAGIC]);
	return 0;
    }

    info("Format version '%s'.\n", &buf[ULT_OFFSET_MAGIC]);
    
    i=32*buf[ULT_OFFSET_SONGTEXTLEN];
    if(version >= 2 && i) {
	M.songtext=(void *)malloc(i+1);
	if(read(fd, M.songtext, i) != i) {
	    info("Unable to load songtext.\n");
	    return 0;
	}
	*(M.songtext+i)=0;
	fix_string(M.songtext);
	expected_length+=i;
    }
    else
	M.songtext=0;
    
    if(M.songtext && opt.verbose) {
	printf("\n---------- Message: ----------\n");
	i=strlen(M.songtext);
	tmp=0;
	while(tmp < i) {
	    printf("%.32s\n", &M.songtext[tmp]);
	    tmp+=32;
	}
	printf("\n------------------------------\n");
    }
    
    if(read(fd, buf, 1) != 1) {
	info("Unable to read header from file.\n");
	return 0;
    }
    M.nr_samples=(unsigned char)buf[0];
    expected_length++;
    
    if(!M.nr_samples || M.nr_samples > 127) {
	info("Invalid number of samples in module.\n");
	return 0;
    }

    if(version >= 2) { /* Linear volumes */
    }
    else {             /* LOG volumes */
	info("Logarithmic volumes not supported (yet). Might sound"
	     " a bit strange.\n");
    }
	
    M.sample=(struct sample_info *)malloc((1+M.nr_samples)*
					  sizeof(struct sample_info));
    
    current_si_size=(version < 4 ? ULT_SAMPLEINFO_SIZE : 
		     ULT_SAMPLEINFO_SIZE_NEW);

    expected_length+=M.nr_samples*current_si_size;
    print_sample_info_header();

    for(i=1; i <= M.nr_samples; ++i) {
	if(read(fd, buf, current_si_size) != current_si_size) {
	    info("Unable to read sampleheader (%d).\n", i);
	    return 0;
	}
	p=buf;

	s=&M.sample[i];
        s->valid=0;             /* Valid isn't set to TRUE until it's
				 * sampledata has been read.
				 */
	strncpy(s->name, p, 32);
	s->name[32]=0;
        p+=32;
        fix_string(s->name);
	
	strncpy(s->dosname, p, 12);
	s->dosname[12]=0;
        p+=12;
        fix_string(s->dosname);
	
	s->repeat_start=*(unsigned long *)p; p+=4;
	s->repeat_end=*(unsigned long *)p;   p+=4;
	tmp=*(unsigned long *)p;             p+=4;
	s->length=*(unsigned long *)p-tmp;   p+=4;
	
	s->volume=*(unsigned char *)p++;
	tmp=*(unsigned char *)p++; /* Flags */

	if(tmp&ULT_FLAG_BITS_16) {
	    s->length*=2;
	    s->bits_16=1;
	}
	else
	    s->bits_16=0;

	s->unsigned_data=0;
	
	if(tmp&ULT_FLAG_LOOP) {
	    if(tmp&ULT_FLAG_LOOPBACKWARD)
		s->looped=LOOP_BIDIR;
	    else
		s->looped=LOOP_FORWARD;
	}
	else
	    s->looped=0;
	
	/* The docs say finetune comes before c2freq, but that isn't
	 * the case with the included "cybocult.ult" so...
	 */
	if(version >= 4) {
	    s->c2freq=*(short *)p;
	    p+=2;
	}
	else
	    s->c2freq=-1; /* Use PAL/NTSC */

	s->finetune=(*(short *)p)/4096; /* Approximate FT */
	p+=2;

	s->repeat_start=
	    MIN(MAX(0, s->repeat_start), s->length-2);
	
	/* Is this one including/excluding the last sample? */
	s->repeat_end=
	    MIN(MAX(0, s->repeat_end), s->length-2);

	expected_length+=s->length;
	
	print_sample_info(i);
    }

    if(read(fd, buf, 258) != 258) {
	info("Unable to load patterntable.\n");
	return 0;
    }
    expected_length+=258;

    memcpy(M.patterntable, buf, 256);
    M.nr_voices=buf[256]+1;
    M.nr_patterns=buf[257]+1;

    for(i=0; i < 256; ++i)
	if(M.patterntable[i] >= M.nr_patterns)
	    break;
    M.songlength=i;

    if(M.nr_voices > 32 || M.nr_voices <= 0) {
	info("Illegal number of voices (%d).\n", M.nr_voices);
	return 0;
    }
    
    if(version >= 3) {
	if(read(fd, M.panning, M.nr_voices) != M.nr_voices) {
	    info("Unable to load PAN-position table.\n");
	    return 0;
	}
	for(tmp=0; tmp < M.nr_voices; ++tmp)
	    M.panning[tmp]&=0x0f;
	expected_length+=M.nr_voices;
    }
    else {
	for(tmp=0; tmp < M.nr_voices; ++tmp)
	    M.panning[tmp]=get_voice_balance(tmp);
    }
    
    M.restartpos=0;
    M.volrange=255;
    
    info("\nVoices: %d  Patterns: %d  Songlength: %d\n",
	 M.nr_voices, M.nr_patterns, M.songlength);
    
    return 1;
}


static int read_patterndata(int fd)
{
    unsigned char buf[7];
    unsigned short effect, arg;
    struct event *e;
    int p, l, v, repeat;
    struct event events[64];
    
    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 *));

    if(!get_bytes(fd, 0, 0))
	return 0;
    
    for(v=0; v < M.nr_voices; ++v) {
	for(p=0; p < M.nr_patterns; ++p) {
	    l=0;
	    while(l < 64) {
		if(!get_bytes(fd, buf, 5))
		    return 0;
		expected_length+=5;
		if(buf[0] == ULT_REPEATBLOCK_NOTE) {
		    repeat=buf[1];
		    buf[0]=buf[2];
		    buf[1]=buf[3];
		    buf[2]=buf[4];
		    if(!get_bytes(fd, &buf[3], 2))
			return 0;
		    expected_length+=2;
		}
		else
		    repeat=1;

		if(!repeat)
		    info("Repeat block with length 0 detected in "
			 "pattern %d.\n", p);
		
		while(repeat > 0 && l < 64) {
		    e=&events[l];
		    e->note=
			(buf[0] ? buf[0]+BASE_NOTE + 2*12: 0); /* X-2 => X-4 */
		    e->sample=buf[1];
		    arg=*((unsigned short *)&buf[3]);

		    effect=(buf[2]>>4)&0x0f;    /* First effect */

		    if(effect != 0x0e) {
			e->arg=(arg>>8)&0xff;
		    }
		    else {
			effect=0x10|((arg>>12)&0x0f);
			e->arg=(arg>>8)&0x0f;
		    }
		    e->effect=effect;
		    fix_effect(&e->effect, &e->arg);
		    
		    if(e->effect || e->arg)
			effect_used[e->effect]=1;
		    
		    effect=buf[2]&0x0f;        /* Second effect */

		    if(effect != 0x0e) {
			e->arg2=arg&0xff;
		    }
		    else {
			effect=0x10|((arg>>4)&0x0f);
			e->arg2=arg&0x0f;
		    }
		    e->effect2=effect;
		    fix_effect(&e->effect2, &e->arg2);
		    
		    if(e->effect2 || e->arg2)
			effect_used[e->effect2]=1;
		    
		    --repeat;
		    ++l;
		}
		if(repeat)
		    info("Repeat block reached outside pattern %d.\n", p);
	    }
	    M.track_idx[v][p]=get_track_idx(events);
	}
    }
    get_bytes(fd, 0, -1);

    /* Octaves 2-9 (0-7) allowed */
    opt.low_note=BASE_NOTE+2*12;
    opt.high_note=BASE_NOTE+10*12-1;

    print_used_effects();

    return 1;
}

/* Converts the Ultratracker effects to those used by the playrouting (a
 * set of effects containing all effects in the supported module-formats).
 */

static void fix_effect(unsigned char *effect, unsigned char *arg)
{
    switch(*effect) {
      case EFX_PORTAUP:           /* Effects compatible with PT */
      case EFX_PORTADOWN:
      case EFX_PORTANOTE:
      case EFX_VIBRATO:
      case EFX_VOLSLIDE:
      case EFX_VOLUME:
      case EFX_BREAK:
      case EFX_FINEPORTAUP:
      case EFX_FINEPORTADOWN:
      case EFX_RETRIGGER:
      case EFX_FINEVOLSLIDEUP:
      case EFX_FINEVOLSLIDEDOWN:
      case EFX_NOTECUT:
      case EFX_NOTEDELAY:
      case EFX_PTSPEED:
	break;

      /* Effects introduced in 1.5 */
      case EFX_ARPEGGIO:
	if(version < 3)
	    *effect=*arg=0;
	break;

      /* Effects introduced in 1.6 */
      case EFX_TREMOLO:
      case EFX_SAMPLEOFFSET: /* The '99' SO-effect is handled in effects.c */
	if(version < 4)
	    *effect=*arg=0;
	else {
	    if(*effect == EFX_SAMPLEOFFSET)
		*effect=EFX_SAMPLEOFFSET_ULT;
	}
	break;
      case EFX_UNUSED2:           /* ULT: Patterndelay */
	if(version < 4)
	    *effect=*arg=0;
	else
	    *effect=EFX_PATTERNDELAY;
	break;

	
	/* Special ULT-effects */
      case EFX_JUMP:              /* ULT: Balance */
	*effect=EFX_BALANCE;
	break;
      case EFX_FILTER:            /* ULT: Set vibrato "strength" (depth?)
	                           * Not sure if this is correct...
				   */
	*effect=EFX_VIBDEPTH;
	break;

#if 0
      /* These effects are not supported by Ultratracker */	
      case EFX_PORTANOTEVOLSLIDE: /* ULT: Special - not supported */
      case EFX_VIBRATOVOLSLIDE:   /* Not supported */
      case EFX_GLISSANDO:
      case EFX_VIBWAVEFORM:
      case EFX_LOOP:
      case EFX_TREMWAVEFORM:
#endif
      default:
	*effect=*arg=0;
    }
}
