untested saving code and memory leak fix

This commit is contained in:
tildearrow 2021-12-15 14:15:44 -05:00
parent 2879b5e4d0
commit ebb28d912b
12 changed files with 363 additions and 107 deletions

View file

@ -89,10 +89,13 @@ src/engine/platform/ym2610Interface.cpp
src/engine/blip_buf.c src/engine/blip_buf.c
src/engine/safeReader.cpp src/engine/safeReader.cpp
src/engine/safeWriter.cpp
src/engine/engine.cpp src/engine/engine.cpp
src/engine/macroInt.cpp src/engine/macroInt.cpp
src/engine/pattern.cpp src/engine/pattern.cpp
src/engine/playback.cpp src/engine/playback.cpp
src/engine/sample.cpp
src/engine/song.cpp
src/engine/platform/abstract.cpp src/engine/platform/abstract.cpp
src/engine/platform/genesis.cpp src/engine/platform/genesis.cpp
src/engine/platform/genesisext.cpp src/engine/platform/genesisext.cpp

View file

@ -805,6 +805,7 @@ bool DivEngine::load(void* f, size_t slen) {
} }
} }
song.unload();
song=ds; song=ds;
chans=getChannelCount(song.system); chans=getChannelCount(song.system);
renderSamples(); renderSamples();
@ -815,166 +816,162 @@ bool DivEngine::load(void* f, size_t slen) {
return true; return true;
} }
#define ERR_CHECK(x) x; if (ferror(f)) return false; SafeWriter* DivEngine::save() {
SafeWriter* w=new SafeWriter;
bool DivEngine::save(FILE* f) { w->init();
// write magic // write magic
ERR_CHECK(fwrite(DIV_DMF_MAGIC,1,16,f)); w->write(DIV_DMF_MAGIC,16);
// version // version
ERR_CHECK(fputc(24,f)); w->writeC(24);
ERR_CHECK(fputc(systemToFile(song.system),f)); w->writeC(systemToFile(song.system));
// song info // song info
ERR_CHECK(fputc(song.name.size(),f)); w->writeString(song.name,true);
ERR_CHECK(fwrite(song.name.c_str(),1,song.name.size(),f)); w->writeString(song.author,true);
ERR_CHECK(fputc(song.author.size(),f)); w->writeC(song.hilightA);
ERR_CHECK(fwrite(song.author.c_str(),1,song.author.size(),f)); w->writeC(song.hilightB);
ERR_CHECK(fputc(song.hilightA,f));
ERR_CHECK(fputc(song.hilightB,f));
ERR_CHECK(fputc(song.timeBase,f)); w->writeC(song.timeBase);
ERR_CHECK(fputc(song.speed1,f)); w->writeC(song.speed1);
ERR_CHECK(fputc(song.speed2,f)); w->writeC(song.speed2);
ERR_CHECK(fputc(song.pal,f)); w->writeC(song.pal);
ERR_CHECK(fputc(song.customTempo,f)); w->writeC(song.customTempo);
char customHz[4]; char customHz[4];
memset(customHz,0,4); memset(customHz,0,4);
snprintf(customHz,4,"%d",song.hz); snprintf(customHz,4,"%d",song.hz);
ERR_CHECK(fwrite(customHz,1,3,f)); w->write(customHz,3);
ERR_CHECK(fwrite(&song.patLen,4,1,f)); w->writeI(song.patLen);
ERR_CHECK(fputc(song.ordersLen,f)); w->writeC(song.ordersLen);
for (int i=0; i<getChannelCount(song.system); i++) { for (int i=0; i<getChannelCount(song.system); i++) {
for (int j=0; j<song.ordersLen; j++) { for (int j=0; j<song.ordersLen; j++) {
ERR_CHECK(fputc(song.orders.ord[i][j],f)); w->writeC(song.orders.ord[i][j]);
} }
} }
ERR_CHECK(fputc(song.ins.size(),f)); w->writeC(song.ins.size());
for (DivInstrument* i: song.ins) { for (DivInstrument* i: song.ins) {
ERR_CHECK(fputc(i->name.size(),f)); w->writeString(i->name,true);
ERR_CHECK(fwrite(i->name.c_str(),1,i->name.size(),f)); w->writeC(i->mode);
ERR_CHECK(fputc(i->mode,f));
if (i->mode) { // FM if (i->mode) { // FM
ERR_CHECK(fputc(i->fm.alg,f)); w->writeC(i->fm.alg);
ERR_CHECK(fputc(i->fm.fb,f)); w->writeC(i->fm.fb);
ERR_CHECK(fputc(i->fm.fms,f)); w->writeC(i->fm.fms);
ERR_CHECK(fputc(i->fm.ams,f)); w->writeC(i->fm.ams);
for (int j=0; j<4; j++) { for (int j=0; j<4; j++) {
DivInstrumentFM::Operator& op=i->fm.op[j]; DivInstrumentFM::Operator& op=i->fm.op[j];
ERR_CHECK(fputc(op.am,f)); w->writeC(op.am);
ERR_CHECK(fputc(op.ar,f)); w->writeC(op.ar);
ERR_CHECK(fputc(op.dr,f)); w->writeC(op.dr);
ERR_CHECK(fputc(op.mult,f)); w->writeC(op.mult);
ERR_CHECK(fputc(op.rr,f)); w->writeC(op.rr);
ERR_CHECK(fputc(op.sl,f)); w->writeC(op.sl);
ERR_CHECK(fputc(op.tl,f)); w->writeC(op.tl);
ERR_CHECK(fputc(op.dt2,f)); w->writeC(op.dt2);
ERR_CHECK(fputc(op.rs,f)); w->writeC(op.rs);
ERR_CHECK(fputc(op.dt,f)); w->writeC(op.dt);
ERR_CHECK(fputc(op.d2r,f)); w->writeC(op.d2r);
ERR_CHECK(fputc(op.ssgEnv,f)); w->writeC(op.ssgEnv);
} }
} else { // STD } else { // STD
if (song.system!=DIV_SYSTEM_GB) { if (song.system!=DIV_SYSTEM_GB) {
ERR_CHECK(fputc(i->std.volMacroLen,f)); w->writeC(i->std.volMacroLen);
ERR_CHECK(fwrite(i->std.volMacro,4,i->std.volMacroLen,f)); w->write(i->std.volMacro,4*i->std.volMacroLen);
if (i->std.volMacroLen>0) { if (i->std.volMacroLen>0) {
ERR_CHECK(fputc(i->std.volMacroLoop,f)); w->writeC(i->std.volMacroLoop);
} }
} }
ERR_CHECK(fputc(i->std.arpMacroLen,f)); w->writeC(i->std.arpMacroLen);
ERR_CHECK(fwrite(i->std.arpMacro,4,i->std.arpMacroLen,f)); w->write(i->std.arpMacro,4*i->std.arpMacroLen);
if (i->std.arpMacroLen>0) { if (i->std.arpMacroLen>0) {
ERR_CHECK(fputc(i->std.arpMacroLoop,f)); w->writeC(i->std.arpMacroLoop);
} }
ERR_CHECK(fputc(i->std.arpMacroMode,f)); w->writeC(i->std.arpMacroMode);
ERR_CHECK(fputc(i->std.dutyMacroLen,f)); w->writeC(i->std.dutyMacroLen);
ERR_CHECK(fwrite(i->std.dutyMacro,4,i->std.dutyMacroLen,f)); w->write(i->std.dutyMacro,4*i->std.dutyMacroLen);
if (i->std.dutyMacroLen>0) { if (i->std.dutyMacroLen>0) {
ERR_CHECK(fputc(i->std.dutyMacroLoop,f)); w->writeC(i->std.dutyMacroLoop);
} }
ERR_CHECK(fputc(i->std.waveMacroLen,f)); w->writeC(i->std.waveMacroLen);
ERR_CHECK(fwrite(i->std.waveMacro,4,i->std.waveMacroLen,f)); w->write(i->std.waveMacro,4*i->std.waveMacroLen);
if (i->std.waveMacroLen>0) { if (i->std.waveMacroLen>0) {
ERR_CHECK(fputc(i->std.waveMacroLoop,f)); w->writeC(i->std.waveMacroLoop);
} }
if (song.system==DIV_SYSTEM_C64_6581 || song.system==DIV_SYSTEM_C64_8580) { if (song.system==DIV_SYSTEM_C64_6581 || song.system==DIV_SYSTEM_C64_8580) {
ERR_CHECK(fputc(i->c64.triOn,f)); w->writeC(i->c64.triOn);
ERR_CHECK(fputc(i->c64.sawOn,f)); w->writeC(i->c64.sawOn);
ERR_CHECK(fputc(i->c64.pulseOn,f)); w->writeC(i->c64.pulseOn);
ERR_CHECK(fputc(i->c64.noiseOn,f)); w->writeC(i->c64.noiseOn);
ERR_CHECK(fputc(i->c64.a,f)); w->writeC(i->c64.a);
ERR_CHECK(fputc(i->c64.d,f)); w->writeC(i->c64.d);
ERR_CHECK(fputc(i->c64.s,f)); w->writeC(i->c64.s);
ERR_CHECK(fputc(i->c64.r,f)); w->writeC(i->c64.r);
ERR_CHECK(fputc(i->c64.duty,f)); w->writeC(i->c64.duty);
ERR_CHECK(fputc(i->c64.ringMod,f)); w->writeC(i->c64.ringMod);
ERR_CHECK(fputc(i->c64.oscSync,f)); w->writeC(i->c64.oscSync);
ERR_CHECK(fputc(i->c64.toFilter,f)); w->writeC(i->c64.toFilter);
ERR_CHECK(fputc(i->c64.volIsCutoff,f)); w->writeC(i->c64.volIsCutoff);
ERR_CHECK(fputc(i->c64.initFilter,f)); w->writeC(i->c64.initFilter);
ERR_CHECK(fputc(i->c64.res,f)); w->writeC(i->c64.res);
ERR_CHECK(fputc(i->c64.cut,f)); w->writeC(i->c64.cut);
ERR_CHECK(fputc(i->c64.hp,f)); w->writeC(i->c64.hp);
ERR_CHECK(fputc(i->c64.bp,f)); w->writeC(i->c64.bp);
ERR_CHECK(fputc(i->c64.lp,f)); w->writeC(i->c64.lp);
ERR_CHECK(fputc(i->c64.ch3off,f)); w->writeC(i->c64.ch3off);
} }
if (song.system==DIV_SYSTEM_GB) { if (song.system==DIV_SYSTEM_GB) {
ERR_CHECK(fputc(i->gb.envVol,f)); w->writeC(i->gb.envVol);
ERR_CHECK(fputc(i->gb.envDir,f)); w->writeC(i->gb.envDir);
ERR_CHECK(fputc(i->gb.envLen,f)); w->writeC(i->gb.envLen);
ERR_CHECK(fputc(i->gb.soundLen,f)); w->writeC(i->gb.soundLen);
} }
} }
} }
ERR_CHECK(fputc(song.wave.size(),f)); w->writeC(song.wave.size());
for (DivWavetable* i: song.wave) { for (DivWavetable* i: song.wave) {
ERR_CHECK(fwrite(&i->len,4,1,f)); w->writeI(i->len);
ERR_CHECK(fwrite(&i->data,4,i->len,f)); w->write(i->data,4*i->len);
} }
for (int i=0; i<getChannelCount(song.system); i++) { for (int i=0; i<getChannelCount(song.system); i++) {
ERR_CHECK(fputc(song.pat[i].effectRows,f)); w->writeC(song.pat[i].effectRows);
for (int j=0; j<song.ordersLen; j++) { for (int j=0; j<song.ordersLen; j++) {
DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][j],false); DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][j],false);
for (int k=0; k<song.patLen; k++) { for (int k=0; k<song.patLen; k++) {
ERR_CHECK(fwrite(&pat->data[k][0],2,1,f)); // note w->writeS(pat->data[k][0]); // note
ERR_CHECK(fwrite(&pat->data[k][1],2,1,f)); // octave w->writeS(pat->data[k][1]); // octave
ERR_CHECK(fwrite(&pat->data[k][3],2,1,f)); // volume w->writeS(pat->data[k][3]); // volume
ERR_CHECK(fwrite(&pat->data[k][4],2,song.pat[i].effectRows*2,f)); // volume w->write(&pat->data[k][4],2*song.pat[i].effectRows*2); // effects
ERR_CHECK(fwrite(&pat->data[k][2],2,1,f)); // instrument w->writeS(pat->data[k][2]); // instrument
} }
} }
} }
ERR_CHECK(fputc(song.sample.size(),f)); w->writeC(song.sample.size());
for (DivSample* i: song.sample) { for (DivSample* i: song.sample) {
ERR_CHECK(fwrite(&i->length,4,1,f)); w->writeI(i->length);
ERR_CHECK(fputc(i->name.size(),f)); w->writeString(i->name,true);
ERR_CHECK(fwrite(i->name.c_str(),1,i->name.size(),f)); w->writeC(i->rate);
ERR_CHECK(fputc(i->rate,f)); w->writeC(i->pitch);
ERR_CHECK(fputc(i->pitch,f)); w->writeC(i->vol);
ERR_CHECK(fputc(i->vol,f)); w->writeC(i->depth);
ERR_CHECK(fputc(i->depth,f)); w->write(i->data,2*i->length);
ERR_CHECK(fwrite(i->data,2,i->length,f));
} }
return true; return w;
} }
// ADPCM code attribution: https://wiki.neogeodev.org/index.php?title=ADPCM_codecs // ADPCM code attribution: https://wiki.neogeodev.org/index.php?title=ADPCM_codecs

