mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-25 22:15:14 +00:00
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
This commit is contained in:
parent
c7ee0ce642
commit
8bcab6e139
17 changed files with 388 additions and 6 deletions
|
@ -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()
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "safeReader.h"
|
||||
#include "../ta-log.h"
|
||||
#include "../fileutils.h"
|
||||
#include "../utfutils.h"
|
||||
#include "../audio/sdl.h"
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
|
@ -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; i<streamID; i++) {
|
||||
w->writeC(0x94);
|
||||
w->writeC(i);
|
||||
loopSample[i]=-1;
|
||||
}
|
||||
}
|
||||
// get register dumps
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
std::vector<DivRegWrite>& 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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -262,6 +262,9 @@ void DivPlatformSAA1099::reset() {
|
|||
chan[i]=DivPlatformSAA1099::Channel();
|
||||
chan[i].vol=0x0f;
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
|
||||
lastBusy=60;
|
||||
dacMode=0;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue