/**************************************************************************
*
* FunktrackerGOLD - By Jason Nunn
*
* Misc. routines (like load etc)
*
* Snail: 32 Rothdale Road, Moil, Darwin, NT, 0810, Australia
*
**************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <malloc.h>
#pragma pack(1) /*force byte aligement. exact memory alignment is essential*/
#include "everything.h"
#include "funktracker.h"
#include "funkmisc.h"

extern tfunk_info funk_info;
extern tfunk_hr *funk_hr_ptr;
extern tslot *funk_pat_ptr;
extern void *funk_sam_ptrs[64];
extern tfunk_chan funk_chan[MAXIMUM_CHANNELS];
extern tfunk_chan funk_chan[MAXIMUM_CHANNELS];
extern tchmix chmix[MAXIMUM_CHANNELS];

extern int precision;      /*8 or 16 bit*/
extern int stereo;
extern int no_active_channels;
extern void init_dsp_buffers(void);
extern void DSPx08_mono_mixxer(void);
extern void DSPx16_mono_mixxer(void);
extern void DSPx08_stereo_mixxer(void);
extern void DSPx16_stereo_mixxer(void);
extern int sample_precision;
extern int bpm_rate;

#pragma pack()

int ferr_val;
char *ferr_messages[] =
{
  "Operation successful.",                              /*0*/
  "Fatal memory allocation error (no memory left).",    /*1*/
  "File open error.",                                   /*2*/
  "Fatal file read error (pretty serious shit).",       /*3*/
  "Unrecognised Module signature.",                     /*4*/
  "Song is corrupt!!!!!.",                              /*5*/
  "Song exceeds set pattern limit.",                    /*6*/
  "Warning: R1 Funk module. Some cmds arranged & added (RTFM please).", /*7*/
  "File create error.",                                 /*8*/
  "Fatal file write error (pretty serious shit).",      /*9*/
  "Can't save module with empty Sequence table.",       /*10*/
  "Unknown samples format.",                            /*11*/
  "Module too complex to be saved as .MOD format.",     /*12*/
  "Unknown song file. Can't make out extension type.",  /*13*/
  "Already at precision. Conversion not necessary."     /*14*/
};

unsigned char funk_cpu_type;
unsigned char funk_card_type;
FILE *funk_fp;
void (*virtualmixxer)(void);

/***************************************************************************
*
***************************************************************************/
#define SO_TT_SIZE 2

void get_environment(void)
{
  char *os_type = getenv("OSTYPE");
  register int x;
  char *os_tt[] = {
    "Linux",
    "FreeBSD"
  };

  for(x = 0;x < SO_TT_SIZE;x++)
    if(strcmp(os_tt[x],os_type) == 0)
      break;

  switch(x)
  {
    case 0:  funk_cpu_type = FKCPU_LINUX; break;
    case 1:  funk_cpu_type = FKCPU_FREEBSD; break;
    default: funk_cpu_type = FKCPU_UNKNOWN; break;
  }
}

/***************************************************************************
*
* 00000000 11111111 22222222
* \    /\     /\  / \      /
*  note  sample com  command value
*
***************************************************************************/
void clear_slot(tslot *slot)
{
  slot->not_sam = 0xfc;
  slot->sam_com = 0x0f;
  slot->com_val = 0x00;
}

void clear_sam_entry(tfunk_sb *funk_sb)
{
  register int y;

  for(y = 0;y < 19;y++) funk_sb->sname[y] = ' ';
  funk_sb->start = 0xffffffff;
  funk_sb->length = 0;
  funk_sb->volume = 0xff;
  funk_sb->balance = 0x80;
  funk_sb->pt_and_sop = 0x08;
  funk_sb->vv_waveform = 0x0;
  funk_sb->rl_and_as = 0x40;
}

void set_BPM(void)
{
  register int x;

  funk_hr_ptr->info[3] &= 1;
  x = bpm_rate - 125;
  if(x < 0) x = (-x & 63) | 64;
  funk_hr_ptr->info[3] |= x << 1;
}

/***************************************************************************
*
***************************************************************************/
int alloc_funk_hr(void)
{
  if((funk_hr_ptr = malloc(sizeof(tfunk_hr))) != NULL)
    return FERR_OK;
  return FERR_MALLOC;
}

int alloc_pat_blk(void)
{
  register int x,y;
#pragma pack(1)
  tslot *pat_pos;
#pragma pack()

  long alloc_size =
    funk_info.funk_pd_size *
    64 *
    no_active_channels *
    sizeof(tslot);

  if((funk_pat_ptr = malloc(alloc_size)) != NULL)
  {
    for(x = 0;x < funk_info.funk_pd_size;x++)
    {
      pat_pos = funk_pat_ptr + (x * 64 * no_active_channels);
      for(y = 0;y < (64 * no_active_channels);y++)
        clear_slot(pat_pos + y);
    }
    return FERR_OK;
  }
  return FERR_MALLOC;
}

void dealloc_funk_mem(void)
{
  register int x;

  if(funk_hr_ptr != NULL)
  {
    free(funk_hr_ptr);
    funk_hr_ptr = NULL;
  }
  if(funk_pat_ptr != NULL)
  {
    free(funk_pat_ptr);
    funk_pat_ptr = NULL;
  }
  for(x = 0;x < 64;x++)
    if(funk_sam_ptrs[x] != NULL)
    {
      free(funk_sam_ptrs[x]);
      funk_sam_ptrs[x] = NULL;
    }
}

void varedit_init(void)
{
  register int chan_no;

  for(chan_no = 0;chan_no < no_active_channels;chan_no++)
    funk_chan[chan_no].channel_kill = PLAY;
}

/***************************************************************************
*
* 'info' code (diagram much for readable than in Funklite.asm ;):
*
*  3 3 3 3 3 3 3 3   2 2 2 2 2 2 2 2   1 1 1 1 1 1 1 1   0 0 0 0 0 0 0 0
*  \--BPM rate-/ |   \-CPU-/ \-card/   \----year---/ \-month-/ \--day--/
*                |                        |
*                /                         \
*     16 bit = 1                            year + 1980
*
***************************************************************************/
void chg_funk_funktype(void)
{
  char tmp[3];

  strncpy(funk_hr_ptr->funk_type,"F200",4);
  sprintf(tmp,"%d",no_active_channels);
  if(no_active_channels < 10)
  {
    funk_hr_ptr->funk_type[2] = 0x30;
    funk_hr_ptr->funk_type[3] = tmp[0];
  }
  else
  {
    funk_hr_ptr->funk_type[2] = tmp[0];
    funk_hr_ptr->funk_type[3] = tmp[1];
  }
}

