ref: c151f95b68a8500612a49768cec1648c02562779
dir: /src/modloaders/ft2_load_it.c/
/* (Lossy) Impulse Tracker module loader. ** ** It makes little sense to convert this format to XM, as it results ** in severe conversion losses. The reason I wrote this loader anyway, ** is so that you can import IT files to extract samples, pattern data ** and so on. ** ** 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_sysreqs.h" #if defined(_MSC_VER) || defined(__plan9__) #pragma pack on #pragma pack on #endif typedef struct itHdr_t { char ID[4], songName[26]; uint16_t rowHighlight, ordNum, insNum, smpNum, patNum, cwtv, cmwt, flags, special; uint8_t globalVol, mixingVol, speed, BPM, panSep, pitchWheelDepth; uint16_t msgLen; uint32_t msgOffs, reserved; uint8_t initialPans[64], initialVols[64]; } #ifdef __GNUC__ __attribute__ ((packed)) #endif itHdr_t; typedef struct envNode_t { int8_t magnitude; uint16_t tick; } #ifdef __GNUC__ __attribute__ ((packed)) #endif envNode_t; typedef struct env_t { uint8_t flags, num, loopBegin, loopEnd, sustainLoopBegin, sustainLoopEnd; envNode_t nodePoints[25]; uint8_t reserved; } #ifdef __GNUC__ __attribute__ ((packed)) #endif env_t; typedef struct itInsHdr_t { char ID[4], dosFilename[12+1]; uint8_t NNA, DCT, DCA; uint16_t fadeOut; uint8_t pitchPanSep, pitchPanCenter, globVol, defPan, randVol, randPan; uint16_t trackerVer; uint8_t numSamples, res1; char instrumentName[26]; uint8_t filterCutoff, filterResonance, midiChn, midiProg; uint16_t midiBank; uint16_t smpNoteTable[120]; env_t volEnv, panEnv, pitchEnv; } #ifdef __GNUC__ __attribute__ ((packed)) #endif itInsHdr_t; typedef struct itOldInsHdr_t { char ID[4], dosFilename[12+1]; uint8_t volEnvFlags, volEnvLoopBegin, volEnvLoopEnd, volEnvSusLoopBegin, volEnvSusLoopEnd; uint16_t res1, fadeOut; uint8_t NNA, DNC; uint16_t trackerVer; uint8_t numSamples, res2; char instrumentName[26]; uint8_t res3[6]; uint16_t smpNoteTable[120]; uint8_t volEnv[200]; uint16_t volEnvPoints[25]; } #ifdef __GNUC__ __attribute__ ((packed)) #endif itOldInsHdr_t; typedef struct itSmpHdr_t { char ID[4], dosFilename[12+1]; uint8_t globVol, flags, vol; char sampleName[26]; uint8_t cvt, defPan; uint32_t length, loopBegin, loopEnd, c5Speed, sustainLoopBegin, sustainLoopEnd, offsetInFile; uint8_t autoVibratoSpeed, autoVibratoDepth, autoVibratoRate, autoVibratoWaveform; } #ifdef __GNUC__ __attribute__ ((packed)) #endif itSmpHdr_t; #if defined(_MSC_VER) || defined(__plan9__) #pragma pack off #endif static uint8_t decompBuffer[65536]; static uint8_t volPortaConv[9] = { 1, 4, 8, 16, 32, 64, 96, 128, 255 }; static bool loadCompressed16BitSample(FILE *f, sample_t *s, bool deltaEncoded); static bool loadCompressed8BitSample(FILE *f, sample_t *s, bool deltaEncoded); static void setAutoVibrato(instr_t *ins, itSmpHdr_t *itSmp); static bool loadSample(FILE *f, sample_t *s, itSmpHdr_t *itSmp); bool loadIT(FILE *f, uint32_t filesize) { uint32_t insOffs[256], smpOffs[256], patOffs[256]; itSmpHdr_t *itSmp, smpHdrs[256]; itHdr_t itHdr; if (filesize < sizeof (itHdr)) { loaderMsgBox("This IT module is not supported or is corrupt!"); goto error; } fread(&itHdr, sizeof (itHdr), 1, f); if (itHdr.ordNum > 257 || itHdr.insNum > 256 || itHdr.smpNum > 256 || itHdr.patNum > 256) { loaderMsgBox("This IT module is not supported or is corrupt!"); goto error; } tmpLinearPeriodsFlag = !!(itHdr.flags & 8); songTmp.pattNum = itHdr.patNum; songTmp.speed = itHdr.speed; songTmp.BPM = itHdr.BPM; memcpy(songTmp.name, itHdr.songName, 20); songTmp.name[20] = '\0'; bool oldFormat = (itHdr.cmwt < 0x200); bool songUsesInstruments = !!(itHdr.flags & 4); bool oldEffects = !!(itHdr.flags & 16); bool compatGxx = !!(itHdr.flags & 32); // read order list for (int32_t i = 0; i < MAX_ORDERS; i++) { const uint8_t patt = (uint8_t)fgetc(f); if (patt == 254) // separator ("+++"), skip it continue; if (patt == 255) // end of pattern list break; songTmp.orders[songTmp.songLength] = patt; songTmp.songLength++; if (songTmp.songLength == MAX_ORDERS-1) break; } // read file pointers fseek(f, sizeof (itHdr) + itHdr.ordNum, SEEK_SET); fread(insOffs, 4, itHdr.insNum, f); fread(smpOffs, 4, itHdr.smpNum, f); fread(patOffs, 4, itHdr.patNum, f); for (int32_t i = 0; i < itHdr.smpNum; i++) { fseek(f, smpOffs[i], SEEK_SET); fread(&smpHdrs[i], sizeof (itSmpHdr_t), 1, f); } if (!songUsesInstruments) // read samples (as instruments) { int32_t numIns = MIN(itHdr.smpNum, MAX_INST); itSmp = smpHdrs; for (int16_t i = 0; i < numIns; i++, itSmp++) { if (!allocateTmpInstr(1 + i)) { loaderMsgBox("Not enough memory!"); return false; } instr_t *ins = instrTmp[1+i]; sample_t *s = &ins->smp[0]; memcpy(songTmp.instrName[1+i], itSmp->sampleName, 22); songTmp.instrName[1+i][22] = '\0'; ins->numSamples = (itSmp->length > 0) ? 1 : 0; if (ins->numSamples > 0) { setAutoVibrato(ins, itSmp); if (!loadSample(f, s, itSmp)) { loaderMsgBox("Not enough memory!"); goto error; } } } } else if (oldFormat) // read instruments (old format version) { itOldInsHdr_t itIns; int32_t numIns = MIN(itHdr.insNum, MAX_INST); for (int16_t i = 0; i < numIns; i++) { fseek(f, insOffs[i], SEEK_SET); fread(&itIns, sizeof (itIns), 1, f); if (!allocateTmpInstr(1 + i)) { loaderMsgBox("Not enough memory!"); return false; } instr_t *ins = instrTmp[1+i]; memcpy(songTmp.instrName[1+i], itIns.instrumentName, 22); songTmp.instrName[1+i][22] = '\0'; ins->fadeout = itIns.fadeOut * 64; // 0..64 -> 0..4096 if (ins->fadeout > 4095) ins->fadeout = 4095; // find out what samples to load into this XM instrument header int16_t numSamples = 0; uint8_t sampleList[MAX_SMP_PER_INST]; bool sampleAdded[256]; memset(sampleList, 0, sizeof (sampleList)); memset(sampleAdded, 0, sizeof (sampleAdded)); for (int32_t j = 0; j < 96; j++) { uint8_t sample = itIns.smpNoteTable[12+j] >> 8; if (sample > 0 && !sampleAdded[sample-1] && numSamples < MAX_SMP_PER_INST) { sampleAdded[sample-1] = true; sampleList[numSamples] = sample-1; numSamples++; } } /* If instrument only has one sample, copy over the sample's ** auto-vibrato parameters to this instrument. */ bool singleSample = true; if (numSamples > 1) { uint8_t firstSample = sampleList[0]; for (int32_t j = 1; j < numSamples; j++) { if (sampleList[j] != firstSample) { singleSample = false; break; } } } if (singleSample) setAutoVibrato(ins, &smpHdrs[sampleList[0]]); // create new note-to-sample table for (int32_t j = 0; j < 8*12; j++) { uint8_t inSmp = itIns.smpNoteTable[(1 * 12) + j] >> 8; uint8_t outSmp = 0; if (inSmp > 0) { inSmp--; for (; outSmp < numSamples; outSmp++) { if (inSmp == sampleList[outSmp]) break; } if (outSmp >= numSamples) outSmp = 0; } ins->note2SampleLUT[j] = outSmp; } // load volume envelope if (itIns.volEnvFlags & 1) { bool volEnvLoopOn = !!(itIns.volEnvFlags & 2); bool volEnvSusOn = !!(itIns.volEnvFlags & 4); ins->volEnvFlags |= ENV_ENABLED; if (volEnvLoopOn) ins->volEnvFlags |= ENV_LOOP; if (volEnvSusOn) ins->volEnvFlags |= ENV_SUSTAIN; ins->volEnvLoopStart = MIN(itIns.volEnvLoopBegin, 11); ins->volEnvLoopEnd = MIN(itIns.volEnvLoopEnd, 11); ins->volEnvSustain = MIN(itIns.volEnvSusLoopEnd, 11); // hack: if sus loop only, set as normal loop + set sustain point if (!volEnvLoopOn && volEnvSusOn) { ins->volEnvLoopStart = MIN(itIns.volEnvSusLoopBegin, 11); ins->volEnvLoopEnd = MIN(itIns.volEnvSusLoopEnd, 11); ins->volEnvSustain = MIN(itIns.volEnvSusLoopEnd, 11); ins->volEnvFlags |= ENV_LOOP + ENV_SUSTAIN; } int32_t j = 0; for (; j < 12; j++) { if (itIns.volEnvPoints[j] >> 8 == 0xFF) break; // end of volume envelope ins->volEnvPoints[j][0] = itIns.volEnvPoints[j] & 0xFF; ins->volEnvPoints[j][1] = itIns.volEnvPoints[j] >> 8; } ins->volEnvLength = (uint8_t)j; // increase loop end point tick by one to better match IT style env looping if (ins->volEnvFlags & ENV_LOOP) ins->volEnvPoints[ins->volEnvLoopEnd][0]++; } ins->numSamples = numSamples; if (ins->numSamples > 0) { sample_t *s = ins->smp; for (int32_t j = 0; j < ins->numSamples; j++, s++) { if (!loadSample(f, s, &smpHdrs[sampleList[j]])) { loaderMsgBox("Not enough memory!"); goto error; } } } } } else // read instruments (later format version) { itInsHdr_t itIns; int32_t numIns = MIN(itHdr.insNum, MAX_INST); for (int16_t i = 0; i < numIns; i++) { fseek(f, insOffs[i], SEEK_SET); fread(&itIns, sizeof (itIns), 1, f); if (!allocateTmpInstr(1 + i)) { loaderMsgBox("Not enough memory!"); return false; } instr_t *ins = instrTmp[1+i]; memcpy(songTmp.instrName[1+i], itIns.instrumentName, 22); songTmp.instrName[1+i][22] = '\0'; ins->fadeout = itIns.fadeOut * 32; // 0..128 -> 0..4096 if (ins->fadeout > 4095) ins->fadeout = 4095; // find out what samples to load into this XM instrument header int16_t numSamples = 0; uint8_t sampleList[MAX_SMP_PER_INST]; bool sampleAdded[256]; memset(sampleList, 0, sizeof (sampleList)); memset(sampleAdded, 0, sizeof (sampleAdded)); for (int32_t j = 0; j < 96; j++) { uint8_t sample = itIns.smpNoteTable[12+j] >> 8; if (sample > 0 && !sampleAdded[sample-1] && numSamples < MAX_SMP_PER_INST) { sampleAdded[sample-1] = true; sampleList[numSamples] = sample-1; numSamples++; } } /* If instrument only has one sample, copy over the sample's ** auto-vibrato parameters to this instrument. */ bool singleSample = true; if (numSamples > 1) { uint8_t firstSample = sampleList[0]; for (int32_t j = 1; j < numSamples; j++) { if (sampleList[j] != firstSample) { singleSample = false; break; } } } if (singleSample) setAutoVibrato(ins, &smpHdrs[sampleList[0]]); // create new note-to-sample table for (int32_t j = 0; j < 8*12; j++) { uint8_t inSmp = itIns.smpNoteTable[(1 * 12) + j] >> 8; uint8_t outSmp = 0; if (inSmp > 0) { inSmp--; for (; outSmp < numSamples; outSmp++) { if (inSmp == sampleList[outSmp]) break; } if (outSmp >= numSamples) outSmp = 0; } ins->note2SampleLUT[j] = outSmp; } // load volume envelope env_t *volEnv = &itIns.volEnv; bool volEnvEnabled = !!(volEnv->flags & 1); if (volEnvEnabled && volEnv->num > 0) { bool volEnvLoopOn = !!(volEnv->flags & 2); bool volEnvSusOn = !!(volEnv->flags & 4); ins->volEnvFlags |= ENV_ENABLED; if (volEnvLoopOn) ins->volEnvFlags |= ENV_LOOP; if (volEnvSusOn) ins->volEnvFlags |= ENV_SUSTAIN; ins->volEnvLength = MIN(volEnv->num, 12); ins->volEnvLoopStart = MIN(volEnv->loopBegin, 11); ins->volEnvLoopEnd = MIN(volEnv->loopEnd, 11); ins->volEnvSustain = MIN(volEnv->sustainLoopEnd, 11); // hack: if sus loop only, set as normal loop + set sustain point if (!volEnvLoopOn && volEnvSusOn) { ins->volEnvLoopStart = MIN(volEnv->sustainLoopBegin, 11); ins->volEnvLoopEnd = MIN(volEnv->sustainLoopEnd, 11); ins->volEnvSustain = MIN(volEnv->sustainLoopEnd, 11); ins->volEnvFlags |= ENV_LOOP + ENV_SUSTAIN; } for (int32_t j = 0; j < ins->volEnvLength; j++) { ins->volEnvPoints[j][0] = volEnv->nodePoints[j].tick; ins->volEnvPoints[j][1] = volEnv->nodePoints[j].magnitude; } // increase loop end point tick by one to better match IT style env looping if (ins->volEnvFlags & ENV_LOOP) ins->volEnvPoints[ins->volEnvLoopEnd][0]++; } // load pan envelope env_t *panEnv = &itIns.panEnv; bool panEnvEnabled = !!(panEnv->flags & 1); if (panEnvEnabled && panEnv->num > 0) { bool panEnvLoopOn = !!(panEnv->flags & 2); bool panEnvSusOn = !!(panEnv->flags & 4); ins->panEnvFlags |= ENV_ENABLED; if (panEnvLoopOn) ins->panEnvFlags |= ENV_LOOP; if (panEnvSusOn) ins->panEnvFlags |= ENV_SUSTAIN; ins->panEnvLength = MIN(panEnv->num, 12); ins->panEnvLoopStart = MIN(panEnv->loopBegin, 11); ins->panEnvLoopEnd = MIN(panEnv->loopEnd, 11); ins->panEnvSustain = MIN(panEnv->sustainLoopEnd, 11); // hack: if sus loop only, set as normal loop + set sustain point if (!panEnvLoopOn && panEnvSusOn) { ins->panEnvLoopStart = MIN(panEnv->sustainLoopBegin, 11); ins->panEnvLoopEnd = MIN(panEnv->sustainLoopEnd, 11); ins->panEnvSustain = MIN(panEnv->sustainLoopEnd, 11); ins->panEnvFlags |= ENV_LOOP + ENV_SUSTAIN; } for (int32_t j = 0; j < ins->panEnvLength; j++) { ins->panEnvPoints[j][0] = panEnv->nodePoints[j].tick; ins->panEnvPoints[j][1] = panEnv->nodePoints[j].magnitude + 32; } // increase loop end point tick by one to better match IT style env looping if (ins->panEnvFlags & ENV_LOOP) ins->panEnvPoints[ins->panEnvLoopEnd][0] = panEnv->nodePoints[ins->panEnvLoopEnd].tick + 1; } ins->numSamples = numSamples; if (ins->numSamples > 0) { sample_t *s = ins->smp; for (int32_t j = 0; j < ins->numSamples; j++, s++) { if (!loadSample(f, s, &smpHdrs[sampleList[j]])) { loaderMsgBox("Not enough memory!"); goto error; } } } } } // load pattern data uint32_t numChannels = 0; for (int32_t i = 0; i < songTmp.pattNum; i++) { if (patOffs[i] == 0) continue; fseek(f, patOffs[i], SEEK_SET); uint16_t length, numRows; fread(&length, 2, 1, f); fread(&numRows, 2, 1, f); fseek(f, 4, SEEK_CUR); numRows = MIN(numRows, MAX_PATT_LEN); if (numRows == 0) continue; if (!allocateTmpPatt(i, numRows)) { loaderMsgBox("Not enough memory!"); goto error; } uint8_t lastMask[64]; memset(lastMask, 0, sizeof (lastMask)); note_t lastNote[64]; memset(lastNote, 0, sizeof (lastNote)); note_t *patt = patternTmp[i]; int32_t bytesRead = 0; int32_t row = 0; while (bytesRead < length && row < numRows) { uint8_t byte = (uint8_t)fgetc(f); bytesRead++; if (byte == 0) { row++; continue; } const uint8_t ch = (byte - 1) & 63; if (ch > numChannels) numChannels = ch; note_t emptyNote; note_t *p = (ch >= MAX_CHANNELS) ? &emptyNote : &patt[(row * MAX_CHANNELS) + ch]; if (byte & 128) { lastMask[ch] = (uint8_t)fgetc(f); bytesRead++; } if (lastMask[ch] & 16) p->note = lastNote[ch].note; if (lastMask[ch] & 32) p->instr = lastNote[ch].instr; if (lastMask[ch] & 64) p->vol = lastNote[ch].vol; if (lastMask[ch] & 128) { p->efx = lastNote[ch].efx; p->efxData = lastNote[ch].efxData; } if (lastMask[ch] & 1) { uint8_t note = (uint8_t)fgetc(f); bytesRead++; if (note < 120) { note++; if (note < 12 || note >= 96+12) note = 0; else note -= 12; } else if (note != 254) { note = NOTE_OFF; } if (note > NOTE_OFF && note != 254) note = 0; // remove note // 254 (note cut) is handled later! p->note = lastNote[ch].note = note; } if (lastMask[ch] & 2) { uint8_t ins = (uint8_t)fgetc(f); bytesRead++; if (ins > MAX_INST) ins = 0; p->instr = lastNote[ch].instr = ins; } if (lastMask[ch] & 4) { p->vol = lastNote[ch].vol = 1 + (uint8_t)fgetc(f); bytesRead++; } if (lastMask[ch] & 8) { p->efx = lastNote[ch].efx = (uint8_t)fgetc(f); bytesRead++;; p->efxData = lastNote[ch].efxData = (uint8_t)fgetc(f); bytesRead++; } } } numChannels++; songTmp.numChannels = MIN((numChannels + 1) & ~1, MAX_CHANNELS); // convert pattern data uint8_t lastInstr[MAX_CHANNELS], lastGInstr[MAX_CHANNELS]; uint8_t lastDxy[MAX_CHANNELS], lastExy[MAX_CHANNELS], lastFxy[MAX_CHANNELS]; uint8_t lastJxy[MAX_CHANNELS], lastKxy[MAX_CHANNELS], lastLxy[MAX_CHANNELS]; uint8_t lastOxx[MAX_CHANNELS]; memset(lastInstr, 0, sizeof (lastInstr)); memset(lastGInstr, 0, sizeof (lastGInstr)); memset(lastDxy, 0, sizeof (lastDxy)); memset(lastExy, 0, sizeof (lastExy)); memset(lastFxy, 0, sizeof (lastFxy)); memset(lastJxy, 0, sizeof (lastJxy)); memset(lastKxy, 0, sizeof (lastKxy)); memset(lastLxy, 0, sizeof (lastLxy)); memset(lastOxx, 0, sizeof (lastOxx)); for (int32_t i = 0; i < songTmp.pattNum; i++) { note_t *p = patternTmp[i]; if (p == NULL) continue; for (int32_t j = 0; j < patternNumRowsTmp[i]; j++) { for (int32_t ch = 0; ch < songTmp.numChannels; ch++, p++) { if (p->instr > 0) lastInstr[ch] = p->instr; // effect if (p->efx != 0) { const uint8_t itEfx = 'A' + (p->efx - 1); switch (itEfx) { case 'A': // set speed { if (p->efxData == 0) // A00 is ignored in IT { p->efx = p->efxData = 0; } else { p->efx = 0xF; if (p->efxData > 31) p->efxData = 31; } } break; case 'B': p->efx = 0xB; break; // position jump case 'C': p->efx = 0xD; break; // pattern break case 'D': // volume slide { if (p->efxData == 0) { bool lastWasFineSlide = (lastDxy[ch] & 0x0F) == 0x0F || (lastDxy[ch] >> 4) == 0x0F; if (lastWasFineSlide) p->efxData = lastDxy[ch]; } else { lastDxy[ch] = p->efxData; } if ((p->efxData & 0x0F) == 0x0F && (p->efxData >> 4) > 0) { p->efx = 0xE; p->efxData = 0xA0 + (p->efxData >> 4); } else if ((p->efxData >> 4) == 0x0F && (p->efxData & 0x0F) > 0) { p->efx = 0xE; p->efxData = 0xB0 + (p->efxData & 0x0F); } else { p->efx = 0xA; } } break; case 'E': // portamento down { if (p->efxData == 0) { bool lastWasFineSlide = (lastExy[ch] & 0x0F) == 0x0F || (lastExy[ch] >> 4) == 0x0F; bool lastWasExtraFineSlide = (lastExy[ch] & 0x0F) == 0x0E || (lastExy[ch] >> 4) == 0x0E; if (lastWasFineSlide || lastWasExtraFineSlide) p->efxData = lastExy[ch]; } else { lastExy[ch] = p->efxData; } if (p->efxData < 224) { p->efx = 0x2; } else if ((p->efxData >> 4) == 0x0E) { p->efx = 16 + ('X' - 'G'); p->efxData = 0x20 + (p->efxData & 0x0F); } else if ((p->efxData >> 4) == 0x0F) { p->efx = 0xE; p->efxData = 0x20 + (p->efxData & 0x0F); } } break; case 'F': // portamento up { if (p->efxData == 0) { bool lastWasFineSlide = (lastFxy[ch] & 0x0F) == 0x0F || (lastFxy[ch] >> 4) == 0x0F; bool lastWasExtraFineSlide = (lastFxy[ch] & 0x0F) == 0x0E || (lastFxy[ch] >> 4) == 0x0E; if (lastWasFineSlide || lastWasExtraFineSlide) p->efxData = lastFxy[ch]; } else { lastFxy[ch] = p->efxData; } if (p->efxData < 224) { p->efx = 0x1; } else if ((p->efxData >> 4) == 0x0E) { p->efx = 16 + ('X' - 'G'); p->efxData = 0x10 + (p->efxData & 0x0F); } else if ((p->efxData >> 4) == 0x0F) { p->efx = 0xE; p->efxData = 0x10 + (p->efxData & 0x0F); } } break; case 'G': // tone portamento { p->efx = 3; // remove illegal slides (this is not quite right, but good enough) if (!compatGxx && p->instr != 0 && p->instr != lastGInstr[ch]) p->efx = p->efxData = 0; } break; case 'H': // vibrato { p->efx = 4; if (!oldEffects && p->efxData > 0) p->efxData = (p->efxData & 0xF0) | ((p->efxData & 0x0F) >> 1); } break; case 'I': // tremor { p->efx = 16 + ('T' - 'G'); int8_t onTime = p->efxData >> 4; if (onTime > 0) // closer to IT2 (but still off) onTime--; int8_t offTime = p->efxData & 0x0F; if (offTime > 0) // --- offTime--; p->efxData = (onTime << 4) | offTime; } break; case 'J': // arpeggio { p->efx = 0; if (p->efxData != 0) p->efxData = lastJxy[ch] = (p->efxData >> 4) | (p->efxData << 4); // swap order (FT2 = reversed) else p->efxData = lastJxy[ch]; } break; case 'K': // volume slide + vibrato { if (p->efxData == 0) { bool lastWasFineSlide = (lastKxy[ch] & 0x0F) == 0x0F || (lastKxy[ch] >> 4) == 0x0F; if (lastWasFineSlide) p->efxData = lastKxy[ch]; } else { lastKxy[ch] = p->efxData; } if ((p->efxData & 0x0F) == 0x0F && (p->efxData >> 4) > 0) { if (p->vol == 0) p->vol = 1+203; // IT2 vibrato of param 0 (to be converted) p->efx = 0xE; p->efxData = 0xA0 + (p->efxData >> 4); } else if ((p->efxData >> 4) == 0x0F && (p->efxData & 0x0F) > 0) { if (p->vol == 0) p->vol = 1+203; // IT2 vibrato of param 0 (to be converted) p->efx = 0xE; p->efxData = 0xB0 + (p->efxData & 0x0F); } else { p->efx = 0x6; } } break; case 'L': // volume slide + tone portamento { if (p->efxData == 0) { bool lastWasFineSlide = (lastLxy[ch] & 0x0F) == 0x0F || (lastLxy[ch] >> 4) == 0x0F; if (lastWasFineSlide) p->efxData = lastLxy[ch]; } else { lastLxy[ch] = p->efxData; } if ((p->efxData & 0x0F) == 0x0F && (p->efxData >> 4) > 0) { if (p->vol == 0) p->vol = 1+193; // IT2 tone portamento of param 0 (to be converted) p->efx = 0xE; p->efxData = 0xA0 + (p->efxData >> 4); } else if ((p->efxData >> 4) == 0x0F && (p->efxData & 0x0F) > 0) { if (p->vol == 0) p->vol = 1+193; // IT2 tone portamento of param 0 (to be converted) p->efx = 0xE; p->efxData = 0xB0 + (p->efxData & 0x0F); } else { p->efx = 0x5; } } break; case 'O': // set sample offset { p->efx = 0x9; if (p->efxData > 0) lastOxx[ch] = p->efxData; // handle cases where the sample offset is after the end of the sample if (lastInstr[ch] > 0 && lastOxx[ch] > 0 && p->note > 0 && p->note <= 96) { instr_t *ins = instrTmp[lastInstr[ch]]; if (ins != NULL) { const uint8_t sample = ins->note2SampleLUT[p->note-1]; if (sample < MAX_SMP_PER_INST) { sample_t *s = &ins->smp[sample]; if (s->length > 0) { const bool loopEnabled = (GET_LOOPTYPE(s->flags) != LOOP_DISABLED); const uint32_t sampleEnd = loopEnabled ? s->loopStart+s->loopLength : s->length; if (lastOxx[ch]*256UL >= sampleEnd) { if (oldEffects) { if (loopEnabled) p->efxData = (uint8_t)(sampleEnd >> 8); } else { p->efx = p->efxData = 0; } } } } } } } break; case 'P': // panning slide { p->efx = 16 + ('P' - 'G'); if ((p->efxData >> 4) == 0) { uint8_t param = (((p->efxData & 0x0F) * 255) + 32) / 64; if (param > 15) param = 15; p->efxData = param << 4; } else if ((p->efxData & 0x0F) == 0) { uint8_t param = (((p->efxData >> 4) * 255) + 32) / 64; if (param > 15) param = 15; p->efxData = param; } } break; case 'Q': // note retrigger { p->efx = 16 + ('R' - 'G'); if ((p->efxData & 0xF0) == 0x00) p->efxData |= 0x80; } break; case 'R': // tremolo { p->efx = 7; p->efxData = (p->efxData & 0xF0) | ((p->efxData & 0x0F) >> 1); } break; case 'S': // special effects { switch (p->efxData >> 4) { case 0x1: p->efx = 0xE3; break; // set glissando control case 0x3: // set vibrato waveform { if ((p->efxData & 0x0F) > 2) p->efx = p->efxData = 0; else p->efx = 0xE4; } break; case 0x4: // set tremolo waveform { if ((p->efxData & 0x0F) > 2) p->efx = p->efxData = 0; else p->efx = 0xE7; } break; case 0x8: p->efx = 0x08; p->efxData = (p->efxData << 4) | (p->efxData & 0x0F); break; case 0xB: p->efx = 0xE6; break; // pattern loop case 0xC: p->efx = 0xEC; break; // note cut case 0xD: p->efx = 0xED; break; // note delay case 0xE: p->efx = 0xEE; break; // pattern delay default: p->efx = p->efxData = 0; break; } } break; case 'T': // set tempo (BPM) { p->efx = 0xF; if (p->efxData < 32) p->efx = p->efxData = 0; // tempo slide is not supported } break; case 'V': // set global volume { p->efx = 16 + ('G' - 'G'); p->efxData >>= 1; // IT2 g.vol. ranges 0..128, FT2 g.vol. ranges 0..64 if (p->efxData > 64) p->efxData = 64; } break; case 'W': // global volume slide { p->efx = 16 + ('H' - 'G'); // IT2 g.vol. ranges 0..128, FT2 g.vol. ranges 0..64 if (p->efxData >> 4 == 0) { uint8_t param = p->efxData & 0x0F; if (param > 1) p->efxData = param >> 1; } else if ((p->efxData & 0x0F) == 0) { uint8_t param = p->efxData >> 4; if (param > 1) p->efxData = (param >> 1) << 4; } } break; case 'X': p->efx = 8; break; // set 8-bit panning default: p->efx = p->efxData = 0; break; } } else { p->efxData = 0; } if (p->instr != 0 && p->efx != 0x3) lastGInstr[ch] = p->instr; // volume column if (p->vol > 0) { p->vol--; if (p->vol <= 64) // set volume { p->vol += 0x10; } else if (p->vol <= 74) // fine volume slide up { p->vol = 0x90 + (p->vol - 65); } else if (p->vol <= 84) // fine volume slide down { p->vol = 0x80 + (p->vol - 75); } else if (p->vol <= 94) // volume slide up { p->vol = 0x70 + (p->vol - 85); } else if (p->vol <= 104) // volume slide down { p->vol = 0x60 + (p->vol - 95); } else if (p->vol <= 114) // pitch slide down { uint8_t param = p->vol - 105; p->vol = 0; if (p->efx == 0 && p->efxData == 0) { p->efx = 2; p->efxData = param * 4; } } else if (p->vol <= 124) // pitch slide up { uint8_t param = p->vol - 115; p->vol = 0; if (p->efx == 0 && p->efxData == 0) { p->efx = 1; p->efxData = param * 4; } } else if (p->vol <= 192) // set panning { p->vol = 0xC0 + (((p->vol - 128) * 15) / 64); } else if (p->vol >= 193 && p->vol <= 202) // portamento { uint8_t param = p->vol - 193; if (p->efx == 0 && p->efxData == 0) { p->vol = 0; p->efx = 3; p->efxData = (param == 0) ? 0 : volPortaConv[param-1]; } else { p->vol = 0xF0 + param; } } else if (p->vol <= 212) // vibrato { p->vol = 0xB0 + (p->vol - 203); } } // note if (p->note == 254) // note cut { p->note = 0; if (p->efx == 0 && p->efxData == 0) { // EC0 (instant note cut) p->efx = 0xE; p->efxData = 0xC0; } else if (p->vol == 0) { // volume command vol 0 p->vol = 0x10; } } } p += MAX_CHANNELS - songTmp.numChannels; } } // removing this message is considered a criminal act!!! loaderMsgBox("Loading of this format has severe issues. Don't use this for listening to .ITs!"); return true; error: return false; } static void decompress16BitData(int16_t *dst, const uint8_t *src, uint32_t blockLength) { uint8_t byte8, bitDepth, bitDepthInv, bitsRead; uint16_t bytes16, lastVal; uint32_t bytes32; lastVal = 0; bitDepth = 17; bitDepthInv = bitsRead = 0; blockLength >>= 1; while (blockLength != 0) { bytes32 = (*(uint32_t *)src) >> bitsRead; bitsRead += bitDepth; src += bitsRead >> 3; bitsRead &= 7; if (bitDepth <= 6) { bytes32 <<= bitDepthInv & 0x1F; bytes16 = (uint16_t)bytes32; if (bytes16 != 0x8000) { lastVal += (int16_t)bytes16 >> (bitDepthInv & 0x1F); // arithmetic shift *dst++ = lastVal; blockLength--; } else { byte8 = ((bytes32 >> 16) & 0xF) + 1; if (byte8 >= bitDepth) byte8++; bitDepth = byte8; bitDepthInv = 16; if (bitDepthInv < bitDepth) bitDepthInv++; bitDepthInv -= bitDepth; bitsRead += 4; } continue; } bytes16 = (uint16_t)bytes32; if (bitDepth <= 16) { uint16_t tmp16 = 0xFFFF >> (bitDepthInv & 0x1F); bytes16 &= tmp16; tmp16 = (tmp16 >> 1) - 8; if (bytes16 > tmp16+16 || bytes16 <= tmp16) { bytes16 <<= bitDepthInv & 0x1F; bytes16 = (int16_t)bytes16 >> (bitDepthInv & 0x1F); // arithmetic shift lastVal += bytes16; *dst++ = lastVal; blockLength--; continue; } byte8 = (uint8_t)(bytes16 - tmp16); if (byte8 >= bitDepth) byte8++; bitDepth = byte8; bitDepthInv = 16; if (bitDepthInv < bitDepth) bitDepthInv++; bitDepthInv -= bitDepth; continue; } if (bytes32 & 0x10000) { bitDepth = (uint8_t)(bytes16 + 1); bitDepthInv = 16 - bitDepth; } else { lastVal += bytes16; *dst++ = lastVal; blockLength--; } } } static void decompress8BitData(int8_t *dst, const uint8_t *src, uint32_t blockLength) { uint8_t lastVal, byte8, bitDepth, bitDepthInv, bitsRead; uint16_t bytes16; lastVal = 0; bitDepth = 9; bitDepthInv = bitsRead = 0; while (blockLength != 0) { bytes16 = (*(uint16_t *)src) >> bitsRead; bitsRead += bitDepth; src += (bitsRead >> 3); bitsRead &= 7; byte8 = bytes16 & 0xFF; if (bitDepth <= 6) { bytes16 <<= (bitDepthInv & 0x1F); byte8 = bytes16 & 0xFF; if (byte8 != 0x80) { lastVal += (int8_t)byte8 >> (bitDepthInv & 0x1F); // arithmetic shift *dst++ = lastVal; blockLength--; continue; } byte8 = (bytes16 >> 8) & 7; bitsRead += 3; src += (bitsRead >> 3); bitsRead &= 7; } else { if (bitDepth == 8) { if (byte8 < 0x7C || byte8 > 0x83) { lastVal += byte8; *dst++ = lastVal; blockLength--; continue; } byte8 -= 0x7C; } else if (bitDepth < 8) { byte8 <<= 1; if (byte8 < 0x78 || byte8 > 0x86) { lastVal += (int8_t)byte8 >> (bitDepthInv & 0x1F); // arithmetic shift *dst++ = lastVal; blockLength--; continue; } byte8 = (byte8 >> 1) - 0x3C; } else { bytes16 &= 0x1FF; if ((bytes16 & 0x100) == 0) { lastVal += byte8; *dst++ = lastVal; blockLength--; continue; } } } byte8++; if (byte8 >= bitDepth) byte8++; bitDepth = byte8; bitDepthInv = 8; if (bitDepthInv < bitDepth) bitDepthInv++; bitDepthInv -= bitDepth; } } static bool loadCompressed16BitSample(FILE *f, sample_t *s, bool deltaEncoded) { int8_t *dstPtr = (int8_t *)s->dataPtr; uint32_t i = s->length * 2; while (i > 0) { uint32_t bytesToUnpack = 32768; if (bytesToUnpack > i) bytesToUnpack = i; uint16_t packedLen; fread(&packedLen, sizeof (uint16_t), 1, f); fread(decompBuffer, 1, packedLen, f); decompress16BitData((int16_t *)dstPtr, decompBuffer, bytesToUnpack); if (deltaEncoded) // convert from delta values to PCM { int16_t *ptr16 = (int16_t *)dstPtr; int16_t lastSmp16 = 0; // yes, reset this every block! const uint32_t length = bytesToUnpack >> 1; for (uint32_t j = 0; j < length; j++) { lastSmp16 += ptr16[j]; ptr16[j] = lastSmp16; } } dstPtr += bytesToUnpack; i -= bytesToUnpack; } return true; } static bool loadCompressed8BitSample(FILE *f, sample_t *s, bool deltaEncoded) { int8_t *dstPtr = (int8_t *)s->dataPtr; uint32_t i = s->length; while (i > 0) { uint32_t bytesToUnpack = 32768; if (bytesToUnpack > i) bytesToUnpack = i; uint16_t packedLen; fread(&packedLen, sizeof (uint16_t), 1, f); fread(decompBuffer, 1, packedLen, f); decompress8BitData(dstPtr, decompBuffer, bytesToUnpack); if (deltaEncoded) // convert from delta values to PCM { int8_t lastSmp8 = 0; // yes, reset this every block! for (uint32_t j = 0; j < bytesToUnpack; j++) { lastSmp8 += dstPtr[j]; dstPtr[j] = lastSmp8; } } dstPtr += bytesToUnpack; i -= bytesToUnpack; } return true; } static void setAutoVibrato(instr_t *ins, itSmpHdr_t *itSmp) { ins->autoVibType = itSmp->autoVibratoWaveform; if (ins->autoVibType > 3 || itSmp->autoVibratoRate == 0) { // turn off auto-vibrato ins->autoVibDepth = ins->autoVibRate = ins->autoVibSweep = ins->autoVibType = 0; return; } ins->autoVibRate = itSmp->autoVibratoSpeed; if (ins->autoVibRate > 63) ins->autoVibRate = 63; int32_t autoVibSweep = ((itSmp->autoVibratoDepth * 256) + 128) / itSmp->autoVibratoRate; if (autoVibSweep > 255) autoVibSweep = 255; ins->autoVibSweep = (uint8_t)autoVibSweep; ins->autoVibDepth = itSmp->autoVibratoDepth; if (ins->autoVibDepth > 15) ins->autoVibDepth = 15; } static bool loadSample(FILE *f, sample_t *s, itSmpHdr_t *itSmp) { bool sampleIs16Bit = !!(itSmp->flags & 2); bool compressed = !!(itSmp->flags & 8); bool hasLoop = !!(itSmp->flags & 16); bool bidiLoop = !!(itSmp->flags & 64); bool signedSamples = !!(itSmp->cvt & 1); bool deltaEncoded = !!(itSmp->cvt & 4); if (sampleIs16Bit) s->flags |= SAMPLE_16BIT; if (hasLoop) s->flags |= bidiLoop ? LOOP_BIDI : LOOP_FWD; s->length = itSmp->length; s->loopStart = itSmp->loopBegin; s->loopLength = itSmp->loopEnd - itSmp->loopBegin; s->volume = itSmp->vol; s->panning = 128; if (itSmp->defPan & 128) // use panning? { int32_t pan = (itSmp->defPan & 127) * 4; // 0..64 -> 0..256 if (pan > 255) pan = 255; s->panning = (uint8_t)pan; } memcpy(s->name, itSmp->sampleName, 22); s->name[22] = '\0'; setSampleC4Hz(s, itSmp->c5Speed); if (s->length <= 0 || itSmp->offsetInFile == 0) return true; // empty sample, skip data loading if (!allocateSmpData(s, s->length, sampleIs16Bit)) return false; // begin sample loading fseek(f, itSmp->offsetInFile, SEEK_SET); if (compressed) { if (sampleIs16Bit) loadCompressed16BitSample(f, s, deltaEncoded); else loadCompressed8BitSample(f, s, deltaEncoded); } else { fread(s->dataPtr, 1+(size_t)sampleIs16Bit, s->length, f); if (!signedSamples) { if (sampleIs16Bit) { int16_t *ptr16 = (int16_t *)s->dataPtr; for (int32_t i = 0; i < s->length; i++) ptr16[i] ^= 0x8000; } else { int8_t *ptr8 = (int8_t *)s->dataPtr; for (int32_t i = 0; i < s->length; i++) ptr8[i] ^= 0x80; } } } return true; }