From 8bcab6e13991889899299062ed0e4887ab705373 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 25 Jan 2022 18:46:27 -0500 Subject: [PATCH] VGM export: write resets and GD3 tag may be non-standard compliant (yet) also it crashes foobar2000 for some reason but this will be fixed --- CMakeLists.txt | 2 +- src/engine/engine.cpp | 338 +++++++++++++++++++++++++++++++- src/engine/engine.h | 3 + src/engine/platform/arcade.cpp | 3 + src/engine/platform/ay.cpp | 3 + src/engine/platform/ay8930.cpp | 3 + src/engine/platform/gb.cpp | 3 + src/engine/platform/genesis.cpp | 5 + src/engine/platform/nes.cpp | 3 + src/engine/platform/pce.cpp | 3 + src/engine/platform/saa.cpp | 3 + src/engine/platform/sms.cpp | 3 + src/engine/platform/ym2610.cpp | 3 + src/engine/safeWriter.cpp | 15 ++ src/engine/safeWriter.h | 1 + src/ta-utils.h | 2 - src/utfutils.cpp | 1 - 17 files changed, 388 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b18114ea7..86e491c7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,7 @@ endif() set(ENGINE_SOURCES src/log.cpp src/fileutils.cpp +src/utfutils.cpp extern/Nuked-OPN2/ym3438.c extern/opm/opm.c @@ -176,7 +177,6 @@ if (NOT WIN32 AND NOT APPLE) endif() if (WIN32) - list(APPEND ENGINE_SOURCES src/utfutils.cpp) list(APPEND ENGINE_SOURCES src/engine/winStuff.cpp) list(APPEND ENGINE_SOURCES res/furnace.rc) endif() diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 05a916db4..a2f108c4c 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -6,6 +6,7 @@ #include "safeReader.h" #include "../ta-log.h" #include "../fileutils.h" +#include "../utfutils.h" #include "../audio/sdl.h" #include #include @@ -233,6 +234,53 @@ const char* DivEngine::getSystemName(DivSystem sys) { return "Unknown"; } +const char* DivEngine::getSystemNameJ(DivSystem sys) { + switch (sys) { + case DIV_SYSTEM_NULL: + return "不明"; + case DIV_SYSTEM_YMU759: + return ""; + case DIV_SYSTEM_GENESIS: + return "メガドライブ"; + case DIV_SYSTEM_SMS: + return "マスターシステム"; + case DIV_SYSTEM_GB: + return "ゲームボーイ"; + case DIV_SYSTEM_PCE: + return "PCエンジン"; + case DIV_SYSTEM_NES: + return "ファミリーコンピュータ"; + case DIV_SYSTEM_C64_6581: + return "コモドール64 (6581)"; + case DIV_SYSTEM_C64_8580: + return "コモドール64 (8580)"; + case DIV_SYSTEM_ARCADE: + return "Arcade"; + case DIV_SYSTEM_GENESIS_EXT: + return ""; + case DIV_SYSTEM_YM2610: + return "業務用ネオジオ"; + case DIV_SYSTEM_YM2610_EXT: + return ""; + // Furnace-specific systems + case DIV_SYSTEM_AY8910: + return ""; + case DIV_SYSTEM_AMIGA: + return ""; + case DIV_SYSTEM_YM2151: + return ""; + case DIV_SYSTEM_YM2612: + return ""; + case DIV_SYSTEM_TIA: + return ""; + case DIV_SYSTEM_SAA1099: + return ""; + case DIV_SYSTEM_AY8930: + return ""; + } + return "不明"; +} + bool DivEngine::isFMSystem(DivSystem sys) { return (sys==DIV_SYSTEM_GENESIS || sys==DIV_SYSTEM_GENESIS_EXT || @@ -1910,6 +1958,244 @@ SafeWriter* DivEngine::saveDMF() { } void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample) { + if (write.addr==0xffffffff) { // Furnace fake reset + switch (sys) { + case DIV_SYSTEM_GENESIS: + case DIV_SYSTEM_GENESIS_EXT: + case DIV_SYSTEM_YM2612: + for (int i=0; i<3; i++) { // set SL and RR to highest + w->writeC(0x52); + w->writeC(0x80+i); + w->writeC(0xff); + w->writeC(0x52); + w->writeC(0x84+i); + w->writeC(0xff); + w->writeC(0x52); + w->writeC(0x88+i); + w->writeC(0xff); + w->writeC(0x52); + w->writeC(0x8c+i); + w->writeC(0xff); + + w->writeC(0x53); + w->writeC(0x80+i); + w->writeC(0xff); + w->writeC(0x53); + w->writeC(0x84+i); + w->writeC(0xff); + w->writeC(0x53); + w->writeC(0x88+i); + w->writeC(0xff); + w->writeC(0x53); + w->writeC(0x8c+i); + w->writeC(0xff); + } + for (int i=0; i<3; i++) { // note off + w->writeC(0x52); + w->writeC(0x28); + w->writeC(i); + w->writeC(0x52); + w->writeC(0x28); + w->writeC(4+i); + } + w->writeC(0x52); // disable DAC + w->writeC(0x2b); + w->writeC(0); + if (sys!=DIV_SYSTEM_YM2612) { + for (int i=0; i<4; i++) { + w->writeC(0x50); + w->writeC(0x90|(i<<5)|15); + } + } + break; + case DIV_SYSTEM_SMS: + for (int i=0; i<4; i++) { + w->writeC(0x50); + w->writeC(0x90|(i<<5)|15); + } + break; + case DIV_SYSTEM_GB: + // square 1 + w->writeC(0xb3); + w->writeC(2); + w->writeC(0); + w->writeC(0xb3); + w->writeC(4); + w->writeC(0x80); + + // square 2 + w->writeC(0xb3); + w->writeC(7); + w->writeC(0); + w->writeC(0xb3); + w->writeC(9); + w->writeC(0x80); + + // wave + w->writeC(0xb3); + w->writeC(0x0c); + w->writeC(0); + w->writeC(0xb3); + w->writeC(0x0e); + w->writeC(0x80); + + // noise + w->writeC(0xb3); + w->writeC(0x11); + w->writeC(0); + w->writeC(0xb3); + w->writeC(0x13); + w->writeC(0x80); + break; + case DIV_SYSTEM_PCE: + for (int i=0; i<6; i++) { + w->writeC(0xb9); + w->writeC(0); + w->writeC(i); + w->writeC(0xb9); + w->writeC(4); + w->writeC(0); + } + break; + case DIV_SYSTEM_NES: + w->writeC(0xb4); + w->writeC(0x15); + w->writeC(0); + break; + case DIV_SYSTEM_ARCADE: + case DIV_SYSTEM_YM2151: + for (int i=0; i<8; i++) { + w->writeC(0x54); + w->writeC(0xe0+i); + w->writeC(0xff); + w->writeC(0x54); + w->writeC(0xe8+i); + w->writeC(0xff); + w->writeC(0x54); + w->writeC(0xf0+i); + w->writeC(0xff); + w->writeC(0x54); + w->writeC(0xf8+i); + w->writeC(0xff); + + w->writeC(0x54); + w->writeC(0x08); + w->writeC(i); + } + if (sys==DIV_SYSTEM_ARCADE) { + for (int i=0; i<5; i++) { + w->writeC(0xc0); + w->writeS(0x86+(i<<3)); + w->writeC(3); + } + } + break; + case DIV_SYSTEM_YM2610: + case DIV_SYSTEM_YM2610_EXT: + for (int i=0; i<2; i++) { // set SL and RR to highest + w->writeC(0x58); + w->writeC(0x81+i); + w->writeC(0xff); + w->writeC(0x58); + w->writeC(0x85+i); + w->writeC(0xff); + w->writeC(0x58); + w->writeC(0x89+i); + w->writeC(0xff); + w->writeC(0x58); + w->writeC(0x8d+i); + w->writeC(0xff); + + w->writeC(0x59); + w->writeC(0x81+i); + w->writeC(0xff); + w->writeC(0x59); + w->writeC(0x85+i); + w->writeC(0xff); + w->writeC(0x59); + w->writeC(0x89+i); + w->writeC(0xff); + w->writeC(0x59); + w->writeC(0x8d+i); + w->writeC(0xff); + } + for (int i=0; i<2; i++) { // note off + w->writeC(0x58); + w->writeC(0x28); + w->writeC(1+i); + w->writeC(0x58); + w->writeC(0x28); + w->writeC(5+i); + } + + // reset AY + w->writeC(0x58); + w->writeC(7); + w->writeC(0x3f); + + w->writeC(0x58); + w->writeC(8); + w->writeC(0); + + w->writeC(0x58); + w->writeC(9); + w->writeC(0); + + w->writeC(0x58); + w->writeC(10); + w->writeC(0); + + // reset sample + w->writeC(0x59); + w->writeC(0); + w->writeC(0xbf); + break; + case DIV_SYSTEM_AY8910: + w->writeC(0xa0); + w->writeC(7); + w->writeC(0x3f); + + w->writeC(0xa0); + w->writeC(8); + w->writeC(0); + + w->writeC(0xa0); + w->writeC(9); + w->writeC(0); + + w->writeC(0xa0); + w->writeC(10); + w->writeC(0); + break; + case DIV_SYSTEM_AY8930: + w->writeC(0xa0); + w->writeC(0x0d); + w->writeC(0); + w->writeC(0xa0); + w->writeC(0x0d); + w->writeC(0xa0); + break; + case DIV_SYSTEM_SAA1099: + w->writeC(0xbd); + w->writeC(0x1c); + w->writeC(0x02); + w->writeC(0xbd); + w->writeC(0x14); + w->writeC(0); + w->writeC(0xbd); + w->writeC(0x15); + w->writeC(0); + + for (int i=0; i<6; i++) { + w->writeC(0xbd); + w->writeC(i); + w->writeC(0); + } + break; + default: + break; + } + } if (write.addr>=0xffff0000) { // Furnace special command unsigned char streamID=streamOff+((write.addr&0xff00)>>8); switch (write.addr&0xff) { @@ -1996,6 +2282,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write } break; case DIV_SYSTEM_YM2610: + case DIV_SYSTEM_YM2610_EXT: switch (write.addr>>8) { case 0: // port 0 w->writeC(0x58); @@ -2086,6 +2373,8 @@ SafeWriter* DivEngine::saveVGM() { bool done=false; int writeCount=0; + int gd3Off=0; + int hasSN=0; int snNoiseConfig=9; int snNoiseSize=16; @@ -2542,7 +2831,15 @@ SafeWriter* DivEngine::saveVGM() { writeLoop=true; } } - if (nextTick()) done=true; + if (nextTick()) { + done=true; + // stop all streams + for (int i=0; iwriteC(0x94); + w->writeC(i); + loopSample[i]=-1; + } + } // get register dumps for (int i=0; i& writes=disCont[i].dispatch->getRegisterWrites(); @@ -2625,11 +2922,48 @@ SafeWriter* DivEngine::saveVGM() { disCont[i].dispatch->toggleRegisterDump(false); } + // write GD3 tag + gd3Off=w->tell(); + w->write("Gd3 ",4); + w->writeI(0x100); + w->writeI(0); // length. will be written later + + WString ws; + ws=utf8To16(song.name.c_str()); + w->writeWString(ws,false); // name + w->writeS(0); // japanese name + w->writeS(0); // game name + w->writeS(0); // japanese game name + if (song.systemLen>1) { + ws=L"Multiple Systems"; + } else { + ws=utf8To16(getSystemName(song.system[0])); + } + w->writeWString(ws,false); // system name + if (song.systemLen>1) { + ws=L"複数システム"; + } else { + ws=utf8To16(getSystemNameJ(song.system[0])); + } + w->writeWString(ws,false); // japanese system name + ws=utf8To16(song.author.c_str()); + w->writeWString(ws,false); // author name + w->writeS(0); // japanese author name + w->writeS(0); // date + w->writeWString(L"Furnace Tracker",false); // ripper + w->writeS(0); // notes + + int gd3Len=w->tell()-gd3Off-12; + + w->seek(gd3Off+8,SEEK_SET); + w->writeI(gd3Len); + // finish file size_t len=w->size()-4; w->seek(4,SEEK_SET); w->writeI(len); - w->seek(0x18,SEEK_SET); + w->seek(0x14,SEEK_SET); + w->writeI(gd3Off-0x14); w->writeI(tickCount); // loop not handled for now printf("writing loop pos: %d\n",loopPos-0x1c); diff --git a/src/engine/engine.h b/src/engine/engine.h index eee89cf64..3ce39529f 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -303,6 +303,9 @@ class DivEngine { // get sys name const char* getSystemName(DivSystem sys); + + // get japanese system name + const char* getSystemNameJ(DivSystem sys); // convert sample rate format int fileToDivRate(int frate); diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 6070dc537..b3766fbab 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -556,6 +556,9 @@ void DivPlatformArcade::reset() { memset(&fm,0,sizeof(opm_t)); OPM_Reset(&fm); } + if (dumpWrites) { + addWrite(0xffffffff,0); + } for (int i=0; i<13; i++) { chan[i]=DivPlatformArcade::Channel(); chan[i].vol=0x7f; diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 32a923aff..5aba42992 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -297,6 +297,9 @@ void DivPlatformAY8910::reset() { chan[i]=DivPlatformAY8910::Channel(); chan[i].vol=0x0f; } + if (dumpWrites) { + addWrite(0xffffffff,0); + } for (int i=0; i<16; i++) { oldWrites[i]=-1; diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 8010fb8a3..cd7523273 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -339,6 +339,9 @@ void DivPlatformAY8930::reset() { ayEnvSlide[i]=0; ayEnvSlideLow[i]=0; } + if (dumpWrites) { + addWrite(0xffffffff,0); + } for (int i=0; i<32; i++) { oldWrites[i]=-1; diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 04beb3c1f..50d2ce812 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -305,6 +305,9 @@ void DivPlatformGB::reset() { for (int i=0; i<4; i++) { chan[i]=DivPlatformGB::Channel(); } + if (dumpWrites) { + addWrite(0xffffffff,0); + } memset(gb,0,sizeof(GB_gameboy_t)); gb->model=GB_MODEL_DMG_B; GB_apu_init(gb); diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index ee6f01b95..7602823ab 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -538,6 +538,7 @@ void DivPlatformGenesis::forceIns() { rWrite(0x2b,0x80); } immWrite(0x22,lfoValue); + psg.forceIns(); } void DivPlatformGenesis::toggleRegisterDump(bool enable) { @@ -548,6 +549,9 @@ void DivPlatformGenesis::toggleRegisterDump(bool enable) { void DivPlatformGenesis::reset() { while (!writes.empty()) writes.pop(); OPN2_Reset(&fm); + if (dumpWrites) { + addWrite(0xffffffff,0); + } for (int i=0; i<10; i++) { chan[i]=DivPlatformGenesis::Channel(); chan[i].vol=0x7f; @@ -576,6 +580,7 @@ void DivPlatformGenesis::reset() { // PSG psg.reset(); + psg.getRegisterWrites().clear(); psgClocks=0; psgOut=0; } diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 582b111c1..64620d3ff 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -348,6 +348,9 @@ void DivPlatformNES::reset() { for (int i=0; i<5; i++) { chan[i]=DivPlatformNES::Channel(); } + if (dumpWrites) { + addWrite(0xffffffff,0); + } dacPeriod=0; dacPos=0; diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index c0e820702..fb3345ec2 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -345,6 +345,9 @@ void DivPlatformPCE::reset() { for (int i=0; i<6; i++) { chan[i]=DivPlatformPCE::Channel(); } + if (dumpWrites) { + addWrite(0xffffffff,0); + } pce->Power(0); lastPan=0xff; memset(tempL,0,32*sizeof(int)); diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index 4455ff4b4..ce0c0d9ae 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -262,6 +262,9 @@ void DivPlatformSAA1099::reset() { chan[i]=DivPlatformSAA1099::Channel(); chan[i].vol=0x0f; } + if (dumpWrites) { + addWrite(0xffffffff,0); + } lastBusy=60; dacMode=0; diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index ec0286da4..eda4c8fb1 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -191,6 +191,9 @@ void DivPlatformSMS::reset() { for (int i=0; i<4; i++) { chan[i]=DivPlatformSMS::Channel(); } + if (dumpWrites) { + addWrite(0xffffffff,0); + } sn->device_start(); snNoiseMode=3; updateSNMode=false; diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index cdce9e8f2..864f2f511 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -572,6 +572,9 @@ void DivPlatformYM2610::forceIns() { void DivPlatformYM2610::reset() { while (!writes.empty()) writes.pop(); + if (dumpWrites) { + addWrite(0xffffffff,0); + } fm->reset(); for (int i=0; i<13; i++) { chan[i]=DivPlatformYM2610::Channel(); diff --git a/src/engine/safeWriter.cpp b/src/engine/safeWriter.cpp index 400b568b7..80e4003dd 100644 --- a/src/engine/safeWriter.cpp +++ b/src/engine/safeWriter.cpp @@ -82,6 +82,21 @@ int SafeWriter::writeString(String val, bool pascal) { return write(val.c_str(),val.size()+1); } } +int SafeWriter::writeWString(WString val, bool pascal) { + if (pascal) { + writeS((unsigned short)val.size()); + for (wchar_t& i: val) { + writeS(i); + } + return 2+val.size()*2; + } else { + for (wchar_t& i: val) { + writeS(i); + } + writeS(0); + return 2+val.size()*2; + } +} void SafeWriter::init() { if (operative) return; diff --git a/src/engine/safeWriter.h b/src/engine/safeWriter.h index 8f62ce368..17249d38f 100644 --- a/src/engine/safeWriter.h +++ b/src/engine/safeWriter.h @@ -36,6 +36,7 @@ class SafeWriter { int writeF_BE(float val); int writeD(double val); int writeD_BE(double val); + int writeWString(WString val, bool pascal); int writeString(String val, bool pascal); void init(); diff --git a/src/ta-utils.h b/src/ta-utils.h index 5035b3916..a1e2441c1 100644 --- a/src/ta-utils.h +++ b/src/ta-utils.h @@ -14,9 +14,7 @@ typedef std::string String; #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) -#ifdef _WIN32 typedef std::wstring WString; -#endif struct TAParam { String shortName; diff --git a/src/utfutils.cpp b/src/utfutils.cpp index 06650fc32..348ac978d 100644 --- a/src/utfutils.cpp +++ b/src/utfutils.cpp @@ -87,7 +87,6 @@ String utf16To8(const wchar_t* s) { ret+=(0xe0+((s[i]>>12)&15)); ret+=(0x80+((s[i]>>6)&63)); ret+=(0x80+((s[i])&63)); - } } return ret;