code:
#ifndef LIBXMP_LIST_H
#define LIBXMP_LIST_H

#include <stddef.h> /* offsetof */

#ifdef _MSC_VER
#define __inline__ __inline
#endif
#ifdef __WATCOMC__
#define __inline__   inline
#endif

/*
 * Simple doubly linked list implementation.
 *
 * Some of the internal functions ("__xxx") are useful when
 * manipulating whole lists rather than single entries, as
 * sometimes we already know the next/prev entries and we can
 * generate better code by using them directly rather than
 * using the generic single-entry routines.
 */

struct list_head {
	struct list_head *next, *prev;
};

#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \
	struct list_head name = LIST_HEAD_INIT(name)

#define INIT_LIST_HEAD(ptr) do { \
	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)

/*
 * Insert a new entry between two known consecutive entries.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static __inline__ void __list_add(struct list_head *_new,
	struct list_head * prev,
	struct list_head * next)
{
	next->prev = _new;
	_new->next = next;
	_new->prev = prev;
	prev->next = _new;
}

/**
 * list_add - add a new entry
 * @_new: new entry to be added
 * @head: list head to add it after
 *
 * Insert a new entry after the specified head.
 * This is good for implementing stacks.
 */
static __inline__ void list_add(struct list_head *_new, struct list_head *head)
{
	__list_add(_new, head, head->next);
}

/**
 * list_add_tail - add a new entry
 * @_new: new entry to be added
 * @head: list head to add it before
 *
 * Insert a new entry before the specified head.
 * This is useful for implementing queues.
 */
static __inline__ void list_add_tail(struct list_head *_new, struct list_head *head)
{
	__list_add(_new, head->prev, head);
}

/*
 * Delete a list entry by making the prev/next entries
 * point to each other.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static __inline__ void __list_del(struct list_head * prev,
				  struct list_head * next)
{
	next->prev = prev;
	prev->next = next;
}

/**
 * list_del - deletes entry from list.
 * @entry: the element to delete from the list.
 */
static __inline__ void list_del(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
}

/**
 * list_empty - tests whether a list is empty
 * @head: the list to test.
 */
static __inline__ int list_empty(struct list_head *head)
{
	return head->next == head;
}

/**
 * list_splice - join two lists
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 */
static __inline__ void list_splice(struct list_head *list, struct list_head *head)
{
	struct list_head *first = list->next;

	if (first != list) {
		struct list_head *last = list->prev;
		struct list_head *at = head->next;

		first->prev = head;
		head->next = first;

		last->next = at;
		at->prev = last;
	}
}

/**
 * list_entry - get the struct for this entry
 * @ptr:	the &struct list_head pointer.
 * @type:	the type of the struct this is embedded in.
 * @member:	the name of the list_struct within the struct.
 */
#define list_entry(ptr, type, member) \
	((type *)((char *)(ptr) - offsetof(type, member)))

/**
 * list_for_each	-	iterate over a list
 * @pos:	the &struct list_head to use as a loop counter.
 * @head:	the head for your list.
 */
#define list_for_each(pos, head) \
	for (pos = (head)->next; pos != (head); pos = pos->next)

#endif


code:
/* Extended Module Player
 * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <sys/stat.h>
#include <errno.h>

#include "format.h"
#include "list.h"
#include "hio.h"
#ifdef EDUKE32_DISABLED
#include "tempfile.h"
#endif // EDUKE32_DISABLED
#include "loader.h"

#ifndef LIBXMP_NO_DEPACKERS
#include "depackers/depacker.h"
#endif

#ifndef LIBXMP_CORE_PLAYER
#include "md5.h"
#include "extras.h"
#endif

extern struct format_loader *format_loaders[];

void libxmp_load_prologue(struct context_data *);
void libxmp_load_epilogue(struct context_data *);
int  libxmp_prepare_scan(struct context_data *);

#ifndef LIBXMP_CORE_PLAYER
#define BUFLEN 16384
#endif

#ifndef LIBXMP_CORE_PLAYER
static void set_md5sum(HIO_HANDLE *f, unsigned char *digest)
{
	unsigned char buf[BUFLEN];
	MD5_CTX ctx;
	int bytes_read;

	hio_seek(f, 0, SEEK_SET);

	MD5Init(&ctx);
	while ((bytes_read = hio_read(buf, 1, BUFLEN, f)) > 0) {
		MD5Update(&ctx, buf, bytes_read);
	}
	MD5Final(digest, &ctx);
}

static char *get_dirname(const char *name)
{
	char *dirname;
	const char *p;
	ptrdiff_t len;

	if ((p = strrchr(name, '/')) != NULL) {
		len = p - name + 1;
		dirname = malloc(len + 1);
		if (dirname != NULL) {
			memcpy(dirname, name, len);
			dirname[len] = 0;
		}
	} else {
		dirname = strdup("");
	}

	return dirname;
}

static char *get_basename(const char *name)
{
	const char *p;
	char *basename;

	if ((p = strrchr(name, '/')) != NULL) {
		basename = strdup(p + 1);
	} else {
		basename = strdup(name);
	}

	return basename;
}
#endif /* LIBXMP_CORE_PLAYER */

static int test_module(struct xmp_test_info *info, HIO_HANDLE *h)
{
	char buf[XMP_NAME_SIZE];
	int i;

	if (info != NULL) {
		*info->name = 0;	/* reset name prior to testing */
		*info->type = 0;	/* reset type prior to testing */
	}

	for (i = 0; format_loaders[i] != NULL; i++) {
		hio_seek(h, 0, SEEK_SET);
		if (format_loaders[i]->test(h, buf, 0) == 0) {
			int is_prowizard = 0;

#if !defined(LIBXMP_CORE_PLAYER) && !defined(LIBXMP_NO_PROWIZARD)
			if (strcmp(format_loaders[i]->name, "prowizard") == 0) {
				hio_seek(h, 0, SEEK_SET);
				pw_test_format(h, buf, 0, info);
				is_prowizard = 1;
			}
#endif

			if (info != NULL && !is_prowizard) {
				strncpy(info->name, buf, XMP_NAME_SIZE - 1);
				info->name[XMP_NAME_SIZE - 1] = '\0';

				strncpy(info->type, format_loaders[i]->name,
							XMP_NAME_SIZE - 1);
				info->type[XMP_NAME_SIZE - 1] = '\0';
			}
			return 0;
		}
	}
	return -XMP_ERROR_FORMAT;
}

#ifdef EDUKE32_DISABLE
int xmp_test_module(const char *path, struct xmp_test_info *info)
{
	HIO_HANDLE *h;
	struct stat st;
	int ret;
#ifndef LIBXMP_NO_DEPACKERS
	char *temp = NULL;
#endif

	if (stat(path, &st) < 0)
		return -XMP_ERROR_SYSTEM;

	if (S_ISDIR(st.st_mode)) {
		errno = EISDIR;
		return -XMP_ERROR_SYSTEM;
	}

	if ((h = hio_open(path, "rb")) == NULL)
		return -XMP_ERROR_SYSTEM;

#ifndef LIBXMP_NO_DEPACKERS
	if (libxmp_decrunch(&h, path, &temp) < 0) {
		ret = -XMP_ERROR_DEPACK;
		goto err;
	}
#endif

	ret = test_module(info, h);

#ifndef LIBXMP_NO_DEPACKERS
    err:
	hio_close(h);
	unlink_temp_file(temp);
#else
	hio_close(h);
#endif
	return ret;
}
#endif // EDUKE32_DISABLE

int xmp_test_module_from_memory(const void *mem, long size, struct xmp_test_info *info)
{
	HIO_HANDLE *h;
	int ret;

	if (size <= 0) {
		return -XMP_ERROR_INVALID;
	}

	if ((h = hio_open_mem(mem, size)) == NULL)
		return -XMP_ERROR_SYSTEM;

	ret = test_module(info, h);

	hio_close(h);
	return ret;
}

#ifdef EDUKE32_DISABLE
int xmp_test_module_from_file(void *file, struct xmp_test_info *info)
{
	HIO_HANDLE *h;
	int ret;
#ifndef LIBXMP_NO_DEPACKERS
	char *temp = NULL;
#endif

	if ((h = hio_open_file((FILE *)file)) == NULL)
		return -XMP_ERROR_SYSTEM;

#ifndef LIBXMP_NO_DEPACKERS
	if (libxmp_decrunch(&h, NULL, &temp) < 0) {
		ret = -XMP_ERROR_DEPACK;
		goto err;
	}
#endif

	ret = test_module(info, h);

#ifndef LIBXMP_NO_DEPACKERS
    err:
	hio_close(h);
	unlink_temp_file(temp);
#else
	hio_close(h);
#endif
	return ret;
}

int xmp_test_module_from_callbacks(void *priv, struct xmp_callbacks callbacks,
				struct xmp_test_info *info)
{
	HIO_HANDLE *h;
	int ret;

	if ((h = hio_open_callbacks(priv, callbacks)) == NULL)
		return -XMP_ERROR_SYSTEM;

	ret = test_module(info, h);

	hio_close(h);
	return ret;
}
#endif // EDUKE32_DISABLE

static int load_module(xmp_context opaque, HIO_HANDLE *h)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	int i, j, ret;
	int test_result, load_result;

	libxmp_load_prologue(ctx);

	D_(D_WARN "load");
	test_result = load_result = -1;
	for (i = 0; format_loaders[i] != NULL; i++) {
		hio_seek(h, 0, SEEK_SET);
		hio_error(h); /* reset error flag */

		D_(D_WARN "test %s", format_loaders[i]->name);
		test_result = format_loaders[i]->test(h, NULL, 0);
		if (test_result == 0) {
			hio_seek(h, 0, SEEK_SET);
			D_(D_WARN "load format: %s", format_loaders[i]->name);
			load_result = format_loaders[i]->loader(m, h, 0);
			break;
		}
	}

#ifndef LIBXMP_CORE_PLAYER
	if (test_result == 0 && load_result == 0)
		set_md5sum(h, m->md5);
#endif

	if (test_result < 0) {
		xmp_release_module(opaque);
		return -XMP_ERROR_FORMAT;
	}

	if (load_result < 0) {
		goto err_load;
	}

	/* Sanity check: number of channels, module length */
	if (mod->chn > XMP_MAX_CHANNELS || mod->len > XMP_MAX_MOD_LENGTH) {
		goto err_load;
	}

	/* Sanity check: channel pan */
	for (i = 0; i < mod->chn; i++) {
		if (mod->xxc[i].vol < 0 || mod->xxc[i].vol > 0xff) {
			goto err_load;
		}
		if (mod->xxc[i].pan < 0 || mod->xxc[i].pan > 0xff) {
			goto err_load;
		}
	}

	/* Sanity check: patterns */
	if (mod->xxp == NULL) {
		goto err_load;
	}
	for (i = 0; i < mod->pat; i++) {
		if (mod->xxp[i] == NULL) {
			goto err_load;
		}
		for (j = 0; j < mod->chn; j++) {
			int t = mod->xxp[i]->index[j];
			if (t < 0 || t >= mod->trk || mod->xxt[t] == NULL) {
				goto err_load;
			}
		}
	}

	libxmp_adjust_string(mod->name);
	for (i = 0; i < mod->ins; i++) {
		libxmp_adjust_string(mod->xxi[i].name);
	}
	for (i = 0; i < mod->smp; i++) {
		libxmp_adjust_string(mod->xxs[i].name);
	}

	libxmp_load_epilogue(ctx);

	ret = libxmp_prepare_scan(ctx);
	if (ret < 0) {
		xmp_release_module(opaque);
		return ret;
	}

	ret = libxmp_scan_sequences(ctx);
	if (ret < 0) {
		xmp_release_module(opaque);
		return -XMP_ERROR_LOAD;
	}

	ctx->state = XMP_STATE_LOADED;

	return 0;

    err_load:
	xmp_release_module(opaque);
	return -XMP_ERROR_LOAD;
}

#ifdef EDUKE32_DISABLE
int xmp_load_module(xmp_context opaque, const char *path)
{
	struct context_data *ctx = (struct context_data *)opaque;
#ifndef LIBXMP_CORE_PLAYER
	struct module_data *m = &ctx->m;
#endif
#ifndef LIBXMP_NO_DEPACKERS
	char *temp_name;
#endif
	HIO_HANDLE *h;
	struct stat st;
	int ret;

	D_(D_WARN "path = %s", path);

	if (stat(path, &st) < 0) {
		return -XMP_ERROR_SYSTEM;
	}

	if (S_ISDIR(st.st_mode)) {
		errno = EISDIR;
		return -XMP_ERROR_SYSTEM;
	}

	if ((h = hio_open(path, "rb")) == NULL) {
		return -XMP_ERROR_SYSTEM;
	}

#ifndef LIBXMP_NO_DEPACKERS
	D_(D_INFO "decrunch");
	if (libxmp_decrunch(&h, path, &temp_name) < 0) {
		ret = -XMP_ERROR_DEPACK;
		goto err;
	}
#endif

	if (ctx->state > XMP_STATE_UNLOADED)
		xmp_release_module(opaque);

#ifndef LIBXMP_CORE_PLAYER
	m->dirname = get_dirname(path);
	if (m->dirname == NULL) {
		ret = -XMP_ERROR_SYSTEM;
		goto err;
	}

	m->basename = get_basename(path);
	if (m->basename == NULL) {
		ret = -XMP_ERROR_SYSTEM;
		goto err;
	}

	m->filename = path;	/* For ALM, SSMT, etc */
	m->size = hio_size(h);
#else
	ctx->m.filename = NULL;
	ctx->m.dirname = NULL;
	ctx->m.basename = NULL;
#endif

	ret = load_module(opaque, h);
	hio_close(h);

#ifndef LIBXMP_NO_DEPACKERS
	unlink_temp_file(temp_name);
#endif

	return ret;

#ifndef LIBXMP_CORE_PLAYER
    err:
	hio_close(h);
#ifndef LIBXMP_NO_DEPACKERS
	unlink_temp_file(temp_name);
#endif
	return ret;
#endif
}
#endif // EDUKE32_DISABLE

int xmp_load_module_from_memory(xmp_context opaque, const void *mem, long size)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct module_data *m = &ctx->m;
	HIO_HANDLE *h;
	int ret;

	if (size <= 0) {
		return -XMP_ERROR_INVALID;
	}

	if ((h = hio_open_mem(mem, size)) == NULL)
		return -XMP_ERROR_SYSTEM;

	if (ctx->state > XMP_STATE_UNLOADED)
		xmp_release_module(opaque);

	m->filename = NULL;
	m->basename = NULL;
	m->dirname = NULL;
	m->size = size;

	ret = load_module(opaque, h);

	hio_close(h);

	return ret;
}

