1 /* OpenCP Module Player
2 * copyright (c) 2019-'21 Stian Skjelstad <stian.skjelstad@gmail.com>
3 *
4 * GMDPlay loader for ScreamTracker ][ modules
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "config.h"
22 #include <errno.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include "types.h"
27 #include "boot/plinkman.h"
28 #include "dev/mcp.h"
29 #include "filesel/filesystem.h"
30 #include "gmdplay.h"
31 #include "stuff/err.h"
32
33 /*
34 Commands are tested against Scream Tracker v2.3 (stores files as v2.21)
35 A Tempo/Speed - OK (cmdFineSpeed, cmdSpeed, cmdTempo - OCP has Speed and Tempo in reverse)
36 B Break Pattern and Jump to order - OK (Goto, PreConfigures which order next break will jump to)
37 C Break Pattern - OK (cmdBreak)
38 D Volume Slide - OK (our wave-device makes ramps, instead of instant volume changes)
39 E Portamento down - TODO
40 F Portamento up - TODO
41 G Portomento to - TODO
42 H Vibrato - TODO
43 I Tremor - OK (our wave-device makes ramps, instead of instant volume changes
44 J Arpieggo - N/A
45 K Portamento + volume slide - N/A
46 L Vibrato + volume slide - N/A
47 */
48
49 static uint16_t tempo_table[256] =
50 {
51 10, 1053, 826, 600, 375, 175, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
52 1256, 1176, 1100, 1025, 951, 876, 801, 726, 625, 550, 475, 400, 325, 250, 175, 100,
53 1255, 1231, 1178, 1151, 1105, 1076, 1027, 1001, 952, 901, 875, 826, 802, 751, 727, 676,
54 1256, 1256, 1230, 1201, 1178, 1151, 1130, 1101, 1076, 1053, 1027, 1002, 979, 952, 928, 901,
55 1255, 1255, 1231, 1231, 1201, 1177, 1178, 1152, 1130, 1130, 1105, 1105, 1076, 1053, 1053, 1027,
56 1255, 1255, 1255, 1231, 1231, 1201, 1201, 1178, 1178, 1178, 1151, 1151, 1130, 1130, 1105, 1105,
57 1255, 1251, 1251, 1231, 1231, 1225, 1201, 1200, 1178, 1178, 1178, 1151, 1151, 1151, 1130, 1130,
58 1255, 1255, 1255, 1255, 1231, 1231, 1231, 1231, 1201, 1201, 1201, 1201, 1178, 1177, 1177, 1178,
59 1256, 1255, 1255, 1255, 1256, 1255, 1231, 1231, 1231, 1231, 1231, 1201, 1201, 1201, 1201, 1201,
60 1255, 1255, 1255, 1255, 1255, 1255, 1231, 1231, 1231, 1231, 1231, 1201, 1201, 1201, 1201, 1201,
61 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231,
62 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231,
63 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231,
64 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231,
65 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255,
66 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255,
67 };
68
69 struct STMPattern
70 {
71 uint8_t note;
72 uint8_t instrument;
73 uint8_t volume;
74 uint8_t command;
75 uint8_t parameters;
76 };
77
putcmd(uint8_t ** p,uint8_t c,uint8_t d)78 static inline void putcmd(uint8_t **p, uint8_t c, uint8_t d)
79 {
80 *(*p)++=c;
81 *(*p)++=d;
82 }
83
_mpLoadSTM(struct gmdmodule * m,struct ocpfilehandle_t * file)84 static int _mpLoadSTM(struct gmdmodule *m, struct ocpfilehandle_t *file)
85 {
86 unsigned int t,t2,i;
87 uint8_t orders[128];
88 int ordersn;
89 uint32_t smppara[31];
90 struct gmdpattern *pp;
91 uint8_t temptrack[2000];
92
93 struct __attribute__((packed))
94 {
95 char name[20];
96 char tracker[8];
97 uint8_t sig, type, maj, min, tempo, pats, gv;
98 uint8_t reserved[13];
99 } hdr;
100
101 mpReset(m);
102
103 #ifdef STM_LOAD_DEBUG
104 fprintf(stderr, "Reading header: %d bytes\n", (int)sizeof(hdr));
105 #endif
106
107 if (file->read (file, &hdr, sizeof (hdr)) != sizeof (hdr))
108 {
109 fprintf (stderr, "STM: reading header failed: %s\n", strerror (errno));
110 return errFormStruc;
111 }
112
113 if (hdr.type != 2)
114 {
115 fprintf (stderr, "STM: Not a module\n");
116 return errFormStruc;
117 }
118
119 if (hdr.min <= 10)
120 {
121 hdr.gv = 64;
122 } else if (hdr.gv > 64)
123 {
124 hdr.gv = 64;
125 }
126
127 if (hdr.pats>99)
128 {
129 fprintf(stderr, "STM: too many patterns\n");
130 return errFormStruc;
131 }
132
133 if (hdr.pats==0)
134 {
135 fprintf(stderr, "STM: no patterns\n");
136 return errFormStruc;
137 }
138
139 m->patnum=hdr.pats;
140
141 memcpy(m->name, hdr.name, 20);
142 m->name[20]=0;
143
144 m->channum = 4;
145
146 m->modsampnum=m->sampnum=m->instnum=31;
147 if (hdr.min < 21)
148 {
149 ordersn = 64;
150 } else {
151 ordersn = 128;
152 }
153 m->tracknum=hdr.pats*(4+1)+1;
154 m->options=MOD_S3M; /* we have Tremor with 00 parameters etc. */
155 m->loopord=0;
156
157 /* m->options|=MOD_MODPAN; */
158
159 if (!mpAllocInstruments(m, m->instnum)||!mpAllocSamples(m, m->sampnum)||!mpAllocModSamples(m, m->modsampnum))
160 {
161 fprintf(stderr, "STM: Out of mem?\n");
162 return errAllocMem;
163 }
164
165 for (i=0; i<31; i++)
166 {
167 struct gmdinstrument *ip;
168 struct gmdsample *sp;
169 struct sampleinfo *sip;
170 unsigned int j;
171
172 struct __attribute__((packed))
173 {
174 char dosname[12];
175 uint8_t type;
176 uint8_t disk;
177 uint16_t sampptr;
178 uint16_t length;
179 uint16_t loopstart;
180 uint16_t loopend;
181 uint8_t volume;
182 char d1[1];
183 uint16_t c3spd;
184 char d2[6];
185 } sins;
186
187 #ifdef STM_LOAD_DEBUG
188 fprintf(stderr, "Reading %d bytes of instrument %02d header (@0x%08x)\n", (int)sizeof(sins), i + 1, (int)file->getpos (file));
189 #endif
190 if (file->read (file, &sins, sizeof (sins)) != sizeof (sins))
191 {
192 fprintf (stderr, "STM: reading instrument %02d header failed: %s\n", i+1, strerror (errno));
193 return errFileRead;
194 }
195
196 sins.sampptr = uint16_little (sins.sampptr);
197 sins.length = uint16_little (sins.length);
198 sins.loopstart = uint16_little (sins.loopstart);
199 sins.loopend = uint16_little (sins.loopend);
200 sins.c3spd = uint16_little (sins.c3spd);
201
202 smppara[i]=sins.sampptr<<4;
203
204 ip=&m->instruments[i];
205 sp=&m->modsamples[i];
206 sip=&m->samples[i];
207
208 ip->name[0]=0;
209 if (!sins.length)
210 continue;
211 if (sins.length > 64000)
212 { /* limitiation of Scream Tracker series */
213 sins.length = 64000;
214 }
215 if (sins.type!=0)
216 {
217 fprintf (stderr, "STM: warning, non-PCM type sample\n");
218 }
219
220 sp->handle=i;
221 for (j=0; j<128; j++)
222 ip->samples[j]=i;
223 memcpy(sp->name, sins.dosname, 12);
224 sp->name[12]=0;
225 sp->normnote=-mcpGetNote8363(sins.c3spd); // TODO, this seems off
226 sp->stdvol=(sins.volume>0x3F)?0xFF:(sins.volume<<2);
227 sp->stdpan=-1;
228 sp->opt=0;
229
230 if ((sins.loopstart >= sins.loopend) || (sins.loopstart >= sins.length))
231 {
232 sins.loopstart = 0;
233 sins.loopend = 65535;
234 } else if ((sins.loopend > sins.length) && (sins.loopend != 65535))
235 {
236 sins.loopend = sins.length;
237 }
238
239 sip->length=sins.length;
240 sip->loopstart=sins.loopstart;
241 sip->loopend=sins.loopend;
242 sip->samprate=8363; // TODO, this seems off
243 sip->type=0; //mcpSampUnsigned;
244
245 if (sins.loopstart || (sins.loopend != 65535))
246 {
247 sip->type |= mcpSampLoop;
248 }
249 }
250
251
252 #ifdef STM_LOAD_DEBUG
253 fprintf(stderr, "Reading pattern orders, %d bytes\n", (int)m->patnum);
254 #endif
255 if (file->read (file, orders, ordersn) != ordersn)
256 {
257 fprintf (stderr, "STM: reading orders failed: %s\n", strerror (errno));
258 return errFileRead;
259 }
260
261 #if 0
262 for (t=m->patnum-1; (signed)t>=0; t--)
263 {
264 if (orders[t]<254)
265 break;
266 m->patnum--;
267 }
268 #endif
269
270 t2=0;
271 for (t=0; t<ordersn; t++)
272 {
273 if (orders[t] == 255) /* End of song */
274 {
275 break;
276 }
277 if (orders[t]<hdr.pats) /* 99 = Ignore this entry */
278 {
279 t2 = t+1;
280 }
281 }
282
283 m->endord = m->ordnum = t2;
284 if (!m->ordnum)
285 {
286 fprintf(stderr, "STM: No orders\n");
287 return errFormMiss;
288 }
289
290 if (!mpAllocTracks(m, m->tracknum)||!mpAllocPatterns(m, m->patnum)||!mpAllocOrders(m, m->ordnum))
291 {
292 fprintf(stderr, "STM: Out of mem?\n");
293 return errAllocMem;
294 }
295
296 for (t=0; t<t2; t++)
297 {
298 if (orders[t] == 255) /* End of song */
299 {
300 break;
301 }
302 if (orders[t]<hdr.pats) /* 99 = Ignore this entry */
303 {
304 /* does jump hit correct, if we have a hole in the series now? */
305 m->orders[t]=orders[t];
306 } else {
307 m->orders[t]=0xffff;
308 }
309 }
310
311 for (pp=m->patterns, t=0; t<m->patnum; pp++, t++)
312 {
313 pp->patlen=64;
314 for (i=0; i<m->channum; i++)
315 {
316 pp->tracks[i]=t*(m->channum+1)+i;
317 pp->gtrack=t*(m->channum+1)+m->channum;
318 }
319 }
320
321 for (t=0; t<hdr.pats; t++)
322 {
323 unsigned int row;
324 unsigned int j;
325 /* we need to load the full-pattern, since we need to have data sorted by [channel][row], not [row][channel] */
326 struct STMPattern pat[64][4];
327
328 for (row=0; row < 64; row++)
329 {
330 for (j=0; j<4; j++)
331 {
332 if (ocpfilehandle_read_uint8 (file, &pat[row][j].note))
333 {
334 fprintf (stderr, "STM: reading pattern data failed: %s\n", strerror (errno));
335 return errFileRead;
336 }
337 switch (pat[row][j].note)
338 {
339 case 0xfb:
340 pat[row][j].note = 0; pat[row][j].instrument = 0; pat[row][j].volume = 0; pat[row][j].command = 0; pat[row][j].parameters = 0; break;
341 case 0xfc:
342 pat[row][j].note = 255; pat[row][j].instrument = 32; pat[row][j].volume = 65; pat[row][j].command = 0; pat[row][j].parameters = 0; break;
343 case 0xfd:
344 pat[row][j].note = 0xfe; pat[row][j].instrument = 32; pat[row][j].volume = 65; pat[row][j].command = 0; pat[row][j].parameters = 0; break;
345 default:
346 {
347 uint8_t insvol, volcmd;
348 if (ocpfilehandle_read_uint8 (file, &insvol))
349 {
350 fprintf (stderr, "STM: reading pattern data failed: %s\n", strerror (errno));
351 return errFileRead;
352 }
353
354 if (ocpfilehandle_read_uint8 (file, &volcmd))
355 {
356 fprintf (stderr, "STM: reading pattern data failed: %s\n", strerror (errno));
357 return errFileRead;
358 }
359
360 if (ocpfilehandle_read_uint8 (file, &pat[row][j].parameters))
361 {
362 fprintf (stderr, "STM: reading pattern data failed: %s\n", strerror (errno));
363 return errFileRead;
364 }
365
366 pat[row][j].instrument = insvol >> 3;
367 pat[row][j].volume = (insvol & 0x07) | ((volcmd & 0xf0) >> 1);
368 pat[row][j].command = volcmd & 0x0f;
369 }
370 }
371 }
372 }
373
374 for (j=0; j<m->channum; j++)
375 {
376 //char setorgpan=t==orders[0];
377 struct gmdtrack *trk;
378 uint16_t len;
379 uint8_t *tp, *cp;
380
381 tp=temptrack;
382 cp=tp+2;
383
384 for (row = 0; row < 64; row++)
385 {
386 int nte=-1, vol=-1, ins=-1;
387
388 if (!row&&(t==orders[0]))
389 {
390 putcmd(&cp, cmdVolVibratoSetWave, 0x10);
391 putcmd(&cp, cmdPitchVibratoSetWave, 0x10);
392 }
393
394 if (pat[row][j].note < 254)
395 {
396 // TonePortemento behaves differently on STM....
397 //if (pat[row][j].command != 7)
398 nte=(pat[row][j].note>>4)*12+(pat[row][j].note&0x0F)+36;
399 } else if (pat[row][j].note == 254)
400 {
401 putcmd(&cp, cmdNoteCut, 0);
402 }
403
404 if (pat[row][j].volume <= 64)
405 {
406 vol = (pat[row][j].volume > 0x3f)?0xFF:(pat[row][j].volume<<2);
407 }
408 if (pat[row][j].instrument)
409 {
410 ins = pat[row][j].instrument - 1;
411 }
412
413 if (pat[row][j].command == 7) /* cmdPitchSlideToNote, so flag this note as a portamento-target instead of a note-on event */
414 {
415 nte |= 0x80;
416 }
417
418 if ((ins!=-1)||(nte!=-1)||(vol!=-1))
419 {
420 uint8_t *act=cp;
421 *cp++=cmdPlayNote;
422 if (ins!=-1)
423 {
424 *act|=cmdPlayIns;
425 *cp++=ins;
426 }
427 if (nte!=-1)
428 {
429 *act|=cmdPlayNte;
430 *cp++=nte;
431 }
432 if (vol!=-1)
433 {
434 *act|=cmdPlayVol;
435 *cp++=vol;
436 }
437 }
438
439 switch (pat[row][j].command)
440 {
441 case 0: /* . */
442 case 1: /* A - Speed, global */
443 case 2: /* B - Position Jump, global */
444 case 3: /* C - Pattern Break, global */
445 break;
446 case 4: /* D - VolumeSlide */
447 if (pat[row][j].parameters & 0x0f)
448 {
449 putcmd(&cp, cmdVolSlideDown, (pat[row][j].parameters&0x0f)<<2);
450 } else {
451 putcmd(&cp, cmdVolSlideUp, (pat[row][j].parameters>>4)<<2);
452 }
453 break;
454 case 5: /* E - PortamentoDown */
455 putcmd(&cp, cmdRowPitchSlideDown, pat[row][j].parameters);
456 break;
457 case 6: /* F - PortamentoUp */
458 putcmd(&cp, cmdRowPitchSlideUp, pat[row][j].parameters);
459 break;
460 case 7: /* G - TonePortamento */
461 putcmd(&cp, cmdPitchSlideToNote, pat[row][j].parameters);
462 break;
463 case 8: /* H - Vibrato */
464 putcmd(&cp, cmdPitchVibrato, pat[row][j].parameters);
465 break;
466 case 9: /* I - Tremor */
467 putcmd(&cp, cmdTremor, pat[row][j].parameters);
468 break;
469 case 10: /* J - Arpeggio, not implemented in Scream Tracker 2 */
470 case 11: /* K - Vibra, VolumeSlide, not implemented in Scream Tracker 2 */
471 case 12: /* L - */
472 case 13: /* M - */
473 case 14: /* N - */
474 case 15: /* O - Tone, VolumeSlide, not implemented in Scream Tracker 2*/
475 break;
476 }
477
478 if (cp!=(tp+2))
479 {
480 tp[0]=row;
481 tp[1]=cp-tp-2;
482 tp=cp;
483 cp=tp+2;
484 }
485 }
486
487 trk=&m->tracks[t*(m->channum+1)+j];
488 len=tp-temptrack;
489
490 if (!len)
491 {
492 trk->ptr=trk->end=0;
493 } else {
494 trk->ptr=malloc(sizeof(uint8_t)*len);
495 trk->end=trk->ptr+len;
496 if (!trk->ptr)
497 {
498 fprintf(stderr, "STM: Out of mem (4) ?\n");
499 return errAllocMem;
500 }
501 memcpy(trk->ptr, temptrack, len);
502 }
503 }
504
505 do {
506 uint8_t *cp, *tp;
507 struct gmdtrack *trk;
508 uint16_t len;
509
510 tp=temptrack;
511 cp=tp+2;
512
513 if (t==orders[0])
514 {
515 int p = hdr.tempo;
516
517 if (hdr.min < 21)
518 {
519 p = ((p/10)<<4) | (p % 10);
520 }
521
522 if (!p)
523 {
524 p = 0x60;
525 }
526
527 putcmd(&cp, cmdTempo, p>>4);
528
529 putcmd(&cp, cmdSpeed, tempo_table[p] / 10);
530 putcmd(&cp, cmdFineSpeed, tempo_table[p]%10);
531
532 if (hdr.gv!=0x40)
533 {
534 putcmd(&cp, cmdGlobVol, hdr.gv<<2);
535 }
536 }
537
538 for (row=0; row < 64; row++)
539 {
540 for (j=0; j<m->channum; j++)
541 {
542 switch (pat[row][j].command)
543 {
544 case 0: /* . */
545 break;
546 case 1: /* A - Speed, global */
547 {
548 int p = pat[row][j].parameters;
549 if (hdr.min < 21)
550 {
551 p = ((p/10)<<4) | (p % 10);
552 }
553
554 if (p)
555 {
556 putcmd(&cp, cmdTempo, p>>4);
557
558 putcmd(&cp, cmdSpeed, tempo_table[p] / 10);
559 putcmd(&cp, cmdFineSpeed, tempo_table[p]%10);
560 }
561 }
562 break;
563 case 2: /* B - Position Jump, global */
564 putcmd(&cp, cmdGoto, pat[row][j].parameters);
565 break;
566 case 3: /* C - Pattern Break, global */
567 putcmd(&cp, cmdBreak, pat[row][j].parameters);
568 break;
569 case 4: /* D - VolumeSlide */
570 case 5: /* E - PortamentoDown */
571 case 6: /* F - PortamentoUp */
572 case 7: /* G - TonePortamento */
573 case 8: /* H - Vibrato */
574 case 9: /* I - Tremor */
575 case 10: /* J - Arpeggio, not implemented in Scream Tracker 2 */
576 case 11: /* K - Vibra, VolumeSlide, not implemented in Scream Tracker 2 */
577 case 12: /* L - */
578 case 13: /* M - */
579 case 14: /* N - */
580 case 15: /* O - Tone, VolumeSlide, not implemented in Scream Tracker 2*/
581 break;
582 }
583 }
584
585 if (cp!=(tp+2))
586 {
587 tp[0]=row;
588 tp[1]=cp-tp-2;
589 tp=cp;
590 cp=tp+2;
591 }
592 }
593
594 trk=&m->tracks[t*(m->channum+1)+m->channum];
595 len=tp-temptrack;
596
597 if (!len)
598 {
599 trk->ptr=trk->end=0;
600 } else {
601 trk->ptr=malloc(sizeof(uint8_t)*len);
602 trk->end=trk->ptr+len;
603 if (!trk->ptr)
604 {
605 fprintf(stderr, "STM: Out of mem (6)?\n");
606 return errAllocMem;
607 }
608 memcpy(trk->ptr, temptrack, len);
609 }
610 } while (0);
611 }
612
613 for (i=0; i<m->instnum; i++)
614 {
615 struct gmdsample *sp=&m->modsamples[i];
616 struct sampleinfo *sip=&m->samples[i];
617 int l;
618 if (sp->handle==0xFFFF)
619 continue;
620
621 l=sip->length;
622 #ifdef STM_LOAD_DEBUG
623 fprintf (stderr, "Instrument %d sample, seeking to 0x%08x\n", i+1, smppara[i]);
624 #endif
625 file->seek_set (file, smppara[i]);
626
627 sip->ptr=malloc(sizeof(int8_t)*(l+16));
628 if (!sip->ptr)
629 {
630 fprintf(stderr, "STM: Out of mem (7)?\n");
631 return errAllocMem;
632 }
633 if (file->read (file, sip->ptr, l) != l)
634 {
635 fprintf(stderr, __FILE__ ": warning, read failed #8\n");
636 }
637 }
638
639 return errOk;
640 }
641
642 struct gmdloadstruct mpLoadSTM = { _mpLoadSTM };
643
644 struct linkinfostruct dllextinfo = {.name = "gmdlstm", .desc = "OpenCP Module Loader: *.STM (c) 2019-'21, Stian Skjelstad", .ver = DLLVERSION, .size = 0};
645