From 3e15a066f8c661a676d854823c7dee5e247efcfa Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 00:45:55 -0500 Subject: [PATCH] Soundtracker .mod loading support this required a re-organization of DivEngine::load() but it works eventually we'll be able to load other file formats this way --- src/engine/fileOps.cpp | 122 +++++++++++++++++++++++++++-------------- src/gui/gui.h | 3 +- 2 files changed, 83 insertions(+), 42 deletions(-) diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index e4e7f0541..5eaed546f 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -42,6 +42,12 @@ struct InflateBlock { } }; +struct NotZlibException { + int what; + NotZlibException(int w): + what(w) {} +}; + static double samplePitches[11]={ 0.1666666666, 0.2, 0.25, 0.333333333, 0.5, 1, @@ -1598,7 +1604,10 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { logD("couldn't seek to 1080"); throw EndOfFileException(&reader,reader.tell()); } - reader.read(magic,4); + if (reader.read(magic,4)!=4) { + logD("the magic isn't complete"); + throw EndOfFileException(&reader,reader.tell()); + } if (memcmp(magic,"M.K.",4)==0 || memcmp(magic,"M!K!",4)==0 || memcmp(magic,"M&K!",4)==0) { logD("detected a ProTracker module"); chCount=4; @@ -1619,20 +1628,26 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { logD("detected a Fast/TakeTracker module"); chCount=((magic[0]-'0')*10)+(magic[1]-'0'); } else { - // TODO: Soundtracker MOD? insCount=15; - throw InvalidHeaderException(); + logD("possibly a Soundtracker module"); + chCount=4; } // song name - reader.seek(0,SEEK_SET); + if (!reader.seek(0,SEEK_SET)) { + logD("couldn't seek to 0"); + throw EndOfFileException(&reader,reader.tell()); + } ds.name=reader.readString(20); + logI("%s",ds.name); // samples + logD("reading samples... (%d)",insCount); for (int i=0; idepth=8; sample->name=reader.readString(22); + logD("%d: %s",i+1,sample->name); int slen=((unsigned short)reader.readS_BE())*2; sampLens[i]=slen; if (slen==2) slen=0; @@ -1649,7 +1664,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { loopStart=0; loopLen=0; } - if(loopLen>=2) { + if (loopLen>=2) { if (loopEndloopStart=loopStart; } @@ -1660,7 +1675,21 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { // orders ds.ordersLen=ordCount=reader.readC(); - reader.readC(); // restart position, unused + if (ds.ordersLen<1 || ds.ordersLen>127) { + logD("invalid order count!"); + throw EndOfFileException(&reader,reader.tell()); + } + unsigned char restartPos=reader.readC(); // restart position, unused + logD("restart position byte: %.2x",restartPos); + if (insCount==15) { + if (restartPos>0x60 && restartPos<0x80) { + logD("detected a Soundtracker module"); + } else { + logD("no Soundtracker signature found"); + throw EndOfFileException(&reader,reader.tell()); + } + } + int patMax=0; for (int i=0; i<128; i++) { unsigned char pat=reader.readC(); @@ -1670,8 +1699,17 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { } } - // TODO: maybe change if this is a Soundtracker module? - reader.seek(1084,SEEK_SET); + if (insCount==15) { + if (!reader.seek(600,SEEK_SET)) { + logD("couldn't seek to 600"); + throw EndOfFileException(&reader,reader.tell()); + } + } else { + if (!reader.seek(1084,SEEK_SET)) { + logD("couldn't seek to 1084"); + throw EndOfFileException(&reader,reader.tell()); + } + } // patterns ds.patLen=64; @@ -1742,8 +1780,12 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { // samples size_t pos=reader.tell(); - for (int i=0; i<31; i++) { - reader.seek(pos,SEEK_SET); + logD("reading sample data..."); + for (int i=0; idata8,ds.sample[i]->samples); pos+=sampLens[i]; } @@ -1944,18 +1986,10 @@ bool DivEngine::load(unsigned char* f, size_t slen) { delete[] f; return false; } - if (memcmp(f,DIV_DMF_MAGIC,16)!=0 && memcmp(f,DIV_FUR_MAGIC,16)!=0) { - // try loading as a .mod first before trying to decompress - // TODO: move to a different location? - logD("loading as .mod..."); - if (loadMod(f,slen)) { - delete[] f; - return true; - } - - lastError="not a .mod song"; - logD("loading as zlib..."); - // try zlib + + // step 1: try loading as a zlib-compressed file + logD("trying zlib..."); + try { z_stream zl; memset(&zl,0,sizeof(z_stream)); @@ -1969,14 +2003,13 @@ bool DivEngine::load(unsigned char* f, size_t slen) { nextErr=inflateInit(&zl); if (nextErr!=Z_OK) { if (zl.msg==NULL) { - logE("zlib error: unknown! %d",nextErr); + logD("zlib error: unknown! %d",nextErr); } else { - logE("zlib error: %s",zl.msg); + logD("zlib error: %s",zl.msg); } inflateEnd(&zl); - delete[] f; - lastError="not a .dmf song"; - return false; + lastError="not a .dmf/.fur song"; + throw NotZlibException(0); } std::vector blocks; @@ -1988,18 +2021,17 @@ bool DivEngine::load(unsigned char* f, size_t slen) { nextErr=inflate(&zl,Z_SYNC_FLUSH); if (nextErr!=Z_OK && nextErr!=Z_STREAM_END) { if (zl.msg==NULL) { - logE("zlib error: unknown error! %d",nextErr); + logD("zlib error: unknown error! %d",nextErr); lastError="unknown decompression error"; } else { - logE("zlib inflate: %s",zl.msg); + logD("zlib inflate: %s",zl.msg); lastError=fmt::sprintf("decompression error: %s",zl.msg); } for (InflateBlock* i: blocks) delete i; blocks.clear(); delete ib; inflateEnd(&zl); - delete[] f; - return false; + throw NotZlibException(0); } ib->blockSize=ib->len-zl.avail_out; blocks.push_back(ib); @@ -2010,16 +2042,15 @@ bool DivEngine::load(unsigned char* f, size_t slen) { nextErr=inflateEnd(&zl); if (nextErr!=Z_OK) { if (zl.msg==NULL) { - logE("zlib end error: unknown error! %d",nextErr); + logD("zlib end error: unknown error! %d",nextErr); lastError="unknown decompression finish error"; } else { - logE("zlib end: %s",zl.msg); + logD("zlib end: %s",zl.msg); lastError=fmt::sprintf("decompression finish error: %s",zl.msg); } for (InflateBlock* i: blocks) delete i; blocks.clear(); - delete[] f; - return false; + throw NotZlibException(0); } size_t finalSize=0; @@ -2028,12 +2059,11 @@ bool DivEngine::load(unsigned char* f, size_t slen) { finalSize+=i->blockSize; } if (finalSize<1) { - logE("compressed too small!"); + logD("compressed too small!"); lastError="file too small"; for (InflateBlock* i: blocks) delete i; blocks.clear(); - delete[] f; - return false; + throw NotZlibException(0); } file=new unsigned char[finalSize]; for (InflateBlock* i: blocks) { @@ -2044,16 +2074,26 @@ bool DivEngine::load(unsigned char* f, size_t slen) { blocks.clear(); len=finalSize; delete[] f; - } else { - logD("loading as uncompressed"); - file=(unsigned char*)f; + } catch (NotZlibException& e) { + logD("not zlib. loading as raw..."); + file=f; len=slen; } + + // step 2: try loading as .fur or .dmf if (memcmp(file,DIV_DMF_MAGIC,16)==0) { return loadDMF(file,len); } else if (memcmp(file,DIV_FUR_MAGIC,16)==0) { return loadFur(file,len); } + + // step 3: try loading as .mod + if (loadMod(f,slen)) { + delete[] f; + return true; + } + + // step 4: not a valid file logE("not a valid module!"); lastError="not a compatible song"; delete[] file; diff --git a/src/gui/gui.h b/src/gui/gui.h index 136b666d7..934f09540 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -490,7 +490,8 @@ enum PasteMode { GUI_PASTE_MODE_OVERFLOW }; -#define FURKMOD_CTRL (1<<31) +// 1<<31 +#define FURKMOD_CTRL (-2147483648) #define FURKMOD_SHIFT (1<<29) #define FURKMOD_META (1<<28) #define FURKMOD_ALT (1<<27)