--- /dev/null
+/* MikMod sound library\r
+ (c) 2004, Raphael Assenat and others - see file AUTHORS for\r
+ complete list.\r
+\r
+ This library is free software; you can redistribute it and/or modify\r
+ it under the terms of the GNU Library General Public License as\r
+ published by the Free Software Foundation; either version 2 of\r
+ the License, or (at your option) any later version.\r
+ \r
+ This program is distributed in the hope that it will be useful,\r
+ but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ GNU Library General Public License for more details.\r
+ \r
+ You should have received a copy of the GNU Library General Public\r
+ License along with this library; if not, write to the Free Software\r
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\r
+ 02111-1307, USA.\r
+*/\r
+\r
+/*==============================================================================\r
+\r
+ $Id: load_asy.c,v 1.3 2004/01/28 01:18:22 raph Exp $\r
+\r
+ ASYLUM Music Format v1.0 (.amf) loader\r
+ adapted from load_mod.c by Raphael Assenat <raph@raphnet.net>,\r
+ with the help of the AMF2MOD utility sourcecode,\r
+ written to convert crusader's amf files into 8\r
+ channels mod file in 1995 by Mr. P / Powersource\r
+ mrp@fish.share.net, ac054@sfn.saskatoon.sk.ca \r
+ \r
+\r
+==============================================================================*/\r
+\r
+#ifdef HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif\r
+\r
+#ifdef HAVE_UNISTD_H\r
+#include <unistd.h>\r
+#endif\r
+\r
+//#include <ctype.h>\r
+#include <string.h>\r
+\r
+#include "mikmod_internals.h"\r
+\r
+/*========== Module structure */\r
+\r
+typedef struct MSAMPINFO {\r
+ CHAR samplename[24];\r
+ UBYTE finetune;\r
+ UBYTE volume;\r
+ ULONG length;\r
+ ULONG reppos;\r
+ ULONG replen;\r
+} MSAMPINFO;\r
+\r
+typedef struct MODULEHEADER {\r
+ CHAR songname[21];\r
+ UBYTE num_patterns; /* number of patterns used */\r
+ UBYTE num_orders;\r
+ UBYTE positions[256]; /* which pattern to play at pos */\r
+ MSAMPINFO samples[64]; /* all sampleinfo */\r
+} MODULEHEADER;\r
+\r
+typedef struct MODTYPE {\r
+ CHAR id[5];\r
+ UBYTE channels;\r
+ CHAR *name;\r
+} MODTYPE;\r
+\r
+typedef struct MODNOTE {\r
+ UBYTE a, b, c, d;\r
+} MODNOTE;\r
+\r
+/* This table is taken from AMF2MOD.C\r
+ * written in 1995 by Mr. P / Powersource\r
+ * mrp@fish.share.net, ac054@sfn.saskatoon.sk.ca */ \r
+UWORD periodtable[]={6848,6464,6096,5760,5424,5120,4832,4560,4304,\r
+ 4064,3840,3628,3424,3232,3048,2880,2712,2560,\r
+ 2416,2280,2152,2032,1920,1814,1712,1616,1524,\r
+ 1440,1356,1280,1208,1140,1076,1016, 960, 907,\r
+ 856, 808, 762, 720, 678, 640, 604, 570, 538,\r
+ 508, 480, 453, 428, 404, 381, 360, 339, 320,\r
+ 302, 285, 269, 254, 240, 226, 214, 202, 190,\r
+ 180, 170, 160, 151, 143, 135, 127, 120, 113,\r
+ 107, 101, 95, 90, 85, 80, 75, 71, 67,\r
+ 63, 60, 56, 53, 50, 47, 45, 42, 40,\r
+ 37, 35, 33, 31, 30, 28};\r
+\r
+/*========== Loader variables */\r
+\r
+static CHAR asylum[] = "Asylum 1.0";\r
+\r
+static MODULEHEADER *mh = NULL;\r
+static MODNOTE *patbuf = NULL;\r
+static int modtype = 0;\r
+\r
+/*========== Loader code */\r
+\r
+static BOOL ASY_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr)\r
+{\r
+ if (!memcmp(id, "ASYLUM Music Format V1.0", 24))\r
+ {\r
+ *descr = asylum;\r
+ *numchn = 8;\r
+ modtype = 1;\r
+ return 1;\r
+ }\r
+ \r
+ return 0;\r
+}\r
+\r
+static BOOL ASY_Test(void)\r
+{\r
+ UBYTE namestring[24], numchn;\r
+ CHAR *descr;\r
+\r
+ /* Read the magic string */\r
+ _mm_fseek(modreader, 0, SEEK_SET);\r
+ if (!_mm_read_UBYTES(namestring, 24, modreader))\r
+ return 0;\r
+\r
+ /* Test if the string is what we expect */\r
+ if (ASY_CheckType(namestring, &numchn, &descr))\r
+ return 1;\r
+\r
+ return 0;\r
+}\r
+\r
+static BOOL ASY_Init(void)\r
+{\r
+ if (!(mh = (MODULEHEADER *)_mm_malloc(sizeof(MODULEHEADER))))\r
+ return 0;\r
+ return 1;\r
+}\r
+\r
+static void ASY_Cleanup(void)\r
+{\r
+ _mm_free(mh);\r
+ _mm_free(patbuf);\r
+}\r
+\r
+static void ConvertNote(MODNOTE *n)\r
+{\r
+ UBYTE instrument, effect, effdat, note;\r
+ UWORD period;\r
+ UBYTE lastnote = 0;\r
+ \r
+ instrument = n->b&0x1f;\r
+ effect = n->c;\r
+ effdat = n->d;\r
+\r
+ /* convert amf note to mod period */\r
+ if (n->a) {\r
+ period = periodtable[n->a];\r
+ } else {\r
+ period = 0;\r
+ }\r
+ \r
+ /* Convert the period to a note number */\r
+ note = 0;\r
+ if (period) \r
+ {\r
+ for (note = 0; note < 7 * OCTAVE; note++)\r
+ if (period >= npertab[note])\r
+ break;\r
+ if (note == 7 * OCTAVE)\r
+ note = 0;\r
+ else\r
+ note++;\r
+ }\r
+\r
+ if (instrument) {\r
+ /* if instrument does not exist, note cut */\r
+ if ((instrument > 31) || (!mh->samples[instrument - 1].length)) {\r
+ UniPTEffect(0xc, 0);\r
+ if (effect == 0xc)\r
+ effect = effdat = 0;\r
+ } else {\r
+ /* Protracker handling */\r
+ if (!modtype) {\r
+ /* if we had a note, then change instrument...*/\r
+ if (note)\r
+ UniInstrument(instrument - 1);\r
+ /* ...otherwise, only adjust volume... */\r
+ else {\r
+ /* ...unless an effect was specified, \r
+ * which forces a new note to be \r
+ * played */\r
+ if (effect || effdat) {\r
+ UniInstrument(instrument - 1);\r
+ note = lastnote;\r
+ } else\r
+ UniPTEffect(0xc,\r
+ mh->samples[instrument -\r
+ 1].volume & 0x7f);\r
+ }\r
+ } else {\r
+ /* Fasttracker handling */\r
+ UniInstrument(instrument - 1);\r
+ if (!note)\r
+ note = lastnote;\r
+ }\r
+ }\r
+ }\r
+ if (note) {\r
+ UniNote(note + 2 * OCTAVE - 1);\r
+ lastnote = note;\r
+ }\r
+\r
+ /* Convert pattern jump from Dec to Hex */\r
+ if (effect == 0xd)\r
+ effdat = (((effdat & 0xf0) >> 4) * 10) + (effdat & 0xf);\r
+\r
+ /* Volume slide, up has priority */\r
+ if ((effect == 0xa) && (effdat & 0xf) && (effdat & 0xf0))\r
+ effdat &= 0xf0;\r
+\r
+ UniPTEffect(effect, effdat);\r
+}\r
+\r
+static UBYTE *ConvertTrack(MODNOTE *n)\r
+{\r
+ int t;\r
+\r
+ UniReset();\r
+ for (t = 0; t < 64; t++) {\r
+ ConvertNote(n);\r
+ UniNewline();\r
+ n += of.numchn;\r
+ }\r
+ return UniDup();\r
+}\r
+\r
+/* Loads all patterns of a modfile and converts them into the 3 byte format. */\r
+static BOOL ML_LoadPatterns(void)\r
+{\r
+ int t, s, tracks = 0;\r
+\r
+ if (!AllocPatterns()) {\r
+ return 0;\r
+ }\r
+ if (!AllocTracks()) {\r
+ return 0;\r
+ }\r
+ \r
+ /* Allocate temporary buffer for loading and converting the patterns */\r
+ if (!(patbuf = (MODNOTE *)_mm_calloc(64U * of.numchn, sizeof(MODNOTE))))\r
+ return 0;\r
+\r
+\r
+ /* patterns start here */\r
+ _mm_fseek(modreader, 0xA66, SEEK_SET);\r
+ for (t = 0; t < of.numpat; t++) {\r
+ /* Load the pattern into the temp buffer and convert it */\r
+ for (s = 0; s < (64U * of.numchn); s++) {\r
+ patbuf[s].a = _mm_read_UBYTE(modreader);\r
+ patbuf[s].b = _mm_read_UBYTE(modreader);\r
+ patbuf[s].c = _mm_read_UBYTE(modreader);\r
+ patbuf[s].d = _mm_read_UBYTE(modreader);\r
+ }\r
+ for (s = 0; s < of.numchn; s++) {\r
+ if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s))) {\r
+ return 0;\r
+ }\r
+ }\r
+ }\r
+ return 1;\r
+}\r
+\r
+static BOOL ASY_Load(BOOL curious)\r
+{\r
+ int t;\r
+ SAMPLE *q;\r
+ MSAMPINFO *s;\r
+ CHAR *descr=asylum;\r
+ ULONG seekpos;\r
+ (void)curious;\r
+\r
+ // no title in asylum amf files :(\r
+ strcpy(mh->songname, "");\r
+\r
+ _mm_fseek(modreader, 0x23, SEEK_SET);\r
+ mh->num_patterns = _mm_read_UBYTE(modreader);\r
+ mh->num_orders = _mm_read_UBYTE(modreader);\r
+ \r
+ // skip unknown byte\r
+ _mm_read_UBYTE(modreader);\r
+ _mm_read_UBYTES(mh->positions, 256, modreader);\r
+ \r
+ /* read samples headers*/\r
+ for (t = 0; t < 64; t++) {\r
+ s = &mh->samples[t];\r
+ \r
+ _mm_fseek(modreader, 0x126 + (t*37), SEEK_SET);\r
+ \r
+ _mm_read_string(s->samplename, 22, modreader);\r
+ s->samplename[21] = 0; /* just in case */\r
+ \r
+ s->finetune = _mm_read_UBYTE(modreader);\r
+ s->volume = _mm_read_UBYTE(modreader);\r
+ _mm_read_UBYTE(modreader); // skip unknown byte\r
+ s->length = _mm_read_I_ULONG(modreader);\r
+ s->reppos = _mm_read_I_ULONG(modreader);\r
+ s->replen = _mm_read_I_ULONG(modreader);\r
+ }\r
+\r
+ if (_mm_eof(modreader)) {\r
+ _mm_errno = MMERR_LOADING_HEADER;\r
+ return 0;\r
+ }\r
+\r
+ /* set module variables */\r
+ of.initspeed = 6;\r
+ of.inittempo = 125;\r
+ of.numchn = 8;\r
+ modtype = 0;\r
+ of.songname = DupStr(mh->songname, 21, 1);\r
+ of.numpos = mh->num_orders;\r
+ of.reppos = 0;\r
+ of.numpat = mh->num_patterns;\r
+ of.numtrk = of.numpat * of.numchn;\r
+\r
+ \r
+ /* Copy positions (orders) */\r
+ if (!AllocPositions(of.numpos))\r
+ return 0;\r
+ for (t = 0; t < of.numpos; t++) {\r
+ of.positions[t] = mh->positions[t];\r
+ }\r
+\r
+ /* Finally, init the sampleinfo structures */\r
+ of.numins = 31;\r
+ of.numsmp = 31;\r
+ if (!AllocSamples())\r
+ return 0;\r
+ s = mh->samples;\r
+ q = of.samples;\r
+ seekpos = 2662+(2048*(of.numpat));\r
+ for (t = 0; t < of.numins; t++) {\r
+ /* convert the samplename */\r
+ q->samplename = DupStr(s->samplename, 23, 1);\r
+ \r
+ /* init the sampleinfo variables */\r
+ q->speed = finetune[s->finetune & 0xf];\r
+ q->volume = s->volume & 0x7f;\r
+\r
+ q->loopstart = (ULONG)s->reppos;\r
+ q->loopend = (ULONG)q->loopstart + (s->replen);\r
+ q->length = (ULONG)s->length;\r
+ \r
+ q->flags = SF_SIGNED;\r
+ \r
+ q->seekpos = seekpos;\r
+ seekpos += q->length;\r
+ \r
+ if ((s->replen) > 2) {\r
+ q->flags |= SF_LOOP;\r
+ }\r
+ \r
+ /* fix replen if repend > length */\r
+ if (q->loopend > q->length)\r
+ q->loopend = q->length;\r
+\r
+ s++;\r
+ q++;\r
+ }\r
+\r
+ of.modtype = strdup(descr);\r
+\r
+ if (!ML_LoadPatterns())\r
+ return 0;\r
+\r
+ return 1;\r
+}\r
+\r
+static CHAR *ASY_LoadTitle(void)\r
+{\r
+ CHAR *s = ""; // no titles\r
+\r
+ return (DupStr(s, 21, 1));\r
+}\r
+\r
+/*========== Loader information */\r
+\r
+MLOADER load_asy = {\r
+ NULL,\r
+ "AMF",\r
+ "AMF (ASYLUM Music Format V1.0)",\r
+ ASY_Init,\r
+ ASY_Test,\r
+ ASY_Load,\r
+ ASY_Cleanup,\r
+ ASY_LoadTitle\r
+};\r
+\r
+/* ex:set ts=4: */\r