fceux/src/drivers/Qt/QtScriptManager.h

862 lines
25 KiB
C++

// QtScriptManager.h
//
#pragma once
#ifdef __FCEU_QSCRIPT_ENABLE__
#include <stdio.h>
#include <stdarg.h>
#include <QFile>
#include <QColor>
#include <QWidget>
#include <QDialog>
#include <QTabWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QCheckBox>
#include <QPushButton>
#include <QLabel>
#include <QFrame>
#include <QGroupBox>
#include <QLineEdit>
#include <QTextEdit>
#include <QTreeView>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QJSEngine>
#include <QThread>
#include <QTemporaryFile>
#include <QMenu>
#include <QMenuBar>
#include <QAction>
#include "Qt/main.h"
#include "utils/mutex.h"
#include "utils/timeStamp.h"
class QScriptDialog_t;
class QtScriptInstance;
namespace FCEU
{
class JSEngine : public QJSEngine
{
Q_OBJECT
public:
JSEngine(QObject* parent = nullptr);
~JSEngine();
QScriptDialog_t* getDialog(){ return dialog; }
QtScriptInstance* getScript(){ return script; }
void setDialog(QScriptDialog_t* _dialog){ dialog = _dialog; }
void setScript(QtScriptInstance* _script){ script = _script; }
enum logLevel
{
FATAL = 0,
CRITICAL,
WARNING,
INFO,
DEBUG,
};
void acquireThreadContext();
void releaseThreadContext();
void setLogLevel(enum logLevel lvl){ _logLevel = lvl; }
void logMessage(int lvl, const QString& msg);
static JSEngine* getCurrent();
private:
QScriptDialog_t* dialog = nullptr;
QtScriptInstance* script = nullptr;
int _logLevel = WARNING;
JSEngine* prevContext = nullptr;
};
} // FCEU
namespace JS
{
class ColorScriptObject: public QObject
{
Q_OBJECT
Q_PROPERTY(int red READ getRed WRITE setRed)
Q_PROPERTY(int green READ getGreen WRITE setGreen)
Q_PROPERTY(int blue READ getBlue WRITE setBlue)
Q_PROPERTY(int palette READ getPalette WRITE setPalette)
public:
Q_INVOKABLE ColorScriptObject(int r=0, int g=0, int b=0);
~ColorScriptObject();
private:
QColor color;
int _palette;
static int numInstances;
public slots:
Q_INVOKABLE int getRed(){ return color.red(); }
Q_INVOKABLE int getGreen(){ return color.green(); }
Q_INVOKABLE int getBlue(){ return color.blue(); }
Q_INVOKABLE void setRed(int r){ color.setRed(r); }
Q_INVOKABLE void setGreen(int g){ color.setGreen(g); }
Q_INVOKABLE void setBlue(int b){ color.setBlue(b); }
Q_INVOKABLE int getPalette(){ return _palette; }
Q_INVOKABLE void setPalette(int p){ _palette = p; }
Q_INVOKABLE int toRGB8(){ return color.value(); }
Q_INVOKABLE QString name(){ return color.name(QColor::HexRgb); }
};
class FileScriptObject: public QObject
{
Q_OBJECT
public:
Q_INVOKABLE FileScriptObject(const QString& path = QString());
~FileScriptObject();
enum Mode
{
ReadOnly = 0x01,
WriteOnly = 0x02,
ReadWrite = 0x03,
Append = 0x04,
Truncate = 0x08
};
Q_ENUM(Mode);
private:
static int numInstances;
QString filepath;
bool tmp = false;
QFile *file = nullptr;
public slots:
Q_INVOKABLE bool open(int mode = ReadOnly);
Q_INVOKABLE void close();
Q_INVOKABLE bool isOpen();
Q_INVOKABLE bool isReadable();
Q_INVOKABLE bool isWritable();
Q_INVOKABLE bool flush();
Q_INVOKABLE bool atEnd();
Q_INVOKABLE bool truncate();
Q_INVOKABLE bool resize(int64_t size);
Q_INVOKABLE bool seek(int64_t pos);
Q_INVOKABLE int64_t pos();
Q_INVOKABLE int64_t bytesAvailable();
Q_INVOKABLE bool isTemporary(){ return tmp; }
Q_INVOKABLE void setTemporary(bool value);
Q_INVOKABLE void setFilePath(const QString& path);
Q_INVOKABLE QString fileName();
Q_INVOKABLE QString fileSuffix();
Q_INVOKABLE QString filePath(){ return filepath; }
Q_INVOKABLE QString readLine();
Q_INVOKABLE int64_t skip(int64_t size);
Q_INVOKABLE QByteArray readData(unsigned int maxBytes = 0);
Q_INVOKABLE QByteArray peekData(unsigned int maxSize);
Q_INVOKABLE int writeString(const QString& s);
Q_INVOKABLE int writeData(const QByteArray& data);
Q_INVOKABLE bool putChar(char c);
Q_INVOKABLE char getChar();
};
class JoypadScriptObject: public QObject
{
Q_OBJECT
Q_PROPERTY(bool up READ getUp)
Q_PROPERTY(bool down READ getDown)
Q_PROPERTY(bool left READ getLeft)
Q_PROPERTY(bool right READ getRight)
Q_PROPERTY(bool select READ getSelect)
Q_PROPERTY(bool start READ getStart)
Q_PROPERTY(bool a READ getA)
Q_PROPERTY(bool b READ getB)
Q_PROPERTY(int player READ getPlayer WRITE setPlayer)
public:
Q_INVOKABLE JoypadScriptObject(int playerIdx = 0, bool immediate = false);
~JoypadScriptObject();
static constexpr int MAX_JOYPAD_PLAYERS = 4;
enum Button
{
FIRST_BUTTON = 0,
A_BUTTON = FIRST_BUTTON,
B_BUTTON,
SELECT_BUTTON,
START_BUTTON,
UP_BUTTON,
DOWN_BUTTON,
LEFT_BUTTON,
RIGHT_BUTTON,
LAST_BUTTON = RIGHT_BUTTON,
END_BUTTON
};
Q_ENUM(Button);
// Joypad Override Function
static uint8_t readOverride(int which, uint8_t joyl);
private:
// Joypad Override Bit Masks
static uint8_t jsOverrideMask1[MAX_JOYPAD_PLAYERS];
static uint8_t jsOverrideMask2[MAX_JOYPAD_PLAYERS];
struct buttonState
{
uint32_t buttonMask = 0;
bool _immediate = false;
};
buttonState current;
buttonState prev;
int player = 0;
static int numInstances;
static constexpr uint32_t ButtonMaskA = 0x01;
static constexpr uint32_t ButtonMaskB = 0x02;
static constexpr uint32_t ButtonMaskSelect = 0x04;
static constexpr uint32_t ButtonMaskStart = 0x08;
static constexpr uint32_t ButtonMaskUp = 0x10;
static constexpr uint32_t ButtonMaskDown = 0x20;
static constexpr uint32_t ButtonMaskLeft = 0x40;
static constexpr uint32_t ButtonMaskRight = 0x80;
template <uint32_t mask> bool isBitSet(uint32_t& byte)
{
return (byte & mask) ? true : false;
}
template <uint32_t mask> bool isChanged()
{
return ((current.buttonMask ^ prev.buttonMask) & mask) ? true : false;
}
template <uint32_t mask> void setButtonOverride(bool value)
{
if (value)
{
jsOverrideMask1[player] |= mask;
jsOverrideMask2[player] |= mask;
}
else
{
jsOverrideMask1[player] &= ~mask;
jsOverrideMask2[player] &= ~mask;
}
}
template <uint32_t mask> void clearButtonOverride()
{
jsOverrideMask1[player] |= mask;
jsOverrideMask2[player] &= ~mask;
}
template <uint32_t mask> void invertButtonOverride()
{
jsOverrideMask1[player] &= ~mask;
jsOverrideMask2[player] |= mask;
}
public slots:
Q_INVOKABLE void refreshData(bool immediate = false);
Q_INVOKABLE bool getUp(){ return isBitSet<ButtonMaskUp>(current.buttonMask); }
Q_INVOKABLE bool getDown(){ return isBitSet<ButtonMaskDown>(current.buttonMask); }
Q_INVOKABLE bool getLeft(){ return isBitSet<ButtonMaskLeft>(current.buttonMask); }
Q_INVOKABLE bool getRight(){ return isBitSet<ButtonMaskRight>(current.buttonMask); }
Q_INVOKABLE bool getSelect(){ return isBitSet<ButtonMaskSelect>(current.buttonMask); }
Q_INVOKABLE bool getStart(){ return isBitSet<ButtonMaskStart>(current.buttonMask); }
Q_INVOKABLE bool getA(){ return isBitSet<ButtonMaskA>(current.buttonMask); }
Q_INVOKABLE bool getB(){ return isBitSet<ButtonMaskB>(current.buttonMask); }
Q_INVOKABLE bool upChanged(){ return isChanged<ButtonMaskUp>(); }
Q_INVOKABLE bool downChanged(){ return isChanged<ButtonMaskDown>(); }
Q_INVOKABLE bool leftChanged(){ return isChanged<ButtonMaskLeft>(); }
Q_INVOKABLE bool rightChanged(){ return isChanged<ButtonMaskRight>(); }
Q_INVOKABLE bool selectChanged(){ return isChanged<ButtonMaskSelect>(); }
Q_INVOKABLE bool startChanged(){ return isChanged<ButtonMaskStart>(); }
Q_INVOKABLE bool aChanged(){ return isChanged<ButtonMaskA>(); }
Q_INVOKABLE bool bChanged(){ return isChanged<ButtonMaskB>(); }
Q_INVOKABLE bool isImmediate(){ return current._immediate; }
Q_INVOKABLE bool getButton(enum Button b);
Q_INVOKABLE bool buttonChanged(enum Button b);
Q_INVOKABLE bool stateChanged(){ return prev.buttonMask != current.buttonMask; }
Q_INVOKABLE void setState(int mask){ prev.buttonMask = current.buttonMask; current.buttonMask = mask; }
Q_INVOKABLE int getState(){ return current.buttonMask; }
Q_INVOKABLE int maxPlayers(){ return MAX_JOYPAD_PLAYERS; }
Q_INVOKABLE int getPlayer(){ return player; }
Q_INVOKABLE void setPlayer(int newPlayerIdx){ player = newPlayerIdx; }
Q_INVOKABLE void ovrdClearA(){ clearButtonOverride<ButtonMaskA>(); }
Q_INVOKABLE void ovrdClearB(){ clearButtonOverride<ButtonMaskB>(); }
Q_INVOKABLE void ovrdClearStart(){ clearButtonOverride<ButtonMaskStart>(); }
Q_INVOKABLE void ovrdClearSelect(){ clearButtonOverride<ButtonMaskSelect>(); }
Q_INVOKABLE void ovrdClearUp(){ clearButtonOverride<ButtonMaskUp>(); }
Q_INVOKABLE void ovrdClearDown(){ clearButtonOverride<ButtonMaskDown>(); }
Q_INVOKABLE void ovrdClearLeft(){ clearButtonOverride<ButtonMaskLeft>(); }
Q_INVOKABLE void ovrdClearRight(){ clearButtonOverride<ButtonMaskRight>(); }
Q_INVOKABLE void ovrdClear(){ ovrdReset(); }
Q_INVOKABLE void ovrdInvertA(){ invertButtonOverride<ButtonMaskA>(); }
Q_INVOKABLE void ovrdInvertB(){ invertButtonOverride<ButtonMaskB>(); }
Q_INVOKABLE void ovrdInvertStart(){ invertButtonOverride<ButtonMaskStart>(); }
Q_INVOKABLE void ovrdInvertSelect(){ invertButtonOverride<ButtonMaskSelect>(); }
Q_INVOKABLE void ovrdInvertUp(){ invertButtonOverride<ButtonMaskUp>(); }
Q_INVOKABLE void ovrdInvertDown(){ invertButtonOverride<ButtonMaskDown>(); }
Q_INVOKABLE void ovrdInvertLeft(){ invertButtonOverride<ButtonMaskLeft>(); }
Q_INVOKABLE void ovrdInvertRight(){ invertButtonOverride<ButtonMaskRight>(); }
Q_INVOKABLE void ovrdA(bool value){ setButtonOverride<ButtonMaskA>(value); }
Q_INVOKABLE void ovrdB(bool value){ setButtonOverride<ButtonMaskB>(value); }
Q_INVOKABLE void ovrdStart(bool value){ setButtonOverride<ButtonMaskStart>(value); }
Q_INVOKABLE void ovrdSelect(bool value){ setButtonOverride<ButtonMaskSelect>(value); }
Q_INVOKABLE void ovrdUp(bool value){ setButtonOverride<ButtonMaskUp>(value); }
Q_INVOKABLE void ovrdDown(bool value){ setButtonOverride<ButtonMaskDown>(value); }
Q_INVOKABLE void ovrdLeft(bool value){ setButtonOverride<ButtonMaskLeft>(value); }
Q_INVOKABLE void ovrdRight(bool value){ setButtonOverride<ButtonMaskRight>(value); }
Q_INVOKABLE void ovrdReset()
{
jsOverrideMask1[player] = 0xFF;
jsOverrideMask2[player] = 0x00;
}
Q_INVOKABLE static void ovrdResetAll();
};
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;
void logMessage(int lvl, QString& msg);
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);
Q_INVOKABLE bool saveFileExists();
Q_INVOKABLE QJSValue copy();
};
class EmuScriptObject: public QObject
{
Q_OBJECT
public:
EmuScriptObject(QObject* parent = nullptr);
~EmuScriptObject();
void setEngine(FCEU::JSEngine* _engine){ engine = _engine; }
void setDialog(QScriptDialog_t* _dialog){ dialog = _dialog; }
private:
FCEU::JSEngine* engine = nullptr;
QScriptDialog_t* dialog = nullptr;
QtScriptInstance* script = nullptr;
public slots:
Q_INVOKABLE void print(const QString& msg);
Q_INVOKABLE void powerOn();
Q_INVOKABLE void softReset();
Q_INVOKABLE void pause();
Q_INVOKABLE void unpause();
Q_INVOKABLE bool paused();
Q_INVOKABLE void frameAdvance();
Q_INVOKABLE int frameCount();
Q_INVOKABLE int lagCount();
Q_INVOKABLE bool lagged();
Q_INVOKABLE void setLagFlag(bool flag);
Q_INVOKABLE bool emulating();
Q_INVOKABLE bool isReadOnly();
Q_INVOKABLE void setReadOnly(bool flag);
Q_INVOKABLE void setRenderPlanes(bool sprites, bool background);
Q_INVOKABLE void registerBeforeFrame(const QJSValue& func);
Q_INVOKABLE void registerAfterFrame(const QJSValue& func);
Q_INVOKABLE void registerStop(const QJSValue& func);
Q_INVOKABLE void message(const QString& msg);
Q_INVOKABLE void speedMode(const QString& mode);
Q_INVOKABLE bool loadRom(const QString& romPath);
Q_INVOKABLE bool onEmulationThread();
Q_INVOKABLE bool addGameGenie(const QString& code);
Q_INVOKABLE bool delGameGenie(const QString& code);
Q_INVOKABLE void exit();
Q_INVOKABLE QString getDir();
Q_INVOKABLE QJSValue getScreenPixel(int x, int y, bool useBackup = false);
Q_INVOKABLE QJSValue createState(int slot = -1);
};
class RomScriptObject: public QObject
{
Q_OBJECT
public:
RomScriptObject(QObject* parent = nullptr);
~RomScriptObject();
void setEngine(FCEU::JSEngine* _engine){ engine = _engine; }
void setDialog(QScriptDialog_t* _dialog){ dialog = _dialog; }
private:
FCEU::JSEngine* engine = nullptr;
QScriptDialog_t* dialog = nullptr;
QtScriptInstance* script = nullptr;
public slots:
Q_INVOKABLE bool isLoaded();
Q_INVOKABLE QString getFileName();
Q_INVOKABLE QString getHash(const QString& type);
Q_INVOKABLE uint8_t readByte(int address);
Q_INVOKABLE int8_t readByteSigned(int address);
Q_INVOKABLE uint8_t readByteUnsigned(int address);
Q_INVOKABLE QJSValue readByteRange(int start, int end);
Q_INVOKABLE void writeByte(int address, int value);
};
class MemoryScriptObject: public QObject
{
Q_OBJECT
public:
MemoryScriptObject(QObject* parent = nullptr);
~MemoryScriptObject();
void setEngine(FCEU::JSEngine* _engine){ engine = _engine; }
void setDialog(QScriptDialog_t* _dialog){ dialog = _dialog; }
void reset();
QtScriptInstance* getScript(){ return script; }
QJSValue* getReadFunc(int address) { return readFunc[address]; }
QJSValue* getWriteFunc(int address) { return writeFunc[address]; }
QJSValue* getExecFunc(int address) { return execFunc[address]; }
private:
static constexpr int AddressRange = 0x10000;
FCEU::JSEngine* engine = nullptr;
QScriptDialog_t* dialog = nullptr;
QtScriptInstance* script = nullptr;
QJSValue* readFunc[AddressRange] = { nullptr };
QJSValue* writeFunc[AddressRange] = { nullptr };
QJSValue* execFunc[AddressRange] = { nullptr };
int numReadFuncsRegistered = 0;
int numWriteFuncsRegistered = 0;
int numExecFuncsRegistered = 0;
void registerCallback(int type, const QJSValue& func, int address, int size = 1);
void unregisterCallback(int type, const QJSValue& func, int address, int size = 1);
public slots:
Q_INVOKABLE uint8_t readByte(int address);
Q_INVOKABLE int8_t readByteSigned(int address);
Q_INVOKABLE uint8_t readByteUnsigned(int address);
Q_INVOKABLE uint16_t readWord(int addressLow, int addressHigh = -1);
Q_INVOKABLE int16_t readWordSigned(int addressLow, int addressHigh = -1);
Q_INVOKABLE uint16_t readWordUnsigned(int addressLow, int addressHigh = -1);
Q_INVOKABLE void writeByte(int address, int value);
Q_INVOKABLE uint16_t getRegisterPC();
Q_INVOKABLE uint8_t getRegisterA();
Q_INVOKABLE uint8_t getRegisterX();
Q_INVOKABLE uint8_t getRegisterY();
Q_INVOKABLE uint8_t getRegisterS();
Q_INVOKABLE uint8_t getRegisterP();
Q_INVOKABLE void setRegisterPC(uint16_t v);
Q_INVOKABLE void setRegisterA(uint8_t v);
Q_INVOKABLE void setRegisterX(uint8_t v);
Q_INVOKABLE void setRegisterY(uint8_t v);
Q_INVOKABLE void setRegisterS(uint8_t v);
Q_INVOKABLE void setRegisterP(uint8_t v);
Q_INVOKABLE void registerRead(const QJSValue& func, int address, int size = 1);
Q_INVOKABLE void registerWrite(const QJSValue& func, int address, int size = 1);
Q_INVOKABLE void registerExec(const QJSValue& func, int address, int size = 1);
Q_INVOKABLE void unregisterRead(const QJSValue& func, int address, int size = 1);
Q_INVOKABLE void unregisterWrite(const QJSValue& func, int address, int size = 1);
Q_INVOKABLE void unregisterExec(const QJSValue& func, int address, int size = 1);
Q_INVOKABLE void unregisterAll();
};
class PpuScriptObject: public QObject
{
Q_OBJECT
public:
PpuScriptObject(QObject* parent = nullptr);
~PpuScriptObject();
void setEngine(FCEU::JSEngine* _engine){ engine = _engine; }
void setDialog(QScriptDialog_t* _dialog){ dialog = _dialog; }
private:
FCEU::JSEngine* engine = nullptr;
QScriptDialog_t* dialog = nullptr;
QtScriptInstance* script = nullptr;
public slots:
Q_INVOKABLE uint8_t readByte(int address);
Q_INVOKABLE int8_t readByteSigned(int address);
Q_INVOKABLE uint8_t readByteUnsigned(int address);
Q_INVOKABLE QJSValue readByteRange(int start, int end);
Q_INVOKABLE void writeByte(int address, int value);
};
class MovieScriptObject: public QObject
{
Q_OBJECT
public:
MovieScriptObject(QObject* parent = nullptr);
~MovieScriptObject();
void setEngine(FCEU::JSEngine* _engine){ engine = _engine; }
enum Mode
{
IDLE = 0,
RECORD,
PLAYBACK,
FINISHED,
TAS_EDITOR
};
Q_ENUM(Mode);
enum SaveType
{
FROM_POWERON = 0,
FROM_SAVESTATE,
FROM_SAVERAM,
};
Q_ENUM(SaveType);
static bool skipRerecords;
private:
FCEU::JSEngine* engine = nullptr;
QtScriptInstance* script = nullptr;
public slots:
Q_INVOKABLE bool active();
Q_INVOKABLE bool isPlaying();
Q_INVOKABLE bool isRecording();
Q_INVOKABLE bool isPowerOn();
Q_INVOKABLE bool isFromSaveState();
Q_INVOKABLE void replay();
Q_INVOKABLE bool getReadOnly();
Q_INVOKABLE void setReadOnly(bool which);
Q_INVOKABLE bool play(const QString& filename, bool readOnly = false, int pauseFrame = 0);
Q_INVOKABLE bool record(const QString& filename, int saveType = FROM_POWERON, const QString author = QString());
Q_INVOKABLE int frameCount();
Q_INVOKABLE int mode();
Q_INVOKABLE void stop();
Q_INVOKABLE int length();
Q_INVOKABLE int rerecordCount();
Q_INVOKABLE void rerecordCounting(bool counting);
Q_INVOKABLE QString getFilename();
Q_INVOKABLE QString getFilepath();
Q_INVOKABLE void close(){ stop(); }
Q_INVOKABLE bool readOnly(){ return getReadOnly(); }
Q_INVOKABLE void playBeginning(){ replay(); }
Q_INVOKABLE bool playback(const QString& filename, bool readOnly = false, int pauseFrame = 0){ return play(filename, readOnly, pauseFrame); }
Q_INVOKABLE bool load(const QString& filename, bool readOnly = false, int pauseFrame = 0){ return play(filename, readOnly, pauseFrame); }
Q_INVOKABLE bool save(const QString& filename, int saveType = FROM_POWERON, const QString author = QString()){ return record(filename, saveType, author); }
};
class InputScriptObject: public QObject
{
Q_OBJECT
public:
InputScriptObject(QObject* parent = nullptr);
~InputScriptObject();
void setEngine(FCEU::JSEngine* _engine){ engine = _engine; }
private:
FCEU::JSEngine* engine = nullptr;
QtScriptInstance* script = nullptr;
public slots:
Q_INVOKABLE QJSValue readJoypad(int player = 0, bool immediate = false);
Q_INVOKABLE int maxJoypadPlayers(){ return JoypadScriptObject::MAX_JOYPAD_PLAYERS; }
};
class ModuleLoaderObject: public QObject
{
Q_OBJECT
public:
ModuleLoaderObject(QObject* parent = nullptr);
~ModuleLoaderObject();
void setEngine(FCEU::JSEngine* _engine){ engine = _engine; }
private:
FCEU::JSEngine* engine = nullptr;
QtScriptInstance* script = nullptr;
int scopeCounter = 0;
public slots:
Q_INVOKABLE void GlobalImport(const QString& ns, const QString& file);
};
} // JS
class ScriptExecutionState
{
public:
void start()
{
timeMs = 0;
executing = true;
}
void stop()
{
executing = false;
timeMs = 0;
}
bool isRunning(){ return executing; }
unsigned int timeCheck()
{
unsigned int retval = timeMs;
timeMs += checkPeriod;
return retval;
}
static constexpr unsigned int checkPeriod = 100;
private:
bool executing = false;
unsigned int timeMs = 0;
};
class QtScriptInstance : public QObject
{
Q_OBJECT
public:
QtScriptInstance(QObject* parent = nullptr);
~QtScriptInstance();
void resetEngine();
int loadScriptFile(QString filepath);
bool isRunning(){ return running; };
void stopRunning();
int call(const QString& funcName, const QJSValueList& args = QJSValueList());
void frameAdvance();
void onFrameBegin();
void onFrameFinish();
void onGuiUpdate();
void checkForHang();
void flushLog();
int runFunc(QJSValue &func, const QJSValueList& args = QJSValueList());
int throwError(QJSValue::ErrorType errorType, const QString &message = QString());
const QString& getSrcFile(){ return srcFile; };
FCEU::JSEngine* getEngine(){ return engine; };
private:
int initEngine();
void shutdownEngine();
void printSymbols(QJSValue& val, int iter = 0);
void loadObjectChildren(QJSValue& jsObject, QObject* obj);
ScriptExecutionState* getExecutionState();
FCEU::JSEngine* engine = nullptr;
QScriptDialog_t* dialog = nullptr;
JS::EmuScriptObject* emu = nullptr;
JS::RomScriptObject* rom = nullptr;
JS::PpuScriptObject* ppu = nullptr;
JS::MemoryScriptObject* mem = nullptr;
JS::InputScriptObject* input = nullptr;
JS::MovieScriptObject* movie = nullptr;
JS::ModuleLoaderObject* moduleLoader = nullptr;
QWidget* ui_rootWidget = nullptr;
QJSValue *onFrameBeginCallback = nullptr;
QJSValue *onFrameFinishCallback = nullptr;
QJSValue *onScriptStopCallback = nullptr;
QJSValue *onGuiUpdateCallback = nullptr;
ScriptExecutionState guiFuncState;
ScriptExecutionState emuFuncState;
int frameAdvanceCount = 0;
int frameAdvanceState = 0;
bool running = false;
QString srcFile;
signals:
void errorNotify();
public slots:
Q_INVOKABLE void print(const QString& msg);
Q_INVOKABLE void loadUI(const QString& uiFilePath);
Q_INVOKABLE QString openFileBrowser(const QString& initialPath = QString());
Q_INVOKABLE void registerBeforeEmuFrame(const QJSValue& func);
Q_INVOKABLE void registerAfterEmuFrame(const QJSValue& func);
Q_INVOKABLE void registerStop(const QJSValue& func);
Q_INVOKABLE void registerGuiUpdate(const QJSValue& func);
Q_INVOKABLE bool onGuiThread();
Q_INVOKABLE bool onEmulationThread();
};
class ScriptMonitorThread_t : public QThread
{
Q_OBJECT
protected:
void run( void ) override;
public:
ScriptMonitorThread_t( QObject *parent = 0 );
};
class QtScriptManager : public QObject
{
Q_OBJECT
public:
QtScriptManager(QObject* parent = nullptr);
~QtScriptManager();
static QtScriptManager* getInstance(){ return _instance; }
static QtScriptManager* create(QObject* parent = nullptr);
static void destroy();
static void logMessageQt(QtMsgType type, const QString &msg);
int numScriptsLoaded(void){ return scriptList.size(); }
void addScriptInstance(QtScriptInstance* script);
void removeScriptInstance(QtScriptInstance* script);
private:
static QtScriptManager* _instance;
FCEU::mutex scriptListMutex;
QList<QtScriptInstance*> scriptList;
QTimer *periodicUpdateTimer = nullptr;
ScriptMonitorThread_t *monitorThread = nullptr;
friend class ScriptMonitorThread_t;
public slots:
void frameBeginUpdate();
void frameFinishedUpdate();
void guiUpdate();
};
class JsPropertyItem : public QTreeWidgetItem
{
public:
JsPropertyItem()
: QTreeWidgetItem()
{
}
virtual ~JsPropertyItem() override
{
}
QJSValue jsValue;
QMap<QString, JsPropertyItem*> childMap;
};
class JsPropertyTree : public QTreeWidget
{
Q_OBJECT
public:
JsPropertyTree(QWidget *parent = nullptr)
: QTreeWidget(parent)
{
}
virtual ~JsPropertyTree() override
{
}
QMap<QString, JsPropertyItem*> childMap;
};
class QScriptDialog_t : public QDialog
{
Q_OBJECT
public:
QScriptDialog_t(QWidget *parent = nullptr);
~QScriptDialog_t(void);
void refreshState(void);
void logOutput(const QString& text);
protected:
void resetLog();
void closeEvent(QCloseEvent *bar);
void openJSKillMessageBox(void);
void clearPropertyTree();
void loadPropertyTree(QJSValue& val, JsPropertyItem* parentItem = nullptr);
QMenuBar* buildMenuBar();
QMenuBar* menuBar;
QTemporaryFile *logFile = nullptr;
QTimer *periodicTimer;
QLineEdit *scriptPath;
QLineEdit *scriptArgs;
QPushButton *browseButton;
QPushButton *stopButton;
QPushButton *startButton;
QPushButton *clearButton;
QTabWidget *tabWidget;
QTextEdit *jsOutput;
JsPropertyTree *propTree;
QtScriptInstance *scriptInstance;
QString emuThreadText;
QString logSavePath;
QLabel *logFilepathLbl;
QLabel *logFilepath;
private:
public slots:
void flushLog();
void closeWindow(void);
private slots:
void saveLog(bool openFileBrowser = false);
void updatePeriodic(void);
void openScriptFile(void);
void startScript(void);
void stopScript(void);
void onLogLinkClicked(const QString&);
void onScriptError(void);
void reloadGlobalTree(void);
};
bool FCEU_JSRerecordCountSkip();
uint8_t FCEU_JSReadJoypad(int which, uint8_t phyState);
#endif // __FCEU_QSCRIPT_ENABLE__