1
0
Fork 0
mirror of https://github.com/melonDS-emu/melonDS.git synced 2025-03-06 21:00:31 +01:00
melonDS/src/frontend/qt_sdl/LuaMain.cpp
NPO 839f73321c Revised Keyboard input, currentDir
Changed KeyboardMask to HeldKeys function.
Changed filesystem::current_path to QDir::setCurrent to avoid use of 'c_str'...
2024-11-05 13:50:57 -05:00

633 lines
No EOL
17 KiB
C++

#include "LuaMain.h"
#include <filesystem>
#include <QDir>
#include <QFileDialog>
#include <QPushButton>
#include <QGuiApplication>
#include <QScrollBar>
#include <QPainter>
#include "types.h"
#include "NDS.h"
#include <SDL_joystick.h>
#include <NDS_Header.h>
#include "main.h"
LuaBundle::LuaBundle(LuaConsoleDialog* dialog, EmuInstance* inst)
{
emuInstance = inst;
emuThread = emuInstance->getEmuThread();
luaDialog = dialog;
overlays = new std::vector<OverlayCanvas>;
imageHash = new QHash<QString, QImage>;
}
LuaConsoleDialog::LuaConsoleDialog(QWidget* parent) : QDialog(parent)
{
QWidget* w = parent;
MainWindow* mainWindow;
for (;;) //copied from ScreenPanel in Screen.cpp
{
mainWindow = qobject_cast<MainWindow*>(w);
if (mainWindow) break;
w = w->parentWidget();
if (!w) break;
}
bundle = new LuaBundle(this,mainWindow->getEmuInstance());
console = new LuaConsole(this);
console->setGeometry(0,20,302,80);
bar = console->verticalScrollBar();
buttonPausePlay = new QPushButton("Pause/UnPause",this);
buttonPausePlay->setGeometry(0,0,100,20);
buttonStartStop = new QPushButton("Stop",this);
buttonStartStop->setGeometry(101,0,100,20);
buttonOpenScript = new QPushButton("OpenLuaFile",this);
buttonOpenScript->setGeometry(202,0,100,20);
connect(buttonOpenScript,&QPushButton::clicked,this,&LuaConsoleDialog::onOpenScript);
connect(buttonStartStop,&QPushButton::clicked,this,&LuaConsoleDialog::onStop);
connect(buttonPausePlay,&QPushButton::clicked,this,&LuaConsoleDialog::onPausePlay);
this->setWindowTitle("Lua Script");
}
void LuaConsoleDialog::closeEvent(QCloseEvent *event)
{
onStop();
bundle->overlays->clear();
flagClosed = true;
event->accept();
}
void LuaConsoleDialog::onOpenScript()
{
QFileInfo file = QFileInfo(QFileDialog::getOpenFileName(this, "Load Lua Script",QDir::currentPath()));
if (!file.exists()) return;
currentScript = file;
bundle->flagNewLua = true;
}
LuaConsole::LuaConsole(QWidget* parent)
{
this->setParent(parent);
}
void LuaConsole::onGetText(const QString& string)
{
this->appendPlainText(string);
QScrollBar* bar = verticalScrollBar();
bar->setValue(bar->maximum());
}
void LuaBundle::printText(QString string)
{
this->luaDialog->console->onGetText(string);
}
void LuaConsoleDialog::onLuaSaveState(QString string)
{
emit signalLuaSaveState(string);
}
void LuaConsoleDialog::onLuaLoadState(QString string)
{
emit signalLuaLoadState(string);
}
void LuaConsole::onClear()
{
this->clear();
}
LuaFunction::LuaFunction(luaFunctionPointer cf,const char* n,std::vector<LuaFunction*>* container)
{
this->cfunction = cf;
this->name = n;
container->push_back(this);
}
static_assert(sizeof(LuaBundle*) <= LUA_EXTRASPACE,"LUA_EXTRASPACE too small");
LuaBundle* get_bundle(lua_State * L)
{
LuaBundle* pBundle;
std::memcpy(&pBundle, lua_getextraspace(L), sizeof(LuaBundle*));
return pBundle;
}
#define MELON_LUA_HOOK_INSTRUCTION_COUNT 50 //number of vm instructions between hook calls
void luaHookFunction(lua_State* L, lua_Debug *arg)
{
LuaBundle* bundle = get_bundle(L);
if (bundle->flagStop and (arg->event == LUA_HOOKCOUNT))
luaL_error(L, "Force Stopped");
}
std::vector<LuaFunction*> definedLuaFunctions;//List of all defined lua functions
void LuaBundle::createLuaState()
{
if (!flagNewLua) return;
overlays->clear();
flagNewLua = false;
luaState = nullptr;
QByteArray fileName = luaDialog->currentScript.fileName().toLocal8Bit();
QString filedir = luaDialog->currentScript.dir().path();
lua_State* L = luaL_newstate();
LuaBundle* pBundle = this;
std::memcpy(lua_getextraspace(L), &pBundle, sizeof(LuaBundle*)); //Write a pointer to this LuaBundle into the extra space of the new lua_State
luaL_openlibs(L);
for (LuaFunction* function : definedLuaFunctions)
lua_register(L,function->name,function->cfunction);
QDir::setCurrent(filedir);
lua_sethook(L,&luaHookFunction,LUA_MASKCOUNT,MELON_LUA_HOOK_INSTRUCTION_COUNT);
if (luaL_dofile(L,fileName.data())==LUA_OK)
{
luaState = L;
}
else //Error loading script
{
printText(lua_tostring(L,-1));
}
}
void LuaConsoleDialog::onStop()
{
if (bundle->getLuaState())
bundle->flagStop = true;
}
void LuaConsoleDialog::onPausePlay()
{
bundle->flagPause = !bundle->flagPause;
}
void LuaConsoleDialog::onLuaUpdate()
{
bundle->createLuaState();
bundle->luaUpdate();
}
//Gets Called once a frame
void LuaBundle::luaUpdate()
{
if (!luaState || flagPause) return;
if (lua_getglobal(luaState,"_Update")!=LUA_TFUNCTION)
{
printText("No \"_Update\" Function found, pausing script...");
flagPause = true;
return;
}
if (lua_pcall(luaState,0,0,0)!=0)
{
//Handel Errors
printText(lua_tostring(luaState,-1));
luaState = nullptr;
}
}
OverlayCanvas::OverlayCanvas(int x,int y,int width,int height,LuaCanvasTarget t)
{
target = t;
buffer1 = new QImage(width,height,QImage::Format_ARGB32_Premultiplied);
buffer2 = new QImage(width,height,QImage::Format_ARGB32_Premultiplied);
buffer1->fill(0xffffff00); //initializes buffer with yellow pixels (probably should change this to transparent black pixels at some point...)
buffer2->fill(0xffffff00);
imageBuffer = buffer1;
displayBuffer = buffer2;
rectangle = QRect(x,y,width,height);
flipped = false;
GLTextureLoaded = false;
}
void OverlayCanvas::flip()
{
if (imageBuffer == buffer1)
{
imageBuffer = buffer2;
displayBuffer = buffer1;
}
else
{
imageBuffer = buffer1;
displayBuffer = buffer2;
}
flipped = true;
}
void LuaBundle::luaResetOSD()
{
for (auto lo = overlays->begin(); lo != overlays->end();)
{
OverlayCanvas& overlay = *lo;
overlay.GLTextureLoaded = false;
lo++;
}
}
//TODO: Organize lua functions into different named tables similar to bizhawk.
/*--------------------------------------------------------------------------------------------------
Start of lua function definitions
--------------------------------------------------------------------------------------------------*/
namespace luaDefinitions
{
#define AddLuaFunction(functPointer,name)LuaFunction name(functPointer,#name,&definedLuaFunctions)
int lua_MelonPrint(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
const char* string = luaL_checkstring(L,1);
bundle->printText((QString)string);
return 0;
}
AddLuaFunction(lua_MelonPrint,print);
int lua_MelonClear(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
bundle->getluaDialog()->console->clear();
return 0;
}
AddLuaFunction(lua_MelonClear,MelonClear);
enum ramInfo_ByteType
{
ramInfo_OneByte = 1,
ramInfo_TwoBytes = 2,
ramInfo_FourBytes = 4
};
melonDS::u32 GetMainRAMValueU(const melonDS::u32& addr, const ramInfo_ByteType& byteType,LuaBundle* bundle)
{
melonDS::NDS* nds = bundle->getEmuInstance()->getNDS();
switch (byteType)
{
case ramInfo_OneByte:
return *(melonDS::u8*)(nds->MainRAM + (addr&nds->MainRAMMask));
case ramInfo_TwoBytes:
return *(melonDS::u16*)(nds->MainRAM + (addr&nds->MainRAMMask));
case ramInfo_FourBytes:
return *(melonDS::u32*)(nds->MainRAM + (addr&nds->MainRAMMask));
default:
return 0;
}
}
melonDS::s32 GetMainRAMValueS(const melonDS::u32& addr, const ramInfo_ByteType& byteType,LuaBundle* bundle)
{
melonDS::NDS* nds = bundle->getEmuInstance()->getNDS();
switch (byteType)
{
case ramInfo_OneByte:
return *(melonDS::s8*)(nds->MainRAM + (addr&nds->MainRAMMask));
case ramInfo_TwoBytes:
return *(melonDS::s16*)(nds->MainRAM + (addr&nds->MainRAMMask));
case ramInfo_FourBytes:
return *(melonDS::s32*)(nds->MainRAM + (addr&nds->MainRAMMask));
default:
return 0;
}
}
int Lua_ReadDatau(lua_State* L,ramInfo_ByteType byteType)
{
LuaBundle* bundle = get_bundle(L);
melonDS::u32 address = luaL_checkinteger(L,1);
melonDS::u32 value = GetMainRAMValueU(address,byteType,bundle);
lua_pushinteger(L, value);
return 1;
}
int Lua_ReadDatas(lua_State* L,ramInfo_ByteType byteType)
{
LuaBundle* bundle = get_bundle(L);
melonDS::u32 address = luaL_checkinteger(L,1);
melonDS::s32 value = GetMainRAMValueS(address,byteType,bundle);
lua_pushinteger(L, value);
return 1;
}
int Lua_Readu8(lua_State* L)
{
return Lua_ReadDatau(L,ramInfo_OneByte);
}
AddLuaFunction(Lua_Readu8,Readu8);
int Lua_Readu16(lua_State* L)
{
return Lua_ReadDatau(L,ramInfo_TwoBytes);
}
AddLuaFunction(Lua_Readu16,Readu16);
int Lua_Readu32(lua_State* L)
{
return Lua_ReadDatau(L,ramInfo_FourBytes);
}
AddLuaFunction(Lua_Readu32,Readu32);
int Lua_Reads8(lua_State* L)
{
return Lua_ReadDatas(L,ramInfo_OneByte);
}
AddLuaFunction(Lua_Reads8,Reads8);
int Lua_Reads16(lua_State* L)
{
return Lua_ReadDatas(L,ramInfo_TwoBytes);
}
AddLuaFunction(Lua_Reads16,Reads16);
int Lua_Reads32(lua_State* L)
{
return Lua_ReadDatas(L,ramInfo_FourBytes);
}
AddLuaFunction(Lua_Reads32,Reads32);
int Lua_NDSTapDown(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
melonDS::NDS* nds = bundle->getEmuInstance()->getNDS();
int x = luaL_checkinteger(L,1);
int y = luaL_checkinteger(L,2);
nds->TouchScreen(x,y);
return 0;
}
AddLuaFunction(Lua_NDSTapDown,NDSTapDown);
int Lua_NDSTapUp(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
melonDS::NDS* nds = bundle->getEmuInstance()->getNDS();
nds->ReleaseScreen();
return 0;
}
AddLuaFunction(Lua_NDSTapUp,NDSTapUp);
int Lua_StateSave(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
const char* filename = luaL_checkstring(L,1);
bundle->getluaDialog()->onLuaSaveState((QString)filename);
return 0;
}
AddLuaFunction(Lua_StateSave,StateSave);
int Lua_StateLoad(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
const char* filename = luaL_checkstring(L,1);
bundle->getluaDialog()->onLuaLoadState((QString)filename);
return 0;
}
AddLuaFunction(Lua_StateLoad,StateLoad);
int Lua_getMouse(lua_State* L)
{
Qt::MouseButtons btns = QGuiApplication::mouseButtons();
LuaBundle* bundle = get_bundle(L);
QPoint pos = bundle->getEmuInstance()->getMainWindow()->panel->mapFromGlobal(QCursor::pos(QGuiApplication::primaryScreen()));
const char* keys[6] = {"Left","Middle","Right","XButton1","XButton2","Wheel"};
bool vals[6] =
{
btns.testFlag(Qt::LeftButton),
btns.testFlag(Qt::MiddleButton),
btns.testFlag(Qt::RightButton),
btns.testFlag(Qt::XButton1),
btns.testFlag(Qt::XButton2),
false //TODO: add mouse wheel support
};
lua_createtable(L, 0, 8);
lua_pushinteger(L, pos.x());
lua_setfield(L, -2, "X");
lua_pushinteger(L, pos.y());
lua_setfield(L, -2, "Y");
for (int i=0;i<6;i++)
{
lua_pushboolean(L,vals[i]);
lua_setfield(L,-2,keys[i]);
}
return 1;//returns table describing the current pos and state of the mouse
}
AddLuaFunction(Lua_getMouse,GetMouse);
int Lua_HeldKeys(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
lua_createtable(L,0,bundle->getEmuInstance()->heldKeys.size());
for (int key : bundle->getEmuInstance()->heldKeys)
{
lua_pushboolean(L,true);
lua_seti(L,-2,key);
}
return 1;//returns table of currently held keys.
}
AddLuaFunction(Lua_HeldKeys,HeldKeys);
/*--------------------------------------------------------------------------------------------------
Front-end lua function definitions
--------------------------------------------------------------------------------------------------*/
//TODO: Lua Colors
//MakeCanvas(int x,int y,int width,int height,[int target,topScreen=0,bottomScreen=1,OSD(default)>=2)],[bool active = true])
int Lua_MakeCanvas(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
int x = luaL_checknumber(L,1);
int y = luaL_checknumber(L,2);
int w = luaL_checknumber(L,3);
int h = luaL_checknumber(L,4);
int t = luaL_optinteger(L,5,2);
bool a = 0 != luaL_optinteger(L,6,1);
OverlayCanvas canvas(x,y,w,h,(LuaCanvasTarget)t);
canvas.isActive = a;
lua_pushinteger(L,bundle->overlays->size());
bundle->overlays->push_back(canvas);
return 1; //returns index of the new overlay
}
AddLuaFunction(Lua_MakeCanvas,MakeCanvas);
int Lua_SetCanvas(lua_State* L) //SetCanvas(int index)
{
LuaBundle* bundle = get_bundle(L);
int index = luaL_checknumber(L,1);
bundle->luaCanvas = &bundle->overlays->at(index);
return 0;
}
AddLuaFunction(Lua_SetCanvas,SetCanvas);
int Lua_ClearOverlay(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
bundle->luaCanvas->imageBuffer->fill(0x00000000);
return 0;
}
AddLuaFunction(Lua_ClearOverlay,ClearOverlay);
int Lua_Flip(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
bundle->luaCanvas->flip();
return 0;
}
AddLuaFunction(Lua_Flip,Flip);
//Text(int x, int y, string message, [u32 color = 'black'], [int fontsize = 9], [string fontfamily = Helvetica])
int Lua_text(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
int x = luaL_checknumber(L,1);
int y = luaL_checknumber(L,2);
const char* message = luaL_checklstring(L,3,NULL);
melonDS::u32 color = luaL_optnumber(L,4,0x00000000);
const char* FontFamily = luaL_optlstring(L,6,"Helvetica",NULL);
int size = luaL_optnumber(L,5,9);
QPainter painter(bundle->luaCanvas->imageBuffer);
QFont font(FontFamily,size,0,false);
//font.setStyleStrategy(QFont::NoAntialias);
//font.setLetterSpacing(QFont::AbsoluteSpacing,-1);
painter.setFont(font);
painter.setPen(color);
painter.drawText(x,y,message);
return 0;
}
AddLuaFunction(Lua_text,Text);
int Lua_line(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
int x1 = luaL_checknumber(L,1);
int y1 = luaL_checknumber(L,2);
int x2 = luaL_checknumber(L,3);
int y2 = luaL_checknumber(L,4);
melonDS::u32 color = luaL_checknumber(L,5);
QPainter painter(bundle->luaCanvas->imageBuffer);
painter.setPen(color);
painter.drawLine(x1,y1,x2,y2);
return 0;
}
AddLuaFunction(Lua_line,Line);
int Lua_rect(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
melonDS::u32 color = luaL_checknumber(L,5);
int x = luaL_checknumber(L,1);
int y = luaL_checknumber(L,2);
int width = luaL_checknumber(L,3);
int height = luaL_checknumber(L,4);
QPainter painter(bundle->luaCanvas->imageBuffer);
painter.setPen(color);
painter.drawRect(x,y,width,height);
return 0;
}
AddLuaFunction(Lua_rect,Rect);
int Lua_fillrect(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
melonDS::u32 color = luaL_checknumber(L,5);
int x = luaL_checknumber(L,1);
int y = luaL_checknumber(L,2);
int width = luaL_checknumber(L,3);
int height = luaL_checknumber(L,4);
QPainter painter(bundle->luaCanvas->imageBuffer);
painter.setPen(color);
painter.fillRect(x,y,width,height,color);
return 0;
}
AddLuaFunction(Lua_fillrect,FillRect);
int Lua_Ellipse(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
melonDS::u32 color = luaL_checknumber(L,5);
int x = luaL_checknumber(L,1);
int y = luaL_checknumber(L,2);
int width = luaL_checknumber(L,3);
int height = luaL_checknumber(L,4);
QPainter painter(bundle->luaCanvas->imageBuffer);
painter.setPen(color);
painter.drawEllipse(x,y,width,height);
return 0;
}
AddLuaFunction(Lua_Ellipse,Ellipse);
int Lua_keystrokes(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
lua_createtable(L,0,bundle->getEmuInstance()->keyStrokes.size());
for (int i = 0; i<bundle->getEmuInstance()->keyStrokes.size(); i++)
{
lua_pushinteger(L,bundle->getEmuInstance()->keyStrokes.at(i));
lua_seti(L,-2,i);
}
bundle->getEmuInstance()->keyStrokes.clear();
return 1;
}
AddLuaFunction(Lua_keystrokes,Keys);
//DrawImage(string path, int x, int y,[int source x=0], [int source y=0], [int source w=-1],[int source h=-1])
int Lua_drawImage(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
const char* path = luaL_checklstring(L,1,NULL);
int x = luaL_checkinteger(L,2);
int y = luaL_checkinteger(L,3);
int sx = luaL_optinteger(L,4,0);
int sy = luaL_optinteger(L,5,0);
int sw = luaL_optinteger(L,6,-1);
int sh = luaL_optinteger(L,7,-1);
QPainter painter(bundle->luaCanvas->imageBuffer);
QImage image;
if (bundle->imageHash->contains(path))
{
image=(*bundle->imageHash)[path];
}
else
{
image=QImage((QString)path);
(*bundle->imageHash)[path] = image;
}
painter.drawImage(x,y,image,sx,sy,sw,sh);
return 0;
}
AddLuaFunction(Lua_drawImage,DrawImage);
int Lua_clearImageHash(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
bundle->imageHash->clear();
return 0;
}
AddLuaFunction(Lua_clearImageHash,ClearHash);
int Lua_getJoy(lua_State* L)
{
LuaBundle* bundle = get_bundle(L);
melonDS::u32 buttonMask=bundle->getEmuInstance()->getInputMask();//current button state.
const char* keys[12] =
{//Buttons in order of mask.
"A","B","Select","Start",
"Right","Left","Up","Down",
"R","L","X","Y"
};
lua_createtable(L, 0, 12);
for (melonDS::u32 i=0;i<12;i++)
{
lua_pushboolean(L,0 >= (buttonMask&(1<<i)));
lua_setfield(L,-2,keys[i]);
}
return 1;
}
AddLuaFunction(Lua_getJoy,GetJoy);
}