.ftm loading progress (incomplete, not working)

This commit is contained in:
tildearrow 2022-05-06 01:26:49 -05:00
parent 509e46417f
commit 05af3c147a
1 changed files with 331 additions and 3 deletions

View File

@ -19,6 +19,7 @@
#include "engine.h"
#include "../ta-log.h"
#include "instrument.h"
#include "song.h"
#include <zlib.h>
#include <fmt/printf.h>
@ -2034,11 +2035,28 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
return success;
}
#define CHECK_BLOCK_VERSION(x) \
if (blockVersion>x) { \
logE("incompatible block version %d for %s!",blockVersion,blockName); \
lastError="incompatible block version"; \
delete[] file; \
return false; \
}
bool DivEngine::loadFTM(unsigned char* file, size_t len) {
SafeReader reader=SafeReader(file,len);
warnings="";
try {
DivSong ds;
String blockName;
unsigned char expansions=0;
unsigned int tchans=0;
unsigned int n163Chans=0;
bool hasSequence[256][8];
unsigned char sequenceIndex[256][8];
memset(hasSequence,0,256*8*sizeof(bool));
memset(sequenceIndex,0,256*8);
if (!reader.seek(18,SEEK_SET)) {
logE("premature end of file!");
@ -2046,17 +2064,327 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len) {
delete[] file;
return false;
}
ds.version=(unsigned short)reader.readS();
ds.version=(unsigned short)reader.readI();
logI("module version %d (0x%.4x)",ds.version,ds.version);
if (ds.version>0x0440) {
if (ds.version>0x0450) {
logE("incompatible version %x!",ds.version);
lastError="incompatible version";
delete[] file;
return false;
}
while (true) {
blockName=reader.readString(3);
if (blockName=="END") {
// end of module
logD("end of data");
break;
}
// not the end
reader.seek(-3,SEEK_CUR);
blockName=reader.readString(16);
unsigned int blockVersion=(unsigned int)reader.readI();
unsigned int blockSize=(unsigned int)reader.readI();
size_t blockStart=reader.tell();
logD("reading block %s (version %d, %d bytes)",blockName,blockVersion,blockSize);
if (blockName=="PARAMS") {
CHECK_BLOCK_VERSION(6);
unsigned int oldSpeedTempo=0;
if (blockVersion<=1) {
oldSpeedTempo=reader.readI();
}
if (blockVersion>=2) {
expansions=reader.readC();
}
tchans=reader.readI();
unsigned int pal=reader.readI();
unsigned int customHz=reader.readI();
unsigned int newVibrato=0;
unsigned int speedSplitPoint=0;
if (blockVersion>=3) {
newVibrato=reader.readI();
}
if (blockVersion>=4) {
ds.hilightA=reader.readI();
ds.hilightB=reader.readI();
}
if (expansions&8) if (blockVersion>=5) { // N163 channels
n163Chans=reader.readI();
}
if (blockVersion>=6) {
speedSplitPoint=reader.readI();
}
logV("old speed/tempo: %d",oldSpeedTempo);
logV("expansions: %x",expansions);
logV("channels: %d",tchans);
logV("PAL: %d",pal);
logV("custom Hz: %d",customHz);
logV("new vibrato: %d",newVibrato);
logV("N163 channels: %d",n163Chans);
logV("highlight 1: %d",ds.hilightA);
logV("highlight 2: %d",ds.hilightB);
logV("split point: %d",speedSplitPoint);
if (customHz!=0) {
ds.hz=customHz;
}
// initialize channels
int systemID=0;
ds.system[systemID++]=DIV_SYSTEM_NES;
if (expansions&1) {
ds.system[systemID++]=DIV_SYSTEM_VRC6;
}
if (expansions&2) {
ds.system[systemID++]=DIV_SYSTEM_VRC7;
}
if (expansions&4) {
ds.system[systemID++]=DIV_SYSTEM_FDS;
}
if (expansions&8) {
ds.system[systemID++]=DIV_SYSTEM_MMC5;
}
if (expansions&16) {
ds.system[systemID]=DIV_SYSTEM_N163;
ds.systemFlags[systemID++]=n163Chans;
}
if (expansions&32) {
ds.system[systemID]=DIV_SYSTEM_AY8910;
ds.systemFlags[systemID++]=38; // Sunsoft 5B
}
ds.systemLen=systemID;
unsigned int calcChans=0;
for (int i=0; i<ds.systemLen; i++) {
calcChans+=getChannelCount(ds.system[i]);
}
if (calcChans!=tchans) {
logE("channel counts do not match! %d != %d",tchans,calcChans);
lastError="channel counts do not match";
delete[] file;
return false;
}
if (tchans>DIV_MAX_CHANS) {
tchans=DIV_MAX_CHANS;
logW("too many channels!");
}
} else if (blockName=="INFO") {
CHECK_BLOCK_VERSION(1);
ds.name=reader.readString(32);
ds.author=reader.readString(32);
ds.copyright=reader.readString(32);
} else if (blockName=="HEADER") {
CHECK_BLOCK_VERSION(3);
unsigned char totalSongs=reader.readC();
logV("%d songs:",totalSongs+1);
for (int i=0; i<=totalSongs; i++) {
String subSongName=reader.readString();
logV("- %s",subSongName);
}
for (unsigned int i=0; i<tchans; i++) {
unsigned char chID=reader.readC();
logV("for channel ID %d",chID);
for (int j=0; j<=totalSongs; j++) {
unsigned char effectCols=reader.readC();
if (j==0) {
ds.pat[i].effectCols=effectCols+1;
}
logV("- song %d has %d effect columns",j,effectCols);
}
}
} else if (blockName=="INSTRUMENTS") {
CHECK_BLOCK_VERSION(6);
ds.insLen=reader.readI();
if (ds.insLen<0 || ds.insLen>256) {
logE("too many instruments/out of range!");
lastError="too many instruments/out of range";
delete[] file;
return false;
}
for (int i=0; i<ds.insLen; i++) {
DivInstrument* ins=new DivInstrument;
ds.ins.push_back(ins);
}
logV("instruments:");
for (int i=0; i<ds.insLen; i++) {
unsigned int insIndex=reader.readI();
if (insIndex>=ds.ins.size()) {
logE("instrument index %d is out of range!",insIndex);
lastError="instrument index out of range";
delete[] file;
return false;
}
DivInstrument* ins=ds.ins[insIndex];
unsigned char insType=reader.readC();
switch (insType) {
case 1:
ins->type=DIV_INS_STD;
break;
case 2: // TODO: tell VRC6 and VRC6 saw instruments apart
ins->type=DIV_INS_VRC6;
break;
case 3:
ins->type=DIV_INS_OPLL;
break;
case 4:
ins->type=DIV_INS_FDS;
break;
case 5:
ins->type=DIV_INS_N163;
break;
case 6: // 5B?
ins->type=DIV_INS_AY;
break;
default: {
logE("%d: invalid instrument type %d",insIndex,insType);
lastError="invalid instrument type";
delete[] file;
return false;
}
}
// instrument data
switch (ins->type) {
case DIV_INS_STD: {
unsigned int totalSeqs=reader.readI();
if (totalSeqs>5) {
logE("%d: too many sequences!",insIndex);
lastError="too many sequences";
delete[] file;
return false;
}
for (unsigned int j=0; j<totalSeqs; j++) {
hasSequence[insIndex][j]=reader.readC();
sequenceIndex[insIndex][j]=reader.readC();
}
const int dpcmNotes=(blockVersion>=2)?96:72;
for (int j=0; j<dpcmNotes; j++) {
ins->amiga.noteMap[j]=(short)((unsigned char)reader.readC())-1;
ins->amiga.noteFreq[j]=(unsigned char)reader.readC();
if (blockVersion>=6) {
reader.readC(); // DMC value
}
}
break;
}
case DIV_INS_VRC6: {
unsigned int totalSeqs=reader.readI();
if (totalSeqs>4) {
logE("%d: too many sequences!",insIndex);
lastError="too many sequences";
delete[] file;
return false;
}
for (unsigned int j=0; j<totalSeqs; j++) {
hasSequence[insIndex][j]=reader.readC();
sequenceIndex[insIndex][j]=reader.readC();
}
break;
}
case DIV_INS_OPLL: {
ins->fm.opllPreset=(unsigned int)reader.readI();
// TODO
break;
}
case DIV_INS_FDS: {
DivWavetable* wave=new DivWavetable;
wave->len=64;
wave->max=64;
for (int j=0; j<64; j++) {
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++) {
ins->fds.modTable[j]=reader.readC()-3;
}
ins->fds.modSpeed=reader.readI();
ins->fds.modDepth=reader.readI();
reader.readI(); // this is delay. currently ignored. TODO.
ds.wave.push_back(wave);
ins->std.volMacro.len=reader.readC();
ins->std.volMacro.loop=reader.readI();
ins->std.volMacro.rel=reader.readI();
reader.readI(); // arp mode does not apply here
for (int j=0; j<ins->std.volMacro.len; j++) {
ins->std.volMacro.val[j]=reader.readC();
}
ins->std.arpMacro.len=reader.readC();
ins->std.arpMacro.loop=reader.readI();
ins->std.arpMacro.rel=reader.readI();
ins->std.arpMacro.mode=reader.readI();
for (int j=0; j<ins->std.arpMacro.len; j++) {
ins->std.arpMacro.val[j]=reader.readC();
}
ins->std.pitchMacro.len=reader.readC();
ins->std.pitchMacro.loop=reader.readI();
ins->std.pitchMacro.rel=reader.readI();
reader.readI(); // arp mode does not apply here
for (int j=0; j<ins->std.pitchMacro.len; j++) {
ins->std.pitchMacro.val[j]=reader.readC();
}
break;
}
case DIV_INS_N163: {
// TODO!
break;
}
// TODO: 5B!
default: {
logE("%d: what's going on here?",insIndex);
lastError="invalid instrument type";
delete[] file;
return false;
}
}
// name
ins->name=reader.readString((unsigned int)reader.readI());
logV("- %d: %s",insIndex,ins->name);
}
} else if (blockName=="SEQUENCES") {
CHECK_BLOCK_VERSION(6);
} else if (blockName=="FRAMES") {
CHECK_BLOCK_VERSION(3);
} else if (blockName=="PATTERNS") {
CHECK_BLOCK_VERSION(5);
} else if (blockName=="DPCM SAMPLES") {
CHECK_BLOCK_VERSION(1);
} else if (blockName=="SEQUENCES_VRC6") {
// where are the 5B and FDS sequences?
CHECK_BLOCK_VERSION(6);
} else if (blockName=="SEQUENCES_N163") {
CHECK_BLOCK_VERSION(1);
} else if (blockName=="COMMENTS") {
CHECK_BLOCK_VERSION(1);
} else {
logE("block %s is unknown!",blockName);
lastError="unknown block "+blockName;
delete[] file;
return false;
}
if ((reader.tell()-blockStart)!=blockSize) {
logE("block %s is incomplete!",blockName);
lastError="incomplete block "+blockName;
delete[] file;
return false;
}
}
} catch (EndOfFileException& e) {
logE("premature end of file!");
lastError="incomplete file";