/* * Schism Tracker - a cross-platform Impulse Tracker clone * copyright (c) 2003-2005 Storlek * copyright (c) 2005-2008 Mrs. Brisby * copyright (c) 2009 Storlek & Mrs. Brisby * copyright (c) 2010-2012 Storlek * URL: http://schismtracker.org/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define NEED_BYTESWAP #include "headers.h" #include "fmt.h" #include "it.h" #include "song.h" #include "sndfile.h" #include #include /* --------------------------------------------------------------------- */ #pragma pack(push, 1) struct xm_point { uint16_t ticks; // Time in tracker ticks uint16_t val; // Value from 0x00 to 0x40. }; struct xm_sample_header { uint32_t samplen; uint32_t loopstart; uint32_t looplen; uint8_t vol; signed char finetune; uint8_t type; uint8_t pan; signed char relnote; uint8_t res; char name[22]; }; struct xi_sample_header { uint8_t snum[96]; union { uint16_t env[48]; // Occupies same mem as venv,penv struct { struct xm_point venv[12], penv[12]; }; }; uint8_t vnum, pnum; uint8_t vsustain, vloops, vloope, psustain, ploops, ploope; uint8_t vtype, ptype; uint8_t vibtype, vibsweep, vibdepth, vibrate; uint16_t volfade; uint8_t reserved1[0x16]; uint16_t nsamples; }; struct xi_file_header { int8_t header[0x15]; // "Extended Instrument: " int8_t name[0x16]; // Name of instrument uint8_t magic; // 0x1a, DOS EOF char so you can 'type file.xi' int8_t tracker[0x14]; // Name of tracker uint16_t version; // big-endian 0x0102 struct xi_sample_header xish; struct xm_sample_header sheader[]; }; #pragma pack(pop) static int validate_xi(const struct xi_file_header *xi, size_t length) { if (length <= sizeof(struct xi_file_header)) return 0; if (memcmp(xi->header, "Extended Instrument: ", 21) != 0) return 0; if (xi->magic != 0x1a) return 0; if (bswapLE16(xi->version) != 0x0102) return(0); return(1); } /* --------------------------------------------------------------------- */ int fmt_xi_read_info(dmoz_file_t *file, const uint8_t *data, size_t length) { struct xi_file_header *xi = (struct xi_file_header *) data; if (!validate_xi(xi, length)) return 0; file->description = "FastTracker Instrument"; file->title = strn_dup((const char *)xi->name, 22); file->type = TYPE_INST_XI; return 1; } int fmt_xi_load_instrument(const uint8_t *data, size_t length, int slot) { const struct xi_file_header *xi = (const struct xi_file_header *) data; struct xi_sample_header xmsh; struct instrumentloader ii; song_instrument_t *g; const uint8_t *sampledata, *end; int k, prevtick; if (!slot) return 0; if (!validate_xi(xi, length)) return 0; end = data + length; song_delete_instrument(slot, 0); g = instrument_loader_init(&ii, slot); memcpy(g->name, xi->name, 22); g->name[22] = '\0'; xmsh = xi->xish; for (k = 0; k < 96; k++) { if (xmsh.snum[k] > 15) xmsh.snum[k] = 15; xmsh.snum[k] = instrument_loader_sample(&ii, xmsh.snum[k] + 1); g->note_map[k + 12] = k + 1 + 12; if (xmsh.snum[k]) g->sample_map[k + 12] = xmsh.snum[k]; } for (k = 0; k < 12; k++) { g->note_map[k] = 0; g->sample_map[k] = 0; g->note_map[k + 108] = 0; g->sample_map[k + 108] = 0; } // bswap all volume and panning envelope points for (k = 0; k < 48; k++) xmsh.env[k] = bswapLE16(xmsh.env[k]); // Set up envelope types in instrument if (xmsh.vtype & 0x01) g->flags |= ENV_VOLUME; if (xmsh.vtype & 0x02) g->flags |= ENV_VOLSUSTAIN; if (xmsh.vtype & 0x04) g->flags |= ENV_VOLLOOP; if (xmsh.ptype & 0x01) g->flags |= ENV_PANNING; if (xmsh.ptype & 0x02) g->flags |= ENV_PANSUSTAIN; if (xmsh.ptype & 0x04) g->flags |= ENV_PANLOOP; prevtick = -1; // Copy envelopes into instrument for (k = 0; k < xmsh.vnum; k++) { if (xmsh.venv[k].ticks < prevtick) prevtick++; else prevtick = xmsh.venv[k].ticks; g->vol_env.ticks[k] = prevtick; g->vol_env.values[k] = xmsh.venv[k].val; } prevtick = -1; for (k = 0; k < xmsh.pnum; k++) { if (xmsh.penv[k].ticks < prevtick) prevtick++; else prevtick = xmsh.penv[k].ticks; g->pan_env.ticks[k] = prevtick; g->pan_env.values[k] = xmsh.penv[k].val; } g->vol_env.loop_start = xmsh.vloops; g->vol_env.loop_end = xmsh.vloope; g->vol_env.sustain_start = xmsh.vsustain; g->vol_env.nodes = xmsh.vnum; g->pan_env.loop_start = xmsh.ploops; g->pan_env.loop_end = xmsh.ploope; g->pan_env.sustain_start = xmsh.psustain; g->pan_env.nodes = xmsh.pnum; xmsh.volfade = bswapLE16(xmsh.volfade); xmsh.nsamples = bswapLE16(xmsh.nsamples); // Sample data begins at the end of the sample headers sampledata = (const uint8_t *) (xi->sheader + xmsh.nsamples); for (k = 0; k < xmsh.nsamples; k++) { struct xm_sample_header xmss = xi->sheader[k]; song_sample_t *smp; unsigned int rs, samplesize, n; xmss.samplen = bswapLE32(xmss.samplen); xmss.loopstart = bswapLE32(xmss.loopstart); xmss.looplen = bswapLE32(xmss.looplen); rs = SF_LE | SF_PCMD; // endianness; encoding rs |= (xmss.type & 0x20) ? SF_SS : SF_M; // channels rs |= (xmss.type & 0x10) ? SF_16 : SF_8; // bits if (xmss.type & 0x10) { xmss.looplen >>= 1; xmss.loopstart >>= 1; xmss.samplen >>= 1; } if (xmss.type & 0x20) { xmss.looplen >>= 1; xmss.loopstart >>= 1; xmss.samplen >>= 1; } if (xmss.loopstart >= xmss.samplen) xmss.type &= ~3; xmss.looplen += xmss.loopstart; if (xmss.looplen > xmss.samplen) xmss.looplen = xmss.samplen; if (!xmss.looplen) xmss.type &= ~3; n = instrument_loader_sample(&ii, k + 1); smp = song_get_sample(n); smp->flags = 0; memcpy(smp->name, xmss.name, 22); smp->name[21] = '\0'; samplesize = xmss.samplen; smp->length = samplesize; smp->loop_start = xmss.loopstart; smp->loop_end = xmss.looplen; if (smp->loop_end < smp->loop_start) smp->loop_end = smp->length; if (smp->loop_start >= smp->loop_end) smp->loop_start = smp->loop_end = 0; switch (xmss.type & 3) { case 3: case 2: smp->flags |= CHN_PINGPONGLOOP; case 1: smp->flags |= CHN_LOOP; } smp->volume = xmss.vol << 2; if (smp->volume > 256) smp->volume = 256; smp->global_volume = 64; smp->panning = xmss.pan; smp->flags |= CHN_PANNING; smp->vib_type = xmsh.vibtype; smp->vib_speed = xmsh.vibsweep; smp->vib_depth = xmsh.vibdepth; smp->vib_rate = xmsh.vibrate / 4; // XXX xm.c does not divide here, which is wrong? smp->c5speed = transpose_to_frequency(xmss.relnote, xmss.finetune); sampledata += csf_read_sample(current_song->samples + n, rs, sampledata, (end-sampledata)); } return 1; }