void new_funk_module(void)
{
  time_t timer = time(NULL);
  struct tm *tblock = localtime(&timer);
  register int x;

  ferr_val = FERR_OK;
  bpm_rate = 125;
  dealloc_funk_mem();
/*CREATE & CLEAR HEADER*/
  ferr_val = alloc_funk_hr();
  if(ferr_val == FERR_OK)
  {
    strncpy(funk_hr_ptr->sig,"Funk",4);
    chg_funk_funktype();
    x = tblock->tm_mon + 1;
    funk_hr_ptr->info[0]  = tblock->tm_mday & 0x1f;
    funk_hr_ptr->info[0] |= (x & 0x7) << 5;
    funk_hr_ptr->info[1]  = (x & 0xf) >> 3;
    x = tblock->tm_year - 80;
    if(x < 0) x = 80;
    funk_hr_ptr->info[1] |= (x & 0x7f) << 1;
    funk_hr_ptr->info[2] =
      (funk_cpu_type << 4) | (funk_card_type & 0xf);
    funk_hr_ptr->info[3] = 0;
    if(sample_precision == 16)
      funk_hr_ptr->info[3] |= 1;
    set_BPM();
    funk_hr_ptr->loop_order = 0xff;
    for(x = 0;x < 256;x++) funk_hr_ptr->order_list[x] = 0xff;
    for(x = 0;x < 128;x++) funk_hr_ptr->break_list[x] = 0x3f;
    for(x = 0;x < 64;x++)
      clear_sam_entry(&funk_hr_ptr->funk_sb[x]);
/*CREATE & CLEAR PATTERNS*/
    ferr_val = alloc_pat_blk();
    varedit_init();
  }
}

/***************************************************************************
*
***************************************************************************/
unsigned long find_file_size(FILE *fp)
{
  register long x;

  fseek(fp,0,SEEK_END);
  x = ftell(fp);
  rewind(fp);
  return x;
}

/***************************************************************************
*
***************************************************************************/
void find_pats_seqs(void)
{
  register int x;

  funk_info.no_of_sequences = 0;
  funk_info.no_of_patterns = 0;
  for(x = 0;x < 256;x++)
    if(funk_hr_ptr->order_list[x] == 0xFF)
      break;
    else
    {
      funk_info.no_of_sequences++;
      if(funk_hr_ptr->order_list[x] > funk_info.no_of_patterns)
        funk_info.no_of_patterns = funk_hr_ptr->order_list[x];
    }
  funk_info.no_of_sequences--;
  funk_info.no_of_patterns++;
}

/***************************************************************************
* L O A D   S O N G
***************************************************************************/
int dsp_load_sample(FILE *fp,int sample_no)
{
  register unsigned long samsize =
    funk_hr_ptr->funk_sb[sample_no].length * (sample_precision >> 3);
  funk_sam_ptrs[sample_no] = malloc(samsize);
  if(funk_sam_ptrs[sample_no] == NULL)
  {
    ferr_val = FERR_MALLOC;
    return 0;
  }
  if(fread(funk_sam_ptrs[sample_no],1,samsize,fp) != samsize)
  {
    ferr_val = FERR_FREAD;
    return 0;
  }
  return 1;
}

void clean_old_fnk(void)
{
  register int x,y;

  for(x = 0;x < 64;x++)
    for(y = 0;y < 19;y++)
      if(((uDB)funk_hr_ptr->funk_sb[x].sname[y] < ' ') ||
         ((uDB)funk_hr_ptr->funk_sb[x].sname[y] > 127))
        funk_hr_ptr->funk_sb[x].sname[y] = ' ';
}

void load_funk_module(char *filename)
{
  register int x;
  register long file_size,pattern_block_size;
  char tmp[3];

  ferr_val = FERR_OK;
  if((funk_fp = fopen(filename,"rb")) == NULL)
  {
    ferr_val = FERR_FOPEN;
    return;
  }
  file_size = find_file_size(funk_fp);
  dealloc_funk_mem();
  ferr_val = alloc_funk_hr();
  if(ferr_val == FERR_OK)
  {
    if(fread(funk_hr_ptr,sizeof(tfunk_hr),1,funk_fp) != 1)
    {
      ferr_val = FERR_FREAD;
      return;
    }
    if(strncmp(funk_hr_ptr->sig,"Funk",4) != 0)
    {
      ferr_val = FERR_FUNK_SIG;
      return;
    }
    if(funk_hr_ptr->LZH_check_size != file_size)
    {
      ferr_val = FERR_FCORRUPT;
      return;
    }
    no_active_channels = 8;
    sample_precision = 8;
    bpm_rate = 125;
    if(funk_hr_ptr->funk_type[0] == 'F')
      if((funk_hr_ptr->funk_type[1] == 'v') ||
         (funk_hr_ptr->funk_type[1] == 'k') ||
         (funk_hr_ptr->funk_type[1] == '2'))
      {
        tmp[0] = funk_hr_ptr->funk_type[2];
        tmp[1] = funk_hr_ptr->funk_type[3];
        tmp[2] = 0;
        sscanf(tmp,"%d",(int *)&no_active_channels);
        if(funk_hr_ptr->funk_type[1] == '2')
        {
          if(funk_hr_ptr->info[3] & 1) sample_precision = 16;
          bpm_rate = funk_hr_ptr->info[3] >> 1;
          if(bpm_rate & 64) bpm_rate = -(bpm_rate & 63);
          bpm_rate += 125;
        }
      }
    find_pats_seqs();
    if(funk_info.no_of_patterns > funk_info.funk_pd_size)
    {
      ferr_val = FERR_PAT_LIM;
      return;
    }
    ferr_val = alloc_pat_blk();
    if(ferr_val == FERR_OK)
    {
      pattern_block_size =
        funk_info.no_of_patterns * 64 * no_active_channels;
      if(fread(funk_pat_ptr,sizeof(tslot),
               pattern_block_size,funk_fp) != pattern_block_size)
      {
        ferr_val = FERR_FREAD;
        return;
      }
      for(x = 0;x < 64;x++)
        if(funk_hr_ptr->funk_sb[x].length)
          if(!dsp_load_sample(funk_fp,x))
            return;
      if(funk_hr_ptr->funk_type[1] != '2')
      {
        clean_old_fnk();
        ferr_val = FERR_OLD_FK;
      }
      varedit_init();
    }
  }
  fclose(funk_fp);
}

/***************************************************************************
* S A V E    S O N G
***************************************************************************/
int dsp_save_sample(FILE *fp,int sample_no)
{
  register unsigned long samsize =
    funk_hr_ptr->funk_sb[sample_no].length * (sample_precision >> 3);
  if(fwrite(funk_sam_ptrs[sample_no],1,samsize,fp) != samsize)
  {
    ferr_val = FERR_FWRITE;
    return 0;
  }
  return 1;
}

void save_funk_module(char *filename)
{
  register int x,a = 0;
  register long pattern_block_size;

  ferr_val = FERR_OK;
  find_pats_seqs();
  for(x = 0;x < 256;x++)
    if(funk_hr_ptr->order_list[x] != 0xFF)
    {
      a = 1;
      break;
    }
  if(!a)
  {
    ferr_val = FERR_ESEQTABLE;
    return;
  }
  else
  {
    set_BPM();
    pattern_block_size =
      funk_info.no_of_patterns * 64 * no_active_channels;
    funk_hr_ptr->LZH_check_size =
      sizeof(tfunk_hr) + (pattern_block_size * sizeof(tslot));
    for(x = 0;x < 64;x++)
      funk_hr_ptr->LZH_check_size +=
        funk_hr_ptr->funk_sb[x].length * ((funk_hr_ptr->info[3] & 0x1) + 1);
    if((funk_fp = fopen(filename,"wb")) == NULL)
    {
      ferr_val = FERR_FCREATE;
      return;
    }
    if(fwrite(funk_hr_ptr,sizeof(tfunk_hr),1,funk_fp) != 1)
    {
      ferr_val = FERR_FWRITE;
      return;
    }
    if(fwrite(funk_pat_ptr,sizeof(tslot),
       pattern_block_size,funk_fp) != pattern_block_size)
    {
      ferr_val = FERR_FWRITE;
      return;
    }
    for(x = 0;x < 64;x++)
      if(funk_hr_ptr->funk_sb[x].length)
        if(!dsp_save_sample(funk_fp,x))
          return;
    fclose(funk_fp);
  }
}