#ifdef EDUKE32_DISABLE
int xmp_load_module_from_file(xmp_context opaque, void *file, long size)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct module_data *m = &ctx->m;
	HIO_HANDLE *h;
	int ret;

	if ((h = hio_open_file((FILE *)file)) == NULL)
		return -XMP_ERROR_SYSTEM;

	if (ctx->state > XMP_STATE_UNLOADED)
		xmp_release_module(opaque);

	m->filename = NULL;
	m->basename = NULL;
	m->dirname = NULL;
	m->size = hio_size(h);

	ret = load_module(opaque, h);

	hio_close(h);

	return ret;
}

int xmp_load_module_from_callbacks(xmp_context opaque, void *priv,
				struct xmp_callbacks callbacks)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct module_data *m = &ctx->m;
	HIO_HANDLE *h;
	int ret;

	if ((h = hio_open_callbacks(priv, callbacks)) == NULL)
		return -XMP_ERROR_SYSTEM;

	if (ctx->state > XMP_STATE_UNLOADED)
		xmp_release_module(opaque);

	m->filename = NULL;
	m->basename = NULL;
	m->dirname = NULL;
	m->size = hio_size(h);

	ret = load_module(opaque, h);

	hio_close(h);

	return ret;
}
#endif // EDUKE32_DISABLE

void xmp_release_module(xmp_context opaque)
{
	struct context_data *ctx = (struct context_data *)opaque;
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	int i;

	/* can't test this here, we must call release_module to clean up
	 * load errors
	if (ctx->state < XMP_STATE_LOADED)
		return;
	 */

	if (ctx->state > XMP_STATE_LOADED)
		xmp_end_player(opaque);

	ctx->state = XMP_STATE_UNLOADED;

	D_(D_INFO "Freeing memory");

#ifndef LIBXMP_CORE_PLAYER
	libxmp_release_module_extras(ctx);
#endif

	if (mod->xxt != NULL) {
		for (i = 0; i < mod->trk; i++) {
			free(mod->xxt[i]);
		}
		free(mod->xxt);
		mod->xxt = NULL;
	}

	if (mod->xxp != NULL) {
		for (i = 0; i < mod->pat; i++) {
			free(mod->xxp[i]);
		}
		free(mod->xxp);
		mod->xxp = NULL;
	}

	if (mod->xxi != NULL) {
		for (i = 0; i < mod->ins; i++) {
			free(mod->xxi[i].sub);
			free(mod->xxi[i].extra);
		}
		free(mod->xxi);
		mod->xxi = NULL;
	}

	if (mod->xxs != NULL) {
		for (i = 0; i < mod->smp; i++) {
			libxmp_free_sample(&mod->xxs[i]);
		}
		free(mod->xxs);
		mod->xxs = NULL;
	}

	free(m->xtra);
	m->xtra = NULL;

#ifndef LIBXMP_CORE_DISABLE_IT
	if (m->xsmp != NULL) {
		for (i = 0; i < mod->smp; i++) {
			libxmp_free_sample(&m->xsmp[i]);
		}
		free(m->xsmp);
		m->xsmp = NULL;
	}
#endif

	libxmp_free_scan(ctx);

	free(m->comment);
	m->comment = NULL;

	D_("free dirname/basename");
	free(m->dirname);
	free(m->basename);
	m->basename = NULL;
	m->dirname = NULL;
}

void xmp_scan_module(xmp_context opaque)
{
	struct context_data *ctx = (struct context_data *)opaque;

	if (ctx->state < XMP_STATE_LOADED)
		return;

	libxmp_scan_sequences(ctx);
}

repo_name: orochi/nblood

license: mit


code:
/* Extended Module Player
 * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <ctype.h>
#include "common.h"
#include "loader.h"

#ifndef LIBXMP_CORE_PLAYER

/*
 * Handle special "module quirks" that can't be detected automatically
 * such as Protracker 2.x compatibility, vblank timing, etc.
 */

struct module_quirk {
	uint8 md5[16];
	int flags;
	int mode;
};

const struct module_quirk mq[] = {
	/* "No Mercy" by Alf/VTL (added by Martin Willers) */
	{
		{ 0x36, 0x6e, 0xc0, 0xfa, 0x96, 0x2a, 0xeb, 0xee,
	  	  0x03, 0x4a, 0xa2, 0xdb, 0xaa, 0x49, 0xaa, 0xea },
		0, XMP_MODE_PROTRACKER
	},

	/* mod.souvenir of china */
	{
		{ 0x93, 0xf1, 0x46, 0xae, 0xb7, 0x58, 0xc3, 0x9d,
		  0x8b, 0x5f, 0xbc, 0x98, 0xbf, 0x23, 0x7a, 0x43 },
		XMP_FLAGS_FIXLOOP, XMP_MODE_AUTO
	},

	/* "siedler ii" (added by Daniel Åkerud) */
	{
		{ 0x70, 0xaa, 0x03, 0x4d, 0xfb, 0x2f, 0x1f, 0x73,
		  0xd9, 0xfd, 0xba, 0xfe, 0x13, 0x1b, 0xb7, 0x01 },
		XMP_FLAGS_VBLANK, XMP_MODE_AUTO
	},

	/* "Klisje paa klisje" (added by Kjetil Torgrim Homme) */
	{
		{ 0xe9, 0x98, 0x01, 0x2c, 0x70, 0x0e, 0xb4, 0x3a,
		  0xf0, 0x32, 0x17, 0x11, 0x30, 0x58, 0x29, 0xb2 },
		0, XMP_MODE_NOISETRACKER
	},

#if 0
	/* -- Already covered by Noisetracker fingerprinting -- */

	/* Another version of Klisje paa klisje sent by Steve Fernandez */
	{
		{ 0x12, 0x19, 0x1c, 0x90, 0x41, 0xe3, 0xfd, 0x70,
		  0xb7, 0xe6, 0xb3, 0x94, 0x8b, 0x21, 0x07, 0x63 },
		XMP_FLAGS_VBLANK
	},
#endif

	/* "((((( nebulos )))))" sent by Tero Auvinen (AMP version) */
	{
		{ 0x51, 0x6e, 0x8d, 0xcc, 0x35, 0x7d, 0x50, 0xde,
		  0xa9, 0x85, 0xbe, 0xbf, 0x90, 0x2e, 0x42, 0xdc },
		0, XMP_MODE_NOISETRACKER
	},

	/* Purple Motion's Sundance.mod, Music Channel BBS edit */
	{
		{ 0x5d, 0x3e, 0x1e, 0x08, 0x28, 0x52, 0x12, 0xc7,
		  0x17, 0x64, 0x95, 0x75, 0x98, 0xe6, 0x95, 0xc1 },
		0, XMP_MODE_ST3
	},

	/* Asle's Ode to Protracker */
	{
		{ 0x97, 0xa3, 0x7d, 0x30, 0xd7, 0xae, 0x6d, 0x50,
		  0xc9, 0x62, 0xe9, 0xd8, 0x87, 0x1b, 0x7e, 0x8a },
		0, XMP_MODE_PROTRACKER
	},

	{
		{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
		0, 0
	}
};

static void module_quirks(struct context_data *ctx)
{
	struct player_data *p = &ctx->p;
	struct module_data *m = &ctx->m;
	int i;

	for (i = 0; mq[i].flags != 0 || mq[i].mode != 0; i++) {
		if (!memcmp(m->md5, mq[i].md5, 16)) {
			p->flags |= mq[i].flags;
			p->mode = mq[i].mode;
		}
	}
}

#endif /* LIBXMP_CORE_PLAYER */

char *libxmp_adjust_string(char *s)
{
	int i;

	for (i = 0; i < strlen(s); i++) {
		if (!isprint((int)s[i]) || ((uint8) s[i] > 127))
			s[i] = ' ';
	}

	while (*s && (s[strlen(s) - 1] == ' ')) {
		s[strlen(s) - 1] = 0;
	}

	return s;
}

static void check_envelope(struct xmp_envelope *env)
{
	/* Disable envelope if invalid number of points */
	if (env->npt <= 0 || env->npt > XMP_MAX_ENV_POINTS) {
		env->flg &= ~XMP_ENVELOPE_ON;
	}

	/* Disable envelope loop if invalid loop parameters */
	if (env->lps >= env->npt || env->lpe >= env->npt) {
		env->flg &= ~XMP_ENVELOPE_LOOP;
	}

	/* Disable envelope loop if invalid sustain */
	if (env->sus >= env->npt) {
		env->flg &= ~XMP_ENVELOPE_ON;
	}
}

void libxmp_load_prologue(struct context_data *ctx)
{
	struct module_data *m = &ctx->m;
	int i;

	/* Reset variables */
	memset(&m->mod, 0, sizeof (struct xmp_module));
	m->rrate = PAL_RATE;
	m->c4rate = C4_PAL_RATE;
	m->volbase = 0x40;
	m->gvol = m->gvolbase = 0x40;
	m->vol_table = NULL;
	m->quirk = 0;
	m->read_event_type = READ_EVENT_MOD;
	m->period_type = PERIOD_AMIGA;
	m->comment = NULL;
	m->scan_cnt = NULL;

	/* Set defaults */
    	m->mod.pat = 0;
    	m->mod.trk = 0;
    	m->mod.chn = 4;
    	m->mod.ins = 0;
    	m->mod.smp = 0;
    	m->mod.spd = 6;
    	m->mod.bpm = 125;
    	m->mod.len = 0;
    	m->mod.rst = 0;

#ifndef LIBXMP_CORE_PLAYER
	m->extra = NULL;
#endif
#ifndef LIBXMP_CORE_DISABLE_IT
	m->xsmp = NULL;
#endif

	m->time_factor = DEFAULT_TIME_FACTOR;

	for (i = 0; i < 64; i++) {
		int pan = (((i + 1) / 2) % 2) * 0xff;
		m->mod.xxc[i].pan = 0x80 + (pan - 0x80) * m->defpan / 100;
		m->mod.xxc[i].vol = 0x40;
		m->mod.xxc[i].flg = 0;
	}
}

void libxmp_load_epilogue(struct context_data *ctx)
{
	struct player_data *p = &ctx->p;
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	int i, j;

    	mod->gvl = m->gvol;

	/* Sanity check for module parameters */
	CLAMP(mod->len, 0, XMP_MAX_MOD_LENGTH);
	CLAMP(mod->pat, 0, 257);   /* some formats have an extra pattern */
	CLAMP(mod->ins, 0, 255);
	CLAMP(mod->smp, 0, MAX_SAMPLES);
	CLAMP(mod->chn, 0, XMP_MAX_CHANNELS);

	/* Fix cases where the restart value is invalid e.g. kc_fall8.xm
	 * from http://aminet.net/mods/mvp/mvp_0002.lha (reported by
	 * Ralf Hoffmann <ralf@boomerangsworld.de>)
	 */
	if (mod->rst >= mod->len) {
		mod->rst = 0;
	}

	/* Sanity check for tempo and BPM */
	if (mod->spd <= 0 || mod->spd > 255) {
		mod->spd = 6;
	}
	CLAMP(mod->bpm, XMP_MIN_BPM, 255);

	/* Set appropriate values for instrument volumes and subinstrument
	 * global volumes when QUIRK_INSVOL is not set, to keep volume values
	 * consistent if the user inspects struct xmp_module. We can later
	 * set volumes in the loaders and eliminate the quirk.
	 */
	for (i = 0; i < mod->ins; i++) {
		if (~m->quirk & QUIRK_INSVOL) {
			mod->xxi[i].vol = m->volbase;
		}
		for (j = 0; j < mod->xxi[i].nsm; j++) {
			if (~m->quirk & QUIRK_INSVOL) {
				mod->xxi[i].sub[j].gvl = m->volbase;
			}
		}
	}

	/* Sanity check for envelopes
	 */
	for (i = 0; i < mod->ins; i++) {
		check_envelope(&mod->xxi[i].aei);
		check_envelope(&mod->xxi[i].fei);
		check_envelope(&mod->xxi[i].pei);
	}

	p->filter = 0;
	p->mode = XMP_MODE_AUTO;
	p->flags = p->player_flags;
#ifndef LIBXMP_CORE_PLAYER
	module_quirks(ctx);
#endif
	libxmp_set_player_mode(ctx);
}

int libxmp_prepare_scan(struct context_data *ctx)
{
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	int i, ord;

	if (mod->xxp == NULL || mod->xxt == NULL)
		return -XMP_ERROR_LOAD;
	ord = 0;
	while (ord < mod->len && mod->xxo[ord] >= mod->pat) {
		ord++;
	}

	if (ord >= mod->len) {
		mod->len = 0;
		return 0;
	}

	m->scan_cnt = (uint8 **)calloc(sizeof (uint8 *), mod->len);
	if (m->scan_cnt == NULL)
		return -XMP_ERROR_SYSTEM;

	for (i = 0; i < mod->len; i++) {
		int pat_idx = mod->xxo[i];
		struct xmp_pattern *pat;

		/* Add pattern if referenced in orders */
		if (pat_idx < mod->pat && !mod->xxp[pat_idx]) {
			if (libxmp_alloc_pattern(mod, pat_idx) < 0) {
				return -XMP_ERROR_SYSTEM;
			}
		}

		pat = pat_idx >= mod->pat ? NULL : mod->xxp[pat_idx];
		m->scan_cnt[i] = (uint8 *)calloc(1, pat && pat->rows ? pat->rows : 1);
		if (m->scan_cnt[i] == NULL)
			return -XMP_ERROR_SYSTEM;
	}

	return 0;
}

void libxmp_free_scan(struct context_data *ctx)
{
	struct player_data *p = &ctx->p;
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	int i;

	if (m->scan_cnt) {
		for (i = 0; i < mod->len; i++)
			free(m->scan_cnt[i]);

		free(m->scan_cnt);
		m->scan_cnt = NULL;
	}

	free(p->scan);
	p->scan = NULL;
}

/* Process player personality flags */
int libxmp_set_player_mode(struct context_data *ctx)
{
	struct player_data *p = &ctx->p;
	struct module_data *m = &ctx->m;
	int q;

	switch (p->mode) {
	case XMP_MODE_AUTO:
		break;
	case XMP_MODE_MOD:
		m->c4rate = C4_PAL_RATE;
		m->quirk = 0;
		m->read_event_type = READ_EVENT_MOD;
		m->period_type = PERIOD_AMIGA;
		break;
	case XMP_MODE_NOISETRACKER:
		m->c4rate = C4_PAL_RATE;
		m->quirk = QUIRK_NOBPM;
		m->read_event_type = READ_EVENT_MOD;
		m->period_type = PERIOD_MODRNG;
		break;
	case XMP_MODE_PROTRACKER:
		m->c4rate = C4_PAL_RATE;
		m->quirk = QUIRK_PROTRACK;
		m->read_event_type = READ_EVENT_MOD;
		m->period_type = PERIOD_MODRNG;
		break;
	case XMP_MODE_S3M:
		q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM);
		m->c4rate = C4_NTSC_RATE;
		m->quirk = QUIRKS_ST3 | q;
		m->read_event_type = READ_EVENT_ST3;
		break;
	case XMP_MODE_ST3:
		q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM);
		m->c4rate = C4_NTSC_RATE;
		m->quirk = QUIRKS_ST3 | QUIRK_ST3BUGS | q;
		m->read_event_type = READ_EVENT_ST3;
		break;
	case XMP_MODE_ST3GUS:
		q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM);
		m->c4rate = C4_NTSC_RATE;
		m->quirk = QUIRKS_ST3 | QUIRK_ST3BUGS | q;
		m->quirk &= ~QUIRK_RSTCHN;
		m->read_event_type = READ_EVENT_ST3;
		break;
	case XMP_MODE_XM:
		m->c4rate = C4_NTSC_RATE;
		m->quirk = QUIRKS_FT2;
		m->read_event_type = READ_EVENT_FT2;
		break;
	case XMP_MODE_FT2:
		m->c4rate = C4_NTSC_RATE;
		m->quirk = QUIRKS_FT2 | QUIRK_FT2BUGS;
		m->read_event_type = READ_EVENT_FT2;
		break;
	case XMP_MODE_IT:
		m->c4rate = C4_NTSC_RATE;
		m->quirk = QUIRKS_IT | QUIRK_VIBHALF | QUIRK_VIBINV;
		m->read_event_type = READ_EVENT_IT;
		break;
	case XMP_MODE_ITSMP:
		m->c4rate = C4_NTSC_RATE;
		m->quirk = QUIRKS_IT | QUIRK_VIBHALF | QUIRK_VIBINV;
		m->quirk &= ~(QUIRK_VIRTUAL | QUIRK_RSTCHN);
		m->read_event_type = READ_EVENT_IT;
		break;
	default:
		return -1;
	}

	return 0;
}


