1.1 .DMF saving

as of now you're given a choice between 1.1 and 1.0 module
This commit is contained in:
tildearrow 2022-02-20 03:18:20 -05:00
parent 2326c4250e
commit fa363384aa
6 changed files with 96 additions and 29 deletions

View file

@ -267,7 +267,7 @@ class DivEngine {
// load a file. // load a file.
bool load(unsigned char* f, size_t length); bool load(unsigned char* f, size_t length);
// save as .dmf. // save as .dmf.
SafeWriter* saveDMF(); SafeWriter* saveDMF(unsigned char version);
// save as .fur. // save as .fur.
SafeWriter* saveFur(); SafeWriter* saveFur();
// build a ROM file (TODO). // build a ROM file (TODO).

View file

@ -53,6 +53,8 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.nullWave.data[i]=15; ds.nullWave.data[i]=15;
} }
ds.isDMF=true;
if (!reader.seek(16,SEEK_SET)) { if (!reader.seek(16,SEEK_SET)) {
logE("premature end of file!\n"); logE("premature end of file!\n");
lastError="incomplete file"; lastError="incomplete file";
@ -724,6 +726,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
if (ds.version<50) { if (ds.version<50) {
ds.ignoreDuplicateSlides=false; ds.ignoreDuplicateSlides=false;
} }
ds.isDMF=false;
reader.readS(); // reserved reader.readS(); // reserved
int infoSeek=reader.readI(); int infoSeek=reader.readI();
@ -1148,6 +1151,9 @@ SafeWriter* DivEngine::saveFur() {
size_t ptrSeek; size_t ptrSeek;
warnings=""; warnings="";
song.isDMF=false;
song.version=DIV_ENGINE_VERSION;
SafeWriter* w=new SafeWriter; SafeWriter* w=new SafeWriter;
w->init(); w->init();
/// HEADER /// HEADER
@ -1375,19 +1381,34 @@ SafeWriter* DivEngine::saveFur() {
return w; return w;
} }
SafeWriter* DivEngine::saveDMF() { SafeWriter* DivEngine::saveDMF(unsigned char version) {
// fail if version is not supported
if (version<24 || version>25) {
logE("cannot save in this version!\n");
lastError="invalid version to save in! this is a bug!";
return NULL;
}
// fail if more than one system // fail if more than one system
// TODO: fix this mess for the flattening in 0.6
if (!(song.system[0]==DIV_SYSTEM_SMS && song.system[1]==DIV_SYSTEM_OPLL)) {
if (song.systemLen!=1) { if (song.systemLen!=1) {
logE("cannot save multiple systems in this format!\n"); logE("cannot save multiple systems in this format!\n");
lastError="multiple systems not possible on .dmf"; lastError="multiple systems not possible on .dmf";
return NULL; return NULL;
} }
}
// fail if this is an YMU759 song // fail if this is an YMU759 song
if (song.system[0]==DIV_SYSTEM_YMU759) { if (song.system[0]==DIV_SYSTEM_YMU759) {
logE("cannot save YMU759 song!\n"); logE("cannot save YMU759 song!\n");
lastError="YMU759 song saving is not supported"; lastError="YMU759 song saving is not supported";
return NULL; return NULL;
} }
// fail if the system is SMS+OPLL and version<25
if (version<25 && song.system[0]==DIV_SYSTEM_SMS && song.system[1]==DIV_SYSTEM_OPLL) {
logE("Master System FM expansion not supported in 1.0/legacy .dmf!\n");
lastError="Master System FM expansion not supported in 1.0/legacy .dmf!";
return NULL;
}
// fail if the system is Furnace-exclusive // fail if the system is Furnace-exclusive
if (systemToFile(song.system[0])&0x80) { if (systemToFile(song.system[0])&0x80) {
logE("cannot save Furnace-exclusive system song!\n"); logE("cannot save Furnace-exclusive system song!\n");
@ -1395,14 +1416,23 @@ SafeWriter* DivEngine::saveDMF() {
return NULL; return NULL;
} }
warnings=""; warnings="";
song.version=version;
song.isDMF=true;
SafeWriter* w=new SafeWriter; SafeWriter* w=new SafeWriter;
w->init(); w->init();
// write magic // write magic
w->write(DIV_DMF_MAGIC,16); w->write(DIV_DMF_MAGIC,16);
// version // version
w->writeC(24); w->writeC(version);
DivSystem sys=DIV_SYSTEM_NULL;
if (song.system[0]==DIV_SYSTEM_SMS && song.system[1]==DIV_SYSTEM_OPLL) {
w->writeC(systemToFile(DIV_SYSTEM_SMS_OPLL));
sys=DIV_SYSTEM_SMS_OPLL;
} else {
w->writeC(systemToFile(song.system[0])); w->writeC(systemToFile(song.system[0]));
sys=song.system[0];
}
// song info // song info
w->writeString(song.name,true); w->writeString(song.name,true);
@ -1425,10 +1455,14 @@ SafeWriter* DivEngine::saveDMF() {
for (int i=0; i<chans; i++) { for (int i=0; i<chans; i++) {
for (int j=0; j<song.ordersLen; j++) { for (int j=0; j<song.ordersLen; j++) {
w->writeC(song.orders.ord[i][j]); w->writeC(song.orders.ord[i][j]);
if (version>=25) {
DivPattern* pat=song.pat[i].getPattern(j,false);
w->writeString(pat->name,true);
}
} }
} }
if (song.system[0]==DIV_SYSTEM_C64_6581 || song.system[0]==DIV_SYSTEM_C64_8580) { if (sys==DIV_SYSTEM_C64_6581 || sys==DIV_SYSTEM_C64_8580) {
addWarning("absolute duty/cutoff macro not available in .dmf!"); addWarning("absolute duty/cutoff macro not available in .dmf!");
addWarning("duty precision will be lost"); addWarning("duty precision will be lost");
} }
@ -1452,10 +1486,10 @@ SafeWriter* DivEngine::saveDMF() {
w->writeString(i->name,true); w->writeString(i->name,true);
// safety check // safety check
if (!isFMSystem(song.system[0]) && i->mode) { if (!isFMSystem(sys) && i->mode) {
i->mode=0; i->mode=0;
} }
if (!isSTDSystem(song.system[0]) && i->mode==0) { if (!isSTDSystem(sys) && i->mode==0) {
i->mode=1; i->mode=1;
} }
@ -1475,14 +1509,25 @@ SafeWriter* DivEngine::saveDMF() {
w->writeC(op.rr); w->writeC(op.rr);
w->writeC(op.sl); w->writeC(op.sl);
w->writeC(op.tl); w->writeC(op.tl);
if (sys==DIV_SYSTEM_SMS_OPLL && j==0) {
w->writeC(i->fm.opllPreset);
} else {
w->writeC(op.dt2); w->writeC(op.dt2);
}
if (sys==DIV_SYSTEM_SMS_OPLL) {
w->writeC(op.ksr);
w->writeC(op.vib);
w->writeC(op.ksl);
w->writeC(op.ssgEnv);
} else {
w->writeC(op.rs); w->writeC(op.rs);
w->writeC(op.dt); w->writeC(op.dt);
w->writeC(op.d2r); w->writeC(op.d2r);
w->writeC(op.ssgEnv); w->writeC(op.ssgEnv);
} }
}
} else { // STD } else { // STD
if (song.system[0]!=DIV_SYSTEM_GB) { if (sys!=DIV_SYSTEM_GB) {
w->writeC(i->std.volMacroLen); w->writeC(i->std.volMacroLen);
w->write(i->std.volMacro,4*i->std.volMacroLen); w->write(i->std.volMacro,4*i->std.volMacroLen);
if (i->std.volMacroLen>0) { if (i->std.volMacroLen>0) {
@ -1515,7 +1560,7 @@ SafeWriter* DivEngine::saveDMF() {
w->writeC(i->std.waveMacroLoop); w->writeC(i->std.waveMacroLoop);
} }
if (song.system[0]==DIV_SYSTEM_C64_6581 || song.system[0]==DIV_SYSTEM_C64_8580) { if (sys==DIV_SYSTEM_C64_6581 || sys==DIV_SYSTEM_C64_8580) {
w->writeC(i->c64.triOn); w->writeC(i->c64.triOn);
w->writeC(i->c64.sawOn); w->writeC(i->c64.sawOn);
w->writeC(i->c64.pulseOn); w->writeC(i->c64.pulseOn);
@ -1544,7 +1589,7 @@ SafeWriter* DivEngine::saveDMF() {
w->writeC(i->c64.ch3off); w->writeC(i->c64.ch3off);
} }
if (song.system[0]==DIV_SYSTEM_GB) { if (sys==DIV_SYSTEM_GB) {
w->writeC(i->gb.envVol); w->writeC(i->gb.envVol);
w->writeC(i->gb.envDir); w->writeC(i->gb.envDir);
w->writeC(i->gb.envLen); w->writeC(i->gb.envLen);
@ -1559,7 +1604,7 @@ SafeWriter* DivEngine::saveDMF() {
w->write(i->data,4*i->len); w->write(i->data,4*i->len);
} }
for (int i=0; i<getChannelCount(song.system[0]); i++) { for (int i=0; i<getChannelCount(sys); i++) {
w->writeC(song.pat[i].effectRows); w->writeC(song.pat[i].effectRows);
for (int j=0; j<song.ordersLen; j++) { for (int j=0; j<song.ordersLen; j++) {

View file

@ -140,6 +140,7 @@ struct DivSong {
// - basic format, no system number, 16 instruments, one speed, YMU759-only // - basic format, no system number, 16 instruments, one speed, YMU759-only
// - if somebody manages to find a version 2 or even 1 module, please tell me as it will be worth more than a luxury vehicle // - if somebody manages to find a version 2 or even 1 module, please tell me as it will be worth more than a luxury vehicle
unsigned short version; unsigned short version;
bool isDMF;
// system // system
DivSystem system[32]; DivSystem system[32];
@ -267,7 +268,8 @@ struct DivSong {
void unload(); void unload();
DivSong(): DivSong():
version(24), version(0),
isDMF(false),
systemLen(1), systemLen(1),
name(""), name(""),
author(""), author(""),

View file

@ -650,6 +650,7 @@ const char* DivEngine::getSystemNameJ(DivSystem sys) {
bool DivEngine::isFMSystem(DivSystem sys) { bool DivEngine::isFMSystem(DivSystem sys) {
return (sys==DIV_SYSTEM_GENESIS || return (sys==DIV_SYSTEM_GENESIS ||
sys==DIV_SYSTEM_GENESIS_EXT || sys==DIV_SYSTEM_GENESIS_EXT ||
sys==DIV_SYSTEM_SMS_OPLL ||
sys==DIV_SYSTEM_ARCADE || sys==DIV_SYSTEM_ARCADE ||
sys==DIV_SYSTEM_YM2610 || sys==DIV_SYSTEM_YM2610 ||
sys==DIV_SYSTEM_YM2610_EXT || sys==DIV_SYSTEM_YM2610_EXT ||

View file

@ -2892,7 +2892,7 @@ void FurnaceGUI::doAction(int what) {
if (curFileName=="") { if (curFileName=="") {
openFileDialog(GUI_FILE_SAVE); openFileDialog(GUI_FILE_SAVE);
} else { } else {
if (save(curFileName)>0) { if (save(curFileName,e->song.isDMF?e->song.version:0)>0) {
showError(fmt::sprintf("Error while saving file! (%s)",lastError)); showError(fmt::sprintf("Error while saving file! (%s)",lastError));
} }
} }
@ -3809,7 +3809,10 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
ImGuiFileDialog::Instance()->OpenModal("FileDialog","Open File","compatible files{.fur,.dmf},.*",workingDir); ImGuiFileDialog::Instance()->OpenModal("FileDialog","Open File","compatible files{.fur,.dmf},.*",workingDir);
break; break;
case GUI_FILE_SAVE: case GUI_FILE_SAVE:
ImGuiFileDialog::Instance()->OpenModal("FileDialog","Save File","Furnace song{.fur},DefleMask module{.dmf}",workingDir,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite); ImGuiFileDialog::Instance()->OpenModal("FileDialog","Save File","Furnace song{.fur},DefleMask 1.1 module{.dmf}",workingDir,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite);
break;
case GUI_FILE_SAVE_DMF_LEGACY:
ImGuiFileDialog::Instance()->OpenModal("FileDialog","Save File","DefleMask 1.0/legacy module{.dmf}",workingDir,1,nullptr,ImGuiFileDialogFlags_ConfirmOverwrite);
break; break;
case GUI_FILE_INS_OPEN: case GUI_FILE_INS_OPEN:
ImGuiFileDialog::Instance()->OpenModal("FileDialog","Load Instrument","compatible files{.fui,.dmp},.*",workingDir); ImGuiFileDialog::Instance()->OpenModal("FileDialog","Load Instrument","compatible files{.fui,.dmp},.*",workingDir);
@ -3857,10 +3860,10 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
#define FURNACE_ZLIB_COMPRESS #define FURNACE_ZLIB_COMPRESS
int FurnaceGUI::save(String path) { int FurnaceGUI::save(String path, int dmfVersion) {
SafeWriter* w; SafeWriter* w;
if (path.rfind(".dmf")==path.size()-4) { if (dmfVersion) {
w=e->saveDMF(); w=e->saveDMF(dmfVersion);
} else { } else {
w=e->saveFur(); w=e->saveFur();
} }
@ -4305,7 +4308,7 @@ bool FurnaceGUI::loop() {
if (curFileName=="") { if (curFileName=="") {
openFileDialog(GUI_FILE_SAVE); openFileDialog(GUI_FILE_SAVE);
} else { } else {
if (save(curFileName)>0) { if (save(curFileName,e->song.isDMF?e->song.version:0)>0) {
showError(fmt::sprintf("Error while saving file! (%s)",lastError)); showError(fmt::sprintf("Error while saving file! (%s)",lastError));
} }
} }
@ -4313,6 +4316,9 @@ bool FurnaceGUI::loop() {
if (ImGui::MenuItem("save as...",BIND_FOR(GUI_ACTION_SAVE_AS))) { if (ImGui::MenuItem("save as...",BIND_FOR(GUI_ACTION_SAVE_AS))) {
openFileDialog(GUI_FILE_SAVE); openFileDialog(GUI_FILE_SAVE);
} }
if (ImGui::MenuItem("save as .dmf (1.0/legacy)...",BIND_FOR(GUI_ACTION_SAVE_AS))) {
openFileDialog(GUI_FILE_SAVE_DMF_LEGACY);
}
ImGui::Separator(); ImGui::Separator();
if (ImGui::BeginMenu("export audio...")) { if (ImGui::BeginMenu("export audio...")) {
if (ImGui::MenuItem("one file")) { if (ImGui::MenuItem("one file")) {
@ -4796,7 +4802,19 @@ bool FurnaceGUI::loop() {
break; break;
case GUI_FILE_SAVE: case GUI_FILE_SAVE:
printf("saving: %s\n",copyOfName.c_str()); printf("saving: %s\n",copyOfName.c_str());
if (save(copyOfName)>0) { if (ImGuiFileDialog::Instance()->GetCurrentFilter()=="Furnace song") {
if (save(copyOfName,0)>0) {
showError(fmt::sprintf("Error while saving file! (%s)",lastError));
}
} else {
if (save(copyOfName,25)>0) {
showError(fmt::sprintf("Error while saving file! (%s)",lastError));
}
}
break;
case GUI_FILE_SAVE_DMF_LEGACY:
printf("saving: %s\n",copyOfName.c_str());
if (save(copyOfName,24)>0) {
showError(fmt::sprintf("Error while saving file! (%s)",lastError)); showError(fmt::sprintf("Error while saving file! (%s)",lastError));
} }
break; break;

View file

@ -132,6 +132,7 @@ enum FurnaceGUIWindows {
enum FurnaceGUIFileDialogs { enum FurnaceGUIFileDialogs {
GUI_FILE_OPEN, GUI_FILE_OPEN,
GUI_FILE_SAVE, GUI_FILE_SAVE,
GUI_FILE_SAVE_DMF_LEGACY,
GUI_FILE_INS_OPEN, GUI_FILE_INS_OPEN,
GUI_FILE_INS_SAVE, GUI_FILE_INS_SAVE,
GUI_FILE_WAVE_OPEN, GUI_FILE_WAVE_OPEN,
@ -690,7 +691,7 @@ class FurnaceGUI {
void keyUp(SDL_Event& ev); void keyUp(SDL_Event& ev);
void openFileDialog(FurnaceGUIFileDialogs type); void openFileDialog(FurnaceGUIFileDialogs type);
int save(String path); int save(String path, int dmfVersion);
int load(String path); int load(String path);
void exportAudio(String path, DivAudioExportModes mode); void exportAudio(String path, DivAudioExportModes mode);