/***************************************************************************
*
***************************************************************************/
void dsp_init_for_play(void)
{
  if(precision == 8)
    if(stereo)
      virtualmixxer = DSPx08_stereo_mixxer;
    else
      virtualmixxer = DSPx08_mono_mixxer;
  else
    if(stereo)
      virtualmixxer = DSPx16_stereo_mixxer;
    else
      virtualmixxer = DSPx16_mono_mixxer;

  init_dsp_buffers();
}

void funk_init_for_play(void)
{
  register int chan_no;

  funk_info.trek_status = STOP;
  funk_info.sequence_ofs = 0;
  funk_info.pattern_ofs = 0;
  funk_info.pattern_ofs_display = funk_info.pattern_ofs;
  funk_info.sequence_ofs_display = funk_info.sequence_ofs;
  funk_info.tempo = 4;
  funk_info.tempo_count = 0;
  funk_info.master_volume = 0xff;
  find_pats_seqs();

  for(chan_no = 0;chan_no < no_active_channels;chan_no++)
  {
/*-control system-----------------*/
    funk_chan[chan_no].command = 0xf;
    funk_chan[chan_no].com_val = 0;
    funk_chan[chan_no].comspd_count = 0;
    funk_chan[chan_no].sample = 0x3f;
    funk_chan[chan_no].port_type = 0;
    funk_chan[chan_no].sample_ofs_parm = 0x08;
    funk_chan[chan_no].vib_waveform = 0;
    funk_chan[chan_no].vol_vib_waveform = 0;
    funk_chan[chan_no].retrig_limit = 4;
    funk_chan[chan_no].arp_speed = 0;
    funk_chan[chan_no].balance = 0x80;
/*-note system--------------------*/
    funk_chan[chan_no].note_command = 0xf;
    funk_chan[chan_no].note_com_val = 0;
    funk_chan[chan_no].note_comspd_count = 0;
    funk_chan[chan_no].note = 0x3f;
    funk_chan[chan_no].ifreq = 0;
    funk_chan[chan_no].ifreq_vibrato = 0;
    funk_chan[chan_no].ifreq_portdest = 0;
    funk_chan[chan_no].rfreq = 0;
    funk_chan[chan_no].rfreq_portdest = 0;
    funk_chan[chan_no].vib_ptr = 0;
    funk_chan[chan_no].note_beat_count = 0;
/*-volume system------------------*/
    funk_chan[chan_no].volume_command = 0xf;
    funk_chan[chan_no].volume_com_val = 0;
    funk_chan[chan_no].volume_comspd_count = 0;
    funk_chan[chan_no].volume = 0;
    funk_chan[chan_no].volume_vibrato = 0;
    funk_chan[chan_no].volume_portdest = 0;
    funk_chan[chan_no].rvolume = 0;
    funk_chan[chan_no].vol_vib_ptr = 0;
    funk_chan[chan_no].volume_beat_count = 0;

/*-card dependant----------------*/
    chmix[chan_no].start = 0xffffffff;
    chmix[chan_no].length = 0x100000;
    chmix[chan_no].funkctrl = 0;
    chmix[chan_no].sample_ptr = 0;
    chmix[chan_no].freq = 0;
    chmix[chan_no].left_volume = 0;
    chmix[chan_no].right_volume = 0;
    chmix[chan_no].echo_ptr = 0;
    chmix[chan_no].echo_delay = 0;
    chmix[chan_no].echo_decay = 0;
    chmix[chan_no].echo_feedback = 0;
  }
  dsp_init_for_play();
}

/***************************************************************************
*
* read sample data
*
***************************************************************************/

/***************************************
*
***************************************/
FILE *tmp_f;
#define FNBUF_SIZE 4096
#pragma pack(1)
void *sam_load_tb;
void *sam_load_tb2;
#pragma pack()

void write_bloc_sambloc(unsigned int *samoutpos,int prec)
{
  if(*samoutpos > 0)
  {
    if(fwrite(sam_load_tb2,prec,*samoutpos,tmp_f) != *samoutpos)
    {
      ferr_val = FERR_FWRITE;
      return;
    }
    *samoutpos = 0;
  }
}

void *load_samblock(
  FILE *samfile,
  int sam_no,
  unsigned long read_length,
  double saminfreqinc,
  int prec)
{
  unsigned long rs;
  void *sam_alloc = NULL;
  double saminpos;
  unsigned int samoutpos = 0;

  do
  {
    if(read_length > FNBUF_SIZE)
      rs = FNBUF_SIZE;
    else
      rs = read_length;
    if(fread(sam_load_tb,prec,rs,samfile) != rs)
    {
      ferr_val = FERR_FREAD;
      return NULL;
    }
    read_length -= rs;
    if(rs > 0)
    {
      saminpos = 0;
      do
      {
        if(samoutpos == FNBUF_SIZE) write_bloc_sambloc(&samoutpos,prec);
        if(ferr_val != FERR_OK) return NULL;
        if((unsigned int)saminpos < rs)
        {
#pragma pack(1)
          if(prec == 1)
            *(uDB *)((uDB *)sam_load_tb2 + samoutpos++) =
              *(uDB *)((uDB *)sam_load_tb + (unsigned int)saminpos);
          else
            *(uDW *)((uDW *)sam_load_tb2 + samoutpos++) =
              *(uDW *)((uDW *)sam_load_tb + (unsigned int)saminpos);
#pragma pack()
          saminpos += saminfreqinc;
        }
      } while((unsigned int)saminpos < rs);
    }
  } while(rs);
  write_bloc_sambloc(&samoutpos,prec);
  if(ferr_val != FERR_OK) return NULL;
  fseek(tmp_f,0,SEEK_END);
  funk_hr_ptr->funk_sb[sam_no].length = ftell(tmp_f);
  fseek(tmp_f,0,SEEK_SET);
  if((sam_alloc = malloc(funk_hr_ptr->funk_sb[sam_no].length)) == NULL)
  {
    ferr_val = FERR_MALLOC;
    return NULL;
  }
  if(prec == 2) funk_hr_ptr->funk_sb[sam_no].length >>= 1;
  if(fread(sam_alloc,prec,funk_hr_ptr->funk_sb[sam_no].length,tmp_f) !=
    funk_hr_ptr->funk_sb[sam_no].length)
  {
    ferr_val = FERR_FREAD;
    free(sam_alloc);
    return NULL;
  }
  return sam_alloc;
}

