/*
	gus_pat.c

 	Midi Wavetable Processing library

    Copyright (C) Chris Ison 2001-2011

    This file is part of WildMIDI.

    WildMIDI is free software: you can redistribute and/or modify the player
    under the terms of the GNU General Public License and you can redistribute
    and/or modify the library under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation, either version 3 of
    the licenses, or(at your option) any later version.

    WildMIDI is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and
    the GNU Lesser General Public License for more details.

    You should have received a copy of the GNU General Public License and the
    GNU Lesser General Public License along with WildMIDI.  If not,  see
    <http://www.gnu.org/licenses/>.

    Email: wildcode@users.sourceforge.net
*/

#include "config.h"

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

#include "gus_pat.h"
#include "common.h"
#include "wm_error.h"
#include "file_io.h"

#ifdef DEBUG_GUSPAT
#define GUSPAT_FILENAME_DEBUG(dx) fprintf(stderr,"\r%s\n",dx)

#define GUSPAT_INT_DEBUG(dx,dy) fprintf(stderr,"\r%s: %i\n",dx,dy)
#define GUSPAT_FLOAT_DEBUG(dx,dy) fprintf(stderr,"\r%s: %f\n",dx,dy)
#define GUSPAT_START_DEBUG() fprintf(stderr,"\r")
#define GUSPAT_MODE_DEBUG(dx,dy,dz) if (dx & dy) fprintf(stderr,"%s",dz)
#define GUSPAT_END_DEBUG() fprintf(stderr,"\n")
#else
#define GUSPAT_FILENAME_DEBUG(dx)
#define GUSPAT_INT_DEBUG(dx,dy)
#define GUSPAT_FLOAT_DEBUG(dx,dy)
#define GUSPAT_START_DEBUG()
#define GUSPAT_MODE_DEBUG(dx,dy,dz)
#define GUSPAT_END_DEBUG()
#endif


/*
 * sample data conversion functions
 * convert data to signed shorts
 */

