audit .ftm import code

to-do: dkc_ending.ftm and fcm9.ftm no longer load... check out why
This commit is contained in:
tildearrow 2024-04-14 12:45:17 -05:00
parent 11d6cbc748
commit 6efef65b48

View file

@ -23,7 +23,6 @@
// portions apparently taken from FamiTracker source, which is under GPLv2+ // portions apparently taken from FamiTracker source, which is under GPLv2+
// TODO: // TODO:
// - audit for CVEs
// - format code? // - format code?
#include "fileOpsCommon.h" #include "fileOpsCommon.h"
@ -258,6 +257,7 @@ const int eff_conversion_050[][2] = {
}; };
constexpr int ftEffectMapSize = sizeof(ftEffectMap) / sizeof(int); constexpr int ftEffectMapSize = sizeof(ftEffectMap) / sizeof(int);
constexpr int eftEffectMapSize = sizeof(eftEffectMap) / sizeof(int);
int convertMacros2A03[5] = {(int)DIV_MACRO_VOL, (int)DIV_MACRO_ARP, (int)DIV_MACRO_PITCH, -1, (int)DIV_MACRO_DUTY}; int convertMacros2A03[5] = {(int)DIV_MACRO_VOL, (int)DIV_MACRO_ARP, (int)DIV_MACRO_PITCH, -1, (int)DIV_MACRO_DUTY};
int convertMacrosVRC6[5] = {(int)DIV_MACRO_VOL, (int)DIV_MACRO_ARP, (int)DIV_MACRO_PITCH, -1, (int)DIV_MACRO_DUTY}; int convertMacrosVRC6[5] = {(int)DIV_MACRO_VOL, (int)DIV_MACRO_ARP, (int)DIV_MACRO_PITCH, -1, (int)DIV_MACRO_DUTY};
@ -434,7 +434,8 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
bool hasSequence[256][8]; bool hasSequence[256][8];
unsigned char sequenceIndex[256][8]; unsigned char sequenceIndex[256][8];
unsigned char macro_types[256][8]; unsigned char macro_types[256][8];
std::vector<std::vector<DivInstrumentMacro>> macros; std::vector<DivInstrumentMacro> macros[256];
std::vector<String> encounteredBlocks;
unsigned char map_channels[DIV_MAX_CHANS]; unsigned char map_channels[DIV_MAX_CHANS];
unsigned int hilightA = 4; unsigned int hilightA = 4;
unsigned int hilightB = 16; unsigned int hilightB = 16;
@ -457,13 +458,9 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
memset(map_channels, 0xfe, DIV_MAX_CHANS * sizeof(unsigned char)); memset(map_channels, 0xfe, DIV_MAX_CHANS * sizeof(unsigned char));
for (int i = 0; i < 256; i++) { for (int i = 0; i < 256; i++) {
std::vector<DivInstrumentMacro> mac;
for (int j = 0; j < 8; j++) { for (int j = 0; j < 8; j++) {
mac.push_back(DivInstrumentMacro(DIV_MACRO_VOL)); macros[i].push_back(DivInstrumentMacro(DIV_MACRO_VOL));
} }
macros.push_back(mac);
} }
if (!reader.seek((dnft && dnft_sig) ? 21 : 18, SEEK_SET)) { if (!reader.seek((dnft && dnft_sig) ? 21 : 18, SEEK_SET)) {
@ -501,13 +498,30 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
} }
// not the end // not the end
reader.seek(-3, SEEK_CUR); if (!reader.seek(-3, SEEK_CUR)) {
logE("couldn't seek back by 3!");
lastError = "couldn't seek back by 3";
delete[] file;
return false;
}
blockName = reader.readString(16); blockName = reader.readString(16);
unsigned int blockVersion = (unsigned int)reader.readI(); unsigned int blockVersion = (unsigned int)reader.readI();
unsigned int blockSize = (unsigned int)reader.readI(); unsigned int blockSize = (unsigned int)reader.readI();
size_t blockStart = reader.tell(); size_t blockStart = reader.tell();
logD("reading block %s (version %d, %d bytes, position %x)", blockName, blockVersion, blockSize, reader.tell()); logD("reading block %s (version %d, %d bytes, position %x)", blockName, blockVersion, blockSize, reader.tell());
for (String& i: encounteredBlocks) {
if (blockName==i) {
logE("duplicate block %s!",blockName);
lastError = "duplicate block "+blockName;
ds.unload();
delete[] file;
return false;
}
}
encounteredBlocks.push_back(blockName);
if (blockName == "PARAMS") { if (blockName == "PARAMS") {
// versions 7-9 don't change anything? // versions 7-9 don't change anything?
CHECK_BLOCK_VERSION(9); CHECK_BLOCK_VERSION(9);
@ -529,6 +543,13 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
tchans = reader.readI(); tchans = reader.readI();
if (tchans<0 || tchans>=DIV_MAX_CHANS) {
logE("invalid channel count! %d",tchans);
lastError = "invalid channel count";
delete[] file;
return false;
}
if (tchans == 5) { if (tchans == 5) {
expansions = 0; // This is strange. Sometimes expansion chip is set to 0xFF in files expansions = 0; // This is strange. Sometimes expansion chip is set to 0xFF in files
} }
@ -538,12 +559,15 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
if (blockVersion >= 7) { if (blockVersion >= 7) {
// advanced Hz control // advanced Hz control
int controlType = reader.readI(); int controlType = reader.readI();
switch (controlType) { int readHz=reader.readI();
if (readHz<=0) {
customHz=60.0;
} else switch (controlType) {
case 1: case 1:
customHz = 1000000.0 / (double)reader.readI(); customHz = 1000000.0 / (double)readHz;
break; break;
default: default:
reader.readI(); logW("unsupported tick rate control type %d",controlType);
break; break;
} }
} else { } else {
@ -553,6 +577,9 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
customHz = reader.readI(); customHz = reader.readI();
} }
if (customHz<1.0) customHz=1.0;
if (customHz>1000.0) customHz=1000.0;
unsigned int newVibrato = 0; unsigned int newVibrato = 0;
bool sweepReset = false; bool sweepReset = false;
unsigned int speedSplitPoint = 0; unsigned int speedSplitPoint = 0;
@ -576,6 +603,12 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
if ((expansions & 16) && blockVersion >= 5) { // N163 channels if ((expansions & 16) && blockVersion >= 5) { // N163 channels
n163Chans = reader.readI(); n163Chans = reader.readI();
if (n163Chans<1 || n163Chans>=9) {
logE("invalid Namco 163 channel count! %d",n163Chans);
lastError = "invalid Namco 163 channel count";
delete[] file;
return false;
}
} }
if (blockVersion >= 6) { if (blockVersion >= 6) {
speedSplitPoint = reader.readI(); speedSplitPoint = reader.readI();
@ -779,6 +812,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
} }
} }
if (calcChans != tchans) { if (calcChans != tchans) {
// TODO: would ignore trigger CVE? too bad if so!
if (!eft || (eft && (expansions & 8) == 0)) // ignore since I have no idea how to tell apart E-FT versions which do or do not have PCM chan. Yes, this may lead to all the righer channels to be shifted but at least you still get note data! if (!eft || (eft && (expansions & 8) == 0)) // ignore since I have no idea how to tell apart E-FT versions which do or do not have PCM chan. Yes, this may lead to all the righer channels to be shifted but at least you still get note data!
{ {
logE("channel counts do not match! %d != %d", tchans, calcChans); logE("channel counts do not match! %d != %d", tchans, calcChans);
@ -788,17 +822,20 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
} }
} }
if (tchans > DIV_MAX_CHANS) { if (tchans > DIV_MAX_CHANS) {
tchans = DIV_MAX_CHANS; logE("too many channels!");
logW("too many channels!"); lastError = "too many channels";
delete[] file;
return false;
} }
if (blockVersion == 9 && blockSize - (reader.tell() - blockStart) == 2) // weird if (blockVersion == 9 && blockSize - (reader.tell() - blockStart) == 2) // weird
{ {
reader.seek(2, SEEK_CUR); if (!reader.seek(2, SEEK_CUR)) {
logE("could not weird-seek by 2!");
lastError = "could not weird-seek by 2";
delete[] file;
return false;
} }
if (eft) {
// reader.seek(8,SEEK_CUR);
} }
} else if (blockName == "INFO") { } else if (blockName == "INFO") {
CHECK_BLOCK_VERSION(1); CHECK_BLOCK_VERSION(1);
ds.name = reader.readString(32); ds.name = reader.readString(32);
@ -831,6 +868,13 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
for (int j = 0; j <= totalSongs; j++) { for (int j = 0; j <= totalSongs; j++) {
unsigned char effectCols = reader.readC(); unsigned char effectCols = reader.readC();
if (effectCols>7) {
logE("too many effect columns!");
lastError = "too many effect columns";
delete[] file;
return false;
}
if (map_channels[i] == 0xfe) { if (map_channels[i] == 0xfe) {
ds.subsong[j]->pat[i].effectCols = 1; ds.subsong[j]->pat[i].effectCols = 1;
logV("- song %d has %d effect columns", j, effectCols); logV("- song %d has %d effect columns", j, effectCols);
@ -850,8 +894,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
} else if (blockName == "INSTRUMENTS") { } else if (blockName == "INSTRUMENTS") {
CHECK_BLOCK_VERSION(9); CHECK_BLOCK_VERSION(9);
// reader.seek(blockSize,SEEK_CUR);
ds.insLen = reader.readI(); ds.insLen = reader.readI();
if (ds.insLen < 0 || ds.insLen > 256) { if (ds.insLen < 0 || ds.insLen > 256) {
logE("too many instruments/out of range!"); logE("too many instruments/out of range!");
@ -870,10 +912,10 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
for (int i = 0; i < ds.insLen; i++) { for (int i = 0; i < ds.insLen; i++) {
unsigned int insIndex = reader.readI(); unsigned int insIndex = reader.readI();
if (insIndex >= ds.ins.size()) { if (insIndex >= ds.ins.size()) {
// logE("instrument index %d is out of range!",insIndex); logE("instrument index %d is out of range!",insIndex);
// lastError="instrument index out of range"; lastError="instrument index out of range";
// delete[] file; delete[] file;
// return false; return false;
} }
DivInstrument* ins = ds.ins[insIndex]; DivInstrument* ins = ds.ins[insIndex];
@ -937,6 +979,12 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
if (blockVersion >= 7) { if (blockVersion >= 7) {
note = reader.readC(); note = reader.readC();
} }
if (note<0 || note>=120) {
logE("DPCM note %d out of range!",note);
lastError = "DPCM note out of range";
delete[] file;
return false;
}
ins->amiga.noteMap[note].map = (short)((unsigned char)reader.readC()) - 1; ins->amiga.noteMap[note].map = (short)((unsigned char)reader.readC()) - 1;
unsigned char freq = reader.readC(); unsigned char freq = reader.readC();
ins->amiga.noteMap[note].dpcmFreq = (freq & 15); // 0-15 = 0-15 unlooped, 128-143 = 0-15 looped ins->amiga.noteMap[note].dpcmFreq = (freq & 15); // 0-15 = 0-15 unlooped, 128-143 = 0-15 looped
@ -981,8 +1029,9 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
} }
case DIV_INS_OPLL: { case DIV_INS_OPLL: {
ins->fm.opllPreset = (unsigned int)reader.readI(); ins->fm.opllPreset = (unsigned int)reader.readI();
ins->fm.opllPreset&=15;
unsigned char custom_patch[8] = {0}; unsigned char custom_patch[8];
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
custom_patch[i] = reader.readC(); custom_patch[i] = reader.readC();
@ -1020,24 +1069,35 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
for (int j = 0; j < 64; j++) { for (int j = 0; j < 64; j++) {
wave->data[j] = reader.readC(); wave->data[j] = reader.readC();
} }
ins->std.waveMacro.len = 1;
ins->std.waveMacro.val[0] = ds.wave.size();
for (int j = 0; j < 32; j++) { for (int j = 0; j < 32; j++) {
ins->fds.modTable[j] = reader.readC() - 3; ins->fds.modTable[j] = reader.readC() - 3;
} }
ins->fds.modSpeed = reader.readI(); ins->fds.modSpeed = reader.readI();
ins->fds.modDepth = reader.readI(); ins->fds.modDepth = reader.readI();
reader.readI(); // this is delay. currently ignored. TODO. reader.readI(); // this is delay. currently ignored. TODO.
if (ds.wave.size()>=256) {
logW("too many waves! ignoring...");
delete wave;
} else {
ins->std.waveMacro.len = 1;
ins->std.waveMacro.val[0] = ds.wave.size();
ds.wave.push_back(wave); ds.wave.push_back(wave);
ds.waveLen++; ds.waveLen++;
}
unsigned int a = reader.readI(); unsigned int a = reader.readI();
unsigned int b = reader.readI(); unsigned int b = reader.readI();
reader.seek(-8, SEEK_CUR); if (!reader.seek(-8, SEEK_CUR)) {
logE("couldn't seek back by 8 reading FDS ins");
lastError = "couldn't seek back by 8 reading FDS ins";
delete[] file;
return false;
}
if (a < 256 && (b & 0xFF) != 0x00) { if (a < 256 && (b & 0xFF) != 0x00) {
// don't look at me like this. I don't know why this should be like this either! // don't look at me like this. I don't know why this should be like this either!
logW("a is less than 256 and b is not zero!");
} else { } else {
ins->std.volMacro.len = reader.readC(); ins->std.volMacro.len = reader.readC();
ins->std.volMacro.loop = reader.readI(); ins->std.volMacro.loop = reader.readI();
@ -1112,12 +1172,19 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
if (blockVersion >= 8) { if (blockVersion >= 8) {
unsigned int autopos = reader.readI(); unsigned int autopos = reader.readI();
(void)autopos; logV("autopos: %d",autopos);
} }
unsigned int wave_count = reader.readI(); unsigned int wave_count = reader.readI();
size_t waveOff = ds.wave.size(); size_t waveOff = ds.wave.size();
if (wave_size>256) {
logE("wave size %d out of range",wave_size);
lastError = "wave size out of range";
delete[] file;
return false;
}
for (unsigned int ii = 0; ii < wave_count; ii++) { for (unsigned int ii = 0; ii < wave_count; ii++) {
DivWavetable* wave = new DivWavetable(); DivWavetable* wave = new DivWavetable();
wave->len = wave_size; wave->len = wave_size;
@ -1131,6 +1198,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
if (ds.wave.size()<256) { if (ds.wave.size()<256) {
ds.wave.push_back(wave); ds.wave.push_back(wave);
} else { } else {
logW("too many waves...");
delete wave; delete wave;
} }
} }
@ -1296,16 +1364,30 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
} }
if (instVersion == 2) { if (instVersion == 2) {
reader.seek(seek_amount, SEEK_CUR); // what the fuck // I know right?
if (!reader.seek(seek_amount, SEEK_CUR)) {
logE("EFT seek fail");
lastError = "EFT seek fail";
delete[] file;
return false;
}
} }
// this commented out block left here intentionally.
// total mess of code style... for with no space, UNDEFINED CHAR, escaping the unescapable, silly var names...
// ...whatever.
/*for(int tti = 0; tti < 20; tti++) /*for(int tti = 0; tti < 20; tti++)
{ {
char aaaa = reader.readC(); char aaaa = reader.readC();
logV("\'%c\'", aaaa); logV("\'%c\'", aaaa);
}*/ }*/
} else { } else {
reader.seek(-4, SEEK_CUR); if (!reader.seek(-4, SEEK_CUR)) {
logE("EFT -4 seek fail");
lastError = "EFT -4 seek fail";
delete[] file;
return false;
}
} }
break; break;
@ -1325,7 +1407,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
} }
ds.insLen = 128; ds.insLen = 128;
} else if (blockName == "SEQUENCES") { } else if (blockName == "SEQUENCES") {
CHECK_BLOCK_VERSION(6); CHECK_BLOCK_VERSION(6);
@ -1342,6 +1423,14 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
unsigned int index = reader.readI(); unsigned int index = reader.readI();
unsigned int type = reader.readI(); unsigned int type = reader.readI();
unsigned char size = reader.readC(); unsigned char size = reader.readC();
if (index>=256 || type>=8) {
logE("%d: index/type out of range",i);
lastError = "sequence index/type out of range";
delete[] file;
return false;
}
macros[index][type].len = size; macros[index][type].len = size;
for (int j = 0; j < size; j++) { for (int j = 0; j < size; j++) {
@ -1361,12 +1450,34 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
unsigned char* Indices = new unsigned char[128 * 5]; unsigned char* Indices = new unsigned char[128 * 5];
unsigned char* Types = new unsigned char[128 * 5]; unsigned char* Types = new unsigned char[128 * 5];
memset(Indices,0,128*5);
memset(Types,0,128*5);
for (unsigned int i = 0; i < seq_count; i++) { for (unsigned int i = 0; i < seq_count; i++) {
unsigned int index = reader.readI(); unsigned int index = reader.readI();
if (index>=128*5) {
logE("%d: index out of range",i);
lastError = "sequence index out of range";
delete[] file;
return false;
}
Indices[i] = index; Indices[i] = index;
unsigned int type = reader.readI(); unsigned int type = reader.readI();
if (type>=128*5) {
logE("%d: type out of range",i);
lastError = "sequence type out of range";
delete[] file;
return false;
}
Types[i] = type; Types[i] = type;
if (index>=256 || type>=8) {
logE("%d: index/type out of range",i);
lastError = "sequence index/type out of range";
delete[] file;
return false;
}
unsigned char size = reader.readC(); unsigned char size = reader.readC();
unsigned int setting = 0; unsigned int setting = 0;
@ -1393,7 +1504,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
DivInstrument* ins = ds.ins[k]; DivInstrument* ins = ds.ins[k];
if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_NES && hasSequence[k][Types[i]]) { if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_NES && hasSequence[k][Types[i]]) {
copyMacro(ins, &macros[index][type], Types[i], setting); copyMacro(ins, &macros[index][type], Types[i], setting);
// memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)Types[i], true), &macros[sequenceIndex[index][type]][type], sizeof(DivInstrumentMacro));
} }
} }
} }
@ -1412,7 +1522,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
macro_types[k][j] = setting; macro_types[k][j] = setting;
copyMacro(ins, &macros[sequenceIndex[k][j]][j], j, setting); copyMacro(ins, &macros[sequenceIndex[k][j]][j], j, setting);
// memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)j, true), &macros[sequenceIndex[k][j]][j], sizeof(DivInstrumentMacro));
} }
} }
} }
@ -1425,9 +1534,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
unsigned int release = reader.readI(); unsigned int release = reader.readI();
unsigned int setting = reader.readI(); unsigned int setting = reader.readI();
// macros[index][type].rel = release;
// macro_types[index][type] = setting;
for (int k = 0; k < (int)ds.ins.size(); k++) { for (int k = 0; k < (int)ds.ins.size(); k++) {
DivInstrument* ins = ds.ins[k]; DivInstrument* ins = ds.ins[k];
if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_NES && hasSequence[k][Types[i]]) { if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_NES && hasSequence[k][Types[i]]) {
@ -1435,7 +1541,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
macro_types[k][Types[i]] = setting; macro_types[k][Types[i]] = setting;
copyMacro(ins, &macros[sequenceIndex[k][Types[i]]][Types[i]], Types[i], setting); copyMacro(ins, &macros[sequenceIndex[k][Types[i]]][Types[i]], Types[i], setting);
// memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)Types[i], true), &macros[sequenceIndex[k][Types[i]]][Types[i]], sizeof(DivInstrumentMacro));
} }
} }
} }
@ -1446,12 +1551,11 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
} }
} else if (blockName == "GROOVES") { } else if (blockName == "GROOVES") {
CHECK_BLOCK_VERSION(6); CHECK_BLOCK_VERSION(6);
// reader.seek(blockSize,SEEK_CUR);
unsigned char num_grooves = reader.readC(); unsigned char num_grooves = reader.readC();
int max_groove = 0; int max_groove = 0;
for (int i = 0; i < 0xff; i++) { for (int i = 0; i < 256; i++) {
ds.grooves.push_back(DivGroovePattern()); ds.grooves.push_back(DivGroovePattern());
} }
@ -1459,16 +1563,19 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
unsigned char index = reader.readC(); unsigned char index = reader.readC();
unsigned char size = reader.readC(); unsigned char size = reader.readC();
if (index > max_groove) if (index > max_groove) {
max_groove = index + 1; max_groove = index + 1;
}
DivGroovePattern gp; DivGroovePattern gp;
gp.len = size; gp.len = size;
for (int sz = 0; sz < size; sz++) { for (int sz = 0; sz < size; sz++) {
unsigned char value = reader.readC(); unsigned char value = reader.readC();
if (sz<16) {
gp.val[sz] = value; gp.val[sz] = value;
} }
}
ds.grooves[index] = gp; ds.grooves[index] = gp;
} }
@ -1488,14 +1595,21 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
if ((reader.tell() - blockStart) != blockSize) { if ((reader.tell() - blockStart) != blockSize) {
logE("block %s size does not match! block size %d curr pos %d", blockName, blockSize, reader.tell() - blockStart); logE("block %s size does not match! block size %d curr pos %d", blockName, blockSize, reader.tell() - blockStart);
} }
} else if (blockName == "FRAMES") { } else if (blockName == "FRAMES") {
CHECK_BLOCK_VERSION(3); CHECK_BLOCK_VERSION(3);
for (size_t i = 0; i < ds.subsong.size(); i++) { for (size_t i = 0; i < ds.subsong.size(); i++) {
DivSubSong* s = ds.subsong[i]; DivSubSong* s = ds.subsong[i];
s->ordersLen = reader.readI(); int framesLen=reader.readI();
if (framesLen<=1 || framesLen>=256) {
logE("frames out of range");
lastError = "frames out of range";
delete[] file;
return false;
}
s->ordersLen = framesLen;
if (blockVersion >= 3) { if (blockVersion >= 3) {
s->speeds.val[0] = reader.readI(); s->speeds.val[0] = reader.readI();
} }
@ -1510,18 +1624,31 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
s->virtualTempoN = tempo; s->virtualTempoN = tempo;
} }
s->patLen = reader.readI(); int patLen=reader.readI();
if (patLen<1 || patLen>=256) {
logE("pattern length out of range");
lastError = "pattern length out of range";
delete[] file;
return false;
}
s->patLen = patLen;
} }
int why = tchans; int why = tchans;
if (blockVersion == 1) { if (blockVersion == 1) {
why = reader.readI(); why = reader.readI();
if (why<0 || why>=DIV_MAX_CHANS) {
logE("why out of range!");
lastError = "why out of range";
delete[] file;
return false;
}
} }
logV("reading %d and %d orders", tchans, s->ordersLen); logV("reading %d and %d orders", tchans, s->ordersLen);
for (int j = 0; j < s->ordersLen; j++) { for (int j = 0; j < s->ordersLen; j++) {
for (int k = 0; k < why; k++) { for (int k = 0; k < why; k++) {
unsigned char o = reader.readC(); unsigned char o = reader.readC();
// logV("%.2x",o); if (map_channels[k]>=DIV_MAX_CHANS) continue;
s->orders.ord[map_channels[k]][j] = o; s->orders.ord[map_channels[k]][j] = o;
} }
} }
@ -1533,6 +1660,12 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
if (blockVersion == 1) { if (blockVersion == 1) {
int patLenOld = reader.readI(); int patLenOld = reader.readI();
if (patLenOld<1 || patLenOld>=256) {
logE("old pattern length out of range");
lastError = "old pattern length out of range";
delete[] file;
return false;
}
for (DivSubSong* i : ds.subsong) { for (DivSubSong* i : ds.subsong) {
i->patLen = patLenOld; i->patLen = patLenOld;
} }
@ -1553,6 +1686,37 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
logV("patNum: %d",patNum); logV("patNum: %d",patNum);
logV("rows: %d",numRows); logV("rows: %d",numRows);
if (subs<0 || subs>=(int)ds.subsong.size()) {
logE("subsong out of range!");
lastError = "subsong out of range";
delete[] file;
return false;
}
if (ch<0 || ch>=DIV_MAX_CHANS) {
logE("channel out of range!");
lastError = "channel out of range";
delete[] file;
return false;
}
if (map_channels[ch]>=DIV_MAX_CHANS) {
logE("mapped channel out of range!");
lastError = "mapped channel out of range";
delete[] file;
return false;
}
if (patNum<0 || patNum>=256) {
logE("pattern number out of range!");
lastError = "pattern number out of range";
delete[] file;
return false;
}
if (numRows<0) {
logE("row count is negative!");
lastError = "row count is negative";
delete[] file;
return false;
}
DivPattern* pat = ds.subsong[subs]->pat[map_channels[ch]].getPattern(patNum, true); DivPattern* pat = ds.subsong[subs]->pat[map_channels[ch]].getPattern(patNum, true);
for (int i = 0; i < numRows; i++) { for (int i = 0; i < numRows; i++) {
unsigned int row = 0; unsigned int row = 0;
@ -1562,6 +1726,13 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
row = reader.readI(); row = reader.readI();
} }
if (row>=256) {
logE("row index out of range");
lastError = "row index out of range";
delete[] file;
return false;
}
unsigned char nextNote = reader.readC(); unsigned char nextNote = reader.readC();
unsigned char nextOctave = reader.readC(); unsigned char nextOctave = reader.readC();
@ -1592,6 +1763,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
} }
unsigned char nextIns = reader.readC(); unsigned char nextIns = reader.readC();
// TODO: you sure about 0xff?
if (map_channels[ch] != 0xff) { if (map_channels[ch] != 0xff) {
if (nextIns < 0x40 && nextNote != 0x0d && nextNote != 0x0e) { if (nextIns < 0x40 && nextNote != 0x0d && nextNote != 0x0e) {
pat->data[row][2] = nextIns; pat->data[row][2] = nextIns;
@ -1606,6 +1778,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
pat->data[row][3] = nextVol; pat->data[row][3] = nextVol;
if (map_channels[ch] == vrc6_saw_chan) // scale volume if (map_channels[ch] == vrc6_saw_chan) // scale volume
{ {
// TODO: shouldn't it be 32?
pat->data[row][3] = (pat->data[row][3] * 42) / 15; pat->data[row][3] = (pat->data[row][3] * 42) / 15;
} }
@ -1625,13 +1798,9 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
effectCols = 1; effectCols = 1;
} }
logV("effectCols: %d",effectCols);
unsigned char nextEffectVal = 0; unsigned char nextEffectVal = 0;
unsigned char nextEffect = 0; unsigned char nextEffect = 0;
//logV("row %d effects are read at %x",row,reader.tell());
for (int j = 0; j < effectCols; j++) { for (int j = 0; j < effectCols; j++) {
nextEffect = reader.readC(); nextEffect = reader.readC();
@ -1727,14 +1896,12 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
} }
// logW("next effect %d val %d", nextEffect, nextEffectVal);
if (map_channels[ch] != 0xff) { if (map_channels[ch] != 0xff) {
if (nextEffect == 0 && nextEffectVal == 0) { if (nextEffect == 0 && nextEffectVal == 0) {
pat->data[row][4 + (j * 2)] = -1; pat->data[row][4 + (j * 2)] = -1;
pat->data[row][5 + (j * 2)] = -1; pat->data[row][5 + (j * 2)] = -1;
} else { } else {
if (nextEffect < ftEffectMapSize) { if ((eft && nextEffect<eftEffectMapSize) || (!eft && nextEffect<ftEffectMapSize)) {
if (eft) { if (eft) {
pat->data[row][4 + (j * 2)] = eftEffectMap[nextEffect]; pat->data[row][4 + (j * 2)] = eftEffectMap[nextEffect];
pat->data[row][5 + (j * 2)] = eftEffectMap[nextEffect] == -1 ? -1 : nextEffectVal; pat->data[row][5 + (j * 2)] = eftEffectMap[nextEffect] == -1 ? -1 : nextEffectVal;
@ -1782,7 +1949,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
} }
} else if (blockName == "DPCM SAMPLES") { } else if (blockName == "DPCM SAMPLES") {
CHECK_BLOCK_VERSION(1); CHECK_BLOCK_VERSION(1);
// reader.seek(blockSize,SEEK_CUR);
unsigned char num_samples = reader.readC(); unsigned char num_samples = reader.readC();
for (int i = 0; i < 256; i++) { for (int i = 0; i < 256; i++) {
@ -1808,14 +1974,16 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
unsigned int sample_len = reader.readI(); unsigned int sample_len = reader.readI();
if (sample_len>=2097152) {
logE("%d: sample too large! %d",index,sample_len);
lastError = "sample too large";
delete[] file;
return false;
}
true_size = sample_len + ((1 - (int)sample_len) & 0x0f); true_size = sample_len + ((1 - (int)sample_len) & 0x0f);
sample->lengthDPCM = true_size; sample->init(true_size * 8);
sample->samples = true_size * 8;
sample->dataDPCM = new unsigned char[true_size];
memset(sample->dataDPCM, 0xAA, true_size); memset(sample->dataDPCM, 0xAA, true_size);
reader.read(sample->dataDPCM, sample_len); reader.read(sample->dataDPCM, sample_len);
} }
@ -1824,7 +1992,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
for (int i = 255; i > 0; i--) { for (int i = 255; i > 0; i--) {
DivSample* s = ds.sample[i]; DivSample* s = ds.sample[i];
if (s->dataDPCM) { if (s->samples>0) {
last_non_empty_sample = i; last_non_empty_sample = i;
break; break;
} }
@ -1835,21 +2003,42 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
} }
ds.sampleLen = ds.sample.size(); ds.sampleLen = ds.sample.size();
} else if (blockName == "SEQUENCES_VRC6") { } else if (blockName == "SEQUENCES_VRC6") {
CHECK_BLOCK_VERSION(6); CHECK_BLOCK_VERSION(6);
unsigned char* Indices = new unsigned char[128 * 5]; unsigned char* Indices = new unsigned char[128 * 5];
unsigned char* Types = new unsigned char[128 * 5]; unsigned char* Types = new unsigned char[128 * 5];
memset(Indices,0,128*5);
memset(Types,0,128*5);
unsigned int seq_count = reader.readI(); unsigned int seq_count = reader.readI();
for (unsigned int i = 0; i < seq_count; i++) { for (unsigned int i = 0; i < seq_count; i++) {
unsigned int index = reader.readI(); unsigned int index = reader.readI();
if (index>=128*5) {
logE("%d: index out of range",i);
lastError = "sequence index out of range";
delete[] file;
return false;
}
Indices[i] = index; Indices[i] = index;
unsigned int type = reader.readI(); unsigned int type = reader.readI();
if (type>=128*5) {
logE("%d: type out of range",i);
lastError = "sequence type out of range";
delete[] file;
return false;
}
Types[i] = type; Types[i] = type;
if (index>=256 || type>=8) {
logE("%d: index/type out of range",i);
lastError = "sequence index/type out of range";
delete[] file;
return false;
}
unsigned char size = reader.readC(); unsigned char size = reader.readC();
unsigned int setting = 0; unsigned int setting = 0;
@ -1914,9 +2103,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
unsigned int release = reader.readI(); unsigned int release = reader.readI();
unsigned int setting = reader.readI(); unsigned int setting = reader.readI();
// macros[index][type].rel = release;
// macro_types[index][type] = setting;
for (int k = 0; k < (int)ds.ins.size(); k++) { for (int k = 0; k < (int)ds.ins.size(); k++) {
DivInstrument* ins = ds.ins[k]; DivInstrument* ins = ds.ins[k];
if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_VRC6 && hasSequence[k][Types[i]]) { if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_VRC6 && hasSequence[k][Types[i]]) {
@ -1937,19 +2123,40 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
delete[] Types; delete[] Types;
} else if (blockName == "SEQUENCES_N163" || blockName == "SEQUENCES_N106") { } else if (blockName == "SEQUENCES_N163" || blockName == "SEQUENCES_N106") {
CHECK_BLOCK_VERSION(1); CHECK_BLOCK_VERSION(1);
// reader.seek(blockSize,SEEK_CUR);
unsigned char* Indices = new unsigned char[128 * 5]; unsigned char* Indices = new unsigned char[128 * 5];
unsigned char* Types = new unsigned char[128 * 5]; unsigned char* Types = new unsigned char[128 * 5];
memset(Indices,0,128*5);
memset(Types,0,128*5);
unsigned int seq_count = reader.readI(); unsigned int seq_count = reader.readI();
for (unsigned int i = 0; i < seq_count; i++) { for (unsigned int i = 0; i < seq_count; i++) {
unsigned int index = reader.readI(); unsigned int index = reader.readI();
if (index>=128*5) {
logE("%d: index out of range",i);
lastError = "sequence index out of range";
delete[] file;
return false;
}
Indices[i] = index; Indices[i] = index;
unsigned int type = reader.readI(); unsigned int type = reader.readI();
if (type>=128*5) {
logE("%d: type out of range",i);
lastError = "sequence type out of range";
delete[] file;
return false;
}
Types[i] = type; Types[i] = type;
if (index>=256 || type>=8) {
logE("%d: index/type out of range",i);
lastError = "sequence index/type out of range";
delete[] file;
return false;
}
unsigned char size = reader.readC(); unsigned char size = reader.readC();
unsigned int setting = 0; unsigned int setting = 0;
@ -1974,7 +2181,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
DivInstrument* ins = ds.ins[k]; DivInstrument* ins = ds.ins[k];
if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_N163 && hasSequence[k][Types[i]]) { if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_N163 && hasSequence[k][Types[i]]) {
copyMacro(ins, &macros[index][type], type, setting); copyMacro(ins, &macros[index][type], type, setting);
// memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)Types[i], true), &macros[sequenceIndex[index][type]][type], sizeof(DivInstrumentMacro));
} }
} }
} }
@ -1984,19 +2190,40 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
} else if (blockName == "SEQUENCES_S5B") { } else if (blockName == "SEQUENCES_S5B") {
CHECK_BLOCK_VERSION(1); CHECK_BLOCK_VERSION(1);
// reader.seek(blockSize,SEEK_CUR);
unsigned char* Indices = new unsigned char[128 * 5]; unsigned char* Indices = new unsigned char[128 * 5];
unsigned char* Types = new unsigned char[128 * 5]; unsigned char* Types = new unsigned char[128 * 5];
memset(Indices,0,128*5);
memset(Types,0,128*5);
unsigned int seq_count = reader.readI(); unsigned int seq_count = reader.readI();
for (unsigned int i = 0; i < seq_count; i++) { for (unsigned int i = 0; i < seq_count; i++) {
unsigned int index = reader.readI(); unsigned int index = reader.readI();
if (index>=128*5) {
logE("%d: index out of range",i);
lastError = "sequence index out of range";
delete[] file;
return false;
}
Indices[i] = index; Indices[i] = index;
unsigned int type = reader.readI(); unsigned int type = reader.readI();
if (type>=128*5) {
logE("%d: type out of range",i);
lastError = "sequence type out of range";
delete[] file;
return false;
}
Types[i] = type; Types[i] = type;
if (index>=256 || type>=8) {
logE("%d: index/type out of range",i);
lastError = "sequence index/type out of range";
delete[] file;
return false;
}
unsigned char size = reader.readC(); unsigned char size = reader.readC();
unsigned int setting = 0; unsigned int setting = 0;
@ -2021,7 +2248,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
DivInstrument* ins = ds.ins[k]; DivInstrument* ins = ds.ins[k];
if (sequenceIndex[k][type] == Indices[i] && ins->type == DIV_INS_AY && hasSequence[k][type]) { if (sequenceIndex[k][type] == Indices[i] && ins->type == DIV_INS_AY && hasSequence[k][type]) {
copyMacro(ins, &macros[index][type], type, setting); copyMacro(ins, &macros[index][type], type, setting);
// memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)Types[i], true), &macros[sequenceIndex[index][type]][type], sizeof(DivInstrumentMacro));
} }
} }
} }
@ -2034,14 +2260,36 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
unsigned char* Indices = new unsigned char[128 * 5]; unsigned char* Indices = new unsigned char[128 * 5];
unsigned char* Types = new unsigned char[128 * 5]; unsigned char* Types = new unsigned char[128 * 5];
memset(Indices,0,128*5);
memset(Types,0,128*5);
unsigned int seq_count = reader.readI(); unsigned int seq_count = reader.readI();
for (unsigned int i = 0; i < seq_count; i++) { for (unsigned int i = 0; i < seq_count; i++) {
unsigned int index = reader.readI(); unsigned int index = reader.readI();
if (index>=128*5) {
logE("%d: index out of range",i);
lastError = "sequence index out of range";
delete[] file;
return false;
}
Indices[i] = index; Indices[i] = index;
unsigned int type = reader.readI(); unsigned int type = reader.readI();
if (type>=128*5) {
logE("%d: type out of range",i);
lastError = "sequence type out of range";
delete[] file;
return false;
}
Types[i] = type; Types[i] = type;
if (index>=256 || type>=8) {
logE("%d: index/type out of range",i);
lastError = "sequence index/type out of range";
delete[] file;
return false;
}
unsigned char size = reader.readC(); unsigned char size = reader.readC();
unsigned int setting = 0; unsigned int setting = 0;
@ -2066,7 +2314,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
DivInstrument* ins = ds.ins[k]; DivInstrument* ins = ds.ins[k];
if (sequenceIndex[k][type] == Indices[i] && ins->type == DIV_INS_C64 && hasSequence[k][type]) { if (sequenceIndex[k][type] == Indices[i] && ins->type == DIV_INS_C64 && hasSequence[k][type]) {
copyMacro(ins, &macros[index][type], type, setting); copyMacro(ins, &macros[index][type], type, setting);
// memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)Types[i], true), &macros[sequenceIndex[index][type]][type], sizeof(DivInstrumentMacro));
} }
} }
} }
@ -2075,31 +2322,33 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
delete[] Types; delete[] Types;
} else if (blockName == "JSON") { } else if (blockName == "JSON") {
CHECK_BLOCK_VERSION(1); CHECK_BLOCK_VERSION(1);
logW("block JSON not supported...");
reader.seek(blockSize, SEEK_CUR); reader.seek(blockSize, SEEK_CUR);
} else if (blockName == "PARAMS_EMU") { } else if (blockName == "PARAMS_EMU") {
CHECK_BLOCK_VERSION(1); CHECK_BLOCK_VERSION(1);
logW("block PARAMS_EMU not supported...");
reader.seek(blockSize, SEEK_CUR); reader.seek(blockSize, SEEK_CUR);
} else if (blockName == "DETUNETABLES") { } else if (blockName == "DETUNETABLES") {
CHECK_BLOCK_VERSION(1); CHECK_BLOCK_VERSION(1);
logW("block DETUNETABLES not supported...");
reader.seek(blockSize, SEEK_CUR); reader.seek(blockSize, SEEK_CUR);
} else if (blockName == "COMMENTS") { } else if (blockName == "COMMENTS") {
CHECK_BLOCK_VERSION(1); CHECK_BLOCK_VERSION(1);
// reader.seek(blockSize,SEEK_CUR);
unsigned int display_comment = reader.readI(); unsigned int display_comment = reader.readI();
(void)display_comment;
char ch = 1; logV("displayComment: %d",display_comment);
do { char ch = 0;
// why not readString?
while (true) {
ch = reader.readC(); ch = reader.readC();
String sss = String() + ch; if (ch==0) break;
ds.subsong[0]->notes += sss; ds.subsong[0]->notes += ch;
} while (ch != 0); }
// ds.subsong[0]->notes = reader.readS();
} else if (blockName == "PARAMS_EXTRA") { } else if (blockName == "PARAMS_EXTRA") {
CHECK_BLOCK_VERSION(3); CHECK_BLOCK_VERSION(3);
// reader.seek(blockSize,SEEK_CUR);
unsigned int linear_pitch = reader.readI(); unsigned int linear_pitch = reader.readI();
ds.linearPitch = linear_pitch == 0 ? 0 : 2; ds.linearPitch = linear_pitch == 0 ? 0 : 2;
@ -2112,11 +2361,10 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
} }
if (blockVersion >= 3) { if (blockVersion >= 3) {
unsigned char flats = reader.readC(); unsigned char flats = reader.readC();
(void)flats; logV("flats: %d",(int)flats);
} }
} else if (blockName == "TUNING") { } else if (blockName == "TUNING") {
CHECK_BLOCK_VERSION(1); CHECK_BLOCK_VERSION(1);
// reader.seek(blockSize,SEEK_CUR);
if (blockVersion == 1) { if (blockVersion == 1) {
int fineTuneCents = reader.readC() * 100; int fineTuneCents = reader.readC() * 100;
fineTuneCents += reader.readC(); fineTuneCents += reader.readC();
@ -2125,6 +2373,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
} }
} else if (blockName == "BOOKMARKS") { } else if (blockName == "BOOKMARKS") {
CHECK_BLOCK_VERSION(1); CHECK_BLOCK_VERSION(1);
logW("block BOOKMARKS not supported...");
reader.seek(blockSize, SEEK_CUR); reader.seek(blockSize, SEEK_CUR);
} else { } else {
logE("block %s is unknown!", blockName); logE("block %s is unknown!", blockName);
@ -2152,6 +2401,8 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si
if (index < 0) if (index < 0)
index = 0; index = 0;
if (index>=(int)ds.ins.size()) continue;
DivInstrument* ins = ds.ins[index]; DivInstrument* ins = ds.ins[index];
if (ins->type == DIV_INS_FM) { if (ins->type == DIV_INS_FM) {