void *load_samdata(
  FILE *samfile,
  int sam_no,
  unsigned long read_length,
  double saminfreqinc,
  int prec)
{
  void *sam_alloc = NULL;
  int alloc_size = FNBUF_SIZE * prec;
  if((sam_load_tb = malloc(alloc_size)) != NULL)
  {
    if((sam_load_tb2 = malloc(alloc_size)) != NULL)
    {
      if((tmp_f = tmpfile()) != NULL)
      {
        sam_alloc = load_samblock(samfile,sam_no,read_length,saminfreqinc,prec);
        fclose(tmp_f);
      }
      else
        ferr_val = FERR_FOPEN;
    }
    else
      ferr_val = FERR_MALLOC;
    free(sam_load_tb);
  }
  else
    ferr_val = FERR_MALLOC;

  return sam_alloc;
}

/***************************************************************************
*
***************************************************************************/
void set_sample_name(int sam_no,char *filename)
{
  register int x;
  register int b = strlen(filename);
  for(x = 0;x < 19;x++)
  {
    if(x < b)
    {
      unsigned char a = filename[x];
      if((a >= 32) && (a <= 127))
        funk_hr_ptr->funk_sb[sam_no].sname[x] = filename[x];
      else
        funk_hr_ptr->funk_sb[sam_no].sname[x] = ' ';
    }
    else
      funk_hr_ptr->funk_sb[sam_no].sname[x] = ' ';
  }
}

void unsign_8bit_data(void *sam_ptr,unsigned long num2read)
{
  register unsigned long a;

  if(sample_precision == 8)
    for(a = 0;a < num2read;a++) *(unsigned char *)(sam_ptr + a) ^= 0x80;
}

/***************************************************************************
*
* load 8 bit unsigned file (.SND, .RAW)
*
***************************************************************************/
void snd_load_sample(int sam_no,char *filename,double resamf)
{
  FILE *fp;
  register unsigned long file_size;

  ferr_val = FERR_OK;
  if((fp = fopen(filename,"rb")) == NULL)
  {
    ferr_val = FERR_FOPEN;
    return;
  }
  file_size = find_file_size(fp);
  if(sample_precision == 16) file_size >>= 1;
  funk_sam_ptrs[sam_no] =
    load_samdata(fp,sam_no,file_size,resamf,sample_precision >> 3);
  fclose(fp);
  if(funk_sam_ptrs[sam_no] != NULL)
  {
    unsign_8bit_data(funk_sam_ptrs[sam_no],funk_hr_ptr->funk_sb[sam_no].length);
    set_sample_name(sam_no,filename);
  }
}

/***************************************************************************
*
* Load MessyWindows WAVE file
*
***************************************************************************/
typedef struct
{
  sDB rID[4];
  uDD rLen;
  sDB wID[4];
  sDB fId[4];
  uDD fLen;
  uDW wFormatTag;
  uDW nChannels;
  uDW nSamplesPerSec;
  uDW nAvgBytesPerSec;
  uDW nBlockAlign;
  uDW FormatSpecific;
  uDD blank;
  sDB dId[4];
  uDD dLen;
} twav_hr;

void wav_load_sample(int sam_no,char *filename,double resamf)
{
  FILE *fp;
  twav_hr wav_hr;

  ferr_val = FERR_OK;
  if((fp = fopen(filename,"rb")) == NULL)
  {
    ferr_val = FERR_FOPEN;
    return;
  }
  if(fread(&wav_hr,1,sizeof(twav_hr),fp) != sizeof(twav_hr))
  {
    ferr_val = FERR_FREAD;
    return;
  }
  if((strncmp(wav_hr.rID,"RIFF",4) != 0) &&
     (strncmp(wav_hr.wID,"WAVE",4) != 0) &&
     (strncmp(wav_hr.fId,"fmt ",4) != 0) &&
     (strncmp(wav_hr.dId,"data",4) != 0))
  {
    ferr_val = FERR_SAMUNKNOWN;
    return;
  }
  if(sample_precision == 16) wav_hr.dLen >>= 1;
  funk_sam_ptrs[sam_no] =
    load_samdata(fp,sam_no,wav_hr.dLen,resamf,sample_precision >> 3);
  fclose(fp);
  if(funk_sam_ptrs[sam_no] != NULL)
  {
    unsign_8bit_data(funk_sam_ptrs[sam_no],funk_hr_ptr->funk_sb[sam_no].length);
    set_sample_name(sam_no,filename);
  }
}

/***************************************************************************
*
***************************************************************************/
void load_sample(int sam_no,char *filename,double resamf)
{
  ferr_val = FERR_OK;
  if(strstr(filename,".snd") != NULL)        /*8 bit unsigned .SND*/
    snd_load_sample(sam_no,filename,resamf);
  else if(strstr(filename,".raw") != NULL)   /*8 bit unsigned .RAW*/
    snd_load_sample(sam_no,filename,resamf);
  else if(strstr(filename,".wav") != NULL)   /*.WAV*/
    wav_load_sample(sam_no,filename,resamf);
  else
    ferr_val = FERR_SAMUNKNOWN;
}

/***************************************************************************
****************************************************************************
****************************************************************************
*
* LOAD/SAVE .MOD CONVERTER FORMAT (NB/ this is a hack. If you regularly use
* this facility, and you know a bit about ANSI C, then finetune the pattern
* commands converter, and send me a patch. It's been a VERY long time since
* I've tracked with a .MOD tracker, so i'm not really qualified to accurately
* finetune it.
*
* The code you are looking at is basically from the MOD2FNK.PAS program
* provided in the DOS32 Funktracker project.
*
****************************************************************************
****************************************************************************
***************************************************************************/
#pragma pack(1)
/*=MOD STRUCTURES==============================*/
typedef struct
{
  sDB sname[22];
  uDW slength;
  sDB sfinetune;
  uDB svolume;
  uDW srepeat;
  uDW sreplen;
} tmodsamples;

typedef struct
{
  sDB         songname[20];
  tmodsamples samples[31];
  uDB         songlen;
  uDB         restart;
  uDB         sequences[128];
  sDB         mk[4];
} tmodheader;

typedef struct
{
  uDB byte1;
  uDB byte2;
  uDB byte3;
  uDB byte4;
} tmodslot;

tmodheader    modheader;
tslot         *fnkpat_ptr;
tmodslot      *modpat_ptr;
tmodslot      *mod_pattern;
#pragma pack()

int period_match[61] =
{
  1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,912,
  856,808,762,720,678,640,604,570,538,508,480,453,
  428,404,381,360,339,320,302,285,269,254,240,226,
  214,202,190,180,170,160,151,143,135,127,120,113,
  107,101,95,90,85,80,75,71,67,63,60,56,0
};
int MOD_tune_table[16] =
{
  (0x369e9a / 428),(0x369e9a / 425),(0x369e9a / 422),(0x369e9a / 419),
  (0x369e9a / 416),(0x369e9a / 413),(0x369e9a / 410),(0x369e9a / 407),
  (0x369e9a / 453),(0x369e9a / 450),(0x369e9a / 447),(0x369e9a / 444),
  (0x369e9a / 441),(0x369e9a / 437),(0x369e9a / 434),(0x369e9a / 431)
};

