
/*
*	gmod.c	- Module player for GUS and Linux.
*		(C) Hannu Savolainen, 1993
*
*	NOTE!	This program doesn't try to be a complete module player.
*		It's just a too I used while developing the driver. In
*		addition it can be used as an example on programming
*		the VoxWare Sound Driver with GUS.
*/

/*
* Many modifications have been done by Andrew J. Robinson.
* Refer to the ChangeLog for details.
*/


#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/soundcard.h>
#include <sys/ultrasound.h>

#include "commands.h"
#include "defines.h"
#include "structs.h"
#include "globals.h"
#include "protos.h"

int
load_669_module (FILE * mod_fd, char *name, struct song_info *song_char,
		 struct options_info options, char *buffer)
{
  struct sample_header
    {
      char name[13];
      unsigned long length;	/* In bytes */
      unsigned long loop_start;
      unsigned long loop_end;
    };

  int i, total_mem;
  int gus_onboard_mem;
  int sample_ptr;

  int position;
  int voice;

  unsigned char *tune_ptr, *len_ptr, *tempo_ptr;	/* array 0-127 */

  unsigned char header[0x1f1];	/* changed from 1084 */
  unsigned char *sample_info;
  char msg[110];

  int nr_samples;		/* 16 or 32 samples */
  int slen, npat;
  int format_version = 0;

  gus_onboard_mem = gus_mem_free (gus_dev);

  fprintf (stderr, "Loading .669 module: %s\n", name);

  memcpy (header, buffer, HDR_SIZE);

  if (fread (header + HDR_SIZE, 1, sizeof (header) - HDR_SIZE, mod_fd) !=
      sizeof (header) - HDR_SIZE)
    {
      fprintf (stderr, "%s: Short file (header)\n", name);
      return 0;
    }

  if (*(unsigned short *) &header[0] != 0x6669)
    format_version = 1;

  strncpy (msg, &header[2], 108);

  for (i = 0; i < strlen (msg); i++)
    if ((msg[i] >= ' ' && msg[i] <= 'z') || msg[i] == '\n')
      printf ("%c", msg[i]);
  printf ("\n");

  npat = header[0x6f];

  tune_ptr = &header[0x71];

  for (slen = 0; slen < 128 && tune_ptr[slen] != 0xff; slen++);
  /* slen--; */

  for (i = 0; i < slen; i++)
    tune[i] = tune_ptr[i];

  len_ptr = &header[0x171];
  for (i = 0; i < 0x80; i++)
    pattern_len[i] = len_ptr[i] + 1;	/* AJR: changed from -1 */

  tempo_ptr = &header[0xf1];
  for (i = 0; i < slen; i++)
    pattern_tempo[i] = tempo_ptr[i];

  nr_samples = header[0x6e];

  fprintf (stderr, "Song length %d, %d patterns, %d samples.\n", slen, npat, nr_samples);

  if ((sample_info = (char *) malloc (nr_samples * 0x19)) == NULL)
    {
      fprintf (stderr, "Could not allocate memory for sample information.\n");
      return 0;
    }

  if (fread (sample_info, 1, nr_samples * 0x19, mod_fd) != (nr_samples * 0x19))
    {
      fprintf (stderr, "Could not read sample information.\n");
      free (sample_info);
      return 0;
    }

  song_char->nr_tracks = npat * 8;
  song_char->songlength = slen;
  song_char->nr_channels = 8;
  song_char->lowest_note = 36;
  song_char->highest_note = 99;
  song_char->play_speed = 4;
  song_char->tempo = 75;
  song_char->vol_type = VOL_LINEAR;
  song_char->slide_type = SLIDE_FREQ_LIN;

