A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/* MikMod sound library
2 (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file
3 AUTHORS for 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 Screamtracker 2 (STM) module loader
26
27==============================================================================*/
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#ifdef HAVE_UNISTD_H
34#include <unistd.h>
35#endif
36
37#include <stdio.h>
38#ifdef HAVE_MEMORY_H
39#include <memory.h>
40#endif
41#include <string.h>
42
43#include "mikmod_internals.h"
44
45#ifdef SUNOS
46extern int fprintf(FILE *, const char *, ...);
47#endif
48
49/*========== Module structure */
50
51/* sample information */
52typedef struct STMSAMPLE {
53 CHAR filename[12];
54 UBYTE unused; /* 0x00 */
55 UBYTE instdisk; /* Instrument disk */
56 UWORD reserved;
57 UWORD length; /* Sample length */
58 UWORD loopbeg; /* Loop start point */
59 UWORD loopend; /* Loop end point */
60 UBYTE volume; /* Volume */
61 UBYTE reserved2;
62 UWORD c2spd; /* Good old c2spd */
63 ULONG reserved3;
64 UWORD isa;
65} STMSAMPLE;
66
67/* header */
68typedef struct STMHEADER {
69 CHAR songname[20];
70 CHAR trackername[8]; /* !Scream! for ST 2.xx */
71 UBYTE unused; /* 0x1A */
72 UBYTE filetype; /* 1=song, 2=module */
73 UBYTE ver_major;
74 UBYTE ver_minor;
75 UBYTE inittempo; /* initspeed= stm inittempo>>4 */
76 UBYTE numpat; /* number of patterns */
77 UBYTE globalvol;
78 UBYTE reserved[13];
79 STMSAMPLE sample[31]; /* STM sample data */
80 UBYTE patorder[128]; /* Docs say 64 - actually 128 */
81} STMHEADER;
82
83typedef struct STMNOTE {
84 UBYTE note,insvol,volcmd,cmdinf;
85} STMNOTE;
86
87/*========== Loader variables */
88
89static STMNOTE *stmbuf = NULL;
90static STMHEADER *mh = NULL;
91
92/* tracker identifiers */
93static const CHAR * STM_Version[STM_NTRACKERS] = {
94 "Screamtracker 2",
95 "Converted by MOD2STM (STM format)",
96 "Wuzamod (STM format)"
97};
98
99/*========== Loader code */
100
101static int STM_Test(void)
102{
103 UBYTE str[44];
104 int t;
105
106 memset(str,0,44);
107 _mm_fseek(modreader,20,SEEK_SET);
108 _mm_read_UBYTES(str,44,modreader);
109 if(str[9]!=2) return 0; /* STM Module = filetype 2 */
110
111 /* Prevent false positives for S3M files */
112 if(!memcmp(str+40,"SCRM",4))
113 return 0;
114
115 for (t=0;t<STM_NTRACKERS;t++)
116 if(!memcmp(str,STM_Signatures[t],8))
117 return 1;
118
119 return 0;
120}
121
122static int STM_Init(void)
123{
124 if(!(mh=(STMHEADER*)MikMod_malloc(sizeof(STMHEADER)))) return 0;
125 if(!(stmbuf=(STMNOTE*)MikMod_calloc(64U*4,sizeof(STMNOTE)))) return 0;
126
127 return 1;
128}
129
130static void STM_Cleanup(void)
131{
132 MikMod_free(mh);
133 MikMod_free(stmbuf);
134 mh=NULL;
135 stmbuf=NULL;
136}
137
138static void STM_ConvertNote(STMNOTE *n)
139{
140 UBYTE note,ins,vol,cmd,inf;
141
142 /* extract the various information from the 4 bytes that make up a note */
143 note = n->note;
144 ins = n->insvol>>3;
145 vol = (n->insvol&7)+((n->volcmd&0x70)>>1);
146 cmd = n->volcmd&15;
147 inf = n->cmdinf;
148
149 if((ins)&&(ins<32)) UniInstrument(ins-1);
150
151 /* special values of [SBYTE0] are handled here
152 we have no idea if these strange values will ever be encountered.
153 but it appears as those stms sound correct. */
154 if((note==254)||(note==252)) {
155 UniPTEffect(0xc,0); /* note cut */
156 n->volcmd|=0x80;
157 } else
158 /* if note < 251, then all three bytes are stored in the file */
159 if(note<251) UniNote((((note>>4)+2)*OCTAVE)+(note&0xf));
160
161 if((!(n->volcmd&0x80))&&(vol<65)) UniPTEffect(0xc,vol);
162 if(cmd!=255)
163 switch(cmd) {
164 case 1: /* Axx set speed to xx */
165 UniPTEffect(0xf,inf>>4);
166 break;
167 case 2: /* Bxx position jump */
168 UniPTEffect(0xb,inf);
169 break;
170 case 3: /* Cxx patternbreak to row xx */
171 UniPTEffect(0xd,(((inf&0xf0)>>4)*10)+(inf&0xf));
172 break;
173 case 4: /* Dxy volumeslide */
174 UniEffect(UNI_S3MEFFECTD,inf);
175 break;
176 case 5: /* Exy toneslide down */
177 UniEffect(UNI_S3MEFFECTE,inf);
178 break;
179 case 6: /* Fxy toneslide up */
180 UniEffect(UNI_S3MEFFECTF,inf);
181 break;
182 case 7: /* Gxx Tone portamento,speed xx */
183 UniPTEffect(0x3,inf);
184 break;
185 case 8: /* Hxy vibrato */
186 UniPTEffect(0x4,inf);
187 break;
188 case 9: /* Ixy tremor, ontime x, offtime y */
189 UniEffect(UNI_S3MEFFECTI,inf);
190 break;
191 case 0: /* protracker arpeggio */
192 if(!inf) break;
193 /* fall through */
194 case 0xa: /* Jxy arpeggio */
195 UniPTEffect(0x0,inf);
196 break;
197 case 0xb: /* Kxy Dual command H00 & Dxy */
198 UniPTEffect(0x4,0);
199 UniEffect(UNI_S3MEFFECTD,inf);
200 break;
201 case 0xc: /* Lxy Dual command G00 & Dxy */
202 UniPTEffect(0x3,0);
203 UniEffect(UNI_S3MEFFECTD,inf);
204 break;
205 /* Support all these above, since ST2 can LOAD these values but can
206 actually only play up to J - and J is only half-way implemented
207 in ST2 */
208 case 0x18: /* Xxx amiga panning command 8xx */
209 UniPTEffect(0x8,inf);
210 of.flags |= UF_PANNING;
211 break;
212 }
213}
214
215static UBYTE *STM_ConvertTrack(STMNOTE *n)
216{
217 int t;
218
219 UniReset();
220 for(t=0;t<64;t++) {
221 STM_ConvertNote(n);
222 UniNewline();
223 n+=of.numchn;
224 }
225 return UniDup();
226}
227
228static int STM_LoadPatterns(unsigned int pattoload)
229{
230 unsigned int t,s,tracks=0;
231
232 if(!AllocPatterns()) return 0;
233 if(!AllocTracks()) return 0;
234
235 /* Allocate temporary buffer for loading and converting the patterns */
236 for(t=0;t<pattoload;t++) {
237 for(s=0;s<(64U*of.numchn);s++) {
238 stmbuf[s].note = _mm_read_UBYTE(modreader);
239 stmbuf[s].insvol = _mm_read_UBYTE(modreader);
240 stmbuf[s].volcmd = _mm_read_UBYTE(modreader);
241 stmbuf[s].cmdinf = _mm_read_UBYTE(modreader);
242 }
243
244 if(_mm_eof(modreader)) {
245 _mm_errno = MMERR_LOADING_PATTERN;
246 return 0;
247 }
248
249 for(s=0;s<of.numchn;s++)
250 if(!(of.tracks[tracks++]=STM_ConvertTrack(stmbuf+s))) return 0;
251 }
252 return 1;
253}
254
255static int STM_Load(int curious)
256{
257 int blankpattern=0;
258 int pattoload;
259 int t;
260 ULONG samplestart;
261 ULONG sampleend;
262 SAMPLE *q;
263 (void)curious;
264 /* try to read stm header */
265 _mm_read_string(mh->songname,20,modreader);
266 _mm_read_string(mh->trackername,8,modreader);
267 mh->unused =_mm_read_UBYTE(modreader);
268 mh->filetype =_mm_read_UBYTE(modreader);
269 mh->ver_major =_mm_read_UBYTE(modreader);
270 mh->ver_minor =_mm_read_UBYTE(modreader);
271 mh->inittempo =_mm_read_UBYTE(modreader);
272 if(!mh->inittempo) {
273 _mm_errno=MMERR_NOT_A_MODULE;
274 return 0;
275 }
276 mh->numpat =_mm_read_UBYTE(modreader);
277 mh->globalvol =_mm_read_UBYTE(modreader);
278 _mm_read_UBYTES(mh->reserved,13,modreader);
279 if(mh->numpat > 128) {
280 _mm_errno = MMERR_NOT_A_MODULE;
281 return 0;
282 }
283
284 for(t=0;t<31;t++) {
285 STMSAMPLE *s=&mh->sample[t]; /* STM sample data */
286
287 _mm_read_string(s->filename,12,modreader);
288 s->unused =_mm_read_UBYTE(modreader);
289 s->instdisk =_mm_read_UBYTE(modreader);
290 s->reserved =_mm_read_I_UWORD(modreader);
291 s->length =_mm_read_I_UWORD(modreader);
292 s->loopbeg =_mm_read_I_UWORD(modreader);
293 s->loopend =_mm_read_I_UWORD(modreader);
294 s->volume =_mm_read_UBYTE(modreader);
295 s->reserved2=_mm_read_UBYTE(modreader);
296 s->c2spd =_mm_read_I_UWORD(modreader);
297 s->reserved3=_mm_read_I_ULONG(modreader);
298 s->isa =_mm_read_I_UWORD(modreader);
299 }
300 _mm_read_UBYTES(mh->patorder,128,modreader);
301
302 if(_mm_eof(modreader)) {
303 _mm_errno = MMERR_LOADING_HEADER;
304 return 0;
305 }
306
307 /* set module variables */
308 for(t=0;t<STM_NTRACKERS;t++)
309 if(!memcmp(mh->trackername,STM_Signatures[t],8)) break;
310 of.modtype = MikMod_strdup(STM_Version[t]);
311 of.songname = DupStr(mh->songname,20,1); /* make a cstr of songname */
312 of.numpat = mh->numpat;
313 of.inittempo = 125; /* mh->inittempo+0x1c; */
314 of.initspeed = mh->inittempo>>4;
315 of.numchn = 4; /* get number of channels */
316 of.reppos = 0;
317 of.flags |= UF_S3MSLIDES;
318 of.bpmlimit = 32;
319
320 t=0;
321 if(!AllocPositions(0x80)) return 0;
322 /* 99 terminates the patorder list */
323 while(mh->patorder[t]<99) {
324 of.positions[t]=mh->patorder[t];
325
326 /* Screamtracker 2 treaks patterns >= numpat as blank patterns.
327 * Example modules: jimmy.stm, Rauno/dogs.stm, Skaven/hevijanis istu maas.stm.
328 *
329 * Patterns>=64 have unpredictable behavior in Screamtracker 2,
330 * but nothing seems to rely on them, so they're OK to blank too.
331 */
332 if(of.positions[t]>=mh->numpat) {
333 of.positions[t]=mh->numpat;
334 blankpattern=1;
335 }
336
337 if(++t == 0x80) {
338 _mm_errno = MMERR_NOT_A_MODULE;
339 return 0;
340 }
341 }
342 /* Allocate an extra blank pattern if the module references one. */
343 pattoload=of.numpat;
344 if(blankpattern) of.numpat++;
345 of.numpos=t;
346 of.numtrk=of.numpat*of.numchn;
347 of.numins=of.numsmp=31;
348
349 if(!AllocSamples()) return 0;
350 if(!STM_LoadPatterns(pattoload)) return 0;
351
352 samplestart=_mm_ftell(modreader);
353 _mm_fseek(modreader,0,SEEK_END);
354 sampleend=_mm_ftell(modreader);
355
356 for(q=of.samples,t=0;t<of.numsmp;t++,q++) {
357 /* load sample info */
358 q->samplename = DupStr(mh->sample[t].filename,12,1);
359 q->speed = (mh->sample[t].c2spd * 8363) / 8448;
360 q->volume = mh->sample[t].volume;
361 q->length = mh->sample[t].length;
362 if (!mh->sample[t].volume || q->length==1) q->length=0;
363 q->loopstart = mh->sample[t].loopbeg;
364 q->loopend = mh->sample[t].loopend;
365 q->seekpos = mh->sample[t].reserved << 4;
366
367 /* Sanity checks to make sure samples are bounded within the file. */
368 if(q->length) {
369 if(q->seekpos<samplestart) {
370#ifdef MIKMOD_DEBUG
371 fprintf(stderr,"rejected sample # %d (seekpos=%u < samplestart=%u)\n",t,q->seekpos,samplestart);
372#endif
373 _mm_errno = MMERR_LOADING_SAMPLEINFO;
374 return 0;
375 }
376 /* Some .STMs seem to rely on allowing truncated samples... */
377 if(q->seekpos>=sampleend) {
378#ifdef MIKMOD_DEBUG
379 fprintf(stderr,"truncating sample # %d from length %u to 0\n",t,q->length);
380#endif
381 q->seekpos = q->length = 0;
382 } else if(q->seekpos+q->length>sampleend) {
383#ifdef MIKMOD_DEBUG
384 fprintf(stderr,"truncating sample # %d from length %u to %u\n",t,q->length,sampleend - q->seekpos);
385#endif
386 q->length = sampleend - q->seekpos;
387 }
388 }
389 else
390 q->seekpos = 0;
391
392 /* contrary to the STM specs, sample data is signed */
393 q->flags = SF_SIGNED;
394
395 if(q->loopend && q->loopend != 0xffff && q->loopstart < q->length) {
396 q->flags|=SF_LOOP;
397 if (q->loopend > q->length)
398 q->loopend = q->length;
399 }
400 else
401 q->loopstart = q->loopend = 0;
402 }
403 return 1;
404}
405
406static CHAR *STM_LoadTitle(void)
407{
408 CHAR s[20];
409
410 _mm_fseek(modreader,0,SEEK_SET);
411 if(!_mm_read_UBYTES(s,20,modreader)) return NULL;
412
413 return(DupStr(s,20,1));
414}
415
416/*========== Loader information */
417
418MIKMODAPI MLOADER load_stm={
419 NULL,
420 "STM",
421 "STM (Scream Tracker)",
422 STM_Init,
423 STM_Test,
424 STM_Load,
425 STM_Cleanup,
426 STM_LoadTitle
427};
428
429/* ex:set ts=4: */