code:
#ifndef XMP_LOADER_H
#define XMP_LOADER_H

#include "common.h"
#include "effects.h"
#include "format.h"
#include "hio.h"

/* Sample flags */
#define SAMPLE_FLAG_DIFF	0x0001	/* Differential */
#define SAMPLE_FLAG_UNS		0x0002	/* Unsigned */
#define SAMPLE_FLAG_8BDIFF	0x0004
#define SAMPLE_FLAG_7BIT	0x0008
#define SAMPLE_FLAG_NOLOAD	0x0010	/* Get from buffer, don't load */
#define SAMPLE_FLAG_BIGEND	0x0040	/* Big-endian */
#define SAMPLE_FLAG_VIDC	0x0080	/* Archimedes VIDC logarithmic */
/*#define SAMPLE_FLAG_STEREO	0x0100	   Interleaved stereo sample */
#define SAMPLE_FLAG_FULLREP	0x0200	/* Play full sample before looping */
#define SAMPLE_FLAG_ADLIB	0x1000	/* Adlib synth instrument */
#define SAMPLE_FLAG_HSC		0x2000	/* HSC Adlib synth instrument */
#define SAMPLE_FLAG_ADPCM	0x4000	/* ADPCM4 encoded samples */

#define DEFPAN(x) (0x80 + ((x) - 0x80) * m->defpan / 100)

int	libxmp_init_instrument		(struct module_data *);
int	libxmp_realloc_samples		(struct module_data *, int);
int	libxmp_alloc_subinstrument	(struct xmp_module *, int, int);
int	libxmp_init_pattern		(struct xmp_module *);
int	libxmp_alloc_pattern		(struct xmp_module *, int);
int	libxmp_alloc_track		(struct xmp_module *, int, int);
int	libxmp_alloc_tracks_in_pattern	(struct xmp_module *, int);
int	libxmp_alloc_pattern_tracks	(struct xmp_module *, int, int);
int	libxmp_alloc_pattern_tracks_long(struct xmp_module *, int, int);
char	*libxmp_instrument_name		(struct xmp_module *, int, uint8 *, int);

char	*libxmp_copy_adjust		(char *, uint8 *, int);
int	libxmp_copy_name_for_fopen	(char *, const char *, int);
int	libxmp_test_name		(uint8 *, int);
void	libxmp_read_title		(HIO_HANDLE *, char *, int);
void	libxmp_set_xxh_defaults		(struct xmp_module *);
void	libxmp_decode_protracker_event	(struct xmp_event *, uint8 *);
void	libxmp_decode_noisetracker_event(struct xmp_event *, uint8 *);
void	libxmp_disable_continue_fx	(struct xmp_event *);
int	libxmp_check_filename_case	(const char *, const char *, char *, int);
void	libxmp_get_instrument_path	(struct module_data *, char *, int);
void	libxmp_set_type			(struct module_data *, const char *, ...);
int	libxmp_load_sample		(struct module_data *, HIO_HANDLE *, int,
					 struct xmp_sample *, const void *);
void	libxmp_free_sample		(struct xmp_sample *);
void	libxmp_schism_tracker_string	(char *, size_t, int, int);

extern uint8		libxmp_ord_xlat[];
extern const int	libxmp_arch_vol_table[];

#define MAGIC4(a,b,c,d) \
    (((uint32)(a)<<24)|((uint32)(b)<<16)|((uint32)(c)<<8)|(d))

#define LOAD_INIT()

#define MODULE_INFO() do { \
    D_(D_WARN "Module title: \"%s\"", m->mod.name); \
    D_(D_WARN "Module type: %s", m->mod.type); \
} while (0)

#endif


code:
#ifndef LIBXMP_MDATAIO_H
#define LIBXMP_MDATAIO_H

#include <stddef.h>
#include <limits.h>
#include "common.h"

static inline ptrdiff_t CAN_READ(MFILE *m)
{
	if (m->size >= 0)
		return m->pos >= 0 ? m->size - m->pos : 0;

	return INT_MAX;
}

static inline uint8 mread8(MFILE *m, int *err)
{
	uint8 x = 0xff;
	size_t r = mread(&x, 1, 1, m);
	if (err) {
	    *err = (r == 1) ? 0 : EOF;
	}
	return x;
}

static inline int8 mread8s(MFILE *m, int *err)
{
	int r = mgetc(m);
	if (err) {
	    *err = (r < 0) ? EOF : 0;
	}
	return (int8)r;
}

static inline uint16 mread16l(MFILE *m, int *err)
{
	ptrdiff_t can_read = CAN_READ(m);
	if (can_read >= 2) {
		uint16 n = readmem16l(m->start + m->pos);
		m->pos += 2;
		if(err) *err = 0;
		return n;
	} else {
		m->pos += can_read;
		if(err) *err = EOF;
		return EOF;
	}
}

static inline uint16 mread16b(MFILE *m, int *err)
{
	ptrdiff_t can_read = CAN_READ(m);
	if (can_read >= 2) {
		uint16 n = readmem16b(m->start + m->pos);
		m->pos += 2;
		if(err) *err = 0;
		return n;
	} else {
		m->pos += can_read;
		if(err) *err = EOF;
		return EOF;
	}
}

static inline uint32 mread24l(MFILE *m, int *err)
{
	ptrdiff_t can_read = CAN_READ(m);
	if (can_read >= 3) {
		uint32 n = readmem24l(m->start + m->pos);
		m->pos += 3;
		if(err) *err = 0;
		return n;
	} else {
		m->pos += can_read;
		if(err) *err = EOF;
		return EOF;
	}
}

static inline uint32 mread24b(MFILE *m, int *err)
{
	ptrdiff_t can_read = CAN_READ(m);
	if (can_read >= 3) {
		uint32 n = readmem24b(m->start + m->pos);
		m->pos += 3;
		if(err) *err = 0;
		return n;
	} else {
		m->pos += can_read;
		if(err) *err = EOF;
		return EOF;
	}
}

static inline uint32 mread32l(MFILE *m, int *err)
{
	ptrdiff_t can_read = CAN_READ(m);
	if (can_read >= 4) {
		uint32 n = readmem32l(m->start + m->pos);
		m->pos += 4;
		if(err) *err = 0;
		return n;
	} else {
		m->pos += can_read;
		if(err) *err = EOF;
		return EOF;
	}
}

static inline uint32 mread32b(MFILE *m, int *err)
{
	ptrdiff_t can_read = CAN_READ(m);
	if (can_read >= 4) {
		uint32 n = readmem32b(m->start + m->pos);
		m->pos += 4;
		if(err) *err = 0;
		return n;
	} else {
		m->pos += can_read;
		if(err) *err = EOF;
		return EOF;
	}
}

#endif


code:
/* Extended Module Player
 * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "common.h"
#include "memio.h"

static inline ptrdiff_t CAN_READ(MFILE *m)
{
	return m->pos >= 0 ? m->size - m->pos : 0;
}

int mgetc(MFILE *m)
{
	if (CAN_READ(m) >= 1)
		return *(const uint8 *)(m->start + m->pos++);
	return EOF;
}

size_t mread(void *buf, size_t size, size_t num, MFILE *m)
{
	size_t should_read = size * num;
	ptrdiff_t can_read = CAN_READ(m);

	if (!size || !num || can_read <= 0) {
		return 0;
	}

	if (should_read > can_read) {
		should_read = can_read;
	}

	memcpy(buf, m->start + m->pos, should_read);
	m->pos += should_read;

	return should_read / size;
}

int mseek(MFILE *m, long offset, int whence)
{
	ptrdiff_t ofs = offset;

	switch (whence) {
	case SEEK_SET:
		break;
	case SEEK_CUR:
		ofs += m->pos;
		break;
	case SEEK_END:
		ofs += m->size;
		break;
	default:
		return -1;
	}
	if (ofs < 0) return -1;
	if (ofs > m->size)
		ofs = m->size;
	m->pos = ofs;
	return 0;
}

long mtell(MFILE *m)
{
	return (long)m->pos;
}

int meof(MFILE *m)
{
	return CAN_READ(m) <= 0;
}

MFILE *mopen(const void *ptr, long size)
{
	MFILE *m;

	m = (MFILE *)malloc(sizeof (MFILE));
	if (m == NULL)
		return NULL;

	m->start = (const unsigned char *)ptr;
	m->pos = 0;
	m->size = size;

	return m;
}

int mclose(MFILE *m)
{
	free(m);
	return 0;
}


code:
#ifndef LIBXMP_MEMIO_H
#define LIBXMP_MEMIO_H

#include <stddef.h>

typedef struct {
	const unsigned char *start;
	ptrdiff_t pos;
	ptrdiff_t size;
} MFILE;

#ifdef __cplusplus
extern "C" {
#endif

MFILE  *mopen(const void *, long);
int     mgetc(MFILE *stream);
size_t  mread(void *, size_t, size_t, MFILE *);
int     mseek(MFILE *, long, int);
long    mtell(MFILE *);
int     mclose(MFILE *);
int	meof(MFILE *);

#ifdef __cplusplus
}
#endif

#endif


code:
/* Extended Module Player
 * Copyright (C) 1996-2018 Claudio Matsuoka and Hipolito Carraro Jr
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "common.h"
#include "virtual.h"
#include "mixer.h"
#include "precomp_lut.h"

/* Mixers
 *
 * To increase performance eight mixers are defined, one for each
 * combination of the following parameters: interpolation, resolution
 * and number of channels.
 */
#define NEAREST_NEIGHBOR() do { \
    smp_in = ((int16)sptr[pos] << 8); \
} while (0)

#define NEAREST_NEIGHBOR_16BIT() do { \
    smp_in = sptr[pos]; \
} while (0)

#define LINEAR_INTERP() do { \
    smp_l1 = ((int16)sptr[pos] << 8); \
    smp_dt = ((int16)sptr[pos + 1] << 8) - smp_l1; \
    smp_in = smp_l1 + (((frac >> 1) * smp_dt) >> (SMIX_SHIFT - 1)); \
} while (0)

#define LINEAR_INTERP_16BIT() do { \
    smp_l1 = sptr[pos]; \
    smp_dt = sptr[pos + 1] - smp_l1; \
    smp_in = smp_l1 + (((frac >> 1) * smp_dt) >> (SMIX_SHIFT - 1)); \
} while (0)

/* The following lut settings are PRECOMPUTED. If you plan on changing these
 * settings, you MUST also regenerate the arrays.
 */
/* number of bits used to scale spline coefs */
#define SPLINE_QUANTBITS  14
#define SPLINE_SHIFT    (SPLINE_QUANTBITS)

/* log2(number) of precalculated splines (range is [4..14]) */
#define SPLINE_FRACBITS 10
#define SPLINE_LUTLEN (1L<<SPLINE_FRACBITS)

#define SPLINE_FRACSHIFT ((16 - SPLINE_FRACBITS) - 2)
#define SPLINE_FRACMASK  (((1L << (16 - SPLINE_FRACSHIFT)) - 1) & ~3)

#define SPLINE_INTERP() do { \
    int f = frac >> 6; \
    smp_in = (cubic_spline_lut0[f] * sptr[(int)pos - 1] + \
              cubic_spline_lut1[f] * sptr[pos    ] + \
              cubic_spline_lut3[f] * sptr[pos + 2] + \
              cubic_spline_lut2[f] * sptr[pos + 1]) >> (SPLINE_SHIFT - 8); \
} while (0)

#define SPLINE_INTERP_16BIT() do { \
    int f = frac >> 6; \
    smp_in = (cubic_spline_lut0[f] * sptr[(int)pos - 1] + \
              cubic_spline_lut1[f] * sptr[pos    ] + \
              cubic_spline_lut3[f] * sptr[pos + 2] + \
              cubic_spline_lut2[f] * sptr[pos + 1]) >> SPLINE_SHIFT; \
} while (0)

#define LOOP_AC for (; count > ramp; count--)

#define LOOP for (; count; count--)

#define UPDATE_POS() do { \
    frac += step; \
    pos += frac >> SMIX_SHIFT; \
    frac &= SMIX_MASK; \
} while (0)

#define MIX_MONO() do { \
    *(buffer++) += smp_in * vl; \
} while (0)

#define MIX_MONO_AC() do { \
    *(buffer++) += smp_in * (old_vl >> 8); old_vl += delta_l; \
} while (0)

#define MIX_MONO_FILTER() do { \
    sl = (a0 * smp_in * vl + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \
    fl2 = fl1; fl1 = sl; \
    *(buffer++) += sl; \
} while (0)

#define MIX_MONO_FILTER_AC() do { \
    int vl = old_vl >> 8; \
    MIX_MONO_FILTER(); \
    old_vl += delta_l; \
} while (0)

#define MIX_STEREO() do { \
    *(buffer++) += smp_in * vr; \
    *(buffer++) += smp_in * vl; \
} while (0)

