277 lines
7.5 KiB
C++
277 lines
7.5 KiB
C++
/**
|
|
* Furnace Tracker - multi-system chiptune tracker
|
|
* Copyright (C) 2021-2023 tildearrow and contributors
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "amigaValidation.h"
|
|
#include "../engine.h"
|
|
#include "../platform/amiga.h"
|
|
|
|
struct WaveEntry {
|
|
unsigned int pos;
|
|
short width;
|
|
signed char data[256];
|
|
WaveEntry():
|
|
pos(0),
|
|
width(32) {
|
|
memset(data,0,256);
|
|
}
|
|
};
|
|
|
|
struct SampleBookEntry {
|
|
unsigned int loc;
|
|
unsigned short len;
|
|
SampleBookEntry():
|
|
loc(0),
|
|
len(0) {}
|
|
};
|
|
|
|
std::vector<DivROMExportOutput> DivExportAmigaValidation::go(DivEngine* e) {
|
|
std::vector<DivROMExportOutput> ret;
|
|
std::vector<WaveEntry> waves;
|
|
std::vector<SampleBookEntry> sampleBook;
|
|
unsigned int wavesDataPtr=0;
|
|
WaveEntry curWaveState[4];
|
|
unsigned int sampleBookLoc=0;
|
|
|
|
DivPlatformAmiga* amiga=(DivPlatformAmiga*)e->getDispatch(0);
|
|
|
|
e->stop();
|
|
e->repeatPattern=false;
|
|
e->setOrder(0);
|
|
EXTERN_BUSY_BEGIN_SOFT;
|
|
|
|
// determine loop point
|
|
int loopOrder=0;
|
|
int loopRow=0;
|
|
int loopEnd=0;
|
|
e->walkSong(loopOrder,loopRow,loopEnd);
|
|
|
|
e->curOrder=0;
|
|
e->freelance=false;
|
|
e->playing=false;
|
|
e->extValuePresent=false;
|
|
e->remainingLoops=-1;
|
|
|
|
// play the song ourselves
|
|
bool done=false;
|
|
|
|
// sample.bin
|
|
SafeWriter* sample=new SafeWriter;
|
|
sample->init();
|
|
for (int i=0; i<256; i++) {
|
|
sample->writeI(0);
|
|
}
|
|
sample->write(&((const unsigned char*)amiga->getSampleMem(0))[0x400],amiga->getSampleMemUsage(0)-0x400);
|
|
if (sample->tell()&1) sample->writeC(0);
|
|
|
|
// seq.bin
|
|
SafeWriter* seq=new SafeWriter;
|
|
seq->init();
|
|
|
|
amiga->toggleRegisterDump(true);
|
|
|
|
// write song data
|
|
e->playSub(false);
|
|
size_t songTick=0;
|
|
size_t lastTick=0;
|
|
//bool writeLoop=false;
|
|
int loopPos=-1;
|
|
for (int i=0; i<e->chans; i++) {
|
|
e->chan[i].wentThroughNote=false;
|
|
e->chan[i].goneThroughNote=false;
|
|
}
|
|
while (!done) {
|
|
if (loopPos==-1) {
|
|
if (loopOrder==e->curOrder && loopRow==e->curRow && e->ticks==1) {
|
|
//writeLoop=true;
|
|
}
|
|
}
|
|
if (e->nextTick(false,true)) {
|
|
done=true;
|
|
amiga->getRegisterWrites().clear();
|
|
if (lastTick!=songTick) {
|
|
int delta=songTick-lastTick;
|
|
if (delta==1) {
|
|
seq->writeC(0xf1);
|
|
} else if (delta<256) {
|
|
seq->writeC(0xf2);
|
|
seq->writeC(delta-1);
|
|
} else if (delta<32768) {
|
|
seq->writeC(0xf3);
|
|
seq->writeS_BE(delta-1);
|
|
}
|
|
lastTick=songTick;
|
|
}
|
|
break;
|
|
}
|
|
// check wavetable changes
|
|
for (int i=0; i<4; i++) {
|
|
if (amiga->chan[i].useWave) {
|
|
if ((amiga->chan[i].audLen*2)!=curWaveState[i].width || memcmp(curWaveState[i].data,&(((signed char*)amiga->getSampleMem())[i<<8]),amiga->chan[i].audLen*2)!=0) {
|
|
curWaveState[i].width=amiga->chan[i].audLen*2;
|
|
memcpy(curWaveState[i].data,&(((signed char*)amiga->getSampleMem())[i<<8]),amiga->chan[i].audLen*2);
|
|
|
|
int waveNum=-1;
|
|
for (size_t j=0; j<waves.size(); j++) {
|
|
if (waves[j].width!=curWaveState[i].width) continue;
|
|
if (memcmp(waves[j].data,curWaveState[i].data,curWaveState[i].width)==0) {
|
|
waveNum=j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (waveNum==-1) {
|
|
// write new wavetable
|
|
waveNum=(int)waves.size();
|
|
curWaveState[i].pos=wavesDataPtr;
|
|
waves.push_back(curWaveState[i]);
|
|
wavesDataPtr+=curWaveState[i].width;
|
|
}
|
|
|
|
if (waveNum<256) {
|
|
seq->writeC((i<<4)|3);
|
|
seq->writeC(waveNum);
|
|
} else if (waveNum<65536) {
|
|
seq->writeC((i<<4)|4);
|
|
seq->writeS_BE(waveNum);
|
|
} else{
|
|
seq->writeC((i<<4)|1);
|
|
seq->writeC(waves[waveNum].pos>>16);
|
|
seq->writeC(waves[waveNum].pos>>8);
|
|
seq->writeC(waves[waveNum].pos);
|
|
seq->writeS_BE(waves[waveNum].width);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// get register writes
|
|
std::vector<DivRegWrite>& writes=amiga->getRegisterWrites();
|
|
for (DivRegWrite& j: writes) {
|
|
if (lastTick!=songTick) {
|
|
int delta=songTick-lastTick;
|
|
if (delta==1) {
|
|
seq->writeC(0xf1);
|
|
} else if (delta<256) {
|
|
seq->writeC(0xf2);
|
|
seq->writeC(delta-1);
|
|
} else if (delta<32768) {
|
|
seq->writeC(0xf3);
|
|
seq->writeS_BE(delta-1);
|
|
}
|
|
lastTick=songTick;
|
|
}
|
|
if (j.addr>=0x200) { // direct loc/len change
|
|
if (j.addr&4) { // len
|
|
int sampleBookIndex=-1;
|
|
for (size_t i=0; i<sampleBook.size(); i++) {
|
|
if (sampleBook[i].loc==sampleBookLoc && sampleBook[i].len==(j.val&0xffff)) {
|
|
sampleBookIndex=i;
|
|
break;
|
|
}
|
|
}
|
|
if (sampleBookIndex==-1) {
|
|
if (sampleBook.size()<256) {
|
|
SampleBookEntry e;
|
|
e.loc=sampleBookLoc;
|
|
e.len=j.val&0xffff;
|
|
sampleBookIndex=sampleBook.size();
|
|
sampleBook.push_back(e);
|
|
}
|
|
}
|
|
|
|
if (sampleBookIndex==-1) {
|
|
seq->writeC((j.addr&3)<<4);
|
|
seq->writeC(sampleBookLoc>>16);
|
|
seq->writeC(sampleBookLoc>>8);
|
|
seq->writeC(sampleBookLoc);
|
|
seq->writeS_BE(j.val);
|
|
} else {
|
|
seq->writeC(((j.addr&3)<<4)|2);
|
|
seq->writeC(sampleBookIndex);
|
|
}
|
|
} else { // loc
|
|
sampleBookLoc=j.val;
|
|
}
|
|
} else if (j.addr<0xa0) {
|
|
// don't write INTENA
|
|
if ((j.addr&15)!=10) {
|
|
seq->writeC(0xf0|(j.addr&15));
|
|
seq->writeS_BE(j.val);
|
|
}
|
|
} else if ((j.addr&15)!=0 && (j.addr&15)!=2 && (j.addr&15)!=4) {
|
|
seq->writeC(j.addr-0xa0);
|
|
if ((j.addr&15)==8) {
|
|
seq->writeC(j.val);
|
|
} else {
|
|
seq->writeS_BE(j.val);
|
|
}
|
|
}
|
|
}
|
|
writes.clear();
|
|
|
|
songTick++;
|
|
}
|
|
// end of song
|
|
seq->writeC(0xff);
|
|
|
|
amiga->toggleRegisterDump(false);
|
|
|
|
e->remainingLoops=-1;
|
|
e->playing=false;
|
|
e->freelance=false;
|
|
e->extValuePresent=false;
|
|
|
|
EXTERN_BUSY_END;
|
|
|
|
// wave.bin
|
|
SafeWriter* wave=new SafeWriter;
|
|
wave->init();
|
|
for (WaveEntry& i: waves) {
|
|
wave->write(i.data,i.width);
|
|
}
|
|
|
|
// sbook.bin
|
|
SafeWriter* sbook=new SafeWriter;
|
|
sbook->init();
|
|
for (SampleBookEntry& i: sampleBook) {
|
|
// 8 bytes per entry
|
|
sbook->writeI_BE(i.loc);
|
|
sbook->writeI_BE(i.len);
|
|
}
|
|
|
|
// wbook.bin
|
|
SafeWriter* wbook=new SafeWriter;
|
|
wbook->init();
|
|
for (WaveEntry& i: waves) {
|
|
wbook->writeC(i.width);
|
|
wbook->writeC(i.pos>>16);
|
|
wbook->writeC(i.pos>>8);
|
|
wbook->writeC(i.pos);
|
|
}
|
|
|
|
// finish
|
|
ret.push_back(DivROMExportOutput("sbook.bin",sbook));
|
|
ret.push_back(DivROMExportOutput("wbook.bin",wbook));
|
|
ret.push_back(DivROMExportOutput("sample.bin",sample));
|
|
ret.push_back(DivROMExportOutput("wave.bin",wave));
|
|
ret.push_back(DivROMExportOutput("seq.bin",seq));
|
|
|
|
return ret;
|
|
}
|