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/safeReader.cpp
src/engine/safeWriter.cpp
src/engine/engine.cpp
src/engine/macroInt.cpp
src/engine/pattern.cpp
src/engine/playback.cpp
src/engine/sample.cpp
src/engine/song.cpp
src/engine/platform/abstract.cpp
src/engine/platform/genesis.cpp
src/engine/platform/genesisext.cpp

View File

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

View File

@ -2,6 +2,7 @@
#define _ENGINE_H
#include "song.h"
#include "dispatch.h"
#include "safeWriter.h"
#include "../audio/taAudio.h"
#include "blip_buf.h"
#include <mutex>
@ -104,7 +105,7 @@ class DivEngine {
// load a .dmf.
bool load(void* f, size_t length);
// save as .dmf.
bool save(FILE* f);
SafeWriter* save();
// 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 {
String name;
int length, rate;
@ -21,4 +23,5 @@ struct DivSample {
rendOff(0),
rendData(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;
DivWavetable nullWave;
void unload();
DivSong():
version(24),
system(DIV_SYSTEM_GENESIS),

View File

@ -9,6 +9,7 @@
#include "imgui_internal.h"
#include "ImGuiFileDialog.h"
#include "misc/cpp/imgui_stdlib.h"
#include <zlib.h>
#include <fmt/printf.h>
#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) {
if (!path.empty()) {
logI("loading module...\n");
@ -989,10 +1065,14 @@ bool FurnaceGUI::loop() {
ImGui::MenuItem("new");
if (ImGui::MenuItem("open...")) {
ImGuiFileDialog::Instance()->OpenDialog("FileDialog","Open File","DefleMask module{.dmf},.*",workingDir);
isSaving=false;
}
ImGui::Separator();
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();
if (ImGui::MenuItem("exit")) {
quit=true;
@ -1029,7 +1109,7 @@ bool FurnaceGUI::loop() {
String copyOfName=fileName;
if (isSaving) {
printf("saving: %s\n",copyOfName.c_str());
//SaveFile(copyOfName.c_str());
save(copyOfName);
isSaving=false;
} else {
load(copyOfName);

View File

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

View File

@ -283,13 +283,6 @@ int main(int argc, char** argv) {
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)) {
logE("could not initialize engine!\n");
return 1;