View file

@ -2,6 +2,7 @@
#define _ENGINE_H #define _ENGINE_H
#include "song.h" #include "song.h"
#include "dispatch.h" #include "dispatch.h"
#include "safeWriter.h"
#include "../audio/taAudio.h" #include "../audio/taAudio.h"
#include "blip_buf.h" #include "blip_buf.h"
#include <mutex> #include <mutex>
@ -104,7 +105,7 @@ class DivEngine {
// load a .dmf. // load a .dmf.
bool load(void* f, size_t length); bool load(void* f, size_t length);
// save as .dmf. // save as .dmf.
bool save(FILE* f); SafeWriter* save();
// play // play
void play(); void play();

100
src/engine/safeWriter.cpp Normal file
View file

@ -0,0 +1,100 @@
#include "safeWriter.h"
#define WRITER_BUF_SIZE 16384
unsigned char* SafeWriter::getFinalBuf() {
return buf;
}
void SafeWriter::checkSize(size_t amount) {
if (curSeek+amount>=bufLen) {
unsigned char* newBuf=new unsigned char[bufLen+WRITER_BUF_SIZE];
memcpy(newBuf,buf,bufLen);
delete[] buf;
buf=newBuf;
bufLen+=WRITER_BUF_SIZE;
}
}
bool SafeWriter::seek(ssize_t where, int whence) {
ssize_t supposed;
switch (whence) {
case SEEK_SET:
supposed=where;
break;
case SEEK_CUR:
supposed=curSeek+where;
break;
case SEEK_END:
supposed=len+where;
break;
default:
return false;
}
if (supposed<0) supposed=0;
if (supposed>(ssize_t)len) supposed=len;
curSeek=supposed;
return true;
}
size_t SafeWriter::tell() {
return curSeek;
}
size_t SafeWriter::size() {
return len;
}
int SafeWriter::write(const void* what, size_t count) {
if (!operative) return 0;
checkSize(count);
memcpy(buf+curSeek,what,count);
curSeek+=count;
if (curSeek>len) len=curSeek;
return count;
}
int SafeWriter::writeC(signed char val) {
return write(&val,1);
}
int SafeWriter::writeS(short val) {
return write(&val,2);
}
int SafeWriter::writeI(int val) {
return write(&val,4);
}
int SafeWriter::writeL(int64_t val) {
return write(&val,8);
}
int SafeWriter::writeF(float val) {
return write(&val,4);
}
int SafeWriter::writeD(double val) {
return write(&val,8);
}
int SafeWriter::writeString(String val, bool pascal) {
if (pascal) {
writeC((unsigned char)val.size());
return write(val.c_str(),val.size())+1;
} else {
return write(val.c_str(),val.size()+1);
}
}
void SafeWriter::init() {
if (operative) return;
buf=new unsigned char[WRITER_BUF_SIZE];
bufLen=WRITER_BUF_SIZE;
len=0;
curSeek=0;
operative=true;
}
void SafeWriter::finish() {
if (!operative) return;
delete[] buf;
buf=NULL;
operative=false;
}

51
src/engine/safeWriter.h Normal file
View file

@ -0,0 +1,51 @@
#ifndef _SAFEWRITER_H
#define _SAFEWRITER_H
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "../ta-utils.h"
class SafeWriter {
bool operative;
unsigned char* buf;
size_t bufLen;
size_t len;
size_t curSeek;
void checkSize(size_t amount);
public:
unsigned char* getFinalBuf();
bool seek(ssize_t where, int whence);
size_t tell();
size_t size();
int write(const void* what, size_t count);
int writeC(signed char val);
int writeS(short val);
int writeS_BE(short val);
int writeI(int val);
int writeI_BE(int val);
int writeL(int64_t val);
int writeL_BE(int64_t val);
int writeF(float val);
int writeF_BE(float val);
int writeD(double val);
int writeD_BE(double val);
int writeString(String val, bool pascal);
void init();
void finish();
SafeWriter():
operative(false),
buf(NULL),
bufLen(0),
len(0),
curSeek(0) {}
};
#endif

7
src/engine/sample.cpp Normal file
View file

@ -0,0 +1,7 @@
#include "sample.h"
DivSample::~DivSample() {
if (data) delete data;
if (rendData) delete rendData;
if (adpcmRendData) delete adpcmRendData;
}

View file

@ -1,3 +1,5 @@
#include "../ta-utils.h"
struct DivSample { struct DivSample {
String name; String name;
int length, rate; int length, rate;
@ -21,4 +23,5 @@ struct DivSample {
rendOff(0), rendOff(0),
rendData(NULL), rendData(NULL),
adpcmRendData(NULL) {} adpcmRendData(NULL) {}
~DivSample();
}; };

18
src/engine/song.cpp Normal file
View file

@ -0,0 +1,18 @@
#include "song.h"
void DivSong::unload() {
for (DivInstrument* i: ins) {
delete i;
}
ins.clear();
for (DivWavetable* i: wave) {
delete i;
}
wave.clear();
for (DivSample* i: sample) {
delete i;
}
sample.clear();
}

View file

@ -98,6 +98,8 @@ struct DivSong {
DivInstrument nullIns; DivInstrument nullIns;
DivWavetable nullWave; DivWavetable nullWave;
void unload();
DivSong(): DivSong():
version(24), version(24),
system(DIV_SYSTEM_GENESIS), system(DIV_SYSTEM_GENESIS),

View file

@ -9,6 +9,7 @@
#include "imgui_internal.h" #include "imgui_internal.h"
#include "ImGuiFileDialog.h" #include "ImGuiFileDialog.h"
#include "misc/cpp/imgui_stdlib.h" #include "misc/cpp/imgui_stdlib.h"
#include <zlib.h>
#include <fmt/printf.h> #include <fmt/printf.h>
#include <stdexcept> #include <stdexcept>
@ -870,6 +871,81 @@ void FurnaceGUI::keyUp(SDL_Event& ev) {
} }
#define FURNACE_ZLIB_COMPRESS
int FurnaceGUI::save(String path) {
FILE* outFile=fopen(path.c_str(),"wb");
if (outFile==NULL) {
return 1;
}
SafeWriter* w=e->save();
#ifdef FURNACE_ZLIB_COMPRESS
unsigned char zbuf[131072];
int ret;
z_stream zl;
memset(&zl,0,sizeof(z_stream));
ret=deflateInit(&zl,Z_DEFAULT_COMPRESSION);
if (ret!=Z_OK) {
logE("zlib error!\n");
fclose(outFile);
w->finish();
return 2;
}
zl.avail_in=w->size();
zl.next_in=w->getFinalBuf();
while (zl.avail_in>0) {
zl.avail_out=131072;
zl.next_out=zbuf;
if ((ret=deflate(&zl,Z_NO_FLUSH))==Z_STREAM_ERROR) {
logE("zlib stream error!\n");
deflateEnd(&zl);
fclose(outFile);
w->finish();
return 2;
}
size_t amount=131072-zl.avail_out;
if (amount>0) {
if (fwrite(zbuf,1,amount,outFile)!=amount) {
logE("did not write entirely: %s!\n",strerror(errno));
deflateEnd(&zl);
fclose(outFile);
w->finish();
return 1;
}
}
}
zl.avail_out=131072;
zl.next_out=zbuf;
if ((ret=deflate(&zl,Z_FINISH))==Z_STREAM_ERROR) {
logE("zlib finish stream error!\n");
deflateEnd(&zl);
fclose(outFile);
w->finish();
return 2;
}
if (131072-zl.avail_out>0) {
if (fwrite(zbuf,1,131072-zl.avail_out,outFile)!=(131072-zl.avail_out)) {
logE("did not write entirely: %s!\n",strerror(errno));
deflateEnd(&zl);
fclose(outFile);
w->finish();
return 1;
}
}
deflateEnd(&zl);
#else
if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) {
logE("did not write entirely: %s!\n",strerror(errno));
fclose(outFile);
w->finish();
return 1;
}
#endif
fclose(outFile);
w->finish();
return 0;
}
int FurnaceGUI::load(String path) { int FurnaceGUI::load(String path) {
if (!path.empty()) { if (!path.empty()) {
logI("loading module...\n"); logI("loading module...\n");
@ -989,10 +1065,14 @@ bool FurnaceGUI::loop() {
ImGui::MenuItem("new"); ImGui::MenuItem("new");
if (ImGui::MenuItem("open...")) { if (ImGui::MenuItem("open...")) {
ImGuiFileDialog::Instance()->OpenDialog("FileDialog","Open File","DefleMask module{.dmf},.*",workingDir); ImGuiFileDialog::Instance()->OpenDialog("FileDialog","Open File","DefleMask module{.dmf},.*",workingDir);
isSaving=false;
} }
ImGui::Separator(); ImGui::Separator();
ImGui::MenuItem("save"); ImGui::MenuItem("save");
ImGui::MenuItem("save as..."); if (ImGui::MenuItem("save as...")) {
ImGuiFileDialog::Instance()->OpenDialog("FileDialog","Save File","DefleMask module{.dmf}",workingDir);
isSaving=true;
}
ImGui::Separator(); ImGui::Separator();
if (ImGui::MenuItem("exit")) { if (ImGui::MenuItem("exit")) {
quit=true; quit=true;
@ -1029,7 +1109,7 @@ bool FurnaceGUI::loop() {
String copyOfName=fileName; String copyOfName=fileName;
if (isSaving) { if (isSaving) {
printf("saving: %s\n",copyOfName.c_str()); printf("saving: %s\n",copyOfName.c_str());
//SaveFile(copyOfName.c_str()); save(copyOfName);
isSaving=false; isSaving=false;
} else { } else {
load(copyOfName); load(copyOfName);

View file

@ -111,6 +111,7 @@ class FurnaceGUI {
void keyDown(SDL_Event& ev); void keyDown(SDL_Event& ev);
void keyUp(SDL_Event& ev); void keyUp(SDL_Event& ev);
int save(String path);
int load(String path); int load(String path);
public: public:

View file

@ -283,13 +283,6 @@ int main(int argc, char** argv) {
return 1; return 1;
} }
} }
/*FILE* outFile=fopen("testout.dmf","wb");
if (outFile!=NULL) {
if (!e.save(outFile)) {
logE("could not save file!\n");
}
fclose(outFile);
}*/
if (!e.init(outName)) { if (!e.init(outName)) {
logE("could not initialize engine!\n"); logE("could not initialize engine!\n");
return 1; return 1;