Added emulator save state JS interface.
This commit is contained in:
parent
2f2482e950
commit
8ebb560d1c
|
@ -44,6 +44,7 @@
|
||||||
#include "../../video.h"
|
#include "../../video.h"
|
||||||
#include "../../x6502.h"
|
#include "../../x6502.h"
|
||||||
#include "../../debug.h"
|
#include "../../debug.h"
|
||||||
|
#include "../../state.h"
|
||||||
#include "../../ppu.h"
|
#include "../../ppu.h"
|
||||||
|
|
||||||
#include "common/os_utils.h"
|
#include "common/os_utils.h"
|
||||||
|
@ -83,6 +84,8 @@ ColorScriptObject::ColorScriptObject(int r, int g, int b)
|
||||||
{
|
{
|
||||||
numInstances++;
|
numInstances++;
|
||||||
//printf("ColorScriptObject(r,g,b) %p Constructor: %i\n", this, numInstances);
|
//printf("ColorScriptObject(r,g,b) %p Constructor: %i\n", this, numInstances);
|
||||||
|
|
||||||
|
moveToThread(QApplication::instance()->thread());
|
||||||
}
|
}
|
||||||
//----------------------------------------------------
|
//----------------------------------------------------
|
||||||
ColorScriptObject::~ColorScriptObject()
|
ColorScriptObject::~ColorScriptObject()
|
||||||
|
@ -91,6 +94,177 @@ ColorScriptObject::~ColorScriptObject()
|
||||||
//printf("ColorScriptObject %p Destructor: %i\n", this, numInstances);
|
//printf("ColorScriptObject %p Destructor: %i\n", this, numInstances);
|
||||||
}
|
}
|
||||||
//----------------------------------------------------
|
//----------------------------------------------------
|
||||||
|
//---- EMU State Object
|
||||||
|
//----------------------------------------------------
|
||||||
|
int EmuStateScriptObject::numInstances = 0;
|
||||||
|
|
||||||
|
//----------------------------------------------------
|
||||||
|
EmuStateScriptObject::EmuStateScriptObject(const QJSValue& jsArg1, const QJSValue& jsArg2)
|
||||||
|
{
|
||||||
|
numInstances++;
|
||||||
|
//printf("EmuStateScriptObject %p JS Constructor: %i\n", this, numInstances);
|
||||||
|
|
||||||
|
moveToThread(QApplication::instance()->thread());
|
||||||
|
QJSValueList args = { jsArg1, jsArg2 };
|
||||||
|
|
||||||
|
for (auto& jsVal : args)
|
||||||
|
{
|
||||||
|
if (jsVal.isObject())
|
||||||
|
{
|
||||||
|
//printf("EmuStateScriptObject %p JS Constructor(Obj): %i\n", this, numInstances);
|
||||||
|
auto obj = qobject_cast<EmuStateScriptObject*>(jsVal.toQObject());
|
||||||
|
|
||||||
|
if (obj != nullptr)
|
||||||
|
{
|
||||||
|
*this = *obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (jsVal.isNumber())
|
||||||
|
{
|
||||||
|
//printf("EmuStateScriptObject %p JS Constructor(int): %i\n", this, numInstances);
|
||||||
|
setSlot(jsVal.toInt());
|
||||||
|
|
||||||
|
if (slot >= 0)
|
||||||
|
{
|
||||||
|
loadFromFile(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//----------------------------------------------------
|
||||||
|
EmuStateScriptObject::~EmuStateScriptObject()
|
||||||
|
{
|
||||||
|
if (data != nullptr)
|
||||||
|
{
|
||||||
|
if (persist)
|
||||||
|
{
|
||||||
|
saveToFile(filename);
|
||||||
|
}
|
||||||
|
delete data;
|
||||||
|
data = nullptr;
|
||||||
|
}
|
||||||
|
numInstances--;
|
||||||
|
//printf("EmuStateScriptObject %p Destructor: %i\n", this, numInstances);
|
||||||
|
}
|
||||||
|
//----------------------------------------------------
|
||||||
|
EmuStateScriptObject& EmuStateScriptObject::operator= (const EmuStateScriptObject& obj)
|
||||||
|
{
|
||||||
|
setSlot( obj.slot );
|
||||||
|
persist = obj.persist;
|
||||||
|
compression = obj.compression;
|
||||||
|
filename = obj.filename;
|
||||||
|
|
||||||
|
//printf("EmuStateScriptObject Copy Assignment: %p\n", this);
|
||||||
|
|
||||||
|
if (obj.data != nullptr)
|
||||||
|
{
|
||||||
|
data = new EMUFILE_MEMORY(obj.data->size());
|
||||||
|
memcpy( data->buf(), obj.data->buf(), obj.data->size());
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------
|
||||||
|
void EmuStateScriptObject::setSlot(int value)
|
||||||
|
{
|
||||||
|
slot = value;
|
||||||
|
|
||||||
|
if (slot >= 0)
|
||||||
|
{
|
||||||
|
slot = slot % 10;
|
||||||
|
|
||||||
|
std::string fileString = FCEU_MakeFName(FCEUMKF_STATE, slot, 0);
|
||||||
|
|
||||||
|
filename = QString::fromStdString(fileString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//----------------------------------------------------
|
||||||
|
bool EmuStateScriptObject::save()
|
||||||
|
{
|
||||||
|
if (data != nullptr)
|
||||||
|
{
|
||||||
|
delete data;
|
||||||
|
data = nullptr;
|
||||||
|
}
|
||||||
|
data = new EMUFILE_MEMORY();
|
||||||
|
|
||||||
|
FCEU_WRAPPER_LOCK();
|
||||||
|
FCEUSS_SaveMS( data, compression);
|
||||||
|
data->fseek(0,SEEK_SET);
|
||||||
|
FCEU_WRAPPER_UNLOCK();
|
||||||
|
|
||||||
|
if (persist)
|
||||||
|
{
|
||||||
|
saveToFile(filename);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------
|
||||||
|
bool EmuStateScriptObject::load()
|
||||||
|
{
|
||||||
|
bool loaded = false;
|
||||||
|
if (data != nullptr)
|
||||||
|
{
|
||||||
|
FCEU_WRAPPER_LOCK();
|
||||||
|
if (FCEUSS_LoadFP( data, SSLOADPARAM_NOBACKUP))
|
||||||
|
{
|
||||||
|
data->fseek(0,SEEK_SET);
|
||||||
|
loaded = true;
|
||||||
|
}
|
||||||
|
FCEU_WRAPPER_UNLOCK();
|
||||||
|
}
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------
|
||||||
|
bool EmuStateScriptObject::saveToFile(const QString& filepath)
|
||||||
|
{
|
||||||
|
if (filepath.isEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (data == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FILE* outf = fopen(filepath.toLocal8Bit().data(),"wb");
|
||||||
|
if (outf == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fwrite( data->buf(), 1, data->size(), outf);
|
||||||
|
fclose(outf);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------
|
||||||
|
bool EmuStateScriptObject::loadFromFile(const QString& filepath)
|
||||||
|
{
|
||||||
|
if (filepath.isEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (data != nullptr)
|
||||||
|
{
|
||||||
|
delete data;
|
||||||
|
data = nullptr;
|
||||||
|
}
|
||||||
|
FILE* inf = fopen(filepath.toLocal8Bit().data(),"rb");
|
||||||
|
if (inf == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fseek(inf,0,SEEK_END);
|
||||||
|
long int len = ftell(inf);
|
||||||
|
fseek(inf,0,SEEK_SET);
|
||||||
|
data = new EMUFILE_MEMORY(len);
|
||||||
|
if ( fread(data->buf(),1,len,inf) != static_cast<size_t>(len) )
|
||||||
|
{
|
||||||
|
FCEU_printf("Warning: JS EmuState::loadFromFile failed to load full buffer.\n");
|
||||||
|
delete data;
|
||||||
|
data = nullptr;
|
||||||
|
}
|
||||||
|
fclose(inf);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------
|
||||||
//---- EMU Script Object
|
//---- EMU Script Object
|
||||||
//----------------------------------------------------
|
//----------------------------------------------------
|
||||||
EmuScriptObject::EmuScriptObject(QObject* parent)
|
EmuScriptObject::EmuScriptObject(QObject* parent)
|
||||||
|
@ -361,8 +535,6 @@ QJSValue EmuScriptObject::getScreenPixel(int x, int y, bool useBackup)
|
||||||
|
|
||||||
pixelObj->setPalette(p);
|
pixelObj->setPalette(p);
|
||||||
|
|
||||||
pixelObj->moveToThread(QApplication::instance()->thread());
|
|
||||||
|
|
||||||
QJSValue jsVal = engine->newQObject(pixelObj);
|
QJSValue jsVal = engine->newQObject(pixelObj);
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
|
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
|
||||||
|
@ -372,6 +544,22 @@ QJSValue EmuScriptObject::getScreenPixel(int x, int y, bool useBackup)
|
||||||
return jsVal;
|
return jsVal;
|
||||||
}
|
}
|
||||||
//----------------------------------------------------
|
//----------------------------------------------------
|
||||||
|
QJSValue EmuScriptObject::createState(int slot)
|
||||||
|
{
|
||||||
|
QJSValue jsVal;
|
||||||
|
EmuStateScriptObject* emuStateObj = new EmuStateScriptObject(slot);
|
||||||
|
|
||||||
|
if (emuStateObj != nullptr)
|
||||||
|
{
|
||||||
|
jsVal = engine->newQObject(emuStateObj);
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
|
||||||
|
QJSEngine::setObjectOwnership( emuStateObj, QJSEngine::JavaScriptOwnership);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return jsVal;
|
||||||
|
}
|
||||||
|
//----------------------------------------------------
|
||||||
//---- ROM Script Object
|
//---- ROM Script Object
|
||||||
//----------------------------------------------------
|
//----------------------------------------------------
|
||||||
//----------------------------------------------------
|
//----------------------------------------------------
|
||||||
|
@ -1077,8 +1265,11 @@ int QtScriptInstance::initEngine()
|
||||||
engine->globalObject().setProperty("gui", guiObject);
|
engine->globalObject().setProperty("gui", guiObject);
|
||||||
|
|
||||||
// Class Type Definitions for Script Use
|
// Class Type Definitions for Script Use
|
||||||
QJSValue jsMetaObject = engine->newQMetaObject(&JS::ColorScriptObject::staticMetaObject);
|
QJSValue jsColorMetaObject = engine->newQMetaObject(&JS::ColorScriptObject::staticMetaObject);
|
||||||
engine->globalObject().setProperty("Color", jsMetaObject);
|
engine->globalObject().setProperty("Color", jsColorMetaObject);
|
||||||
|
|
||||||
|
QJSValue jsEmuStateMetaObject = engine->newQMetaObject(&JS::EmuStateScriptObject::staticMetaObject);
|
||||||
|
engine->globalObject().setProperty("EmuState", jsEmuStateMetaObject);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,43 @@ public slots:
|
||||||
Q_INVOKABLE void setPalette(int p){ _palette = p; }
|
Q_INVOKABLE void setPalette(int p){ _palette = p; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class EmuStateScriptObject: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE EmuStateScriptObject(const QJSValue& jsArg1 = QJSValue(), const QJSValue& jsArg2 = QJSValue());
|
||||||
|
~EmuStateScriptObject();
|
||||||
|
|
||||||
|
Q_PROPERTY(bool persist READ isPersistent WRITE setPersistent)
|
||||||
|
Q_PROPERTY(int slot READ getSlot WRITE setSlot)
|
||||||
|
Q_PROPERTY(int compressionLevel READ getCompressionLevel WRITE setCompressionLevel)
|
||||||
|
|
||||||
|
EmuStateScriptObject& operator= (const EmuStateScriptObject& obj);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString filename;
|
||||||
|
EMUFILE_MEMORY *data = nullptr;
|
||||||
|
int compression = 0;
|
||||||
|
int slot = -1;
|
||||||
|
bool persist = false;
|
||||||
|
|
||||||
|
static int numInstances;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
Q_INVOKABLE bool save();
|
||||||
|
Q_INVOKABLE bool load();
|
||||||
|
Q_INVOKABLE bool isValid(){ return (data != nullptr); }
|
||||||
|
Q_INVOKABLE bool isPersistent(){ return persist; }
|
||||||
|
Q_INVOKABLE void setPersistent(bool value){ persist = value; }
|
||||||
|
Q_INVOKABLE int getSlot(){ return slot; }
|
||||||
|
Q_INVOKABLE void setSlot(int value);
|
||||||
|
Q_INVOKABLE int getCompressionLevel(){ return compression; }
|
||||||
|
Q_INVOKABLE void setCompressionLevel(int value){ compression = value; }
|
||||||
|
Q_INVOKABLE void setFilename(const QString& name){ filename = name; }
|
||||||
|
Q_INVOKABLE bool saveToFile(const QString& filepath);
|
||||||
|
Q_INVOKABLE bool loadFromFile(const QString& filepath);
|
||||||
|
};
|
||||||
|
|
||||||
class EmuScriptObject: public QObject
|
class EmuScriptObject: public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -108,6 +145,7 @@ public slots:
|
||||||
Q_INVOKABLE void exit();
|
Q_INVOKABLE void exit();
|
||||||
Q_INVOKABLE QString getDir();
|
Q_INVOKABLE QString getDir();
|
||||||
Q_INVOKABLE QJSValue getScreenPixel(int x, int y, bool useBackup = false);
|
Q_INVOKABLE QJSValue getScreenPixel(int x, int y, bool useBackup = false);
|
||||||
|
Q_INVOKABLE QJSValue createState(int slot = -1);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue