/* 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. */ /* Loader for UNIC and Laxity modules based on the format description * written by Sylvain Chipaux (Asle/ReDoX) and the Pro-Wizard docs * by Gryzor. UNIC and Laxity formats created by Anders E. Hansen * (Laxity/Kefrens). * * Gryzor's comments from PW_FORMATS-Engl.guide: * UNIC: "The UNIC format is very similar to Protracker... At least in * the heading... same length: 1084 bytes. Even the 'M.K.' is present, * sometimes!" * LAX: "This format is VERY VERY similar to UNIC (and to Protracker)! * Except that the module name is no longer present... And no 'M.K.' * mark either..." * * Tested with the LAX modules sent by Bert Jahn: * LAX.KEFRENS_DD.checknobankh * LAX.KEFRENS_DD.maj-strings.cc */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "load.h" #include "period.h" struct unic_instrument { uint8 name[20]; int16 finetune; uint16 size; uint8 unknown; uint8 volume; uint16 loop_start; uint16 loop_size; } PACKED; struct unic_header { uint8 title[20]; /* LAX has no title */ struct unic_instrument ins[31]; uint8 len; uint8 zero; uint8 orders[128]; uint8 magic[4]; } PACKED; static int _unic_load (FILE *, int); int unic_load (FILE *f) { return _unic_load (f, 0) ? _unic_load (f, 1) : 0; } static int _unic_load (FILE *f, int lax) { int i, j; int smp_size; struct xxm_event *event; struct unic_header uh; uint8 unic_event[3]; int nomagic, xpo; LOAD_INIT (); xxh->ins = 31; xxh->smp = xxh->ins; smp_size = 0; nomagic = 0; xpo = 36; fread ((uint8 *)&uh + 20 * lax, 1, sizeof (struct unic_header) - 20 * lax, f); for (i = 0; i < xxh->ins; i++) { B_ENDIAN16 (uh.ins[i].finetune); B_ENDIAN16 (uh.ins[i].size); B_ENDIAN16 (uh.ins[i].loop_start); B_ENDIAN16 (uh.ins[i].loop_size); } if (uh.len > 0x7f) return -1; xxh->len = uh.len; memcpy (xxo, uh.orders, xxh->len); for (i = 0; i < xxh->len; i++) if (xxo[i] > xxh->pat) xxh->pat = xxo[i]; xxh->pat++; xxh->trk = xxh->chn * xxh->pat; /* According to Asle: * * ``Gryzor says this ID is not always present. I've only two Unic * files and both have this ID. Now, Marley states there can be the * ID "UNIC" instead or even $00-00-00-00. I've just find out that * the first music of the demo "desert dream" by Kefrens is a UNIC, * has a title and no ID string!'' * * Claudio's note: I'll check for UNIC first -- if it fails, check * module size with and without magic ID. */ if (lax || strncmp ((char *)uh.magic, "UNIC", 4)) { for (i = 0; i < 31; i++) smp_size += uh.ins[i].size * 2; i = xxh->pat * 0x300 + smp_size + sizeof (struct unic_header) - 20 * lax; if ((xmp_ctl->size != (i - 4)) && (xmp_ctl->size != i)) return -1; if (xmp_ctl->size == i - 4) { /* No magic */ fseek (f, -4, SEEK_CUR); nomagic = 1; } } /* Corrputed mod? */ if (xxh->pat > 0x7f || xxh->len == 0 || xxh->len > 0x7f) return -1; if (!lax) { strncpy (xmp_ctl->name, (char *) uh.title, 20); if (nomagic) { sprintf (xmp_ctl->type, "UNIC Tracker (no magic)"); } else { sprintf (xmp_ctl->type, "UNIC Tracker [%02X %02X %02X %02X]", uh.magic[0], uh.magic[1], uh.magic[2], uh.magic[3]); } } else { sprintf (xmp_ctl->type, "UNIC Tracker 2 (Laxity)"); } /* * Just a wild guess based on some of my UNICs (UNIC.Am*-3ch) * Could someone check this? */ if (!nomagic) { if (uh.magic[3] == 0x3c) xpo = 24; } MODULE_INFO (); INSTRUMENT_INIT (); for (i = 0; i < xxh->ins; i++) { xxi[i] = calloc (sizeof (struct xxm_instrument), 1); xxs[i].len = 2 * uh.ins[i].size; if (lax) xxs[i].lps = (2 + (xmp_ctl->fetch & XMP_CTL_FIXLOOP ? 0 : 2)) * uh.ins[i].loop_start; xxs[i].lpe = xxs[i].lps + 2 * uh.ins[i].loop_size; xxs[i].flg = uh.ins[i].loop_size > 1 ? WAVE_LOOPING : 0; xxi[i][0].fin = uh.ins[i].finetune; xxi[i][0].vol = uh.ins[i].volume; xxi[i][0].pan = 0x80; xxi[i][0].sid = i; xxih[i].nsm = !!(xxs[i].len); xxih[i].rls = 0xfff; strncpy (xxih[i].name, uh.ins[i].name, 20); str_adj (xxih[i].name); if (V (1) && (strlen ((char *) xxih[i].name) || (xxs[i].len > 2))) { report ("[%2X] %-20.20s %04x %04x %04x %c V%02x %+d\n", i, xxih[i].name, xxs[i].len, xxs[i].lps, xxs[i].lpe, uh.ins[i].loop_size > 1 ? 'L' : ' ', xxi[i][0].vol, (char) xxi[i][0].fin >> 4); } } PATTERN_INIT (); /* Load and convert patterns */ if (V (0)) report ("Stored patterns: %d ", xxh->pat); for (i = 0; i < xxh->pat; i++) { PATTERN_ALLOC (i); xxp[i]->rows = 64; TRACK_ALLOC (i); for (j = 0; j < 0x100; j++) { event = &EVENT (i, j & 0x3, j >> 2); fread (unic_event, 1, 3, f); /* Event format: * * 0000 0000 0000 0000 0000 0000 * |\ / \ / \ / \ / * | note ins fx parameter * ins * * 0x3f is a blank note. */ event->note = unic_event[0] & 0x3f; if (event->note != 0x00 && event->note != 0x3f) event->note += xpo; else event->note = 0; event->ins = ((unic_event[0] & 0x40) >> 2) | MSN (unic_event[1]); event->fxt = LSN (unic_event[1]); event->fxp = unic_event[2]; disable_continue_fx (event); /* According to Asle: * ``Just note that pattern break effect command (D**) uses * HEX value in UNIC format (while it is DEC values in PTK). * Thus, it has to be converted!'' */ if (event->fxt == 0x0d) event->fxp = (event->fxp / 10) << 4 | (event->fxp % 10); } if (V (0)) report ("."); } xxh->flg |= XXM_FLG_MODRNG; /* Load samples */ if (V (0)) report ("\nStored samples : %d ", xxh->smp); for (i = 0; i < xxh->smp; i++) { if (!xxs[i].len) continue; xmp_drv_loadpatch (f, xxi[i][0].sid, xmp_ctl->c4rate, 0, &xxs[xxi[i][0].sid], NULL); if (V (0)) report ("."); } if (V (0)) report ("\n"); return 0; }