/* Extended Module Player * Copyright (C) 1996-1999 Claudio Matsuoka and Hipolito Carraro Jr * * This file is part of the Extended Module Player and is distributed * under the terms of the GNU General Public License. See doc/COPYING * for more information. */ /* Liquid Tracker module loader based on the format description written * by Nir Oren. Tested with Shell.liq sent by Adi Sapir. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "period.h" #include "load.h" #include "liq.h" #define NONE 0xff static uint8 arpeggio_val[64]; static uint8 fx[] = { NONE, FX_ARPEGGIO, FX_TEMPO, FX_BREAK, FX_PORTA_DN, NONE, NONE /* fine vibrato */, FX_GLOBALVOL, NONE, NONE, FX_JUMP, NONE, FX_VOLSLIDE, FX_EXTENDED, FX_TONEPORTA, FX_OFFSET, FX_SETPAN, NONE, FX_MULTI_RETRIG, FX_S3M_TEMPO, FX_TREMOLO, FX_PORTA_UP, FX_VIBRATO, NONE, FX_TONE_VSLIDE, NONE, FX_VIBRA_VSLIDE }; /* Effect translation */ static void xlat_fx (int c, struct xxm_event *e) { uint8 h = MSN (e->fxp), l = LSN (e->fxp); #if 0 e->fxt = e->fxp = 0; return; #endif switch (e->fxt = fx[e->fxt]) { case FX_ARPEGGIO: /* Arpeggio */ if (e->fxp) arpeggio_val[c] = e->fxp; else e->fxp = arpeggio_val[c]; break; case FX_TEMPO: if (e->fxp < 0x20) e->fxp = 0x20; break; case FX_S3M_TEMPO: if (e->fxp < 0x20) e->fxt = FX_TEMPO; break; case FX_EXTENDED: /* Extended effects */ switch (h) { case 0x3: /* Glissando */ e->fxp = l | (EX_GLISS << 4); break; case 0x4: /* Vibrato wave */ e->fxp = l | (EX_VIBRATO_WF << 4); break; case 0x5: /* Finetune */ e->fxp = l | (EX_FINETUNE << 4); break; case 0x6: /* Pattern loop */ e->fxp = l | (EX_PATTERN_LOOP << 4); break; case 0x7: /* Tremolo wave */ e->fxp = l | (EX_TREMOLO_WF << 4); break; case 0xc: /* Tremolo wave */ e->fxp = l | (EX_CUT << 4); break; case 0xd: /* Tremolo wave */ e->fxp = l | (EX_DELAY << 4); break; case 0xe: /* Tremolo wave */ e->fxp = l | (EX_PATT_DELAY << 4); break; default: /* Ignore */ e->fxt = e->fxp = 0; break; } break; case NONE: /* No effect */ e->fxt = e->fxp = 0; break; } } static void decode_event (uint8 x1, struct xxm_event *event, FILE *f) { uint8 x2; if (x1 & 0x01) { fread (&x2, 1, 1, f); if (x2 == 0xfe) event->note = XMP_KEY_OFF; else event->note = x2 + 1 + 24; } if (x1 & 0x02) { fread (&x2, 1, 1, f); event->ins = x2 + 1; } if (x1 & 0x04) { fread (&x2, 1, 1, f); event->vol = x2 + 1; } if (x1 & 0x08) { fread (&x2, 1, 1, f); event->fxt = x2 + 1 - 'A'; } if (x1 & 0x10) { fread (&x2, 1, 1, f); event->fxp = x2; } } int liq_load (FILE * f) { int i, j, k; struct xxm_event *event, *event2, dummy; struct liq_header lh; struct liq_instrument li; struct liq_pattern lp; uint8 x1, x2, pmag[4]; LOAD_INIT (); fread (&lh, 1, sizeof (lh), f); if (strncmp ((char *) lh.magic, "Liquid Module:", 14)) return -1; L_ENDIAN16 (lh.version); L_ENDIAN16 (lh.speed); L_ENDIAN16 (lh.bpm); L_ENDIAN16 (lh.low); L_ENDIAN16 (lh.high); L_ENDIAN16 (lh.chn); L_ENDIAN32 (lh.flags); L_ENDIAN16 (lh.pat); L_ENDIAN16 (lh.ins); L_ENDIAN16 (lh.len); L_ENDIAN16 (lh.hdrsz); xxh->tpo = lh.speed; xxh->bpm = lh.bpm; xxh->chn = lh.chn; xxh->pat = lh.pat; xxh->ins = xxh->smp = lh.ins; xxh->len = lh.len; xxh->trk = xxh->chn * xxh->pat; xxh->flg = XXM_FLG_INSVOL; strncpy (xmp_ctl->name, lh.name, 30); strncpy (tracker_name, lh.tracker, 20); strncpy (author_name, lh.author, 20); sprintf (xmp_ctl->type, "Liquid module %d.%02d", lh.version >> 8, lh.version & 0x00ff); MODULE_INFO (); for (i = 0; i < xxh->chn; i++) { fread (&x1, 1, 1, f); xxc[i].pan = x1 << 2; } for (i = 0; i < xxh->chn; i++) { fread (&x1, 1, 1, f); xxc[i].vol = x1; } fread (xxo, 1, xxh->len, f); /* Skip 1.01 echo pools */ fseek (f, lh.hdrsz - (0x6d + xxh->chn * 2 + xxh->len), SEEK_CUR); PATTERN_INIT (); /* Read and convert patterns */ if (V (0)) report ("Stored patterns: %d ", xxh->pat); printf ("CHN + %d\n", xxh->chn); for (i = 0; i < xxh->pat; i++) { printf ("\n*** PATTERN %d ***\n", i); PATTERN_ALLOC (i); fread (pmag, 1, 4, f); if (pmag[0] == '!') continue; fread (&lp, sizeof (struct liq_pattern), 1, f); L_ENDIAN16 (lp.rows); L_ENDIAN32 (lp.size); xxp[i]->rows = lp.rows; TRACK_ALLOC (i); for (j = 0; j < 64/*xxh->chn*/; j++) { for (k = 0; k < xxp[i]->rows; k++) { event = j < xxh->chn ? &EVENT (i, j, k) : &dummy; fread (&x1, 1, 1, f); printf ("Fetch: %02x\n", x1); switch (x1) { case 0xc0: goto next_pattern; case 0xa0: goto next_track; case 0xe0: fread (&x1, 1, 1, f); k += x1; case 0x80: goto next_row; case 0xe1: fread (&x1, 1, 1, f); j += x1; goto next_track; } if (x1 > 0x80 && x1 < 0xa0) { fread (&x2, 1, 1, f); printf ("Will copy %d times...\n", x2); decode_event (x1, event, f); xlat_fx (j, event); for (; x2; x2--, k++) { printf ("Copy to (%d %d %d)\n", i, j, k); event2 = j < xxh->chn ? &EVENT (i, j, k) : &dummy; memcpy (event2, event, sizeof (struct xxm_event)); } goto next_row; } if (x1 > 0xa0 && x1 < 0xe0) { if (x1 < 0xc0) { fread (&x2, 1, 1, f); printf ("Will decode %d times...\n", x2); for (; x2; x2--) { printf ("Decode to (%d %d %d)\n", i, j, k); decode_event (x1, event, f); xlat_fx (j, event); fread (&x1, 1, 1, f); k++; event = j < xxh->chn ? &EVENT (i, j, k) : &dummy; } } decode_event (x1, event, f); xlat_fx (j, event); goto next_row; } printf ("Unpacked event\n"); if (++x1) event->note = x1 + 24; else if (x1 == 0xff) event->note = XMP_KEY_OFF; fread (&x1, 1, 1, f); if (++x1) event->ins = x1; fread (&x1, 1, 1, f); if (++x1) event->vol = x1; fread (&x1, 1, 1, f); if (++x1) event->fxt = x1 - 'A' - 1; fread (&x1, 1, 1, f); event->fxp = x1; xlat_fx (j, event); next_row: } next_track: } next_pattern: if (V (0)) report ("."); } /* Read and convert instruments */ INSTRUMENT_INIT (); if (V (0)) report ("\nInstruments : %d ", xxh->ins); for (i = 0; i < xxh->ins; i++) { xxi[i] = calloc (sizeof (struct xxm_instrument), 1); fread (&li, 1, sizeof (struct liq_instrument), f); L_ENDIAN32 (li.length); L_ENDIAN32 (li.loopstart); L_ENDIAN32 (li.loopend); L_ENDIAN32 (li.c2spd); L_ENDIAN16 (li.hdrsz); L_ENDIAN16 (li.comp); xxih[i].nsm = !!(li.length); xxih[i].vol = 0x40; xxs[i].len = li.length; xxs[i].lps = li.loopstart; xxs[i].lpe = li.loopend; if (li.flags & 0x01) { xxs[i].flg = WAVE_16_BITS; xxs[i].len <<= 1; } if (li.loopend > 0) xxs[i].flg = WAVE_LOOPING; xxi[i][0].vol = li.vol; xxi[i][0].gvl = li.gvl; xxi[i][0].pan = li.pan; xxi[i][0].sid = i; strncpy ((char *) xxih[i].name, li.name, 24); str_adj ((char *) li.name); if ((V (1)) && (strlen ((char *) li.name) || xxs[i].len)) { report ("\n[%2X] %-30.30s %05x%c%05x %05x %c %02x %02x %5d ", i, li.name, xxs[i].len, xxs[i].flg & WAVE_16_BITS ? '+' : ' ', xxs[i].lps, xxs[i].lpe, xxs[i].flg & WAVE_LOOPING ? 'L' : ' ', xxi[i][0].vol, xxi[i][0].gvl, li.c2spd); } c2spd_to_note (li.c2spd, &xxi[i][0].xpo, &xxi[i][0].fin); fseek (f, li.hdrsz - 0x90, SEEK_CUR); if (!xxs[i].len) continue; xmp_drv_loadpatch (f, xxi[i][0].sid, xmp_ctl->c4rate, 0, &xxs[i], NULL); if (V (0)) report ("."); } if (V (0)) report ("\n"); return 0; }