/*
 *  load_s3m.c - Loads Scream Tracker III 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, unsigned long *, unsigned short *,
			  unsigned char *);
static int read_patterndata(int, unsigned short *, unsigned char *);
static void fix_effect(unsigned char *, unsigned char *);

/* Defines and variables used while loading module */

#define S3M_HEADER_SIZE     0x60
#define S3M_SAMPLEINFO_SIZE 0x50

#define S3M_NAME            0x00
#define S3M_TYPE            0x1d
#define S3M_SONGLENGTH      0x20
#define S3M_NOS             0x22
#define S3M_PATTERNS        0x24
#define S3M_FLAGS           0x26
#define S3M_TRACKERVERSION  0x28
#define S3M_FORMATVERSION   0x2a
#define S3M_MAGIC           0x2c
#define S3M_GLOBALVOLUME    0x30
#define S3M_SPEED           0x31
#define S3M_TEMPO           0x32
#define S3M_MASTERVOLUME    0x33
#define S3M_CHANNELS        0x40

/* S3M_S_DATAPTR should be 0x0d and 24 bits but as the docs are a bit unclear
 * we use 0x0e and 16 bits. This will work for filesizes <= 1M.
 */

#define S3M_S_TYPE          0x00
#define S3M_S_DOSNAME       0x01
#define S3M_S_DATAPTR       0x0e
#define S3M_S_LEN           0x10
#define S3M_S_LOOPSTART     0x14
#define S3M_S_LOOPEND       0x18
#define S3M_S_VOLUME        0x1c
#define S3M_S_PACKED        0x1e
#define S3M_S_FLAGS         0x1f
#define S3M_S_C4SPD         0x20
#define S3M_S_NAME          0x30
#define S3M_S_MAGIC         0x4c

#define S3M_VOLOPTIMIZATION    8
#define S3M_FLAG_AMIGALIMITS  16

#define S3M_S_FLAG_LOOPED      1
#define S3M_S_FLAG_STEREO      2
#define S3M_S_FLAG_16BIT       4

static int version, expected_length;

int load_s3m(int fd)
{
    unsigned long sampledata_ptrs[256];
    unsigned short pattern_ptrs[256];
    unsigned char channels[32];
    char tmpbuf[80];

    int i, fatalerr;

    print_status("Loading S3M");
    M.nr_tracks=0;

    if(!process_header(fd, sampledata_ptrs, pattern_ptrs, channels))
	return 0;

    if(!read_patterndata(fd, pattern_ptrs, channels)) {
	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(lseek(fd, sampledata_ptrs[i-1]*16, SEEK_SET) == -1) {
		info("Seek failed (%d)", sampledata_ptrs[i-1]*16);
		fatalerr=1;
		break;
	    } else if(!read_and_upload_sample(fd, i)) {
		fatalerr=1;
		break;
	    }
	}
	if(quit == QUIT_SIGNAL) /* Bail out if we got QUIT_SIGNAL */
	    return 0;
    }
    
    if(fatalerr) {
	sprintf(tmpbuf, "Unable to load sample %d", i);
	print_status(tmpbuf);
	info("\nUnable to load sample %d.\n", i);
	sleep(1);
	return 0;
    }

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


