ref: 5dca43d1a307ba6d2288c4be9ff7d3f705aaabc4
dir: /src/modloaders/ft2_load_xm.c/
/* Fasttracker II (or compatible) XM loader
**
** Note: Data sanitation is done in the last stage
** of module loading, so you don't need to do that here.
*/
#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, uint16_t xmVersion);
static void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn);
static void loadADPCMSample(FILE *f, sample_t *s); // ModPlug Tracker
bool loadXM(FILE *f, uint32_t filesize)
{
xmHdr_t 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.version < 0x0102 || h.version > 0x0104)
{
loaderMsgBox("Error loading XM: Unsupported file version (v%01X.%02X).", (h.version >> 8) & 15, h.version & 0xFF);
return false;
}
if (h.numOrders > MAX_ORDERS)
{
loaderMsgBox("Error loading XM: The song has more than 256 orders!");
return false;
}
if (h.numPatterns > MAX_PATTERNS)
{
loaderMsgBox("Error loading XM: The song has more than 256 patterns!");
return false;
}
if (h.numChannels == 0)
{
loaderMsgBox("Error loading XM: This file is corrupt.");
return false;
}
if (h.numInstr > 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.name[20] = '\0';
songTmp.songLength = h.numOrders;
songTmp.songLoopStart = h.songLoopStart;
songTmp.numChannels = (uint8_t)h.numChannels;
songTmp.BPM = h.BPM;
songTmp.speed = h.speed;
tmpLinearPeriodsFlag = h.flags & 1;
if (songTmp.songLength == 0)
songTmp.songLength = 1; // songTmp.songTab is already empty
else
memcpy(songTmp.orders, h.orders, songTmp.songLength);
// some strange XMs have the order list padded with 0xFF, remove them!
for (int16_t j = 255; j >= 0; j--)
{
if (songTmp.orders[j] != 0xFF)
break;
if (songTmp.songLength > j)
songTmp.songLength = j;
}
// even though XM supports 256 orders, FT2 supports only 255...
if (songTmp.songLength > 255)
songTmp.songLength = 255;
if (h.version < 0x0104)
{
// XM v1.02 and XM v1.03
for (uint16_t i = 1; i <= h.numInstr; i++)
{
if (!loadInstrHeader(f, i))
return false;
}
if (!loadPatterns(f, h.numPatterns, h.version))
return false;
for (uint16_t i = 1; i <= h.numInstr; i++)
{
if (!loadInstrSample(f, i))
return false;
}
}
else
{
// XM v1.04 (latest version)
if (!loadPatterns(f, h.numPatterns, h.version))
return false;
for (uint16_t i = 1; i <= h.numInstr; 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.numInstr > MAX_INST)
{
for (int32_t i = MAX_INST+1; i <= h.numInstr; 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 (int32_t i = 1; i <= MAX_INST; i++)
{
if (instrTmp[i] != NULL && instrTmp[i]->numSamples > MAX_SMP_PER_INST)
{
instrHasMoreThan16Samples = true;
instrTmp[i]->numSamples = MAX_SMP_PER_INST;
}
}
if (songTmp.numChannels > MAX_CHANNELS)
{
songTmp.numChannels = MAX_CHANNELS;
loaderMsgBox("Warning: Module contains >32 channels. The extra channels will be discarded!");
}
if (h.numInstr > 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)
{
uint32_t readSize;
xmInsHdr_t ih;
instr_t *ins;
xmSmpHdr_t *src;
sample_t *s;
memset(extraSampleLengths, 0, sizeof (extraSampleLengths));
memset(&ih, 0, sizeof (ih));
fread(&readSize, 4, 1, f);
fseek(f, -4, SEEK_CUR);
// 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 ((int32_t)readSize < 0)
{
loaderMsgBox("Error loading XM: This file is corrupt!");
return false;
}
fread(&ih, readSize, 1, f); // read instrument header
// 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.numSamples < 0 || ih.numSamples > 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.numSamples > 0 && ih.numSamples <= 32)
{
if (!allocateTmpInstr(i))
{
loaderMsgBox("Not enough memory!");
return false;
}
// copy instrument header elements to our instrument struct
ins = instrTmp[i];
memcpy(ins->note2SampleLUT, ih.note2SampleLUT, 96);
memcpy(ins->volEnvPoints, ih.volEnvPoints, 12*2*sizeof(int16_t));
memcpy(ins->panEnvPoints, ih.panEnvPoints, 12*2*sizeof(int16_t));
ins->volEnvLength = ih.volEnvLength;
ins->panEnvLength = ih.panEnvLength;
ins->volEnvSustain = ih.volEnvSustain;
ins->volEnvLoopStart = ih.volEnvLoopStart;
ins->volEnvLoopEnd = ih.volEnvLoopEnd;
ins->panEnvSustain = ih.panEnvSustain;
ins->panEnvLoopStart = ih.panEnvLoopStart;
ins->panEnvLoopEnd = ih.panEnvLoopEnd;
ins->volEnvFlags = ih.volEnvFlags;
ins->panEnvFlags = ih.panEnvFlags;
ins->vibType = ih.vibType;
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->numSamples = ih.numSamples; // used in loadInstrSample()
int32_t sampleHeadersToRead = ih.numSamples;
if (sampleHeadersToRead > MAX_SMP_PER_INST)
sampleHeadersToRead = MAX_SMP_PER_INST;
if (fread(ih.smp, sampleHeadersToRead * sizeof (xmSmpHdr_t), 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.numSamples > MAX_SMP_PER_INST) // can only be 0..32 at this point
{
const int32_t samplesToSkip = ih.numSamples-MAX_SMP_PER_INST;
for (int32_t j = 0; j < samplesToSkip; j++)
{
fread(&extraSampleLengths[j], 4, 1, f); // used for skipping data in loadInstrSample()
fseek(f, sizeof (xmSmpHdr_t)-4, SEEK_CUR);
}
}
for (int32_t j = 0; j < sampleHeadersToRead; j++)
{
s = &instrTmp[i]->smp[j];
src = &ih.smp[j];
// copy sample header elements to our sample struct
s->length = src->length;
s->loopStart = src->loopStart;
s->loopLength = src->loopLength;
s->volume = src->volume;
s->finetune = src->finetune;
s->flags = src->flags;
s->panning = src->panning;
s->relativeNote = src->relativeNote;
/* If the sample is 8-bit mono and nameLength (reserved) is 0xAD,
** then this is a 4-bit ADPCM compressed sample (ModPlug Tracker).
*/
if (src->nameLength == 0xAD && !(src->flags & (SAMPLE_16BIT | SAMPLE_STEREO)))
s->flags |= SAMPLE_ADPCM;
memcpy(s->name, src->name, 22);
// dst->dataPtr is set up later
}
}
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]->numSamples;
if (k > MAX_SMP_PER_INST)
k = MAX_SMP_PER_INST;
sample_t *s = instrTmp[i]->smp;
if (i > MAX_INST) // insNum > 128, just skip sample data
{
for (uint16_t j = 0; j < k; j++, s++)
{
if (s->length > 0)
fseek(f, s->length, SEEK_CUR);
}
}
else
{
for (uint16_t j = 0; j < k; j++, s++)
{
if (s->length <= 0)
{
s->length = 0;
s->loopStart = 0;
s->loopLength = 0;
s->flags = 0;
}
else
{
const int32_t lengthInFile = s->length;
bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
bool stereoSample = !!(s->flags & SAMPLE_STEREO);
bool adpcmSample = !!(s->flags & SAMPLE_ADPCM); // ModPlug Tracker
if (sample16Bit) // we use units of samples (not bytes like in FT2)
{
s->length >>= 1;
s->loopStart >>= 1;
s->loopLength >>= 1;
}
if (s->length > MAX_SAMPLE_LEN)
s->length = MAX_SAMPLE_LEN;
if (!allocateSmpData(s, s->length, sample16Bit))
{
loaderMsgBox("Not enough memory!");
return false;
}
if (adpcmSample)
{
loadADPCMSample(f, s);
}
else
{
const int32_t sampleLengthInBytes = SAMPLE_LENGTH_BYTES(s);
fread(s->dataPtr, 1, sampleLengthInBytes, f);
if (sampleLengthInBytes < lengthInFile)
fseek(f, lengthInFile-sampleLengthInBytes, SEEK_CUR);
delta2Samp(s->dataPtr, s->length, s->flags);
if (stereoSample) // stereo sample - already downmixed to mono in delta2samp()
{
s->length >>= 1;
s->loopStart >>= 1;
s->loopLength >>= 1;
reallocateSmpData(s, s->length, sample16Bit); // dealloc unused memory
}
}
}
// remove stereo flag if present (already handled)
if (s->flags & SAMPLE_STEREO)
s->flags &= ~SAMPLE_STEREO;
}
}
// skip sample headers if we have more than 16 samples in instrument
if (instrTmp[i]->numSamples > MAX_SMP_PER_INST)
{
const int32_t samplesToSkip = instrTmp[i]->numSamples-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, uint16_t xmVersion)
{
uint8_t tmpLen;
xmPatHdr_t ph;
bool pattLenWarn = false;
for (uint16_t i = 0; i < antPtn; i++)
{
if (fread(&ph.headerSize, 4, 1, f) != 1)
goto pattCorrupt;
if (fread(&ph.type, 1, 1, f) != 1)
goto pattCorrupt;
ph.numRows = 0;
if (xmVersion == 0x0102)
{
if (fread(&tmpLen, 1, 1, f) != 1)
goto pattCorrupt;
if (fread(&ph.dataSize, 2, 1, f) != 1)
goto pattCorrupt;
ph.numRows = tmpLen + 1; // +1 in v1.02
if (ph.headerSize > 8)
fseek(f, ph.headerSize - 8, SEEK_CUR);
}
else
{
if (fread(&ph.numRows, 2, 1, f) != 1)
goto pattCorrupt;
if (fread(&ph.dataSize, 2, 1, f) != 1)
goto pattCorrupt;
if (ph.headerSize > 9)
fseek(f, ph.headerSize - 9, SEEK_CUR);
}
if (feof(f))
goto pattCorrupt;
patternNumRowsTmp[i] = ph.numRows;
if (patternNumRowsTmp[i] > MAX_PATT_LEN)
{
patternNumRowsTmp[i] = MAX_PATT_LEN;
pattLenWarn = true;
}
if (ph.dataSize > 0)
{
if (!allocateTmpPatt(i, patternNumRowsTmp[i]))
{
loaderMsgBox("Not enough memory!");
return false;
}
if (fread(packedPattData, 1, ph.dataSize, f) != ph.dataSize)
goto pattCorrupt;
unpackPatt((uint8_t *)patternTmp[i], packedPattData, patternNumRowsTmp[i], songTmp.numChannels);
clearUnusedChannels(patternTmp[i], patternNumRowsTmp[i], songTmp.numChannels);
}
if (tmpPatternEmpty(i))
{
if (patternTmp[i] != NULL)
{
free(patternTmp[i]);
patternTmp[i] = NULL;
}
patternNumRowsTmp[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 (note_t) * antChn);
int32_t srcIdx = 0;
int32_t numChannels = antChn;
if (numChannels > MAX_CHANNELS)
numChannels = MAX_CHANNELS;
const int32_t pitch = sizeof (note_t) * (MAX_CHANNELS - 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 (note_t);
}
// 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 (note_t);
}
// if song has <32 channels, align pointer to next row (skip unused channels)
if (antChn < MAX_CHANNELS)
dst += pitch;
}
}
static void loadADPCMSample(FILE *f, sample_t *s) // ModPlug Tracker
{
int8_t deltaLUT[16];
fread(deltaLUT, 1, 16, f);
int8_t *dataPtr = s->dataPtr;
const int32_t dataLength = (s->length + 1) / 2;
int8_t currSample = 0;
for (int32_t i = 0; i < dataLength; i++)
{
const uint8_t nibbles = (uint8_t)fgetc(f);
currSample += deltaLUT[nibbles & 0x0F];
*dataPtr++ = currSample;
currSample += deltaLUT[nibbles >> 4];
*dataPtr++ = currSample;
}
}