--- /dev/null
+/* MikMod sound library\r
+ (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file\r
+ AUTHORS for 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_mod.c,v 1.2 2004/01/21 13:33:11 raph Exp $\r
+\r
+ Generic MOD loader (Protracker, StarTracker, FastTracker, etc)\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 <stdio.h>\r
+#ifdef HAVE_MEMORY_H\r
+#include <memory.h>\r
+#endif\r
+#include <string.h>\r
+\r
+#include "mikmod_internals.h"\r
+\r
+#ifdef SUNOS\r
+extern int fprintf(FILE *, const char *, ...);\r
+#endif\r
+\r
+/*========== Module structure */\r
+\r
+typedef struct MSAMPINFO {\r
+ CHAR samplename[23]; /* 22 in module, 23 in memory */\r
+ UWORD length;\r
+ UBYTE finetune;\r
+ UBYTE volume;\r
+ UWORD reppos;\r
+ UWORD replen;\r
+} MSAMPINFO;\r
+\r
+typedef struct MODULEHEADER {\r
+ CHAR songname[21]; /* the songname.. 20 in module, 21 in memory */\r
+ MSAMPINFO samples[31]; /* all sampleinfo */\r
+ UBYTE songlength; /* number of patterns used */\r
+ UBYTE magic1; /* should be 127 */\r
+ UBYTE positions[128]; /* which pattern to play at pos */\r
+ UBYTE magic2[4]; /* string "M.K." or "FLT4" or "FLT8" */\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
+/*========== Loader variables */\r
+\r
+#define MODULEHEADERSIZE 0x438\r
+\r
+static CHAR protracker[] = "Protracker";\r
+static CHAR startrekker[] = "Startrekker";\r
+static CHAR fasttracker[] = "Fasttracker";\r
+static CHAR oktalyser[] = "Oktalyser";\r
+static CHAR oktalyzer[] = "Oktalyzer";\r
+static CHAR taketracker[] = "TakeTracker";\r
+static CHAR orpheus[] = "Imago Orpheus (MOD format)";\r
+\r
+static MODULEHEADER *mh = NULL;\r
+static MODNOTE *patbuf = NULL;\r
+static int modtype, trekker;\r
+\r
+/*========== Loader code */\r
+\r
+/* given the module ID, determine the number of channels and the tracker\r
+ description ; also alters modtype */\r
+static BOOL MOD_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr)\r
+{\r
+ modtype = trekker = 0;\r
+\r
+ /* Protracker and variants */\r
+ if ((!memcmp(id, "M.K.", 4)) || (!memcmp(id, "M!K!", 4))) {\r
+ *descr = protracker;\r
+ modtype = 0;\r
+ *numchn = 4;\r
+ return 1;\r
+ }\r
+ \r
+ /* Star Tracker */\r
+ if (((!memcmp(id, "FLT", 3)) || (!memcmp(id, "EXO", 3))) &&\r
+ (isdigit(id[3]))) {\r
+ *descr = startrekker;\r
+ modtype = trekker = 1;\r
+ *numchn = id[3] - '0';\r
+ if (*numchn == 4 || *numchn == 8)\r
+ return 1;\r
+#ifdef MIKMOD_DEBUG\r
+ else\r
+ fprintf(stderr, "\rUnknown FLT%d module type\n", *numchn);\r
+#endif\r
+ return 0;\r
+ }\r
+\r
+ /* Oktalyzer (Amiga) */\r
+ if (!memcmp(id, "OKTA", 4)) {\r
+ *descr = oktalyzer;\r
+ modtype = 1;\r
+ *numchn = 8;\r
+ return 1;\r
+ }\r
+\r
+ /* Oktalyser (Atari) */\r
+ if (!memcmp(id, "CD81", 4)) {\r
+ *descr = oktalyser;\r
+ modtype = 1;\r
+ *numchn = 8;\r
+ return 1;\r
+ }\r
+\r
+ /* Fasttracker */\r
+ if ((!memcmp(id + 1, "CHN", 3)) && (isdigit(id[0]))) {\r
+ *descr = fasttracker;\r
+ modtype = 1;\r
+ *numchn = id[0] - '0';\r
+ return 1;\r
+ }\r
+ /* Fasttracker or Taketracker */\r
+ if (((!memcmp(id + 2, "CH", 2)) || (!memcmp(id + 2, "CN", 2)))\r
+ && (isdigit(id[0])) && (isdigit(id[1]))) {\r
+ if (id[3] == 'H') {\r
+ *descr = fasttracker;\r
+ modtype = 2; /* this can also be Imago Orpheus */\r
+ } else {\r
+ *descr = taketracker;\r
+ modtype = 1;\r
+ }\r
+ *numchn = (id[0] - '0') * 10 + (id[1] - '0');\r
+ return 1;\r
+ }\r
+\r
+ return 0;\r
+}\r
+\r
+static BOOL MOD_Test(void)\r
+{\r
+ UBYTE id[4], numchn;\r
+ CHAR *descr;\r
+\r
+ _mm_fseek(modreader, MODULEHEADERSIZE, SEEK_SET);\r
+ if (!_mm_read_UBYTES(id, 4, modreader))\r
+ return 0;\r
+\r
+ if (MOD_CheckType(id, &numchn, &descr))\r
+ return 1;\r
+\r
+ return 0;\r
+}\r
+\r
+static BOOL MOD_Init(void)\r
+{\r
+ if (!(mh = (MODULEHEADER *)_mm_malloc(sizeof(MODULEHEADER))))\r
+ return 0;\r
+ return 1;\r
+}\r
+\r
+static void MOD_Cleanup(void)\r
+{\r
+ _mm_free(mh);\r
+ _mm_free(patbuf);\r
+}\r
+\r
+/*\r
+Old (amiga) noteinfo:\r
+\r
+_____byte 1_____ byte2_ _____byte 3_____ byte4_\r
+/ \ / \ / \ / \\r
+0000 0000-00000000 0000 0000-00000000\r
+\r
+Upper four 12 bits for Lower four Effect command.\r
+bits of sam- note period. bits of sam-\r
+ple number. ple number.\r
+\r
+*/\r
+\r
+static UBYTE ConvertNote(MODNOTE *n, UBYTE lasteffect)\r
+{\r
+ UBYTE instrument, effect, effdat, note;\r
+ UWORD period;\r
+ UBYTE lastnote = 0;\r
+\r
+ /* extract the various information from the 4 bytes that make up a note */\r
+ instrument = (n->a & 0x10) | (n->c >> 4);\r
+ period = (((UWORD)n->a & 0xf) << 8) + n->b;\r
+ effect = n->c & 0xf;\r
+ effdat = n->d;\r
+\r
+ /* Convert the period to a note number */\r
+ note = 0;\r
+ if (period) {\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, which forces a new\r
+ note to be 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
+ /* Handle ``heavy'' volumes correctly */\r
+ if ((effect == 0xc) && (effdat > 0x40))\r
+ effdat = 0x40;\r
+ \r
+ /* An isolated 100, 200 or 300 effect should be ignored (no\r
+ "standalone" porta memory in mod files). However, a sequence such\r
+ as 1XX, 100, 100, 100 is fine. */\r
+ if ((!effdat) && ((effect == 1)||(effect == 2)||(effect ==3)) &&\r
+ (lasteffect < 0x10) && (effect != lasteffect))\r
+ effect = 0;\r
+\r
+ UniPTEffect(effect, effdat);\r
+ if (effect == 8)\r
+ of.flags |= UF_PANNING;\r
+ \r
+ return effect;\r
+}\r
+\r
+static UBYTE *ConvertTrack(MODNOTE *n, int numchn)\r
+{\r
+ int t;\r
+ UBYTE lasteffect = 0x10; /* non existant effect */\r
+\r
+ UniReset();\r
+ for (t = 0; t < 64; t++) {\r
+ lasteffect = ConvertNote(n,lasteffect);\r
+ UniNewline();\r
+ n += 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
+ if (!AllocTracks())\r
+ return 0;\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
+ if (trekker && of.numchn == 8) {\r
+ /* Startrekker module dual pattern */\r
+ for (t = 0; t < of.numpat; t++) {\r
+ for (s = 0; s < (64U * 4); 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 < 4; s++)\r
+ if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, 4)))\r
+ return 0;\r
+ for (s = 0; s < (64U * 4); 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 < 4; s++)\r
+ if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, 4)))\r
+ return 0;\r
+ }\r
+ } else {\r
+ /* Generic module pattern */\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, of.numchn)))\r
+ return 0;\r
+ }\r
+ }\r
+ return 1;\r
+}\r
+\r
+static BOOL MOD_Load(BOOL curious)\r
+{\r
+ int t, scan;\r
+ SAMPLE *q;\r
+ MSAMPINFO *s;\r
+ CHAR *descr;\r
+\r
+ /* try to read module header */\r
+ _mm_read_string((CHAR *)mh->songname, 20, modreader);\r
+ mh->songname[20] = 0; /* just in case */\r
+\r
+ for (t = 0; t < 31; t++) {\r
+ s = &mh->samples[t];\r
+ _mm_read_string(s->samplename, 22, modreader);\r
+ s->samplename[22] = 0; /* just in case */\r
+ s->length = _mm_read_M_UWORD(modreader);\r
+ s->finetune = _mm_read_UBYTE(modreader);\r
+ s->volume = _mm_read_UBYTE(modreader);\r
+ s->reppos = _mm_read_M_UWORD(modreader);\r
+ s->replen = _mm_read_M_UWORD(modreader);\r
+ }\r
+\r
+ mh->songlength = _mm_read_UBYTE(modreader);\r
+\r
+ /* this fixes mods which declare more than 128 positions. \r
+ * eg: beatwave.mod */\r
+ if (mh->songlength > 128) { mh->songlength = 128; }\r
+ \r
+ mh->magic1 = _mm_read_UBYTE(modreader);\r
+ _mm_read_UBYTES(mh->positions, 128, modreader);\r
+ _mm_read_UBYTES(mh->magic2, 4, modreader);\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
+ if (!(MOD_CheckType(mh->magic2, &of.numchn, &descr))) {\r
+ _mm_errno = MMERR_NOT_A_MODULE;\r
+ return 0;\r
+ }\r
+ if (trekker && of.numchn == 8)\r
+ for (t = 0; t < 128; t++)\r
+ /* if module pretends to be FLT8, yet the order table\r
+ contains odd numbers, chances are it's a lying FLT4... */\r
+ if (mh->positions[t] & 1) {\r
+ of.numchn = 4;\r
+ break;\r
+ }\r
+ if (trekker && of.numchn == 8)\r
+ for (t = 0; t < 128; t++)\r
+ mh->positions[t] >>= 1;\r
+\r
+ of.songname = DupStr(mh->songname, 21, 1);\r
+ of.numpos = mh->songlength;\r
+ of.reppos = 0;\r
+\r
+ /* Count the number of patterns */\r
+ of.numpat = 0;\r
+ for (t = 0; t < of.numpos; t++)\r
+ if (mh->positions[t] > of.numpat)\r
+ of.numpat = mh->positions[t];\r
+\r
+ /* since some old modules embed extra patterns, we have to check the\r
+ whole list to get the samples' file offsets right - however we can find\r
+ garbage here, so check carefully */\r
+ scan = 1;\r
+ for (t = of.numpos; t < 128; t++)\r
+ if (mh->positions[t] >= 0x80)\r
+ scan = 0;\r
+ if (scan)\r
+ for (t = of.numpos; t < 128; t++) {\r
+ if (mh->positions[t] > of.numpat)\r
+ of.numpat = mh->positions[t];\r
+ if ((curious) && (mh->positions[t]))\r
+ of.numpos = t + 1;\r
+ }\r
+ of.numpat++;\r
+ of.numtrk = of.numpat * of.numchn;\r
+\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
+ /* Finally, init the sampleinfo structures */\r
+ of.numins = of.numsmp = 31;\r
+ if (!AllocSamples())\r
+ return 0;\r
+ s = mh->samples;\r
+ q = of.samples;\r
+ for (t = 0; t < of.numins; t++) {\r
+ /* convert the samplename */\r
+ q->samplename = DupStr(s->samplename, 23, 1);\r
+ /* init the sampleinfo variables and convert the size pointers */\r
+ q->speed = finetune[s->finetune & 0xf];\r
+ q->volume = s->volume & 0x7f;\r
+ q->loopstart = (ULONG)s->reppos << 1;\r
+ q->loopend = q->loopstart + ((ULONG)s->replen << 1);\r
+ q->length = (ULONG)s->length << 1;\r
+ q->flags = SF_SIGNED;\r
+ /* Imago Orpheus creates MODs with 16 bit samples, check */\r
+ if ((modtype == 2) && (s->volume & 0x80)) {\r
+ q->flags |= SF_16BITS;\r
+ descr = orpheus;\r
+ }\r
+ if (s->replen > 2)\r
+ q->flags |= SF_LOOP;\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 *MOD_LoadTitle(void)\r
+{\r
+ CHAR s[21];\r
+\r
+ _mm_fseek(modreader, 0, SEEK_SET);\r
+ if (!_mm_read_UBYTES(s, 20, modreader))\r
+ return NULL;\r
+ s[20] = 0; /* just in case */\r
+\r
+ return (DupStr(s, 21, 1));\r
+}\r
+\r
+/*========== Loader information */\r
+\r
+MIKMODAPI MLOADER load_mod = {\r
+ NULL,\r
+ "Standard module",\r
+ "MOD (31 instruments)",\r
+ MOD_Init,\r
+ MOD_Test,\r
+ MOD_Load,\r
+ MOD_Cleanup,\r
+ MOD_LoadTitle\r
+};\r
+\r
+/* ex:set ts=4: */\r