#include /*stderr*/ #include "defs.h" /*->inst.h (u8,u16x,...)*/ #include "inst.h" /*->playnote.h (SampleInfo)*/ #include "playnote.h" /*Note*/ /*#include "dacio.h"*/ /*DacioConfInfo*/ #include "ovs.h" /*(ovs)*/ #include "nspmod.h" /*->play-s3m.h (OptInfo)*/ #include "play-s3m.h" /*(playS3m)*/ #include "mem.h" /*(memXXX)*/ struct { const u8 *p0; const u8 *songName; i15x ordNum; i15x insNum; i15x patNum; i15x flags; i15x cwtv; i15x ffi; i15x gv; i15x is; i15x it; i15x mv; i15x stereo; const u8 *ch; const u8 *ord; const u8 (*ins)[2]; const u8 (*pat)[2]; const u8 *pan; } h; #define u16LittleEndian(x) \ (((const u8 *)(x))[0] + ((const u8 *)(x))[1]*(u16)256) static Note (*pattern)[32]; static void clearPattern(void) { i15x i; static Note n0 = { 255, 0, 255, 255, 0 }; Note *np; np = &pattern[0][0]; for (i = 64 * 32; i > 0; i--,np++) *np = n0; } static void displayNote(Note *np) { static u8 *note1 = "CCDDEFFGGAAB"; static u8 *note2 = "-#-#--#-#-#-"; i15x n; n = np->note; switch (n) { case 255: printf(" ..."); break; case 254: printf(" ^^."); break; default: printf(" %c%c%d", note1[n&15], note2[n&15], n / 16); } n = np->ins; if (n == 0) printf(" .."); else printf(" %02d", n); n = np->vol; if (n == 255) printf(" .."); else printf(" %02d", n); n = np->cmd; if (n == 255) printf(" ."); else printf(" %c", n + '@'); printf("%02X|", np->info); } static i15x playAPattern(const OptInfo *oip) { Note *np; i15x row,ch; i15x loopBeg, loopCnt; static i15x row0; i15x nextOrd; row = row0; row0 = nextOrd = 0; if (oip->showEvents) printf("\n"); for (; row < 64; row++) { np = &pattern[row][0]; if (oip->showEvents) printf("%02d|", row); for (ch=0; ch < 32; ch++,np++) { if (h.ch[ch] >= 16) continue; if (!oip->onlyCh || ch == (oip->onlyCh & 0x7f)) { if (oip->showEvents) { displayNote(np); /*fflush(stdout);*/ } playNoteSetNote(ch, np); } switch (np->cmd) { case 'A' - '@': /* set speed */ playNoteSetSpeed(np->info); break; case 'B' - '@': nextOrd = np->info | 0x100; row = 64; break; case 'C' - '@': row0 = np->info/16*10 + np->info%16; row = 64; break; case 'S' - '@': switch (np->info/16) { case 0xB: /* pattern loop */ if (np->info%16) { /* jump to mark */ if (loopCnt < np->info%16) { row = loopBeg-1; loopCnt++; } else { loopCnt = 0; } } else { /* set mark */ loopBeg = row; } break; case 0xE: /* pattern delay */ playNoteSetPatRepeat(np->info%16); break; } break; case 'T' - '@': /* set tempo */ playNoteSetTempo(np->info); break; } } playNote(); if (oip->showEvents) printf("\n"); } return nextOrd; } static void unpackPattern(const u8 *p) { const u8 *endp; Note *np; Note (*rowp)[32]; clearPattern(); endp = p + u16LittleEndian(p); rowp = &pattern[0]; for (p += 2; p < endp; ) { i15x d = *p++; if (!d) { rowp++; continue; } np = &(*rowp)[d & 31]; if (d & 32) { np->note = *p++; np->ins = *p++; } if (d & 64) { np->vol = *p++; } if (d & 128) { np->cmd = *p++; np->info = *p++; } } } static void tellChSettings(void) { i15x i; for (i = 0; i < 32; i++) { i15x c = h.ch[i]; if (c & 0x80) continue; instSelectCh(i); c &= 0x7f; if (c >= 16) { fprintf(stderr, "Adlib channel!?\n"); } else if (h.pan != NULL && h.pan[i] & 0x20) { instPanPosition((h.pan[i] & 0xf) * 64/15); } else if (c < 8) { instPanPosition(3 * 64/15); } else if (c < 16) { instPanPosition(12 * 64/15); } } } static const u8 **patAddrTab; static const u8 dummyPattern[2]; /* pattern w/o any note */ static void makePatAddrTable(void) { i15x i; /*malloc2((void **)&patAddrTab, sizeof(void *) * patNum);*/ patAddrTab = memSong(sizeof(void *) * h.patNum); for (i = 0; i < h.patNum; i++) { i31x o; o = u16LittleEndian(h.pat[i]); if (o == 0) patAddrTab[i] = dummyPattern; /* yume.s3m */ else patAddrTab[i] = h.p0 + 16 * o; /*printf(" 0x%x", 16 * u16LittleEndian(h.pat[i]));*/ } /*printf("\n");*/ } static void msgOut(const u8 *p, i15x len) { for (; len > 0; len--,p++) { if (!*p) break; if (*p < 0x20 || 0x7e < *p) printf("<%X>", *p); else putchar(*p); } putchar('\n'); } static SampleInfo *sip; static void makeSampleInfo(i15x showMsg) { /*const S3mSample *s3sp;*/ const u8 *s3sp; SampleInfo *p; i15x i; /*malloc2((void **)&sip, sizeof(SampleInfo) * insNum);*/ sip = memSong(sizeof(SampleInfo) * h.insNum); if (showMsg) printf(" # Len LBeg LEnd C4Hz V\n"); for (i = 0, p = sip; i < h.insNum; i++,p++) { i15x len, lBeg, lEnd; s3sp = h.p0 + 16 * u16LittleEndian(h.ins[i]); p->beg = h.p0 + 16 * u16LittleEndian(s3sp + 0xe); /*memseg*/ len = u16LittleEndian(s3sp + 0x10); /*length*/ if (s3sp[0x1f] & 1) { /*flags & loop*/ lBeg = u16LittleEndian(s3sp + 0x14); /*loopBeg*/ p->loopBeg = p->beg + lBeg; lEnd = u16LittleEndian(s3sp + 0x18); /*loopEnd*/ p->end = p->beg + lEnd; } else { p->loopBeg = NULL; p->end = p->beg + len; /*length*/ } p->xor = 128; p->vol = s3sp[0x1c]; /*vol*/ if (p->vol > 64) p->vol = 64; p->c4spd = u16LittleEndian(s3sp + 0x20); /*c2spd*/ p->mag = 1; /*p->tune.period = 1712;*/ if (showMsg) { if (p->loopBeg != NULL) { printf("%2i: %4X %4X %4X %5d %2d ", i+1, len, lBeg, lEnd, p->c4spd, p->vol); } else { printf("%2i: %4X %5d %2d ", i+1, len, p->c4spd, p->vol); } msgOut(s3sp + 0x30, 28); } } } #if 0 static void showSampleInfo(void) { SampleInfo *p; i15x i; for (i = 0; i < h.insNum; i++) { p = &sip[i]; if (p->loopBeg) { printf("%2i: B%x E%x LB%x %dHz V%d\n", i, (unsigned)p->beg, (unsigned)p->end, (unsigned)p->loopBeg, p->c4spd, p->vol); } else { printf("%2i: B%x E%x %dHz V%d\n", i, (unsigned)p->beg, (unsigned)p->end, p->c4spd, p->vol); } } } #endif static void parseS3mHeader(const u8 *p) { h.p0 = h.songName = p; h.ordNum = u16LittleEndian(p + 0x20); h.insNum = u16LittleEndian(p + 0x22); h.patNum = u16LittleEndian(p + 0x24); h.flags = u16LittleEndian(p + 0x26); h.cwtv = u16LittleEndian(p + 0x28); h.ffi = u16LittleEndian(p + 0x2a); h.gv = p[0x30]; if (!h.gv) h.gv = 64; /* overdriv.s3m */ h.is = p[0x31]; h.it = p[0x32]; h.mv = p[0x33] & 0x7f; if (h.mv < 16) h.mv = 16; h.stereo = (p[0x33] & 0x80) != 0; h.ch = p + 0x40; h.ord = p + 0x60; h.ins = (const u8 (*)[2])(h.ord + h.ordNum); h.pat = h.ins + h.insNum; h.pan = (p[0x35] == 252)? (const u8 *)(h.pat + h.patNum) : NULL; } static u8 *repCounter; static void playAlongOrder(const OptInfo *oip) { i15x ord; i15x ord0; i15x nextOrd; ord0 = oip->startOrd; for (ord = 0; ord < h.ordNum; ord++) repCounter[ord] = 0; do { for (ord = ord0; ord < h.ordNum; ) { switch (h.ord[ord]) { case 255: goto BRK; case 254: break; default: if (oip->repLimit && ++repCounter[ord] > oip->repLimit) goto RET; if (h.ord[ord] >= h.patNum) { /* 2k_nitro.s3m */ fprintf(stderr, "No such pattern: %d\n", h.ord[ord]); break; } printf("ord %d/%d, pat %d/%d \r", ord, h.ordNum, h.ord[ord], h.patNum); fflush(stdout); unpackPattern(patAddrTab[h.ord[ord]]); nextOrd = playAPattern(oip); } if (nextOrd) ord = nextOrd & 0xff; else ord++; } BRK: ord0 = 0; } while (oip->forceRep); RET: printf("\n"); } void playS3m(const u8 *p, i31x size, const OptInfo *oip) { pattern = memSong(64 * sizeof(*pattern)); repCounter = memSong(256 * sizeof(*repCounter)); parseS3mHeader(p); /*printf("cwtv = 0x%x\n", h.cwtv);*/ msgOut(h.songName, 28); instSetVolSlideFast(h.cwtv == 0x1300 || h.flags & 64); instSetPeriodAmigaLimit(h.flags & 16); tellChSettings(); makePatAddrTable(); makeSampleInfo(oip->showMsg); ovs(h.insNum, sip, (u16x)1000 * oip->ovsFreq); playNoteSetSample(sip); playNoteSetTempo(h.it); playNoteSetSpeed(h.is); if (oip->masterVol < 0) playNoteSetMasterVol(-oip->masterVol); else playNoteSetMasterVol(h.mv); playNoteSetGlobalVol(h.gv); /*onlyCh = oip->onlyCh;*/ playAlongOrder(oip); }