FILE          *modfile;
int           modsam_no;

/***************************************************************************
* .MOD LOADER
***************************************************************************/

/***************************************
*
***************************************/
int load_mod_header(void)
{
  register int x,y;
  char tmp[3];

  if(fread(&modheader,sizeof(tmodheader),1,modfile) != 1)
  {
    ferr_val = FERR_FREAD;
    return 0;
  }
  if(strncmp(modheader.mk,"M.K.",4) == 0)
    no_active_channels = 4;
  else if(strncmp(modheader.mk,"FLT4",4) == 0)
    no_active_channels = 4;
  else if(strncmp(modheader.mk,"FLT8",4) == 0)
    no_active_channels = 8;
  else if(strncmp(modheader.mk,"6CHN",4) == 0)
    no_active_channels = 6;
  else if(strncmp(modheader.mk,"8CHN",4) == 0)
    no_active_channels = 8;
  else if(strncmp(modheader.mk + 2,"CH",2) == 0)
  {
    tmp[0] = modheader.mk[0];
    tmp[1] = modheader.mk[1];
    tmp[2] = 0;
    sscanf(tmp,"%d",(int *)&no_active_channels);
  }
  else
  {
    ferr_val = FERR_FUNK_SIG;
    return 0;
  }
  sample_precision = 8;
  new_funk_module();
  funk_hr_ptr->info[2] = FKCARD_RIPPED;
  for(x = 0;x < modheader.songlen;x++)
    funk_hr_ptr->order_list[x] = modheader.sequences[x];
  for(x = 0;x < 31;x++)
  {
    for(y = 0;y < 19;y++)
    {
      funk_hr_ptr->funk_sb[x].sname[y] = modheader.samples[x].sname[y];
      if(((uDB)funk_hr_ptr->funk_sb[x].sname[y] < ' ') ||
         ((uDB)funk_hr_ptr->funk_sb[x].sname[y] > 127))
        funk_hr_ptr->funk_sb[x].sname[y] = ' ';
    }
    modheader.samples[x].slength =
      (((modheader.samples[x].slength & 0xff) << 8) +
      ((modheader.samples[x].slength & 0xff00) >> 8)) << 1;
    modheader.samples[x].srepeat =
      (((modheader.samples[x].srepeat & 0xff) << 8) +
      ((modheader.samples[x].srepeat & 0xff00) >> 8)) << 1;
    modheader.samples[x].sreplen =
      (((modheader.samples[x].sreplen & 0xff) << 8) +
      ((modheader.samples[x].sreplen & 0xff00) >> 8)) << 1;

    if(modheader.samples[x].slength > 0)
    {
      if(modheader.samples[x].sreplen > 2)
      {
        funk_hr_ptr->funk_sb[x].length = 
          modheader.samples[x].srepeat + modheader.samples[x].sreplen;
        if(funk_hr_ptr->funk_sb[x].length > modheader.samples[x].slength)
          funk_hr_ptr->funk_sb[x].length = modheader.samples[x].slength;
        funk_hr_ptr->funk_sb[x].start = modheader.samples[x].srepeat;
      }
      else
        funk_hr_ptr->funk_sb[x].length = modheader.samples[x].slength;
    }
    if(modheader.samples[x].svolume > 0)
    {
      y = modheader.samples[x].svolume << 2;
      if(y == 256) y = 255;
      funk_hr_ptr->funk_sb[x].volume = y;
    }
    else
      funk_hr_ptr->funk_sb[x].volume = 0;
  }
  return 1;
}

/***************************************
*
***************************************/
void load_mod_pattern(void)
{
  register int x,pattern,treks,channels;
  char oldsample[32];

  find_pats_seqs();
  for(x = 0;x < 32;x++) oldsample[x] = 0;
  fnkpat_ptr = funk_pat_ptr;
  for(pattern = 0;pattern < funk_info.no_of_patterns;pattern++)
  {
    if(fread(mod_pattern,
       (64 * no_active_channels * sizeof(mod_pattern)),1,modfile) != 1)
    {
      ferr_val = FERR_FREAD;
      return;
    }
    modpat_ptr = mod_pattern;
    for(treks = 0;treks < 64;treks++)
      for(channels = 0;channels < no_active_channels;channels++)
      {
        int period = ((modpat_ptr->byte1 & 0xf) << 8) + modpat_ptr->byte2;
        int pers,note = 0,sample,command,commval;
        for(pers = 0;pers < 60;pers++)
          if(period >= period_match[pers])
          {
            note = pers;
            break;
          }
        sample = (modpat_ptr->byte3 >> 4) | (modpat_ptr->byte1 & 0xf0);
        command = modpat_ptr->byte3 & 0xf;
        commval = modpat_ptr->byte4;
        if(period != 0)
        {
          if(sample == 0)
            sample = oldsample[channels];
          else
            oldsample[channels] = sample;
          if(sample > 0)
          {
            sample--;
            fnkpat_ptr->not_sam = note << 2;
            fnkpat_ptr->sam_com = 0xf;
            fnkpat_ptr->not_sam |= (sample >> 4) & 3;
            fnkpat_ptr->sam_com |= (sample & 15) << 4;
          }
        }
        if(command > 0)
        {
          int fnkcom = 0xf + 'A';
          int fnkcomv = 0;
          int xxx;
          switch(command)
          {
            case 0: /*arpeggio*/
              fnkcom = 'L';
              fnkcomv = commval;
              break;
            case 1: /*portup*/
              fnkcom = 'A';
              fnkcomv = commval;
              break;
            case 2: /*portdn*/
              fnkcom = 'B';
              fnkcomv = commval;
              break;
            case 5: /*porta note + volslide}*/
            case 3: /*porta note*/
              fnkcom = 'C';
              fnkcomv = commval;
              break;
            case 6: /*vibrato + volslide*/
            case 4: /*vibrato*/
              fnkcom = 'D';
              fnkcomv = commval;
              break;
            case 7: /*tremolo*/
              fnkcom = 'K';
              fnkcomv = commval;
              break;
            case 9: /*sample offset*/
              fnkcom = 'M';
              fnkcomv = commval;
              break;
            case 0xa: /*Volume Slide*/
              if((commval & 0xf0) != 0)
                fnkcom = 'G';
              else
                fnkcom = 'H';
              commval &= 0xf;
              break;
            case 0xc: /*set volume*/
              fnkcom = 'N';
              xxx = commval << 2;
              if(xxx == 256) xxx = 255;
              fnkcomv = xxx;
              break;
            case 0xd: /*pattern break*/
              funk_hr_ptr->break_list[pattern] = treks;
              break;
            case 0xe: /*command e*/
              switch(commval >> 4)
              {
                case 1: /*fine slideup*/
                  fnkcom = 'O';
                  fnkcomv = 0x40 | (commval & 0xf);
                  break;
                case 2: /*fine slidedn*/
                  fnkcom = 'O';
                  fnkcomv = 0x50 | (commval & 0xf);
                  break;
                case 4: /*Vibrato waveform cntl*/
                  fnkcom = 'O';
                  switch(commval & 0xf)
                  {
                    case 0: fnkcomv = 0x00; break; /*sine*/
                    case 1: fnkcomv = 0x03; break; /*ramp down*/
                    case 2: fnkcomv = 0x02; break; /*square*/
                    case 3: fnkcomv = 0x04; break; /*random*/
                  }
                  break;
                case 7: /*tremolo waveform cntl*/
                  fnkcom = 'O';
                  switch(commval & 0xf)
                  {
                    case 0: fnkcomv = 0x05; break; /*sine*/
                    case 1: fnkcomv = 0x08; break; /*ramp down*/
                    case 2: fnkcomv = 0x07; break; /*square*/
                    case 3: fnkcomv = 0x09; break; /*random*/
                  }
                  break;
                case 9: /*retrig note*/
                  fnkcom = 'O';
                  fnkcomv = 0xD0 | (commval & 0xf);
                  break;
                case 0xa: /*fine volume up*/
                  fnkcom = 'O';
                  fnkcomv = 0x60 | (commval & 0xf);
                  break;
                case 0xb: /*fine volume dn*/
                  fnkcom = 'O';
                  fnkcomv = 0x70 | (commval & 0xf);
                  break;
                case 0xc: /*note cut*/
                  fnkcom = 'O';
                  fnkcomv = 0x01 | (commval & 0xf);
                  break;
              }
              break;
            case 0xf: /*set tempo*/
              fnkcom = 'O';
              if(commval > 0) commval--;
              fnkcomv = 0xf0 | (commval & 0xf);
              break;
          }
          fnkcom -= 'A';
          fnkpat_ptr->sam_com &= 0xf0;
          fnkpat_ptr->sam_com |= fnkcom & 0xf;
          fnkpat_ptr->com_val = fnkcomv;
        }
        modpat_ptr++;
        fnkpat_ptr++;
      }
  }
}

