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
This commit is contained in:
parent
51bc1119e8
commit
3e15a066f8
|
@ -42,6 +42,12 @@ struct InflateBlock {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct NotZlibException {
|
||||||
|
int what;
|
||||||
|
NotZlibException(int w):
|
||||||
|
what(w) {}
|
||||||
|
};
|
||||||
|
|
||||||
static double samplePitches[11]={
|
static double samplePitches[11]={
|
||||||
0.1666666666, 0.2, 0.25, 0.333333333, 0.5,
|
0.1666666666, 0.2, 0.25, 0.333333333, 0.5,
|
||||||
1,
|
1,
|
||||||
|
@ -1598,7 +1604,10 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
||||||
logD("couldn't seek to 1080");
|
logD("couldn't seek to 1080");
|
||||||
throw EndOfFileException(&reader,reader.tell());
|
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) {
|
if (memcmp(magic,"M.K.",4)==0 || memcmp(magic,"M!K!",4)==0 || memcmp(magic,"M&K!",4)==0) {
|
||||||
logD("detected a ProTracker module");
|
logD("detected a ProTracker module");
|
||||||
chCount=4;
|
chCount=4;
|
||||||
|
@ -1619,20 +1628,26 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
||||||
logD("detected a Fast/TakeTracker module");
|
logD("detected a Fast/TakeTracker module");
|
||||||
chCount=((magic[0]-'0')*10)+(magic[1]-'0');
|
chCount=((magic[0]-'0')*10)+(magic[1]-'0');
|
||||||
} else {
|
} else {
|
||||||
// TODO: Soundtracker MOD?
|
|
||||||
insCount=15;
|
insCount=15;
|
||||||
throw InvalidHeaderException();
|
logD("possibly a Soundtracker module");
|
||||||
|
chCount=4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// song name
|
// 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);
|
ds.name=reader.readString(20);
|
||||||
|
logI("%s",ds.name);
|
||||||
|
|
||||||
// samples
|
// samples
|
||||||
|
logD("reading samples... (%d)",insCount);
|
||||||
for (int i=0; i<insCount; i++) {
|
for (int i=0; i<insCount; i++) {
|
||||||
DivSample* sample=new DivSample;
|
DivSample* sample=new DivSample;
|
||||||
sample->depth=8;
|
sample->depth=8;
|
||||||
sample->name=reader.readString(22);
|
sample->name=reader.readString(22);
|
||||||
|
logD("%d: %s",i+1,sample->name);
|
||||||
int slen=((unsigned short)reader.readS_BE())*2;
|
int slen=((unsigned short)reader.readS_BE())*2;
|
||||||
sampLens[i]=slen;
|
sampLens[i]=slen;
|
||||||
if (slen==2) slen=0;
|
if (slen==2) slen=0;
|
||||||
|
@ -1660,7 +1675,21 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
||||||
|
|
||||||
// orders
|
// orders
|
||||||
ds.ordersLen=ordCount=reader.readC();
|
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;
|
int patMax=0;
|
||||||
for (int i=0; i<128; i++) {
|
for (int i=0; i<128; i++) {
|
||||||
unsigned char pat=reader.readC();
|
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?
|
if (insCount==15) {
|
||||||
reader.seek(1084,SEEK_SET);
|
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
|
// patterns
|
||||||
ds.patLen=64;
|
ds.patLen=64;
|
||||||
|
@ -1742,8 +1780,12 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
||||||
|
|
||||||
// samples
|
// samples
|
||||||
size_t pos=reader.tell();
|
size_t pos=reader.tell();
|
||||||
for (int i=0; i<31; i++) {
|
logD("reading sample data...");
|
||||||
reader.seek(pos,SEEK_SET);
|
for (int i=0; i<insCount; i++) {
|
||||||
|
if (!reader.seek(pos,SEEK_SET)) {
|
||||||
|
logD("%d: couldn't seek to %d",i,pos);
|
||||||
|
throw EndOfFileException(&reader,reader.tell());
|
||||||
|
}
|
||||||
reader.read(ds.sample[i]->data8,ds.sample[i]->samples);
|
reader.read(ds.sample[i]->data8,ds.sample[i]->samples);
|
||||||
pos+=sampLens[i];
|
pos+=sampLens[i];
|
||||||
}
|
}
|
||||||
|
@ -1944,18 +1986,10 @@ bool DivEngine::load(unsigned char* f, size_t slen) {
|
||||||
delete[] f;
|
delete[] f;
|
||||||
return false;
|
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";
|
// step 1: try loading as a zlib-compressed file
|
||||||
logD("loading as zlib...");
|
logD("trying zlib...");
|
||||||
// try zlib
|
try {
|
||||||
z_stream zl;
|
z_stream zl;
|
||||||
memset(&zl,0,sizeof(z_stream));
|
memset(&zl,0,sizeof(z_stream));
|
||||||
|
|
||||||
|
@ -1969,14 +2003,13 @@ bool DivEngine::load(unsigned char* f, size_t slen) {
|
||||||
nextErr=inflateInit(&zl);
|
nextErr=inflateInit(&zl);
|
||||||
if (nextErr!=Z_OK) {
|
if (nextErr!=Z_OK) {
|
||||||
if (zl.msg==NULL) {
|
if (zl.msg==NULL) {
|
||||||
logE("zlib error: unknown! %d",nextErr);
|
logD("zlib error: unknown! %d",nextErr);
|
||||||
} else {
|
} else {
|
||||||
logE("zlib error: %s",zl.msg);
|
logD("zlib error: %s",zl.msg);
|
||||||
}
|
}
|
||||||
inflateEnd(&zl);
|
inflateEnd(&zl);
|
||||||
delete[] f;
|
lastError="not a .dmf/.fur song";
|
||||||
lastError="not a .dmf song";
|
throw NotZlibException(0);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<InflateBlock*> blocks;
|
std::vector<InflateBlock*> blocks;
|
||||||
|
@ -1988,18 +2021,17 @@ bool DivEngine::load(unsigned char* f, size_t slen) {
|
||||||
nextErr=inflate(&zl,Z_SYNC_FLUSH);
|
nextErr=inflate(&zl,Z_SYNC_FLUSH);
|
||||||
if (nextErr!=Z_OK && nextErr!=Z_STREAM_END) {
|
if (nextErr!=Z_OK && nextErr!=Z_STREAM_END) {
|
||||||
if (zl.msg==NULL) {
|
if (zl.msg==NULL) {
|
||||||
logE("zlib error: unknown error! %d",nextErr);
|
logD("zlib error: unknown error! %d",nextErr);
|
||||||
lastError="unknown decompression error";
|
lastError="unknown decompression error";
|
||||||
} else {
|
} else {
|
||||||
logE("zlib inflate: %s",zl.msg);
|
logD("zlib inflate: %s",zl.msg);
|
||||||
lastError=fmt::sprintf("decompression error: %s",zl.msg);
|
lastError=fmt::sprintf("decompression error: %s",zl.msg);
|
||||||
}
|
}
|
||||||
for (InflateBlock* i: blocks) delete i;
|
for (InflateBlock* i: blocks) delete i;
|
||||||
blocks.clear();
|
blocks.clear();
|
||||||
delete ib;
|
delete ib;
|
||||||
inflateEnd(&zl);
|
inflateEnd(&zl);
|
||||||
delete[] f;
|
throw NotZlibException(0);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
ib->blockSize=ib->len-zl.avail_out;
|
ib->blockSize=ib->len-zl.avail_out;
|
||||||
blocks.push_back(ib);
|
blocks.push_back(ib);
|
||||||
|
@ -2010,16 +2042,15 @@ bool DivEngine::load(unsigned char* f, size_t slen) {
|
||||||
nextErr=inflateEnd(&zl);
|
nextErr=inflateEnd(&zl);
|
||||||
if (nextErr!=Z_OK) {
|
if (nextErr!=Z_OK) {
|
||||||
if (zl.msg==NULL) {
|
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";
|
lastError="unknown decompression finish error";
|
||||||
} else {
|
} else {
|
||||||
logE("zlib end: %s",zl.msg);
|
logD("zlib end: %s",zl.msg);
|
||||||
lastError=fmt::sprintf("decompression finish error: %s",zl.msg);
|
lastError=fmt::sprintf("decompression finish error: %s",zl.msg);
|
||||||
}
|
}
|
||||||
for (InflateBlock* i: blocks) delete i;
|
for (InflateBlock* i: blocks) delete i;
|
||||||
blocks.clear();
|
blocks.clear();
|
||||||
delete[] f;
|
throw NotZlibException(0);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t finalSize=0;
|
size_t finalSize=0;
|
||||||
|
@ -2028,12 +2059,11 @@ bool DivEngine::load(unsigned char* f, size_t slen) {
|
||||||
finalSize+=i->blockSize;
|
finalSize+=i->blockSize;
|
||||||
}
|
}
|
||||||
if (finalSize<1) {
|
if (finalSize<1) {
|
||||||
logE("compressed too small!");
|
logD("compressed too small!");
|
||||||
lastError="file too small";
|
lastError="file too small";
|
||||||
for (InflateBlock* i: blocks) delete i;
|
for (InflateBlock* i: blocks) delete i;
|
||||||
blocks.clear();
|
blocks.clear();
|
||||||
delete[] f;
|
throw NotZlibException(0);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
file=new unsigned char[finalSize];
|
file=new unsigned char[finalSize];
|
||||||
for (InflateBlock* i: blocks) {
|
for (InflateBlock* i: blocks) {
|
||||||
|
@ -2044,16 +2074,26 @@ bool DivEngine::load(unsigned char* f, size_t slen) {
|
||||||
blocks.clear();
|
blocks.clear();
|
||||||
len=finalSize;
|
len=finalSize;
|
||||||
delete[] f;
|
delete[] f;
|
||||||
} else {
|
} catch (NotZlibException& e) {
|
||||||
logD("loading as uncompressed");
|
logD("not zlib. loading as raw...");
|
||||||
file=(unsigned char*)f;
|
file=f;
|
||||||
len=slen;
|
len=slen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// step 2: try loading as .fur or .dmf
|
||||||
if (memcmp(file,DIV_DMF_MAGIC,16)==0) {
|
if (memcmp(file,DIV_DMF_MAGIC,16)==0) {
|
||||||
return loadDMF(file,len);
|
return loadDMF(file,len);
|
||||||
} else if (memcmp(file,DIV_FUR_MAGIC,16)==0) {
|
} else if (memcmp(file,DIV_FUR_MAGIC,16)==0) {
|
||||||
return loadFur(file,len);
|
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!");
|
logE("not a valid module!");
|
||||||
lastError="not a compatible song";
|
lastError="not a compatible song";
|
||||||
delete[] file;
|
delete[] file;
|
||||||
|
|
|
@ -490,7 +490,8 @@ enum PasteMode {
|
||||||
GUI_PASTE_MODE_OVERFLOW
|
GUI_PASTE_MODE_OVERFLOW
|
||||||
};
|
};
|
||||||
|
|
||||||
#define FURKMOD_CTRL (1<<31)
|
// 1<<31
|
||||||
|
#define FURKMOD_CTRL (-2147483648)
|
||||||
#define FURKMOD_SHIFT (1<<29)
|
#define FURKMOD_SHIFT (1<<29)
|
||||||
#define FURKMOD_META (1<<28)
|
#define FURKMOD_META (1<<28)
|
||||||
#define FURKMOD_ALT (1<<27)
|
#define FURKMOD_ALT (1<<27)
|
||||||
|
|
Loading…
Reference in a new issue