1 /*      MikMod sound library
2    (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for
3    complete list.
4 
5    This library is free software; you can redistribute it and/or modify
6    it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of
8    the License, or (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA.
19  */
20 
21 /*==============================================================================
22 
23   $Id$
24 
25   UNIMOD (libmikmod's and APlayer's internal module format) loader
26 
27 ==============================================================================*/
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 #include <string.h>
34 
35 #include "unimod_priv.h"
36 
37 /*========== Module structure */
38 
39 typedef struct UNIHEADER
40   {
41     CHAR id[4];
42     UBYTE numchn;
43     UWORD numpos;
44     UWORD reppos;
45     UWORD numpat;
46     UWORD numtrk;
47     UWORD numins;
48     UWORD numsmp;
49     UBYTE initspeed;
50     UBYTE inittempo;
51     UBYTE initvolume;
52     UBYTE flags;
53     UBYTE numvoices;
54 
55     UBYTE positions[256];
56     UBYTE panning[32];
57   }
58 UNIHEADER;
59 
60 typedef struct UNISMP05
61   {
62     UWORD c2spd;
63     UWORD transpose;
64     UBYTE volume;
65     UBYTE panning;
66     ULONG length;
67     ULONG loopstart;
68     ULONG loopend;
69     UWORD flags;
70     CHAR *samplename;
71     UBYTE vibtype;
72     UBYTE vibsweep;
73     UBYTE vibdepth;
74     UBYTE vibrate;
75   }
76 UNISMP05;
77 
78 /*========== Loader variables */
79 
80 static UWORD universion;
81 static UNIHEADER mh;
82 
83 #define UNI_SMPINCR 64
84 static UNISMP05 *wh = NULL, *s = NULL;
85 
86 /*========== Loader code */
87 
88 static char *
readstring(void)89 readstring (void)
90 {
91   char *s = NULL;
92   UWORD len;
93 
94   len = _mm_read_I_UWORD (modreader);
95   if (len)
96     {
97       s = _mm_malloc (len + 1);
98       _mm_read_UBYTES (s, len, modreader);
99       s[len] = 0;
100     }
101   return s;
102 }
103 
104 static BOOL
UNI_Test(void)105 UNI_Test (void)
106 {
107   char id[6];
108 
109   if (!_mm_read_UBYTES (id, 6, modreader))
110     return 0;
111 
112   /* UNIMod created by MikCvt */
113   if (!(memcmp (id, "UN0", 3)))
114     {
115       if ((id[3] >= '4') && (id[3] <= '6'))
116 	return 1;
117     }
118   /* UNIMod created by APlayer */
119   if (!(memcmp (id, "APUN\01", 5)))
120     {
121       if ((id[5] >= 1) && (id[5] <= 4))
122 	return 1;
123     }
124   return 0;
125 }
126 
127 static BOOL
UNI_Init(void)128 UNI_Init (void)
129 {
130   return 1;
131 }
132 
133 static void
UNI_Cleanup(void)134 UNI_Cleanup (void)
135 {
136   _mm_free (wh);
137   s = NULL;
138 }
139 
140 static UBYTE *
readtrack(void)141 readtrack (void)
142 {
143   UBYTE *t;
144   UWORD len;
145   int cur = 0, chunk;
146 
147   if (universion >= 6)
148     len = _mm_read_M_UWORD (modreader);
149   else
150     len = _mm_read_I_UWORD (modreader);
151 
152   if (!len)
153     return NULL;
154   if (!(t = _mm_malloc (len)))
155     return NULL;
156   _mm_read_UBYTES (t, len, modreader);
157 
158   /* Check if the track is correct */
159   while (1)
160     {
161       chunk = t[cur++];
162       if (!chunk)
163 	break;
164       chunk = (chunk & 0x1f) - 1;
165       while (chunk > 0)
166 	{
167 	  int opcode, oplen;
168 
169 	  if (cur >= len)
170 	    {
171 	      free (t);
172 	      return NULL;
173 	    }
174 	  opcode = t[cur];
175 
176 	  /* Remap opcodes */
177 	  if (universion <= 5)
178 	    {
179 	      if (opcode > 29)
180 		{
181 		  free (t);
182 		  return NULL;
183 		}
184 	      switch (opcode)
185 		{
186 		  /* UNI_NOTE .. UNI_S3MEFFECTQ are the same */
187 		case 25:
188 		  opcode = UNI_S3MEFFECTT;
189 		  break;
190 		case 26:
191 		  opcode = UNI_XMEFFECTA;
192 		  break;
193 		case 27:
194 		  opcode = UNI_XMEFFECTG;
195 		  break;
196 		case 28:
197 		  opcode = UNI_XMEFFECTH;
198 		  break;
199 		case 29:
200 		  opcode = UNI_XMEFFECTP;
201 		  break;
202 		}
203 	    }
204 	  else
205 	    {
206 	      if (opcode > UNI_ITEFFECTP)
207 		{
208 		  /* APlayer < 1.03 does not have ITEFFECTT */
209 		  if (universion < 0x103)
210 		    opcode++;
211 		  /* APlayer < 1.02 does not have ITEFFECTZ */
212 		  if ((opcode > UNI_ITEFFECTY) && (universion < 0x102))
213 		    opcode++;
214 		}
215 	    }
216 
217 	  if ((!opcode) || (opcode >= UNI_LAST))
218 	    {
219 	      free (t);
220 	      return NULL;
221 	    }
222 	  oplen = unioperands[opcode] + 1;
223 	  cur += oplen;
224 	  chunk -= oplen;
225 	}
226       if ((chunk < 0) || (cur >= len))
227 	{
228 	  free (t);
229 	  return NULL;
230 	}
231     }
232   return t;
233 }
234 
235 static BOOL
loadsmp6(void)236 loadsmp6 (void)
237 {
238   int t;
239   SAMPLE *s;
240 
241   s = of.samples;
242   for (t = 0; t < of.numsmp; t++, s++)
243     {
244       int flags;
245 
246       flags = _mm_read_M_UWORD (modreader);
247       s->flags = 0;
248       if (flags & 0x0100)
249 	s->flags |= SF_REVERSE;
250       if (flags & 0x0004)
251 	s->flags |= SF_STEREO;
252       if (flags & 0x0002)
253 	s->flags |= SF_SIGNED;
254       if (flags & 0x0001)
255 	s->flags |= SF_16BITS;
256       /* convert flags */
257       if (universion >= 0x102)
258 	{
259 	  if (flags & 0x0800)
260 	    s->flags |= SF_UST_LOOP;
261 	  if (flags & 0x0400)
262 	    s->flags |= SF_OWNPAN;
263 	  if (flags & 0x0200)
264 	    s->flags |= SF_SUSTAIN;
265 	  if (flags & 0x0080)
266 	    s->flags |= SF_BIDI;
267 	  if (flags & 0x0040)
268 	    s->flags |= SF_LOOP;
269 	  if (flags & 0x0020)
270 	    s->flags |= SF_ITPACKED;
271 	  if (flags & 0x0010)
272 	    s->flags |= SF_DELTA;
273 	  if (flags & 0x0008)
274 	    s->flags |= SF_BIG_ENDIAN;
275 	}
276       else
277 	{
278 	  if (flags & 0x400)
279 	    s->flags |= SF_UST_LOOP;
280 	  if (flags & 0x200)
281 	    s->flags |= SF_OWNPAN;
282 	  if (flags & 0x080)
283 	    s->flags |= SF_SUSTAIN;
284 	  if (flags & 0x040)
285 	    s->flags |= SF_BIDI;
286 	  if (flags & 0x020)
287 	    s->flags |= SF_LOOP;
288 	  if (flags & 0x010)
289 	    s->flags |= SF_BIG_ENDIAN;
290 	  if (flags & 0x008)
291 	    s->flags |= SF_DELTA;
292 	}
293 
294       s->speed = _mm_read_M_ULONG (modreader);
295       s->volume = _mm_read_UBYTE (modreader);
296       s->panning = _mm_read_M_UWORD (modreader);
297       s->length = _mm_read_M_ULONG (modreader);
298       s->loopstart = _mm_read_M_ULONG (modreader);
299       s->loopend = _mm_read_M_ULONG (modreader);
300       s->susbegin = _mm_read_M_ULONG (modreader);
301       s->susend = _mm_read_M_ULONG (modreader);
302       s->globvol = _mm_read_UBYTE (modreader);
303       s->vibflags = _mm_read_UBYTE (modreader);
304       s->vibtype = _mm_read_UBYTE (modreader);
305       s->vibsweep = _mm_read_UBYTE (modreader);
306       s->vibdepth = _mm_read_UBYTE (modreader);
307       s->vibrate = _mm_read_UBYTE (modreader);
308 
309       s->samplename = readstring ();
310 
311       if (_mm_eof (modreader))
312 	{
313 	  _mm_errno = MMERR_LOADING_SAMPLEINFO;
314 	  return 0;
315 	}
316     }
317   return 1;
318 }
319 
320 static BOOL
loadinstr6(void)321 loadinstr6 (void)
322 {
323   int t, w;
324   INSTRUMENT *i;
325 
326   i = of.instruments;
327   for (t = 0; t < of.numins; t++, i++)
328     {
329       i->flags = _mm_read_UBYTE (modreader);
330       i->nnatype = _mm_read_UBYTE (modreader);
331       i->dca = _mm_read_UBYTE (modreader);
332       i->dct = _mm_read_UBYTE (modreader);
333       i->globvol = _mm_read_UBYTE (modreader);
334       i->panning = _mm_read_M_UWORD (modreader);
335       i->pitpansep = _mm_read_UBYTE (modreader);
336       i->pitpancenter = _mm_read_UBYTE (modreader);
337       i->rvolvar = _mm_read_UBYTE (modreader);
338       i->rpanvar = _mm_read_UBYTE (modreader);
339       i->volfade = _mm_read_M_UWORD (modreader);
340 
341 #define UNI_LoadEnvelope6(name) 											\
342 		i->name##flg=_mm_read_UBYTE(modreader);							\
343 		i->name##pts=_mm_read_UBYTE(modreader);							\
344 		i->name##susbeg=_mm_read_UBYTE(modreader);						\
345 		i->name##susend=_mm_read_UBYTE(modreader);						\
346 		i->name##beg=_mm_read_UBYTE(modreader);							\
347 		i->name##end=_mm_read_UBYTE(modreader);							\
348 		for(w=0;w<(universion>=0x100?32:i->name##pts);w++) {				\
349 			i->name##env[w].pos=_mm_read_M_SWORD(modreader);				\
350 			i->name##env[w].val=_mm_read_M_SWORD(modreader);				\
351 		}
352 
353       UNI_LoadEnvelope6 (vol);
354       UNI_LoadEnvelope6 (pan);
355       UNI_LoadEnvelope6 (pit);
356 #undef UNI_LoadEnvelope6
357 
358       if (universion == 0x103)
359 	_mm_read_M_UWORDS (i->samplenumber, 120, modreader);
360       else
361 	for (w = 0; w < 120; w++)
362 	  i->samplenumber[w] = _mm_read_UBYTE (modreader);
363       _mm_read_UBYTES (i->samplenote, 120, modreader);
364 
365       i->insname = readstring ();
366 
367       if (_mm_eof (modreader))
368 	{
369 	  _mm_errno = MMERR_LOADING_SAMPLEINFO;
370 	  return 0;
371 	}
372     }
373   return 1;
374 }
375 
376 static BOOL
loadinstr5(void)377 loadinstr5 (void)
378 {
379   INSTRUMENT *i;
380   int t;
381   UWORD wavcnt = 0;
382   UBYTE vibtype, vibsweep, vibdepth, vibrate;
383 
384   i = of.instruments;
385   for (of.numsmp = t = 0; t < of.numins; t++, i++)
386     {
387       int u, numsmp;
388 
389       numsmp = _mm_read_UBYTE (modreader);
390 
391       memset (i->samplenumber, 0xff, INSTNOTES * sizeof (UWORD));
392       for (u = 0; u < 96; u++)
393 	i->samplenumber[u] = of.numsmp + _mm_read_UBYTE (modreader);
394 
395 #define UNI_LoadEnvelope5(name) 										\
396 		i->name##flg=_mm_read_UBYTE(modreader);						\
397 		i->name##pts=_mm_read_UBYTE(modreader);						\
398 		i->name##susbeg=_mm_read_UBYTE(modreader);					\
399 		i->name##susend=i->name##susbeg;							\
400 		i->name##beg=_mm_read_UBYTE(modreader);						\
401 		i->name##end=_mm_read_UBYTE(modreader);						\
402 		for(u=0;u<12;u++) {												\
403 			i->name##env[u].pos=_mm_read_I_SWORD(modreader);			\
404 			i->name##env[u].val=_mm_read_I_SWORD(modreader);			\
405 		}
406 
407       UNI_LoadEnvelope5 (vol);
408       UNI_LoadEnvelope5 (pan);
409 #undef UNI_LoadEnvelope5
410 
411       vibtype = _mm_read_UBYTE (modreader);
412       vibsweep = _mm_read_UBYTE (modreader);
413       vibdepth = _mm_read_UBYTE (modreader);
414       vibrate = _mm_read_UBYTE (modreader);
415 
416       i->volfade = _mm_read_I_UWORD (modreader);
417       i->insname = readstring ();
418 
419       for (u = 0; u < numsmp; u++, s++, of.numsmp++)
420 	{
421 	  /* Allocate more room for sample information if necessary */
422 	  if (of.numsmp + u == wavcnt)
423 	    {
424 	      wavcnt += UNI_SMPINCR;
425 	      if (!(wh = realloc (wh, wavcnt * sizeof (UNISMP05))))
426 		{
427 		  _mm_errno = MMERR_OUT_OF_MEMORY;
428 		  return 0;
429 		}
430 	      s = wh + (wavcnt - UNI_SMPINCR);
431 	    }
432 
433 	  s->c2spd = _mm_read_I_UWORD (modreader);
434 	  s->transpose = _mm_read_SBYTE (modreader);
435 	  s->volume = _mm_read_UBYTE (modreader);
436 	  s->panning = _mm_read_UBYTE (modreader);
437 	  s->length = _mm_read_I_ULONG (modreader);
438 	  s->loopstart = _mm_read_I_ULONG (modreader);
439 	  s->loopend = _mm_read_I_ULONG (modreader);
440 	  s->flags = _mm_read_I_UWORD (modreader);
441 	  s->samplename = readstring ();
442 
443 	  s->vibtype = vibtype;
444 	  s->vibsweep = vibsweep;
445 	  s->vibdepth = vibdepth;
446 	  s->vibrate = vibrate;
447 
448 	  if (_mm_eof (modreader))
449 	    {
450 	      free (wh);
451 	      wh = NULL;
452 	      _mm_errno = MMERR_LOADING_SAMPLEINFO;
453 	      return 0;
454 	    }
455 	}
456     }
457 
458   /* sanity check */
459   if (!of.numsmp)
460     {
461       if (wh)
462 	{
463 	  free (wh);
464 	  wh = NULL;
465 	}
466       _mm_errno = MMERR_LOADING_SAMPLEINFO;
467       return 0;
468     }
469   return 1;
470 }
471 
472 static BOOL
loadsmp5(void)473 loadsmp5 (void)
474 {
475   int t, u;
476   SAMPLE *q;
477   INSTRUMENT *d;
478 
479   q = of.samples;
480   s = wh;
481   for (u = 0; u < of.numsmp; u++, q++, s++)
482     {
483       q->samplename = s->samplename;
484 
485       q->length = s->length;
486       q->loopstart = s->loopstart;
487       q->loopend = s->loopend;
488       q->volume = s->volume;
489       q->speed = s->c2spd;
490       q->panning = s->panning;
491       q->vibtype = s->vibtype;
492       q->vibsweep = s->vibsweep;
493       q->vibdepth = s->vibdepth;
494       q->vibrate = s->vibrate;
495 
496       /* convert flags */
497       q->flags = 0;
498       if (s->flags & 128)
499 	q->flags |= SF_REVERSE;
500       if (s->flags & 64)
501 	q->flags |= SF_SUSTAIN;
502       if (s->flags & 32)
503 	q->flags |= SF_BIDI;
504       if (s->flags & 16)
505 	q->flags |= SF_LOOP;
506       if (s->flags & 8)
507 	q->flags |= SF_BIG_ENDIAN;
508       if (s->flags & 4)
509 	q->flags |= SF_DELTA;
510       if (s->flags & 2)
511 	q->flags |= SF_SIGNED;
512       if (s->flags & 1)
513 	q->flags |= SF_16BITS;
514     }
515 
516   d = of.instruments;
517   s = wh;
518   for (u = 0; u < of.numins; u++, d++)
519     for (t = 0; t < INSTNOTES; t++)
520       d->samplenote[t] = (d->samplenumber[t] >= of.numsmp) ?
521 	255 : (t + s[d->samplenumber[t]].transpose);
522 
523   free (wh);
524   wh = NULL;
525 
526   return 1;
527 }
528 
529 static BOOL
UNI_Load(BOOL curious)530 UNI_Load (BOOL curious)
531 {
532   int t;
533   char *modtype, *oldtype = NULL;
534   INSTRUMENT *d;
535   SAMPLE *q;
536 
537   /* read module header */
538   _mm_read_UBYTES (mh.id, 4, modreader);
539   if (mh.id[3] != 'N')
540     universion = mh.id[3] - '0';
541   else
542     universion = 0x100;
543 
544   if (universion >= 6)
545     {
546       if (universion == 6)
547 	_mm_read_UBYTE (modreader);
548       else
549 	universion = _mm_read_M_UWORD (modreader);
550 
551       mh.flags = _mm_read_M_UWORD (modreader);
552       mh.numchn = _mm_read_UBYTE (modreader);
553       mh.numvoices = _mm_read_UBYTE (modreader);
554       mh.numpos = _mm_read_M_UWORD (modreader);
555       mh.numpat = _mm_read_M_UWORD (modreader);
556       mh.numtrk = _mm_read_M_UWORD (modreader);
557       mh.numins = _mm_read_M_UWORD (modreader);
558       mh.numsmp = _mm_read_M_UWORD (modreader);
559       mh.reppos = _mm_read_M_UWORD (modreader);
560       mh.initspeed = _mm_read_UBYTE (modreader);
561       mh.inittempo = _mm_read_UBYTE (modreader);
562       mh.initvolume = _mm_read_UBYTE (modreader);
563 
564       mh.flags &= (UF_XMPERIODS | UF_LINEAR | UF_INST | UF_NNA);
565     }
566   else
567     {
568       mh.numchn = _mm_read_UBYTE (modreader);
569       mh.numpos = _mm_read_I_UWORD (modreader);
570       mh.reppos = (universion == 5) ? _mm_read_I_UWORD (modreader) : 0;
571       mh.numpat = _mm_read_I_UWORD (modreader);
572       mh.numtrk = _mm_read_I_UWORD (modreader);
573       mh.numins = _mm_read_I_UWORD (modreader);
574       mh.initspeed = _mm_read_UBYTE (modreader);
575       mh.inittempo = _mm_read_UBYTE (modreader);
576       _mm_read_UBYTES (mh.positions, 256, modreader);
577       _mm_read_UBYTES (mh.panning, 32, modreader);
578       mh.flags = _mm_read_UBYTE (modreader);
579 
580       mh.flags &= (UF_XMPERIODS | UF_LINEAR);
581       mh.flags |= UF_INST | UF_NOWRAP;
582     }
583 
584   /* set module parameters */
585   of.flags = mh.flags;
586   of.numchn = mh.numchn;
587   of.numpos = mh.numpos;
588   of.numpat = mh.numpat;
589   of.numtrk = mh.numtrk;
590   of.numins = mh.numins;
591   of.reppos = mh.reppos;
592   of.initspeed = mh.initspeed;
593   of.inittempo = mh.inittempo;
594 
595   of.songname = readstring ();
596   if (universion < 0x102)
597     oldtype = readstring ();
598   if (oldtype)
599     {
600       int len = strlen (oldtype) + 20;
601       if (!(modtype = _mm_malloc (len)))
602 	return 0;
603 #ifdef HAVE_SNPRINTF
604       snprintf (modtype, len, "%s (was %s)", (universion >= 0x100) ? "APlayer" : "MikCvt2", oldtype);
605 #else
606       sprintf (modtype, "%s (was %s)", (universion >= 0x100) ? "APlayer" : "MikCvt2", oldtype);
607 #endif
608     }
609   else
610     {
611       if (!(modtype = _mm_malloc (10)))
612 	return 0;
613 #ifdef HAVE_SNPRINTF
614       snprintf (modtype, 10, "%s", (universion >= 0x100) ? "APlayer" : "MikCvt3");
615 #else
616       sprintf (modtype, "%s", (universion >= 0x100) ? "APlayer" : "MikCvt3");
617 #endif
618     }
619   of.modtype = strdup (modtype);
620   free (modtype);
621   free (oldtype);
622   of.comment = readstring ();
623 
624   if (universion >= 6)
625     {
626       of.numvoices = mh.numvoices;
627       of.initvolume = mh.initvolume;
628     }
629 
630   if (_mm_eof (modreader))
631     {
632       _mm_errno = MMERR_LOADING_HEADER;
633       return 0;
634     }
635 
636   /* positions */
637   if (!AllocPositions (of.numpos))
638     return 0;
639   if (universion >= 6)
640     {
641       if (universion >= 0x100)
642 	_mm_read_M_UWORDS (of.positions, of.numpos, modreader);
643       else
644 	for (t = 0; t < of.numpos; t++)
645 	  of.positions[t] = _mm_read_UBYTE (modreader);
646       _mm_read_M_UWORDS (of.panning, of.numchn, modreader);
647       _mm_read_UBYTES (of.chanvol, of.numchn, modreader);
648     }
649   else
650     {
651       if ((mh.numpos > 256) || (mh.numchn > 32))
652 	{
653 	  _mm_errno = MMERR_LOADING_HEADER;
654 	  return 0;
655 	}
656       for (t = 0; t < of.numpos; t++)
657 	of.positions[t] = mh.positions[t];
658       for (t = 0; t < of.numchn; t++)
659 	of.panning[t] = mh.panning[t];
660     }
661 
662   /* instruments and samples */
663   if (universion >= 6)
664     {
665       of.numsmp = mh.numsmp;
666       if (!AllocSamples ())
667 	return 0;
668       if (!loadsmp6 ())
669 	return 0;
670 
671       if (of.flags & UF_INST)
672 	{
673 	  if (!AllocInstruments ())
674 	    return 0;
675 	  if (!loadinstr6 ())
676 	    return 0;
677 	}
678     }
679   else
680     {
681       if (!AllocInstruments ())
682 	return 0;
683       if (!loadinstr5 ())
684 	return 0;
685       if (!AllocSamples ())
686 	{
687 	  if (wh)
688 	    {
689 	      free (wh);
690 	      wh = NULL;
691 	    }
692 	  return 0;
693 	}
694       if (!loadsmp5 ())
695 	return 0;
696 
697       /* check if the original file had no instruments */
698       if (of.numsmp == of.numins)
699 	{
700 	  for (t = 0, d = of.instruments; t < of.numins; t++, d++)
701 	    {
702 	      int u;
703 
704 	      if ((d->volpts) || (d->panpts) || (d->globvol != 64))
705 		break;
706 	      for (u = 0; u < 96; u++)
707 		if ((d->samplenumber[u] != t) || (d->samplenote[u] != u))
708 		  break;
709 	      if (u != 96)
710 		break;
711 	    }
712 	  if (t == of.numins)
713 	    {
714 	      of.flags &= ~UF_INST;
715 	      of.flags &= ~UF_NOWRAP;
716 	      for (t = 0, d = of.instruments, q = of.samples; t < of.numins; t++, d++, q++)
717 		{
718 		  q->samplename = d->insname;
719 		  d->insname = NULL;
720 		}
721 	    }
722 	}
723     }
724 
725   /* patterns */
726   if (!AllocPatterns ())
727     return 0;
728   if (universion >= 6)
729     {
730       _mm_read_M_UWORDS (of.pattrows, of.numpat, modreader);
731       _mm_read_M_UWORDS (of.patterns, of.numpat * of.numchn, modreader);
732     }
733   else
734     {
735       _mm_read_I_UWORDS (of.pattrows, of.numpat, modreader);
736       _mm_read_I_UWORDS (of.patterns, of.numpat * of.numchn, modreader);
737     }
738 
739   /* tracks */
740   if (!AllocTracks ())
741     return 0;
742   for (t = 0; t < of.numtrk; t++)
743     if (!(of.tracks[t] = readtrack ()))
744       {
745 	_mm_errno = MMERR_LOADING_TRACK;
746 	return 0;
747       }
748 
749   return 1;
750 }
751 
752 static CHAR *
UNI_LoadTitle(void)753 UNI_LoadTitle (void)
754 {
755   UBYTE ver;
756   int posit[3] =
757   {304, 306, 26};
758 
759   _mm_fseek (modreader, 3, SEEK_SET);
760   ver = _mm_read_UBYTE (modreader);
761   if (ver == 'N')
762     ver = '6';
763 
764   _mm_fseek (modreader, posit[ver - '4'], SEEK_SET);
765   return readstring ();
766 }
767 
768 /*========== Loader information */
769 
770 MLOADER load_uni =
771 {
772   NULL,
773   "UNI",
774   "APUN (APlayer) and UNI (MikMod)",
775   UNI_Init,
776   UNI_Test,
777   UNI_Load,
778   UNI_Cleanup,
779   UNI_LoadTitle
780 };
781 
782 /* ex:set ts=4: */
783