/* 8bit signed */
int
convert_8s (unsigned char *data, struct _sample *gus_sample ) {
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->data_length;
	signed short int *write_data = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc((gus_sample->data_length + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data++ = (*read_data++) << 8;
		} while (read_data != read_end);
		return 0;
	}

    WM_ERROR_NEW("(%s:%i) ERROR: calloc failed (%s)", __FUNCTION__, __LINE__, strerror(errno));
//	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 8bit signed ping pong */
int
convert_8sp (unsigned char *data, struct _sample *gus_sample ) {
	unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start;
	unsigned long int dloop_length = loop_length * 2;
	unsigned long int new_length = gus_sample->data_length + dloop_length;
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->loop_start;
	signed short int *write_data = NULL;
	signed short int *write_data_a = NULL;
	signed short int *write_data_b = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc((new_length + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data++ = (*read_data++) << 8;
		} while (read_data != read_end);

		*write_data = (*read_data++ << 8);
		write_data_a = write_data + dloop_length;
		*write_data_a-- = *write_data;
		write_data++;
		write_data_b = write_data + dloop_length;
		read_end = data + gus_sample->loop_end;
		do {
			*write_data = (*read_data++) << 8;
			*write_data_a-- = *write_data;
			*write_data_b++ = *write_data;
			write_data++;
		} while (read_data != read_end);

		*write_data = (*read_data++ << 8);
		*write_data_b++ = *write_data;
		read_end = data + gus_sample->data_length;
		if (__builtin_expect((read_data != read_end),1)) {
			do {
				*write_data_b++ = (*read_data++) << 8;
			} while (read_data != read_end);
		}
		gus_sample->loop_start += loop_length;
		gus_sample->loop_end += dloop_length;
		gus_sample->data_length = new_length;
		gus_sample->modes ^= SAMPLE_PINGPONG;
		return 0;
	}

	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 8bit signed reverse */
int
convert_8sr (unsigned char *data, struct _sample *gus_sample ) {
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->data_length;
	signed short int *write_data = NULL;
	unsigned long int tmp_loop = 0;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc((gus_sample->data_length + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data + gus_sample->data_length - 1;
		do {
			*write_data-- = (*read_data++) << 8;
		} while (read_data != read_end);
		tmp_loop = gus_sample->loop_end;
		gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start;
		gus_sample->loop_start = gus_sample->data_length - tmp_loop;
		gus_sample->loop_fraction  = ((gus_sample->loop_fraction & 0x0f) << 4) | ((gus_sample->loop_fraction & 0xf0) >> 4);
		gus_sample->modes ^= SAMPLE_REVERSE;
		return 0;
	}
	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}


/* 8bit signed reverse ping pong */
int
convert_8srp (unsigned char *data, struct _sample *gus_sample ) {
	unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start;
	unsigned long int dloop_length = loop_length * 2;
	unsigned long int new_length = gus_sample->data_length + dloop_length;
	unsigned char *read_data = data + gus_sample->data_length - 1;
	unsigned char *read_end = data + gus_sample->loop_end;
	signed short int *write_data = NULL;
	signed short int *write_data_a = NULL;
	signed short int *write_data_b = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc((new_length + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data++ = (*read_data--) << 8;
		} while (read_data != read_end);

		*write_data = (*read_data-- << 8);
		write_data_a = write_data + dloop_length;
		*write_data_a-- = *write_data;
		write_data++;
		write_data_b = write_data + dloop_length;
		read_end = data + gus_sample->loop_start;
		do {
			*write_data = (*read_data--) << 8;
			*write_data_a-- = *write_data;
			*write_data_b++ = *write_data;
			write_data++;
		} while (read_data != read_end);

		*write_data = (*read_data-- << 8);
		*write_data_b++ = *write_data;
		read_end = data - 1;
		do {
			*write_data_b++ = (*read_data--) << 8;
			write_data_b++;
		} while (read_data != read_end);
		gus_sample->loop_start += loop_length;
		gus_sample->loop_end += dloop_length;
		gus_sample->data_length = new_length;
		gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE;
		return 0;
	}

	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 8bit unsigned */
int
convert_8u (unsigned char *data, struct _sample *gus_sample ) {
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->data_length;
	signed short int *write_data = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc((gus_sample->data_length + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data++ = ((*read_data++) ^ 0x80) << 8;
		} while (read_data != read_end);
		gus_sample->modes ^= SAMPLE_UNSIGNED;
		return 0;
	}
	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 8bit unsigned ping pong */
int
convert_8up (unsigned char *data, struct _sample *gus_sample ) {
	unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start;
	unsigned long int dloop_length = loop_length * 2;
	unsigned long int new_length = gus_sample->data_length + dloop_length;
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->loop_start;
	signed short int *write_data = NULL;
	signed short int *write_data_a = NULL;
	signed short int *write_data_b = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc((new_length + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data++ = ((*read_data++) ^ 0x80) << 8;
		} while (read_data != read_end);

		*write_data = ((*read_data++) ^ 0x80) << 8;
		write_data_a = write_data + dloop_length;
		*write_data_a-- = *write_data;
		write_data++;
		write_data_b = write_data + dloop_length;
		read_end = data + gus_sample->loop_end;
		do {
			*write_data = ((*read_data++) ^ 0x80) << 8;
			*write_data_a-- = *write_data;
			*write_data_b++ = *write_data;
			write_data++;
		} while (read_data != read_end);

		*write_data = ((*read_data++) ^ 0x80) << 8;
		*write_data_b++ = *write_data;
		read_end = data + gus_sample->data_length;
		if (__builtin_expect((read_data != read_end),1)) {
			do {
				*write_data_b++ = ((*read_data++) ^ 0x80) << 8;
			} while (read_data != read_end);
		}
		gus_sample->loop_start += loop_length;
		gus_sample->loop_end += dloop_length;
		gus_sample->data_length = new_length;
		gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_UNSIGNED;
		return 0;
	}

	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 8bit unsigned reverse */
int
convert_8ur (unsigned char *data, struct _sample *gus_sample ) {
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->data_length;
	signed short int *write_data = NULL;
	unsigned long int tmp_loop = 0;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc((gus_sample->data_length + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data + gus_sample->data_length - 1;
		do {
			*write_data-- = ((*read_data++) ^ 0x80) << 8;
		} while (read_data != read_end);
		tmp_loop = gus_sample->loop_end;
		gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start;
		gus_sample->loop_start = gus_sample->data_length - tmp_loop;
		gus_sample->loop_fraction  = ((gus_sample->loop_fraction & 0x0f) << 4) | ((gus_sample->loop_fraction & 0xf0) >> 4);
		gus_sample->modes ^= SAMPLE_REVERSE | SAMPLE_UNSIGNED;
		return 0;
	}
	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 8bit unsigned reverse ping pong */
int
convert_8urp (unsigned char *data, struct _sample *gus_sample ) {
	unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start;
	unsigned long int dloop_length = loop_length * 2;
	unsigned long int new_length = gus_sample->data_length + dloop_length;
	unsigned char *read_data = data + gus_sample->data_length - 1;
	unsigned char *read_end = data + gus_sample->loop_end;
	signed short int *write_data = NULL;
	signed short int *write_data_a = NULL;
	signed short int *write_data_b = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc((new_length + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data++ = ((*read_data--) ^ 0x80) << 8;
		} while (read_data != read_end);

		*write_data = ((*read_data--) ^ 0x80) << 8;
		write_data_a = write_data + dloop_length;
		*write_data_a-- = *write_data;
		write_data++;
		write_data_b = write_data + dloop_length;
		read_end = data + gus_sample->loop_start;
		do {
			*write_data = ((*read_data--) ^ 0x80) << 8;
			*write_data_a-- = *write_data;
			*write_data_b++ = *write_data;
			write_data++;
		} while (read_data != read_end);

		*write_data = ((*read_data--) ^ 0x80) << 8;
		*write_data_b++ = *write_data;
		read_end = data - 1;
		do {
			*write_data_b++ = ((*read_data--) ^ 0x80) << 8;
		} while (read_data != read_end);
		gus_sample->loop_start += loop_length;
		gus_sample->loop_end += dloop_length;
		gus_sample->data_length = new_length;
		gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE | SAMPLE_UNSIGNED;
		return 0;
	}

	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 16bit signed */
int
convert_16s (unsigned char *data, struct _sample *gus_sample ) {
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->data_length;
	signed short int *write_data = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc(((gus_sample->data_length >> 1) + 1),sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data = *read_data++;
			*write_data++ |= (*read_data++) << 8;
		} while (read_data < read_end);

		gus_sample->loop_start >>= 1;
		gus_sample->loop_end >>= 1;
		gus_sample->data_length >>= 1;
		return 0;
	}
	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 16bit signed ping pong */
int
convert_16sp (unsigned char *data, struct _sample *gus_sample ) {
	unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start;
	unsigned long int dloop_length = loop_length * 2;
	unsigned long int new_length = gus_sample->data_length + dloop_length;
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->loop_start;
	signed short int *write_data = NULL;
	signed short int *write_data_a = NULL;
	signed short int *write_data_b = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc(((new_length >> 1) + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data = (*read_data++);
			*write_data++ |= (*read_data++) << 8;
		} while (read_data < read_end);

		*write_data = (*read_data++);
		*write_data |= (*read_data++) << 8;
		write_data_a = write_data + (dloop_length >> 1);
		*write_data_a-- = *write_data;
		write_data++;
		write_data_b = write_data + (dloop_length >> 1);
		read_end = data + gus_sample->loop_end;
		do {
			*write_data = (*read_data++);
			*write_data |= (*read_data++) << 8;
			*write_data_a-- = *write_data;
			*write_data_b++ = *write_data;
			write_data++;
		} while (read_data < read_end);

		*write_data = *(read_data++);
		*write_data |= (*read_data++) << 8;
		*write_data_b++ = *write_data;
		read_end = data + gus_sample->data_length;
		if (__builtin_expect((read_data != read_end),1)) {
			do {
				*write_data_b = *(read_data++);
				*write_data_b++ |= (*read_data++) << 8;
			} while (read_data < read_end);
		}
		gus_sample->loop_start += loop_length;
		gus_sample->loop_end += dloop_length;
		gus_sample->data_length = new_length;
		gus_sample->modes ^= SAMPLE_PINGPONG;
		gus_sample->loop_start >>= 1;
		gus_sample->loop_end >>= 1;
		gus_sample->data_length >>= 1;
		return 0;
	}

	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 16bit signed reverse */
int
convert_16sr (unsigned char *data, struct _sample *gus_sample ) {
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->data_length;
	signed short int *write_data = NULL;
	unsigned long int tmp_loop = 0;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc(((gus_sample->data_length >> 1) + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data + (gus_sample->data_length >> 1) - 1;
		do {
			*write_data = *read_data++;
			*write_data-- |= (*read_data++) << 8;
		} while (read_data < read_end);
		tmp_loop = gus_sample->loop_end;
		gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start;
		gus_sample->loop_start = gus_sample->data_length - tmp_loop;
		gus_sample->loop_fraction  = ((gus_sample->loop_fraction & 0x0f) << 4) | ((gus_sample->loop_fraction & 0xf0) >> 4);
		gus_sample->loop_start >>= 1;
		gus_sample->loop_end >>= 1;
		gus_sample->data_length >>= 1;
		gus_sample->modes ^= SAMPLE_REVERSE;
		return 0;
	}
	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}


/* 16bit signed reverse ping pong */
int
convert_16srp (unsigned char *data, struct _sample *gus_sample ) {
	unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start;
	unsigned long int dloop_length = loop_length * 2;
	unsigned long int new_length = gus_sample->data_length + dloop_length;
	unsigned char *read_data = data + gus_sample->data_length - 1;
	unsigned char *read_end = data + gus_sample->loop_end;
	signed short int *write_data = NULL;
	signed short int *write_data_a = NULL;
	signed short int *write_data_b = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc(((new_length >> 1) + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {

			*write_data = (*read_data--) << 8;
			*write_data++ |= *read_data--;
		} while (read_data < read_end);

		*write_data = (*read_data-- << 8);
		*write_data |= *read_data--;
		write_data_a = write_data + (dloop_length >> 1);
		*write_data_a-- = *write_data;
		write_data++;
		write_data_b = write_data + (dloop_length >> 1);
		read_end = data + gus_sample->loop_start;
		do {
			*write_data = (*read_data--) << 8;
			*write_data |= *read_data--;
			*write_data_a-- = *write_data;
			*write_data_b++ = *write_data;
			write_data++;
		} while (read_data < read_end);

		*write_data = ((*read_data--) << 8);
		*write_data |= *read_data--;
		*write_data_b++ = *write_data;
		read_end = data - 1;
		do {
			*write_data_b = (*read_data--) << 8;
			*write_data_b++ |= *read_data--;
		} while (read_data < read_end);
		gus_sample->loop_start += loop_length;
		gus_sample->loop_end += dloop_length;
		gus_sample->data_length = new_length;
		gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE;
		return 0;
	}

	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 16bit unsigned */
int
convert_16u (unsigned char *data, struct _sample *gus_sample ) {
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->data_length;
	signed short int *write_data = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc(((gus_sample->data_length >> 1) + 1),sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data = *read_data++;
			*write_data++ |= ((*read_data++) ^ 0x80) << 8;
		} while (read_data < read_end);
		gus_sample->loop_start >>= 1;
		gus_sample->loop_end >>= 1;
		gus_sample->data_length >>= 1;
		gus_sample->modes ^= SAMPLE_UNSIGNED;
		return 0;
	}
	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 16bit unsigned ping pong */
int
convert_16up (unsigned char *data, struct _sample *gus_sample ) {
	unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start;
	unsigned long int dloop_length = loop_length * 2;
	unsigned long int new_length = gus_sample->data_length + dloop_length;
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->loop_start;
	signed short int *write_data = NULL;
	signed short int *write_data_a = NULL;
	signed short int *write_data_b = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc(((new_length >> 1) + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data = (*read_data++);
			*write_data++ |= ((*read_data++) ^ 0x80) << 8;
		} while (read_data < read_end);

		*write_data = (*read_data++);
		*write_data |= ((*read_data++) ^ 0x80) << 8;
		write_data_a = write_data + (dloop_length >> 1);
		*write_data_a-- = *write_data;
		write_data++;
		write_data_b = write_data + (dloop_length >> 1);
		read_end = data + gus_sample->loop_end;
		do {
			*write_data = (*read_data++);
			*write_data |= ((*read_data++) ^ 0x80) << 8;
			*write_data_a-- = *write_data;
			*write_data_b++ = *write_data;
			write_data++;
		} while (read_data < read_end);

		*write_data = (*read_data++);
		*write_data |= ((*read_data++) ^ 0x80) << 8;
		*write_data_b++ = *write_data;
		read_end = data + gus_sample->data_length;
		if (__builtin_expect((read_data != read_end),1)) {
			do {
				*write_data_b = (*read_data++);
				*write_data_b++ |= ((*read_data++) ^ 0x80) << 8;
			} while (read_data < read_end);
		}
		gus_sample->loop_start += loop_length;
		gus_sample->loop_end += dloop_length;
		gus_sample->data_length = new_length;
		gus_sample->modes ^= SAMPLE_PINGPONG;
		gus_sample->loop_start >>= 1;
		gus_sample->loop_end >>= 1;
		gus_sample->data_length >>= 1;
		return 0;
	}

	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 16bit unsigned reverse */
int
convert_16ur (unsigned char *data, struct _sample *gus_sample ) {
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->data_length;
	signed short int *write_data = NULL;
	unsigned long int tmp_loop = 0;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc(((gus_sample->data_length >> 1) + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data + (gus_sample->data_length >> 1) - 1;
		do {
			*write_data = *read_data++;
			*write_data-- |= ((*read_data++) ^ 0x80) << 8;
		} while (read_data < read_end);
		tmp_loop = gus_sample->loop_end;
		gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start;
		gus_sample->loop_start = gus_sample->data_length - tmp_loop;
		gus_sample->loop_fraction  = ((gus_sample->loop_fraction & 0x0f) << 4) | ((gus_sample->loop_fraction & 0xf0) >> 4);
		gus_sample->loop_start >>= 1;
		gus_sample->loop_end >>= 1;
		gus_sample->data_length >>= 1;
		gus_sample->modes ^= SAMPLE_REVERSE | SAMPLE_UNSIGNED;
		return 0;
	}
	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 16bit unsigned reverse ping pong */
int
convert_16urp (unsigned char *data, struct _sample *gus_sample ) {
	unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start;
	unsigned long int dloop_length = loop_length * 2;
	unsigned long int new_length = gus_sample->data_length + dloop_length;
	unsigned char *read_data = data + gus_sample->data_length - 1;
	unsigned char *read_end = data + gus_sample->loop_end;
	signed short int *write_data = NULL;
	signed short int *write_data_a = NULL;
	signed short int *write_data_b = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc(((new_length >> 1) + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data = ((*read_data--) ^ 0x80) << 8;
			*write_data++ |= *read_data--;
		} while (read_data < read_end);

		*write_data = ((*read_data--) ^ 0x80) << 8;
		*write_data |= *read_data--;
		write_data_a = write_data + (dloop_length >> 1);
		*write_data_a-- = *write_data;
		write_data++;
		write_data_b = write_data + (dloop_length >> 1);
		read_end = data + gus_sample->loop_start;
		do {
			*write_data = ((*read_data--) ^ 0x80) << 8;
			*write_data |= *read_data--;
			*write_data_a-- = *write_data;
			*write_data_b++ = *write_data;
			write_data++;
		} while (read_data < read_end);

		*write_data = ((*read_data--) ^ 0x80) << 8;
		*write_data |= *read_data--;
		*write_data_b++ = *write_data;
		read_end = data - 1;
		do {
			*write_data_b = ((*read_data--) ^ 0x80) << 8;
			*write_data_b++ |= *read_data--;
		} while (read_data < read_end);
		gus_sample->loop_start += loop_length;
		gus_sample->loop_end += dloop_length;
		gus_sample->data_length = new_length;
		gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE | SAMPLE_UNSIGNED;
		return 0;
	}

	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}



/* sample loading */

struct _sample * load_gus_pat (char * filename, int fix_release) {

	unsigned char *gus_patch;
	unsigned long int gus_size;
	unsigned long int gus_ptr;
	unsigned char no_of_samples;
	struct _sample *gus_sample = NULL;
	struct _sample *first_gus_sample = NULL;
	unsigned long int i = 0;

	int (*do_convert[])(unsigned char *data, struct _sample *gus_sample ) = {
		convert_8s,
		convert_16s,
		convert_8u,
		convert_16u,
		convert_8sp,
		convert_16sp,
		convert_8up,
		convert_16up,
		convert_8sr,
		convert_16sr,
		convert_8ur,
		convert_16ur,
		convert_8srp,
		convert_16srp,
		convert_8urp,
		convert_16urp
	};
	unsigned long int tmp_loop;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	SAMPLE_CONVERT_DEBUG(filename);

	if ((gus_patch = WM_BufferFile(filename,&gus_size)) == NULL) {
		return NULL;
	}
	if (gus_size < 239) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0);
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0);
		free(gus_patch);
		return NULL;
	}
	if (memcmp(gus_patch, "GF1PATCH110\0ID#000002", 22) && memcmp(gus_patch, "GF1PATCH100\0ID#000002", 22)) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID,"(unsupported format)", 0);
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0);
		free(gus_patch);
		return NULL;
	}
	if (gus_patch[82] > 1) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID,"(unsupported format)", 0);
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0);
		free(gus_patch);
		return NULL;
	}
	if (gus_patch[151] > 1) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID,"(unsupported format)", 0);
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0);
		free(gus_patch);
		return NULL;
	}

    GUSPAT_FILENAME_DEBUG(filename);
    GUSPAT_INT_DEBUG("voices",gus_patch[83]);

	no_of_samples = gus_patch[198];
	gus_ptr = 239;
	while (no_of_samples) {
		unsigned long int tmp_cnt;
		if (first_gus_sample == NULL) {
			first_gus_sample = malloc(sizeof(struct _sample));
			gus_sample = first_gus_sample;
		} else {
			gus_sample->next = malloc(sizeof(struct _sample));
			gus_sample = gus_sample->next;
		}
		if (gus_sample == NULL) {
			WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0);
			WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, 0);
			free(gus_patch);
			return NULL;
		}

		gus_sample->next = NULL;
		gus_sample->loop_fraction = gus_patch[gus_ptr+7];
		gus_sample->data_length = (gus_patch[gus_ptr+11] << 24) | (gus_patch[gus_ptr+10] << 16) | (gus_patch[gus_ptr+9] << 8) | gus_patch[gus_ptr+8];
		gus_sample->loop_start = (gus_patch[gus_ptr+15] << 24) | (gus_patch[gus_ptr+14] << 16) | (gus_patch[gus_ptr+13] << 8) | gus_patch[gus_ptr+12];
		gus_sample->loop_end = (gus_patch[gus_ptr+19] << 24) | (gus_patch[gus_ptr+18] << 16) | (gus_patch[gus_ptr+17] << 8) | gus_patch[gus_ptr+16];
		gus_sample->rate = (gus_patch[gus_ptr+21] << 8) | gus_patch[gus_ptr+20];
		gus_sample->freq_low = ((gus_patch[gus_ptr+25] << 24) | (gus_patch[gus_ptr+24] << 16) | (gus_patch[gus_ptr+23] << 8) | gus_patch[gus_ptr+22]);
		gus_sample->freq_high = ((gus_patch[gus_ptr+29] << 24) | (gus_patch[gus_ptr+28] << 16) | (gus_patch[gus_ptr+27] << 8) | gus_patch[gus_ptr+26]);
		gus_sample->freq_root = ((gus_patch[gus_ptr+33] << 24) | (gus_patch[gus_ptr+32] << 16) | (gus_patch[gus_ptr+31] << 8) | gus_patch[gus_ptr+30]);

		/* This is done this way instead of ((freq * 1024) / rate) to avoid 32bit overflow. */
		/* Result is 0.001% inacurate */
		gus_sample->inc_div = ((gus_sample->freq_root * 512) / gus_sample->rate) * 2;

#if 0
        // We dont use this info at this time ... kept in here for info
		printf("\rTremolo Sweep: %i, Rate: %i, Depth %i\n",
			gus_patch[gus_ptr+49], gus_patch[gus_ptr+50], gus_patch[gus_ptr+51]);
		printf("\rVibrato Sweep: %i, Rate: %i, Depth %i\n",
			gus_patch[gus_ptr+52], gus_patch[gus_ptr+53], gus_patch[gus_ptr+54]);
#endif
		gus_sample->modes = gus_patch[gus_ptr+55];
        GUSPAT_START_DEBUG();
        GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_16BIT, "16bit ");
        GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_UNSIGNED, "Unsigned ");
        GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_LOOP, "Loop ");
        GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_PINGPONG, "PingPong ");
        GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_REVERSE, "Reverse ");
        GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_SUSTAIN, "Sustain ");
        GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_ENVELOPE, "Envelope ");
        GUSPAT_MODE_DEBUG(gus_patch[gus_ptr+55], SAMPLE_CLAMPED, "Clamped ");
        GUSPAT_END_DEBUG();

		if (gus_sample->loop_start > gus_sample->loop_end) {
			tmp_loop = gus_sample->loop_end;
			gus_sample->loop_end = gus_sample->loop_start;
			gus_sample->loop_start = tmp_loop;
			gus_sample->loop_fraction  = ((gus_sample->loop_fraction & 0x0f) << 4) | ((gus_sample->loop_fraction & 0xf0) >> 4);
		}


/*
    FIXME: Experimental Hacky Fix
*/

        if (fix_release) {
            if (env_time_table[gus_patch[gus_ptr+40]] < env_time_table[gus_patch[gus_ptr+41]]) {
                unsigned char tmp_hack_rate = gus_patch[gus_ptr+41];
                gus_patch[gus_ptr+41] = gus_patch[gus_ptr+40];
                gus_patch[gus_ptr+40] = tmp_hack_rate;
            }
        }

		for (i = 0; i < 6; i++) {
			if (gus_sample->modes & SAMPLE_ENVELOPE) {
				unsigned char env_rate = gus_patch[gus_ptr+37+i];
				gus_sample->env_target[i] = 16448 * gus_patch[gus_ptr+43+i];
                GUSPAT_INT_DEBUG("Envelope Level",gus_patch[gus_ptr+43+i]);
				GUSPAT_FLOAT_DEBUG("Envelope Time",env_time_table[env_rate]);
				gus_sample->env_rate[i]  = (unsigned long int)(4194303.0 / ((float)WM_SampleRate * env_time_table[env_rate]));

					if (gus_sample->env_rate[i] == 0) {
						fprintf(stderr,"\rWarning: libWildMidi %s found invalid envelope(%lu) rate setting in %s. Using %f instead.\n",__FUNCTION__, i, filename, env_time_table[63]);
						gus_sample->env_rate[i]  = (unsigned long int)(4194303.0 / ((float)WM_SampleRate * env_time_table[63]));
                GUSPAT_FLOAT_DEBUG("Envelope Time",env_time_table[63]);
					}
			} else {
				gus_sample->env_target[i] = 4194303;
				gus_sample->env_rate[i]  = (unsigned long int)(4194303.0 / ((float)WM_SampleRate * env_time_table[63]));
                GUSPAT_FLOAT_DEBUG("Envelope Time",env_time_table[63]);
			}
		}

		gus_sample->env_target[6] = 0;
		gus_sample->env_rate[6]  = (unsigned long int)(4194303.0 / ((float)WM_SampleRate * env_time_table[63]));

		gus_ptr += 96;
		tmp_cnt = gus_sample->data_length;

		if (do_convert[(((gus_sample->modes & 0x18) >> 1)| (gus_sample->modes & 0x03))](&gus_patch[gus_ptr], gus_sample) == -1) {
            free(gus_patch);
			return NULL;
		}

		gus_ptr += tmp_cnt;
		gus_sample->loop_start = (gus_sample->loop_start << 10) | (((gus_sample->loop_fraction & 0x0f) << 10) / 16);
		gus_sample->loop_end = (gus_sample->loop_end << 10) | (((gus_sample->loop_fraction & 0xf0) << 6) / 16);
		gus_sample->loop_size = gus_sample->loop_end - gus_sample->loop_start;
		gus_sample->data_length = gus_sample->data_length << 10;
		no_of_samples--;
	}
	free(gus_patch);
	return first_gus_sample;
}