/***************************************
*
***************************************/
void load_mod_samples(void)
{
  unsigned long read_length;

  for(modsam_no = 0;modsam_no < 31;modsam_no++)
  {
    if(modheader.samples[modsam_no].slength > 0)
    {
      double saminfreqinc =
        (double)MOD_tune_table[(int)modheader.samples[modsam_no].sfinetune] /
        (double)MOD_tune_table[0];
      if(modheader.samples[modsam_no].sreplen > 2)
      {
        read_length =
          modheader.samples[modsam_no].srepeat +
          modheader.samples[modsam_no].sreplen;
        if(read_length >= modheader.samples[modsam_no].slength)
        {
          read_length = modheader.samples[modsam_no].slength;
          funk_sam_ptrs[modsam_no] =
            load_samdata(modfile,modsam_no,read_length,saminfreqinc,1);
          if(ferr_val != FERR_OK) return;
        }
        else
        {
          funk_sam_ptrs[modsam_no] =
            load_samdata(modfile,modsam_no,read_length,saminfreqinc,1);
          if(ferr_val != FERR_OK) return;
          read_length =
            modheader.samples[modsam_no].slength -
            (modheader.samples[modsam_no].srepeat +
             modheader.samples[modsam_no].sreplen);
          fseek(modfile,read_length,SEEK_CUR);
        }
      }
      else
      {
        read_length = modheader.samples[modsam_no].slength;
        funk_sam_ptrs[modsam_no] =
          load_samdata(modfile,modsam_no,read_length,saminfreqinc,1);
        if(ferr_val != FERR_OK) return;
      }
    }
  }
}

/***************************************
*
***************************************/
void load_mod_module(char *filename)
{
  ferr_val = FERR_OK;
  if((mod_pattern = malloc(64 * MAXIMUM_CHANNELS * sizeof(mod_pattern))) != NULL)
  {
    if((modfile = fopen(filename,"rb")) != NULL)
    {
      if(load_mod_header())
      {
        load_mod_pattern();
        load_mod_samples();
        if(strlen(filename) < 255)
          strcpy(strstr(filename,".mod"),".Funk");
        else
          strcpy(strstr(filename,".mod") - 1,".Funk");
      }
      fclose(modfile);
    }
    else
      ferr_val = FERR_FOPEN;
    free(mod_pattern);
  }
  else
    ferr_val = FERR_MALLOC;
}

/***************************************************************************
* .MOD SAVER
***************************************************************************/

/***************************************
*
***************************************/
int save_mod_header(void)
{
  register int x;
  char tmp[3];

  strncpy(modheader.songname,"Ripped from FnkGOLD",20);
  for(x = 0;x < 31;x++)
  {
    strncpy(modheader.samples[x].sname,funk_hr_ptr->funk_sb[x].sname,19);
    modheader.samples[x].sfinetune = 0;
    modheader.samples[x].svolume = funk_hr_ptr->funk_sb[x].volume >> 2;
    if(funk_hr_ptr->funk_sb[x].length)
    {
      register unsigned int length = funk_hr_ptr->funk_sb[x].length >> 1;
      modheader.samples[x].slength =
        ((length & 0xff) << 8) + ((length & 0xff00) >> 8);
      if(funk_hr_ptr->funk_sb[x].start == 0xffffffff)
      {
        modheader.samples[x].srepeat = 0;
        modheader.samples[x].sreplen = 0;
      }
      else
      {
        register unsigned int replen;
        register unsigned int start = funk_hr_ptr->funk_sb[x].start >> 1;
        modheader.samples[x].srepeat =
          ((start & 0xff) << 8) + ((start & 0xff00) >> 8);
        replen =
          (funk_hr_ptr->funk_sb[x].length - funk_hr_ptr->funk_sb[x].start) >> 1;
        modheader.samples[x].sreplen =
          ((replen & 0xff) << 8) + ((replen & 0xff00) >> 8);
      }
    }
    else
    {
      modheader.samples[x].slength = 0;
      modheader.samples[x].srepeat = 0;
      modheader.samples[x].sreplen = 0;
    }
  }
  modheader.songlen = funk_info.no_of_sequences + 1;
  modheader.restart = funk_hr_ptr->loop_order;
  for(x = 0;x < 128;x++)
    if(funk_hr_ptr->order_list[x] == 0xff)
      modheader.sequences[x] = 0;
    else
      modheader.sequences[x] = funk_hr_ptr->order_list[x];

  if(no_active_channels == 4)
    strncpy(modheader.mk,"M.K.",4);
  else if(no_active_channels == 6)
    strncpy(modheader.mk,"6CHN",4);
  else if(no_active_channels == 8)
    strncpy(modheader.mk,"8CHN",4);
  else
  {
    strncpy(modheader.mk,"00CH",4);
    sprintf(tmp,"%d",no_active_channels);
    if(no_active_channels < 10)
    {
      modheader.mk[0] = 0x30;
      modheader.mk[1] = tmp[0];
    }
    else
    {
      modheader.mk[0] = tmp[0];
      modheader.mk[1] = tmp[1];
    }
  }
  if(fwrite(&modheader,sizeof(tmodheader),1,modfile) != 1)
  {
    ferr_val = FERR_FWRITE;
    return 0;
  }
  return 1;
}