#define MIX_STEREO_AC() do { \
    *(buffer++) += smp_in * (old_vr >> 8); old_vr += delta_r; \
    *(buffer++) += smp_in * (old_vl >> 8); old_vl += delta_l; \
} while (0)

#define MIX_STEREO_FILTER() do { \
    sr = (a0 * smp_in * vr + b0 * fr1 + b1 * fr2) >> FILTER_SHIFT; \
    fr2 = fr1; fr1 = sr; \
    sl = (a0 * smp_in * vl + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \
    fl2 = fl1; fl1 = sl; \
    *(buffer++) += sr; \
    *(buffer++) += sl; \
} while (0)

#define MIX_STEREO_FILTER_AC() do { \
    int vr = old_vr >> 8; \
    int vl = old_vl >> 8; \
    MIX_STEREO_FILTER(); \
    old_vr += delta_r; \
    old_vl += delta_l; \
} while (0)

#define MIX_STEREO_FILTER_AC() do { \
    int vr = old_vr >> 8; \
    int vl = old_vl >> 8; \
    MIX_STEREO_FILTER(); \
    old_vr += delta_r; \
    old_vl += delta_l; \
} while (0)

#define VAR_NORM(x) \
    int smp_in; \
    x *sptr = (x *)vi->sptr; \
    unsigned int pos = (unsigned int)vi->pos; \
    int frac = (int)((1 << SMIX_SHIFT) * (vi->pos - (int)vi->pos))

#define VAR_LINEAR_MONO(x) \
    VAR_NORM(x); \
    int old_vl = vi->old_vl; \
    int smp_l1, smp_dt

#define VAR_LINEAR_STEREO(x) \
    VAR_LINEAR_MONO(x); \
    int old_vr = vi->old_vr

#define VAR_SPLINE_MONO(x) \
    int old_vl = vi->old_vl; \
    VAR_NORM(x)

#define VAR_SPLINE_STEREO(x) \
    VAR_SPLINE_MONO(x); \
    int old_vr = vi->old_vr

#ifndef LIBXMP_CORE_DISABLE_IT

#define VAR_FILTER_MONO \
    int fl1 = vi->filter.l1, fl2 = vi->filter.l2; \
    int64 a0 = vi->filter.a0, b0 = vi->filter.b0, b1 = vi->filter.b1; \
    int sl

#define VAR_FILTER_STEREO \
    VAR_FILTER_MONO; \
    int fr1 = vi->filter.r1, fr2 = vi->filter.r2; \
    int sr

#define SAVE_FILTER_MONO() do { \
    vi->filter.l1 = fl1; \
    vi->filter.l2 = fl2; \
} while (0)

#define SAVE_FILTER_STEREO() do { \
    SAVE_FILTER_MONO(); \
    vi->filter.r1 = fr1; \
    vi->filter.r2 = fr2; \
} while (0)

#endif

/*
 * Nearest neighbor mixers
 */

/* Handler for 8 bit samples, nearest neighbor mono output
 */
MIXER(mono_8bit_nearest)
{
    VAR_NORM(int8);

    LOOP { NEAREST_NEIGHBOR(); MIX_MONO(); UPDATE_POS(); }
}

/* Handler for 16 bit samples, nearest neighbor mono output
 */
MIXER(mono_16bit_nearest)
{
    VAR_NORM(int16);

    LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_MONO(); UPDATE_POS(); }
}

/* Handler for 8 bit samples, nearest neighbor stereo output
 */
MIXER(stereo_8bit_nearest)
{
    VAR_NORM(int8);

    LOOP { NEAREST_NEIGHBOR(); MIX_STEREO(); UPDATE_POS(); }
}

/* Handler for 16 bit samples, nearest neighbor stereo output
 */
MIXER(stereo_16bit_nearest)
{
    VAR_NORM(int16);

    LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_STEREO(); UPDATE_POS(); }
}

/*
 * Linear mixers
 */

/* Handler for 8 bit samples, linear interpolated mono output
 */
MIXER(mono_8bit_linear)
{
    VAR_LINEAR_MONO(int8);

    LOOP_AC { LINEAR_INTERP(); MIX_MONO_AC(); UPDATE_POS(); }
    LOOP    { LINEAR_INTERP(); MIX_MONO(); UPDATE_POS(); }
}

/* Handler for 16 bit samples, linear interpolated mono output
 */
MIXER(mono_16bit_linear)
{
    VAR_LINEAR_MONO(int16);

    LOOP_AC { LINEAR_INTERP_16BIT(); MIX_MONO_AC(); UPDATE_POS(); }
    LOOP    { LINEAR_INTERP_16BIT(); MIX_MONO(); UPDATE_POS(); }
}

/* Handler for 8 bit samples, linear interpolated stereo output
 */
MIXER(stereo_8bit_linear)
{
   VAR_LINEAR_STEREO(int8);

    LOOP_AC { LINEAR_INTERP(); MIX_STEREO_AC(); UPDATE_POS(); }
    LOOP    { LINEAR_INTERP(); MIX_STEREO(); UPDATE_POS(); }
}

/* Handler for 16 bit samples, linear interpolated stereo output
 */
MIXER(stereo_16bit_linear)
{
    VAR_LINEAR_STEREO(int16);

    LOOP_AC { LINEAR_INTERP_16BIT(); MIX_STEREO_AC(); UPDATE_POS(); }
    LOOP    { LINEAR_INTERP_16BIT(); MIX_STEREO(); UPDATE_POS(); }
}

#ifndef LIBXMP_CORE_DISABLE_IT

/* Handler for 8 bit samples, filtered linear interpolated mono output
 */
MIXER(mono_8bit_linear_filter)
{
    VAR_LINEAR_MONO(int8);
    VAR_FILTER_MONO;

    LOOP_AC { LINEAR_INTERP(); MIX_MONO_FILTER_AC(); UPDATE_POS(); }
    LOOP    { LINEAR_INTERP(); MIX_MONO_FILTER(); UPDATE_POS(); }

    SAVE_FILTER_MONO();
}

/* Handler for 16 bit samples, filtered linear interpolated mono output
 */
MIXER(mono_16bit_linear_filter)
{
    VAR_LINEAR_MONO(int16);
    VAR_FILTER_MONO;

    LOOP_AC { LINEAR_INTERP_16BIT(); MIX_MONO_FILTER_AC(); UPDATE_POS(); }
    LOOP    { LINEAR_INTERP_16BIT(); MIX_MONO_FILTER(); UPDATE_POS(); }

    SAVE_FILTER_MONO();
}

/* Handler for 8 bit samples, filtered linear interpolated stereo output
 */
MIXER(stereo_8bit_linear_filter)
{
    VAR_LINEAR_STEREO(int8);
    VAR_FILTER_STEREO;

    LOOP_AC { LINEAR_INTERP(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); }
    LOOP    { LINEAR_INTERP(); MIX_STEREO_FILTER(); UPDATE_POS(); }

    SAVE_FILTER_STEREO();
}

/* Handler for 16 bit samples, filtered linear interpolated stereo output
 */
MIXER(stereo_16bit_linear_filter)
{
    VAR_LINEAR_STEREO(int16);
    VAR_FILTER_STEREO;

    LOOP_AC { LINEAR_INTERP_16BIT(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); }
    LOOP    { LINEAR_INTERP_16BIT(); MIX_STEREO_FILTER(); UPDATE_POS(); }

    SAVE_FILTER_STEREO();
}

#endif

/*
 * Spline mixers
 */

/* Handler for 8 bit samples, spline interpolated mono output
 */
MIXER(mono_8bit_spline)
{
    VAR_SPLINE_MONO(int8);

    LOOP_AC { SPLINE_INTERP(); MIX_MONO_AC(); UPDATE_POS(); }
    LOOP    { SPLINE_INTERP(); MIX_MONO(); UPDATE_POS(); }
}

/* Handler for 16 bit samples, spline interpolated mono output
 */
MIXER(mono_16bit_spline)
{
    VAR_SPLINE_MONO(int16);

    LOOP_AC { SPLINE_INTERP_16BIT(); MIX_MONO_AC(); UPDATE_POS(); }
    LOOP    { SPLINE_INTERP_16BIT(); MIX_MONO(); UPDATE_POS(); }
}

/* Handler for 8 bit samples, spline interpolated stereo output
 */
MIXER(stereo_8bit_spline)
{
    VAR_SPLINE_STEREO(int8);

    LOOP_AC { SPLINE_INTERP(); MIX_STEREO_AC(); UPDATE_POS(); }
    LOOP    { SPLINE_INTERP(); MIX_STEREO(); UPDATE_POS(); }
}

/* Handler for 16 bit samples, spline interpolated stereo output
 */
MIXER(stereo_16bit_spline)
{
    VAR_SPLINE_STEREO(int16);

    LOOP_AC { SPLINE_INTERP_16BIT(); MIX_STEREO_AC(); UPDATE_POS(); }
    LOOP    { SPLINE_INTERP_16BIT(); MIX_STEREO(); UPDATE_POS(); }
}

#ifndef LIBXMP_CORE_DISABLE_IT

/* Handler for 8 bit samples, filtered spline interpolated mono output
 */
MIXER(mono_8bit_spline_filter)
{
    VAR_SPLINE_MONO(int8);
    VAR_FILTER_MONO;

    LOOP_AC { SPLINE_INTERP(); MIX_MONO_FILTER_AC(); UPDATE_POS(); }
    LOOP    { SPLINE_INTERP(); MIX_MONO_FILTER(); UPDATE_POS(); }

    SAVE_FILTER_MONO();
}

/* Handler for 16 bit samples, filtered spline interpolated mono output
 */
MIXER(mono_16bit_spline_filter)
{
    VAR_SPLINE_MONO(int16);
    VAR_FILTER_MONO;

    LOOP_AC { SPLINE_INTERP_16BIT(); MIX_MONO_FILTER_AC(); UPDATE_POS(); }
    LOOP    { SPLINE_INTERP_16BIT(); MIX_MONO_FILTER(); UPDATE_POS(); }

    SAVE_FILTER_MONO();
}

/* Handler for 8 bit samples, filtered spline interpolated stereo output
 */
MIXER(stereo_8bit_spline_filter)
{
    VAR_SPLINE_STEREO(int8);
    VAR_FILTER_STEREO;

    LOOP_AC { SPLINE_INTERP(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); }
    LOOP    { SPLINE_INTERP(); MIX_STEREO_FILTER(); UPDATE_POS(); }

    SAVE_FILTER_STEREO();
}

/* Handler for 16 bit samples, filtered spline interpolated stereo output
 */
MIXER(stereo_16bit_spline_filter)
{
    VAR_SPLINE_STEREO(int16);
    VAR_FILTER_STEREO;

    LOOP_AC { SPLINE_INTERP_16BIT(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); }
    LOOP    { SPLINE_INTERP_16BIT(); MIX_STEREO_FILTER(); UPDATE_POS(); }

    SAVE_FILTER_STEREO();
}

#endif


code:
/* Extended Module Player
 * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <math.h>
#include "common.h"
#include "virtual.h"
#include "mixer.h"
#include "period.h"
#include "player.h"	/* for set_sample_end() */

#ifdef LIBXMP_PAULA_SIMULATOR
#include "paula.h"
#endif

#define FLAG_16_BITS	0x01
#define FLAG_STEREO	0x02
#define FLAG_FILTER	0x04
#define FLAG_ACTIVE	0x10
/* #define FLAG_SYNTH	0x20 */
#define FIDX_FLAGMASK	(FLAG_16_BITS | FLAG_STEREO | FLAG_FILTER)

#define DOWNMIX_SHIFT	 12
#define LIM8_HI		 127
#define LIM8_LO		-128
#define LIM16_HI	 32767
#define LIM16_LO	-32768

#define MIX_FN(x) void libxmp_mix_##x(struct mixer_voice *, int32 *, int, int, int, int, int, int, int)

MIX_FN(mono_8bit_nearest);
MIX_FN(mono_8bit_linear);
MIX_FN(mono_16bit_nearest);
MIX_FN(mono_16bit_linear);
MIX_FN(stereo_8bit_nearest);
MIX_FN(stereo_8bit_linear);
MIX_FN(stereo_16bit_nearest);
MIX_FN(stereo_16bit_linear);
MIX_FN(mono_8bit_spline);
MIX_FN(mono_16bit_spline);
MIX_FN(stereo_8bit_spline);
MIX_FN(stereo_16bit_spline);

#ifndef LIBXMP_CORE_DISABLE_IT
MIX_FN(mono_8bit_linear_filter);
MIX_FN(mono_16bit_linear_filter);
MIX_FN(stereo_8bit_linear_filter);
MIX_FN(stereo_16bit_linear_filter);
MIX_FN(mono_8bit_spline_filter);
MIX_FN(mono_16bit_spline_filter);
MIX_FN(stereo_8bit_spline_filter);
MIX_FN(stereo_16bit_spline_filter);
#endif

#ifdef LIBXMP_PAULA_SIMULATOR
MIX_FN(mono_a500);
MIX_FN(mono_a500_filter);
MIX_FN(stereo_a500);
MIX_FN(stereo_a500_filter);
#endif

/* Mixers array index:
 *
 * bit 0: 0=8 bit sample, 1=16 bit sample
 * bit 1: 0=mono output, 1=stereo output
 * bit 2: 0=unfiltered, 1=filtered
 */

typedef void (*MIX_FP) (struct mixer_voice *, int32 *, int, int, int, int, int, int, int);

static MIX_FP nearest_mixers[] = {
	libxmp_mix_mono_8bit_nearest,
	libxmp_mix_mono_16bit_nearest,
	libxmp_mix_stereo_8bit_nearest,
	libxmp_mix_stereo_16bit_nearest,

#ifndef LIBXMP_CORE_DISABLE_IT
	libxmp_mix_mono_8bit_nearest,
	libxmp_mix_mono_16bit_nearest,
	libxmp_mix_stereo_8bit_nearest,
	libxmp_mix_stereo_16bit_nearest,
#endif
};

static MIX_FP linear_mixers[] = {
	libxmp_mix_mono_8bit_linear,
	libxmp_mix_mono_16bit_linear,
	libxmp_mix_stereo_8bit_linear,
	libxmp_mix_stereo_16bit_linear,

#ifndef LIBXMP_CORE_DISABLE_IT
	libxmp_mix_mono_8bit_linear_filter,
	libxmp_mix_mono_16bit_linear_filter,
	libxmp_mix_stereo_8bit_linear_filter,
	libxmp_mix_stereo_16bit_linear_filter
#endif
};

static MIX_FP spline_mixers[] = {
	libxmp_mix_mono_8bit_spline,
	libxmp_mix_mono_16bit_spline,
	libxmp_mix_stereo_8bit_spline,
	libxmp_mix_stereo_16bit_spline,

#ifndef LIBXMP_CORE_DISABLE_IT
	libxmp_mix_mono_8bit_spline_filter,
	libxmp_mix_mono_16bit_spline_filter,
	libxmp_mix_stereo_8bit_spline_filter,
	libxmp_mix_stereo_16bit_spline_filter
#endif
};

