ref: 6a594c824f0e29689297d1347faebaf451a1f382
dir: /src/modloaders/ft2_load_xm.c/
// Fasttracker II (or compatible) XM loader
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "../ft2_header.h"
#include "../ft2_module_loader.h"
#include "../ft2_sample_ed.h"
#include "../ft2_tables.h"
#include "../ft2_sysreqs.h"
static uint8_t packedPattData[65536];
/* ModPlug Tracker & OpenMPT supports up to 32 samples per instrument for XMs - we don't.
** For such modules, we use a temporary array here to store the extra sample data lengths
** we need to skip to be able to load the file (we lose the extra samples, though...).
*/
static uint32_t extraSampleLengths[32-MAX_SMP_PER_INST];
static bool loadInstrHeader(FILE *f, uint16_t i);
static bool loadInstrSample(FILE *f, uint16_t i);
static void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn);
static bool loadPatterns(FILE *f, uint16_t antPtn);
static void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn);
static void sanitizeInstrument(instrTyp *ins);
bool loadXM(FILE *f, uint32_t filesize)
{
uint16_t i;
songHeaderTyp h;
if (filesize < sizeof (h))
{
loaderMsgBox("Error: This file is either not a module, or is not supported.");
return false;
}
if (fread(&h, 1, sizeof (h), f) != sizeof (h))
{
loaderMsgBox("Error: This file is either not a module, or is not supported.");
return false;
}
if (h.ver < 0x0102 || h.ver > 0x0104)
{
loaderMsgBox("Error loading XM: Unsupported file version (v%01X.%02X).", (h.ver >> 8) & 15, h.ver & 0xFF);
return false;
}
if (h.len > MAX_ORDERS)
{
loaderMsgBox("Error loading XM: The song has more than 256 orders!");
return false;
}
if (h.antPtn > MAX_PATTERNS)
{
loaderMsgBox("Error loading XM: The song has more than 256 patterns!");
return false;
}
if (h.antChn == 0)
{
loaderMsgBox("Error loading XM: This file is corrupt.");
return false;
}
if (h.antInstrs > 256) // if >128 instruments, we fake-load up to 128 extra instruments and discard them
{
loaderMsgBox("Error loading XM: This file is corrupt.");
return false;
}
fseek(f, 60 + h.headerSize, SEEK_SET);
if (filesize != 336 && feof(f)) // 336 in length at this point = empty XM
{
loaderMsgBox("Error loading XM: The module is empty!");
return false;
}
memcpy(songTmp.name, h.name, 20);
songTmp.len = h.len;
songTmp.repS = h.repS;
songTmp.antChn = (uint8_t)h.antChn;
songTmp.speed = h.defSpeed;
songTmp.tempo = h.defTempo;
songTmp.ver = h.ver;
tmpLinearPeriodsFlag = h.flags & 1;
// non-FT2: clamp to max numbers that are okay for GUI
songTmp.speed = CLAMP(songTmp.speed, 1, 999);
songTmp.initialTempo = songTmp.tempo = CLAMP(songTmp.tempo, 1, 99);
if (songTmp.len == 0)
songTmp.len = 1; // songTmp.songTab is already empty
else
memcpy(songTmp.songTab, h.songTab, songTmp.len);
// some strange XMs have the order list padded with 0xFF, remove them!
for (int16_t j = 255; j >= 0; j--)
{
if (songTmp.songTab[j] != 0xFF)
break;
if (songTmp.len > j)
songTmp.len = j;
}
// even though XM supports 256 orders, FT2 supports only 255...
if (songTmp.len > 0xFF)
songTmp.len = 0xFF;
if (songTmp.ver < 0x0104)
{
// XM v1.02 and XM v1.03
for (i = 1; i <= h.antInstrs; i++)
{
if (!loadInstrHeader(f, i))
return false;
}
if (!loadPatterns(f, h.antPtn))
return false;
for (i = 1; i <= h.antInstrs; i++)
{
if (!loadInstrSample(f, i))
return false;
}
}
else
{
// XM v1.04 (latest version)
if (!loadPatterns(f, h.antPtn))
return false;
for (i = 1; i <= h.antInstrs; i++)
{
if (!loadInstrHeader(f, i))
return false;
if (!loadInstrSample(f, i))
return false;
}
}
// if we temporarily loaded more than 128 instruments, clear the extra allocated memory
if (h.antInstrs > MAX_INST)
{
for (i = MAX_INST+1; i <= h.antInstrs; i++)
{
if (instrTmp[i] != NULL)
{
free(instrTmp[i]);
instrTmp[i] = NULL;
}
}
}
/* We support loading XMs with up to 32 samples per instrument (ModPlug/OpenMPT),
** but only the first 16 will be loaded. Now make sure we set the number of samples
** back to max 16 in the headers before loading is done.
*/
bool instrHasMoreThan16Samples = false;
for (i = 1; i <= MAX_INST; i++)
{
if (instrTmp[i] != NULL && instrTmp[i]->antSamp > MAX_SMP_PER_INST)
{
instrHasMoreThan16Samples = true;
instrTmp[i]->antSamp = MAX_SMP_PER_INST;
}
}
if (songTmp.antChn > MAX_VOICES)
{
songTmp.antChn = MAX_VOICES;
loaderMsgBox("Warning: Module contains >32 channels. The extra channels will be discarded!");
}
if (h.antInstrs > MAX_INST)
loaderMsgBox("Warning: Module contains >128 instruments. The extra instruments will be discarded!");
if (instrHasMoreThan16Samples)
loaderMsgBox("Warning: Module contains instrument(s) with >16 samples. The extra samples will be discarded!");
return true;
}
static bool loadInstrHeader(FILE *f, uint16_t i)
{
uint8_t j;
uint32_t readSize;
instrHeaderTyp ih;
instrTyp *ins;
sampleHeaderTyp *src;
sampleTyp *s;
memset(extraSampleLengths, 0, sizeof (extraSampleLengths));
memset(&ih, 0, sizeof (ih));
fread(&ih.instrSize, 4, 1, f);
readSize = ih.instrSize;
// yes, some XMs can have a header size of 0, and it usually means 263 bytes (INSTR_HEADER_SIZE)
if (readSize == 0 || readSize > INSTR_HEADER_SIZE)
readSize = INSTR_HEADER_SIZE;
if (readSize < 4)
{
loaderMsgBox("Error loading XM: This file is corrupt (or not supported)!");
return false;
}
// load instrument data into temp buffer
fread(ih.name, readSize-4, 1, f); // -4 = skip ih.instrSize
// FT2 bugfix: skip instrument header data if instrSize is above INSTR_HEADER_SIZE
if (ih.instrSize > INSTR_HEADER_SIZE)
fseek(f, ih.instrSize-INSTR_HEADER_SIZE, SEEK_CUR);
if (ih.antSamp < 0 || ih.antSamp > 32)
{
loaderMsgBox("Error loading XM: This file is corrupt (or not supported)!");
return false;
}
if (i <= MAX_INST) // copy over instrument names
memcpy(songTmp.instrName[i], ih.name, 22);
if (ih.antSamp > 0)
{
if (!allocateTmpInstr(i))
{
loaderMsgBox("Not enough memory!");
return false;
}
// copy instrument header elements to our instrument struct
ins = instrTmp[i];
memcpy(ins->ta, ih.ta, 96);
memcpy(ins->envVP, ih.envVP, 12*2*sizeof(int16_t));
memcpy(ins->envPP, ih.envPP, 12*2*sizeof(int16_t));
ins->envVPAnt = ih.envVPAnt;
ins->envPPAnt = ih.envPPAnt;
ins->envVSust = ih.envVSust;
ins->envVRepS = ih.envVRepS;
ins->envVRepE = ih.envVRepE;
ins->envPSust = ih.envPSust;
ins->envPRepS = ih.envPRepS;
ins->envPRepE = ih.envPRepE;
ins->envVTyp = ih.envVTyp;
ins->envPTyp = ih.envPTyp;
ins->vibTyp = ih.vibTyp;
ins->vibSweep = ih.vibSweep;
ins->vibDepth = ih.vibDepth;
ins->vibRate = ih.vibRate;
ins->fadeOut = ih.fadeOut;
ins->midiOn = (ih.midiOn == 1) ? true : false;
ins->midiChannel = ih.midiChannel;
ins->midiProgram = ih.midiProgram;
ins->midiBend = ih.midiBend;
ins->mute = (ih.mute == 1) ? true : false; // correct logic, don't change this!
ins->antSamp = ih.antSamp; // used in loadInstrSample()
sanitizeInstrument(ins);
int32_t sampleHeadersToRead = ih.antSamp;
if (sampleHeadersToRead > MAX_SMP_PER_INST)
sampleHeadersToRead = MAX_SMP_PER_INST;
if (fread(ih.samp, sampleHeadersToRead * sizeof (sampleHeaderTyp), 1, f) != 1)
{
loaderMsgBox("General I/O error during loading!");
return false;
}
// if instrument contains more than 16 sample headers (unsupported), skip them
if (ih.antSamp > MAX_SMP_PER_INST) // can only be 0..32 at this point
{
const int32_t samplesToSkip = ih.antSamp-MAX_SMP_PER_INST;
for (j = 0; j < samplesToSkip; j++)
{
fread(&extraSampleLengths[j], 4, 1, f); // used for skipping data in loadInstrSample()
fseek(f, sizeof (sampleHeaderTyp)-4, SEEK_CUR);
}
}
for (j = 0; j < sampleHeadersToRead; j++)
{
s = &instrTmp[i]->samp[j];
src = &ih.samp[j];
// copy sample header elements to our sample struct
s->len = src->len;
s->repS = src->repS;
s->repL = src->repL;
s->vol = src->vol;
s->fine = src->fine;
s->typ = src->typ;
s->pan = src->pan;
s->relTon = src->relTon;
memcpy(s->name, src->name, 22);
// dst->pek is set up later
// sanitize stuff broken/unsupported samples (FT2 doesn't do this, but we do!)
if (s->vol > 64)
s->vol = 64;
s->relTon = CLAMP(s->relTon, -48, 71);
}
}
return true;
}
static bool loadInstrSample(FILE *f, uint16_t i)
{
if (instrTmp[i] == NULL)
return true; // empty instrument, let's just pretend it got loaded successfully
uint16_t k = instrTmp[i]->antSamp;
if (k > MAX_SMP_PER_INST)
k = MAX_SMP_PER_INST;
sampleTyp *s = instrTmp[i]->samp;
if (i > MAX_INST) // insNum > 128, just skip sample data
{
for (uint16_t j = 0; j < k; j++, s++)
{
if (s->len > 0)
fseek(f, s->len, SEEK_CUR);
}
}
else
{
for (uint16_t j = 0; j < k; j++, s++)
{
// FT2: a sample with both forward loop and pingpong loop set results in pingpong
if ((s->typ & 3) == 3)
s->typ &= 0xFE; // remove forward loop flag
int32_t l = s->len;
if (l <= 0)
{
s->len = 0;
s->repL = 0;
s->repS = 0;
if (s->typ & 32) // remove stereo flag if present
s->typ &= ~32;
}
else
{
int32_t bytesToSkip = 0;
if (l > MAX_SAMPLE_LEN)
{
bytesToSkip = l - MAX_SAMPLE_LEN;
l = MAX_SAMPLE_LEN;
}
if (!allocateTmpSmpData(s, l))
{
loaderMsgBox("Not enough memory!");
return false;
}
const int32_t bytesRead = (int32_t)fread(s->pek, 1, l, f);
if (bytesRead < l)
{
const int32_t bytesToClear = l - bytesRead;
memset(&s->pek[bytesRead], 0, bytesToClear);
}
if (bytesToSkip > 0)
fseek(f, bytesToSkip, SEEK_CUR);
delta2Samp(s->pek, l, s->typ);
if (s->typ & 32) // stereo sample - already downmixed to mono in delta2samp()
{
s->typ &= ~32; // remove stereo flag
s->len >>= 1;
s->repL >>= 1;
s->repS >>= 1;
reallocateTmpSmpData(s, s->len); // dealloc unused memory
}
}
// NON-FT2 FIX: Align to 2-byte if 16-bit sample
if (s->typ & 16)
{
s->repL &= 0xFFFFFFFE;
s->repS &= 0xFFFFFFFE;
s->len &= 0xFFFFFFFE;
}
}
}
if (instrTmp[i]->antSamp > MAX_SMP_PER_INST)
{
const int32_t samplesToSkip = instrTmp[i]->antSamp-MAX_SMP_PER_INST;
for (i = 0; i < samplesToSkip; i++)
{
if (extraSampleLengths[i] > 0)
fseek(f, extraSampleLengths[i], SEEK_CUR);
}
}
return true;
}
static bool loadPatterns(FILE *f, uint16_t antPtn)
{
uint8_t tmpLen;
patternHeaderTyp ph;
bool pattLenWarn = false;
for (uint16_t i = 0; i < antPtn; i++)
{
if (fread(&ph.patternHeaderSize, 4, 1, f) != 1)
goto pattCorrupt;
if (fread(&ph.typ, 1, 1, f) != 1)
goto pattCorrupt;
ph.pattLen = 0;
if (songTmp.ver == 0x0102)
{
if (fread(&tmpLen, 1, 1, f) != 1)
goto pattCorrupt;
if (fread(&ph.dataLen, 2, 1, f) != 1)
goto pattCorrupt;
ph.pattLen = tmpLen + 1; // +1 in v1.02
if (ph.patternHeaderSize > 8)
fseek(f, ph.patternHeaderSize - 8, SEEK_CUR);
}
else
{
if (fread(&ph.pattLen, 2, 1, f) != 1)
goto pattCorrupt;
if (fread(&ph.dataLen, 2, 1, f) != 1)
goto pattCorrupt;
if (ph.patternHeaderSize > 9)
fseek(f, ph.patternHeaderSize - 9, SEEK_CUR);
}
if (feof(f))
goto pattCorrupt;
pattLensTmp[i] = ph.pattLen;
if (pattLensTmp[i] > MAX_PATT_LEN)
{
pattLensTmp[i] = MAX_PATT_LEN;
pattLenWarn = true;
}
if (ph.dataLen > 0)
{
if (!allocateTmpPatt(i, pattLensTmp[i]))
{
loaderMsgBox("Not enough memory!");
return false;
}
if (fread(packedPattData, 1, ph.dataLen, f) != ph.dataLen)
goto pattCorrupt;
unpackPatt((uint8_t *)pattTmp[i], packedPattData, pattLensTmp[i], songTmp.antChn);
clearUnusedChannels(pattTmp[i], pattLensTmp[i], songTmp.antChn);
}
if (tmpPatternEmpty(i))
{
if (pattTmp[i] != NULL)
{
free(pattTmp[i]);
pattTmp[i] = NULL;
}
pattLensTmp[i] = 64;
}
}
if (pattLenWarn)
loaderMsgBox("This module contains pattern(s) with a length above 256! They will be truncated.");
return true;
pattCorrupt:
loaderMsgBox("Error loading XM: This file is corrupt!");
return false;
}
static void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn)
{
int32_t j;
if (dst == NULL)
return;
const int32_t srcEnd = len * (sizeof (tonTyp) * antChn);
int32_t srcIdx = 0;
int32_t numChannels = antChn;
if (numChannels > MAX_VOICES)
numChannels = MAX_VOICES;
const int32_t pitch = sizeof (tonTyp) * (MAX_VOICES - antChn);
for (int32_t i = 0; i < len; i++)
{
for (j = 0; j < numChannels; j++)
{
if (srcIdx >= srcEnd)
return; // error!
const uint8_t note = *src++;
if (note & 0x80)
{
*dst++ = (note & 0x01) ? *src++ : 0;
*dst++ = (note & 0x02) ? *src++ : 0;
*dst++ = (note & 0x04) ? *src++ : 0;
*dst++ = (note & 0x08) ? *src++ : 0;
*dst++ = (note & 0x10) ? *src++ : 0;
}
else
{
*dst++ = note;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
}
srcIdx += sizeof (tonTyp);
}
// if more than 32 channels, skip rest of the channels for this row
for (; j < antChn; j++)
{
if (srcIdx >= srcEnd)
return; // error!
const uint8_t note = *src++;
if (note & 0x80)
{
if (note & 0x01) src++;
if (note & 0x02) src++;
if (note & 0x04) src++;
if (note & 0x08) src++;
if (note & 0x10) src++;
}
else
{
src++;
src++;
src++;
src++;
}
srcIdx += sizeof (tonTyp);
}
// if song has <32 channels, align pointer to next row (skip unused channels)
if (antChn < MAX_VOICES)
dst += pitch;
}
}
static void sanitizeInstrument(instrTyp *ins) // FT2 doesn't do this, but we do!
{
ins->midiProgram = CLAMP(ins->midiProgram, 0, 127);
ins->midiBend = CLAMP(ins->midiBend, 0, 36);
if (ins->midiChannel > 15) ins->midiChannel = 15;
if (ins->vibDepth > 0x0F) ins->vibDepth = 0x0F;
if (ins->vibRate > 0x3F) ins->vibRate = 0x3F;
if (ins->vibTyp > 3) ins->vibTyp = 0;
for (int32_t i = 0; i < 96; i++)
{
if (ins->ta[i] >= MAX_SMP_PER_INST)
ins->ta[i] = MAX_SMP_PER_INST-1;
}
if (ins->envVPAnt > 12) ins->envVPAnt = 12;
if (ins->envVRepS > 11) ins->envVRepS = 11;
if (ins->envVRepE > 11) ins->envVRepE = 11;
if (ins->envVSust > 11) ins->envVSust = 11;
if (ins->envPPAnt > 12) ins->envPPAnt = 12;
if (ins->envPRepS > 11) ins->envPRepS = 11;
if (ins->envPRepE > 11) ins->envPRepE = 11;
if (ins->envPSust > 11) ins->envPSust = 11;
for (int32_t i= 0; i < 12; i++)
{
if ((uint16_t)ins->envVP[i][0] > 32767) ins->envVP[i][0] = 32767;
if ((uint16_t)ins->envPP[i][0] > 32767) ins->envPP[i][0] = 32767;
if ((uint16_t)ins->envVP[i][1] > 64) ins->envVP[i][1] = 64;
if ((uint16_t)ins->envPP[i][1] > 63) ins->envPP[i][1] = 63;
}
}