diff --git a/src/engine/fileOps/tfm.cpp b/src/engine/fileOps/tfm.cpp index 1fc250bce..c2bb8878f 100644 --- a/src/engine/fileOps/tfm.cpp +++ b/src/engine/fileOps/tfm.cpp @@ -161,18 +161,40 @@ String TFMparseDate(short date) { return fmt::sprintf("%02d.%02d.%02d",date>>11,(date>>7)&0xF,date&0x7F); } -struct TFMparsePatternInfo { +struct TFMSpeed { + unsigned char speedEven; + unsigned char speedOdd; + unsigned char interleaveFactor; + + bool operator==(const TFMSpeed &s) const { + return speedEven==s.speedEven && speedOdd==s.speedOdd && interleaveFactor==s.interleaveFactor; + } +}; + +// to make it work with map +template<> +struct std::hash +{ + size_t operator()(const TFMSpeed& s) const noexcept { + return s.speedEven<<16|s.speedOdd<<8|s.interleaveFactor; + } +}; + +struct TFMParsePatternInfo { TFMRLEReader* reader; unsigned char maxPat; unsigned char* patLens; unsigned char* orderList; + unsigned char speedEven; + unsigned char speedOdd; + unsigned char interleaveFactor; bool* patExists; DivSong* ds; int* insNumMaps; bool v2; }; -void TFMparsePattern(struct TFMparsePatternInfo info) { +void TFMParsePattern(struct TFMParsePatternInfo info) { // PATTERN DATA FORMAT (not described properly in the documentation) // for each channel in a pattern: // - note data (256 bytes) @@ -187,6 +209,28 @@ void TFMparsePattern(struct TFMparsePatternInfo info) { unsigned short lastSlide=0; unsigned short lastVibrato=0; + struct TFMSpeed speed; + DivGroovePattern groove; + speed.speedEven=info.speedEven; + speed.speedOdd=info.speedOdd; + speed.interleaveFactor=info.interleaveFactor; + int speedGrooveIndex=1; + + std::unordered_map speeds({{speed, 0}}); + + // initialize the global groove pattern first + if (speed.interleaveFactor>8) { + logW("speed interleave factor is bigger than 8, speed information may be inaccurate"); + speed.interleaveFactor=8; + } + for (int i=0; igrooves.push_back(groove); + for (int i=0; i<256; i++) { if (i>info.maxPat) break; else if (!info.patExists[i]) { @@ -349,9 +393,50 @@ void TFMparsePattern(struct TFMparsePatternInfo info) { break; } break; - default: - pat->data[k][4]=effectNum[k]; - pat->data[k][5]=effectVal[k]; + case 15: + // speed + + if (effectVal[k]==0) { + // if speed is set to zero (reset to global values) + speed.speedEven=info.speedEven; + speed.speedOdd=info.speedOdd; + speed.interleaveFactor=info.interleaveFactor; + } else if (effectVal[k]>>4==0) { + // if the top nibble is set to zero (set interleave factor) + speed.interleaveFactor=effectVal[k]&0xF; + } else if ((effectVal[k]>>4)==(effectVal[k]&0xF)) { + // if both speeds are equal + pat->data[k][4]=0x0F; + unsigned char speedSet=effectVal[k]>>4; + pat->data[k][5]=speedSet; + break; + } else { + speed.speedEven=effectVal[k]>>4; + speed.speedOdd=effectVal[k]&0xF; + } + + auto speedIndex = speeds.find(speed); + if (speedIndex != speeds.end()) { + pat->data[k][4]=0x09; + pat->data[k][5]=speedIndex->second; + break; + } + if (speed.interleaveFactor>8) { + logW("speed interleave factor is bigger than 8, speed information may be inaccurate"); + speed.interleaveFactor=8; + } + for (int i=0; igrooves.push_back(groove); + speeds[speed]=speedGrooveIndex; + + pat->data[k][4]=0x09; + pat->data[k][5]=speedGrooveIndex; + speedGrooveIndex++; break; } } @@ -410,15 +495,6 @@ void TFMparsePattern(struct TFMparsePatternInfo info) { case 0xA: chVolumeSlide[j]=true; break; - case 0xF: - // correct speed - - // if both speeds are equal - if ((pat->data[k][5]>>4)==(pat->data[k][5]&0xF)) { - unsigned char speed=pat->data[k][5]>>4; - pat->data[k][5]=speed; - } - break; default: break; } @@ -578,16 +654,19 @@ bool DivEngine::loadTFMv1(unsigned char* file, size_t len) { ds.subsong[0]->patLen=maxPatLen; - struct TFMparsePatternInfo info; + struct TFMParsePatternInfo info; info.ds=&ds; info.insNumMaps=insNumMaps; info.maxPat=maxPat; info.patExists=patExists; info.orderList=orderList; + info.speedEven=speed>>4; + info.speedOdd=speed&0xF; + info.interleaveFactor=interleaveFactor; info.patLens=patLens; info.reader=&reader; info.v2=false; - TFMparsePattern(info); + TFMParsePattern(info); if (active) quitDispatch(); BUSY_BEGIN_SOFT; @@ -761,7 +840,7 @@ bool DivEngine::loadTFMv2(unsigned char* file, size_t len) { unsigned char patLens[256]; int maxPatLen=0; reader.read(patLens, 256); - for (int i=0;i<256;i++) { + for (int i=0; i<256; i++) { if (patLens[i]==0) { maxPatLen=256; break; @@ -772,16 +851,19 @@ bool DivEngine::loadTFMv2(unsigned char* file, size_t len) { ds.subsong[0]->patLen=maxPatLen; - struct TFMparsePatternInfo info; + struct TFMParsePatternInfo info; info.ds=&ds; info.insNumMaps=insNumMaps; info.maxPat=maxPat; info.patExists=patExists; info.orderList=orderList; + info.speedEven=speedEven; + info.speedOdd=speedOdd; + info.interleaveFactor=interleaveFactor; info.patLens=patLens; info.reader=&reader; info.v2=true; - TFMparsePattern(info); + TFMParsePattern(info); if (active) quitDispatch(); BUSY_BEGIN_SOFT;