#ifdef LIBXMP_PAULA_SIMULATOR
static MIX_FP a500_mixers[] = {
	libxmp_mix_mono_a500,
	NULL,
	libxmp_mix_stereo_a500,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL
};

static MIX_FP a500led_mixers[] = {
	libxmp_mix_mono_a500_filter,
	NULL,
	libxmp_mix_stereo_a500_filter,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL
};
#endif

/* Downmix 32bit samples to 8bit, signed or unsigned, mono or stereo output */
static void downmix_int_8bit(char *dest, int32 *src, int num, int amp, int offs)
{
	int smp;
	int shift = DOWNMIX_SHIFT + 8 - amp;

	for (; num--; src++, dest++) {
		smp = *src >> shift;
		if (smp > LIM8_HI) {
			*dest = LIM8_HI;
		} else if (smp < LIM8_LO) {
			*dest = LIM8_LO;
		} else {
			*dest = smp;
		}

		if (offs) *dest += offs;
	}
}

/* Downmix 32bit samples to 16bit, signed or unsigned, mono or stereo output */
static void downmix_int_16bit(int16 *dest, int32 *src, int num, int amp, int offs)
{
	int smp;
	int shift = DOWNMIX_SHIFT - amp;

	for (; num--; src++, dest++) {
		smp = *src >> shift;
		if (smp > LIM16_HI) {
			*dest = LIM16_HI;
		} else if (smp < LIM16_LO) {
			*dest = LIM16_LO;
		} else {
			*dest = smp;
		}

		if (offs) *dest += offs;
	}
}

static void anticlick(struct mixer_voice *vi)
{
	vi->flags |= ANTICLICK;
	vi->old_vl = 0;
	vi->old_vr = 0;
}

/* Ok, it's messy, but it works :-) Hipolito */
static void do_anticlick(struct context_data *ctx, int voc, int32 *buf, int count)
{
	struct player_data *p = &ctx->p;
	struct mixer_data *s = &ctx->s;
	struct mixer_voice *vi = &p->virt.voice_array[voc];
	int smp_l, smp_r, max_x2;
	int discharge = s->ticksize >> ANTICLICK_SHIFT;

	smp_r = vi->sright;
	smp_l = vi->sleft;
	vi->sright = vi->sleft = 0;

	if (smp_l == 0 && smp_r == 0) {
		return;
	}

	if (buf == NULL) {
		buf = s->buf32;
		count = discharge;
	} else if (count > discharge) {
		count = discharge;
	}

	if (count <= 0) {
		return;
	}

	max_x2 = count * count;

	while (count--) {
		if (~s->format & XMP_FORMAT_MONO) {
			*buf++ += (count * (smp_r >> 10) / max_x2 * count) << 10;
		}

		*buf++ += (count * (smp_l >> 10) / max_x2 * count) << 10;
	}
}

static void set_sample_end(struct context_data *ctx, int voc, int end)
{
	struct player_data *p = &ctx->p;
	struct module_data *m = &ctx->m;
	struct mixer_voice *vi = &p->virt.voice_array[voc];
	struct channel_data *xc;

	if ((uint32)voc >= p->virt.maxvoc)
		return;

	xc = &p->xc_data[vi->chn];

	if (end) {
		SET_NOTE(NOTE_SAMPLE_END);
		if (HAS_QUIRK(QUIRK_RSTCHN)) {
			libxmp_virt_resetvoice(ctx, voc, 0);
		}
	} else {
		RESET_NOTE(NOTE_SAMPLE_END);
	}
}

static void adjust_voice_end(struct mixer_voice *vi, struct xmp_sample *xxs)
{
	if (xxs->flg & XMP_SAMPLE_LOOP) {
		if ((xxs->flg & XMP_SAMPLE_LOOP_FULL) && (~vi->flags & SAMPLE_LOOP)) {
			vi->end = xxs->len;
		} else {
			vi->end = xxs->lpe;
		}
	} else {
		vi->end = xxs->len;
	}
}

static void loop_reposition(struct context_data *ctx, struct mixer_voice *vi, struct xmp_sample *xxs)
{
#ifndef LIBXMP_CORE_DISABLE_IT
	struct module_data *m = &ctx->m;
#endif
	int loop_size = xxs->lpe - xxs->lps;

	/* Reposition for next loop */
	vi->pos -= loop_size;		/* forward loop */
	vi->end = xxs->lpe;
	vi->flags |= SAMPLE_LOOP;

	if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
		vi->end += loop_size;	/* unrolled loop */
		vi->pos -= loop_size;	/* forward loop */

#ifndef LIBXMP_CORE_DISABLE_IT
		/* OpenMPT Bidi-Loops.it: "In Impulse Tracker’s software mixer,
		 * ping-pong loops are shortened by one sample.
		 */
		if (IS_PLAYER_MODE_IT()) {
			vi->end--;
			vi->pos++;
		}
#endif
	}
}

/* Prepare the mixer for the next tick */
void libxmp_mixer_prepare(struct context_data *ctx)
{
	struct player_data *p = &ctx->p;
	struct module_data *m = &ctx->m;
	struct mixer_data *s = &ctx->s;
	int bytelen;

	s->ticksize = s->freq * m->time_factor * m->rrate / p->bpm / 1000;

	bytelen = s->ticksize * sizeof(int);
	if (~s->format & XMP_FORMAT_MONO) {
		bytelen *= 2;
	}
	memset(s->buf32, 0, bytelen);
}
/* Fill the output buffer calling one of the handlers. The buffer contains
 * sound for one tick (a PAL frame or 1/50s for standard vblank-timed mods)
 */
void libxmp_mixer_softmixer(struct context_data *ctx)
{
	struct player_data *p = &ctx->p;
	struct mixer_data *s = &ctx->s;
	struct module_data *m = &ctx->m;
	struct xmp_module *mod = &m->mod;
	struct xmp_sample *xxs;
	struct mixer_voice *vi;
	double step;
	int samples, size;
	int vol_l, vol_r, voc, usmp;
	int prev_l, prev_r = 0;
	int lps, lpe;
	int32 *buf_pos;
	MIX_FP  mix_fn;
	MIX_FP *mixerset;

	switch (s->interp) {
	case XMP_INTERP_NEAREST:
		mixerset = nearest_mixers;
		break;
	case XMP_INTERP_LINEAR:
		mixerset = linear_mixers;
		break;
	case XMP_INTERP_SPLINE:
		mixerset = spline_mixers;
		break;
	default:
		mixerset = linear_mixers;
	}

#ifdef LIBXMP_PAULA_SIMULATOR
	if (p->flags & XMP_FLAGS_A500) {
		if (IS_AMIGA_MOD()) {
			if (p->filter) {
				mixerset = a500led_mixers;
			} else {
				mixerset = a500_mixers;
			}
		}
	}
#endif

	libxmp_mixer_prepare(ctx);

	for (voc = 0; voc < p->virt.maxvoc; voc++) {
		int c5spd, rampsize, delta_l, delta_r;

		vi = &p->virt.voice_array[voc];

		if (vi->flags & ANTICLICK) {
			if (s->interp > XMP_INTERP_NEAREST) {
				do_anticlick(ctx, voc, NULL, 0);
			}
			vi->flags &= ~ANTICLICK;
		}

		if (vi->chn < 0) {
			continue;
		}

		if (vi->period < 1) {
			libxmp_virt_resetvoice(ctx, voc, 1);
			continue;
		}

		vi->pos0 = vi->pos;

		buf_pos = s->buf32;
		if (vi->pan == PAN_SURROUND) {
			vol_r = vi->vol * 0x80;
			vol_l = -vi->vol * 0x80;
		} else {
			vol_r = vi->vol * (0x80 - vi->pan);
			vol_l = vi->vol * (0x80 + vi->pan);
		}

		if (vi->smp < mod->smp) {
			xxs = &mod->xxs[vi->smp];
			c5spd = m->xtra[vi->smp].c5spd;
		} else {
			xxs = &ctx->smix.xxs[vi->smp - mod->smp];
			c5spd = m->c4rate;
		}

		step = C4_PERIOD * c5spd / s->freq / vi->period;

		if (step < 0.001) {	/* otherwise m5v-nwlf.it crashes */
			continue;
		}

#ifndef LIBXMP_CORE_DISABLE_IT
		if (xxs->flg & XMP_SAMPLE_SLOOP && vi->smp < mod->smp) {
			if (~vi->flags & VOICE_RELEASE) {
				if (vi->pos < m->xsmp[vi->smp].lpe) {
					xxs = &m->xsmp[vi->smp];
				}
			}
		}

		adjust_voice_end(vi, xxs);
#endif

		lps = xxs->lps;
		lpe = xxs->lpe;

		if (p->flags & XMP_FLAGS_FIXLOOP) {
			lps >>= 1;
		}

		if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
			vi->end += lpe - lps;

#ifndef LIBXMP_CORE_DISABLE_IT
			if (IS_PLAYER_MODE_IT()) {
				vi->end--;
			}
#endif
		}

		rampsize = s->ticksize >> ANTICLICK_SHIFT;
		delta_l = (vol_l - vi->old_vl) / rampsize;
		delta_r = (vol_r - vi->old_vr) / rampsize;

		usmp = 0;
		for (size = s->ticksize; size > 0; ) {
			int split_noloop = 0;

			if (p->xc_data[vi->chn].split) {
				split_noloop = 1;
			}

			/* How many samples we can write before the loop break
			 * or sample end... */
			if (vi->pos >= vi->end) {
				samples = 0;
				usmp = 1;
			} else {
				int c = ceil(((double)vi->end - vi->pos) / step);
				/* ...inside the tick boundaries */
				if (c > size) {
					c = size;
				}

				samples = c;
				if (samples > 0) {
					usmp = 0;
				}
			}

			if (vi->vol) {
				int mix_size = samples;
				int mixer_id = vi->fidx & FIDX_FLAGMASK;

				if (~s->format & XMP_FORMAT_MONO) {
					mix_size *= 2;
				}

				/* For Hipolito's anticlick routine */
				if (samples > 0) {
					if (~s->format & XMP_FORMAT_MONO) {
						prev_r = buf_pos[mix_size - 2];
					}
					prev_l = buf_pos[mix_size - 1];
				} else {
					prev_r = prev_l = 0;
				}

#ifndef LIBXMP_CORE_DISABLE_IT
				/* See OpenMPT env-flt-max.it */
				if (vi->filter.cutoff >= 0xfe &&
                                    vi->filter.resonance == 0) {
					mixer_id &= ~FLAG_FILTER;
				}
#endif

				mix_fn = mixerset[mixer_id];

				/* Call the output handler */
				if (samples > 0 && vi->sptr != NULL) {
					int rsize = 0;

					if (rampsize > samples) {
						rampsize -= samples;
					} else {
						rsize = samples - rampsize;
						rampsize = 0;
					}

					if (delta_l == 0 && delta_r == 0) {
						/* no need to ramp */
						rsize = samples;
					}

					if (mix_fn != NULL) {
						mix_fn(vi, buf_pos, samples,
							vol_l >> 8, vol_r >> 8, step * (1 << SMIX_SHIFT), rsize, delta_l, delta_r);
					}

					buf_pos += mix_size;
					vi->old_vl += samples * delta_l;
					vi->old_vr += samples * delta_r;

					/* For Hipolito's anticlick routine */
					if (~s->format & XMP_FORMAT_MONO) {
						vi->sright = buf_pos[-2] - prev_r;
					}
					vi->sleft = buf_pos[-1] - prev_l;
				}
			}

			vi->pos += step * samples;

			/* No more samples in this tick */
			size -= samples + usmp;
			if (size <= 0) {
				if (xxs->flg & XMP_SAMPLE_LOOP) {
					if (vi->pos + step > vi->end) {
						vi->pos += step;
						loop_reposition(ctx, vi, xxs);
					}
				}
				continue;
			}

			/* First sample loop run */
			if ((~xxs->flg & XMP_SAMPLE_LOOP) || split_noloop) {
				do_anticlick(ctx, voc, buf_pos, size);
				set_sample_end(ctx, voc, 1);
				size = 0;
				continue;
			}

			loop_reposition(ctx, vi, xxs);
		}

		vi->old_vl = vol_l;
		vi->old_vr = vol_r;
	}

	/* Render final frame */

	size = s->ticksize;
	if (~s->format & XMP_FORMAT_MONO) {
		size *= 2;
	}

	if (size > XMP_MAX_FRAMESIZE) {
		size = XMP_MAX_FRAMESIZE;
	}

	if (s->format & XMP_FORMAT_8BIT) {
		downmix_int_8bit(s->buffer, s->buf32, size, s->amplify,
				s->format & XMP_FORMAT_UNSIGNED ? 0x80 : 0);
	} else {
		downmix_int_16bit((int16 *)s->buffer, s->buf32, size,s->amplify,
				s->format & XMP_FORMAT_UNSIGNED ? 0x8000 : 0);
	}

	s->dtright = s->dtleft = 0;
}

void libxmp_mixer_voicepos(struct context_data *ctx, int voc, double pos, int ac)
{
	struct player_data *p = &ctx->p;
	struct module_data *m = &ctx->m;
	struct mixer_voice *vi = &p->virt.voice_array[voc];
	struct xmp_sample *xxs;
	int lps;

	if (vi->smp < m->mod.smp) {
 		xxs = &m->mod.xxs[vi->smp];
	} else {
 		xxs = &ctx->smix.xxs[vi->smp - m->mod.smp];
	}

	if (xxs->flg & XMP_SAMPLE_SYNTH) {
		return;
	}

	vi->pos = pos;

	adjust_voice_end(vi, xxs);

	if (vi->pos >= vi->end) {
		if (xxs->flg & XMP_SAMPLE_LOOP) {
			vi->pos = xxs->lps;
		} else {
			vi->pos = xxs->len;
		}
	}

	lps = xxs->lps;
	if (p->flags & XMP_FLAGS_FIXLOOP) {
		lps >>= 1;
	}

	if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
		vi->end += (xxs->lpe - lps);

#ifndef LIBXMP_CORE_DISABLE_IT
		if (IS_PLAYER_MODE_IT()) {
			vi->end--;
		}
#endif
	}

	if (ac) {
		anticlick(vi);
	}
}

double libxmp_mixer_getvoicepos(struct context_data *ctx, int voc)
{
	struct player_data *p = &ctx->p;
	struct mixer_voice *vi = &p->virt.voice_array[voc];
	struct xmp_sample *xxs;

	xxs = libxmp_get_sample(ctx, vi->smp);

	if (xxs->flg & XMP_SAMPLE_SYNTH) {
		return 0;
	}

	if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
		if (vi->pos >= xxs->lpe) {
			return xxs->lpe - (vi->pos - xxs->lpe) - 1;
		}
	}

	return vi->pos;
}

