From 9e252566c63d3e74611d686f511637f806de7d2c Mon Sep 17 00:00:00 2001 From: LTVA1 <87536432+LTVA1@users.noreply.github.com> Date: Sat, 2 Dec 2023 14:33:53 +0300 Subject: [PATCH] looks like the pasting works, untested though --- src/gui/editing.cpp | 825 ++++++++++++++++++++++++++++++++++++++++++-- src/gui/gui.h | 2 + 2 files changed, 796 insertions(+), 31 deletions(-) diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index 45603e68c..379dddde1 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -24,6 +24,18 @@ #include "actionUtil.h" +static const char* text_format_headers[] = +{ + "ModPlug Tracker MOD", + "ModPlug Tracker S3M", + "ModPlug Tracker XM", + "ModPlug Tracker XM", + "ModPlug Tracker IT", + "ModPlug Tracker IT", + "ModPlug Tracker MPT", + NULL, +}; + const char* FurnaceGUI::noteNameNormal(short note, short octave) { if (note==100) { // note cut return "OFF"; @@ -433,36 +445,8 @@ String FurnaceGUI::doCopy(bool cut, bool writeClipboard, const SelectionPoint& s return clipb; } -void FurnaceGUI::doPaste(PasteMode mode, int arg, bool readClipboard, String clipb) { - if (readClipboard) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_PASTE); - char* clipText=SDL_GetClipboardText(); - if (clipText!=NULL) { - if (clipText[0]) { - clipboard=clipText; - } - SDL_free(clipText); - } - clipb=clipboard; - } - std::vector data; - String tempS; - for (char i: clipb) { - if (i=='\r') continue; - if (i=='\n') { - data.push_back(tempS); - tempS=""; - continue; - } - tempS+=i; - } - data.push_back(tempS); - - int startOff=-1; - bool invalidData=false; - if (data.size()<2) return; - if (data[0].find("org.tildearrow.furnace - Pattern Data")!=0) return; +void FurnaceGUI::doPasteFurnace(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int startOff, bool invalidData) +{ if (sscanf(data[1].c_str(),"%d",&startOff)!=1) return; if (startOff<0) return; @@ -513,7 +497,7 @@ void FurnaceGUI::doPaste(PasteMode mode, int arg, bool readClipboard, String cli } if ((mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG || - mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) && strcmp(note,"...")==0) { + mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) && strcmp(note,"...")==0) { // do nothing. } else { if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || (pat->data[j][0]==0 && pat->data[j][1]==0)) { @@ -607,6 +591,785 @@ void FurnaceGUI::doPaste(PasteMode mode, int arg, bool readClipboard, String cli } } +uint64_t convert_effect_openmpt_mod(char symbol, uint16_t val) +{ + switch(symbol) + { + case '0': + { + return ((0x00) << 8) | (val); + break; + } + + case '1': + { + return ((0x01) << 8) | (val); + break; + } + + case '2': + { + return ((0x02) << 8) | (val); + break; + } + + case '3': + { + return ((0x03) << 8) | (val); + break; + } + + case '4': + { + return ((0x04) << 8) | (val); + break; + } + + case '5': + { + return ((0x0a) << 8) | (val) | ((0x03) << 24); //Axy + 300 + break; + } + + case '6': + { + return ((0x0a) << 8) | (val) | ((0x04) << 24); //Axy + 400 + break; + } + + case '7': + { + return ((0x07) << 8) | (val); + break; + } + + case '8': + { + return ((0x80) << 8) | (val); + break; + } + + case '9': + { + return ((0x90) << 8) | (val); + break; + } + + case 'A': + { + return ((0x0A) << 8) | (val); + break; + } + + case 'B': + { + return ((0x0B) << 8) | (val); + break; + } + + case 'C': + { + return ((0x0C) << 8) | (val); //interpreted as volume later + break; + } + + case 'D': + { + uint8_t new_param = (val & 0xf) + ((val & 0xff) >> 4) * 10; //hex to decimal, Protracker (and XM too!) lol + return ((0x0D) << 8) | (new_param); + break; + } + + case 'E': + { + switch(val >> 4) + { + case 1: + { + return ((0xF1) << 8) | (val & 0xf); + break; + } + + case 2: + { + return ((0xF2) << 8) | (val & 0xf); + break; + } + + //glissando and vib shape not supported in Furnace + + case 5: + { + return ((0xF5) << 8) | ((val & 0xf) << 4); + break; + } + + //pattern loop not supported + + case 8: + { + return ((0x80) << 8) | ((val & 0xf) << 4); + break; + } + + case 9: + { + return ((0x0C) << 8) | (val & 0xf); + break; + } + + case 0xA: + { + return ((0xF3) << 8) | (val & 0xf); + break; + } + + case 0xB: + { + return ((0xF4) << 8) | (val & 0xf); + break; + } + + case 0xC: + { + return ((0xFC) << 8) | (val & 0xf); + break; + } + + case 0xD: + { + return ((0xFD) << 8) | (val & 0xf); + break; + } + + default: break; + } + } + + case 'F': + { + if(val < 0x20) + { + return ((0x09) << 8) | (val); + } + + else + { + return ((0xF0) << 8) | (val); + } + break; + } + + default: break; + } +} + +uint64_t convert_effect_openmpt_s3m(char symbol, uint16_t val) +{ + switch(symbol) + { + case 'A': + { + return ((0x09) << 8) | (val); + break; + } + + case 'B': + { + return ((0x0B) << 8) | (val); + break; + } + + case 'C': + { + return ((0x0D) << 8) | (val); + break; + } + + case 'D': //who the fuck invented this... + { + if((val & 0xf0) == 0xf0) + { + return ((0xF4) << 8) | (val & 0xf); + } + + else if((val & 0xf) == 0xf) + { + return ((0xF3) << 8) | ((val & 0xf0) >> 4); + } + + else + { + return ((0x0A) << 8) | val; + } + + break; + } + + case 'E': //who the fuck invented this... + { + if(val < 0xe0) + { + return ((0x02) << 8) | val; + } + + else if(val >= 0xe0 && val < 0xf0) + { + return ((0xF2) << 8) | (val & 0xf); + } + + else + { + return ((0xF2) << 8) | ((val & 0xf) / 2); + } + + break; + } + + case 'F': //who the fuck invented this... + { + if(val < 0xe0) + { + return ((0x01) << 8) | val; + } + + else if(val >= 0xe0 && val < 0xf0) + { + return ((0xF1) << 8) | (val & 0xf); + } + + else + { + return ((0xF1) << 8) | ((val & 0xf) / 2); + } + + break; + } + + case 'G': + { + return ((0x03) << 8) | (val); + break; + } + + case 'H': + { + return ((0x04) << 8) | (val); + break; + } + + case 'J': + { + return ((0x00) << 8) | (val); + break; + } + + case 'K': + { + return ((0x0a) << 8) | (val) | ((0x04) << 24); //Axy + 400 + break; + } + + case 'L': + { + return ((0x0a) << 8) | (val) | ((0x03) << 24); //Axy + 300 + break; + } + + case 'O': + { + return ((0x90) << 8) | (val); + break; + } + + case 'Q': + { + return ((0xC0) << 8) | (val & 0xf); + break; + } + + case 'R': + { + return ((0x07) << 8) | (val & 0xf); + break; + } + + case 'S': + { + switch(val >> 4) + { + case 2: + { + return ((0xE5) << 8) | ((val & 0xf) << 4); + break; + } + + case 8: + { + return ((0x80) << 8) | ((val & 0xf) << 4); + break; + } + + case 0xC: + { + return ((0xFC) << 8) | (val & 0xf); + break; + } + + case 0xD: + { + return ((0xFD) << 8) | (val & 0xf); + break; + } + + default: break; + } + } + + case 'T': + { + return ((0xF0) << 8) | (val & 0xf); + break; + } + + case 'U': + { + return ((0x04) << 8) | MAX(1, ((val & 0xf0) >> 4 / 4 << 4)) | MAX(1, ((val & 0xf) / 4)); + break; + } + + case 'X': + { + return ((0x80) << 8) | (val); + break; + } + + default: break; + } +} + +uint64_t convert_effect_openmpt_xm(char symbol, uint16_t val) +{ + return convert_effect_openmpt_mod(symbol, val); + //other effects too obscure bruh +} + +uint64_t convert_effect_openmpt_it(char symbol, uint16_t val) +{ + return convert_effect_openmpt_s3m(symbol, val); + //other effects too obscure bruh +} + +uint64_t convert_effect_openmpt_mptm(char symbol, uint16_t val) +{ + if(symbol == ':') + { + logW("dshit %d", ((val & 0xf0) >> 4)); + return ((0xED) << 8) | ((val & 0xf0) >> 4) | ((0xEC) << 24) | ((((val & 0xf0) >> 4) + (val & 0xf)) << 16); + } + + return convert_effect_openmpt_it(symbol, val); +} + +void FurnaceGUI::doPasteOpenMPT(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int openmpt_format) +{ + DETERMINE_LAST; + + int j=cursor.y; + char note[4]; + bool invalidData = true; + + for(size_t i=1; icurSubSong->patLen; i++) + { + size_t charPos=1; + int iCoarse=cursor.xCoarse; + int iFine=0; + + String& line=data[i]; + + while (charPoscurPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true); + if (line[charPos]=='|' && charPos != 0) //OpenMPT format starts every pattern line with '|' + { + iCoarse++; + + if (iCoarsecurSubSong->chanShow[iCoarse]) + { + iCoarse++; + if (iCoarse>=lastChannel) break; + } + + iFine=0; + charPos++; + continue; + } + + if (iFine==0) //note + { + if (charPos>=line.size()) + { + invalidData=true; + break; + } + note[0]=line[charPos++]; + if (charPos>=line.size()) { + invalidData=true; + break; + } + note[1]=line[charPos++]; + if (charPos>=line.size()) { + invalidData=true; + break; + } + note[2]=line[charPos++]; + note[3]=0; + + logW("note \"%s\"", note); + + if (iFine==0 && !opMaskPaste.note) { + iFine++; + continue; + } + + if (strcmp(note,"...")==0 || strcmp(note," ")==0) + { + // do nothing. + + logW("note empty"); + } + + else + { + if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || (pat->data[j][0]==0 && pat->data[j][1]==0)) + { + if (!decodeNote(note,pat->data[j][0],pat->data[j][1])) + { + invalidData=true; + break; + } + + else + { + pat->data[j][1]--; //OpenMPT is one octave higher... + } + + if (mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) pat->data[j][2]=arg; + } + } + } + + else if (iFine==1) //instrument + { + if (charPos>=line.size()) + { + invalidData=true; + break; + } + note[0]=line[charPos++]; + if (charPos>=line.size()) + { + invalidData=true; + break; + } + note[1]=line[charPos++]; + note[2]=0; + + logW("ins \"%s\"", note); + + if (iFine==1) + { + if (!opMaskPaste.ins || mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) + { + iFine++; + continue; + } + } + + if (strcmp(note,"..")==0 || strcmp(note," ")==0) + { + if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG || + mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG)) + { + pat->data[j][iFine+1]=-1; + } + } + + else + { + unsigned int val=0; + if (sscanf(note,"%2X",&val)!=1) + { + invalidData=true; + break; + } + + if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || pat->data[j][iFine+1]==-1) + { + pat->data[j][iFine+1]=val; + } + } + } + + else //volume and effects + { + if (charPos>=line.size()) + { + invalidData=true; + break; + } + note[0]=line[charPos++]; + if (charPos>=line.size()) + { + invalidData=true; + break; + } + note[1]=line[charPos++]; + if (charPos>=line.size()) + { + invalidData=true; + break; + } + note[2]=line[charPos++]; + note[3]=0; + + logW("vol/eff \"%s\"", note); + + if (iFine==2) + { + if (!opMaskPaste.vol) + { + iFine++; + continue; + } + } + + else if ((iFine&1)==0) + { + if (!opMaskPaste.effectVal) + { + iFine++; + continue; + } + } + + else if ((iFine&1)==1) + { + if (!opMaskPaste.effect) + { + iFine++; + continue; + } + } + + if (strcmp(note,"...")==0 || strcmp(note," ")==0) + { + if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG || + mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG)) + { + pat->data[j][iFine+1]=-1; + } + } + + else + { + unsigned int val=0; + char symbol = '\0'; + + symbol = note[0]; + + if(iFine == 2) + { + sscanf(¬e[1],"%2d",&val); + } + + else + { + sscanf(¬e[1],"%2X",&val); + } + + logW("vol/eff symbol %c, value %02X", symbol, val); + + if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || pat->data[j][iFine+1]==-1) + { + //if (iFine<(3+e->curPat[iCoarse].effectCols*2)) pat->data[j][iFine+1]=val; + if(iFine == 2) //volume + { + switch(symbol) + { + case 'v': + { + pat->data[j][iFine+1]=val; + break; + } + default: break; + } + } + + else //effect + { + uint64_t eff = 0; + + if(openmpt_format == 0) + { + eff = convert_effect_openmpt_mod(symbol, val); //up to 4 effects stored in one variable + + if(((eff & 0x0f00) >> 8) == 0x0C) //set volume + { + pat->data[j][iFine]=eff & 0xff; + } + } + + if(openmpt_format == 1) + { + eff = convert_effect_openmpt_s3m(symbol, val); + } + + if(openmpt_format == 2 || openmpt_format == 3) //set volume + { + eff = convert_effect_openmpt_xm(symbol, val); + + if(((eff & 0x0f00) >> 8) == 0x0C) + { + pat->data[j][iFine]=eff & 0xff; + } + } + + if(openmpt_format == 4 || openmpt_format == 5) + { + eff = convert_effect_openmpt_it(symbol, val); + } + + if(openmpt_format == 6) + { + eff = convert_effect_openmpt_mptm(symbol, val); + } + + pat->data[j][iFine+1]=((eff & 0xff00) >> 8); + pat->data[j][iFine+2]=(eff & 0xff); + + if(eff > 0xffff) + { + pat->data[j][iFine+3]=((eff & 0xff000000) >> 24); + pat->data[j][iFine+4]=((eff & 0xff0000) >> 16); + } + + } + } + } + } + + iFine++; + + if(charPos >= line.size() - 1) + { + logW("line end"); + invalidData = false; + break; + } + } + + if (invalidData) + { + logW("invalid OpenMPT clipboard data! failed at line %d char %d",i,charPos); + logW("%s",line.c_str()); + break; + } + + j++; + if (mode==GUI_PASTE_MODE_OVERFLOW && j>=e->curSubSong->patLen && curOrdercurSubSong->ordersLen-1) + { + j=0; + curOrder++; + } + + if (mode==GUI_PASTE_MODE_FLOOD && i==data.size()-1) + { + i=1; + } + } + + if (readClipboard) { + if (settings.cursorPastePos) { + cursor.y=j; + if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1; + selStart=cursor; + selEnd=cursor; + updateScroll(cursor.y); + } + + makeUndo(GUI_UNDO_PATTERN_PASTE); + } +} + +void FurnaceGUI::doPaste(PasteMode mode, int arg, bool readClipboard, String clipb) { + if (readClipboard) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_PASTE); + char* clipText=SDL_GetClipboardText(); + if (clipText!=NULL) { + if (clipText[0]) { + clipboard=clipText; + } + SDL_free(clipText); + } + clipb=clipboard; + } + std::vector data; + String tempS; + bool found_string = false; + bool is_furnace = false; + bool is_openmpt = false; + int openmpt_format = 0; + for (char i: clipb) { + if (i=='\r') continue; + if (i=='\n') { + data.push_back(tempS); + tempS=""; + continue; + } + tempS+=i; + } + data.push_back(tempS); + + int startOff=-1; + bool invalidData=false; + if (data.size()<2) return; + + if (data[0].find("org.tildearrow.furnace - Pattern Data")==0) + { + found_string = true; + is_furnace = true; + } + + int i = 0; + + while(text_format_headers[i] != NULL) + { + if (data[0].find(text_format_headers[i])==0) + { + found_string = true; + is_openmpt = true; + openmpt_format = i; + break; + } + + i++; + } + + if(!found_string) return; + + if(is_furnace) + { + doPasteFurnace(mode, arg, readClipboard, clipb, data, startOff, invalidData); + } + + if(is_openmpt) + { + doPasteOpenMPT(mode, arg, readClipboard, clipb, data, openmpt_format); + } +} + void FurnaceGUI::doChangeIns(int ins) { finishSelection(); prepareUndo(GUI_UNDO_PATTERN_CHANGE_INS); diff --git a/src/gui/gui.h b/src/gui/gui.h index 67b83484c..83a8d3dc7 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -2426,6 +2426,8 @@ class FurnaceGUI { void doInsert(); void doTranspose(int amount, OperationMask& mask); String doCopy(bool cut, bool writeClipboard, const SelectionPoint& sStart, const SelectionPoint& sEnd); + void doPasteFurnace(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int startOff, bool invalidData); + void doPasteOpenMPT(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector data, int openmpt_format); void doPaste(PasteMode mode=GUI_PASTE_MODE_NORMAL, int arg=0, bool readClipboard=true, String clipb=""); void doChangeIns(int ins); void doInterpolate();