/***************************************
*
***************************************/
void save_mod_pattern(void)
{
  register int pattern,treks,channels,old_note,
    note = 0,sample,commnd,comval,period,modcommnd;

  fnkpat_ptr = funk_pat_ptr;
  for(pattern = 0;pattern < funk_info.no_of_patterns;pattern++)
  {
    modpat_ptr = mod_pattern;
    for(treks = 0;treks < 64;treks++)
      for(channels = 0;channels < no_active_channels;channels++)
      {
        old_note = note;
        note = fnkpat_ptr->not_sam >> 2;
        sample = 
          ((fnkpat_ptr->not_sam & 0x3) << 4) + (fnkpat_ptr->sam_com >> 4) + 1;
        commnd = fnkpat_ptr->sam_com & 0xf;
        comval = fnkpat_ptr->com_val;
        if(note == 0x3f)
        {
          modpat_ptr->byte1 = 0;
          modpat_ptr->byte2 = 0;
          modpat_ptr->byte3 = 0;
        }
        else
        {
          if(note == 0x3E)
            period = period_match[old_note];
          else
            period = period_match[note];
          modpat_ptr->byte1 = (sample & 0xf0) | ((period >> 8) & 0x0f);
          modpat_ptr->byte2 = period & 0xff;
          modpat_ptr->byte3 = ((sample & 0x0f) << 4);
        }
        modcommnd = 0;
        modpat_ptr->byte4 = 0;
        switch(commnd)
        {
          case 0xd: /*volume*/
            modcommnd = 0xc;
            modpat_ptr->byte4 = comval >> 2;
            break;
          case 0xe: /*cmd O*/
            switch(comval >> 4)
            {
              case 0xf: /*tempo*/
                modcommnd = 0xf;
                modpat_ptr->byte4 = comval & 0xf;
                break;
            }
            break;
        }
        modpat_ptr->byte3 |= modcommnd;
        fnkpat_ptr++;
        modpat_ptr++;
      }

    if(fwrite(mod_pattern,
      (64 * no_active_channels * sizeof(mod_pattern)),1,modfile) != 1)
    {
      ferr_val = FERR_FWRITE;
      return;
    }
  }
}

/***************************************
*
***************************************/
void save_mod_samples(void)
{
  register int x;

  for(x = 0;x < 31;x++)
  {
    register unsigned long new_length =
      funk_hr_ptr->funk_sb[x].length & 0xfffffffe;
    if(new_length)
      if(fwrite(funk_sam_ptrs[x],1,new_length,modfile) != new_length)
      {
        ferr_val = FERR_FWRITE;
        return;
      }
  }
}

/***************************************
*
***************************************/
void save_mod_module(char *filename)
{
  register int x,sample_count;

  ferr_val = FERR_OK;
  find_pats_seqs();
  if((funk_info.no_of_patterns > 64) ||
     (sample_precision == 16) ||
     (funk_info.no_of_sequences > 127))
  { /*Funktracker modules are too complex in .MOD format as it is!*/
    ferr_val = FERR_MODCOMPLEX;
    return;
  }
  sample_count = 0;
  for(x = 0;x < 31;x++)
    if(funk_hr_ptr->funk_sb[x].length)
    {
      if(funk_hr_ptr->funk_sb[x].length > 0x1ffff)
      {
        ferr_val = FERR_MODCOMPLEX;
        return;
      }
      sample_count++;
    }
  if(sample_count > 31)
  {
    ferr_val = FERR_MODCOMPLEX;
    return;
  }

  if((mod_pattern = malloc(64 * MAXIMUM_CHANNELS * sizeof(mod_pattern))) != NULL)
  {
    char *new_filename;
    int new_filename_size = strlen(filename) + 4;
    new_filename = malloc(new_filename_size);
    if(new_filename != NULL)
    {
      memset(new_filename,0,new_filename_size);
      if(strstr(filename,".Funk") != NULL)
        strncpy(new_filename,filename,strlen(filename) - 4);
      else if(strstr(filename,".fnk") != NULL)
        strncpy(new_filename,filename,strlen(filename) - 3);
      strcat(new_filename,"mod");
      if((modfile = fopen(new_filename,"wb")) != NULL)
      {
        if(save_mod_header())
        {
          save_mod_pattern();
          save_mod_samples();
        }
        fclose(modfile);
      }
      else
        ferr_val = FERR_FCREATE;
      free(new_filename);
    }
    else
      ferr_val = FERR_MALLOC;
    free(mod_pattern);
  }
  else
    ferr_val = FERR_MALLOC;
}

/***************************************************************************
* N TRACK TO N TRACK CONVERTER
*
* change the number of channels a song has.
*
***************************************************************************/
void save_ntrac_funk_module(char *filename,int new_no_active_channels)
{
  register int x,y,z,old_no_active_channels,trac_c_Len;
#pragma pack(1)
  tslot new_pat_trek[MAXIMUM_CHANNELS];
#pragma pack()

  old_no_active_channels = no_active_channels;
  no_active_channels = new_no_active_channels;
  chg_funk_funktype();
  ferr_val = FERR_OK;
  if(strlen(filename) < 255) strcat(filename,"*");
  funk_hr_ptr->LZH_check_size =
    sizeof(tfunk_hr) +
    (funk_info.no_of_patterns * 64 * no_active_channels * sizeof(tslot));
  for(x = 0;x < 64;x++)
    funk_hr_ptr->LZH_check_size +=
      funk_hr_ptr->funk_sb[x].length * ((funk_hr_ptr->info[3] & 0x1) + 1);
  funk_fp = fopen(filename,"wb");
  if(funk_fp == NULL)
  {
    ferr_val = FERR_FCREATE;
    return;
  }
  if(fwrite(funk_hr_ptr,sizeof(tfunk_hr),1,funk_fp) != 1)
  {
    ferr_val = FERR_FWRITE;
    return;
  }
  fnkpat_ptr = funk_pat_ptr;
  trac_c_Len = old_no_active_channels * sizeof(tslot);
  for(x = 0;x < funk_info.no_of_patterns;x++)
    for(y = 0;y < 64;y++)
    {
      for(z = 0;z < no_active_channels;z++) clear_slot(&new_pat_trek[z]);
      memcpy(&new_pat_trek,fnkpat_ptr,trac_c_Len);
      if(fwrite(&new_pat_trek,sizeof(tslot),
        no_active_channels,funk_fp) != no_active_channels)
      {
        ferr_val = FERR_FWRITE;
        return;
      }
      fnkpat_ptr += old_no_active_channels;
    }
  for(x = 0;x < 64;x++)
    if(funk_hr_ptr->funk_sb[x].length)
      if(!dsp_save_sample(funk_fp,x))
        return;
  fclose(funk_fp);
  dealloc_funk_mem();
}