static int process_header(int fd, unsigned long *sampledata_ptrs,
			  unsigned short *pattern_ptrs, unsigned char *channels)
{
    unsigned char buf[S3M_HEADER_SIZE];
    int tmp, i, silent_sample;
    struct sample_info *s;
    short s3m_flags;
    unsigned short sample_ptrs[256];

    if(read(fd, buf, S3M_HEADER_SIZE) != S3M_HEADER_SIZE) {
	info("Unable to read header from file.\n");
	return 0;
    }
    expected_length=S3M_HEADER_SIZE;

    if(strncmp("SCRM", &buf[S3M_MAGIC], 4)) {
	info("Not a Scream Tracker III module.\n");
	return 0;
    }

    if(buf[S3M_TYPE] != 16)
	info("Type-field in header does not equal ST3. Trying anyways.\n");
    
    tmp=*(short*)&buf[S3M_TRACKERVERSION];
    version=*(unsigned short *)&buf[S3M_FORMATVERSION];
    info("Tracker version %d.%0x  Format version %d\n",
	 (tmp>>8)&0x0f, tmp&0xff, version);
    
    strncpy(M.name, &buf[S3M_NAME], 0x1b);
    M.name[0x1b]=0;
    fix_string(M.name);

    /* Global / master volume currently ignored */
    opt.speed=buf[S3M_SPEED];
    opt.tempo=buf[S3M_TEMPO];
    
    M.songlength=*(short *)&buf[S3M_SONGLENGTH];
    M.nr_samples=*(short *)&buf[S3M_NOS];
    M.nr_patterns=*(short *)&buf[S3M_PATTERNS];
    
    if(!M.nr_samples || M.nr_samples > 255) {
	info("Invalid number of samples in module.\n");
	return 0;
    }
    
    if(M.nr_patterns > 255) {
	info("Invalid number of patterns.\n");
	return 0;
    }
    
    s3m_flags=*(short *)&buf[S3M_FLAGS];
    if(s3m_flags&S3M_FLAG_AMIGALIMITS) {
	opt.low_note=BASE_NOTE+3*12;
	opt.high_note=BASE_NOTE+6*12-1;
	info("Amiga limits.\n");
    }
    else {
	opt.low_note=BASE_NOTE+0*12;
	opt.high_note=BASE_NOTE+8*12-1;
    }
    if(s3m_flags&S3M_VOLOPTIMIZATION)
	info("Volume optimization flag set in module (not supported yet).\n");

    memcpy(channels, &buf[S3M_CHANNELS], 32);
    for(i=31; i >= 0 && channels[i] >= 16; --i) /* Channel <= 15 for samples */
	;
    M.nr_voices=i+1;
    if(M.nr_voices <= 0 || M.nr_voices > 31) {
	info("Illegal number of voices (%d).\n", M.nr_voices);
	return 0;
    }
    
    if(read(fd, M.patterntable, M.songlength) != M.songlength) {
	info("Unable to read patterntable from file.\n");
	return 0;
    }
    
    if(read(fd, sample_ptrs, M.nr_samples*2) != M.nr_samples*2) {
	info("Unable to read sample-pointers from file.\n");
	return 0;
    }
    
    if(read(fd, pattern_ptrs, M.nr_patterns*2) != M.nr_patterns*2) {
	info("Unable to read pattern-pointers from file.\n");
	return 0;
    }
    
    expected_length+=M.songlength+M.nr_samples*2+M.nr_patterns*2;
    
    M.sample=(struct sample_info *)malloc((1+M.nr_samples)*
					  sizeof(struct sample_info));
    
    print_sample_info_header();
    
    for(i=1; i <= M.nr_samples; ++i) {
	if(lseek(fd, sample_ptrs[i-1]*16, SEEK_SET) == -1 ||
	   read(fd, buf, S3M_SAMPLEINFO_SIZE) != S3M_SAMPLEINFO_SIZE) {
	    info("Unable to read sampleheader (%d).\n", i);
	    return 0;
	}
	expected_length+=S3M_SAMPLEINFO_SIZE;
	s=&M.sample[i];

	/* Valid isn't set to TRUE until it's data has been read */
	bzero((void *)s, sizeof(struct sample_info));
	
	if(!strncmp("SCRI", &buf[S3M_S_MAGIC], 4)) {
	    info("Sample %d is an Adlib instrument - skipped.\n", i);
	    strcpy(s->name, "Adlib-instrument");
	    continue;
	}
	
	silent_sample=0;
	if(strncmp("SCRS", &buf[S3M_S_MAGIC], 4)) {
	    if(*(int *)&buf[S3M_S_MAGIC]) {
		info("Bad sampleheader (%d) magic = %d %d %d %d.\n", i,
		     buf[S3M_S_MAGIC], buf[S3M_S_MAGIC+1], buf[S3M_S_MAGIC+2],
		     buf[S3M_S_MAGIC+3]);
		strcpy(s->name, "Empty sample");
		continue;
	    }
	    silent_sample=1;
	}
	
	strncpy(s->name, &buf[S3M_S_NAME], 28);
	s->name[28]=0;
        fix_string(s->name);
	strncpy(s->dosname, &buf[S3M_S_DOSNAME], 12);
	s->dosname[12]=0;
        fix_string(s->dosname);
	
	s->length=*(unsigned long *)&buf[S3M_S_LEN];
	s->repeat_start=*(unsigned long *)&buf[S3M_S_LOOPSTART];
	s->repeat_end=*(unsigned long *)&buf[S3M_S_LOOPEND];
	s->volume=buf[S3M_S_VOLUME];
	
	expected_length+=s->length;
	
	if(buf[S3M_S_PACKED])
	    info("Packed sample. Trying to load it as unpacked.\n");
	
	if((buf[S3M_S_FLAGS]&S3M_S_FLAG_LOOPED) &&
	   s->repeat_end > s->repeat_start+4)
	    s->looped=LOOP_FORWARD;
	else
	    s->looped=0;
	
	if(buf[S3M_S_FLAGS]&S3M_S_FLAG_16BIT)
	    s->bits_16=1;
	else
	    s->bits_16=0;
	
	if(buf[S3M_S_FLAGS]&S3M_S_FLAG_STEREO)
	    info("Stereo-sample. Trying to load it as a mono-sample.\n");
	
	s->repeat_start=MIN(MAX(0, s->repeat_start), s->length-2);
	s->repeat_end=MIN(MAX(0, s->repeat_end), s->length-
			  (s->bits_16 ? 2 : 1));

	if(version == 2)
	    s->unsigned_data=1;
	
	s->c2freq=*(long*)&buf[S3M_S_C4SPD];
	
	sampledata_ptrs[i-1]=(*(unsigned short*)&buf[S3M_S_DATAPTR]);
	
	if(silent_sample)
	    s->length=0;
	print_sample_info(i);
    }
    
    for(i=0; i < M.nr_voices; ++i) {
	if(channels[i] <= 7)
	    M.panning[i]=1;  /* Left */
	else if(channels[i] <= 15)
	    M.panning[i]=14; /* Right */
	else
	    M.panning[i]=7;  /* Center (adlib/unused) */
    }
    
    M.restartpos=0;
    M.volrange=64;
    opt.speed0stop=0; /* Ignored in S3M's */
    
    info("\nSamples: %d  Patterns: %d  Songlength: %d\n",
	 M.nr_samples, M.nr_patterns, M.songlength);
    
    return 1;
}


