From 71e66cf8c8f59b44b3a51e5ab3437c48bf4899cc Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 12 Apr 2024 03:30:57 -0500 Subject: [PATCH 01/22] GUI: user presets, part 2 --- src/gui/userPresets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/userPresets.cpp b/src/gui/userPresets.cpp index f78848300..80b8c06a6 100644 --- a/src/gui/userPresets.cpp +++ b/src/gui/userPresets.cpp @@ -177,7 +177,7 @@ bool FurnaceGUI::loadUserPresets(bool redundancy) { } indent>>=1; - if (!key.empty() && !value.empty()) { + if (!key.empty()) { std::vector* where=digDeep(userCategory->systems,indent); where->push_back(FurnaceGUISysDef(key.c_str(),value.c_str(),e)); } From f73380f0c7803719a46c24171ed548ea0c4868f1 Mon Sep 17 00:00:00 2001 From: aloelucidity <106764798+aloelucidity@users.noreply.github.com> Date: Thu, 11 Apr 2024 19:38:14 +0200 Subject: [PATCH 02/22] Infinity (demo song) --- demos/nes/infinity.fur | Bin 0 -> 2803 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 demos/nes/infinity.fur diff --git a/demos/nes/infinity.fur b/demos/nes/infinity.fur new file mode 100644 index 0000000000000000000000000000000000000000..24242d330975bf81a97f24c8e678ac38a7124fd9 GIT binary patch literal 2803 zcmV?AoRg6_(!#XfIlEqg!n@R{{Vp-f%w6nAcE8iiRHUz z?mXwE04Lw zD{i4yFV`lAswFVo59U7|B~24jObB0r5SvY+&jbGedSXNs;75RC5nTlS0Gx~y{Qy{* zAi54{Nuo0F8{l{s(a(TADWX?^e**()qTd34%@Dn36J@%Iz6adjL-aQA+zO)Jl|;V+ z&aZ+E;PPss+rT&15FJ`e^mpJ(>xjD76a5r;Vgu3dfM+)nt;rJY1kM3J1XgS!ssq0V z4)zkg3j754GvK&H{lE*rH1JVGMxD7^~urFxZ5{bs@B~z^|6UUtyy!+IOmrZ+aO+xa*3k82z5oMCqgX| z>WJiQQ9Fd1A=C?@UdU!?2l5W&9mqS7cOdUT-hsRWc?a?iR6kar;OK;D781NpaS z1fT&rU;ri%11um8B!DE)1*Cv9kO6Ffr~&x~{x8<3Z>>Ois#BNjR6FNg)|gjNH6$4)+;RiPr(KGKWYu;PhiS1O&co$j6$D_2RT zq|c;FCP^{;4lppd=Pb@|(PkRxv*;7$Ldo4tr}_@dAR99?vT8DRCapOb>&U$x^PSrw zRT`4sPvTuC$R&?v`Uj-$a-iFwzOlH|sb}GX5@#@eeupN+&B)!?%I4nb!oiIW91=oB|_bQWt z2;?;3ar?F(3zQ~VsjD2=B{j#HSF^pt*Q-grNMjD|DTj8IL;2>Ey2k@`F&Z2#jFqA4 zP{he}Dw9l^8X_ZNo-*{C+;Lz(@CdLE*we<*Gp*U)1Jwtcvuz6v<318IX!%N9<4hkT zaXZXeG}FU@-h|Aw+dp)%-jZ%X?OM!|(_M+gNLt*}VlKwL;hvexvpE~);&h@gHc}tG z0)rp(4PtG?duHfCFFKdyhtRz!-nnx}SHYF!V9A7mbR%}23hbDiAQ;&jF+%PPsnt6a z)z%1I_tJ81u$%lwf=3O-Bzbn^PL58D1&LCHno4%XOdSs>6(MRHp}eGqGdfDbdDm^? zP&0ZyV(#czVf-S@4f#_NWTL&Kx{`^w6*CQ8$0oz!k~ZBX1vH%PW1)HBtX#7`laS3*T9;2rk8Tm3lyao2;y)s1_+YykJ{_2_XmDbzI$DWl z4Ucsv49p`PY9JS6jWZ_klAyyz&d=IJ5XNrRr`T-g$X&OC!$4S-`1jz`co3L z#6CVkVS77zu|8IEy>1>DmD_>jzxXK}8YNl&`g@*HlcMWCjKZ-dqq&IDdSEma8s%la z5VQYTs@YFb_{=&{nDDE`TN@(Ll{5`mC8~*oJ2wE}sggUPpvim00T*PbF7VG@-@vD- zr3LSWjsE6GP$r#DXEJs$g)p6KgLFyy+F%Ot$P_+FVLb*GwnmzKuj_7Fc;|4*ufZ#U z>v3->b2_4nSyP)3Z#Clyr53Zlkh2P#6nYgpUHiby&hnC=x|-7v=(ZIYObJy#Uyp z5MF`^FF>>v*apaV%I&}dz=weyz)oNnAd7uBfDH`4h(82iLqiV(A0clUW1~X{fP=tC zfk%NuYW{75;wn6#gtsfj`<0RdO7TO=iS5eqN7eoP>fTN@fz8VB0fmQ^v%A#<{7LLn zd|TD^Atk>}(V_u~1|u4X)*wU!5S_dm8E;WZIHY_%(|2~@6^;fE( zWg8i@iVq`gq>L^j*>qtfj5rhKVW~-$T9l=>FqRqV+Gp7{RO*XMSF~7<6${&cRh0@^ z)j0E+W$#oB^~^^aZX#7@bLAIK8V<)w7rXf#_7SyhR=hiV*5n9ISf)pw*h!hzsqf5_CsmaZXA5ZU#NVIP;ech@$ z%V4=)_0|fOt5xg17wdH~(_#<4%nJcA!TqF|zGfA>!fYr7#a0Ey`B}O&-kl zG664>xJ(-@x1yUI18X9ZQeCDrwyaaido_Wrx^)4y=lx{VPg?z?yq|2*)^~+0K^B^X zWaQ@7Qfk)XKgpVe*tJP^4Q~)~)pXP;^&Q5XhHd>T5w+D;wd|=?vh68R!Y_&Pl>Eh@ zG9`b@#PlhVXXD>s<4GBp6*gN6lP%L7Bzz;KX1vG*OfoXO;+w=|WaiUTYStrF$Vo)S z;*rMI<;A9h){UCdxG{|Z$0t->ZB^|VE6uH~Z0q%T<#?j(CI6X3lYbAGzNzPpYhs$o z_c0ky50CbRb|+?hQtj`UQLkzwMoAQ>M0E;-j|vV4alslOb- z4LxsyW-`-uW|GZ3ugB$`8;T=(z77NuJ6M8CYZhaxfIPF%J{Hp23PqWz`8w(&v5$!b z`FMmSVk{B6(O&nw*R1ITYvL3xO1Smf*LRBbCGSdK6s+_BD~)?K@Uq9*gS$QIJm)oQ z=w}THnvc1|wb_&}{$gByV-`MzO;deiqI#}XP4^m`%ql*Ntg+G9V61PtFxDAsnJ^Da zO|sOYEVa2<+I?eU#`qhP#VP8*vH*`23)?@<_GMug6Rv8yLHBJl@Y{apL0Ds^{AUBd z|IRg-!=4(j(r>X+tl%lDm|S+-zHkd~qEV<#;Bwo^vy(5flg3h=yr(;P{;Q8^$&FxU z^-Z-q*v;GEj2{T_o8=ZEGe3_O z77KJpakCPkuw!3i$Iwlo0I&6PXv~(nv6Z&7!`-}<_SIHf)p~|Xb7-3F%50gL{{d8l F2Kau_TCxBD literal 0 HcmV?d00001 From 302496cb6d41662c9845da43dae7297cde2d69d0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 12 Apr 2024 14:23:22 -0500 Subject: [PATCH 03/22] start with 5 lives --- src/gui/tutorial.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/tutorial.cpp b/src/gui/tutorial.cpp index eb46ec595..b72fbb074 100644 --- a/src/gui/tutorial.cpp +++ b/src/gui/tutorial.cpp @@ -453,7 +453,7 @@ struct FurnaceCV { newHiScore(false), playSongs(true), pleaseInitSongs(false), - lives(3), + lives(5), respawnTime(0), stage(0), shotType(0), From 8722fe4acb521c6a91aac784b3fce80ffa9b4c2f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 12 Apr 2024 14:38:44 -0500 Subject: [PATCH 04/22] GUI: update credits --- src/gui/about.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 71f88faa8..b19db9d5b 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -71,6 +71,7 @@ const char* aboutLine[]={ "Aburtos", "ActualNK358", "akumanatt", + "aloelucidity", "AmigaX", "AquaDoesStuff", "AURORA*FIELDS", From f2753d6ceb7d5379131dd27913d1037514f66f91 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 12 Apr 2024 17:26:52 -0500 Subject: [PATCH 05/22] GUI: user presets, part 3 working --- .github/workflows/build.yml | 7 +++ src/gui/gui.h | 1 + src/gui/presets.cpp | 17 ++++--- src/gui/userPresets.cpp | 92 ++++++++++++++++++++++++++++++++++++- 4 files changed, 109 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 673e18ff3..fadc87023 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -337,3 +337,10 @@ jobs: with: name: ${{ steps.package-identify.outputs.id }} path: ${{ steps.package-identify.outputs.filename }} + + - name: Upload Metal artifact + if: ${{ github.repository == 'tildearrow/furnace' && github.ref_name == 'metal' }} + uses: actions/upload-artifact@v4.3.0 + with: + name: ${{ steps.package-identify.outputs.id }} + path: ${{ steps.package-identify.outputs.filename }} diff --git a/src/gui/gui.h b/src/gui/gui.h index ef06e3ad7..5a21f028b 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1259,6 +1259,7 @@ struct FurnaceGUISysDef { String definition; std::vector orig; std::vector subDefs; + void bake(); FurnaceGUISysDef(const char* n, std::initializer_list def, const char* e=NULL); FurnaceGUISysDef(const char* n, const char* def, DivEngine* e); }; diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 96912d0c5..f91a45905 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -3102,11 +3102,9 @@ void FurnaceGUI::initSystemPresets() { CATEGORY_END; } -FurnaceGUISysDef::FurnaceGUISysDef(const char* n, std::initializer_list def, const char* e): - name(n), - extra((e==NULL)?"":e) { - orig=def; +void FurnaceGUISysDef::bake() { int index=0; + definition=""; for (FurnaceGUISysDefChip& i: orig) { definition+=fmt::sprintf( "id%d=%d\nvol%d=%f\npan%d=%f\nflags%d=%s\n", @@ -3126,12 +3124,19 @@ FurnaceGUISysDef::FurnaceGUISysDef(const char* n, std::initializer_list def, const char* e): + name(n), + extra((e==NULL)?"":e) { + orig=def; + bake(); +} + FurnaceGUISysDef::FurnaceGUISysDef(const char* n, const char* def, DivEngine* e): name(n), - definition(def) { + definition(taDecodeBase64(def)) { // extract definition DivConfig conf; - conf.loadFromBase64(def); + conf.loadFromMemory(definition.c_str()); for (int i=0; i& entries, int depth) for (int i=0; iname); ImGui::Separator(); - ImGui::Text("the rest..."); + + int doRemove=-1; + bool mustBake=false; + + for (size_t i=0; iorig.size(); i++) { + String tempID; + FurnaceGUISysDefChip& chip=preset->orig[i]; + + bool doInvert=(chip.vol<0); + float vol=fabs(chip.vol); + ImGui::PushID(i); + + tempID=fmt::sprintf("%s##USystem",getSystemName(chip.sys)); + ImGui::Button(tempID.c_str(),ImVec2(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize("Invert").x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0,0)); + if (ImGui::BeginPopupContextItem("SysPickerCU",ImGuiPopupFlags_MouseButtonLeft)) { + DivSystem picked=systemPicker(); + if (picked!=DIV_SYSTEM_NULL) { + chip.sys=picked; + mustBake=true; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + ImGui::SameLine(); + if (ImGui::Checkbox("Invert",&doInvert)) { + chip.vol=-chip.vol; + mustBake=true; + } + ImGui::SameLine(); + pushDestColor(); + if (ImGui::Button(ICON_FA_MINUS "##USysRemove")) { + doRemove=i; + mustBake=true; + } + popDestColor(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0); + if (CWSliderFloat("Volume",&vol,0.0f,3.0f)) { + if (doInvert) { + if (vol<0.0001) vol=0.0001; + } + if (vol<0) vol=0; + if (vol>10) vol=10; + chip.vol=doInvert?-vol:vol; + mustBake=true; + } rightClickable + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0); + if (CWSliderFloat("Panning",&chip.pan,-1.0f,1.0f)) { + if (chip.pan<-1.0f) chip.pan=-1.0f; + if (chip.pan>1.0f) chip.pan=1.0f; + mustBake=true; + } rightClickable + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0); + if (CWSliderFloat("Front/Rear",&chip.panFR,-1.0f,1.0f)) { + if (chip.panFR<-1.0f) chip.panFR=-1.0f; + if (chip.panFR>1.0f) chip.panFR=1.0f; + mustBake=true; + } rightClickable + + if (ImGui::TreeNode("Configure")) { + DivConfig sysFlags; + sysFlags.loadFromBase64(chip.flags.c_str()); + if (drawSysConf(-1,i,chip.sys,sysFlags,false)) { + chip.flags=sysFlags.toBase64(); + mustBake=true; + } + ImGui::TreePop(); + } + + ImGui::PopID(); + } + + if (doRemove>=0) { + preset->orig.erase(preset->orig.begin()+doRemove); + mustBake=true; + } + + ImGui::Button(ICON_FA_PLUS "##SysAddU"); + if (ImGui::BeginPopupContextItem("SysPickerU",ImGuiPopupFlags_MouseButtonLeft)) { + DivSystem picked=systemPicker(); + if (picked!=DIV_SYSTEM_NULL) { + preset->orig.push_back(FurnaceGUISysDefChip(picked,1.0f,0.0f,"")); + mustBake=true; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + if (mustBake) preset->bake(); } } From 809ba42ca60fa821982b0d65f74e6817373647ea Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 12 Apr 2024 17:37:08 -0500 Subject: [PATCH 06/22] GUI: user presets, part 4 rename to User Systems --- src/gui/gui.cpp | 2 +- src/gui/userPresets.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 2f1bb9a8e..0e1a80ec5 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4391,7 +4391,7 @@ bool FurnaceGUI::loop() { toggleMobileUI(!mobileUI); } #endif - if (ImGui::MenuItem("manage presets...",BIND_FOR(GUI_ACTION_WINDOW_USER_PRESETS))) { + if (ImGui::MenuItem("user systems...",BIND_FOR(GUI_ACTION_WINDOW_USER_PRESETS))) { userPresetsOpen=true; } if (ImGui::MenuItem("settings...",BIND_FOR(GUI_ACTION_WINDOW_SETTINGS))) { diff --git a/src/gui/userPresets.cpp b/src/gui/userPresets.cpp index 99c3c084d..62783cd6b 100644 --- a/src/gui/userPresets.cpp +++ b/src/gui/userPresets.cpp @@ -322,7 +322,7 @@ void FurnaceGUI::drawUserPresets() { nextWindow=GUI_WINDOW_NOTHING; } if (!userPresetsOpen) return; - if (ImGui::Begin("User Presets",&userPresetsOpen,globalWinFlags)) { + if (ImGui::Begin("User Systems",&userPresetsOpen,globalWinFlags)) { FurnaceGUISysCategory* userCategory=NULL; for (FurnaceGUISysCategory& i: sysCategories) { if (strcmp(i.name,"User")==0) { From e0c575042e80c082c8af4d67e15f409f80dce550 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 13 Apr 2024 14:09:36 -0500 Subject: [PATCH 07/22] GUI: fix crash when using MinMod in init pres conf --- src/gui/sysConf.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 324ca722e..653a7b299 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -453,13 +453,17 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl if (sampRate>65536) sampRate=65536; altered=true; } rightClickable - DivPlatformGBAMinMod* dispatch=(DivPlatformGBAMinMod*)e->getDispatch(chan); - float maxCPU=dispatch->maxCPU*100; - ImGui::Text("Actual sample rate: %d Hz", dispatch->chipClock); - if (maxCPU>90) ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_WARNING]); - ImGui::Text("Max mixer CPU usage: %.0f%%", maxCPU); - if (maxCPU>90) ImGui::PopStyleColor(); - FurnaceGUI::popWarningColor(); + if (chan>=0) { + DivPlatformGBAMinMod* dispatch=(DivPlatformGBAMinMod*)e->getDispatch(chan); + if (dispatch!=NULL) { + float maxCPU=dispatch->maxCPU*100; + ImGui::Text("Actual sample rate: %d Hz", dispatch->chipClock); + if (maxCPU>90) ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_WARNING]); + ImGui::Text("Max mixer CPU usage: %.0f%%",maxCPU); + if (maxCPU>90) ImGui::PopStyleColor(); + FurnaceGUI::popWarningColor(); + } + } if (altered) { e->lockSave([&]() { flags.set("volScale",volScale); From 801e0e4b218b1ad18521f5d5679f4d863cd319e4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 13 Apr 2024 14:10:06 -0500 Subject: [PATCH 08/22] GUI: user presets, part 5 --- src/gui/userPresets.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/userPresets.cpp b/src/gui/userPresets.cpp index 62783cd6b..f0925c789 100644 --- a/src/gui/userPresets.cpp +++ b/src/gui/userPresets.cpp @@ -336,6 +336,8 @@ void FurnaceGUI::drawUserPresets() { if (userCategory==NULL) { ImGui::Text("Error! User category does not exist!"); } else if (ImGui::BeginTable("UserPresets",2,ImGuiTableFlags_BordersInnerV)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.25f); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.75f); // preset list ImGui::TableNextRow(); ImGui::TableNextColumn(); From 8743ea9a6e406d4cd2095fa9c2fb400a41f1b4db Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 13 Apr 2024 15:08:36 -0500 Subject: [PATCH 09/22] GUI: user presets, part 6 --- src/gui/userPresets.cpp | 217 ++++++++++++++++++++++++---------------- 1 file changed, 129 insertions(+), 88 deletions(-) diff --git a/src/gui/userPresets.cpp b/src/gui/userPresets.cpp index f0925c789..c6392dd28 100644 --- a/src/gui/userPresets.cpp +++ b/src/gui/userPresets.cpp @@ -335,124 +335,165 @@ void FurnaceGUI::drawUserPresets() { if (userCategory==NULL) { ImGui::Text("Error! User category does not exist!"); - } else if (ImGui::BeginTable("UserPresets",2,ImGuiTableFlags_BordersInnerV)) { + } else if (ImGui::BeginTable("UserPresets",2,ImGuiTableFlags_BordersInnerV,ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().y-ImGui::GetFrameHeightWithSpacing()))) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.25f); ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.75f); // preset list ImGui::TableNextRow(); ImGui::TableNextColumn(); - if (ImGui::Button(ICON_FA_PLUS "##AddPreset")) { - userCategory->systems.push_back(FurnaceGUISysDef("New Preset",{})); - selectedUserPreset.clear(); - selectedUserPreset.push_back(userCategory->systems.size()-1); + if (ImGui::BeginChild("UList",ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().y-ImGui::GetFrameHeightWithSpacing()))) { + ImGui::AlignTextToFramePadding(); + ImGui::Text("Systems"); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_PLUS "##AddPreset")) { + userCategory->systems.push_back(FurnaceGUISysDef("New Preset",{})); + selectedUserPreset.clear(); + selectedUserPreset.push_back(userCategory->systems.size()-1); + } + printPresets(userCategory->systems,0,depthStack); } - printPresets(userCategory->systems,0,depthStack); + ImGui::EndChild(); // editor ImGui::TableNextColumn(); - if (selectedUserPreset.empty()) { - ImGui::Text("select a preset"); - } else { - FurnaceGUISysDef* preset=selectPreset(userCategory->systems); + if (ImGui::BeginChild("UEdit",ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().y-ImGui::GetFrameHeightWithSpacing()))) { + if (selectedUserPreset.empty()) { + ImGui::Text("select a preset"); + } else { + FurnaceGUISysDef* preset=selectPreset(userCategory->systems); + bool doRemovePreset=false; - if (preset!=NULL) { - ImGui::AlignTextToFramePadding(); - ImGui::Text("Name"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - ImGui::InputText("##PName",&preset->name); - ImGui::Separator(); + if (preset!=NULL) { + ImGui::AlignTextToFramePadding(); + ImGui::Text("Name"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize("Remove").x-ImGui::GetStyle().ItemSpacing.x*2.0-ImGui::GetStyle().ItemInnerSpacing.x*2.0); + ImGui::InputText("##PName",&preset->name); + ImGui::SameLine(); + pushDestColor(); + if (ImGui::Button("Remove##UPresetRemove")) { + doRemovePreset=true; + } + popDestColor(); - int doRemove=-1; - bool mustBake=false; + ImGui::Separator(); - for (size_t i=0; iorig.size(); i++) { - String tempID; - FurnaceGUISysDefChip& chip=preset->orig[i]; + int doRemove=-1; + bool mustBake=false; - bool doInvert=(chip.vol<0); - float vol=fabs(chip.vol); - ImGui::PushID(i); + for (size_t i=0; iorig.size(); i++) { + String tempID; + FurnaceGUISysDefChip& chip=preset->orig[i]; - tempID=fmt::sprintf("%s##USystem",getSystemName(chip.sys)); - ImGui::Button(tempID.c_str(),ImVec2(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize("Invert").x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0,0)); - if (ImGui::BeginPopupContextItem("SysPickerCU",ImGuiPopupFlags_MouseButtonLeft)) { + bool doInvert=(chip.vol<0); + float vol=fabs(chip.vol); + ImGui::PushID(i); + + tempID=fmt::sprintf("%s##USystem",getSystemName(chip.sys)); + ImGui::Button(tempID.c_str(),ImVec2(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize("Invert").x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0,0)); + if (ImGui::BeginPopupContextItem("SysPickerCU",ImGuiPopupFlags_MouseButtonLeft)) { + DivSystem picked=systemPicker(); + if (picked!=DIV_SYSTEM_NULL) { + chip.sys=picked; + mustBake=true; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + ImGui::SameLine(); + if (ImGui::Checkbox("Invert",&doInvert)) { + chip.vol=-chip.vol; + mustBake=true; + } + ImGui::SameLine(); + pushDestColor(); + if (ImGui::Button(ICON_FA_MINUS "##USysRemove")) { + doRemove=i; + mustBake=true; + } + popDestColor(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0); + if (CWSliderFloat("Volume",&vol,0.0f,3.0f)) { + if (doInvert) { + if (vol<0.0001) vol=0.0001; + } + if (vol<0) vol=0; + if (vol>10) vol=10; + chip.vol=doInvert?-vol:vol; + mustBake=true; + } rightClickable + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0); + if (CWSliderFloat("Panning",&chip.pan,-1.0f,1.0f)) { + if (chip.pan<-1.0f) chip.pan=-1.0f; + if (chip.pan>1.0f) chip.pan=1.0f; + mustBake=true; + } rightClickable + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0); + if (CWSliderFloat("Front/Rear",&chip.panFR,-1.0f,1.0f)) { + if (chip.panFR<-1.0f) chip.panFR=-1.0f; + if (chip.panFR>1.0f) chip.panFR=1.0f; + mustBake=true; + } rightClickable + + if (ImGui::TreeNode("Configure")) { + DivConfig sysFlags; + sysFlags.loadFromBase64(chip.flags.c_str()); + if (drawSysConf(-1,i,chip.sys,sysFlags,false)) { + chip.flags=sysFlags.toBase64(); + mustBake=true; + } + ImGui::TreePop(); + } + + ImGui::PopID(); + } + + if (doRemove>=0) { + preset->orig.erase(preset->orig.begin()+doRemove); + mustBake=true; + } + + ImGui::Button(ICON_FA_PLUS "##SysAddU"); + if (ImGui::BeginPopupContextItem("SysPickerU",ImGuiPopupFlags_MouseButtonLeft)) { DivSystem picked=systemPicker(); if (picked!=DIV_SYSTEM_NULL) { - chip.sys=picked; + preset->orig.push_back(FurnaceGUISysDefChip(picked,1.0f,0.0f,"")); mustBake=true; ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } - ImGui::SameLine(); - if (ImGui::Checkbox("Invert",&doInvert)) { - chip.vol=-chip.vol; + ImGui::Separator(); + + if (ImGui::InputTextMultiline("##UExtra",&preset->extra,ImVec2(ImGui::GetContentRegionAvail().x,120.0f*dpiScale),ImGuiInputTextFlags_UndoRedo)) { mustBake=true; } - ImGui::SameLine(); - pushDestColor(); - if (ImGui::Button(ICON_FA_MINUS "##USysRemove")) { - doRemove=i; - mustBake=true; - } - popDestColor(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0); - if (CWSliderFloat("Volume",&vol,0.0f,3.0f)) { - if (doInvert) { - if (vol<0.0001) vol=0.0001; + + if (mustBake) preset->bake(); + } else { + selectedUserPreset.clear(); + } + + if (doRemovePreset) { + std::vector& items=userCategory->systems; + FurnaceGUISysDef* target=NULL; + for (size_t i=0; i(int)items.size()) break; + target=&items[selectedUserPreset[i]]; + if (isubDefs; + } else { + items.erase(items.begin()+selectedUserPreset[i]); } - if (vol<0) vol=0; - if (vol>10) vol=10; - chip.vol=doInvert?-vol:vol; - mustBake=true; - } rightClickable - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0); - if (CWSliderFloat("Panning",&chip.pan,-1.0f,1.0f)) { - if (chip.pan<-1.0f) chip.pan=-1.0f; - if (chip.pan>1.0f) chip.pan=1.0f; - mustBake=true; - } rightClickable - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0); - if (CWSliderFloat("Front/Rear",&chip.panFR,-1.0f,1.0f)) { - if (chip.panFR<-1.0f) chip.panFR=-1.0f; - if (chip.panFR>1.0f) chip.panFR=1.0f; - mustBake=true; - } rightClickable - - if (ImGui::TreeNode("Configure")) { - DivConfig sysFlags; - sysFlags.loadFromBase64(chip.flags.c_str()); - if (drawSysConf(-1,i,chip.sys,sysFlags,false)) { - chip.flags=sysFlags.toBase64(); - mustBake=true; - } - ImGui::TreePop(); } - ImGui::PopID(); + selectedUserPreset.clear(); } - - if (doRemove>=0) { - preset->orig.erase(preset->orig.begin()+doRemove); - mustBake=true; - } - - ImGui::Button(ICON_FA_PLUS "##SysAddU"); - if (ImGui::BeginPopupContextItem("SysPickerU",ImGuiPopupFlags_MouseButtonLeft)) { - DivSystem picked=systemPicker(); - if (picked!=DIV_SYSTEM_NULL) { - preset->orig.push_back(FurnaceGUISysDefChip(picked,1.0f,0.0f,"")); - mustBake=true; - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } - - if (mustBake) preset->bake(); } } + ImGui::EndChild(); ImGui::EndTable(); } From 93970f08404359c03d249af827c0fa75c092ae3e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 13 Apr 2024 16:45:58 -0500 Subject: [PATCH 10/22] update to-do list --- TODO.md | 1 - 1 file changed, 1 deletion(-) diff --git a/TODO.md b/TODO.md index f8a0e67de..96f349a9b 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,6 @@ # to-do for 0.6.3 - 9xxx for more chips -- user presets # to-do long term From 48f8afd6b9adf3c25a840d058ba0050b167a352d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 13 Apr 2024 16:49:38 -0500 Subject: [PATCH 11/22] why --- src/gui/gui.h | 2 +- src/gui/settings.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index 5a21f028b..2faa77d20 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -2084,7 +2084,7 @@ class FurnaceGUI { displayRenderTime(0), maxUndoSteps(100), vibrationStrength(0.5f), - vibrationLength(100), + vibrationLength(20), mainFontPath(""), headFontPath(""), patFontPath(""), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 02ad0b98b..7d12db269 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -4104,7 +4104,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) { settings.centerPopup=conf.getInt("centerPopup",1); settings.vibrationStrength=conf.getFloat("vibrationStrength",0.5f); - settings.vibrationLength=conf.getInt("vibrationLength",100); + settings.vibrationLength=conf.getInt("vibrationLength",20); } if (groups&GUI_SETTINGS_AUDIO) { From d2204100f078518e8af0304dcedcebf4c850bbf3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 13 Apr 2024 16:56:02 -0500 Subject: [PATCH 12/22] GUI: user presets, part 7 --- src/gui/userPresets.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/gui/userPresets.cpp b/src/gui/userPresets.cpp index c6392dd28..2882fea15 100644 --- a/src/gui/userPresets.cpp +++ b/src/gui/userPresets.cpp @@ -467,9 +467,17 @@ void FurnaceGUI::drawUserPresets() { ImGui::Separator(); + ImGui::Text("Advanced"); if (ImGui::InputTextMultiline("##UExtra",&preset->extra,ImVec2(ImGui::GetContentRegionAvail().x,120.0f*dpiScale),ImGuiInputTextFlags_UndoRedo)) { mustBake=true; } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip( + "insert additional settings in `option=value` format.\n" + "available options:\n" + "- tickRate" + ); + } if (mustBake) preset->bake(); } else { From 012108975fbddc538357a3c6d526d6ac64221634 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 14 Apr 2024 02:22:07 -0500 Subject: [PATCH 13/22] more cat and mouse --- src/engine/fileOpsIns.cpp | 2 +- src/engine/instrument.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 7f9559b3a..6a181f5f2 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -1897,7 +1897,7 @@ std::vector DivEngine::instrumentFromFile(const char* path, bool bool isOldFurnaceIns=false; try { reader.read(magic,4); - if (memcmp("FINS",magic,4)==0) { + if (memcmp("FINS",magic,4)==0 || memcmp("FINB",magic,4)==0) { isFurnaceInstr=true; logV("found a new Furnace ins"); } else { diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 6225765aa..ba99b28f4 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -3005,6 +3005,8 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version, DivS type=1; } else if (memcmp(magic,"FINS",4)==0) { type=2; + } else if (memcmp(magic,"FINB",4)==0) { // DIV_FUR_VARIANT_B + type=2; } else { logE("invalid instrument header!"); return DIV_DATA_INVALID_HEADER; From 9926fc48376d6ff05cf3fbb6f4cec804a3112e65 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 14 Apr 2024 09:18:23 -0500 Subject: [PATCH 14/22] GUI: fix macro scale being limited to length 128 --- src/gui/insEdit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 420fa9bfb..85598899d 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -7469,7 +7469,7 @@ void FurnaceGUI::drawInsEdit() { memset(oldData,0,256*sizeof(int)); memcpy(oldData,lastMacroDesc.macro->val,lastMacroDesc.macro->len*sizeof(int)); - lastMacroDesc.macro->len=MIN(128,((double)lastMacroDesc.macro->len*(macroScaleX/100.0))); + lastMacroDesc.macro->len=MIN(255,((double)lastMacroDesc.macro->len*(macroScaleX/100.0))); for (int i=0; ilen; i++) { int val=0; From 71e14e459a261d25c215d16c9dd74f34b1f10133 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 14 Apr 2024 10:15:23 -0500 Subject: [PATCH 15/22] update documentation - 5E01 - mention software backend --- doc/2-interface/settings.md | 1 + doc/7-systems/5e01.md | 62 +++++++++++++++++++++++++++++++++++++ doc/7-systems/nes.md | 4 ++- 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 doc/7-systems/5e01.md diff --git a/doc/2-interface/settings.md b/doc/2-interface/settings.md index f53d5aa2b..ec61e9371 100644 --- a/doc/2-interface/settings.md +++ b/doc/2-interface/settings.md @@ -15,6 +15,7 @@ settings are saved when clicking the **OK** or **Apply** buttons at the bottom o - DirectX 11: works with the majority of graphics chips/cards and is optimized specifically for Windows. - SDL Renderer: this was the only available render backend prior to the addition of dedicated OpenGL/DirectX backends in 0.6. default on macOS. - it is slower than the other backends. + - Software: this is a last resort backend which renders the interface in software. very slow! - **Render driver**: this setting appears when using the SDL Renderer backend. it allows you to select an SDL render driver. - **VSync**: synchronizes rendering to VBlank and eliminates tearing. - **Frame rate limit**: allows you to set a frame rate limit (in frames per second). diff --git a/doc/7-systems/5e01.md b/doc/7-systems/5e01.md new file mode 100644 index 000000000..a7d650c98 --- /dev/null +++ b/doc/7-systems/5e01.md @@ -0,0 +1,62 @@ +# 5E01 + +a fantasy sound chip created by Euly, based on the Ricoh 2A03, with some improvements: + +- a 37.5% duty cycle, +- 32 noise pitches instead of 16, and +- triangle channel becomes a wave channel, with four available waveforms: triangle, saw, sine and square. + +## effects + +- `11xx`: **write to delta modulation counter.** range is `00` to `7F`. + - this may be used to attenuate the triangle and noise channels; at `7F`, they will be at about 57% volume. + - will not work if a sample is playing. +- `12xx`: **set duty cycle/noise mode/waveform of channel.** + - may be `0` to `3` for the pulse channels: + - `0`: 12.5% + - `1`: 25% + - `2`: 37.5% + - `3`: 50% + - may be `0` or `1` for the noise channel: + - `0`: long (15-bit LFSR, 32767-step) + - `1`: short (9-bit LFSR, 93-step) + - may be `0` to `3` for the wave channel: + - `0`: triangle + - `1`: saw + - `2`: square + - `3`: sine +- `13xy`: **setup sweep up.** + - `x` is the time. + - `y` is the shift. + - set to `0` to disable it. +- `14xy`: **setup sweep down.** + - `x` is the time. + - `y` is the shift. + - set to `0` to disable it. +- `15xx`: **set envelope mode.** + - `0`: envelope + length counter. volume represents envelope duration. + - `1`: length counter. volume represents output volume. + - `2`: looping envelope. volume represents envelope duration. + - `3`: constant volume. default value. volume represents output volume. + - pulse and noise channels only. + - you may need to apply a phase reset (using the macro) to make the envelope effective. +- `16xx`: **set length counter.** + - see [NES](nes.md) for more information. + - this will trigger phase reset. +- `17xx`: **set frame counter mode.** + - `0`: 4-step. + - `1`: 5-step. +- `18xx`: **set PCM channel mode.** + - `00`: PCM (software). + - `01`: DPCM (hardware). + - when in DPCM mode, samples will sound muffled (due to its nature), availables pitches are limited, and loop point is ignored. +- `19xx`: **set triangle linear counter.** + - `00` to `7F` set the counter. + - `80` and higher halt it. +- `20xx`: **set DPCM frequency.** + - only works in DPCM mode. + + +## info + +this chip uses the [NES](../4-instrument/nes.md) instrument editor. diff --git a/doc/7-systems/nes.md b/doc/7-systems/nes.md index cfd1823a6..3130a9f61 100644 --- a/doc/7-systems/nes.md +++ b/doc/7-systems/nes.md @@ -2,7 +2,9 @@ the console from Nintendo that plays Super Mario Bros. and helped revive the agonizing video game market in the US during mid-80s. -also known as Famicom. it is a five-channel sound generator: first two channels play pulse wave with three different duty cycles, third is a fixed-volume triangle channel, fourth is a noise channel (can work in both pseudo-random and periodic modes) and fifth is a (D)PCM sample channel. +also known as Family Computer (Famicom), especially in Japan. + +the console is powered by the Ricoh 2A03, a CPU with sound generator built-in. it has five channels: first two channels play pulse wave with three different duty cycles, third is a fixed-volume triangle channel, fourth is a noise channel (can work in both pseudo-random and periodic modes) and fifth is a (D)PCM sample channel. ## effects From 11d6cbc74814fd8659c3ac7809d4a8dce74882db Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 14 Apr 2024 10:23:26 -0500 Subject: [PATCH 16/22] update emulation cores guide --- doc/9-guides/emulation-cores.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/9-guides/emulation-cores.md b/doc/9-guides/emulation-cores.md index 2624f6ddc..d42dc459e 100644 --- a/doc/9-guides/emulation-cores.md +++ b/doc/9-guides/emulation-cores.md @@ -47,3 +47,10 @@ Furnace achieves the authentic sound of videogame hardware by emulating sound ch - **YMF262-LLE**: a new core written by the author of the Nuked cores. it features extremely accurate emulation. - this core uses even more CPU than YM3812-LLE. not suitable for playback or even rendering if you're impatient! +- **ESFM core**: + - **ESFMu**: the ESFM emulator. best choice but CPU intensive. + - **ESFMu (fast)**: this is a modification of ESFMu to reduce CPU usage at the cost of less accuracy. + +- **OPLL core**: + - **Nuked-OPLL**: this core is accurate and the default. + - **emu2413**: a less accurate core that uses less CPU. From 6efef65b489b34352b0c842093de5683d4081dee Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 14 Apr 2024 12:45:17 -0500 Subject: [PATCH 17/22] audit .ftm import code to-do: dkc_ending.ftm and fcm9.ftm no longer load... check out why --- src/engine/fileOps/ftm.cpp | 419 +++++++++++++++++++++++++++++-------- 1 file changed, 335 insertions(+), 84 deletions(-) diff --git a/src/engine/fileOps/ftm.cpp b/src/engine/fileOps/ftm.cpp index e687e73f0..aab9744c1 100644 --- a/src/engine/fileOps/ftm.cpp +++ b/src/engine/fileOps/ftm.cpp @@ -23,7 +23,6 @@ // portions apparently taken from FamiTracker source, which is under GPLv2+ // TODO: -// - audit for CVEs // - format code? #include "fileOpsCommon.h" @@ -258,6 +257,7 @@ const int eff_conversion_050[][2] = { }; constexpr int ftEffectMapSize = sizeof(ftEffectMap) / sizeof(int); +constexpr int eftEffectMapSize = sizeof(eftEffectMap) / sizeof(int); int convertMacros2A03[5] = {(int)DIV_MACRO_VOL, (int)DIV_MACRO_ARP, (int)DIV_MACRO_PITCH, -1, (int)DIV_MACRO_DUTY}; int convertMacrosVRC6[5] = {(int)DIV_MACRO_VOL, (int)DIV_MACRO_ARP, (int)DIV_MACRO_PITCH, -1, (int)DIV_MACRO_DUTY}; @@ -434,7 +434,8 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si bool hasSequence[256][8]; unsigned char sequenceIndex[256][8]; unsigned char macro_types[256][8]; - std::vector> macros; + std::vector macros[256]; + std::vector encounteredBlocks; unsigned char map_channels[DIV_MAX_CHANS]; unsigned int hilightA = 4; unsigned int hilightB = 16; @@ -457,13 +458,9 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si memset(map_channels, 0xfe, DIV_MAX_CHANS * sizeof(unsigned char)); for (int i = 0; i < 256; i++) { - std::vector mac; - for (int j = 0; j < 8; j++) { - mac.push_back(DivInstrumentMacro(DIV_MACRO_VOL)); + macros[i].push_back(DivInstrumentMacro(DIV_MACRO_VOL)); } - - macros.push_back(mac); } if (!reader.seek((dnft && dnft_sig) ? 21 : 18, SEEK_SET)) { @@ -482,7 +479,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si return false; } - for (DivSubSong* i : ds.subsong) { + for (DivSubSong* i: ds.subsong) { i->clearData(); delete i; } @@ -501,13 +498,30 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si } // not the end - reader.seek(-3, SEEK_CUR); + if (!reader.seek(-3, SEEK_CUR)) { + logE("couldn't seek back by 3!"); + lastError = "couldn't seek back by 3"; + delete[] file; + return false; + } blockName = reader.readString(16); unsigned int blockVersion = (unsigned int)reader.readI(); unsigned int blockSize = (unsigned int)reader.readI(); size_t blockStart = reader.tell(); logD("reading block %s (version %d, %d bytes, position %x)", blockName, blockVersion, blockSize, reader.tell()); + + for (String& i: encounteredBlocks) { + if (blockName==i) { + logE("duplicate block %s!",blockName); + lastError = "duplicate block "+blockName; + ds.unload(); + delete[] file; + return false; + } + } + encounteredBlocks.push_back(blockName); + if (blockName == "PARAMS") { // versions 7-9 don't change anything? CHECK_BLOCK_VERSION(9); @@ -529,6 +543,13 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si tchans = reader.readI(); + if (tchans<0 || tchans>=DIV_MAX_CHANS) { + logE("invalid channel count! %d",tchans); + lastError = "invalid channel count"; + delete[] file; + return false; + } + if (tchans == 5) { expansions = 0; // This is strange. Sometimes expansion chip is set to 0xFF in files } @@ -538,12 +559,15 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si if (blockVersion >= 7) { // advanced Hz control int controlType = reader.readI(); - switch (controlType) { + int readHz=reader.readI(); + if (readHz<=0) { + customHz=60.0; + } else switch (controlType) { case 1: - customHz = 1000000.0 / (double)reader.readI(); + customHz = 1000000.0 / (double)readHz; break; default: - reader.readI(); + logW("unsupported tick rate control type %d",controlType); break; } } else { @@ -553,6 +577,9 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si customHz = reader.readI(); } + if (customHz<1.0) customHz=1.0; + if (customHz>1000.0) customHz=1000.0; + unsigned int newVibrato = 0; bool sweepReset = false; unsigned int speedSplitPoint = 0; @@ -576,6 +603,12 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si if ((expansions & 16) && blockVersion >= 5) { // N163 channels n163Chans = reader.readI(); + if (n163Chans<1 || n163Chans>=9) { + logE("invalid Namco 163 channel count! %d",n163Chans); + lastError = "invalid Namco 163 channel count"; + delete[] file; + return false; + } } if (blockVersion >= 6) { speedSplitPoint = reader.readI(); @@ -779,6 +812,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si } } if (calcChans != tchans) { + // TODO: would ignore trigger CVE? too bad if so! if (!eft || (eft && (expansions & 8) == 0)) // ignore since I have no idea how to tell apart E-FT versions which do or do not have PCM chan. Yes, this may lead to all the righer channels to be shifted but at least you still get note data! { logE("channel counts do not match! %d != %d", tchans, calcChans); @@ -788,17 +822,20 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si } } if (tchans > DIV_MAX_CHANS) { - tchans = DIV_MAX_CHANS; - logW("too many channels!"); + logE("too many channels!"); + lastError = "too many channels"; + delete[] file; + return false; } if (blockVersion == 9 && blockSize - (reader.tell() - blockStart) == 2) // weird { - reader.seek(2, SEEK_CUR); + if (!reader.seek(2, SEEK_CUR)) { + logE("could not weird-seek by 2!"); + lastError = "could not weird-seek by 2"; + delete[] file; + return false; + } } - if (eft) { - // reader.seek(8,SEEK_CUR); - } - } else if (blockName == "INFO") { CHECK_BLOCK_VERSION(1); ds.name = reader.readString(32); @@ -831,6 +868,13 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si for (int j = 0; j <= totalSongs; j++) { unsigned char effectCols = reader.readC(); + if (effectCols>7) { + logE("too many effect columns!"); + lastError = "too many effect columns"; + delete[] file; + return false; + } + if (map_channels[i] == 0xfe) { ds.subsong[j]->pat[i].effectCols = 1; logV("- song %d has %d effect columns", j, effectCols); @@ -850,8 +894,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si } else if (blockName == "INSTRUMENTS") { CHECK_BLOCK_VERSION(9); - // reader.seek(blockSize,SEEK_CUR); - ds.insLen = reader.readI(); if (ds.insLen < 0 || ds.insLen > 256) { logE("too many instruments/out of range!"); @@ -870,10 +912,10 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si for (int i = 0; i < ds.insLen; i++) { unsigned int insIndex = reader.readI(); if (insIndex >= ds.ins.size()) { - // logE("instrument index %d is out of range!",insIndex); - // lastError="instrument index out of range"; - // delete[] file; - // return false; + logE("instrument index %d is out of range!",insIndex); + lastError="instrument index out of range"; + delete[] file; + return false; } DivInstrument* ins = ds.ins[insIndex]; @@ -937,6 +979,12 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si if (blockVersion >= 7) { note = reader.readC(); } + if (note<0 || note>=120) { + logE("DPCM note %d out of range!",note); + lastError = "DPCM note out of range"; + delete[] file; + return false; + } ins->amiga.noteMap[note].map = (short)((unsigned char)reader.readC()) - 1; unsigned char freq = reader.readC(); ins->amiga.noteMap[note].dpcmFreq = (freq & 15); // 0-15 = 0-15 unlooped, 128-143 = 0-15 looped @@ -981,8 +1029,9 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si } case DIV_INS_OPLL: { ins->fm.opllPreset = (unsigned int)reader.readI(); + ins->fm.opllPreset&=15; - unsigned char custom_patch[8] = {0}; + unsigned char custom_patch[8]; for (int i = 0; i < 8; i++) { custom_patch[i] = reader.readC(); @@ -1020,24 +1069,35 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si for (int j = 0; j < 64; j++) { wave->data[j] = reader.readC(); } - ins->std.waveMacro.len = 1; - ins->std.waveMacro.val[0] = ds.wave.size(); for (int j = 0; j < 32; j++) { ins->fds.modTable[j] = reader.readC() - 3; } ins->fds.modSpeed = reader.readI(); ins->fds.modDepth = reader.readI(); reader.readI(); // this is delay. currently ignored. TODO. - ds.wave.push_back(wave); - ds.waveLen++; + if (ds.wave.size()>=256) { + logW("too many waves! ignoring..."); + delete wave; + } else { + ins->std.waveMacro.len = 1; + ins->std.waveMacro.val[0] = ds.wave.size(); + ds.wave.push_back(wave); + ds.waveLen++; + } unsigned int a = reader.readI(); unsigned int b = reader.readI(); - reader.seek(-8, SEEK_CUR); + if (!reader.seek(-8, SEEK_CUR)) { + logE("couldn't seek back by 8 reading FDS ins"); + lastError = "couldn't seek back by 8 reading FDS ins"; + delete[] file; + return false; + } if (a < 256 && (b & 0xFF) != 0x00) { // don't look at me like this. I don't know why this should be like this either! + logW("a is less than 256 and b is not zero!"); } else { ins->std.volMacro.len = reader.readC(); ins->std.volMacro.loop = reader.readI(); @@ -1112,12 +1172,19 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si if (blockVersion >= 8) { unsigned int autopos = reader.readI(); - (void)autopos; + logV("autopos: %d",autopos); } unsigned int wave_count = reader.readI(); size_t waveOff = ds.wave.size(); + if (wave_size>256) { + logE("wave size %d out of range",wave_size); + lastError = "wave size out of range"; + delete[] file; + return false; + } + for (unsigned int ii = 0; ii < wave_count; ii++) { DivWavetable* wave = new DivWavetable(); wave->len = wave_size; @@ -1131,6 +1198,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si if (ds.wave.size()<256) { ds.wave.push_back(wave); } else { + logW("too many waves..."); delete wave; } } @@ -1296,16 +1364,30 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si } if (instVersion == 2) { - reader.seek(seek_amount, SEEK_CUR); // what the fuck + // I know right? + if (!reader.seek(seek_amount, SEEK_CUR)) { + logE("EFT seek fail"); + lastError = "EFT seek fail"; + delete[] file; + return false; + } } + // this commented out block left here intentionally. + // total mess of code style... for with no space, UNDEFINED CHAR, escaping the unescapable, silly var names... + // ...whatever. /*for(int tti = 0; tti < 20; tti++) { char aaaa = reader.readC(); logV("\'%c\'", aaaa); }*/ } else { - reader.seek(-4, SEEK_CUR); + if (!reader.seek(-4, SEEK_CUR)) { + logE("EFT -4 seek fail"); + lastError = "EFT -4 seek fail"; + delete[] file; + return false; + } } break; @@ -1325,7 +1407,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si } ds.insLen = 128; - } else if (blockName == "SEQUENCES") { CHECK_BLOCK_VERSION(6); @@ -1342,6 +1423,14 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si unsigned int index = reader.readI(); unsigned int type = reader.readI(); unsigned char size = reader.readC(); + + if (index>=256 || type>=8) { + logE("%d: index/type out of range",i); + lastError = "sequence index/type out of range"; + delete[] file; + return false; + } + macros[index][type].len = size; for (int j = 0; j < size; j++) { @@ -1361,12 +1450,34 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si unsigned char* Indices = new unsigned char[128 * 5]; unsigned char* Types = new unsigned char[128 * 5]; + memset(Indices,0,128*5); + memset(Types,0,128*5); + for (unsigned int i = 0; i < seq_count; i++) { unsigned int index = reader.readI(); + if (index>=128*5) { + logE("%d: index out of range",i); + lastError = "sequence index out of range"; + delete[] file; + return false; + } Indices[i] = index; unsigned int type = reader.readI(); + if (type>=128*5) { + logE("%d: type out of range",i); + lastError = "sequence type out of range"; + delete[] file; + return false; + } Types[i] = type; + if (index>=256 || type>=8) { + logE("%d: index/type out of range",i); + lastError = "sequence index/type out of range"; + delete[] file; + return false; + } + unsigned char size = reader.readC(); unsigned int setting = 0; @@ -1393,7 +1504,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si DivInstrument* ins = ds.ins[k]; if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_NES && hasSequence[k][Types[i]]) { copyMacro(ins, ¯os[index][type], Types[i], setting); - // memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)Types[i], true), ¯os[sequenceIndex[index][type]][type], sizeof(DivInstrumentMacro)); } } } @@ -1412,7 +1522,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si macro_types[k][j] = setting; copyMacro(ins, ¯os[sequenceIndex[k][j]][j], j, setting); - // memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)j, true), ¯os[sequenceIndex[k][j]][j], sizeof(DivInstrumentMacro)); } } } @@ -1425,9 +1534,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si unsigned int release = reader.readI(); unsigned int setting = reader.readI(); - // macros[index][type].rel = release; - // macro_types[index][type] = setting; - for (int k = 0; k < (int)ds.ins.size(); k++) { DivInstrument* ins = ds.ins[k]; if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_NES && hasSequence[k][Types[i]]) { @@ -1435,7 +1541,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si macro_types[k][Types[i]] = setting; copyMacro(ins, ¯os[sequenceIndex[k][Types[i]]][Types[i]], Types[i], setting); - // memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)Types[i], true), ¯os[sequenceIndex[k][Types[i]]][Types[i]], sizeof(DivInstrumentMacro)); } } } @@ -1446,12 +1551,11 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si } } else if (blockName == "GROOVES") { CHECK_BLOCK_VERSION(6); - // reader.seek(blockSize,SEEK_CUR); unsigned char num_grooves = reader.readC(); int max_groove = 0; - for (int i = 0; i < 0xff; i++) { + for (int i = 0; i < 256; i++) { ds.grooves.push_back(DivGroovePattern()); } @@ -1459,15 +1563,18 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si unsigned char index = reader.readC(); unsigned char size = reader.readC(); - if (index > max_groove) + if (index > max_groove) { max_groove = index + 1; + } DivGroovePattern gp; gp.len = size; for (int sz = 0; sz < size; sz++) { unsigned char value = reader.readC(); - gp.val[sz] = value; + if (sz<16) { + gp.val[sz] = value; + } } ds.grooves[index] = gp; @@ -1488,14 +1595,21 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si if ((reader.tell() - blockStart) != blockSize) { logE("block %s size does not match! block size %d curr pos %d", blockName, blockSize, reader.tell() - blockStart); } - } else if (blockName == "FRAMES") { CHECK_BLOCK_VERSION(3); for (size_t i = 0; i < ds.subsong.size(); i++) { DivSubSong* s = ds.subsong[i]; - s->ordersLen = reader.readI(); + int framesLen=reader.readI(); + if (framesLen<=1 || framesLen>=256) { + logE("frames out of range"); + lastError = "frames out of range"; + delete[] file; + return false; + } + + s->ordersLen = framesLen; if (blockVersion >= 3) { s->speeds.val[0] = reader.readI(); } @@ -1510,18 +1624,31 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si s->virtualTempoN = tempo; } - s->patLen = reader.readI(); + int patLen=reader.readI(); + if (patLen<1 || patLen>=256) { + logE("pattern length out of range"); + lastError = "pattern length out of range"; + delete[] file; + return false; + } + s->patLen = patLen; } int why = tchans; if (blockVersion == 1) { why = reader.readI(); + if (why<0 || why>=DIV_MAX_CHANS) { + logE("why out of range!"); + lastError = "why out of range"; + delete[] file; + return false; + } } logV("reading %d and %d orders", tchans, s->ordersLen); for (int j = 0; j < s->ordersLen; j++) { for (int k = 0; k < why; k++) { unsigned char o = reader.readC(); - // logV("%.2x",o); + if (map_channels[k]>=DIV_MAX_CHANS) continue; s->orders.ord[map_channels[k]][j] = o; } } @@ -1533,6 +1660,12 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si if (blockVersion == 1) { int patLenOld = reader.readI(); + if (patLenOld<1 || patLenOld>=256) { + logE("old pattern length out of range"); + lastError = "old pattern length out of range"; + delete[] file; + return false; + } for (DivSubSong* i : ds.subsong) { i->patLen = patLenOld; } @@ -1553,6 +1686,37 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si logV("patNum: %d",patNum); logV("rows: %d",numRows); + if (subs<0 || subs>=(int)ds.subsong.size()) { + logE("subsong out of range!"); + lastError = "subsong out of range"; + delete[] file; + return false; + } + if (ch<0 || ch>=DIV_MAX_CHANS) { + logE("channel out of range!"); + lastError = "channel out of range"; + delete[] file; + return false; + } + if (map_channels[ch]>=DIV_MAX_CHANS) { + logE("mapped channel out of range!"); + lastError = "mapped channel out of range"; + delete[] file; + return false; + } + if (patNum<0 || patNum>=256) { + logE("pattern number out of range!"); + lastError = "pattern number out of range"; + delete[] file; + return false; + } + if (numRows<0) { + logE("row count is negative!"); + lastError = "row count is negative"; + delete[] file; + return false; + } + DivPattern* pat = ds.subsong[subs]->pat[map_channels[ch]].getPattern(patNum, true); for (int i = 0; i < numRows; i++) { unsigned int row = 0; @@ -1562,6 +1726,13 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si row = reader.readI(); } + if (row>=256) { + logE("row index out of range"); + lastError = "row index out of range"; + delete[] file; + return false; + } + unsigned char nextNote = reader.readC(); unsigned char nextOctave = reader.readC(); @@ -1592,6 +1763,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si } unsigned char nextIns = reader.readC(); + // TODO: you sure about 0xff? if (map_channels[ch] != 0xff) { if (nextIns < 0x40 && nextNote != 0x0d && nextNote != 0x0e) { pat->data[row][2] = nextIns; @@ -1606,6 +1778,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si pat->data[row][3] = nextVol; if (map_channels[ch] == vrc6_saw_chan) // scale volume { + // TODO: shouldn't it be 32? pat->data[row][3] = (pat->data[row][3] * 42) / 15; } @@ -1625,13 +1798,9 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si effectCols = 1; } - logV("effectCols: %d",effectCols); - unsigned char nextEffectVal = 0; unsigned char nextEffect = 0; - //logV("row %d effects are read at %x",row,reader.tell()); - for (int j = 0; j < effectCols; j++) { nextEffect = reader.readC(); @@ -1727,14 +1896,12 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si } - // logW("next effect %d val %d", nextEffect, nextEffectVal); - if (map_channels[ch] != 0xff) { if (nextEffect == 0 && nextEffectVal == 0) { pat->data[row][4 + (j * 2)] = -1; pat->data[row][5 + (j * 2)] = -1; } else { - if (nextEffect < ftEffectMapSize) { + if ((eft && nextEffectdata[row][4 + (j * 2)] = eftEffectMap[nextEffect]; pat->data[row][5 + (j * 2)] = eftEffectMap[nextEffect] == -1 ? -1 : nextEffectVal; @@ -1782,7 +1949,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si } } else if (blockName == "DPCM SAMPLES") { CHECK_BLOCK_VERSION(1); - // reader.seek(blockSize,SEEK_CUR); unsigned char num_samples = reader.readC(); for (int i = 0; i < 256; i++) { @@ -1808,14 +1974,16 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si unsigned int sample_len = reader.readI(); + if (sample_len>=2097152) { + logE("%d: sample too large! %d",index,sample_len); + lastError = "sample too large"; + delete[] file; + return false; + } + true_size = sample_len + ((1 - (int)sample_len) & 0x0f); - sample->lengthDPCM = true_size; - sample->samples = true_size * 8; - - sample->dataDPCM = new unsigned char[true_size]; - + sample->init(true_size * 8); memset(sample->dataDPCM, 0xAA, true_size); - reader.read(sample->dataDPCM, sample_len); } @@ -1824,7 +1992,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si for (int i = 255; i > 0; i--) { DivSample* s = ds.sample[i]; - if (s->dataDPCM) { + if (s->samples>0) { last_non_empty_sample = i; break; } @@ -1835,21 +2003,42 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si } ds.sampleLen = ds.sample.size(); - } else if (blockName == "SEQUENCES_VRC6") { CHECK_BLOCK_VERSION(6); unsigned char* Indices = new unsigned char[128 * 5]; unsigned char* Types = new unsigned char[128 * 5]; + memset(Indices,0,128*5); + memset(Types,0,128*5); + unsigned int seq_count = reader.readI(); for (unsigned int i = 0; i < seq_count; i++) { unsigned int index = reader.readI(); + if (index>=128*5) { + logE("%d: index out of range",i); + lastError = "sequence index out of range"; + delete[] file; + return false; + } Indices[i] = index; unsigned int type = reader.readI(); + if (type>=128*5) { + logE("%d: type out of range",i); + lastError = "sequence type out of range"; + delete[] file; + return false; + } Types[i] = type; + if (index>=256 || type>=8) { + logE("%d: index/type out of range",i); + lastError = "sequence index/type out of range"; + delete[] file; + return false; + } + unsigned char size = reader.readC(); unsigned int setting = 0; @@ -1914,9 +2103,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si unsigned int release = reader.readI(); unsigned int setting = reader.readI(); - // macros[index][type].rel = release; - // macro_types[index][type] = setting; - for (int k = 0; k < (int)ds.ins.size(); k++) { DivInstrument* ins = ds.ins[k]; if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_VRC6 && hasSequence[k][Types[i]]) { @@ -1937,19 +2123,40 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si delete[] Types; } else if (blockName == "SEQUENCES_N163" || blockName == "SEQUENCES_N106") { CHECK_BLOCK_VERSION(1); - // reader.seek(blockSize,SEEK_CUR); unsigned char* Indices = new unsigned char[128 * 5]; unsigned char* Types = new unsigned char[128 * 5]; + memset(Indices,0,128*5); + memset(Types,0,128*5); + unsigned int seq_count = reader.readI(); for (unsigned int i = 0; i < seq_count; i++) { unsigned int index = reader.readI(); + if (index>=128*5) { + logE("%d: index out of range",i); + lastError = "sequence index out of range"; + delete[] file; + return false; + } Indices[i] = index; unsigned int type = reader.readI(); + if (type>=128*5) { + logE("%d: type out of range",i); + lastError = "sequence type out of range"; + delete[] file; + return false; + } Types[i] = type; + if (index>=256 || type>=8) { + logE("%d: index/type out of range",i); + lastError = "sequence index/type out of range"; + delete[] file; + return false; + } + unsigned char size = reader.readC(); unsigned int setting = 0; @@ -1974,7 +2181,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si DivInstrument* ins = ds.ins[k]; if (sequenceIndex[k][Types[i]] == Indices[i] && ins->type == DIV_INS_N163 && hasSequence[k][Types[i]]) { copyMacro(ins, ¯os[index][type], type, setting); - // memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)Types[i], true), ¯os[sequenceIndex[index][type]][type], sizeof(DivInstrumentMacro)); } } } @@ -1984,19 +2190,40 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si } else if (blockName == "SEQUENCES_S5B") { CHECK_BLOCK_VERSION(1); - // reader.seek(blockSize,SEEK_CUR); unsigned char* Indices = new unsigned char[128 * 5]; unsigned char* Types = new unsigned char[128 * 5]; + memset(Indices,0,128*5); + memset(Types,0,128*5); + unsigned int seq_count = reader.readI(); for (unsigned int i = 0; i < seq_count; i++) { unsigned int index = reader.readI(); + if (index>=128*5) { + logE("%d: index out of range",i); + lastError = "sequence index out of range"; + delete[] file; + return false; + } Indices[i] = index; unsigned int type = reader.readI(); + if (type>=128*5) { + logE("%d: type out of range",i); + lastError = "sequence type out of range"; + delete[] file; + return false; + } Types[i] = type; + if (index>=256 || type>=8) { + logE("%d: index/type out of range",i); + lastError = "sequence index/type out of range"; + delete[] file; + return false; + } + unsigned char size = reader.readC(); unsigned int setting = 0; @@ -2021,7 +2248,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si DivInstrument* ins = ds.ins[k]; if (sequenceIndex[k][type] == Indices[i] && ins->type == DIV_INS_AY && hasSequence[k][type]) { copyMacro(ins, ¯os[index][type], type, setting); - // memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)Types[i], true), ¯os[sequenceIndex[index][type]][type], sizeof(DivInstrumentMacro)); } } } @@ -2034,14 +2260,36 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si unsigned char* Indices = new unsigned char[128 * 5]; unsigned char* Types = new unsigned char[128 * 5]; + memset(Indices,0,128*5); + memset(Types,0,128*5); + unsigned int seq_count = reader.readI(); for (unsigned int i = 0; i < seq_count; i++) { unsigned int index = reader.readI(); + if (index>=128*5) { + logE("%d: index out of range",i); + lastError = "sequence index out of range"; + delete[] file; + return false; + } Indices[i] = index; unsigned int type = reader.readI(); + if (type>=128*5) { + logE("%d: type out of range",i); + lastError = "sequence type out of range"; + delete[] file; + return false; + } Types[i] = type; + if (index>=256 || type>=8) { + logE("%d: index/type out of range",i); + lastError = "sequence index/type out of range"; + delete[] file; + return false; + } + unsigned char size = reader.readC(); unsigned int setting = 0; @@ -2066,7 +2314,6 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si DivInstrument* ins = ds.ins[k]; if (sequenceIndex[k][type] == Indices[i] && ins->type == DIV_INS_C64 && hasSequence[k][type]) { copyMacro(ins, ¯os[index][type], type, setting); - // memcpy(ins->std.get_macro(DIV_MACRO_VOL + (DivMacroType)Types[i], true), ¯os[sequenceIndex[index][type]][type], sizeof(DivInstrumentMacro)); } } } @@ -2075,31 +2322,33 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si delete[] Types; } else if (blockName == "JSON") { CHECK_BLOCK_VERSION(1); + logW("block JSON not supported..."); reader.seek(blockSize, SEEK_CUR); } else if (blockName == "PARAMS_EMU") { CHECK_BLOCK_VERSION(1); + logW("block PARAMS_EMU not supported..."); reader.seek(blockSize, SEEK_CUR); } else if (blockName == "DETUNETABLES") { CHECK_BLOCK_VERSION(1); + logW("block DETUNETABLES not supported..."); reader.seek(blockSize, SEEK_CUR); } else if (blockName == "COMMENTS") { CHECK_BLOCK_VERSION(1); - // reader.seek(blockSize,SEEK_CUR); unsigned int display_comment = reader.readI(); - (void)display_comment; - char ch = 1; + logV("displayComment: %d",display_comment); - do { + char ch = 0; + + // why not readString? + while (true) { ch = reader.readC(); - String sss = String() + ch; - ds.subsong[0]->notes += sss; - } while (ch != 0); + if (ch==0) break; + ds.subsong[0]->notes += ch; + } - // ds.subsong[0]->notes = reader.readS(); } else if (blockName == "PARAMS_EXTRA") { CHECK_BLOCK_VERSION(3); - // reader.seek(blockSize,SEEK_CUR); unsigned int linear_pitch = reader.readI(); ds.linearPitch = linear_pitch == 0 ? 0 : 2; @@ -2112,11 +2361,10 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si } if (blockVersion >= 3) { unsigned char flats = reader.readC(); - (void)flats; + logV("flats: %d",(int)flats); } } else if (blockName == "TUNING") { CHECK_BLOCK_VERSION(1); - // reader.seek(blockSize,SEEK_CUR); if (blockVersion == 1) { int fineTuneCents = reader.readC() * 100; fineTuneCents += reader.readC(); @@ -2125,6 +2373,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si } } else if (blockName == "BOOKMARKS") { CHECK_BLOCK_VERSION(1); + logW("block BOOKMARKS not supported..."); reader.seek(blockSize, SEEK_CUR); } else { logE("block %s is unknown!", blockName); @@ -2152,6 +2401,8 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si if (index < 0) index = 0; + if (index>=(int)ds.ins.size()) continue; + DivInstrument* ins = ds.ins[index]; if (ins->type == DIV_INS_FM) { From 5b495ec86628e0d16a126cb4610f4e572f9850a6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 14 Apr 2024 12:47:58 -0500 Subject: [PATCH 18/22] and now fix fcm9/dkc loading --- src/engine/fileOps/ftm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/fileOps/ftm.cpp b/src/engine/fileOps/ftm.cpp index aab9744c1..7f885708c 100644 --- a/src/engine/fileOps/ftm.cpp +++ b/src/engine/fileOps/ftm.cpp @@ -1602,7 +1602,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si DivSubSong* s = ds.subsong[i]; int framesLen=reader.readI(); - if (framesLen<=1 || framesLen>=256) { + if (framesLen<=1 || framesLen>256) { logE("frames out of range"); lastError = "frames out of range"; delete[] file; @@ -1625,7 +1625,7 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si } int patLen=reader.readI(); - if (patLen<1 || patLen>=256) { + if (patLen<1 || patLen>256) { logE("pattern length out of range"); lastError = "pattern length out of range"; delete[] file; From 50282fb2df61e362196006fb9281b9c648a88122 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 14 Apr 2024 13:01:18 -0500 Subject: [PATCH 19/22] de-irritate the user --- src/engine/sysDef.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 0b1be7a55..087124dcb 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1303,7 +1303,7 @@ void DivEngine::registerSystems() { sysDefs[DIV_SYSTEM_VRC7]=new DivSysDef( "Konami VRC7", NULL, 0x9d, 0, 6, true, false, 0x151, false, 0, 0, 0, - "like OPLL, but even more cost reductions applied. three less FM channels, and no drums mode...", + "like OPLL, but even more cost reductions applied. three FM channels went missing, and drums mode did as well...", {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6"}, {"F1", "F2", "F3", "F4", "F5", "F6"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, From 65cf86ff40ac327eb938e91836ce927ab796002e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 14 Apr 2024 13:19:10 -0500 Subject: [PATCH 20/22] use new issue templates issue #1814 --- .github/issue_template.md | 48 ------------------------------- .github/issue_template/config.yml | 1 + .github/issue_template/issue.yml | 34 ++++++++++++++++++++++ 3 files changed, 35 insertions(+), 48 deletions(-) delete mode 100644 .github/issue_template.md create mode 100644 .github/issue_template/config.yml create mode 100644 .github/issue_template/issue.yml diff --git a/.github/issue_template.md b/.github/issue_template.md deleted file mode 100644 index 60746d3c3..000000000 --- a/.github/issue_template.md +++ /dev/null @@ -1,48 +0,0 @@ -# EXTREMELY IMPORTANT NOTICE - PLEASE **READ**!!!!!!! - -BY SUBMITTING AN ISSUE, YOU HEREBY AGREE TO COMPLY WITH THESE TERMS. -FAILURE TO DO SO MAY RESULT IN YOUR ISSUE BEING DECLARED VOID. - -**ADDITIONALLY, FAILURE TO COMPLY WITH POINTS 1 AND 2 WILL RESULT IN THE INABILITY TO ISSUE FURTHER ISSUE REPORTS.** - -1. this section is exclusively for ISSUES related to Furnace (bugs, major annoyances and others). ONLY THINGS THAT COUNT AS **ISSUES** (ad pedem litterae). -2. **THIS SECTION IS NOT FOR SUGGESTIONS, REQUESTS, QUESTIONS, SHOWCASE OR ANY OTHER DISCUSSIONS THAT DO NOT MEET THE CRITERIA AND DEFINITION OF AN __ISSUE__.** - - see the Discussions section if you wish to submit these. -3. check whether your issue has been reported already. - - go to the Issues section, and use the search bar that appears on top of the Issues list. -4. include the following information: - - version of Furnace (help > about) - - operating system (and version) - - whether you have downloaded Furnace, or built it from source. -5. provide these details if you believe the issue is operating system and/or computer-specific: - - CPU model - - Windows: go to Control Panel > System. - - macOS: go to the Apple menu and select About This Mac... - - Linux: use `lscpu` or `cat /proc/cpuinfo`. - - graphics card (and driver version) - - Windows: open `dxdiag` and observe the Render tab. - - macOS: go to the Apple menu and select About This Mac... - - this information is not always shown. - - this information is not necessary if you use an Apple silicon Mac. - - Linux: use `glxinfo | grep OpenGL`. -6. if your issue is an abnormal program termination (a "Crash"), you must provide additional details: - - the furnace_crash.txt file that is created by Furnace after a Crash. this file is located in the following paths: - - Windows: `C:\Users\\furnace_crash.txt` - - Linux/other: `/tmp/furnace_crash.txt` - - on macOS this file is not generated. you may retrieve information about the Crash by clicking on "Report..." or "Show Details" in the "quit unexpectedly" dialog that appears following the Crash. - - make sure to remove any personal information for privacy reasons. - - be sure to select "Don't Send" afterwards. - - the furnace.log file located in: - - Windows: `C:\Users\\AppData\Roaming\furnace\furnace.log` - - macOS: `~/Library/Application Support/furnace/furnace.log` - - Linux: `~/.config/furnace/furnace.log` - - make sure to remove any personal information for privacy reasons. - -BY SUBMITTING AN ISSUE, YOU HEREBY AGREE TO COMPLY WITH THESE TERMS. -FAILURE TO DO SO MAY RESULT IN YOUR ISSUE BEING DECLARED VOID. - -**ADDITIONALLY, FAILURE TO COMPLY WITH POINTS 1 AND 2 WILL RESULT IN THE INABILITY TO ISSUE FURTHER ISSUE REPORTS.** - -***END OF NOTICE*** -PLEASE REMOVE THIS NOTICE AFTER READING. -FAILURE TO REMOVE THIS NOTICE IS NEGLIGENCE. diff --git a/.github/issue_template/config.yml b/.github/issue_template/config.yml new file mode 100644 index 000000000..3ba13e0ce --- /dev/null +++ b/.github/issue_template/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/issue_template/issue.yml b/.github/issue_template/issue.yml new file mode 100644 index 000000000..6356265d3 --- /dev/null +++ b/.github/issue_template/issue.yml @@ -0,0 +1,34 @@ +name: Issue Report +description: for issues (bugs, annoyances, crashes and similar). +body: + - type: markdown + attributes: + value: | + Let's report an issue. | + Suggestions, feature requests, questions, showcase or anything else? Go to the Discussions section. + - type: textarea + id: description + attributes: + label: Description + - type: textarea + id: steps + attributes: + label: Steps to Reproduce (if applicable) + - type: textarea + id: info + attributes: + label: Additional Information + - type: input + id: version + attributes: + label: Furnace version? (help > about) + validations: + required: true + - type: checkboxes + id: terms + attributes: + label: This is an issue + description: By submitting this issue, you affirm that this is an actual issue (not a suggestion, question or otherwise non-issue). + options: + - label: I hereby certify that the ticket I am submitting is an issue. + required: true From 7db722a8a710eb3674406b4d00165890286eea88 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 14 Apr 2024 13:28:38 -0500 Subject: [PATCH 21/22] what --- .github/issue_template/issue.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/issue_template/issue.yml b/.github/issue_template/issue.yml index 6356265d3..6cfc6ba6b 100644 --- a/.github/issue_template/issue.yml +++ b/.github/issue_template/issue.yml @@ -4,7 +4,6 @@ body: - type: markdown attributes: value: | - Let's report an issue. | Suggestions, feature requests, questions, showcase or anything else? Go to the Discussions section. - type: textarea id: description From 2a5487ac1988e3213e96d153ae2c08a1802b99aa Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 14 Apr 2024 13:35:52 -0500 Subject: [PATCH 22/22] a --- src/engine/fileOps/ftm.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine/fileOps/ftm.cpp b/src/engine/fileOps/ftm.cpp index 7f885708c..9b3b7d55b 100644 --- a/src/engine/fileOps/ftm.cpp +++ b/src/engine/fileOps/ftm.cpp @@ -577,7 +577,8 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len, bool dnft, bool dnft_si customHz = reader.readI(); } - if (customHz<1.0) customHz=1.0; + logV("before clamp: %f",customHz); + if (customHz>1000.0) customHz=1000.0; unsigned int newVibrato = 0;