/***************************************************************************
* OPTIMISE FUNK MODULE
*
* - de-fragment pattern block removing holes/gaps
* - removes used tracks
* - removes used samples
*
***************************************************************************/
void opti_funk_module(char *filename)
{
  register int pattern,treks,channels,frm_channels,x,y;
  char sample_flag[64];
  char track_flag[MAXIMUM_CHANNELS];
#pragma pack(1)
  uDB order_list[256];
  uDB break_list[128];
  tslot *funk_pat_ptr_old;
#pragma pack()

  load_funk_module(filename);
  if(ferr_val == FERR_OLD_FK)
    ferr_val = FERR_OK;
  else if(ferr_val != FERR_OK)
    return;
/*find out what's used*/
  for(x = 0;x < 64;x++) sample_flag[x] = 0;
  for(x = 0;x < MAXIMUM_CHANNELS;x++) track_flag[x] = 0;
  fnkpat_ptr = funk_pat_ptr;
  for(pattern = 0;pattern < funk_info.no_of_patterns;pattern++)
    for(treks = 0;treks < 64;treks++)
      for(channels = 0;channels < no_active_channels;channels++)
      {
        if(!((fnkpat_ptr->not_sam == 0xfc) &&
            (fnkpat_ptr->sam_com == 0x0f) &&
            (fnkpat_ptr->com_val == 0x00)))
          track_flag[channels] = 1;
        if((fnkpat_ptr->not_sam >> 2) != 0x3f)
        {
          int sample = 
            ((fnkpat_ptr->not_sam & 0x3) << 4) + (fnkpat_ptr->sam_com >> 4);
          if(funk_hr_ptr->funk_sb[sample].length)
            sample_flag[sample] = 1;
        }
        fnkpat_ptr++;
      }
/*de-fragment pattern block removing holes/gaps*/
  memcpy(&order_list,funk_hr_ptr->order_list,256);
  memcpy(&break_list,funk_hr_ptr->break_list,128);
  for(x = 0;x < 256;x++) funk_hr_ptr->order_list[x] = 0xff;
  for(x = 0;x < 128;x++) funk_hr_ptr->break_list[x] = 0x3f;
  funk_pat_ptr_old = funk_pat_ptr;
  ferr_val = alloc_pat_blk();
  if(ferr_val == FERR_OK)
  {
    register int pat_size = 64 * no_active_channels;
    register int pattern_count = 0;
    for(x = 0;x <= funk_info.no_of_sequences;x++)
    {
      register int c_ol = order_list[x];
      if(c_ol != 0xff)
      {
        memcpy(
          funk_pat_ptr + (pat_size * pattern_count),
          funk_pat_ptr_old + (pat_size * c_ol),
          pat_size * sizeof(tslot));
        for(y = 0;y <= funk_info.no_of_sequences;y++)
          if(order_list[y] == c_ol)
          {
            funk_hr_ptr->order_list[y] = pattern_count;
            order_list[y] = 0xff;
          }
        funk_hr_ptr->break_list[pattern_count] = break_list[c_ol];
        pattern_count++;
      }
    }
    free(funk_pat_ptr_old);
  }
  else
    return;
  find_pats_seqs();
/*removes used tracks*/
  y = 0;
  for(channels = 0;channels < no_active_channels;channels++)
    if(!track_flag[channels])
    {
      if(channels == no_active_channels) return;
      for(frm_channels = (channels + 1);frm_channels < no_active_channels;frm_channels++)
        if(track_flag[frm_channels])
        {
          fnkpat_ptr = funk_pat_ptr + channels;
          funk_pat_ptr_old = funk_pat_ptr + frm_channels;
          for(x = 0;x < (funk_info.funk_pd_size * 64);x++)
          {
            memcpy(fnkpat_ptr,funk_pat_ptr_old,3);
            clear_slot(funk_pat_ptr_old);
            fnkpat_ptr += no_active_channels;
            funk_pat_ptr_old += no_active_channels;
          }
          track_flag[channels] = 1;
          track_flag[frm_channels] = 0;
          y++;
          break;
        }
    }
    else
      y++;
  if(y < 4) y = 4;
/*removes used samples*/
  for(x = 0;x < 64;x++)
  {
    if(!sample_flag[x])
    {
      funk_hr_ptr->funk_sb[x].start = 0xffffffff;
      funk_hr_ptr->funk_sb[x].length = 0;
      free(funk_sam_ptrs[x]);
      funk_sam_ptrs[x] = NULL;
    }
  }
/*save optimised module*/
  save_ntrac_funk_module(filename,y);
}

/***************************************************************************
* 8 -> 16 -> 8 bit module converter
*
* to_type : 0 = to 8 bit
*           1 = to 16 bit
***************************************************************************/
void convprec_funk_module(char *filename,int to_type)
{
  register int x = 0;
  register long pattern_block_size;

  load_funk_module(filename);
  if(ferr_val == FERR_OLD_FK)
    ferr_val = FERR_OK;
  else if(ferr_val != FERR_OK)
    return;
  if(funk_hr_ptr->info[3] & 1)
  {
    if(to_type)
    {
      ferr_val = FERR_PREC_NN;
      return;
    }
  }
  else
  {
    if(!to_type)
    {
      ferr_val = FERR_PREC_NN;
      return;
    }
  }
  if(strlen(filename) < 255) strcat(filename,"*");
  funk_hr_ptr->info[3] = to_type;
  set_BPM();
  chg_funk_funktype();
  pattern_block_size =
    funk_info.no_of_patterns * 64 * no_active_channels;
  funk_hr_ptr->LZH_check_size =
    sizeof(tfunk_hr) + (pattern_block_size * sizeof(tslot));
  for(x = 0;x < 64;x++)
    funk_hr_ptr->LZH_check_size +=
      funk_hr_ptr->funk_sb[x].length * ((funk_hr_ptr->info[3] & 0x1) + 1);
  if((funk_fp = fopen(filename,"wb")) == NULL)
  {
    ferr_val = FERR_FCREATE;
    return;
  }
  if(fwrite(funk_hr_ptr,sizeof(tfunk_hr),1,funk_fp) != 1)
  {
    ferr_val = FERR_FWRITE;
    return;
  }
  if(fwrite(funk_pat_ptr,sizeof(tslot),
     pattern_block_size,funk_fp) != pattern_block_size)
  {
    ferr_val = FERR_FWRITE;
    return;
  }
  for(x = 0;x < 64;x++)
    if(funk_hr_ptr->funk_sb[x].length)
    {
#pragma pack(1)
      void *new_sam_ptr;
#pragma pack()
      register unsigned long samsize =
        funk_hr_ptr->funk_sb[x].length << to_type;
      register unsigned long samno;

      if((new_sam_ptr = malloc(samsize)) != NULL)
      {
        for(samno = 0;samno < funk_hr_ptr->funk_sb[x].length;samno++)
#pragma pack(1)
          if(to_type == 1)
            *(sDW *)((sDW *)new_sam_ptr + (unsigned int)samno) =
              *(sDW *)((sDB *)(funk_sam_ptrs[x]) + samno) << 8;
          else
            *((sDB *)new_sam_ptr + samno) =
              *((sDW *)funk_sam_ptrs[x] + samno) >> 8;
#pragma pack()
        if(fwrite(new_sam_ptr,1,samsize,funk_fp) != samsize)
        {
          ferr_val = FERR_FWRITE;
          return;
        }
        free(new_sam_ptr);
      }
      else
      {
        ferr_val = FERR_MALLOC;
        return;
      }
    }
  fclose(funk_fp);
  dealloc_funk_mem();
}