static int read_patterndata(int fd, unsigned short *pattern_ptrs,
			    unsigned char *channels)
{
    unsigned char buf[3];
    struct event *e;
    int p, l, v;
    struct event events[64*32];
    
    for(v=0; v < NR_EFX; ++v)
	effect_used[v]=0;
    
    /* Allocate for worst case (no identical tracks) */
    M.tracks=(struct event **)malloc(M.nr_patterns*M.nr_voices*
				     sizeof(struct event *));
    
    for(v=0; v < M.nr_voices; ++v)
	M.track_idx[v]=(int *)malloc(M.nr_patterns*sizeof(int));
    
    /* Add the empty track */
    bzero((void *)events, sizeof(struct event)*64);
    get_track_idx(events);
    
    for(p=0; p < M.nr_patterns; ++p) {
	bzero(events, 64*32*sizeof(struct event));
	if(lseek(fd, pattern_ptrs[p]*16, SEEK_SET) == -1)
	    return 0;
	if(!get_bytes(fd, 0, 0))
	    return 0;
	if(!get_bytes(fd, buf, 2)) /* Don't trust length-value */
	    return 0;
	expected_length+=2;
	for(l=0; l < 64; ++l) {
	    while(1) {
		if(!get_bytes(fd, buf, 1))
		    return 0;
		expected_length++;

		if(!buf[0])
		    break;
		
		e=&events[(buf[0]&0x1f)*64+l];
		if(buf[0]&32) {
		    if(!get_bytes(fd, &buf[1], 2))
			return 0;
		    expected_length+=2;
		    switch(buf[1]) {
		      case 255:
			e->note=0;
			break;
		      case 254:
			e->note=NOTE_OFF;
			break;
		      default:
			e->note=((buf[1]>>4)&0x0f)*12+(buf[1]&0x0f)+BASE_NOTE;
		    }
		    e->sample=buf[2];
		}
		if(buf[0]&64) {
		    if(!get_bytes(fd, &buf[1], 1))
			return 0;
		    expected_length++;
		    e->effect2=EFX_VOLUME;
		    e->arg2=buf[1];
		}
		if(buf[0]&128) {
		    if(!get_bytes(fd, &buf[1], 2))
			return 0;
		    expected_length+=2;
		    e->effect=buf[1];
		    e->arg=buf[2];
		}
		fix_effect(&e->effect, &e->arg);
		if(e->effect || (!e->effect && e->arg))
		    effect_used[e->effect]=1;
	    }
	}
	for(v=0; v < M.nr_voices; ++v)
	    M.track_idx[v][p]=(channels[v] == 255 ? 0 :
			    get_track_idx(&events[v*64]));
    }
    print_used_effects();
    
    return 1;
}