void libxmp_mixer_setpatch(struct context_data *ctx, int voc, int smp, int ac)
{
	struct player_data *p = &ctx->p;
#ifndef LIBXMP_CORE_DISABLE_IT
	struct module_data *m = &ctx->m;
#endif
	struct mixer_data *s = &ctx->s;
	struct mixer_voice *vi = &p->virt.voice_array[voc];
	struct xmp_sample *xxs;

	xxs = libxmp_get_sample(ctx, smp);

	vi->smp = smp;
	vi->vol = 0;
	vi->pan = 0;
	vi->flags &= ~SAMPLE_LOOP;

	vi->fidx = 0;

	if (~s->format & XMP_FORMAT_MONO) {
		vi->fidx |= FLAG_STEREO;
	}

	set_sample_end(ctx, voc, 0);

	/*mixer_setvol(ctx, voc, 0);*/

	vi->sptr = xxs->data;
	vi->fidx |= FLAG_ACTIVE;

#ifndef LIBXMP_CORE_DISABLE_IT
	if (HAS_QUIRK(QUIRK_FILTER) && s->dsp & XMP_DSP_LOWPASS) {
		vi->fidx |= FLAG_FILTER;
	}
#endif

	if (xxs->flg & XMP_SAMPLE_16BIT) {
		vi->fidx |= FLAG_16_BITS;
	}

	libxmp_mixer_voicepos(ctx, voc, 0, ac);
}

void libxmp_mixer_setnote(struct context_data *ctx, int voc, int note)
{
	struct player_data *p = &ctx->p;
	struct mixer_voice *vi = &p->virt.voice_array[voc];

	/* FIXME: Workaround for crash on notes that are too high
	 *        see 6nations.it (+114 transposition on instrument 16)
	 */
	if (note > 149) {
		note = 149;
	}

	vi->note = note;
	vi->period = libxmp_note_to_period_mix(note, 0);

	anticlick(vi);
}

void libxmp_mixer_setperiod(struct context_data *ctx, int voc, double period)
{
	struct player_data *p = &ctx->p;
	struct mixer_voice *vi = &p->virt.voice_array[voc];

	vi->period = period;
}

void libxmp_mixer_setvol(struct context_data *ctx, int voc, int vol)
{
	struct player_data *p = &ctx->p;
	struct mixer_voice *vi = &p->virt.voice_array[voc];

	if (vol == 0) {
		anticlick(vi);
	}

	vi->vol = vol;
}

void libxmp_mixer_release(struct context_data *ctx, int voc, int rel)
{
	struct player_data *p = &ctx->p;
	struct mixer_voice *vi = &p->virt.voice_array[voc];

	if (rel) {
		vi->flags |= VOICE_RELEASE;
	} else {
		vi->flags &= ~VOICE_RELEASE;
	}
}

void libxmp_mixer_seteffect(struct context_data *ctx, int voc, int type, int val)
{
#ifndef LIBXMP_CORE_DISABLE_IT
	struct player_data *p = &ctx->p;
	struct mixer_voice *vi = &p->virt.voice_array[voc];

	switch (type) {
	case DSP_EFFECT_CUTOFF:
		vi->filter.cutoff = val;
		break;
	case DSP_EFFECT_RESONANCE:
		vi->filter.resonance = val;
		break;
	case DSP_EFFECT_FILTER_A0:
		vi->filter.a0 = val;
		break;
	case DSP_EFFECT_FILTER_B0:
		vi->filter.b0 = val;
		break;
	case DSP_EFFECT_FILTER_B1:
		vi->filter.b1 = val;
		break;
	}
#endif
}

void libxmp_mixer_setpan(struct context_data *ctx, int voc, int pan)
{
	struct player_data *p = &ctx->p;
	struct mixer_voice *vi = &p->virt.voice_array[voc];

	vi->pan = pan;
}

int libxmp_mixer_numvoices(struct context_data *ctx, int num)
{
	struct mixer_data *s = &ctx->s;

	if (num > s->numvoc || num < 0) {
		return s->numvoc;
	} else {
		return num;
	}
}

int libxmp_mixer_on(struct context_data *ctx, int rate, int format, int c4rate)
{
	struct mixer_data *s = &ctx->s;

	s->buffer = (char *) calloc(2, XMP_MAX_FRAMESIZE);
	if (s->buffer == NULL)
		goto err;

	s->buf32 = (int32 *) calloc(sizeof(int32), XMP_MAX_FRAMESIZE);
	if (s->buf32 == NULL)
		goto err1;

	s->freq = rate;
	s->format = format;
	s->amplify = DEFAULT_AMPLIFY;
	s->mix = DEFAULT_MIX;
	/* s->pbase = C4_PERIOD * c4rate / s->freq; */(void) c4rate;
	s->interp = XMP_INTERP_LINEAR;	/* default interpolation type */
	s->dsp = XMP_DSP_LOWPASS;	/* enable filters by default */
	/* s->numvoc = SMIX_NUMVOC; */
	s->dtright = s->dtleft = 0;

	return 0;

    err1:
	free(s->buffer);
	s->buffer = NULL;
    err:
	return -1;
}

void libxmp_mixer_off(struct context_data *ctx)
{
	struct mixer_data *s = &ctx->s;

	free(s->buffer);
	free(s->buf32);
	s->buf32 = NULL;
	s->buffer = NULL;
}


code:
#ifndef LIBXMP_MIXER_H
#define LIBXMP_MIXER_H

#define C4_PERIOD	428.0

#define SMIX_NUMVOC	128	/* default number of softmixer voices */
#define SMIX_SHIFT	16
#define SMIX_MASK	0xffff

#define FILTER_SHIFT	16
#define ANTICLICK_SHIFT	3

#ifdef LIBXMP_PAULA_SIMULATOR
#include "paula.h"
#endif

#define MIXER(f) void libxmp_mix_##f(struct mixer_voice *vi, int *buffer, \
	int count, int vl, int vr, int step, int ramp, int delta_l, int delta_r)

struct mixer_voice {
	int chn;		/* channel number */
	int root;		/* */
	int note;		/* */
#define PAN_SURROUND 0x8000
	int pan;		/* */
	int vol;		/* */
	double period;		/* current period */
	double pos;		/* position in sample */
	int pos0;		/* position in sample before mixing */
	int fidx;		/* mixer function index */
	int ins;		/* instrument number */
	int smp;		/* sample number */
	int end;		/* loop end */
	int act;		/* nna info & status of voice */
	int old_vl;		/* previous volume, left channel */
	int old_vr;		/* previous volume, right channel */
	int sleft;		/* last left sample output, in 32bit */
	int sright;		/* last right sample output, in 32bit */
#define VOICE_RELEASE	(1 << 0)
#define ANTICLICK	(1 << 1)
#define SAMPLE_LOOP	(1 << 2)
	int flags;		/* flags */
	void *sptr;		/* sample pointer */
#ifdef LIBXMP_PAULA_SIMULATOR
	struct paula_state *paula; /* paula simulation state */
#endif

#ifndef LIBXMP_CORE_DISABLE_IT
	struct {
		int r1;		/* filter variables */
		int r2;
		int l1;
		int l2;
		int a0;
		int b0;
		int b1;
		int cutoff;
		int resonance;
	} filter;
#endif
};

int	libxmp_mixer_on		(struct context_data *, int, int, int);
void	libxmp_mixer_off	(struct context_data *);
void    libxmp_mixer_setvol	(struct context_data *, int, int);
void    libxmp_mixer_seteffect	(struct context_data *, int, int, int);
void    libxmp_mixer_setpan	(struct context_data *, int, int);
int	libxmp_mixer_numvoices	(struct context_data *, int);
void	libxmp_mixer_softmixer	(struct context_data *);
void	libxmp_mixer_reset	(struct context_data *);
void	libxmp_mixer_setpatch	(struct context_data *, int, int, int);
void	libxmp_mixer_voicepos	(struct context_data *, int, double, int);
double	libxmp_mixer_getvoicepos(struct context_data *, int);
void	libxmp_mixer_setnote	(struct context_data *, int, int);
void	libxmp_mixer_setperiod	(struct context_data *, int, double);
void	libxmp_mixer_release	(struct context_data *, int, int);

#endif /* LIBXMP_MIXER_H */


code:
/* Extended Module Player
 * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

struct mod_instrument {
	uint8 name[22];		/* Instrument name */
	uint16 size;		/* Sample length in 16-bit words */
	int8 finetune;		/* Finetune (signed nibble) */
	int8 volume;		/* Linear playback volume */
	uint16 loop_start;	/* Loop start in 16-bit words */
	uint16 loop_size;	/* Loop length in 16-bit words */
};

struct mod_header {
	uint8 name[20];
	struct mod_instrument ins[31];
	uint8 len;
	uint8 restart;		/* Number of patterns in Soundtracker,
				 * Restart in Noisetracker/Startrekker,
				 * 0x7F in Protracker
				 */
	uint8 order[128];
	uint8 magic[4];
};

#ifndef LIBXMP_CORE_PLAYER
/* Soundtracker 15-instrument module header */

struct st_header {
	uint8 name[20];
	struct mod_instrument ins[15];
	uint8 len;
	uint8 restart;
	uint8 order[128];
};
#endif


code:
/* Extended Module Player
 * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/* This loader recognizes the following variants of the Protracker
 * module format:
 *
 * - Protracker M.K. and M!K!
 * - Protracker songs
 * - Noisetracker N.T. and M&K! (not tested)
 * - Fast Tracker 6CHN and 8CHN
 * - Fasttracker II/Take Tracker ?CHN and ??CH
 * - Mod's Grave M.K. w/ 8 channels (WOW)
 * - Atari Octalyser CD61 and CD81
 * - Digital Tracker FA04, FA06 and FA08
 * - TakeTracker TDZ4
 * - (unknown) NSMS
 */

#include <ctype.h>
#include <limits.h>
#include "loader.h"
#include "mod.h"

struct mod_magic {
	const char *magic;
	int flag;
	int id;
	int ch;
};

#define TRACKER_PROTRACKER	0
#define TRACKER_NOISETRACKER	1
#define TRACKER_SOUNDTRACKER	2
#define TRACKER_FASTTRACKER	3
#define TRACKER_FASTTRACKER2	4
#define TRACKER_OCTALYSER	5
#define TRACKER_TAKETRACKER	6
#define TRACKER_DIGITALTRACKER	7
#define TRACKER_FLEXTRAX	8
#define TRACKER_MODSGRAVE	9
#define TRACKER_SCREAMTRACKER3	10
#define TRACKER_OPENMPT		11
#define TRACKER_UNKNOWN_CONV	95
#define TRACKER_CONVERTEDST	96
#define TRACKER_CONVERTED	97
#define TRACKER_CLONE		98
#define TRACKER_UNKNOWN		99

#define TRACKER_PROBABLY_NOISETRACKER 20

const struct mod_magic mod_magic[] = {
	{"M.K.", 0, TRACKER_PROTRACKER, 4},
	{"M!K!", 1, TRACKER_PROTRACKER, 4},
	{"M&K!", 1, TRACKER_NOISETRACKER, 4},
	{"N.T.", 1, TRACKER_NOISETRACKER, 4},
	{"6CHN", 0, TRACKER_FASTTRACKER, 6},
	{"8CHN", 0, TRACKER_FASTTRACKER, 8},
	{"CD61", 1, TRACKER_OCTALYSER, 6},	/* Atari STe/Falcon */
	{"CD81", 1, TRACKER_OCTALYSER, 8},	/* Atari STe/Falcon */
	{"TDZ4", 1, TRACKER_TAKETRACKER, 4},	/* see XModule SaveTracker.c */
	{"FA04", 1, TRACKER_DIGITALTRACKER, 4},	/* Atari Falcon */
	{"FA06", 1, TRACKER_DIGITALTRACKER, 6},	/* Atari Falcon */
	{"FA08", 1, TRACKER_DIGITALTRACKER, 8},	/* Atari Falcon */
	{"NSMS", 1, TRACKER_UNKNOWN, 4},	/* in Kingdom.mod */
	{"", 0, 0, 0}
};

static int mod_test(HIO_HANDLE *, char *, const int);
static int mod_load(struct module_data *, HIO_HANDLE *, const int);

extern const struct format_loader libxmp_loader_mod;
const struct format_loader libxmp_loader_mod = {
	"Amiga Protracker/Compatible",
	mod_test,
	mod_load
};

static int validate_pattern(uint8 *buf)
{
	int i, j;

	for (i = 0; i < 64; i++) {
		for (j = 0; j < 4; j++) {
			uint8 *d = buf + (i * 4 + j) * 4;
			if ((d[0] >> 4) > 1) {
				D_(D_CRIT "invalid pattern data: row %d ch %d: %02x", i, j, d[0]);
				return -1;
			}
		}
	}

	return 0;
}

