diff --git a/src/engine/engine.h b/src/engine/engine.h index 4d75dfa93..c8f539e93 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -305,7 +305,7 @@ class DivEngine { // specify system to build ROM for. SafeWriter* buildROM(int sys); // dump to VGM. - SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true); + SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171); // export to an audio file bool saveAudio(const char* path, int loops, DivAudioExportModes mode); // wait for audio export to finish @@ -317,8 +317,8 @@ class DivEngine { // notify wavetable change void notifyWaveChange(int wave); - // returns whether a system is VGM compatible - bool isVGMExportable(DivSystem which); + // returns the minimum VGM version which may carry the specified system, or 0 if none. + int minVGMVersion(DivSystem which); // save config bool saveConf(); diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index e16a55104..58a932605 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1670,41 +1670,44 @@ DivInstrumentType DivEngine::getPreferInsType(int chan) { return DIV_INS_FM; } -bool DivEngine::isVGMExportable(DivSystem which) { +int DivEngine::minVGMVersion(DivSystem which) { switch (which) { - case DIV_SYSTEM_SMS: - case DIV_SYSTEM_GB: - case DIV_SYSTEM_PCE: - case DIV_SYSTEM_NES: - case DIV_SYSTEM_YM2151: case DIV_SYSTEM_YM2612: case DIV_SYSTEM_YM2612_EXT: + case DIV_SYSTEM_SMS: + case DIV_SYSTEM_OPLL: + case DIV_SYSTEM_OPLL_DRUMS: + case DIV_SYSTEM_VRC7: + case DIV_SYSTEM_YM2151: + return 0x150; // due to usage of data blocks + case DIV_SYSTEM_SEGAPCM: + case DIV_SYSTEM_SEGAPCM_COMPAT: case DIV_SYSTEM_YM2610: case DIV_SYSTEM_YM2610_EXT: case DIV_SYSTEM_YM2610_FULL: case DIV_SYSTEM_YM2610_FULL_EXT: case DIV_SYSTEM_YM2610B: case DIV_SYSTEM_YM2610B_EXT: - case DIV_SYSTEM_AY8910: - case DIV_SYSTEM_AY8930: - case DIV_SYSTEM_SAA1099: - case DIV_SYSTEM_QSOUND: - case DIV_SYSTEM_SEGAPCM: - case DIV_SYSTEM_SEGAPCM_COMPAT: - case DIV_SYSTEM_OPLL: - case DIV_SYSTEM_OPLL_DRUMS: - case DIV_SYSTEM_VRC7: - case DIV_SYSTEM_X1_010: - case DIV_SYSTEM_SWAN: case DIV_SYSTEM_OPL: case DIV_SYSTEM_OPL_DRUMS: case DIV_SYSTEM_OPL2: case DIV_SYSTEM_OPL2_DRUMS: case DIV_SYSTEM_OPL3: case DIV_SYSTEM_OPL3_DRUMS: - return true; + case DIV_SYSTEM_AY8910: + case DIV_SYSTEM_AY8930: + return 0x151; + case DIV_SYSTEM_GB: + case DIV_SYSTEM_PCE: + case DIV_SYSTEM_NES: + case DIV_SYSTEM_QSOUND: + return 0x161; + case DIV_SYSTEM_SAA1099: + case DIV_SYSTEM_X1_010: + case DIV_SYSTEM_SWAN: + return 0x171; default: - return false; + return 0; } - return false; + return 0; } diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index c5fa63f3d..d06bb195f 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -592,7 +592,11 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write } } -SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) { +SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { + if (version<0x150) { + lastError="VGM version is too low"; + return NULL; + } stop(); repeatPattern=false; setOrder(0); @@ -679,7 +683,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) { // write header w->write("Vgm ",4); w->writeI(0); // will be written later - w->writeI(0x171); // VGM 1.71 + w->writeI(version); bool willExport[32]; bool isSecond[32]; @@ -709,6 +713,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) { if (sysToExport!=NULL) { if (!sysToExport[i]) continue; } + if (minVGMVersion(song.system[i])>version) continue; switch (song.system[i]) { case DIV_SYSTEM_SMS: if (!hasSN) { @@ -979,66 +984,138 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) { w->writeI(0); // tick rate w->writeS(snNoiseConfig); w->writeC(snNoiseSize); - w->writeC(snFlags); + if (version>=0x151) { + w->writeC(snFlags); + } else { + w->writeC(0); + } w->writeI(hasOPN2); w->writeI(hasOPM); w->writeI(0); // data pointer. will be written later - w->writeI(hasSegaPCM); - w->writeI(segaPCMOffset); - w->writeI(hasRFC); - w->writeI(hasOPN); - w->writeI(hasOPNA); - w->writeI(hasOPNB); - w->writeI(hasOPL2); - w->writeI(hasOPL); - w->writeI(hasY8950); - w->writeI(hasOPL3); - w->writeI(hasOPL4); - w->writeI(hasOPX); - w->writeI(hasZ280); - w->writeI(hasRFC1); - w->writeI(hasPWM); - w->writeI(hasAY); - w->writeC(ayConfig); - w->writeC(ayFlags); - w->writeC(ayFlags); // OPN - w->writeC(ayFlags); // OPNA + if (version>=0x151) { + w->writeI(hasSegaPCM); + w->writeI(segaPCMOffset); + w->writeI(hasRFC); + w->writeI(hasOPN); + w->writeI(hasOPNA); + w->writeI(hasOPNB); + w->writeI(hasOPL2); + w->writeI(hasOPL); + w->writeI(hasY8950); + w->writeI(hasOPL3); + w->writeI(hasOPL4); + w->writeI(hasOPX); + w->writeI(hasZ280); + w->writeI(hasRFC1); + w->writeI(hasPWM); + w->writeI(hasAY); + w->writeC(ayConfig); + w->writeC(ayFlags); + w->writeC(ayFlags); // OPN + w->writeC(ayFlags); // OPNA + } else { + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeC(0); + w->writeC(0); + w->writeC(0); // OPN + w->writeC(0); // OPNA + } + // currently not used but is part of 1.60 w->writeC(0); // volume w->writeC(0); // reserved w->writeC(0); // loop count + // 1.51 w->writeC(0); // loop modifier - w->writeI(hasGB); - w->writeI(hasNES); - w->writeI(hasMultiPCM); - w->writeI(hasuPD7759); - w->writeI(hasOKIM6258); - w->writeC(0); // flags - w->writeC(0); // K flags - w->writeC(0); // C140 chip type - w->writeC(0); // reserved - w->writeI(hasOKIM6295); - w->writeI(hasK051649); - w->writeI(hasK054539); - w->writeI(hasPCE); - w->writeI(hasNamco); - w->writeI(hasK053260); - w->writeI(hasPOKEY); - w->writeI(hasQSound); - w->writeI(hasSCSP); + + if (version>=0x161) { + w->writeI(hasGB); + w->writeI(hasNES); + w->writeI(hasMultiPCM); + w->writeI(hasuPD7759); + w->writeI(hasOKIM6258); + w->writeC(0); // flags + w->writeC(0); // K flags + w->writeC(0); // C140 chip type + w->writeC(0); // reserved + w->writeI(hasOKIM6295); + w->writeI(hasK051649); + w->writeI(hasK054539); + w->writeI(hasPCE); + w->writeI(hasNamco); + w->writeI(hasK053260); + w->writeI(hasPOKEY); + w->writeI(hasQSound); + } else { + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeC(0); // flags + w->writeC(0); // K flags + w->writeC(0); // C140 chip type + w->writeC(0); // reserved + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + } + if (version>=0x171) { + w->writeI(hasSCSP); + } else { + w->writeI(0); + } + // 1.70 w->writeI(0); // extra header - w->writeI(hasSwan); - w->writeI(hasVSU); - w->writeI(hasSAA); - w->writeI(hasES5503); - w->writeI(hasES5505); - w->writeC(0); // 5503 chans - w->writeC(0); // 5505 chans - w->writeC(0); // C352 clock divider - w->writeC(0); // reserved - w->writeI(hasX1); - w->writeI(hasC352); - w->writeI(hasGA20); - w->writeI(hasLynx); + // 1.71 + if (version>=0x171) { + w->writeI(hasSwan); + w->writeI(hasVSU); + w->writeI(hasSAA); + w->writeI(hasES5503); + w->writeI(hasES5505); + w->writeC(0); // 5503 chans + w->writeC(0); // 5505 chans + w->writeC(0); // C352 clock divider + w->writeC(0); // reserved + w->writeI(hasX1); + w->writeI(hasC352); + w->writeI(hasGA20); + w->writeI(hasLynx); + } else { + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeC(0); // 5503 chans + w->writeC(0); // 5505 chans + w->writeC(0); // C352 clock divider + w->writeC(0); // reserved + w->writeI(0); + w->writeI(0); + w->writeI(0); + w->writeI(0); + } for (int i=0; i<6; i++) { // reserved w->writeI(0); } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index e8a40a664..56e30acdb 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2131,14 +2131,27 @@ bool FurnaceGUI::loop() { } if (ImGui::BeginMenu("export VGM...")) { ImGui::Text("settings:"); + if (ImGui::BeginCombo("format version",fmt::sprintf("%d.%.2x",vgmExportVersion>>8,vgmExportVersion&0xff).c_str())) { + for (int i=0; i<6; i++) { + if (ImGui::Selectable(fmt::sprintf("%d.%.2x",vgmVersions[i]>>8,vgmVersions[i]&0xff).c_str(),vgmExportVersion==vgmVersions[i])) { + vgmExportVersion=vgmVersions[i]; + } + } + ImGui::EndCombo(); + } ImGui::Checkbox("loop",&vgmExportLoop); - ImGui::Text("systems to export:");; + ImGui::Text("systems to export:"); bool hasOneAtLeast=false; for (int i=0; isong.systemLen; i++) { - ImGui::BeginDisabled(!e->isVGMExportable(e->song.system[i])); + int minVersion=e->minVGMVersion(e->song.system[i]); + ImGui::BeginDisabled(minVersion>vgmExportVersion || minVersion==0); ImGui::Checkbox(fmt::sprintf("%d. %s##_SYSV%d",i+1,getSystemName(e->song.system[i]),i).c_str(),&willExport[i]); ImGui::EndDisabled(); - if (!e->isVGMExportable(e->song.system[i])) { + if (minVersion>vgmExportVersion) { + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + ImGui::SetTooltip("this system is only available in VGM %d.%.2x and higher!",minVersion>>8,minVersion&0xff); + } + } else if (minVersion==0) { if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { ImGui::SetTooltip("this system is not supported by the VGM format!"); } @@ -2147,7 +2160,7 @@ bool FurnaceGUI::loop() { } } ImGui::Text("select the systems you wish to export,"); - ImGui::Text("but only up to 2 of each type."); + ImGui::Text("but only up to %d of each type.",(vgmExportVersion>=0x151)?2:1); if (hasOneAtLeast) { if (ImGui::MenuItem("click to export")) { openFileDialog(GUI_FILE_EXPORT_VGM); @@ -2504,7 +2517,7 @@ bool FurnaceGUI::loop() { MARK_MODIFIED; break; case GUI_FILE_EXPORT_VGM: { - SafeWriter* w=e->saveVGM(willExport,vgmExportLoop); + SafeWriter* w=e->saveVGM(willExport,vgmExportLoop,vgmExportVersion); if (w!=NULL) { FILE* f=fopen(copyOfName.c_str(),"wb"); if (f!=NULL) { @@ -2519,7 +2532,7 @@ bool FurnaceGUI::loop() { showWarning(e->getWarnings(),GUI_WARN_GENERIC); } } else { - showError("could not write VGM. dang it."); + showError(fmt::sprintf("could not write VGM! (%s)",e->getLastError())); } break; } @@ -2918,6 +2931,7 @@ FurnaceGUI::FurnaceGUI(): displayExporting(false), vgmExportLoop(true), displayNew(false), + vgmExportVersion(0x171), curFileDialog(GUI_FILE_OPEN), warnAction(GUI_WARN_OPEN), fileDialog(NULL), diff --git a/src/gui/gui.h b/src/gui/gui.h index bf2050f0c..e23be9902 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -643,6 +643,7 @@ class FurnaceGUI { bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop; bool displayNew; bool willExport[32]; + int vgmExportVersion; FurnaceGUIFileDialogs curFileDialog; FurnaceGUIWarnings warnAction; diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 01c603739..20085cfeb 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -70,6 +70,15 @@ const int altValues[24]={ 0, 10, 1, 11, 2, 3, 12, 4, 13, 5, 14, 6, 7, 15, 8, -1, 9, -1, -1, -1, -1, -1, -1, -1 }; +const int vgmVersions[6]={ + 0x150, + 0x151, + 0x160, + 0x161, + 0x170, + 0x171 +}; + const char* insTypes[DIV_INS_MAX]={ "Standard", "FM (4-operator)", diff --git a/src/gui/guiConst.h b/src/gui/guiConst.h index 6b55df864..dfafea88c 100644 --- a/src/gui/guiConst.h +++ b/src/gui/guiConst.h @@ -28,4 +28,5 @@ extern const char* sampleDepths[17]; extern const char* resampleStrats[]; extern const int availableSystems[]; extern const char* guiActions[][2]; -extern const int altValues[24]; \ No newline at end of file +extern const int altValues[24]; +extern const int vgmVersions[6]; \ No newline at end of file