ref: ba8d4ea83a668289d1a55d67148c0de1c389dbd5
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; } }