static int mod_test(HIO_HANDLE * f, char *t, const int start)
{
	int i;
	char buf[4];
	uint8 pat_buf[1024];
	int smp_size, num_pat;
	long size;
	int count;

	hio_seek(f, start + 1080, SEEK_SET);
	if (hio_read(buf, 1, 4, f) < 4) {
		return -1;
	}

	if (!strncmp(buf + 2, "CH", 2) && isdigit((int)buf[0])
	    && isdigit((int)buf[1])) {
		i = (buf[0] - '0') * 10 + buf[1] - '0';
		if (i > 0 && i <= 32) {
			goto found;
		}
	}

	if (!strncmp(buf + 1, "CHN", 3) && isdigit((int)*buf)) {
		if (*buf - '0') {
			goto found;
		}
	}

	for (i = 0; mod_magic[i].ch; i++) {
		if (!memcmp(buf, mod_magic[i].magic, 4))
			break;
	}
	if (mod_magic[i].ch == 0) {
		return -1;
	}

	/*
	 * Sanity check to prevent loading NoiseRunner and other module
	 * formats with valid magic at offset 1080
	 */

	hio_seek(f, start + 20, SEEK_SET);
	for (i = 0; i < 31; i++) {
		uint8 x;

		hio_seek(f, 22, SEEK_CUR);	/* Instrument name */

		/* OpenMPT can create mods with large samples */
		hio_read16b(f);	/* sample size */

		/* Chris Spiegel tells me that sandman.mod has 0x20 in finetune */
		x = hio_read8(f);
		if (x & 0xf0 && x != 0x20)	/* test finetune */
			return -1;
		if (hio_read8(f) > 0x40)	/* test volume */
			return -1;
		hio_read16b(f);	/* loop start */
		hio_read16b(f);	/* loop size */
	}

	/* Test for UNIC tracker modules
	 *
	 * From Gryzor's Pro-Wizard PW_FORMATS-Engl.guide:
	 * ``The UNIC format is very similar to Protracker... At least in the
	 * heading... same length : 1084 bytes. Even the "M.K." is present,
	 * sometimes !! Maybe to disturb the rippers.. hehe but Pro-Wizard
	 * doesn't test this only!''
	 */

	/* get file size */
	size = hio_size(f);
	smp_size = 0;
	hio_seek(f, start + 20, SEEK_SET);

	/* get samples size */
	for (i = 0; i < 31; i++) {
		hio_seek(f, 22, SEEK_CUR);
		smp_size += 2 * hio_read16b(f);	/* Length in 16-bit words */
		hio_seek(f, 6, SEEK_CUR);
	}

	/* get number of patterns */
	num_pat = 0;
	hio_seek(f, start + 952, SEEK_SET);
	for (i = 0; i < 128; i++) {
		uint8 x = hio_read8(f);
		if (x > 0x7f)
			break;
		if (x > num_pat)
			num_pat = x;
	}
	num_pat++;

	/* see if module size matches UNIC */
	if (start + 1084 + num_pat * 0x300 + smp_size == size) {
		D_(D_CRIT "module size matches UNIC");
		return -1;
	}

	/* validate pattern data in an attempt to catch UNICs with MOD size */
	for (count = i = 0; i < num_pat; i++) {
		hio_seek(f, start + 1084 + 1024 * i, SEEK_SET);
		if (!hio_read(pat_buf, 1024, 1, f)) {
			D_(D_WARN "pattern %d: failed to read pattern data", i);
			return -1;
		}
		if (validate_pattern(pat_buf) < 0) {
			D_(D_WARN "pattern %d: error in pattern data", i);
			/* Allow a few errors, "lexstacy" has 0x52 */
			count++;
		}
	}
	if (count > 2) {
		return -1;
	}

found:
	hio_seek(f, start + 0, SEEK_SET);
	libxmp_read_title(f, t, 20);

	return 0;
}

static int is_st_ins(char *s)
{
	if (s[0] != 's' && s[0] != 'S')
		return 0;
	if (s[1] != 't' && s[1] != 'T')
		return 0;
	if (s[2] != '-' || s[5] != ':')
		return 0;
	if (!isdigit((int)s[3]) || !isdigit((int)s[4]))
		return 0;

	return 1;
}

static int get_tracker_id(struct module_data *m, struct mod_header *mh, int id)
{
	struct xmp_module *mod = &m->mod;
	int has_loop_0 = 0;
	int has_vol_in_empty_ins = 0;
	int i;

	/* Check if has instruments with loop size 0 */
	for (i = 0; i < 31; i++) {
		if (mh->ins[i].loop_size == 0) {
			has_loop_0 = 1;
			break;
		}
	}

	/* Check if has instruments with size 0 and volume > 0 */
	for (i = 0; i < 31; i++) {
		if (mh->ins[i].size == 0 && mh->ins[i].volume > 0) {
			has_vol_in_empty_ins = 1;
			break;
		}
	}

	/*
	 * Test Protracker-like files
	 */
	if (mh->restart == mod->pat) {
		if (mod->chn == 4) {
			id = TRACKER_SOUNDTRACKER;
		} else {
			id = TRACKER_UNKNOWN;
		}
	} else if (mh->restart == 0x78) {
		if (mod->chn == 4) {
			/* Can't trust this for Noisetracker, MOD.Data City Remix
			 * has Protracker effects and Noisetracker restart byte
			 */
			id = TRACKER_PROBABLY_NOISETRACKER;
		} else {
			id = TRACKER_UNKNOWN;
		}
		return id;
	} else if (mh->restart < 0x7f) {
		if (mod->chn == 4 && !has_vol_in_empty_ins) {
			id = TRACKER_NOISETRACKER;
		} else {
			id = TRACKER_UNKNOWN; /* ? */
		}
		mod->rst = mh->restart;
	} else if (mh->restart == 0x7f) {
		if (mod->chn == 4) {
			if (has_loop_0) {
				id = TRACKER_CLONE;
			}
		} else {
			id = TRACKER_SCREAMTRACKER3;
		}
		return id;
	} else if (mh->restart > 0x7f) {
		id = TRACKER_UNKNOWN; /* ? */
		return id;
	}

	if (!has_loop_0) { /* All loops are size 2 or greater */

		for (i = 0; i < 31; i++) {
			if (mh->ins[i].size == 1 && mh->ins[i].volume == 0) {
				return TRACKER_CONVERTED;
			}
		}

		for (i = 0; i < 31; i++) {
			if (is_st_ins((char *)mh->ins[i].name))
				break;
		}
		if (i == 31) {	/* No st- instruments */
			for (i = 0; i < 31; i++) {
				if (mh->ins[i].size != 0
				    || mh->ins[i].loop_size != 1) {
					continue;
				}

				switch (mod->chn) {
				case 4:
					if (has_vol_in_empty_ins) {
						id = TRACKER_OPENMPT;
					} else {
						id = TRACKER_NOISETRACKER;
						/* or Octalyser */
					}
					break;
				case 6:
				case 8:
					id = TRACKER_OCTALYSER;
					break;
				default:
					id = TRACKER_UNKNOWN;
				}
				return id;
			}

			if (mod->chn == 4) {
				id = TRACKER_PROTRACKER;
			} else if (mod->chn == 6 || mod->chn == 8) {
				/* FastTracker 1.01? */
				id = TRACKER_FASTTRACKER;
			} else {
				id = TRACKER_UNKNOWN;
			}
		}
	} else { /* Has loops with size 0 */
		for (i = 15; i < 31; i++) {
			/* Is the name or size set? */
			if (mh->ins[i].name[0] || mh->ins[i].size > 0)
				break;
		}
		if (i == 31 && is_st_ins((char *)mh->ins[14].name)) {
			return TRACKER_CONVERTEDST;
		}

		/* Assume that Fast Tracker modules won't have ST- instruments */
		for (i = 0; i < 31; i++) {
			if (is_st_ins((char *)mh->ins[i].name))
				break;
		}
		if (i < 31) {
			return TRACKER_UNKNOWN_CONV;
		}

		if (mod->chn == 4 || mod->chn == 6 || mod->chn == 8) {
			return TRACKER_FASTTRACKER;
		}

		id = TRACKER_UNKNOWN;	/* ??!? */
	}

	return id;
}

static int mod_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
    struct xmp_module *mod = &m->mod;
    int i, j;
    int smp_size, ptsong = 0;
    struct xmp_event *event;
    struct mod_header mh;
    uint8 mod_event[4];
    const char *tracker = "";
    int detected = 0;
    char magic[8], idbuffer[32];
    int ptkloop = 0;			/* Protracker loop */
    int tracker_id = TRACKER_PROTRACKER;
    int out_of_range = 0;
    int maybe_wow = 1;

    LOAD_INIT();

    mod->ins = 31;
    mod->smp = mod->ins;
    mod->chn = 0;
    smp_size = 0;
    /*pat_size = 0;*/

    m->period_type = PERIOD_MODRNG;

    hio_read(mh.name, 20, 1, f);
    for (i = 0; i < 31; i++) {
	hio_read(mh.ins[i].name, 22, 1, f);	/* Instrument name */
	mh.ins[i].size = hio_read16b(f);	/* Length in 16-bit words */
	mh.ins[i].finetune = hio_read8(f);	/* Finetune (signed nibble) */
	mh.ins[i].volume = hio_read8(f);	/* Linear playback volume */
	mh.ins[i].loop_start = hio_read16b(f);	/* Loop start in 16-bit words */
	mh.ins[i].loop_size = hio_read16b(f);	/* Loop size in 16-bit words */

	/* Mod's Grave WOW files are converted from 669s and have default
	 * finetune and volume.
	 */
	if (mh.ins[i].size && (mh.ins[i].finetune != 0 || mh.ins[i].volume != 64))
	    maybe_wow = 0;

	smp_size += 2 * mh.ins[i].size;
    }
    mh.len = hio_read8(f);
    mh.restart = hio_read8(f);
    hio_read(mh.order, 128, 1, f);
    memset(magic, 0, sizeof(magic));
    hio_read(magic, 1, 4, f);
    if (hio_error(f)) {
        return -1;
    }

    /* Mod's Grave WOW files always have a 0 restart byte; 6692WOW implements
     * 669 repeating by inserting a pattern jump and ignores this byte.
     */
    if (mh.restart != 0)
	maybe_wow = 0;

    for (i = 0; mod_magic[i].ch; i++) {
	if (!(strncmp (magic, mod_magic[i].magic, 4))) {
	    mod->chn = mod_magic[i].ch;
	    tracker_id = mod_magic[i].id;
	    detected = mod_magic[i].flag;
	    break;
	}
    }

    if (mod->chn == 0) {
	if (!strncmp(magic + 2, "CH", 2) &&
	    isdigit((int)magic[0]) && isdigit((int)magic[1])) {
	    mod->chn = (*magic - '0') * 10 + magic[1] - '0';
	} else if (!strncmp(magic + 1, "CHN", 3) && isdigit((int)*magic)) {
	    mod->chn = *magic - '0';
	} else {
	    return -1;
	}
	tracker_id = mod->chn & 1 ? TRACKER_TAKETRACKER : TRACKER_FASTTRACKER2;
	detected = 1;
    }

    strncpy(mod->name, (char *) mh.name, 20);

    mod->len = mh.len;
    /* mod->rst = mh.restart; */

    if (mod->rst >= mod->len)
	mod->rst = 0;
    memcpy(mod->xxo, mh.order, 128);

    for (i = 0; i < 128; i++) {
	/* This fixes dragnet.mod (garbage in the order list) */
	if (mod->xxo[i] > 0x7f)
		break;
	if (mod->xxo[i] > mod->pat)
	    mod->pat = mod->xxo[i];
    }
    mod->pat++;

    /*pat_size = 256 * mod->chn * mod->pat;*/

    if (libxmp_init_instrument(m) < 0)
	return -1;

    for (i = 0; i < mod->ins; i++) {
	struct xmp_instrument *xxi;
	struct xmp_subinstrument *sub;
	struct xmp_sample *xxs;

	if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
	    return -1;

	if (mh.ins[i].size >= 0x8000) {
	    tracker_id = TRACKER_OPENMPT;
	    detected = 1;
	}

	xxi = &mod->xxi[i];
	sub = &xxi->sub[0];
	xxs = &mod->xxs[i];

	xxs->len = 2 * mh.ins[i].size;
	xxs->lps = 2 * mh.ins[i].loop_start;
	xxs->lpe = xxs->lps + 2 * mh.ins[i].loop_size;
	if (xxs->lpe > xxs->len) {
		xxs->lpe = xxs->len;
	}
	xxs->flg = (mh.ins[i].loop_size > 1 && xxs->lpe >= 4) ?
		XMP_SAMPLE_LOOP : 0;
	sub->fin = (int8)(mh.ins[i].finetune << 4);
	sub->vol = mh.ins[i].volume;
	sub->pan = 0x80;
	sub->sid = i;
	libxmp_instrument_name(mod, i, mh.ins[i].name, 22);

	if (xxs->len > 0) {
		xxi->nsm = 1;
	}
    }

    /*
     * Experimental tracker-detection routine
     */

    if (detected)
	goto skip_test;

    /* Test for Flextrax modules
     *
     * FlexTrax is a soundtracker for Atari Falcon030 compatible computers.
     * FlexTrax supports the standard MOD file format (up to eight channels)
     * for compatibility reasons but also features a new enhanced module
     * format FLX. The FLX format is an extended version of the standard
     * MOD file format with support for real-time sound effects like reverb
     * and delay.
     */

    if (0x43c + mod->pat * 4 * mod->chn * 0x40 + smp_size < m->size) {
	int pos = hio_tell(f);
	int num_read;
        if (pos < 0) {
           return -1;
        }
	hio_seek(f, start + 0x43c + mod->pat * 4 * mod->chn * 0x40 + smp_size, SEEK_SET);
	num_read = hio_read(idbuffer, 1, 4, f);
	hio_seek(f, start + pos, SEEK_SET);

	if (num_read == 4 && !memcmp(idbuffer, "FLEX", 4)) {
	    tracker_id = TRACKER_FLEXTRAX;
	    goto skip_test;
	}
    }

    /* Test for Mod's Grave WOW modules
     *
     * Stefan Danes <sdanes@marvels.hacktic.nl> said:
     * This weird format is identical to '8CHN' but still uses the 'M.K.' ID.
     * You can only test for WOW by calculating the size of the module for 8
     * channels and comparing this to the actual module length. If it's equal,
     * the module is an 8 channel WOW.
     *
     * Addendum: very rarely, WOWs will have an odd length due to an extra byte,
     * so round the filesize down in this check. False positive WOWs can be ruled
     * out by checking the restart byte and sample volume (see above).
     *
     * Worst case if there are still issues with this, OpenMPT validates later
     * patterns in potential WOW files (where sample data would be located in a
     * regular M.K. MOD) to rule out false positives.
     */

    if (!strncmp(magic, "M.K.", 4) && maybe_wow &&
		(0x43c + mod->pat * 32 * 0x40 + smp_size) == (m->size & ~1)) {
	mod->chn = 8;
	tracker_id = TRACKER_MODSGRAVE;
    } else {
	/* Test for Protracker song files */
	ptsong = !strncmp((char *)magic, "M.K.", 4) &&
		 (0x43c + mod->pat * 0x400 == m->size);
	if (ptsong) {
		tracker_id = TRACKER_PROTRACKER;
		goto skip_test;
	} else {
	/* something else */
		tracker_id = get_tracker_id(m, &mh, tracker_id);
	}
    }