/* Converts the Scream Tracker effectnumbers to those used by the player.
 */

static void fix_effect(unsigned char *effect, unsigned char *arg)
{
    unsigned char e=*effect+'A'-1, a=*arg;

    if(!*effect) {
	*arg=0;
	return;
    }

    if(e < 'A' || e > 'Z') {
#if 0
	debug("Invalid EFX (%d '%c'.\n", e, e);
#endif
	*effect=*arg=0;
	return;
    }
    
    switch(e) {
      case 'A': e=EFX_SPEED; break;
      case 'B': e=EFX_JUMP; break;
      case 'C': e=EFX_BREAK; break;
      case 'D':
	if(!a) {
	    e=EFX_VOLSLIDECONTINUE;
	}
	else if((a&0x0f) == 0x0f && a != 0x0f) {
	    e=EFX_FINEVOLSLIDEUP;
	    a=(a>>4)&0x0f;
	}
	else if((a&0xf0) == 0xf0 && a != 0xf0) {
	    e=EFX_FINEVOLSLIDEDOWN;
	    a&=0x0f;
	}
	else {
	    e=EFX_VOLSLIDE;
	}
	break;
      case 'E':
	if(!a) {
	    e=EFX_PORTADOWNCONTINUE;
	}
	else if(a >= 0xf0) {
	    e=EFX_FINEPORTADOWN;
	    a&=0x0f;
	}
	else if(a >= 0xe0) {
	    e=EFX_EXTRAFINEPORTADOWN;
	    a&=0x0f;
	}
	else {
	    e=EFX_PORTADOWN;
	}
	break;
      case 'F':
	if(!a) {
	    e=EFX_PORTAUPCONTINUE;
	}
	else if(a >= 0xf0) {
	    e=EFX_FINEPORTAUP;
	    a&=0x0f;
	}
	else if(a >= 0xe0) {
	    e=EFX_EXTRAFINEPORTAUP;
	    a&=0x0f;
	}
	else {
	    e=EFX_PORTAUP;
	}
	break;
      case 'G': e=EFX_PORTANOTE; break;
      case 'H': e=EFX_VIBRATO; break;
      case 'I': e=EFX_TREMOR; break;
      case 'J': e=EFX_ARPEGGIO; break;
      case 'K': e=EFX_VIBRATOVOLSLIDE; break; /* "Retrig" for value 00 ?!?! */
      case 'L': e=EFX_PORTANOTEVOLSLIDE; break;
	
      case 'O': e=EFX_SAMPLEOFFSET_S3M; break;
      case 'Q': e=EFX_RETRIGGERVOLSLIDE; break;
      case 'R': e=EFX_TREMOLO; break;
      case 'S': e=EFX_EXTENDED; break;
      case 'T': e=EFX_TEMPO; break;
      case 'V': e=EFX_GLOBALVOLUME; break;
	
      default:
	e=a=0;
    }
    
    if(e == EFX_EXTENDED) {
	e=(a>>4)&0x0f;
	a&=0x0f;
	switch(e) {
	  case 2:
	    e=EFX_FINETUNE;
	    a=(a-8)&0x0f;
	    break;
	  case 3: e=EFX_VIBWAVEFORM; break;
	  case 4: e=EFX_TREMWAVEFORM; break;
	  case 0x0b: e=EFX_LOOP; break;
	  case 0x0c: e=EFX_NOTECUT; break;
	  case 0x0d: e=EFX_NOTEDELAY; break;
	  case 0x0e: e=EFX_PATTERNDELAY; break;
	    
	    /* Effects not implemented in ST3 */
	  case 0:    /* EFX_FILTER    */
	  case 1:    /* EFX_GLISSANDO */
	  case 0x0a: /* Previous Stereo-control, but remved in ST3 */
	  case 0x0f: /* EFX_INVERTEDLOOP/EFX_FUNK */
	  default:
	    e=a=0;
	}
    }
    
    *arg=a;
    *effect=e;
}
