From defbeae704124bba4ae68883e76e6f9a880058fe Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 6 May 2025 15:19:59 -0500 Subject: [PATCH] VGM export: fix NES DPCM --- src/engine/engine.h | 2 +- src/engine/vgmOps.cpp | 105 +++++++++++++++++++++++++++--------------- 2 files changed, 69 insertions(+), 38 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index 77132ee4c..e630def7a 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -583,7 +583,7 @@ class DivEngine { void processRow(int i, bool afterDelay); void nextOrder(); void nextRow(); - void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, int* setPos, unsigned int* sampleOff8, unsigned int* sampleLen8, size_t bankOffset, bool directStream, bool* sampleStoppable); + void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, int* setPos, unsigned int* sampleOff8, unsigned int* sampleLen8, size_t bankOffset, bool directStream, bool* sampleStoppable, bool dpcm07, DivDispatch** writeNES); // returns true if end of song. bool nextTick(bool noAccum=false, bool inhibitLowLat=false); bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal); diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index ec389d112..625569f66 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -24,7 +24,7 @@ // this function is so long // may as well make it something else -void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, int* setPos, unsigned int* sampleOff8, unsigned int* sampleLen8, size_t bankOffset, bool directStream, bool* sampleStoppable) { +void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, int* setPos, unsigned int* sampleOff8, unsigned int* sampleLen8, size_t bankOffset, bool directStream, bool* sampleStoppable, bool dpcm07, DivDispatch** writeNES) { unsigned char baseAddr1=isSecond?0xa0:0x50; unsigned char baseAddr2=isSecond?0x80:0; unsigned short baseAddr2S=isSecond?0x8000:0; @@ -712,19 +712,37 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write if (write.addr==0xffff0004) { // switch sample bank switch (sys) { case DIV_SYSTEM_NES: { - unsigned int bankAddr=bankOffset+(write.val<<14); - w->writeC(0x68); - w->writeC(0x66); - w->writeC(0x07|(isSecond?0x80:0x00)); - w->writeC(bankAddr&0xff); - w->writeC((bankAddr>>8)&0xff); - w->writeC((bankAddr>>16)&0xff); - w->writeC(0x00); - w->writeC(0xc0); - w->writeC(0x00); - w->writeC(0x00); - w->writeC(0x40); - w->writeC(0x00); + if (dpcm07) { + unsigned int bankAddr=bankOffset+(write.val<<14); + w->writeC(0x68); + w->writeC(0x66); + w->writeC(0x07|(isSecond?0x80:0x00)); + w->writeC(bankAddr&0xff); + w->writeC((bankAddr>>8)&0xff); + w->writeC((bankAddr>>16)&0xff); + w->writeC(0x00); + w->writeC(0xc0); + w->writeC(0x00); + w->writeC(0x00); + w->writeC(0x40); + w->writeC(0x00); + } else { + // write the whole damn bank. + // this code looks like a mess because it is a hack. + // don't blame me if your VGM ends up being over a gigabyte! + size_t howMuchWillBeWritten=writeNES[isSecond?1:0]->getSampleMemUsage(); + // refuse to switch if we're going out of bounds + if ((write.val<<14)>=howMuchWillBeWritten) break; + howMuchWillBeWritten-=(write.val<<14); + if (howMuchWillBeWritten>16384) howMuchWillBeWritten=16384; + w->writeC(0x67); + w->writeC(0x66); + w->writeC(0xc2); + w->writeI(howMuchWillBeWritten+2); + // data + w->writeS(0xc000); + w->write(&(((unsigned char*)writeNES[isSecond?1:0]->getSampleMem())[write.val<<14]),howMuchWillBeWritten); + } break; } default: @@ -2387,28 +2405,41 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p w->write(writeK053260[i]->getSampleMem(),writeK053260[i]->getSampleMemUsage()); } if (writeNES[i]!=NULL && writeNES[i]->getSampleMemUsage()>0) { - size_t howMuchWillBeWritten=writeNES[i]->getSampleMemUsage(); - w->writeC(0x67); - w->writeC(0x66); - w->writeC(7); - w->writeI(howMuchWillBeWritten); - w->write(writeNES[i]->getSampleMem(),howMuchWillBeWritten); - bankOffsetNES[i]=bankOffsetNESCurrent; - bankOffset[writeNESIndex[i]]=bankOffsetNES[i]; - bankOffsetNESCurrent+=howMuchWillBeWritten; - // force the first bank - w->writeC(0x68); - w->writeC(0x6c); - w->writeC(0x07|(i?0x80:0x00)); - w->writeC(bankOffsetNES[i]&0xff); - w->writeC((bankOffsetNES[i]>>8)&0xff); - w->writeC((bankOffsetNES[i]>>16)&0xff); - w->writeC(0x00); - w->writeC(0xc0); - w->writeC(0x00); - w->writeC(0x00); - w->writeC(0x40); - w->writeC(0x00); + if (dpcm07) { + size_t howMuchWillBeWritten=writeNES[i]->getSampleMemUsage(); + w->writeC(0x67); + w->writeC(0x66); + w->writeC(7); + w->writeI(howMuchWillBeWritten); + w->write(writeNES[i]->getSampleMem(),howMuchWillBeWritten); + bankOffsetNES[i]=bankOffsetNESCurrent; + bankOffset[writeNESIndex[i]]=bankOffsetNES[i]; + bankOffsetNESCurrent+=howMuchWillBeWritten; + // force the first bank + w->writeC(0x68); + w->writeC(0x6c); + w->writeC(0x07|(i?0x80:0x00)); + w->writeC(bankOffsetNES[i]&0xff); + w->writeC((bankOffsetNES[i]>>8)&0xff); + w->writeC((bankOffsetNES[i]>>16)&0xff); + w->writeC(0x00); + w->writeC(0xc0); + w->writeC(0x00); + w->writeC(0x00); + w->writeC(0x40); + w->writeC(0x00); + } else { + // write the first bank + size_t howMuchWillBeWritten=writeNES[i]->getSampleMemUsage(); + if (howMuchWillBeWritten>16384) howMuchWillBeWritten=16384; + w->writeC(0x67); + w->writeC(0x66); + w->writeC(0xc2); + w->writeI(howMuchWillBeWritten+2); + // data + w->writeS(0xc000); + w->write(writeNES[i]->getSampleMem(),howMuchWillBeWritten); + } } } @@ -2799,7 +2830,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p lastOne=i.second.time; } // write write - performVGMWrite(w,song.system[i.first],i.second.write,streamIDs[i.first],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i.first],pendingFreq,playingSample,setPos,sampleOff8,sampleLen8,bankOffset[i.first],directStream,sampleStoppable); + performVGMWrite(w,song.system[i.first],i.second.write,streamIDs[i.first],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i.first],pendingFreq,playingSample,setPos,sampleOff8,sampleLen8,bankOffset[i.first],directStream,sampleStoppable,dpcm07,writeNES); writeCount++; } sortedWrites.clear();