skip_test:

    if (mod->chn >= XMP_MAX_CHANNELS) {
        return -1;
    }

    mod->trk = mod->chn * mod->pat;

    for (i = 0; i < mod->ins; i++) {
	D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %+d %c",
		i, mod->xxi[i].name,
		mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe,
		(mh.ins[i].loop_size > 1 && mod->xxs[i].lpe > 8) ?
			'L' : ' ', mod->xxi[i].sub[0].vol,
		mod->xxi[i].sub[0].fin >> 4,
		ptkloop && mod->xxs[i].lps == 0 && mh.ins[i].loop_size > 1 &&
			mod->xxs[i].len > mod->xxs[i].lpe ? '!' : ' ');
    }

    if (libxmp_init_pattern(mod) < 0)
	return -1;

    /* Load and convert patterns */
    D_(D_INFO "Stored patterns: %d", mod->pat);

    for (i = 0; i < mod->pat; i++) {
	long pos;

	if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
	    return -1;

	pos = hio_tell(f);
	if (pos < 0) {
		return -1;
	}

	for (j = 0; j < (64 * mod->chn); j++) {
	    int period;

	    event = &EVENT(i, j % mod->chn, j / mod->chn);
	    if (hio_read(mod_event, 1, 4, f) < 4) {
		return -1;
	    }

	    period = ((int)(LSN(mod_event[0])) << 8) | mod_event[1];
	    if (period != 0 && (period < 108 || period > 907)) {
		out_of_range = 1;
	    }

	    /* Filter noisetracker events */
	    if (tracker_id == TRACKER_PROBABLY_NOISETRACKER) {
		unsigned char fxt = LSN(mod_event[2]);
		unsigned char fxp = LSN(mod_event[3]);

		if ((fxt > 0x06 && fxt < 0x0a) || (fxt == 0x0e && fxp > 1)) {
		    tracker_id = TRACKER_UNKNOWN;
		}
	    }
	}

        if (out_of_range) {
	    if (tracker_id == TRACKER_UNKNOWN && mh.restart == 0x7f) {
		tracker_id = TRACKER_SCREAMTRACKER3;
	    }

	    /* Check out-of-range notes in Amiga trackers */
	    if (tracker_id == TRACKER_PROTRACKER ||
		tracker_id == TRACKER_NOISETRACKER ||
		tracker_id == TRACKER_PROBABLY_NOISETRACKER ||
		tracker_id == TRACKER_SOUNDTRACKER) {   /* note > B-3 */

		tracker_id = TRACKER_UNKNOWN;
	    }
	}

	hio_seek(f, pos, SEEK_SET);

	for (j = 0; j < (64 * mod->chn); j++) {
	    event = &EVENT(i, j % mod->chn, j / mod->chn);
	    if (hio_read(mod_event, 1, 4, f) < 4) {
		return -1;
	    }

	    switch (tracker_id) {
	    case TRACKER_PROBABLY_NOISETRACKER:
	    case TRACKER_NOISETRACKER:
	    	libxmp_decode_noisetracker_event(event, mod_event);
		break;
	    default:
	        libxmp_decode_protracker_event(event, mod_event);
	    }
	}
    }

    switch (tracker_id) {
    case TRACKER_PROTRACKER:
	tracker = "Protracker";
	ptkloop = 1;
	break;
    case TRACKER_PROBABLY_NOISETRACKER:
    case TRACKER_NOISETRACKER:
	tracker = "Noisetracker";
	m->quirk |= QUIRK_NOBPM;
	break;
    case TRACKER_SOUNDTRACKER:
	tracker = "Soundtracker";
	m->quirk |= QUIRK_NOBPM;
	break;
    case TRACKER_FASTTRACKER:
    case TRACKER_FASTTRACKER2:
	tracker = "Fast Tracker";
	m->period_type = PERIOD_AMIGA;
	break;
    case TRACKER_TAKETRACKER:
	tracker = "Take Tracker";
	m->period_type = PERIOD_AMIGA;
	break;
    case TRACKER_OCTALYSER:
	tracker = "Octalyser";
	break;
    case TRACKER_DIGITALTRACKER:
	tracker = "Digital Tracker";
	break;
    case TRACKER_FLEXTRAX:
	tracker = "Flextrax";
	break;
    case TRACKER_MODSGRAVE:
	tracker = "Mod's Grave";
	break;
    case TRACKER_SCREAMTRACKER3:
	tracker = "Scream Tracker";
	m->period_type = PERIOD_AMIGA;
	break;
    case TRACKER_CONVERTEDST:
    case TRACKER_CONVERTED:
	tracker = "Converted";
	break;
    case TRACKER_CLONE:
	tracker = "Protracker clone";
	m->period_type = PERIOD_AMIGA;
	break;
    case TRACKER_OPENMPT:
	tracker = "OpenMPT";
	ptkloop = 1;
	break;
    default:
    case TRACKER_UNKNOWN_CONV:
    case TRACKER_UNKNOWN:
	tracker = "Unknown tracker";
	m->period_type = PERIOD_AMIGA;
	break;
    }

    if (out_of_range) {
	m->period_type = PERIOD_AMIGA;
    }

    if (tracker_id == TRACKER_MODSGRAVE) {
	snprintf(mod->type, XMP_NAME_SIZE, "%s", tracker);
    } else {
	snprintf(mod->type, XMP_NAME_SIZE, "%s %s", tracker, magic);
    }

    MODULE_INFO();

    /* Load samples */

    D_(D_INFO "Stored samples: %d", mod->smp);

    for (i = 0; i < mod->smp; i++) {
	int flags;

	if (!mod->xxs[i].len)
	    continue;

	flags = (ptkloop && mod->xxs[i].lps == 0) ? SAMPLE_FLAG_FULLREP : 0;

	if (ptsong) {
#ifdef EDUKE32_DISABLE
	    HIO_HANDLE *s;
	    char sn[PATH_MAX];
	    char tmpname[32];
#endif // EDUKE32_DISABLE
	    const char *instname = mod->xxi[i].name;

	    if (!instname[0] || !m->dirname)
		continue;

#ifdef EDUKE32_DISABLE
	    if (libxmp_copy_name_for_fopen(tmpname, instname, 32))
		continue;

	    snprintf(sn, PATH_MAX, "%s%s", m->dirname, tmpname);

	    if ((s = hio_open(sn, "rb")) != NULL) {
	        if (libxmp_load_sample(m, s, flags, &mod->xxs[i], NULL) < 0) {
		    hio_close(s);
		    return -1;
		}
		hio_close(s);
	    }
#else
		return -1;
#endif // EDUKE32_DISABLE
	} else {
	    uint8 buf[5];
	    long pos;
	    int num;

	    if ((pos = hio_tell(f)) < 0) {
		return -1;
	    }
	    num = hio_read(buf, 1, 5, f);

	    if (num == 5 && !memcmp(buf, "ADPCM", 5)) {
		flags |= SAMPLE_FLAG_ADPCM;
	    } else {
		hio_seek(f, pos, SEEK_SET);
	    }

	    if (libxmp_load_sample(m, f, flags, &mod->xxs[i], NULL) < 0)
		return -1;
	}
    }

    if (tracker_id == TRACKER_PROTRACKER || tracker_id == TRACKER_OPENMPT) {
	m->quirk |= QUIRK_PROTRACK;
    } else if (tracker_id == TRACKER_SCREAMTRACKER3) {
	m->c4rate = C4_NTSC_RATE;
	m->quirk |= QUIRKS_ST3;
	m->read_event_type = READ_EVENT_ST3;
    } else if (tracker_id == TRACKER_FASTTRACKER || tracker_id == TRACKER_FASTTRACKER2 || tracker_id == TRACKER_TAKETRACKER || tracker_id == TRACKER_MODSGRAVE || mod->chn > 4) {
	m->c4rate = C4_NTSC_RATE;
	m->quirk |= QUIRKS_FT2 | QUIRK_FTMOD;
	m->read_event_type = READ_EVENT_FT2;
	m->period_type = PERIOD_AMIGA;
    }

    return 0;
}


code:
/* Extended Module Player
 * Copyright (C) 1996-2021 Claudio Matsuoka and Hipolito Carraro Jr
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "loader.h"

struct mtm_file_header {
	uint8 magic[3];		/* "MTM" */
	uint8 version;		/* MSN=major, LSN=minor */
	uint8 name[20];		/* ASCIIZ Module name */
	uint16 tracks;		/* Number of tracks saved */
	uint8 patterns;		/* Number of patterns saved */
	uint8 modlen;		/* Module length */
	uint16 extralen;	/* Length of the comment field */
	uint8 samples;		/* Number of samples */
	uint8 attr;		/* Always zero */
	uint8 rows;		/* Number rows per track */
	uint8 channels;		/* Number of tracks per pattern */
	uint8 pan[32];		/* Pan positions for each channel */
};

struct mtm_instrument_header {
	uint8 name[22];		/* Instrument name */
	uint32 length;		/* Instrument length in bytes */
	uint32 loop_start;	/* Sample loop start */
	uint32 loopend;		/* Sample loop end */
	uint8 finetune;		/* Finetune */
	uint8 volume;		/* Playback volume */
	uint8 attr;		/* &0x01: 16bit sample */
};

static int mtm_test(HIO_HANDLE *, char *, const int);
static int mtm_load(struct module_data *, HIO_HANDLE *, const int);

extern const struct format_loader libxmp_loader_mtm;
const struct format_loader libxmp_loader_mtm = {
	"Multitracker",
	mtm_test,
	mtm_load
};

static int mtm_test(HIO_HANDLE *f, char *t, const int start)
{
	uint8 buf[4];

	if (hio_read(buf, 1, 4, f) < 4)
		return -1;
	if (memcmp(buf, "MTM", 3))
		return -1;
	if (buf[3] != 0x10)
		return -1;

	libxmp_read_title(f, t, 20);

	return 0;
}

static int mtm_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
	struct xmp_module *mod = &m->mod;
	int i, j;
	struct mtm_file_header mfh;
	struct mtm_instrument_header mih;
	uint8 mt[192];

	LOAD_INIT();

	hio_read(mfh.magic, 3, 1, f);	/* "MTM" */
	mfh.version = hio_read8(f);	/* MSN=major, LSN=minor */
	hio_read(mfh.name, 20, 1, f);	/* ASCIIZ Module name */
	mfh.tracks = hio_read16l(f);	/* Number of tracks saved */
	mfh.patterns = hio_read8(f);	/* Number of patterns saved */
	mfh.modlen = hio_read8(f);	/* Module length */
	mfh.extralen = hio_read16l(f);	/* Length of the comment field */

	mfh.samples = hio_read8(f);	/* Number of samples */
	if (mfh.samples > 63) {
		return -1;
	}

	mfh.attr = hio_read8(f);	/* Always zero */

	mfh.rows = hio_read8(f);	/* Number rows per track */
	if (mfh.rows != 64)
		return -1;

	mfh.channels = hio_read8(f);	/* Number of tracks per pattern */
	if (mfh.channels > MIN(32, XMP_MAX_CHANNELS)) {
		return -1;
	}

	hio_read(mfh.pan, 32, 1, f);	/* Pan positions for each channel */

	if (hio_error(f)) {
		return -1;
	}

#if 0
	if (strncmp((char *)mfh.magic, "MTM", 3))
		return -1;
#endif

	mod->trk = mfh.tracks + 1;
	mod->pat = mfh.patterns + 1;
	mod->len = mfh.modlen + 1;
	mod->ins = mfh.samples;
	mod->smp = mod->ins;
	mod->chn = mfh.channels;
	mod->spd = 6;
	mod->bpm = 125;

	strncpy(mod->name, (char *)mfh.name, 20);
	libxmp_set_type(m, "MultiTracker %d.%02d MTM", MSN(mfh.version),
		 LSN(mfh.version));

	MODULE_INFO();

	if (libxmp_init_instrument(m) < 0)
		return -1;

	/* Read and convert instruments */
	for (i = 0; i < mod->ins; i++) {
		struct xmp_instrument *xxi = &mod->xxi[i];
		struct xmp_sample *xxs = &mod->xxs[i];
		struct xmp_subinstrument *sub;

		if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
			return -1;

		sub = &xxi->sub[0];

		hio_read(mih.name, 22, 1, f);	/* Instrument name */
		mih.length = hio_read32l(f);	/* Instrument length in bytes */

		if (mih.length > MAX_SAMPLE_SIZE)
			return -1;

		mih.loop_start = hio_read32l(f); /* Sample loop start */
		mih.loopend = hio_read32l(f);	/* Sample loop end */
		mih.finetune = hio_read8(f);	/* Finetune */
		mih.volume = hio_read8(f);	/* Playback volume */
		mih.attr = hio_read8(f);	/* &0x01: 16bit sample */

		xxs->len = mih.length;
		xxs->lps = mih.loop_start;
		xxs->lpe = mih.loopend;
		xxs->flg = xxs->lpe ? XMP_SAMPLE_LOOP : 0;	/* 1 == Forward loop */
		if (mfh.attr & 1) {
			xxs->flg |= XMP_SAMPLE_16BIT;
			xxs->len >>= 1;
			xxs->lps >>= 1;
			xxs->lpe >>= 1;
		}

		sub->vol = mih.volume;
		sub->fin = mih.finetune;
		sub->pan = 0x80;
		sub->sid = i;

		libxmp_instrument_name(mod, i, mih.name, 22);

		if (xxs->len > 0)
			mod->xxi[i].nsm = 1;

		D_(D_INFO "[%2X] %-22.22s %04x%c%04x %04x %c V%02x F%+03d\n", i,
		   xxi->name, xxs->len, xxs->flg & XMP_SAMPLE_16BIT ? '+' : ' ',
		   xxs->lps, xxs->lpe, xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
		   sub->vol, sub->fin - 0x80);
	}

	hio_read(mod->xxo, 1, 128, f);

	if (libxmp_init_pattern(mod) < 0)
		return -1;

	D_(D_INFO "Stored tracks: %d", mod->trk - 1);

	for (i = 0; i < mod->trk; i++) {

		if (libxmp_alloc_track(mod, i, mfh.rows) < 0)
			return -1;

		if (i == 0)
			continue;

		if (hio_read(mt, 3, 64, f) != 64)
			return -1;

		for (j = 0; j < 64; j++) {
			struct xmp_event *e = &mod->xxt[i]->event[j];
			uint8 *d = mt + j * 3;

			e->note = d[0] >> 2;
			if (e->note) {
				e->note += 37;
			}
			e->ins = ((d[0] & 0x3) << 4) + MSN(d[1]);
			e->fxt = LSN(d[1]);
			e->fxp = d[2];
			if (e->fxt > FX_SPEED) {
				e->fxt = e->fxp = 0;
			}

			/* Set pan effect translation */
			if (e->fxt == FX_EXTENDED && MSN(e->fxp) == 0x8) {
				e->fxt = FX_SETPAN;
				e->fxp <<= 4;
			}
		}
	}

	/* Read patterns */
	D_(D_INFO "Stored patterns: %d", mod->pat - 1);

	for (i = 0; i < mod->pat; i++) {
		if (libxmp_alloc_pattern(mod, i) < 0)
			return -1;

		mod->xxp[i]->rows = 64;
		for (j = 0; j < 32; j++) {
			int track = hio_read16l(f);

			if (track >= mod->trk) {
				track = 0;
			}

			if (j < mod->chn) {
				mod->xxp[i]->index[j] = track;
			}
		}
	}

	/* Comments */
	hio_seek(f, mfh.extralen, SEEK_CUR);

	/* Read samples */
	D_(D_INFO "Stored samples: %d", mod->smp);

	for (i = 0; i < mod->ins; i++) {
		if (libxmp_load_sample(m, f, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0)
			return -1;
	}

	for (i = 0; i < mod->chn; i++)
		mod->xxc[i].pan = mfh.pan[i] << 4;

	return 0;
}