  for (position = 0; position < npat; position++)
    {
      unsigned char patterns[0x600];
      int pat, channel, x;

      int pp = 0x1f1 + (nr_samples * 0x19) + (position * 0x600);

      for (voice = 0; voice < song_char->nr_channels; voice++)
	if ((pattern_table[position * song_char->nr_channels + voice] = (pattern *) malloc (sizeof (struct note_info) * 64)) == NULL)
	  {
	    fprintf (stderr, "Can't allocate memory for a pattern\n");
	    free (sample_info);
	    return 0;
	  }

      if ((x = fread (patterns, 1, 0x600, mod_fd)) != 0x600)
	{
	  fprintf (stderr, "Short file (pattern at %d) %d!=1024\n", pp, x);
	  free (sample_info);
	  return 0;
	}

      for (pat = 0; pat < 64; pat++)
	{

	  for (channel = 0; channel < 8; channel++)
	    {
	      unsigned char *p;

	      unsigned vol, period, sample, effect, params;
	      unsigned effect1 = 0, effect2 = 0, parm1 = 0, parm2 = 0;

	      p = &patterns[pat * 24 + channel * 3];

	      if (p[0] >= 0xfe)
		{
		  sample = 0;
		  period = 0;
		}
	      else
		{
		  period = (p[0] >> 2) + 36;	/* AJR:  changed from 48 */
		  sample = (((p[0] << 4) & 0x30) | ((p[1] >> 4) & 0x0f)) + 1;
		}

	      effect = (p[2] >> 4);
	      params = p[2] & 0x0f;
	      vol = (p[1] & 0x0f) * 17;

	      if (period)
		{
		  effect1 = CMD_VOLUME;
		  parm1 = vol;
		}

	      if (p[0] == 0xfe)
		{
		  effect1 = CMD_VOLUME;
		  parm1 = vol;
		}

	      if (p[2] == 0xff)
		{
		  effect2 = CMD_NOP;
		}
	      else
		switch (effect)
		  {
		  case 0:	/* a - Portamento up */
		    parm2 = params * SLIDE_RATE_669;
		    if (parm2 == 0)
		      effect2 = 0;
		    else
		      effect2 = CMD_SLIDEUP;
		    break;

		  case 1:	/* b - Portamento down */
		    parm2 = params * SLIDE_RATE_669;
		    if (parm2 == 0)
		      effect2 = 0;
		    else
		      effect2 = CMD_SLIDEDOWN;
		    break;

		  case 2:	/* c - Port to note */
		    parm2 = params * SLIDE_RATE_669;
		    if (parm2 == 0)
		      effect2 = 0;
		    else
		      effect2 = CMD_SLIDETO;
		    break;

		  case 3:	/* d - Frequency adjust */
		    parm2 = params * SLIDE_RATE_669;
		    if (parm2 == 0)
		      effect2 = 0;
		    else
		      effect2 = CMD_FINEPORTUP;
		    break;

		  case 4:	/* e - Frequency vibrato */
		    parm2 = 0x70 || params;
		    if (parm2 == 0x70)
		      effect2 = 0;
		    else
		      effect2 = CMD_VIBRATO;
		    break;

		  case 5:	/* f - Set tempo */
		    if ((params == 0) && (format_version == 1))
		      parm2 = 1;
		    else
		      parm2 = params;
		    if (parm2 == 0)
		      effect2 = 0;
		    else
		      effect2 = CMD_SET_TICKS;
		    break;

		  default:
		    effect2 = 0;
		    parm2 = 0;
		  }

	      if (period)
		if (period < song_char->lowest_note)
		  period = song_char->lowest_note;
		else if (period > song_char->highest_note)
		  period = song_char->highest_note;

	      voice = position * song_char->nr_channels + channel;
	      voice_table[position][channel] = voice;

	      (*pattern_table[voice])[pat].note = period;
	      (*pattern_table[voice])[pat].sample = sample;
	      (*pattern_table[voice])[pat].command[0] = effect1;
	      (*pattern_table[voice])[pat].parm1[0] = parm1;
	      (*pattern_table[voice])[pat].parm2[0] = 0;
	      (*pattern_table[voice])[pat].command[1] = effect2;
	      (*pattern_table[voice])[pat].parm1[1] = parm2;
	      (*pattern_table[voice])[pat].parm2[1] = 0;
	    }

	}

      if (options.compress)
	for (channel = 0; channel < song_char->nr_channels; channel++)
	  voice_table[position][channel] =
	    compress_voice (voice_table[position][channel],
			    voice_table[position][channel],
			    64, 1);
    }

  sample_ptr = 0x1f1 + (nr_samples * 0x19) + (npat * 0x600);	/* Location where the
							 * first sample is
							 * stored */
  total_mem = 0;

  for (i = 0; i < MAX_SAMPLES; i++)
    sample_ok[i] = 0;

  for (i = 0; i < nr_samples; i++)
    {
      unsigned long len, loop_start, loop_end;
      unsigned short loop_flags = 0;

      struct sample_header *sample;
      char sname[14];

      struct patch_info *patch;

      sample = (struct sample_header *) &sample_info[i * 0x19];

      len = *(unsigned long *) &sample->name[13];
      loop_start = *(unsigned long *) &sample->name[17];
      loop_end = *(unsigned long *) &sample->name[21];
      if (loop_end > len)
	loop_end = 1;
      else if (loop_end == len)
	loop_end--;

      if (loop_end < loop_start)
	{
	  loop_start = 0;
	  loop_end = 0;
	}

      strncpy (sname, sample->name, 13);
      sname[13] = '\0';
      remove_noprint (sname);

      if ((len > 0) || options.show_empty_samples)
	fprintf (stderr, "Sample %02d: %05lu, %05lu, %05lu   %s\n",
		 i,
		 len,
		 loop_start,
		 loop_end,
		 sname);

      if (len > 0)
	{
	  total_mem += len;

	  patch = (struct patch_info *) malloc (sizeof (*patch) + len);

	  if (loop_end == 0)
	    loop_end = 1;
	  if (loop_end >= len)
	    loop_end = 1;

	  if (loop_end > 1)
	    loop_flags = WAVE_LOOPING;

	  patch->key = GUS_PATCH;
	  patch->device_no = gus_dev;
	  patch->instr_no = i;
	  patch->mode = WAVE_UNSIGNED | loop_flags;
	  patch->len = len;
	  patch->loop_start = loop_start;
	  patch->loop_end = loop_end;
	  patch->base_freq = NTSC_RATE;
	  patch->base_note = C2FREQ;
	  patch->low_note = 0;
	  patch->high_note = 0x7fffffff;
	  patch->volume = 0;	/* 120; */
	  patch->panning = 16;

	  sample_ptr += len;

	  patch_load (mod_fd, patch, i);
	  sample_vol[i] = 0;

	  free (patch);
	}
    }


  printf ("=== %d bytes of %d total onboard used ===\n",
	  gus_onboard_mem - gus_mem_free (gus_dev), gus_onboard_mem);


  /* set panning */

  for (i = 0; i < song_char->nr_channels; i++)
    if ((i % 2) == 0)
      song_char->panning[i] = -127;
    else
      song_char->panning[i] = 127;

  free (sample_info);
  return 1;
}
