From 873bad16138f9d936bdf1ecc8303e1040a8c7c28 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 7 Apr 2025 04:33:28 -0500 Subject: [PATCH] stuff --- src/engine/cmdStreamOps.cpp | 416 ++++++++++++++++++++---------------- src/gui/csPlayer.cpp | 11 +- src/gui/exportOptions.cpp | 24 ++- src/gui/gui.cpp | 1 + src/gui/gui.h | 4 + 5 files changed, 269 insertions(+), 187 deletions(-) diff --git a/src/engine/cmdStreamOps.cpp b/src/engine/cmdStreamOps.cpp index e16a6b876..492e863a9 100644 --- a/src/engine/cmdStreamOps.cpp +++ b/src/engine/cmdStreamOps.cpp @@ -273,7 +273,7 @@ int DivCS::getInsLength(unsigned char ins, unsigned char ext, unsigned char* spe return 1; } -void writePackedCommandValues(SafeWriter* w, const DivCommand& c) { +void writeCommandValues(SafeWriter* w, const DivCommand& c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: if (c.value==DIV_NOTE_NULL) { @@ -562,6 +562,8 @@ void writePackedCommandValues(SafeWriter* w, const DivCommand& c) { logW("unimplemented command %s!",cmdName[c.cmd]); break; } + // padding (TODO: optimize) + while (w->tell()&7) w->writeC(0); } #define _EXT(b,x,l) (((size_t)((x)+1)<(size_t)(l))?(b[(x)+1]):0) @@ -608,19 +610,181 @@ SafeWriter* stripNops(SafeWriter* s, unsigned char* speedDial) { // prepare address map size_t addr=0; - for (size_t i=0; isize();) { - int insLen=getInsLength(buf[i],_EXT(buf,i,oldStream->size()),speedDial); - if (insLen<1) { - logE("INS %x NOT IMPLEMENTED...",buf[i]); - break; - } + for (size_t i=0; isize(); i+=8) { addrTable[i]=addr; - if (buf[i]!=0xf1) addr+=insLen; - i+=insLen; + if (buf[i]!=0xf1) addr+=8; } // translate addresses - for (size_t i=0; isize();) { + for (size_t i=0; isize(); i+=8) { + switch (buf[i]) { + case 0xf5: // calli + case 0xfa: { // jmp + unsigned int addr=buf[i+1]|(buf[i+2]<<8)|(buf[i+3]<<16)|(buf[i+4]<<24); + try { + addr=addrTable[addr]; + buf[i+1]=addr&0xff; + buf[i+2]=(addr>>8)&0xff; + buf[i+3]=(addr>>16)&0xff; + buf[i+4]=(addr>>24)&0xff; + } catch (std::out_of_range& e) { + logW("address %x is not mappable!",addr); + } + break; + } + case 0xf8: { // call + unsigned int addr=buf[i+1]|(buf[i+2]<<8); + try { + addr=addrTable[addr]; + buf[i+1]=addr&0xff; + buf[i+2]=(addr>>8)&0xff; + } catch (std::out_of_range& e) { + logW("address %x is not mappable!",addr); + } + break; + } + } + if (buf[i]!=0xf1) { + s->write(&buf[i],8); + } + } + + oldStream->finish(); + delete oldStream; + return s; +} + +SafeWriter* findSubBlocks(SafeWriter* stream, std::vector& subBlocks, unsigned char* speedDial) { + unsigned char* buf=stream->getFinalBuf(); + + for (size_t groupSize=stream->size()>>1; groupSize>=32; groupSize-=8) { + bool foundSomething=false; + logV("...try size %d",groupSize); + for (size_t searchPos=0; (searchPos+groupSize)size();) { + const unsigned char* group=&buf[searchPos]; + size_t groupLen=0; + size_t groupInsCount=0; + size_t subBlockID=subBlocks.size(); + bool haveSub=false; + bool onlyCalls=true; + + // register this block + for (size_t i=0; isize(); i+=8) { + if (buf[searchPos+i]!=0xf4) onlyCalls=false; + if (groupLen+8>groupSize) break; + groupLen+=8; + groupInsCount++; + } + + // don't do anything if we don't have a block large enough + if (groupLen<24) { + searchPos+=8; + continue; + } + + // don't do anything if this is just one or two commands + // TODO: this is a duplicate of the previous statement now that all commands are 8 bytes long + if (groupInsCount<3) { + searchPos+=8; + continue; + } + + // don't do anything if this block only consists of calls + if (onlyCalls) { + logW("nothing but calls."); + searchPos+=8; + continue; + } + + // find identical blocks + for (size_t i=searchPos+groupLen; i+groupLensize();) { + // compare next block to group + if (memcmp(&buf[i],group,groupLen)==0) { + // we have a sub-block + if (!haveSub) { + // isolate this sub-block + SafeWriter* newBlock=new SafeWriter; + newBlock->init(); + newBlock->write(group,groupLen); + newBlock->writeC(0xf9); // ret + // padding + newBlock->writeC(0); + newBlock->writeC(0); + newBlock->writeC(0); + newBlock->writeC(0); + newBlock->writeC(0); + newBlock->writeC(0); + newBlock->writeC(0); + subBlocks.push_back(newBlock); + haveSub=true; + logD("- SUB %x (size %d):",searchPos,groupLen); + } + logD(" - %x",i); + // insert call + buf[i]=0xf4; + buf[i+1]=subBlockID&0xff; + buf[i+2]=(subBlockID>>8)&0xff; + buf[i+3]=(subBlockID>>16)&0xff; + buf[i+4]=(subBlockID>>24)&0xff; + + // replace the rest with nop + for (size_t j=i+8; j>8)&0xff; + buf[searchPos+3]=(subBlockID>>16)&0xff; + buf[searchPos+4]=(subBlockID>>24)&0xff; + + // replace the rest with nop + for (size_t j=searchPos+8; jgetFinalBuf(); + } + } + return stream; +} + +SafeWriter* packStream(SafeWriter* s, unsigned char* speedDial) { + std::unordered_map addrTable; + SafeWriter* oldStream=s; + unsigned char* buf=oldStream->getFinalBuf(); + s=new SafeWriter; + s->init(); + + // prepare address map + size_t addr=0; + for (size_t i=0; isize(); i+=8) { + addrTable[i]=addr; + addr+=getInsLength(buf[i],_EXT(buf,i,oldStream->size()),speedDial); + } + + // translate addresses and write stream + for (size_t i=0; isize(); i+=8) { int insLen=getInsLength(buf[i],_EXT(buf,i,oldStream->size()),speedDial); if (insLen<1) { logE("INS %x NOT IMPLEMENTED...",buf[i]); @@ -653,143 +817,14 @@ SafeWriter* stripNops(SafeWriter* s, unsigned char* speedDial) { break; } } - if (buf[i]!=0xf1) { - s->write(&buf[i],insLen); - } - i+=insLen; + s->write(&buf[i],insLen); } - oldStream->finish(); delete oldStream; return s; } -SafeWriter* findSubBlocks(SafeWriter* stream, std::vector& subBlocks, unsigned char* speedDial) { - unsigned char* buf=stream->getFinalBuf(); - - for (size_t groupSize=stream->size()>>1; groupSize>=8; groupSize--) { - //for (size_t groupSize=7; groupSize<=stream->size()>>1; groupSize++) { - bool foundSomething=false; - logV("...try size %d",groupSize); - for (size_t searchPos=0; (searchPos+groupSize)size();) { - const unsigned char* group=&buf[searchPos]; - size_t groupLen=0; - size_t groupInsCount=0; - size_t subBlockID=subBlocks.size(); - int insLen=getInsLength(buf[searchPos],_EXT(buf,searchPos,stream->size()),speedDial); - bool haveSub=false; - bool onlyCalls=true; - - if (insLen<1) { - logE("INS %x NOT IMPLEMENTED...",buf[searchPos]); - break; - } - - // register this block - for (size_t i=0; isize();) { - if (buf[searchPos+i]!=0xf4) onlyCalls=false; - int insLenI=getInsLength(buf[searchPos+i],_EXT(buf,searchPos+i,stream->size()),speedDial); - if (insLenI<1) { - logE("INS %x NOT IMPLEMENTED...",buf[searchPos+i]); - break; - } - i+=insLenI; - if (groupLen+insLenI>groupSize) break; - groupLen+=insLenI; - groupInsCount++; - } - - // don't do anything if we don't have a block large enough - if (groupLen<5) { - searchPos+=insLen; - continue; - } - - // don't do anything if this is just one or two commands - if (groupInsCount<3) { - searchPos+=insLen; - continue; - } - - // don't do anything if this block only consists of calls - if (onlyCalls) { - logW("nothing but calls."); - searchPos+=insLen; - continue; - } - - // find identical blocks - for (size_t i=searchPos+groupLen; i+groupLensize();) { - int insLenI=getInsLength(buf[i],_EXT(buf,i,stream->size()),speedDial); - if (insLenI<1) { - logE("INS %x NOT IMPLEMENTED...",buf[i]); - break; - } - - // compare next block to group - if (memcmp(&buf[i],group,groupLen)==0) { - // we have a sub-block - if (!haveSub) { - // isolate this sub-block - SafeWriter* newBlock=new SafeWriter; - newBlock->init(); - newBlock->write(group,groupLen); - newBlock->writeC(0xf9); // ret - subBlocks.push_back(newBlock); - haveSub=true; - logD("- SUB %x (size %d):",searchPos,groupLen); - } - logD(" - %x",i); - // insert call - buf[i]=0xf4; - buf[i+1]=subBlockID&0xff; - buf[i+2]=(subBlockID>>8)&0xff; - buf[i+3]=(subBlockID>>16)&0xff; - buf[i+4]=(subBlockID>>24)&0xff; - - // replace the rest with nop - for (size_t j=i+5; j>8)&0xff; - buf[searchPos+3]=(subBlockID>>16)&0xff; - buf[searchPos+4]=(subBlockID>>24)&0xff; - - // replace the rest with nop - for (size_t j=searchPos+5; jgetFinalBuf(); - } - } - return stream; -} - SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, unsigned int disablePasses) { stop(); repeatPattern=false; @@ -867,6 +902,11 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, unsigned int disable chanStream[i]->writeC(0x00); chanStream[i]->writeC(0x00); chanStream[i]->writeC(0x00); + // padding + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); } } } @@ -880,6 +920,10 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, unsigned int disable curDivider=divider; chanStream[0]->writeC(0xfb); chanStream[0]->writeI((int)(curDivider*65536)); + // padding + chanStream[0]->writeC(0x00); + chanStream[0]->writeC(0x00); + chanStream[0]->writeC(0x00); } for (DivCommand& i: cmdStream) { switch (i.cmd) { @@ -894,19 +938,35 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, unsigned int disable break; default: cmdPopularity[i.cmd]++; - writePackedCommandValues(chanStream[i.chan],i); + writeCommandValues(chanStream[i.chan],i); break; } } cmdStream.clear(); for (int i=0; iwriteC(0xfe); + // padding + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); } tick++; } if (!playing || loopTick<0) { for (int i=0; iwriteC(0xff); + // padding + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); } } else { for (int i=0; iwriteC(0xfa); chanStream[i]->writeI(tickPos[i][loopTick]); logD("chan %d loop addr: %x",i,tickPos[i][loopTick]); + // padding + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); } else { logW("chan %d unable to find loop addr!",i); chanStream[i]->writeC(0xff); + // padding + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); + chanStream[i]->writeC(0x00); } } } @@ -956,28 +1028,20 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, unsigned int disable // set speed dial commands for (int h=0; hgetFinalBuf(); - for (size_t i=0; isize();) { - int insLen=getInsLength(buf[i],_EXT(buf,i,chanStream[h]->size()),sortedCmd); - if (insLen<1) { - logE("INS %x NOT IMPLEMENTED...",buf[i]); - break; - } + for (size_t i=0; isize(); i+=8) { if (buf[i]==0xf7) { // find whether this command is in speed dial for (int j=0; j<16; j++) { if (buf[i+1]==sortedCmd[j]) { buf[i]=0xd0+j; // move everything to the left - for (int k=i+2; k<(int)i+insLen; k++) { + for (int k=i+2; k<(int)i+8; k++) { buf[k-1]=buf[k]; } - // put a nop - buf[i+insLen-1]=0xf1; break; } } } - i+=insLen; } } } @@ -988,12 +1052,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, unsigned int disable for (int h=0; hgetFinalBuf(); int delayCount=0; - for (size_t i=0; isize();) { - int insLen=getInsLength(buf[i],_EXT(buf,i,chanStream[h]->size()),sortedCmd); - if (insLen<1) { - logE("INS %x NOT IMPLEMENTED...",buf[i]); - break; - } + for (size_t i=0; isize(); i+=8) { if (buf[i]==0xfe) { delayCount++; } else { @@ -1002,7 +1061,6 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, unsigned int disable } delayCount=0; } - i+=insLen; } } @@ -1035,12 +1093,7 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, unsigned int disable int delayPos=-1; int delayCount=0; int delayLast=0; - for (size_t i=0; isize();) { - int insLen=getInsLength(buf[i],_EXT(buf,i,chanStream[h]->size()),sortedCmd); - if (insLen<1) { - logE("INS %x NOT IMPLEMENTED...",buf[i]); - break; - } + for (size_t i=0; isize(); i+=8) { if (buf[i]==0xfe) { if (delayPos==-1) delayPos=i; delayCount++; @@ -1081,7 +1134,6 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, unsigned int disable delayCount=0; } } - i+=insLen; } } } @@ -1140,6 +1192,8 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, unsigned int disable } else { // write sub-block blockOff.push_back(chanStream[h]->tell()); + logV("block size: %d",(int)block->size()); + assert(!(block->size()&7)); chanStream[h]->write(block->getFinalBuf(),block->size()); } } @@ -1152,39 +1206,26 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, unsigned int disable // resolve symbols unsigned char* buf=chanStream[h]->getFinalBuf(); - for (size_t j=0; jsize();) { - int insLen=getInsLength(buf[j],_EXT(buf,j,chanStream[h]->size()),sortedCmd); - if (insLen<1) { - logE("INS %x NOT IMPLEMENTED...",buf[j]); - break; - } + for (size_t j=0; jsize(); j+=8) { if (buf[j]==0xf4) { // callsym unsigned int addr=buf[j+1]|(buf[j+2]<<8)|(buf[j+3]<<16)|(buf[j+4]<<24); if (addr>8)&0xff; - buf[j+3]=0xf1; - buf[j+4]=0xf1; - } else { - buf[j]=0xf5; - buf[j+1]=addr&0xff; - buf[j+2]=(addr>>8)&0xff; - buf[j+3]=(addr>>16)&0xff; - buf[j+4]=(addr>>24)&0xff; - } + buf[j]=0xf5; + buf[j+1]=addr&0xff; + buf[j+2]=(addr>>8)&0xff; + buf[j+3]=(addr>>16)&0xff; + buf[j+4]=(addr>>24)&0xff; } else { logE("requested symbol %d is out of bounds!",addr); } } - j+=insLen; } size_t afterSize=chanStream[h]->size(); logI("(before: %d - after: %d)",(int)beforeSize,(int)afterSize); + assert(!(chanStream[h]->size()&7)); } } @@ -1193,6 +1234,11 @@ SafeWriter* DivEngine::saveCommand(DivCSProgress* progress, unsigned int disable chanStream[h]=stripNops(chanStream[h],sortedCmd); } + // PASS 6: pack streams + for (int h=0; htell(); diff --git a/src/gui/csPlayer.cpp b/src/gui/csPlayer.cpp index 2e36837fb..9b4506c94 100644 --- a/src/gui/csPlayer.cpp +++ b/src/gui/csPlayer.cpp @@ -206,7 +206,7 @@ void FurnaceGUI::drawCSPlayer() { } ImGui::SameLine(); if (ImGui::Button(_("Burn Current Song"))) { - SafeWriter* w=e->saveCommand(); + SafeWriter* w=e->saveCommand(NULL,csExportDisablePass); if (w!=NULL) { if (!e->playStream(w->getFinalBuf(),w->size())) { showError(e->getLastError()); @@ -218,12 +218,21 @@ void FurnaceGUI::drawCSPlayer() { } } } + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + ImGui::OpenPopup("CSOptions"); + } ImGui::SameLine(); if (e->isHalted()) { if (ImGui::Button("Resume")) e->resume(); } else { if (ImGui::Button("Pause")) e->halt(); } + ImGui::SameLine(); + ImGui::Button(_("Burn Options")); + if (ImGui::BeginPopupContextItem("CSOptions",ImGuiPopupFlags_MouseButtonLeft)) { + commandExportOptions(); + ImGui::EndPopup(); + } DivCSPlayer* cs=e->getStreamPlayer(); if (cs) { diff --git a/src/gui/exportOptions.cpp b/src/gui/exportOptions.cpp index 0e17a112e..3c6ac1533 100644 --- a/src/gui/exportOptions.cpp +++ b/src/gui/exportOptions.cpp @@ -380,16 +380,38 @@ void FurnaceGUI::drawExportText(bool onWindow) { } } +void FurnaceGUI::commandExportOptions() { + bool noCmdCallOpt=(csExportDisablePass&1); + bool noDelayCondense=(csExportDisablePass&2); + bool noSubBlock=(csExportDisablePass&4); + + if (ImGui::Checkbox(_("Don't optimize command calls"),&noCmdCallOpt)) { + csExportDisablePass&=~1; + csExportDisablePass|=noCmdCallOpt?1:0; + } + if (ImGui::Checkbox(_("Don't condense delays"),&noDelayCondense)) { + csExportDisablePass&=~2; + csExportDisablePass|=noDelayCondense?2:0; + } + if (ImGui::Checkbox(_("Don't perform sub-block search"),&noSubBlock)) { + csExportDisablePass&=~4; + csExportDisablePass|=noSubBlock?4:0; + } +} + void FurnaceGUI::drawExportCommand(bool onWindow) { exitDisabledTimer=1; ImGui::Text(_( - "this option exports a text or binary file which\n" + "this option exports a binary file which\n" "contains a dump of the internal command stream\n" "produced when playing the song.\n\n" "technical/development use only!" )); + + commandExportOptions(); + if (onWindow) { ImGui::Separator(); if (ImGui::Button(_("Cancel"),ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup(); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index a5c38611a..fe8159203 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -8877,6 +8877,7 @@ FurnaceGUI::FurnaceGUI(): curTutorialStep(0), dmfExportVersion(0), curExportType(GUI_EXPORT_NONE), + csExportDisablePass(0), romTarget(DIV_ROM_ABSTRACT), romMultiFile(false), romExportSave(false), diff --git a/src/gui/gui.h b/src/gui/gui.h index 95032fa2c..a41ba0b80 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -2761,6 +2761,8 @@ class FurnaceGUI { DivAudioExportOptions audioExportOptions; int dmfExportVersion; FurnaceGUIExportTypes curExportType; + unsigned int csExportDisablePass; + DivCSProgress csProgress; // ROM export specific DivROMExportOptions romTarget; @@ -2778,6 +2780,8 @@ class FurnaceGUI { std::vector randomDemoSong; + void commandExportOptions(); + void drawExportAudio(bool onWindow=false); void drawExportVGM(bool onWindow=false); void drawExportROM(bool onWindow=false);