// -*-C++-*-
// This file is part of the gmod package
// Copyright (C) 1997 by Andrew J. Robinson

#include <errno.h>
#include <string.h>

#include "xm.h"
#include "Sequencer.h"

int catchup(FILE *, int, int *);
void removeNoprint(char *);

int
XM_sample::load(Sequencer &seq, FILE * modFd, int sampleNo, int cutFactor,
		void *pt, void *fp)
{
  u_char header[214];
  u_long headerSize, skipLength;
  u_short numSamples;
  int bytesRead = 0;
  int readCount;
  unsigned short *periodTable = (unsigned short *)pt;
  int *filePos = (int *)fp;
  
  ok_ = 0;
  sampleNum_ = sampleNo;
  
  readCount = fread(header, 1, 29, modFd);
  *filePos += readCount;

  if (readCount != 29)
    return bytesRead;

  strncpy(name_, (char *)(header + 4), 22);
  name_[22] = '\0';
  removeNoprint(name_);

  headerSize = INTEL_LONG(header);
  numSamples = INTEL_SHORT(header + 27);
      
  if (numSamples > 0)
    {
      readCount = fread(header, 1, 214, modFd);
      *filePos += readCount;

      if (readCount != 214)
	return bytesRead;

      if (!catchup(modFd, *filePos + (headerSize - 243), filePos))
	return bytesRead;

      headerSize = INTEL_LONG(header);

      volumeEnvelope.load(int(header[196]), int(header[198]), int(header[199]),
			  int(header[200]), int(header[204]),
			  (unsigned short)(INTEL_SHORT(header + 210)),
			  64, header + 100);

      panEnvelope.load(int(header[197]), int(header[201]), int(header[202]),
		       int(header[203]), int(header[205]), 0, 32, 
		       header + 148);

      vibratoDepth_ = header[208];
      vibratoRate_ = header[209];

      readCount = fread(header, 1, 40, modFd);
      *filePos += readCount;

      if (readCount != 40)
	return bytesRead;

      if (!catchup(modFd, *filePos + (headerSize - 40), filePos))
	return bytesRead;

      skipLength = 0;

      /* skip any remaining sample headers */

      while(numSamples > 1)
	{
	  readCount = fread(header + 40, 1, 40, modFd);
	  *filePos += readCount;

	  if (readCount != 40)
	    return bytesRead;

	  skipLength += INTEL_LONG(header + 40);

	  if (!catchup(modFd, *filePos + (headerSize - 40), filePos))
	    return bytesRead;
;
	  numSamples--;
	}
      
      length_ = INTEL_LONG(header);
      loopStart = INTEL_LONG(header + 4);
      loopEnd = loopStart + INTEL_LONG(header + 8);

      if ((BYTE(header + 14) & 0x03) == 1)
	mode_ = WAVE_LOOPING;
      else if ((BYTE(header + 14) & 0x03) == 2)
	mode_ = WAVE_LOOPING | WAVE_BIDIR_LOOP;
      else
	mode_ = 0;

      if ((BYTE(header + 14) & 0x10) > 0)
	mode_ |= WAVE_16_BITS;

      panning_ = header[15];
      // should check range of relative note!!

      if (((signed char)(header[16])) != 0)
	baseNote_ = 111978496 / periodTable[-((signed char)(header[16])) + 60 - NOTE_BASE];
      else
	baseNote_ = C2FREQ;

     baseFreq_ = NTSC_RATE;
	  
      if (length_ > 0)
	{
	  int rc;

	  rc = seq.patchLoad(modFd, sampleNo, *this, bytesRead,
			     cutFactor);

	  if (!rc)
	    ok_ = 1;
	  
	  *filePos += bytesRead;
	  skipLength += length_ - bytesRead;

	  if (rc == -ENOSPC)
	    bytesRead = -bytesRead;
	}

      if (BYTE(header + 12) > 0)
	volume_ = (BYTE(header + 12) * 4) - 1;
      else
	volume_ = 0;

      finetune_ = (signed char)(BYTE(header + 13)) * 100 / 128;

      /* skip any remaining samples for this instrument */

      catchup(modFd, *filePos + skipLength, filePos);
    }
  else
    catchup(modFd, *filePos + (headerSize - 29), filePos);

  return bytesRead;
}

void
XM_sample::decode(char *data) const
{
  signed short oldVal, newVal;
  int i;

  oldVal = 0;

  if (mode_ & WAVE_16_BITS)
    {
      for (i = 0; i < length_; i+=2)
	{
	  newVal = (signed short)(INTEL_SHORT(data + i)) + oldVal;
	  data[i] = newVal & 0x00ff;
	  data[i+1] = (newVal & 0xff00) >> 8;
	  oldVal = newVal;
	}
    }
  else
    {
      for (i = 0; i < length_; i++)
	{
	  newVal = (signed short)(data[i]) + oldVal;
	  data[i] = newVal;
	  oldVal = newVal;
	}
    }
}
