mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-26 22:43:01 +00:00
.ftm loading progress (incomplete, not working)
This commit is contained in:
parent
509e46417f
commit
05af3c147a
1 changed files with 331 additions and 3 deletions
|
@ -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";
|
||||
|
|
Loading…
Reference in a new issue