diff --git a/papers/format.md b/papers/format.md index da06488fa..acedd9945 100644 --- a/papers/format.md +++ b/papers/format.md @@ -32,6 +32,7 @@ these fields are 0 in format versions prior to 100 (0.6pre1). the format versions are: +- 115: Furnace dev115 - 114: Furnace dev114 - 113: Furnace dev113 - 112: Furnace dev112 @@ -342,7 +343,9 @@ size | description 1 | SN periods under 8 are treated as 1 (>=108) or reserved 1 | cut/delay effect policy (>=110) or reserved 1 | 0B/0D effect treatment (>=113) or reserved - 4 | reserved + 1 | automatic system name detection (>=115) or reserved + | - this one isn't a compatibility flag, but it's here for convenience... + 3 | reserved --- | **virtual tempo data** 2 | virtual tempo numerator of first song (>=96) or reserved 2 | virtual tempo denominator of first song (>=96) or reserved @@ -499,7 +502,11 @@ size | description 1 | ws 1 | ksr 1 | operator enabled (>=114) or reserved - 11 | reserved + 1 | KVS mode (>=115) or reserved + | - 0: off + | - 1: on + | - 2: auto (depending on alg) + 10 | reserved --- | **Game Boy instrument data** 1 | volume 1 | direction diff --git a/src/engine/engine.h b/src/engine/engine.h index 3047a8af7..40bbbbb90 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -46,8 +46,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev114" -#define DIV_ENGINE_VERSION 114 +#define DIV_VERSION "dev115" +#define DIV_ENGINE_VERSION 115 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index aac771e09..4c213744b 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -1085,6 +1085,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<113) { ds.jumpTreatment=1; } + if (ds.version<115) { + ds.autoSystem=false; + } ds.isDMF=false; reader.readS(); // reserved @@ -1512,7 +1515,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - for (int i=0; i<4; i++) { + if (ds.version>=115) { + ds.autoSystem=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<3; i++) { reader.readC(); } } @@ -1549,6 +1557,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { ds.categoryJ=reader.readString(); } else { ds.systemName=getSongSystemLegacyName(ds,!getConfInt("noMultiSystem",0)); + ds.autoSystem=true; } // read subsongs @@ -3751,7 +3760,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeC(song.snNoLowPeriods); w->writeC(song.delayBehavior); w->writeC(song.jumpTreatment); - for (int i=0; i<4; i++) { + w->writeC(song.autoSystem); + for (int i=0; i<3; i++) { w->writeC(0); } diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 22cdddf4b..3296994ba 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -72,9 +72,10 @@ void DivInstrument::putInsData(SafeWriter* w) { w->writeC(op.ksr); w->writeC(op.enable); + w->writeC(op.kvs); // reserved - for (int k=0; k<11; k++) { + for (int k=0; k<10; k++) { w->writeC(0); } } @@ -724,8 +725,15 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { reader.readC(); } + if (version>=115) { + op.kvs=reader.readC(); + } else { + op.kvs=2; + reader.readC(); + } + // reserved - for (int k=0; k<11; k++) reader.readC(); + for (int k=0; k<10; k++) reader.readC(); } // GB diff --git a/src/engine/instrument.h b/src/engine/instrument.h index e5372933d..5652301f4 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -87,6 +87,7 @@ struct DivInstrumentFM { bool enable; unsigned char am, ar, dr, mult, rr, sl, tl, dt2, rs, dt, d2r, ssgEnv; unsigned char dam, dvb, egt, ksl, sus, vib, ws, ksr; // YMU759/OPL/OPZ + unsigned char kvs; Operator(): enable(true), am(0), @@ -108,7 +109,8 @@ struct DivInstrumentFM { sus(0), vib(0), ws(0), - ksr(0) {} + ksr(0), + kvs(2) {} } op[4]; DivInstrumentFM(): alg(0), diff --git a/src/engine/song.h b/src/engine/song.h index 493fc4125..09a89378c 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -511,6 +511,7 @@ struct DivSong { bool e1e2StopOnSameNote; bool brokenPortaArp; bool snNoLowPeriods; + bool autoSystem; std::vector ins; std::vector wave; @@ -614,7 +615,8 @@ struct DivSong { brokenOutVol(false), e1e2StopOnSameNote(false), brokenPortaArp(false), - snNoLowPeriods(false) { + snNoLowPeriods(false), + autoSystem(true) { for (int i=0; i<32; i++) { system[i]=DIV_SYSTEM_NULL; systemVol[i]=64; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index a2eb9c920..2e566bb1a 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -584,6 +584,78 @@ void FurnaceGUI::updateWindowTitle() { if (sdlWin!=NULL) SDL_SetWindowTitle(sdlWin,title.c_str()); } +void FurnaceGUI::autoDetectSystem() { + std::map sysCountMap; + for (int i=0; isong.systemLen; i++) { + try { + sysCountMap.at(e->song.system[i])++; + } catch (std::exception& ex) { + sysCountMap[e->song.system[i]]=1; + } + } + + logV("sysCountMap:"); + for (std::pair k: sysCountMap) { + logV("%s: %d",e->getSystemName(k.first),k.second); + } + + bool isMatch=false; + std::map defCountMap; + for (FurnaceGUISysCategory& i: sysCategories) { + for (FurnaceGUISysDef& j: i.systems) { + defCountMap.clear(); + for (size_t k=0; k k: defCountMap) { + logV("- %s: %d",e->getSystemName(k.first),k.second); + } + for (std::pair k: defCountMap) { + try { + if (sysCountMap.at(k.first)!=k.second) { + isMatch=false; + break; + } + } catch (std::exception& ex) { + isMatch=false; + break; + } + } + if (isMatch) { + logV("match found!"); + e->song.systemName=j.name; + break; + } + } + if (isMatch) break; + } + + if (!isMatch) { + bool isFirst=true; + e->song.systemName=""; + for (std::pair k: sysCountMap) { + if (!isFirst) e->song.systemName+=" + "; + if (k.second>1) { + e->song.systemName+=fmt::sprintf("%d×",k.second); + } + if (k.first==DIV_SYSTEM_N163) { + e->song.systemName+=settings.c163Name; + } else { + e->song.systemName+=e->getSystemName(k.first); + } + isFirst=false; + } + } +} + ImVec4 FurnaceGUI::channelColor(int ch) { switch (settings.channelColors) { case 0: @@ -3262,6 +3334,9 @@ bool FurnaceGUI::loop() { showError("cannot add chip! ("+e->getLastError()+")"); } ImGui::CloseCurrentPopup(); + if (e->song.autoSystem) { + autoDetectSystem(); + } updateWindowTitle(); } ImGui::EndMenu(); @@ -3282,6 +3357,9 @@ bool FurnaceGUI::loop() { DivSystem picked=systemPicker(); if (picked!=DIV_SYSTEM_NULL) { e->changeSystem(i,picked,preserveChanPos); + if (e->song.autoSystem) { + autoDetectSystem(); + } updateWindowTitle(); ImGui::CloseCurrentPopup(); } @@ -3297,6 +3375,10 @@ bool FurnaceGUI::loop() { if (!e->removeSystem(i,preserveChanPos)) { showError("cannot remove chip! ("+e->getLastError()+")"); } + if (e->song.autoSystem) { + autoDetectSystem(); + updateWindowTitle(); + } } } ImGui::EndMenu(); @@ -4415,6 +4497,10 @@ bool FurnaceGUI::loop() { case GUI_WARN_SYSTEM_DEL: if (ImGui::Button("Yes")) { e->removeSystem(sysToDelete,preserveChanPos); + if (e->song.autoSystem) { + autoDetectSystem(); + updateWindowTitle(); + } ImGui::CloseCurrentPopup(); } ImGui::SameLine(); @@ -5069,6 +5155,7 @@ FurnaceGUI::FurnaceGUI(): macroPointSize(16), waveEditStyle(0), mobileMenuPos(0.0f), + autoButtonSize(0.0f), curSysSection(NULL), pendingRawSampleDepth(8), pendingRawSampleChannels(1), diff --git a/src/gui/gui.h b/src/gui/gui.h index 5bf8adfe7..c44d4c122 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1018,7 +1018,7 @@ class FurnaceGUI { int drawHalt; int macroPointSize; int waveEditStyle; - float mobileMenuPos; + float mobileMenuPos, autoButtonSize; const int* curSysSection; String pendingRawSample; @@ -1610,6 +1610,7 @@ class FurnaceGUI { bool CWVSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format="%d", ImGuiSliderFlags flags=0); void updateWindowTitle(); + void autoDetectSystem(); void prepareLayout(); ImVec4 channelColor(int ch); ImVec4 channelTextColor(int ch); diff --git a/src/gui/songInfo.cpp b/src/gui/songInfo.cpp index 44982abaf..5b113c641 100644 --- a/src/gui/songInfo.cpp +++ b/src/gui/songInfo.cpp @@ -77,11 +77,23 @@ void FurnaceGUI::drawSongInfo() { ImGui::TableNextColumn(); ImGui::Text("System"); ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); + ImGui::SetNextItemWidth(MAX(16.0f*dpiScale,avail-autoButtonSize-ImGui::GetStyle().ItemSpacing.x)); if (ImGui::InputText("##SystemName",&e->song.systemName)) { MARK_MODIFIED; updateWindowTitle(); + e->song.autoSystem=false; } + ImGui::SameLine(); + pushToggleColors(e->song.autoSystem); + if (ImGui::Button("Auto")) { + e->song.autoSystem=!e->song.autoSystem; + if (e->song.autoSystem) { + autoDetectSystem(); + updateWindowTitle(); + } + } + popToggleColors(); + autoButtonSize=ImGui::GetItemRectSize().x; ImGui::EndTable(); } diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 3a0c1098e..900845a69 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -765,6 +765,9 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool if (copyOfFlags!=flags) { if (chan>=0) { e->setSysFlags(chan,copyOfFlags,restart); + if (e->song.autoSystem) { + autoDetectSystem(); + } updateWindowTitle(); } else { flags=copyOfFlags; diff --git a/src/gui/sysManager.cpp b/src/gui/sysManager.cpp index 98f939d0f..386d26583 100644 --- a/src/gui/sysManager.cpp +++ b/src/gui/sysManager.cpp @@ -82,6 +82,9 @@ void FurnaceGUI::drawSysManager() { DivSystem picked=systemPicker(); if (picked!=DIV_SYSTEM_NULL) { e->changeSystem(i,picked,preserveChanPos); + if (e->song.autoSystem) { + autoDetectSystem(); + } updateWindowTitle(); ImGui::CloseCurrentPopup(); } @@ -110,6 +113,9 @@ void FurnaceGUI::drawSysManager() { if (!e->addSystem(picked)) { showError("cannot add chip! ("+e->getLastError()+")"); } + if (e->song.autoSystem) { + autoDetectSystem(); + } updateWindowTitle(); ImGui::CloseCurrentPopup(); }