diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 55756767a..06157e841 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -441,6 +441,8 @@ enum DivMemoryEntryType { DIV_MEMORY_WAVE_RAM, DIV_MEMORY_WAVE_STATIC, DIV_MEMORY_ECHO, + DIV_MEMORY_N163_LOAD, + DIV_MEMORY_N163_PLAY, DIV_MEMORY_BANK0, DIV_MEMORY_BANK1, DIV_MEMORY_BANK2, @@ -470,15 +472,25 @@ struct DivMemoryEntry { end(0) {} }; +enum DivMemoryWaveView: unsigned char { + DIV_MEMORY_WAVE_NONE=0, + DIV_MEMORY_WAVE_4BIT, // Namco 163 + DIV_MEMORY_WAVE_6BIT, // Virtual Boy +}; + struct DivMemoryComposition { std::vector entries; String name; size_t capacity; size_t used; + const unsigned char* memory; + DivMemoryWaveView waveformView; DivMemoryComposition(): name(""), capacity(0), - used(0) {} + used(0), + memory(NULL), + waveformView(DIV_MEMORY_WAVE_NONE) {} }; class DivEngine; diff --git a/src/engine/engine.h b/src/engine/engine.h index 242e465dd..48335692f 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -542,7 +542,7 @@ class DivEngine { void testFunction(); bool loadDMF(unsigned char* file, size_t len); - bool loadFur(unsigned char* file, size_t len); + bool loadFur(unsigned char* file, size_t len, int variantID=0); bool loadMod(unsigned char* file, size_t len); bool loadS3M(unsigned char* file, size_t len); bool loadFTM(unsigned char* file, size_t len); diff --git a/src/engine/fileOps/fileOpsCommon.cpp b/src/engine/fileOps/fileOpsCommon.cpp index 56046b078..6eadfef62 100644 --- a/src/engine/fileOps/fileOpsCommon.cpp +++ b/src/engine/fileOps/fileOpsCommon.cpp @@ -131,6 +131,8 @@ bool DivEngine::load(unsigned char* f, size_t slen) { return loadFTM(file,len); } else if (memcmp(file,DIV_FUR_MAGIC,16)==0) { return loadFur(file,len); + } else if (memcmp(file,DIV_FUR_MAGIC_DS0,16)==0) { + return loadFur(file,len,DIV_FUR_VARIANT_B); } else if (memcmp(file,DIV_FC13_MAGIC,4)==0 || memcmp(file,DIV_FC14_MAGIC,4)==0) { return loadFC(file,len); } diff --git a/src/engine/fileOps/fileOpsCommon.h b/src/engine/fileOps/fileOpsCommon.h index baceadaf3..e758f0ed5 100644 --- a/src/engine/fileOps/fileOpsCommon.h +++ b/src/engine/fileOps/fileOpsCommon.h @@ -52,3 +52,10 @@ struct NotZlibException { #define DIV_FC13_MAGIC "SMOD" #define DIV_FC14_MAGIC "FC14" #define DIV_S3M_MAGIC "SCRM" + +#define DIV_FUR_MAGIC_DS0 "Furnace-B module" + +enum DivFurVariants: int { + DIV_FUR_VARIANT_VANILLA=0, + DIV_FUR_VARIANT_B=1, +}; diff --git a/src/engine/fileOps/fur.cpp b/src/engine/fileOps/fur.cpp index 5397a266c..0cd32fa76 100644 --- a/src/engine/fileOps/fur.cpp +++ b/src/engine/fileOps/fur.cpp @@ -691,7 +691,7 @@ void DivEngine::convertOldFlags(unsigned int oldFlags, DivConfig& newFlags, DivS } } -bool DivEngine::loadFur(unsigned char* file, size_t len) { +bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) { unsigned int insPtr[256]; unsigned int wavePtr[256]; unsigned int samplePtr[256]; @@ -720,6 +720,11 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { ds.version=reader.readS(); logI("module version %d (0x%.2x)",ds.version,ds.version); + if (variantID!=DIV_FUR_VARIANT_VANILLA) { + logW("Furnace variant detected: %d",variantID); + addWarning("this module was created with a downstream version of Furnace. certain features may not be compatible."); + } + if (ds.version>DIV_ENGINE_VERSION) { logW("this module was created with a more recent version of Furnace!"); addWarning("this module was created with a more recent version of Furnace!"); diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 37bd99f57..8ad21b100 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -2952,6 +2952,8 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version, DivS type=0; } else if (memcmp(magic,"INS2",4)==0) { type=1; + } else if (memcmp(magic,"IN2B",4)==0) { // DIV_FUR_VARIANT_B + type=1; } else if (memcmp(magic,"FINS",4)==0) { type=2; } else { diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index 5e2ccd9c0..d1ceaf5a0 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -279,6 +279,19 @@ void DivPlatformN163::tick(bool sysTick) { chan[i].freqChanged=false; } } + + // update memory composition positions + for (int i=0; i<=chanMax; i++) { + memCompo.entries[i].begin=chan[i].wavePos>>1; + memCompo.entries[i].end=(chan[i].wavePos+chan[i].waveLen)>>1; + memCompo.entries[i+8].begin=chan[i].curWavePos>>1; + memCompo.entries[i+8].end=(chan[i].curWavePos+chan[i].curWaveLen)>>1; + } + + // update register pool + for (int i=0; i<128; i++) { + regPool[i]=n163.reg(i); + } } int DivPlatformN163::dispatch(DivCommand c) { @@ -476,6 +489,7 @@ void DivPlatformN163::forceIns() { chan[i].waveChanged=true; } } + memCompo.entries[16].begin=120-chanMax*8; } void DivPlatformN163::notifyWaveChange(int wave) { @@ -516,9 +530,6 @@ DivDispatchOscBuffer* DivPlatformN163::getOscBuffer(int ch) { } unsigned char* DivPlatformN163::getRegisterPool() { - for (int i=0; i<128; i++) { - regPool[i]=n163.reg(i); - } return regPool; } @@ -544,6 +555,8 @@ void DivPlatformN163::reset() { loadWave=-1; loadPos=0; rWrite(0x7f,initChanMax<<4); + + memCompo.entries[16].begin=120-chanMax*8; } void DivPlatformN163::poke(unsigned int addr, unsigned short val) { @@ -554,6 +567,11 @@ void DivPlatformN163::poke(std::vector& wlist) { for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); } +const DivMemoryComposition* DivPlatformN163::getMemCompo(int index) { + if (index!=0) return NULL; + return &memCompo; +} + void DivPlatformN163::setFlags(const DivConfig& flags) { switch (flags.getInt("clockSel",0)) { case 1: // PAL @@ -591,6 +609,20 @@ int DivPlatformN163::init(DivEngine* p, int channels, int sugRate, const DivConf isMuted[i]=false; oscBuf[i]=new DivDispatchOscBuffer; } + + memCompo.used=0; + memCompo.capacity=128; + memCompo.memory=regPool; + memCompo.waveformView=DIV_MEMORY_WAVE_4BIT; + + for (int i=0; i<8; i++) { + memCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_N163_LOAD,fmt::sprintf("Channel %d (load)",i),-1,0,0)); + } + for (int i=0; i<8; i++) { + memCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_N163_PLAY,fmt::sprintf("Channel %d (play)",i),-1,0,0)); + } + memCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_RESERVED,"Registers",-1,127,128)); + setFlags(flags); reset(); diff --git a/src/engine/platform/n163.h b/src/engine/platform/n163.h index 648a7af24..2c7c404fc 100644 --- a/src/engine/platform/n163.h +++ b/src/engine/platform/n163.h @@ -65,6 +65,7 @@ class DivPlatformN163: public DivDispatch { n163_core n163; unsigned char regPool[128]; + DivMemoryComposition memCompo; void updateWave(int ch, int wave, int pos, int len); void updateWaveCh(int ch); friend void putDispatchChip(void*,int); @@ -82,6 +83,7 @@ class DivPlatformN163: public DivDispatch { void forceIns(); void tick(bool sysTick=true); void muteChannel(int ch, bool mute); + const DivMemoryComposition* getMemCompo(int index); void setFlags(const DivConfig& flags); void notifyWaveChange(int wave); void notifyInsChange(int ins); diff --git a/src/gui/gui.h b/src/gui/gui.h index e99964454..8358be6d5 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -411,6 +411,7 @@ enum FurnaceGUIColors { GUI_COLOR_PATCHBAY_CONNECTION_HI, GUI_COLOR_MEMORY_BG, + GUI_COLOR_MEMORY_DATA, GUI_COLOR_MEMORY_FREE, GUI_COLOR_MEMORY_PADDING, GUI_COLOR_MEMORY_RESERVED, @@ -421,6 +422,8 @@ enum FurnaceGUIColors { GUI_COLOR_MEMORY_WAVE_RAM, GUI_COLOR_MEMORY_WAVE_STATIC, GUI_COLOR_MEMORY_ECHO, + GUI_COLOR_MEMORY_N163_LOAD, + GUI_COLOR_MEMORY_N163_PLAY, GUI_COLOR_MEMORY_BANK0, GUI_COLOR_MEMORY_BANK1, GUI_COLOR_MEMORY_BANK2, diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 31567ea3b..3d7fffc90 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -1109,6 +1109,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_PATCHBAY_CONNECTION_HI,"",ImVec4(0.2f,1.0f,1.0f,1.0f)), D(GUI_COLOR_MEMORY_BG,"",ImVec4(0.12f,0.12f,0.12f,1.0f)), + D(GUI_COLOR_MEMORY_DATA,"",ImVec4(1.0f,1.0f,1.0f,0.8f)), D(GUI_COLOR_MEMORY_FREE,"",ImVec4(0.25f,0.25f,0.25f,1.0f)), D(GUI_COLOR_MEMORY_PADDING,"",ImVec4(0.25f,0.25f,0.25f,1.0f)), D(GUI_COLOR_MEMORY_RESERVED,"",ImVec4(0.5f,0.5f,0.6f,1.0f)), @@ -1119,6 +1120,8 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_MEMORY_WAVE_RAM,"",ImVec4(1.0f,0.5f,0.1f,1.0f)), D(GUI_COLOR_MEMORY_WAVE_STATIC,"",ImVec4(1.0f,0.3f,0.1f,1.0f)), D(GUI_COLOR_MEMORY_ECHO,"",ImVec4(0.2f,1.0f,1.0f,1.0f)), + D(GUI_COLOR_MEMORY_N163_LOAD,"",ImVec4(1.0f,0.5f,0.1f,1.0f)), + D(GUI_COLOR_MEMORY_N163_PLAY,"",ImVec4(1.0f,1.0f,0.2f,1.0f)), D(GUI_COLOR_MEMORY_BANK0,"",ImVec4(1.0f,0.1f,0.1f,1.0f)), D(GUI_COLOR_MEMORY_BANK1,"",ImVec4(1.0f,0.5f,0.1f,1.0f)), D(GUI_COLOR_MEMORY_BANK2,"",ImVec4(1.0f,1.0f,0.1f,1.0f)), diff --git a/src/gui/memory.cpp b/src/gui/memory.cpp index c98c3f127..3a506a03c 100644 --- a/src/gui/memory.cpp +++ b/src/gui/memory.cpp @@ -56,17 +56,28 @@ void FurnaceGUI::drawMemory() { minArea.y+size.y ); ImRect rect=ImRect(minArea,maxArea); + ImRect dataRect=rect; + ImRect entryRect=rect; ImGuiStyle& style=ImGui::GetStyle(); ImGui::ItemSize(size,style.FramePadding.y); snprintf(tempID,1023,"MC%d_%d",i,j); if (ImGui::ItemAdd(rect,ImGui::GetID(tempID))) { dl->AddRectFilled(rect.Min,rect.Max,ImGui::GetColorU32(uiColors[GUI_COLOR_MEMORY_BG])); + if (mc->memory!=NULL && mc->waveformView!=DIV_MEMORY_WAVE_NONE) { + dataRect.Max.y-=8.0f*dpiScale; + entryRect.Min.y=entryRect.Max.y-8.0f*dpiScale; + } + int curHover=-1; int kIndex=0; if (mc->capacity>0) for (const DivMemoryEntry& k: mc->entries) { - ImVec2 pos1=ImLerp(rect.Min,rect.Max,ImVec2((double)k.begin/(double)mc->capacity,0.0f)); - ImVec2 pos2=ImLerp(rect.Min,rect.Max,ImVec2((double)k.end/(double)mc->capacity,1.0f)); + if (k.begin==k.end) { + kIndex++; + continue; + } + ImVec2 pos1=ImLerp(entryRect.Min,entryRect.Max,ImVec2((double)k.begin/(double)mc->capacity,(k.type==DIV_MEMORY_N163_LOAD)?0.5f:0.0f)); + ImVec2 pos2=ImLerp(entryRect.Min,entryRect.Max,ImVec2((double)k.end/(double)mc->capacity,(k.type==DIV_MEMORY_N163_PLAY)?0.5f:1.0f)); ImVec2 linePos=pos1; linePos.y=rect.Max.y; @@ -79,6 +90,27 @@ void FurnaceGUI::drawMemory() { kIndex++; } + if (mc->memory!=NULL) { + switch (mc->waveformView) { + case DIV_MEMORY_WAVE_4BIT: + for (int k=0; k<(int)(mc->capacity<<1); k++) { + unsigned char nibble=mc->memory[k>>1]; + if (k&1) { + nibble&=15; + } else { + nibble>>=4; + } + + ImVec2 pos1=ImLerp(dataRect.Min,dataRect.Max,ImVec2((double)k/(double)(mc->capacity<<1),1.0f-((float)(nibble+1)/16.0f))); + ImVec2 pos2=ImLerp(dataRect.Min,dataRect.Max,ImVec2((double)(k+1)/(double)(mc->capacity<<1),1.0f)); + dl->AddRectFilled(pos1,pos2,ImGui::GetColorU32(uiColors[GUI_COLOR_MEMORY_DATA])); + } + break; + default: + break; + } + } + if (ImGui::ItemHoverable(rect,ImGui::GetID(tempID),0)) { if (curHover>=0 && curHover<(int)mc->entries.size()) { const DivMemoryEntry& entry=mc->entries[curHover]; diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 68e30445d..6d7d11d69 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -3685,6 +3685,7 @@ void FurnaceGUI::drawSettings() { } if (ImGui::TreeNode("Memory Composition")) { UI_COLOR_CONFIG(GUI_COLOR_MEMORY_BG,"Background"); + UI_COLOR_CONFIG(GUI_COLOR_MEMORY_DATA,"Waveform data"); UI_COLOR_CONFIG(GUI_COLOR_MEMORY_FREE,"Unknown"); //UI_COLOR_CONFIG(GUI_COLOR_MEMORY_PADDING,""); UI_COLOR_CONFIG(GUI_COLOR_MEMORY_RESERVED,"Reserved"); @@ -3695,6 +3696,8 @@ void FurnaceGUI::drawSettings() { UI_COLOR_CONFIG(GUI_COLOR_MEMORY_WAVE_RAM,"Wave RAM"); UI_COLOR_CONFIG(GUI_COLOR_MEMORY_WAVE_STATIC,"Wavetable (static)"); UI_COLOR_CONFIG(GUI_COLOR_MEMORY_ECHO,"Echo buffer"); + UI_COLOR_CONFIG(GUI_COLOR_MEMORY_N163_LOAD,"Namco 163 load pos"); + UI_COLOR_CONFIG(GUI_COLOR_MEMORY_N163_PLAY,"Namco 163 play pos"); UI_COLOR_CONFIG(GUI_COLOR_MEMORY_BANK0,"Sample (bank 0)"); UI_COLOR_CONFIG(GUI_COLOR_MEMORY_BANK1,"Sample (bank 1)"); UI_COLOR_CONFIG(GUI_COLOR_MEMORY_BANK2,"Sample (bank 2)");