Fix Lua string encoding bug (resolves #190)

This commit is contained in:
YoshiRulz 2022-05-24 03:36:05 +10:00 committed by James Groom
parent 9bdf043f36
commit 09061843f4
27 changed files with 593 additions and 364 deletions

View File

@ -86,6 +86,7 @@ namespace BizHawk.Client.Common
=> APIs.EmuClient.FrameSkip(numFrames);
[LuaMethod("get_lua_engine", "returns the name of the Lua engine currently in use")]
[return: LuaASCIIStringParam]
public string GetLuaEngine()
=> _luaLibsImpl.EngineName;
@ -194,10 +195,10 @@ namespace BizHawk.Client.Common
[LuaMethodExample("client.openrom( \"C:\\\" );")]
[LuaMethod("openrom", "opens the Open ROM dialog")]
public void OpenRom(string path)
public void OpenRom([LuaArbitraryStringParam] string path)
{
_luaLibsImpl.IsRebootingCore = true;
APIs.EmuClient.OpenRom(path);
APIs.EmuClient.OpenRom(FixString(path));
_luaLibsImpl.IsRebootingCore = false;
}
@ -242,8 +243,8 @@ namespace BizHawk.Client.Common
[LuaMethodExample("client.screenshot( \"C:\\\" );")]
[LuaMethod("screenshot", "if a parameter is passed it will function as the Screenshot As menu item of EmuHawk, else it will function as the Screenshot menu item")]
public void Screenshot(string path = null)
=> APIs.EmuClient.Screenshot(path);
public void Screenshot([LuaArbitraryStringParam] string path = null)
=> APIs.EmuClient.Screenshot(FixString(path));
[LuaMethodExample("client.screenshottoclipboard( );")]
[LuaMethod("screenshottoclipboard", "Performs the same function as EmuHawk's Screenshot To Clipboard menu item")]
@ -277,6 +278,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local curSpeed = client.getconfig().SpeedPercent")]
[LuaMethod("getconfig", "gets the current config settings object")]
[return: LuaArbitraryStringParam] // too hard to do properly
public object GetConfig()
=> ((EmulationApi) APIs.Emulation).ForbiddenConfigReference;
@ -317,6 +319,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local incbhver = client.getversion( );")]
[LuaMethod("getversion", "Returns the current stable BizHawk version")]
[return: LuaASCIIStringParam]
public static string GetVersion()
{
return VersionInfo.MainVersion;
@ -324,12 +327,14 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local nlcliget = client.getavailabletools( );")]
[LuaMethod("getavailabletools", "Returns a list of the tools currently open")]
[return: LuaASCIIStringParam]
public LuaTable GetAvailableTools()
=> _th.EnumerateToLuaTable(APIs.Tool.AvailableTools.Select(tool => tool.Name.ToLower()), indexFrom: 0);
[LuaMethodExample("local nlcliget = client.gettool( \"Tool name\" );")]
[LuaMethod("gettool", "Returns an object that represents a tool of the given name (not case sensitive). If the tool is not open, it will be loaded if available. Use gettools to get a list of names")]
public LuaTable GetTool(string name)
[return: LuaArbitraryStringParam] // too hard to do properly
public LuaTable GetTool([LuaASCIIStringParam] string name)
{
var selectedTool = APIs.Tool.GetTool(name);
return selectedTool == null ? null : _th.ObjectToTable(selectedTool);
@ -337,7 +342,8 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local nlclicre = client.createinstance( \"objectname\" );")]
[LuaMethod("createinstance", "returns a default instance of the given type of object if it exists (not case sensitive). Note: This will only work on objects which have a parameterless constructor. If no suitable type is found, or the type does not have a parameterless constructor, then nil is returned")]
public LuaTable CreateInstance(string name)
[return: LuaArbitraryStringParam] // too hard to do properly
public LuaTable CreateInstance([LuaASCIIStringParam] string name)
{
var instance = APIs.Tool.CreateInstance(name);
return instance == null ? null : _th.ObjectToTable(instance);
@ -380,7 +386,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("client.addcheat(\"NNNPAK\");")]
[LuaMethod("addcheat", "adds a cheat code, if supported")]
public void AddCheat(string code)
public void AddCheat([LuaASCIIStringParam] string code)
{
if (string.IsNullOrWhiteSpace(code))
{
@ -409,7 +415,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("client.removecheat(\"NNNPAK\");")]
[LuaMethod("removecheat", "removes a cheat, if it already exists")]
public void RemoveCheat(string code)
public void RemoveCheat([LuaASCIIStringParam] string code)
{
if (string.IsNullOrWhiteSpace(code))
{

View File

@ -20,6 +20,7 @@ namespace BizHawk.Client.Common
//TO DO: not fully working yet!
[LuaMethod("getluafunctionslist", "returns a list of implemented functions")]
[return: LuaArbitraryStringParam]
public static string GetLuaFunctionsList()
{
var list = new StringBuilder();
@ -27,7 +28,7 @@ namespace BizHawk.Client.Common
{
list.AppendLine(function.ToString());
}
return list.ToString();
return UnFixString(list.ToString());
}
[LuaMethod("socketServerIsConnected", "socketServerIsConnected")]
@ -35,27 +36,29 @@ namespace BizHawk.Client.Common
=> APIs.Comm.Sockets.Connected;
[LuaMethod("socketServerScreenShot", "sends a screenshot to the Socket server")]
[return: LuaArbitraryStringParam]
public string SocketServerScreenShot()
{
CheckSocketServer();
return APIs.Comm.Sockets?.SendScreenshot();
return UnFixString(APIs.Comm.Sockets?.SendScreenshot());
}
[LuaMethod("socketServerScreenShotResponse", "sends a screenshot to the Socket server and retrieves the response")]
[return: LuaArbitraryStringParam]
public string SocketServerScreenShotResponse()
{
CheckSocketServer();
return APIs.Comm.Sockets?.SendScreenshot(1000);
return UnFixString(APIs.Comm.Sockets?.SendScreenshot(1000));
}
[LuaMethod("socketServerSend", "sends a string to the Socket server")]
public int SocketServerSend(string SendString)
public int SocketServerSend([LuaArbitraryStringParam] string SendString)
{
if (!CheckSocketServer())
{
return -1;
}
return APIs.Comm.Sockets.SendString(SendString);
return APIs.Comm.Sockets.SendString(FixString(SendString));
}
[LuaMethod("socketServerSendBytes", "sends bytes to the Socket server")]
@ -66,10 +69,11 @@ namespace BizHawk.Client.Common
}
[LuaMethod("socketServerResponse", "receives a message from the Socket server")]
[return: LuaArbitraryStringParam]
public string SocketServerResponse()
{
CheckSocketServer();
return APIs.Comm.Sockets?.ReceiveString();
return UnFixString(APIs.Comm.Sockets?.ReceiveString());
}
[LuaMethod("socketServerSuccessful", "returns the status of the last Socket server action")]
@ -86,7 +90,7 @@ namespace BizHawk.Client.Common
}
[LuaMethod("socketServerSetIp", "sets the IP address of the Lua socket server")]
public void SocketServerSetIp(string ip)
public void SocketServerSetIp([LuaASCIIStringParam] string ip)
{
CheckSocketServer();
APIs.Comm.Sockets.IP = ip;
@ -100,6 +104,7 @@ namespace BizHawk.Client.Common
}
[LuaMethod("socketServerGetIp", "returns the IP address of the Lua socket server")]
[return: LuaASCIIStringParam]
public string SocketServerGetIp()
{
return APIs.Comm.Sockets?.IP;
@ -112,6 +117,7 @@ namespace BizHawk.Client.Common
}
[LuaMethod("socketServerGetInfo", "returns the IP and port of the Lua socket server")]
[return: LuaASCIIStringParam]
public string SocketServerGetInfo()
{
return CheckSocketServer()
@ -132,15 +138,14 @@ namespace BizHawk.Client.Common
// All MemoryMappedFile related methods
[LuaMethod("mmfSetFilename", "Sets the filename for the screenshots")]
public void MmfSetFilename(string filename)
{
APIs.Comm.MMF.Filename = filename;
}
public void MmfSetFilename([LuaArbitraryStringParam] string filename)
=> APIs.Comm.MMF.Filename = FixString(filename);
[LuaMethod("mmfGetFilename", "Gets the filename for the screenshots")]
[return: LuaArbitraryStringParam]
public string MmfGetFilename()
{
return APIs.Comm.MMF.Filename;
return UnFixString(APIs.Comm.MMF.Filename);
}
[LuaMethod("mmfScreenshot", "Saves screenshot to memory mapped file")]
@ -150,73 +155,77 @@ namespace BizHawk.Client.Common
}
[LuaMethod("mmfWrite", "Writes a string to a memory mapped file")]
public int MmfWrite(string mmf_filename, string outputString)
{
return APIs.Comm.MMF.WriteToFile(mmf_filename, outputString);
}
public int MmfWrite([LuaArbitraryStringParam] string mmf_filename, [LuaArbitraryStringParam] string outputString)
=> APIs.Comm.MMF.WriteToFile(FixString(mmf_filename), FixString(outputString));
[LuaMethod("mmfWriteBytes", "Write bytes to a memory mapped file")]
public int MmfWriteBytes(string mmf_filename, LuaTable byteArray)
{
return APIs.Comm.MMF.WriteToFile(mmf_filename, _th.EnumerateValues<double>(byteArray).Select(d => (byte)d).ToArray());
}
public int MmfWriteBytes([LuaArbitraryStringParam] string mmf_filename, LuaTable byteArray)
=> APIs.Comm.MMF.WriteToFile(FixString(mmf_filename), _th.EnumerateValues<double>(byteArray).Select(d => (byte)d).ToArray());
[LuaMethod("mmfCopyFromMemory", "Copy a section of the memory to a memory mapped file")]
public int MmfCopyFromMemory(string mmf_filename, long addr, int length, string domain)
{
return APIs.Comm.MMF.WriteToFile(mmf_filename, APIs.Memory.ReadByteRange(addr, length, domain).ToArray());
}
public int MmfCopyFromMemory(
[LuaArbitraryStringParam] string mmf_filename,
long addr,
int length,
[LuaASCIIStringParam] string domain)
=> APIs.Comm.MMF.WriteToFile(FixString(mmf_filename), APIs.Memory.ReadByteRange(addr, length, domain).ToArray());
[LuaMethod("mmfCopyToMemory", "Copy a memory mapped file to a section of the memory")]
public void MmfCopyToMemory(string mmf_filename, long addr, int length, string domain)
{
APIs.Memory.WriteByteRange(addr, new List<byte>(APIs.Comm.MMF.ReadBytesFromFile(mmf_filename, length)), domain);
}
public void MmfCopyToMemory(
[LuaArbitraryStringParam] string mmf_filename,
long addr,
int length,
[LuaASCIIStringParam] string domain)
=> APIs.Memory.WriteByteRange(addr, new List<byte>(APIs.Comm.MMF.ReadBytesFromFile(FixString(mmf_filename), length)), domain);
[LuaMethod("mmfRead", "Reads a string from a memory mapped file")]
public string MmfRead(string mmf_filename, int expectedSize)
{
return APIs.Comm.MMF.ReadFromFile(mmf_filename, expectedSize);
}
[return: LuaArbitraryStringParam]
public string MmfRead([LuaArbitraryStringParam] string mmf_filename, int expectedSize)
=> UnFixString(APIs.Comm.MMF.ReadFromFile(FixString(mmf_filename), expectedSize));
[LuaMethod("mmfReadBytes", "Reads bytes from a memory mapped file")]
public LuaTable MmfReadBytes(string mmf_filename, int expectedSize)
=> _th.ListToTable(APIs.Comm.MMF.ReadBytesFromFile(mmf_filename, expectedSize), indexFrom: 0);
public LuaTable MmfReadBytes([LuaArbitraryStringParam] string mmf_filename, int expectedSize)
=> _th.ListToTable(APIs.Comm.MMF.ReadBytesFromFile(FixString(mmf_filename), expectedSize), indexFrom: 0);
// All HTTP related methods
[LuaMethod("httpTest", "tests HTTP connections")]
[return: LuaArbitraryStringParam]
public string HttpTest()
{
if (APIs.Comm.HTTP == null) throw new NullReferenceException(); // to match previous behaviour
return APIs.Comm.HttpTest();
return UnFixString(APIs.Comm.HttpTest());
}
[LuaMethod("httpTestGet", "tests the HTTP GET connection")]
[return: LuaArbitraryStringParam]
public string HttpTestGet()
{
CheckHttp();
return APIs.Comm.HttpTestGet();
return UnFixString(APIs.Comm.HttpTestGet());
}
[LuaMethod("httpGet", "makes a HTTP GET request")]
public string HttpGet(string url)
[return: LuaArbitraryStringParam]
public string HttpGet([LuaArbitraryStringParam] string url)
{
CheckHttp();
return APIs.Comm.HTTP?.ExecGet(url);
return UnFixString(APIs.Comm.HTTP?.ExecGet(FixString(url)));
}
[LuaMethod("httpPost", "makes a HTTP POST request")]
public string HttpPost(string url, string payload)
[return: LuaArbitraryStringParam]
public string HttpPost([LuaArbitraryStringParam] string url, [LuaArbitraryStringParam] string payload)
{
CheckHttp();
return APIs.Comm.HTTP?.ExecPost(url, payload);
return UnFixString(APIs.Comm.HTTP?.ExecPost(FixString(url), FixString(payload)));
}
[LuaMethod("httpPostScreenshot", "HTTP POST screenshot")]
[return: LuaArbitraryStringParam]
public string HttpPostScreenshot()
{
CheckHttp();
return APIs.Comm.HTTP?.SendScreenshot();
return UnFixString(APIs.Comm.HTTP?.SendScreenshot());
}
[LuaMethod("httpSetTimeout", "Sets HTTP timeout in milliseconds")]
@ -227,31 +236,33 @@ namespace BizHawk.Client.Common
}
[LuaMethod("httpSetPostUrl", "Sets HTTP POST URL")]
public void HttpSetPostUrl(string url)
public void HttpSetPostUrl([LuaArbitraryStringParam] string url)
{
CheckHttp();
APIs.Comm.HTTP.PostUrl = url;
APIs.Comm.HTTP.PostUrl = FixString(url);
}
[LuaMethod("httpSetGetUrl", "Sets HTTP GET URL")]
public void HttpSetGetUrl(string url)
public void HttpSetGetUrl([LuaArbitraryStringParam] string url)
{
CheckHttp();
APIs.Comm.HTTP.GetUrl = url;
APIs.Comm.HTTP.GetUrl = FixString(url);
}
[LuaMethod("httpGetPostUrl", "Gets HTTP POST URL")]
[return: LuaArbitraryStringParam]
public string HttpGetPostUrl()
{
CheckHttp();
return APIs.Comm.HTTP?.PostUrl;
return UnFixString(APIs.Comm.HTTP?.PostUrl);
}
[LuaMethod("httpGetGetUrl", "Gets HTTP GET URL")]
[return: LuaArbitraryStringParam]
public string HttpGetGetUrl()
{
CheckHttp();
return APIs.Comm.HTTP?.GetUrl;
return UnFixString(APIs.Comm.HTTP?.GetUrl);
}
private void CheckHttp()
@ -265,7 +276,8 @@ namespace BizHawk.Client.Common
#if ENABLE_WEBSOCKETS
[LuaMethod("ws_open", "Opens a websocket and returns the id so that it can be retrieved later.")]
[LuaMethodExample("local ws_id = comm.ws_open(\"wss://echo.websocket.org\");")]
public string WebSocketOpen(string uri)
[return: LuaASCIIStringParam]
public string WebSocketOpen([LuaArbitraryStringParam] string uri)
{
var wsServer = APIs.Comm.WebSockets;
if (wsServer == null)
@ -274,36 +286,43 @@ namespace BizHawk.Client.Common
return null;
}
var guid = new Guid();
_websockets[guid] = wsServer.Open(new Uri(uri));
_websockets[guid] = wsServer.Open(new Uri(FixString(uri)));
return guid.ToString();
}
[LuaMethod("ws_send", "Send a message to a certain websocket id (boolean flag endOfMessage)")]
[LuaMethodExample("local ws = comm.ws_send(ws_id, \"some message\", true);")]
public void WebSocketSend(string guid, string content, bool endOfMessage)
public void WebSocketSend(
[LuaASCIIStringParam] string guid,
[LuaArbitraryStringParam] string content,
bool endOfMessage)
{
if (_websockets.TryGetValue(Guid.Parse(guid), out var wrapper)) wrapper.Send(content, endOfMessage);
if (_websockets.TryGetValue(Guid.Parse(guid), out var wrapper)) wrapper.Send(FixString(content), endOfMessage);
}
[LuaMethod("ws_receive", "Receive a message from a certain websocket id and a maximum number of bytes to read")]
[LuaMethodExample("local ws = comm.ws_receive(ws_id, str_len);")]
public string WebSocketReceive(string guid, int bufferCap)
[return: LuaArbitraryStringParam]
public string WebSocketReceive([LuaASCIIStringParam] string guid, int bufferCap)
=> _websockets.TryGetValue(Guid.Parse(guid), out var wrapper)
? wrapper.Receive(bufferCap)
? UnFixString(wrapper.Receive(bufferCap))
: null;
[LuaMethod("ws_get_status", "Get a websocket's status")]
[LuaMethodExample("local ws_status = comm.ws_get_status(ws_id);")]
public int? WebSocketGetStatus(string guid)
public int? WebSocketGetStatus([LuaASCIIStringParam] string guid)
=> _websockets.TryGetValue(Guid.Parse(guid), out var wrapper)
? (int) wrapper.State
: (int?) null;
[LuaMethod("ws_close", "Close a websocket connection with a close status")]
[LuaMethodExample("local ws_status = comm.ws_close(ws_id, close_status);")]
public void WebSocketClose(string guid, WebSocketCloseStatus status, string closeMessage)
public void WebSocketClose(
[LuaASCIIStringParam] string guid,
WebSocketCloseStatus status,
[LuaArbitraryStringParam] string closeMessage)
{
if (_websockets.TryGetValue(Guid.Parse(guid), out var wrapper)) wrapper.Close(status, closeMessage);
if (_websockets.TryGetValue(Guid.Parse(guid), out var wrapper)) wrapper.Close(status, FixString(closeMessage));
}
#endif
}

View File

@ -37,23 +37,25 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local obemudis = emu.disassemble( 0x8000 );")]
[LuaMethod("disassemble", "Returns the disassembly object (disasm string and length int) for the given PC address. Uses System Bus domain if no domain name provided")]
public object Disassemble(uint pc, string name = "")
[return: LuaASCIIStringParam]
public object Disassemble(uint pc, [LuaASCIIStringParam] string name = "")
=> APIs.Emulation.Disassemble(pc, name);
// TODO: what about 64 bit registers?
[LuaMethodExample("local inemuget = emu.getregister( emu.getregisters( )[ 0 ] );")]
[LuaMethod("getregister", "returns the value of a cpu register or flag specified by name. For a complete list of possible registers or flags for a given core, use getregisters")]
public int GetRegister(string name)
public int GetRegister([LuaASCIIStringParam] string name)
=> (int?) APIs.Emulation.GetRegister(name) ?? 0;
[LuaMethodExample("local nlemuget = emu.getregisters( );")]
[LuaMethod("getregisters", "returns the complete set of available flags and registers for a given core")]
[return: LuaASCIIStringParam]
public LuaTable GetRegisters()
=> _th.DictToTable(APIs.Emulation.GetRegisters());
[LuaMethodExample("emu.setregister( emu.getregisters( )[ 0 ], -1000 );")]
[LuaMethod("setregister", "sets the given register name to the given value")]
public void SetRegister(string register, int value)
public void SetRegister([LuaASCIIStringParam] string register, int value)
=> APIs.Emulation.SetRegister(register, value);
[LuaMethodExample("local inemutot = emu.totalexecutedcycles( );")]
@ -63,6 +65,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local stemuget = emu.getsystemid( );")]
[LuaMethod("getsystemid", "Returns the ID string of the current core loaded. Note: No ROM loaded will return the string NULL")]
[return: LuaASCIIStringParam]
public string GetSystemId()
=> APIs.Emulation.GetSystemId();
@ -110,16 +113,19 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local stemuget = emu.getdisplaytype();")]
[LuaMethod("getdisplaytype", "returns the display type (PAL vs NTSC) that the emulator is currently running in")]
[return: LuaEnumStringParam]
public string GetDisplayType()
=> APIs.Emulation.GetDisplayType();
[LuaMethodExample("local stemuget = emu.getboardname();")]
[LuaMethod("getboardname", "returns (if available) the board name of the loaded ROM")]
[return: LuaASCIIStringParam]
public string GetBoardName()
=> APIs.Emulation.GetBoardName();
[LuaDeprecatedMethod]
[LuaMethod("getluacore", "returns the name of the Lua core currently in use")]
[return: LuaEnumStringParam]
public string GetLuaBackend()
{
Log("Deprecated function emu.getluacore() used, replace the call with client.get_lua_engine().");

View File

@ -17,11 +17,13 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local stgamget = gameinfo.getromname( );")]
[LuaMethod("getromname", "returns the name of the currently loaded rom, if a rom is loaded")]
[return: LuaArbitraryStringParam]
public string GetRomName()
=> APIs.Emulation.GetGameInfo()?.Name ?? string.Empty;
=> UnFixString(APIs.Emulation.GetGameInfo()?.Name) ?? string.Empty;
[LuaMethodExample("local stgamget = gameinfo.getromhash( );")]
[LuaMethod("getromhash", "returns the hash of the currently loaded rom, if a rom is loaded")]
[return: LuaASCIIStringParam]
public string GetRomHash()
=> APIs.Emulation.GetGameInfo()?.Hash ?? string.Empty;
@ -32,6 +34,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local stgamget = gameinfo.getstatus( );")]
[LuaMethod("getstatus", "returns the game database status of the currently loaded rom. Statuses are for example: GoodDump, BadDump, Hack, Unknown, NotInDatabase")]
[return: LuaEnumStringParam]
public string GetStatus()
=> (APIs.Emulation.GetGameInfo()?.Status)?.ToString();
@ -42,11 +45,13 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local stgamget = gameinfo.getboardtype( );")]
[LuaMethod("getboardtype", "returns identifying information about the 'mapper' or similar capability used for this game. empty if no such useful distinction can be drawn")]
[return: LuaArbitraryStringParam]
public string GetBoardType()
=> APIs.Emulation.GetBoardName();
=> UnFixString(APIs.Emulation.GetBoardName());
[LuaMethodExample("local nlgamget = gameinfo.getoptions( );")]
[LuaMethod("getoptions", "returns the game options for the currently loaded rom. Options vary per platform")]
[return: LuaASCIIStringParam] // these had better be just flags and not anything localised --yoshi
public LuaTable GetOptions()
=> _th.DictToTable(APIs.Emulation.GetGameOptions());
}

View File

@ -23,7 +23,7 @@ namespace BizHawk.Client.Common
#pragma warning disable CS0612
[LuaDeprecatedMethod]
[LuaMethod("DrawNew", "Changes drawing target to the specified lua surface name. This may clobber any previous drawing to this surface (pass false if you don't want it to)")]
public void DrawNew(string name, bool? clear = true)
public void DrawNew([LuaEnumStringParam] string name, bool? clear = true)
=> APIs.Gui.DrawNew(name, clear ?? true);
[LuaDeprecatedMethod]
@ -34,12 +34,12 @@ namespace BizHawk.Client.Common
[LuaMethodExample("gui.addmessage( \"Some message\" );")]
[LuaMethod("addmessage", "Adds a message to the OSD's message area")]
public void AddMessage(string message)
=> APIs.Gui.AddMessage(message);
public void AddMessage([LuaArbitraryStringParam] string message)
=> APIs.Gui.AddMessage(FixString(message));
[LuaMethodExample("gui.clearGraphics( );")]
[LuaMethod("clearGraphics", "clears all lua drawn graphics from the screen")]
public void ClearGraphics(string surfaceName = null)
public void ClearGraphics([LuaEnumStringParam] string surfaceName = null)
=> APIs.Gui.ClearGraphics(surfaceID: UseOrFallback(surfaceName));
[LuaMethodExample("gui.cleartext( );")]
@ -64,12 +64,15 @@ namespace BizHawk.Client.Common
[LuaMethodExample("gui.defaultPixelFont( \"Arial Narrow\");")]
[LuaMethod("defaultPixelFont", "Sets the default font to use in gui.pixelText(). Two font families are available, \"fceux\" and \"gens\" (or \"0\" and \"1\" respectively), \"gens\" is used by default")]
public void SetDefaultPixelFont(string fontfamily)
public void SetDefaultPixelFont([LuaASCIIStringParam] string fontfamily)
=> APIs.Gui.SetDefaultPixelFont(fontfamily);
[LuaMethodExample("gui.drawBezier( { { 5, 10 }, { 10, 10 }, { 10, 20 }, { 5, 20 } }, 0x000000FF );")]
[LuaMethod("drawBezier", "Draws a Bezier curve using the table of coordinates provided in the given color")]
public void DrawBezier(LuaTable points, [LuaColorParam] object color, string surfaceName = null)
public void DrawBezier(
LuaTable points,
[LuaColorParam] object color,
[LuaEnumStringParam] string surfaceName = null)
{
try
{
@ -102,7 +105,7 @@ namespace BizHawk.Client.Common
int y2,
[LuaColorParam] object line = null,
[LuaColorParam] object background = null,
string surfaceName = null)
[LuaEnumStringParam] string surfaceName = null)
=> APIs.Gui.DrawBox(x, y, x2, y2, _th.SafeParseColor(line), _th.SafeParseColor(background), surfaceID: UseOrFallback(surfaceName));
[LuaMethodExample("gui.drawEllipse( 16, 32, 77, 99, 0x007F00FF, 0x7F7F7FFF );")]
@ -114,25 +117,31 @@ namespace BizHawk.Client.Common
int height,
[LuaColorParam] object line = null,
[LuaColorParam] object background = null,
string surfaceName = null)
[LuaEnumStringParam] string surfaceName = null)
=> APIs.Gui.DrawEllipse(x, y, width, height, _th.SafeParseColor(line), _th.SafeParseColor(background), surfaceID: UseOrFallback(surfaceName));
[LuaMethodExample("gui.drawIcon( \"C:\\sample.ico\", 16, 32, 18, 24 );")]
[LuaMethod("drawIcon", "draws an Icon (.ico) file from the given path at the given coordinate. width and height are optional. If specified, it will resize the image accordingly")]
public void DrawIcon(string path, int x, int y, int? width = null, int? height = null, string surfaceName = null)
=> APIs.Gui.DrawIcon(path, x, y, width, height, surfaceID: UseOrFallback(surfaceName));
public void DrawIcon(
[LuaArbitraryStringParam] string path,
int x,
int y,
int? width = null,
int? height = null,
[LuaEnumStringParam] string surfaceName = null)
=> APIs.Gui.DrawIcon(FixString(path), x, y, width, height, surfaceID: UseOrFallback(surfaceName));
[LuaMethodExample("gui.drawImage( \"C:\\sample.bmp\", 16, 32, 18, 24, false );")]
[LuaMethod("drawImage", "draws an image file from the given path at the given coordinate. width and height are optional. If specified, it will resize the image accordingly")]
public void DrawImage(
string path,
[LuaArbitraryStringParam] string path,
int x,
int y,
int? width = null,
int? height = null,
bool cache = true,
string surfaceName = null)
=> APIs.Gui.DrawImage(path, x, y, width, height, cache, surfaceID: UseOrFallback(surfaceName));
[LuaEnumStringParam] string surfaceName = null)
=> APIs.Gui.DrawImage(FixString(path), x, y, width, height, cache, surfaceID: UseOrFallback(surfaceName));
[LuaMethodExample("gui.clearImageCache( );")]
[LuaMethod("clearImageCache", "clears the image cache that is built up by using gui.drawImage, also releases the file handle for cached images")]
@ -142,7 +151,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("gui.drawImageRegion( \"C:\\sample.png\", 11, 22, 33, 44, 21, 43, 34, 45 );")]
[LuaMethod("drawImageRegion", "draws a given region of an image file from the given path at the given coordinate, and optionally with the given size")]
public void DrawImageRegion(
string path,
[LuaArbitraryStringParam] string path,
int source_x,
int source_y,
int source_width,
@ -151,8 +160,8 @@ namespace BizHawk.Client.Common
int dest_y,
int? dest_width = null,
int? dest_height = null,
string surfaceName = null)
=> APIs.Gui.DrawImageRegion(path, source_x, source_y, source_width, source_height, dest_x, dest_y, dest_width, dest_height, surfaceID: UseOrFallback(surfaceName));
[LuaEnumStringParam] string surfaceName = null)
=> APIs.Gui.DrawImageRegion(FixString(path), source_x, source_y, source_width, source_height, dest_x, dest_y, dest_width, dest_height, surfaceID: UseOrFallback(surfaceName));
[LuaMethodExample("gui.drawLine( 161, 321, 162, 322, 0xFFFFFFFF );")]
[LuaMethod("drawLine", "Draws a line from the first coordinate pair to the 2nd. Color is optional (if not specified it will be drawn black)")]
@ -162,13 +171,18 @@ namespace BizHawk.Client.Common
int x2,
int y2,
[LuaColorParam] object color = null,
string surfaceName = null)
[LuaEnumStringParam] string surfaceName = null)
=> APIs.Gui.DrawLine(x1, y1, x2, y2, _th.SafeParseColor(color), surfaceID: UseOrFallback(surfaceName));
[LuaMethodExample("gui.drawAxis( 16, 32, 15, 0xFFFFFFFF );")]
[LuaMethod("drawAxis", "Draws an axis of the specified size at the coordinate pair.)")]
public void DrawAxis(int x, int y, int size, [LuaColorParam] object color = null, string surfaceName = null)
=> APIs.Gui.DrawAxis(x, y, size, _th.SafeParseColor(color), surfaceID: UseOrFallback(surfaceName));
public void DrawAxis(
int x,
int y,
int size,
[LuaColorParam] object color = null,
[LuaEnumStringParam] string surfaceName = null)
=> APIs.Gui.DrawAxis(x, y, size, _th.SafeParseColor(color), surfaceID: UseOrFallback(surfaceName));
[LuaMethodExample("gui.drawPie( 16, 32, 77, 99, 180, 90, 0x007F00FF, 0x7F7F7FFF );")]
[LuaMethod("drawPie", "draws a Pie shape at the given coordinates and the given width and height")]
@ -181,13 +195,17 @@ namespace BizHawk.Client.Common
int sweepangle,
[LuaColorParam] object line = null,
[LuaColorParam] object background = null,
string surfaceName = null)
[LuaEnumStringParam] string surfaceName = null)
=> APIs.Gui.DrawPie(x, y, width, height, startangle, sweepangle, _th.SafeParseColor(line), _th.SafeParseColor(background), surfaceID: UseOrFallback(surfaceName));
[LuaMethodExample("gui.drawPixel( 16, 32, 0xFFFFFFFF );")]
[LuaMethod("drawPixel", "Draws a single pixel at the given coordinates in the given color. Color is optional (if not specified it will be drawn black)")]
public void DrawPixel(int x, int y, [LuaColorParam] object color = null, string surfaceName = null)
=> APIs.Gui.DrawPixel(x, y, _th.SafeParseColor(color), surfaceID: UseOrFallback(surfaceName));
public void DrawPixel(
int x,
int y,
[LuaColorParam] object color = null,
[LuaEnumStringParam] string surfaceName = null)
=> APIs.Gui.DrawPixel(x, y, _th.SafeParseColor(color), surfaceID: UseOrFallback(surfaceName));
[LuaMethodExample("gui.drawPolygon( { { 5, 10 }, { 10, 10 }, { 10, 20 }, { 5, 20 } }, 10, 30, 0x007F00FF, 0x7F7F7FFF );")]
[LuaMethod("drawPolygon", "Draws a polygon using the table of coordinates specified in points. This should be a table of tables(each of size 2). If x or y is passed, the polygon will be translated by the passed coordinate pair. Line is the color of the polygon. Background is the optional fill color")]
@ -197,7 +215,7 @@ namespace BizHawk.Client.Common
int? offsetY = null,
[LuaColorParam] object line = null,
[LuaColorParam] object background = null,
string surfaceName = null)
[LuaEnumStringParam] string surfaceName = null)
{
var pointsList = _th.EnumerateValues<LuaTable>(points)
.Select(table => _th.EnumerateValues<double>(table).ToList()).ToList();
@ -227,7 +245,7 @@ namespace BizHawk.Client.Common
int height,
[LuaColorParam] object line = null,
[LuaColorParam] object background = null,
string surfaceName = null)
[LuaEnumStringParam] string surfaceName = null)
=> APIs.Gui.DrawRectangle(x, y, width, height, _th.SafeParseColor(line), _th.SafeParseColor(background), surfaceID: UseOrFallback(surfaceName));
[LuaMethodExample("gui.drawString( 16, 32, \"Some message\", 0x7F0000FF, 0x00007FFF, 8, \"Arial Narrow\", \"bold\", \"center\", \"middle\" );")]
@ -235,15 +253,15 @@ namespace BizHawk.Client.Common
public void DrawString(
int x,
int y,
string message,
[LuaArbitraryStringParam] string message,
[LuaColorParam] object forecolor = null,
[LuaColorParam] object backcolor = null,
int? fontsize = null,
string fontfamily = null,
string fontstyle = null,
string horizalign = null,
string vertalign = null,
string surfaceName = null)
[LuaASCIIStringParam] string fontfamily = null,
[LuaEnumStringParam] string fontstyle = null,
[LuaEnumStringParam] string horizalign = null,
[LuaEnumStringParam] string vertalign = null,
[LuaEnumStringParam] string surfaceName = null)
{
DrawText(x, y, message, _th.SafeParseColor(forecolor), _th.SafeParseColor(backcolor), fontsize, fontfamily, fontstyle, horizalign, vertalign, surfaceName: surfaceName);
}
@ -253,42 +271,48 @@ namespace BizHawk.Client.Common
public void DrawText(
int x,
int y,
string message,
[LuaArbitraryStringParam] string message,
[LuaColorParam] object forecolor = null,
[LuaColorParam] object backcolor = null,
int? fontsize = null,
string fontfamily = null,
string fontstyle = null,
string horizalign = null,
string vertalign = null,
string surfaceName = null)
=> APIs.Gui.DrawString(x, y, message, _th.SafeParseColor(forecolor), _th.SafeParseColor(backcolor), fontsize, fontfamily, fontstyle, horizalign, vertalign, surfaceID: UseOrFallback(surfaceName));
[LuaASCIIStringParam] string fontfamily = null,
[LuaEnumStringParam] string fontstyle = null,
[LuaEnumStringParam] string horizalign = null,
[LuaEnumStringParam] string vertalign = null,
[LuaEnumStringParam] string surfaceName = null)
=> APIs.Gui.DrawString(x, y, FixString(message), _th.SafeParseColor(forecolor), _th.SafeParseColor(backcolor), fontsize, fontfamily, fontstyle, horizalign, vertalign, surfaceID: UseOrFallback(surfaceName));
[LuaMethodExample("gui.pixelText( 16, 32, \"Some message\", 0x7F0000FF, 0x00007FFF, \"Arial Narrow\" );")]
[LuaMethod("pixelText", "Draws the given message in the emulator screen space (like all draw functions) at the given x,y coordinates and the given color. The default color is white. Two font families are available, \"fceux\" and \"gens\" (or \"0\" and \"1\" respectively), both are monospace and have the same size as in the emulators they've been taken from. If no font family is specified, it uses \"gens\" font, unless that's overridden via gui.defaultPixelFont()")]
public void DrawText(
int x,
int y,
string message,
[LuaArbitraryStringParam] string message,
[LuaColorParam] object forecolor = null,
[LuaColorParam] object backcolor = null,
string fontfamily = null,
string surfaceName = null)
=> APIs.Gui.DrawText(x, y, message, _th.SafeParseColor(forecolor), _th.SafeParseColor(backcolor) ?? APIs.Gui.GetDefaultTextBackground().Value, fontfamily, surfaceID: UseOrFallback(surfaceName));
[LuaASCIIStringParam] string fontfamily = null,
[LuaEnumStringParam] string surfaceName = null)
=> APIs.Gui.DrawText(x, y, FixString(message), _th.SafeParseColor(forecolor), _th.SafeParseColor(backcolor) ?? APIs.Gui.GetDefaultTextBackground().Value, fontfamily, surfaceID: UseOrFallback(surfaceName));
[LuaMethodExample("gui.text( 16, 32, \"Some message\", 0x7F0000FF, \"bottomleft\" );")]
[LuaMethod("text", "Displays the given text on the screen at the given coordinates. Optional Foreground color. The optional anchor flag anchors the text to one of the four corners. Anchor flag parameters: topleft, topright, bottomleft, bottomright")]
public void Text(int x, int y, string message, [LuaColorParam] object forecolor = null, string anchor = null)
=> APIs.Gui.Text(x, y, message, _th.SafeParseColor(forecolor), anchor);
public void Text(
int x,
int y,
[LuaArbitraryStringParam] string message,
[LuaColorParam] object forecolor = null,
[LuaEnumStringParam] string anchor = null)
=> APIs.Gui.Text(x, y, FixString(message), _th.SafeParseColor(forecolor), anchor);
[LuaMethodExample("local nlguicre = gui.createcanvas( 77, 99, 2, 48 );")]
[LuaMethod("createcanvas", "Creates a canvas of the given size and, if specified, the given coordinates.")]
[return: LuaArbitraryStringParam] // too hard to do properly
public LuaTable Text(int width, int height, int? x = null, int? y = null)
=> CreateLuaCanvasCallback(width, height, x, y);
[LuaMethodExample("gui.use_surface( \"client\" );")]
[LuaMethod("use_surface", "Stores the name of a surface to draw on, so you don't need to pass it to every draw function. The default is \"emucore\", and the other valid value is \"client\".")]
public void UseSurface(string surfaceName)
public void UseSurface([LuaEnumStringParam] string surfaceName)
{
if (surfaceName == null)
{

View File

@ -14,12 +14,14 @@ namespace BizHawk.Client.Common
#pragma warning disable CS0612
[LuaMethodExample("local nlinpget = input.get( );")]
[LuaMethod("get", "Returns a lua table of all the buttons the user is currently pressing on their keyboard and gamepads\nAll buttons that are pressed have their key values set to true; all others remain nil.")]
[return: LuaASCIIStringParam]
public LuaTable Get()
=> _th.DictToTable(APIs.Input.Get());
#pragma warning restore CS0612
[LuaMethodExample("local nlinpget = input.getmouse( );")]
[LuaMethod("getmouse", "Returns a lua table of the mouse X/Y coordinates and button states. Table keys are X, Y, Left, Middle, Right, XButton1, XButton2, Wheel.")]
[return: LuaASCIIStringParam]
public LuaTable GetMouse()
=> _th.DictToTable(APIs.Input.GetMouse());
}

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NLua;
@ -15,44 +16,47 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local nljoyget = joypad.get( 1 );")]
[LuaMethod("get", "returns a lua table of the controller buttons pressed. If supplied, it will only return a table of buttons for the given controller")]
[return: LuaArbitraryStringParam]
public LuaTable Get(int? controller = null)
=> _th.DictToTable(APIs.Joypad.Get(controller));
=> _th.DictToTable(APIs.Joypad.Get(controller).ToDictionary(static kvp => UnFixString(kvp.Key), static kvp => kvp.Value));
[LuaMethodExample("local nljoyget = joypad.getwithmovie( 1 );")]
[LuaMethod("getwithmovie", "returns a lua table of the controller buttons pressed, including ones pressed by the current movie. If supplied, it will only return a table of buttons for the given controller")]
[return: LuaArbitraryStringParam]
public LuaTable GetWithMovie(int? controller = null)
=> _th.DictToTable(APIs.Joypad.GetWithMovie(controller));
=> _th.DictToTable(APIs.Joypad.GetWithMovie(controller).ToDictionary(static kvp => UnFixString(kvp.Key), static kvp => kvp.Value));
[LuaMethodExample("local nljoyget = joypad.getimmediate( );")]
[LuaMethod("getimmediate", "returns a lua table of any controller buttons currently pressed by the user")]
[return: LuaArbitraryStringParam]
public LuaTable GetImmediate(int? controller = null)
=> _th.DictToTable(APIs.Joypad.GetImmediate(controller));
=> _th.DictToTable(APIs.Joypad.GetImmediate(controller).ToDictionary(static kvp => UnFixString(kvp.Key), static kvp => kvp.Value));
[LuaMethodExample("joypad.setfrommnemonicstr( \"| 0, 0, 0, 100,...R..B....|\" );")]
[LuaMethod("setfrommnemonicstr", "sets the given buttons to their provided values for the current frame, string will be interpreted the same way an entry from a movie input log would be")]
public void SetFromMnemonicStr(string inputLogEntry)
public void SetFromMnemonicStr([LuaASCIIStringParam] string inputLogEntry)
=> APIs.Joypad.SetFromMnemonicStr(inputLogEntry);
[LuaMethodExample("joypad.set( { [\"Left\"] = true, [ \"A\" ] = true, [ \"B\" ] = true } );")]
[LuaMethod("set", "sets the given buttons to their provided values for the current frame")]
public void Set(LuaTable buttons, int? controller = null)
public void Set([LuaArbitraryStringParam] LuaTable buttons, int? controller = null)
{
var dict = new Dictionary<string, bool>();
foreach (var (k, v) in _th.EnumerateEntries<object, object>(buttons))
{
dict[k.ToString()] = Convert.ToBoolean(v); // Accepts 1/0 or true/false
dict[FixString(k.ToString())] = Convert.ToBoolean(v); // Accepts 1/0 or true/false
}
APIs.Joypad.Set(dict, controller);
}
[LuaMethodExample("joypad.setanalog( { [ \"Tilt X\" ] = -63, [ \"Tilt Y\" ] = 127 } );")]
[LuaMethod("setanalog", "sets the given analog controls to their provided values for the current frame. Note that unlike set() there is only the logic of overriding with the given value.")]
public void SetAnalog(LuaTable controls, int? controller = null)
public void SetAnalog([LuaArbitraryStringParam] LuaTable controls, int? controller = null)
{
var dict = new Dictionary<string, int?>();
foreach (var (k, v) in _th.EnumerateEntries<object, object>(controls))
{
dict[k.ToString()] = double.TryParse(v.ToString(), out var d) ? (int) d : (int?) null;
dict[FixString(k.ToString())] = double.TryParse(v.ToString(), out var d) ? (int) d : null;
}
APIs.Joypad.SetAnalog(dict, controller);
}

View File

@ -17,16 +17,18 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local nlmemget = memory.getmemorydomainlist();")]
[LuaMethod("getmemorydomainlist", "Returns a string of the memory domains for the loaded platform core. List will be a single string delimited by line feeds")]
[return: LuaASCIIStringParam]
public LuaTable GetMemoryDomainList()
=> _th.ListToTable(APIs.Memory.GetMemoryDomainList(), indexFrom: 0);
[LuaMethodExample("local uimemget = memory.getmemorydomainsize( mainmemory.getname( ) );")]
[LuaMethod("getmemorydomainsize", "Returns the number of bytes of the specified memory domain. If no domain is specified, or the specified domain doesn't exist, returns the current domain size")]
public uint GetMemoryDomainSize(string name = "")
public uint GetMemoryDomainSize([LuaASCIIStringParam] string name = "")
=> APIs.Memory.GetMemoryDomainSize(name);
[LuaMethodExample("local stmemget = memory.getcurrentmemorydomain( );")]
[LuaMethod("getcurrentmemorydomain", "Returns a string name of the current memory domain selected by Lua. The default is Main memory")]
[return: LuaASCIIStringParam]
public string GetCurrentMemoryDomain()
=> APIs.Memory.GetCurrentMemoryDomain();
@ -37,42 +39,43 @@ namespace BizHawk.Client.Common
[LuaMethodExample("if ( memory.usememorydomain( mainmemory.getname( ) ) ) then\r\n\tconsole.log( \"Attempts to set the current memory domain to the given domain. If the name does not match a valid memory domain, the function returns false, else it returns true\" );\r\nend;")]
[LuaMethod("usememorydomain", "Attempts to set the current memory domain to the given domain. If the name does not match a valid memory domain, the function returns false, else it returns true")]
public bool UseMemoryDomain(string domain)
public bool UseMemoryDomain([LuaASCIIStringParam] string domain)
=> APIs.Memory.UseMemoryDomain(domain);
[LuaMethodExample("local stmemhas = memory.hash_region( 0x100, 50, mainmemory.getname( ) );")]
[LuaMethod("hash_region", "Returns a hash as a string of a region of memory, starting from addr, through count bytes. If the domain is unspecified, it uses the current region.")]
public string HashRegion(long addr, int count, string domain = null)
[return: LuaASCIIStringParam]
public string HashRegion(long addr, int count, [LuaASCIIStringParam] string domain = null)
=> APIs.Memory.HashRegion(addr, count, domain);
[LuaMethodExample("local uimemrea = memory.readbyte( 0x100, mainmemory.getname( ) );")]
[LuaMethod("readbyte", "gets the value from the given address as an unsigned byte")]
public uint ReadByte(long addr, string domain = null)
public uint ReadByte(long addr, [LuaASCIIStringParam] string domain = null)
=> APIs.Memory.ReadByte(addr, domain);
[LuaMethodExample("memory.writebyte( 0x100, 1000, mainmemory.getname( ) );")]
[LuaMethod("writebyte", "Writes the given value to the given address as an unsigned byte")]
public void WriteByte(long addr, uint value, string domain = null)
public void WriteByte(long addr, uint value, [LuaASCIIStringParam] string domain = null)
=> APIs.Memory.WriteByte(addr, value, domain);
[LuaDeprecatedMethod]
[LuaMethod("readbyterange", "Reads the address range that starts from address, and is length long. Returns a zero-indexed table containing the read values (an array of bytes.)")]
public LuaTable ReadByteRange(long addr, int length, string domain = null)
public LuaTable ReadByteRange(long addr, int length, [LuaASCIIStringParam] string domain = null)
=> _th.ListToTable(APIs.Memory.ReadByteRange(addr, length, domain), indexFrom: 0);
[LuaMethodExample("local bytes = memory.read_bytes_as_array(0x100, 30, \"WRAM\");")]
[LuaMethod("read_bytes_as_array", "Reads length bytes starting at addr into an array-like table (1-indexed).")]
public LuaTable ReadBytesAsArray(long addr, int length, string domain = null)
public LuaTable ReadBytesAsArray(long addr, int length, [LuaASCIIStringParam] string domain = null)
=> _th.ListToTable(APIs.Memory.ReadByteRange(addr, length, domain));
[LuaMethodExample("local bytes = memory.read_bytes_as_dict(0x100, 30, \"WRAM\");")]
[LuaMethod("read_bytes_as_dict", "Reads length bytes starting at addr into a dict-like table (where the keys are the addresses, relative to the start of the domain).")]
public LuaTable ReadBytesAsDict(long addr, int length, string domain = null)
public LuaTable ReadBytesAsDict(long addr, int length, [LuaASCIIStringParam] string domain = null)
=> _th.MemoryBlockToTable(APIs.Memory.ReadByteRange(addr, length, domain), addr);
[LuaDeprecatedMethod]
[LuaMethod("writebyterange", "Writes the given values to the given addresses as unsigned bytes")]
public void WriteByteRange(LuaTable memoryblock, string domain = null)
public void WriteByteRange(LuaTable memoryblock, [LuaASCIIStringParam] string domain = null)
{
#if true
WriteBytesAsDict(memoryblock, domain);
@ -102,12 +105,12 @@ namespace BizHawk.Client.Common
[LuaMethodExample("memory.write_bytes_as_array(0x100, { 0xAB, 0x12, 0xCD, 0x34 });")]
[LuaMethod("write_bytes_as_array", "Writes sequential bytes starting at addr.")]
public void WriteBytesAsArray(long addr, LuaTable bytes, string domain = null)
public void WriteBytesAsArray(long addr, LuaTable bytes, [LuaASCIIStringParam] string domain = null)
=> APIs.Memory.WriteByteRange(addr, _th.EnumerateValues<double>(bytes).Select(d => (byte) d).ToList(), domain);
[LuaMethodExample("memory.write_bytes_as_dict({ [0x100] = 0xAB, [0x104] = 0xCD, [0x106] = 0x12, [0x107] = 0x34, [0x108] = 0xEF });")]
[LuaMethod("write_bytes_as_dict", "Writes bytes at arbitrary addresses (the keys of the given table are the addresses, relative to the start of the domain).")]
public void WriteBytesAsDict(LuaTable addrMap, string domain = null)
public void WriteBytesAsDict(LuaTable addrMap, [LuaASCIIStringParam] string domain = null)
{
foreach (var (addr, v) in _th.EnumerateEntries<double, double>(addrMap))
{
@ -117,7 +120,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local simemrea = memory.readfloat( 0x100, false, mainmemory.getname( ) );")]
[LuaMethod("readfloat", "Reads the given address as a 32-bit float value from the main memory domain with th e given endian")]
public float ReadFloat(long addr, bool bigendian, string domain = null)
public float ReadFloat(long addr, bool bigendian, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian(bigendian);
return APIs.Memory.ReadFloat(addr, domain);
@ -125,7 +128,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("memory.writefloat( 0x100, 10.0, false, mainmemory.getname( ) );")]
[LuaMethod("writefloat", "Writes the given 32-bit float value to the given address and endian")]
public void WriteFloat(long addr, double value, bool bigendian, string domain = null)
public void WriteFloat(long addr, double value, bool bigendian, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian(bigendian);
APIs.Memory.WriteFloat(addr, value, domain);
@ -133,27 +136,27 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local inmemrea = memory.read_s8( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_s8", "read signed byte")]
public int ReadS8(long addr, string domain = null)
public int ReadS8(long addr, [LuaASCIIStringParam] string domain = null)
=> APIs.Memory.ReadS8(addr, domain);
[LuaMethodExample("memory.write_s8( 0x100, 1000, mainmemory.getname( ) );")]
[LuaMethod("write_s8", "write signed byte")]
public void WriteS8(long addr, uint value, string domain = null)
public void WriteS8(long addr, uint value, [LuaASCIIStringParam] string domain = null)
=> APIs.Memory.WriteS8(addr, unchecked((int) value), domain);
[LuaMethodExample("local uimemrea = memory.read_u8( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_u8", "read unsigned byte")]
public uint ReadU8(long addr, string domain = null)
public uint ReadU8(long addr, [LuaASCIIStringParam] string domain = null)
=> APIs.Memory.ReadU8(addr, domain);
[LuaMethodExample("memory.write_u8( 0x100, 1000, mainmemory.getname( ) );")]
[LuaMethod("write_u8", "write unsigned byte")]
public void WriteU8(long addr, uint value, string domain = null)
public void WriteU8(long addr, uint value, [LuaASCIIStringParam] string domain = null)
=> APIs.Memory.WriteU8(addr, value, domain);
[LuaMethodExample("local inmemrea = memory.read_s16_le( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_s16_le", "read signed 2 byte value, little endian")]
public int ReadS16Little(long addr, string domain = null)
public int ReadS16Little(long addr, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian(false);
return APIs.Memory.ReadS16(addr, domain);
@ -161,7 +164,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("memory.write_s16_le( 0x100, -1000, mainmemory.getname( ) );")]
[LuaMethod("write_s16_le", "write signed 2 byte value, little endian")]
public void WriteS16Little(long addr, int value, string domain = null)
public void WriteS16Little(long addr, int value, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian(false);
APIs.Memory.WriteS16(addr, value, domain);
@ -169,7 +172,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local inmemrea = memory.read_s16_be( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_s16_be", "read signed 2 byte value, big endian")]
public int ReadS16Big(long addr, string domain = null)
public int ReadS16Big(long addr, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian();
return APIs.Memory.ReadS16(addr, domain);
@ -177,7 +180,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("memory.write_s16_be( 0x100, -1000, mainmemory.getname( ) );")]
[LuaMethod("write_s16_be", "write signed 2 byte value, big endian")]
public void WriteS16Big(long addr, int value, string domain = null)
public void WriteS16Big(long addr, int value, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian();
APIs.Memory.WriteS16(addr, value, domain);
@ -185,7 +188,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local uimemrea = memory.read_u16_le( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_u16_le", "read unsigned 2 byte value, little endian")]
public uint ReadU16Little(long addr, string domain = null)
public uint ReadU16Little(long addr, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian(false);
return APIs.Memory.ReadU16(addr, domain);
@ -193,7 +196,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("memory.write_u16_le( 0x100, 1000, mainmemory.getname( ) );")]
[LuaMethod("write_u16_le", "write unsigned 2 byte value, little endian")]
public void WriteU16Little(long addr, uint value, string domain = null)
public void WriteU16Little(long addr, uint value, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian(false);
APIs.Memory.WriteU16(addr, value, domain);
@ -201,7 +204,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local uimemrea = memory.read_u16_be( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_u16_be", "read unsigned 2 byte value, big endian")]
public uint ReadU16Big(long addr, string domain = null)
public uint ReadU16Big(long addr, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian();
return APIs.Memory.ReadU16(addr, domain);
@ -209,7 +212,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("memory.write_u16_be( 0x100, 1000, mainmemory.getname( ) );")]
[LuaMethod("write_u16_be", "write unsigned 2 byte value, big endian")]
public void WriteU16Big(long addr, uint value, string domain = null)
public void WriteU16Big(long addr, uint value, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian();
APIs.Memory.WriteU16(addr, value, domain);
@ -217,7 +220,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local inmemrea = memory.read_s24_le( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_s24_le", "read signed 24 bit value, little endian")]
public int ReadS24Little(long addr, string domain = null)
public int ReadS24Little(long addr, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian(false);
return APIs.Memory.ReadS24(addr, domain);
@ -225,7 +228,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("memory.write_s24_le( 0x100, -1000, mainmemory.getname( ) );")]
[LuaMethod("write_s24_le", "write signed 24 bit value, little endian")]
public void WriteS24Little(long addr, int value, string domain = null)
public void WriteS24Little(long addr, int value, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian(false);
APIs.Memory.WriteS24(addr, value, domain);
@ -233,7 +236,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local inmemrea = memory.read_s24_be( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_s24_be", "read signed 24 bit value, big endian")]
public int ReadS24Big(long addr, string domain = null)
public int ReadS24Big(long addr, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian();
return APIs.Memory.ReadS24(addr, domain);
@ -241,7 +244,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("memory.write_s24_be( 0x100, -1000, mainmemory.getname( ) );")]
[LuaMethod("write_s24_be", "write signed 24 bit value, big endian")]
public void WriteS24Big(long addr, int value, string domain = null)
public void WriteS24Big(long addr, int value, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian();
APIs.Memory.WriteS24(addr, value, domain);
@ -249,7 +252,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local uimemrea = memory.read_u24_le( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_u24_le", "read unsigned 24 bit value, little endian")]
public uint ReadU24Little(long addr, string domain = null)
public uint ReadU24Little(long addr, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian(false);
return APIs.Memory.ReadU24(addr, domain);
@ -257,7 +260,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("memory.write_u24_le( 0x100, 1000, mainmemory.getname( ) );")]
[LuaMethod("write_u24_le", "write unsigned 24 bit value, little endian")]
public void WriteU24Little(long addr, uint value, string domain = null)
public void WriteU24Little(long addr, uint value, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian(false);
APIs.Memory.WriteU24(addr, value, domain);
@ -265,7 +268,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local uimemrea = memory.read_u24_be( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_u24_be", "read unsigned 24 bit value, big endian")]
public uint ReadU24Big(long addr, string domain = null)
public uint ReadU24Big(long addr, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian();
return APIs.Memory.ReadU24(addr, domain);
@ -273,7 +276,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("memory.write_u24_be( 0x100, 1000, mainmemory.getname( ) );")]
[LuaMethod("write_u24_be", "write unsigned 24 bit value, big endian")]
public void WriteU24Big(long addr, uint value, string domain = null)
public void WriteU24Big(long addr, uint value, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian();
APIs.Memory.WriteU24(addr, value, domain);
@ -281,7 +284,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local inmemrea = memory.read_s32_le( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_s32_le", "read signed 4 byte value, little endian")]
public int ReadS32Little(long addr, string domain = null)
public int ReadS32Little(long addr, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian(false);
return APIs.Memory.ReadS32(addr, domain);
@ -289,7 +292,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("memory.write_s32_le( 0x100, -1000, mainmemory.getname( ) );")]
[LuaMethod("write_s32_le", "write signed 4 byte value, little endian")]
public void WriteS32Little(long addr, int value, string domain = null)
public void WriteS32Little(long addr, int value, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian(false);
APIs.Memory.WriteS32(addr, value, domain);
@ -297,7 +300,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local inmemrea = memory.read_s32_be( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_s32_be", "read signed 4 byte value, big endian")]
public int ReadS32Big(long addr, string domain = null)
public int ReadS32Big(long addr, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian();
return APIs.Memory.ReadS32(addr, domain);
@ -305,7 +308,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("memory.write_s32_be( 0x100, -1000, mainmemory.getname( ) );")]
[LuaMethod("write_s32_be", "write signed 4 byte value, big endian")]
public void WriteS32Big(long addr, int value, string domain = null)
public void WriteS32Big(long addr, int value, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian();
APIs.Memory.WriteS32(addr, value, domain);
@ -313,7 +316,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local uimemrea = memory.read_u32_le( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_u32_le", "read unsigned 4 byte value, little endian")]
public uint ReadU32Little(long addr, string domain = null)
public uint ReadU32Little(long addr, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian(false);
return APIs.Memory.ReadU32(addr, domain);
@ -321,7 +324,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("memory.write_u32_le( 0x100, 1000, mainmemory.getname( ) );")]
[LuaMethod("write_u32_le", "write unsigned 4 byte value, little endian")]
public void WriteU32Little(long addr, uint value, string domain = null)
public void WriteU32Little(long addr, uint value, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian(false);
APIs.Memory.WriteU32(addr, value, domain);
@ -329,7 +332,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local uimemrea = memory.read_u32_be( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_u32_be", "read unsigned 4 byte value, big endian")]
public uint ReadU32Big(long addr, string domain = null)
public uint ReadU32Big(long addr, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian();
return APIs.Memory.ReadU32(addr, domain);
@ -337,7 +340,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("memory.write_u32_be( 0x100, 1000, mainmemory.getname( ) );")]
[LuaMethod("write_u32_be", "write unsigned 4 byte value, big endian")]
public void WriteU32Big(long addr, uint value, string domain = null)
public void WriteU32Big(long addr, uint value, [LuaASCIIStringParam] string domain = null)
{
APIs.Memory.SetBigEndian();
APIs.Memory.WriteU32(addr, value, domain);

View File

@ -13,17 +13,18 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local mmsvstsvcst = memorysavestate.savecorestate( );")]
[LuaMethod("savecorestate", "creates a core savestate and stores it in memory. Note: a core savestate is only the raw data from the core, and not extras such as movie input logs, or framebuffers. Returns a unique identifer for the savestate")]
[return: LuaASCIIStringParam]
public string SaveCoreStateToMemory()
=> APIs.MemorySaveState.SaveCoreStateToMemory();
[LuaMethodExample("memorysavestate.loadcorestate( \"3fcf120f-0778-43fd-b2c5-460fb7d34184\" );")]
[LuaMethod("loadcorestate", "loads an in memory state with the given identifier")]
public void LoadCoreStateFromMemory(string identifier)
public void LoadCoreStateFromMemory([LuaASCIIStringParam] string identifier)
=> APIs.MemorySaveState.LoadCoreStateFromMemory(identifier);
[LuaMethodExample("memorysavestate.removestate( \"3fcf120f-0778-43fd-b2c5-460fb7d34184\" );")]
[LuaMethod("removestate", "removes the savestate with the given identifier from memory")]
public void DeleteState(string identifier)
public void DeleteState([LuaASCIIStringParam] string identifier)
=> APIs.MemorySaveState.DeleteState(identifier);
[LuaMethodExample("memorysavestate.clearstatesfrommemory( );")]

View File

@ -1,4 +1,5 @@
using System;
using System.Linq;
using NLua;
@ -24,16 +25,19 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local stmovfil = movie.filename( );")]
[LuaMethod("filename", "Returns the file name including path of the currently loaded movie")]
[return: LuaArbitraryStringParam]
public string Filename()
=> APIs.Movie.Filename();
=> UnFixString(APIs.Movie.Filename());
[LuaMethodExample("local nlmovget = movie.getinput( 500 );")]
[LuaMethod("getinput", "Returns a table of buttons pressed on a given frame of the loaded movie")]
[return: LuaASCIIStringParam]
public LuaTable GetInput(int frame, int? controller = null)
=> _th.DictToTable(APIs.Movie.GetInput(frame, controller));
[LuaMethodExample("local stmovget = movie.getinputasmnemonic( 500 );")]
[LuaMethod("getinputasmnemonic", "Returns the input of a given frame of the loaded movie in a raw inputlog string")]
[return: LuaASCIIStringParam]
public string GetInputAsMnemonic(int frame)
=> APIs.Movie.GetInputAsMnemonic(frame);
@ -64,13 +68,14 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local stmovmod = movie.mode( );")]
[LuaMethod("mode", "Returns the mode of the current movie. Possible modes: \"PLAY\", \"RECORD\", \"FINISHED\", \"INACTIVE\"")]
[return: LuaEnumStringParam]
public string Mode()
=> APIs.Movie.Mode();
[LuaMethodExample("movie.save( \"C:\\moviename.ext\" );")]
[LuaMethod("save", "Saves the current movie to the disc. If the filename is provided (no extension or path needed), the movie is saved under the specified name to the current movie directory. The filename may contain a subdirectory, it will be created if it doesn't exist. Existing files won't get overwritten.")]
public void Save(string filename = "")
=> APIs.Movie.Save(filename);
public void Save([LuaArbitraryStringParam] string filename = "")
=> APIs.Movie.Save(FixString(filename));
[LuaMethodExample("movie.setreadonly( false );")]
[LuaMethod("setreadonly", "Sets the read-only state to the given value. true for read only, false for read+write")]
@ -99,17 +104,20 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local nlmovget = movie.getheader( );")]
[LuaMethod("getheader", "If a movie is active, will return the movie header as a lua table")]
[return: LuaArbitraryStringParam]
public LuaTable GetHeader()
=> _th.DictToTable(APIs.Movie.GetHeader());
=> _th.DictToTable(APIs.Movie.GetHeader().ToDictionary(static kvp => UnFixString(kvp.Key), static kvp => UnFixString(kvp.Value)));
[LuaMethodExample("local nlmovget = movie.getcomments( );")]
[LuaMethod("getcomments", "If a movie is active, will return the movie comments as a lua table")]
[return: LuaArbitraryStringParam]
public LuaTable GetComments()
=> _th.ListToTable(APIs.Movie.GetComments(), indexFrom: 0);
=> _th.ListToTable(APIs.Movie.GetComments().Select(static s => UnFixString(s)).ToList(), indexFrom: 0);
[LuaMethodExample("local nlmovget = movie.getsubtitles( );")]
[LuaMethod("getsubtitles", "If a movie is active, will return the movie subtitles as a lua table")]
[return: LuaArbitraryStringParam]
public LuaTable GetSubtitles()
=> _th.ListToTable(APIs.Movie.GetSubtitles(), indexFrom: 0);
=> _th.ListToTable(APIs.Movie.GetSubtitles().Select(static s => UnFixString(s)).ToList(), indexFrom: 0);
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
// ReSharper disable UnusedMember.Global
namespace BizHawk.Client.Common
@ -15,27 +16,36 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local stSQLcre = SQL.createdatabase( \"eg_db\" );")]
[LuaMethod("createdatabase", "Creates a SQLite Database. Name should end with .db")]
public string CreateDatabase(string name)
=> APIs.SQLite.CreateDatabase(name);
[return: LuaArbitraryStringParam]
public string CreateDatabase([LuaArbitraryStringParam] string name)
=> UnFixString(APIs.SQLite.CreateDatabase(FixString(name)));
[LuaMethodExample("local stSQLope = SQL.opendatabase( \"eg_db\" );")]
[LuaMethod("opendatabase", "Opens a SQLite database. Name should end with .db")]
public string OpenDatabase(string name)
=> APIs.SQLite.OpenDatabase(name);
[return: LuaArbitraryStringParam]
public string OpenDatabase([LuaArbitraryStringParam] string name)
=> UnFixString(APIs.SQLite.OpenDatabase(FixString(name)));
[LuaMethodExample("local stSQLwri = SQL.writecommand( \"CREATE TABLE eg_tab ( eg_tab_id integer PRIMARY KEY, eg_tab_row_name text NOT NULL ); INSERT INTO eg_tab ( eg_tab_id, eg_tab_row_name ) VALUES ( 1, 'Example table row' );\" );")]
[LuaMethod("writecommand", "Runs a SQLite write command which includes CREATE,INSERT, UPDATE. " +
"Ex: create TABLE rewards (ID integer PRIMARY KEY, action VARCHAR(20)) ")]
public string WriteCommand(string query = "")
=> APIs.SQLite.WriteCommand(query);
[return: LuaArbitraryStringParam]
public string WriteCommand([LuaArbitraryStringParam] string query = "")
=> UnFixString(APIs.SQLite.WriteCommand(FixString(query)));
[LuaMethodExample("local obSQLrea = SQL.readcommand( \"SELECT * FROM eg_tab WHERE eg_tab_id = 1;\" );")]
[LuaMethod("readcommand", "Run a SQLite read command which includes Select. Returns all rows into a LuaTable." +
"Ex: select * from rewards")]
public object ReadCommand(string query = "")
[return: LuaArbitraryStringParam]
public object ReadCommand([LuaArbitraryStringParam] string query = "")
{
var result = APIs.SQLite.ReadCommand(query);
return result is Dictionary<string, object> dict ? _th.DictToTable(dict) : result;
var result = APIs.SQLite.ReadCommand(FixString(query));
return result switch
{
Dictionary<string, object> dict => _th.DictToTable(dict.ToDictionary(static kvp => UnFixString(kvp.Key), static kvp => kvp.Value)),
string s => UnFixString(s),
_ => result
};
}
}
}

View File

@ -11,11 +11,11 @@ namespace BizHawk.Client.Common
[LuaMethodExample("savestate.load( \"C:\\state.bin\" );")]
[LuaMethod("load", "Loads a savestate with the given path. If EmuHawk is deferring quicksaves, to TAStudio for example, that form will do what it likes (and the path is ignored).")]
public void Load(string path, bool suppressOSD = false)
public void Load([LuaArbitraryStringParam] string path, bool suppressOSD = false)
{
_luaLibsImpl.IsUpdateSupressed = true;
APIs.SaveState.Load(path, suppressOSD);
APIs.SaveState.Load(FixString(path), suppressOSD);
_luaLibsImpl.IsUpdateSupressed = false;
}
@ -33,8 +33,8 @@ namespace BizHawk.Client.Common
[LuaMethodExample("savestate.save( \"C:\\state.bin\" );")]
[LuaMethod("save", "Saves a state at the given path. If EmuHawk is deferring quicksaves, to TAStudio for example, that form will do what it likes (and the path is ignored).")]
public void Save(string path, bool suppressOSD = false)
=> APIs.SaveState.Save(path, suppressOSD);
public void Save([LuaArbitraryStringParam] string path, bool suppressOSD = false)
=> APIs.SaveState.Save(FixString(path), suppressOSD);
[LuaMethodExample("savestate.saveslot( 7 );")]
[LuaMethod("saveslot", "Saves a state at the given save slot (must be an integer between 0 and 9). If EmuHawk is deferring quicksaves, to TAStudio for example, that form will do what it likes with the slot number.")]

View File

@ -14,13 +14,17 @@ namespace BizHawk.Client.Common
[LuaMethodExample("userdata.set(\"Unique key\", \"Current key data\");")]
[LuaMethod("set", "adds or updates the data with the given key with the given value")]
public void Set(string name, object value)
=> APIs.UserData.Set(name, value);
public void Set([LuaArbitraryStringParam] string name, [LuaArbitraryStringParam] object value)
=> APIs.UserData.Set(FixString(name), value is string s ? FixString(s) : value);
[LuaMethodExample("local obuseget = userdata.get( \"Unique key\" );")]
[LuaMethod("get", "gets the data with the given key, if the key does not exist it will return nil")]
public object Get(string key)
=> APIs.UserData.Get(key);
[return: LuaArbitraryStringParam]
public object Get([LuaArbitraryStringParam] string key)
{
var o = APIs.UserData.Get(FixString(key));
return o is string s ? UnFixString(s) : o;
}
[LuaMethodExample("userdata.clear( );")]
[LuaMethod("clear", "clears all user data")]
@ -29,12 +33,12 @@ namespace BizHawk.Client.Common
[LuaMethodExample("if ( userdata.remove( \"Unique key\" ) ) then\r\n\tconsole.log( \"remove the data with the given key.Returns true if the element is successfully found and removed; otherwise, false.\" );\r\nend;")]
[LuaMethod("remove", "remove the data with the given key. Returns true if the element is successfully found and removed; otherwise, false.")]
public bool Remove(string key)
=> APIs.UserData.Remove(key);
public bool Remove([LuaArbitraryStringParam] string key)
=> APIs.UserData.Remove(FixString(key));
[LuaMethodExample("if ( userdata.containskey( \"Unique key\" ) ) then\r\n\tconsole.log( \"returns whether or not there is an entry for the given key\" );\r\nend;")]
[LuaMethod("containskey", "returns whether or not there is an entry for the given key")]
public bool ContainsKey(string key)
=> APIs.UserData.ContainsKey(key);
public bool ContainsKey([LuaArbitraryStringParam] string key)
=> APIs.UserData.ContainsKey(FixString(key));
}
}

View File

@ -25,7 +25,12 @@ namespace BizHawk.Client.Common
void CallSaveStateEvent(string name);
INamedLuaFunction CreateAndRegisterNamedFunction(LuaFunction function, string theEvent, Action<string> logCallback, LuaFile luaFile, string name = null);
INamedLuaFunction CreateAndRegisterNamedFunction(
LuaFunction function,
string theEvent,
Action<string> logCallback,
LuaFile luaFile,
[LuaArbitraryStringParam] string name = null);
NLuaTableHelper GetTableHelper();

View File

@ -2,12 +2,24 @@
namespace BizHawk.Client.Common
{
/// <summary>Indicates a parameter/return is (or contains) a string which may include non-ASCII characters.</summary>
[AttributeUsage(AttributeTargets.ReturnValue | AttributeTargets.Parameter)]
public sealed class LuaArbitraryStringParamAttribute : LuaStringParamAttributeBase {}
/// <summary>Indicates a parameter/return is (or contains) a string which may only include ASCII characters.</summary>
[AttributeUsage(AttributeTargets.ReturnValue | AttributeTargets.Parameter)]
public sealed class LuaASCIIStringParamAttribute : LuaStringParamAttributeBase {}
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class LuaColorParamAttribute : Attribute {}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public sealed class LuaDeprecatedMethodAttribute : Attribute {}
/// <summary>Indicates a parameter/return is (or contains) a string which is one of a known few constants (and these may only include ASCII characters).</summary>
[AttributeUsage(AttributeTargets.ReturnValue | AttributeTargets.Parameter)]
public sealed class LuaEnumStringParamAttribute : LuaStringParamAttributeBase {}
[AttributeUsage(AttributeTargets.Method)]
public sealed class LuaMethodAttribute : Attribute
{
@ -32,6 +44,8 @@ namespace BizHawk.Client.Common
public string Example { get; }
}
public abstract class LuaStringParamAttributeBase : Attribute {}
[AttributeUsage(AttributeTargets.Class)]
public sealed class LuaLibraryAttribute : Attribute
{

View File

@ -51,19 +51,22 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local steveonf = event.onframeend(\r\n\tfunction()\r\n\t\tconsole.log( \"Calls the given lua function at the end of each frame, after all emulation and drawing has completed. Note: this is the default behavior of lua scripts\" );\r\n\tend\r\n\t, \"Frame name\" );")]
[LuaMethod("onframeend", "Calls the given lua function at the end of each frame, after all emulation and drawing has completed. Note: this is the default behavior of lua scripts")]
public string OnFrameEnd(LuaFunction luaf, string name = null)
[return: LuaASCIIStringParam]
public string OnFrameEnd(LuaFunction luaf, [LuaArbitraryStringParam] string name = null)
=> _luaLibsImpl.CreateAndRegisterNamedFunction(luaf, "OnFrameEnd", LogOutputCallback, CurrentFile, name)
.Guid.ToString();
[LuaMethodExample("local steveonf = event.onframestart(\r\n\tfunction()\r\n\t\tconsole.log( \"Calls the given lua function at the beginning of each frame before any emulation and drawing occurs\" );\r\n\tend\r\n\t, \"Frame name\" );")]
[LuaMethod("onframestart", "Calls the given lua function at the beginning of each frame before any emulation and drawing occurs")]
public string OnFrameStart(LuaFunction luaf, string name = null)
[return: LuaASCIIStringParam]
public string OnFrameStart(LuaFunction luaf, [LuaArbitraryStringParam] string name = null)
=> _luaLibsImpl.CreateAndRegisterNamedFunction(luaf, "OnFrameStart", LogOutputCallback, CurrentFile, name)
.Guid.ToString();
[LuaMethodExample("local steveoni = event.oninputpoll(\r\n\tfunction()\r\n\t\tconsole.log( \"Calls the given lua function after each time the emulator core polls for input\" );\r\n\tend\r\n\t, \"Frame name\" );")]
[LuaMethod("oninputpoll", "Calls the given lua function after each time the emulator core polls for input")]
public string OnInputPoll(LuaFunction luaf, string name = null)
[return: LuaASCIIStringParam]
public string OnInputPoll(LuaFunction luaf, [LuaArbitraryStringParam] string name = null)
{
var nlf = _luaLibsImpl.CreateAndRegisterNamedFunction(luaf, "OnInputPoll", LogOutputCallback, CurrentFile, name);
//TODO should we bother registering the function if the service isn't supported? none of the other events work this way --yoshi
@ -93,13 +96,19 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local steveonl = event.onloadstate(\r\n\tfunction()\r\n\tconsole.log( \"Fires after a state is loaded. Receives a lua function name, and registers it to the event immediately following a successful savestate event\" );\r\nend\", \"Frame name\" );")]
[LuaMethod("onloadstate", "Fires after a state is loaded. Receives a lua function name, and registers it to the event immediately following a successful savestate event")]
public string OnLoadState(LuaFunction luaf, string name = null)
[return: LuaASCIIStringParam]
public string OnLoadState(LuaFunction luaf, [LuaArbitraryStringParam] string name = null)
=> _luaLibsImpl.CreateAndRegisterNamedFunction(luaf, "OnSavestateLoad", LogOutputCallback, CurrentFile, name)
.Guid.ToString();
[LuaMethodExample("local steveonm = event.onmemoryexecute(\r\n\tfunction()\r\n\t\tconsole.log( \"Fires after the given address is executed by the core\" );\r\n\tend\r\n\t, 0x200, \"Frame name\", \"System Bus\" );")]
[LuaMethod("onmemoryexecute", "Fires after the given address is executed by the core")]
public string OnMemoryExecute(LuaFunction luaf, uint address, string name = null, string scope = null)
[return: LuaASCIIStringParam]
public string OnMemoryExecute(
LuaFunction luaf,
uint address,
[LuaArbitraryStringParam] string name = null,
[LuaASCIIStringParam] string scope = null)
{
try
{
@ -130,7 +139,11 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local steveonm = event.onmemoryexecuteany(\r\n\tfunction()\r\n\t\tconsole.log( \"Fires after any address is executed by the core (CPU-intensive)\" );\r\n\tend\r\n\t, \"Frame name\", \"System Bus\" );")]
[LuaMethod("onmemoryexecuteany", "Fires after any address is executed by the core (CPU-intensive)")]
public string OnMemoryExecuteAny(LuaFunction luaf, string name = null, string scope = null)
[return: LuaASCIIStringParam]
public string OnMemoryExecuteAny(
LuaFunction luaf,
[LuaArbitraryStringParam] string name = null,
[LuaASCIIStringParam] string scope = null)
{
try
{
@ -166,7 +179,12 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local steveonm = event.onmemoryread(\r\n\tfunction()\r\n\t\tconsole.log( \"Fires after the given address is read by the core. If no address is given, it will attach to every memory read\" );\r\n\tend\r\n\t, 0x200, \"Frame name\" );")]
[LuaMethod("onmemoryread", "Fires after the given address is read by the core. If no address is given, it will attach to every memory read")]
public string OnMemoryRead(LuaFunction luaf, uint? address = null, string name = null, string scope = null)
[return: LuaASCIIStringParam]
public string OnMemoryRead(
LuaFunction luaf,
uint? address = null,
[LuaArbitraryStringParam] string name = null,
[LuaASCIIStringParam] string scope = null)
{
try
{
@ -196,7 +214,12 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local steveonm = event.onmemorywrite(\r\n\tfunction()\r\n\t\tconsole.log( \"Fires after the given address is written by the core. If no address is given, it will attach to every memory write\" );\r\n\tend\r\n\t, 0x200, \"Frame name\" );")]
[LuaMethod("onmemorywrite", "Fires after the given address is written by the core. If no address is given, it will attach to every memory write")]
public string OnMemoryWrite(LuaFunction luaf, uint? address = null, string name = null, string scope = null)
[return: LuaASCIIStringParam]
public string OnMemoryWrite(
LuaFunction luaf,
uint? address = null,
[LuaArbitraryStringParam] string name = null,
[LuaASCIIStringParam] string scope = null)
{
try
{
@ -226,34 +249,41 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local steveons = event.onsavestate(\r\n\tfunction()\r\n\t\tconsole.log( \"Fires after a state is saved\" );\r\n\tend\r\n\t, \"Frame name\" );")]
[LuaMethod("onsavestate", "Fires after a state is saved")]
public string OnSaveState(LuaFunction luaf, string name = null)
[return: LuaASCIIStringParam]
public string OnSaveState(LuaFunction luaf, [LuaArbitraryStringParam] string name = null)
=> _luaLibsImpl.CreateAndRegisterNamedFunction(luaf, "OnSavestateSave", LogOutputCallback, CurrentFile, name)
.Guid.ToString();
[LuaMethodExample("local steveone = event.onexit(\r\n\tfunction()\r\n\t\tconsole.log( \"Fires after the calling script has stopped\" );\r\n\tend\r\n\t, \"Frame name\" );")]
[LuaMethod("onexit", "Fires after the calling script has stopped")]
public string OnExit(LuaFunction luaf, string name = null)
[return: LuaASCIIStringParam]
public string OnExit(LuaFunction luaf, [LuaArbitraryStringParam] string name = null)
=> _luaLibsImpl.CreateAndRegisterNamedFunction(luaf, "OnExit", LogOutputCallback, CurrentFile, name)
.Guid.ToString();
[LuaMethodExample("local closeGuid = event.onconsoleclose(\r\n\tfunction()\r\n\t\tconsole.log( \"Fires when the emulator console closes\" );\r\n\tend\r\n\t, \"Frame name\" );")]
[LuaMethod("onconsoleclose", "Fires when the emulator console closes")]
public string OnConsoleClose(LuaFunction luaf, string name = null)
[return: LuaASCIIStringParam]
public string OnConsoleClose(LuaFunction luaf, [LuaArbitraryStringParam] string name = null)
=> _luaLibsImpl.CreateAndRegisterNamedFunction(luaf, "OnConsoleClose", LogOutputCallback, CurrentFile, name)
.Guid.ToString();
[LuaMethodExample("if ( event.unregisterbyid( \"4d1810b7 - 0d28 - 4acb - 9d8b - d87721641551\" ) ) then\r\n\tconsole.log( \"Removes the registered function that matches the guid.If a function is found and remove the function will return true.If unable to find a match, the function will return false.\" );\r\nend;")]
[LuaMethod("unregisterbyid", "Removes the registered function that matches the guid. If a function is found and remove the function will return true. If unable to find a match, the function will return false.")]
public bool UnregisterById(string guid)
public bool UnregisterById([LuaASCIIStringParam] string guid)
=> _luaLibsImpl.RemoveNamedFunctionMatching(nlf => nlf.Guid.ToString() == guid);
[LuaMethodExample("if ( event.unregisterbyname( \"Function name\" ) ) then\r\n\tconsole.log( \"Removes the first registered function that matches Name.If a function is found and remove the function will return true.If unable to find a match, the function will return false.\" );\r\nend;")]
[LuaMethod("unregisterbyname", "Removes the first registered function that matches Name. If a function is found and remove the function will return true. If unable to find a match, the function will return false.")]
public bool UnregisterByName(string name)
=> _luaLibsImpl.RemoveNamedFunctionMatching(nlf => nlf.Name == name);
public bool UnregisterByName([LuaArbitraryStringParam] string name)
{
var name1 = FixString(name);
return _luaLibsImpl.RemoveNamedFunctionMatching(nlf => nlf.Name == name1);
}
[LuaMethodExample("local scopes = event.availableScopes();")]
[LuaMethod("availableScopes", "Lists the available scopes that can be passed into memory events")]
[return: LuaASCIIStringParam]
public LuaTable AvailableScopes()
{
return DebuggableCore?.MemoryCallbacksAvailable() == true

View File

@ -27,6 +27,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local stmaiget = mainmemory.getname( );")]
[LuaMethod("getname", "returns the name of the domain defined as main memory for the given core")]
[return: LuaASCIIStringParam]
public string GetName()
=> MainMemName;

View File

@ -23,6 +23,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("if ( nds.getscreenlayout( ) ) then\r\n\tconsole.log( \"Returns which screen layout is active\" );\r\nend;")]
[LuaMethod("getscreenlayout", "Returns which screen layout is active")]
[return: LuaEnumStringParam]
public string GetScreenLayout()
=> Settings.ScreenLayout.ToString();
@ -33,6 +34,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("if ( nds.getscreenrotation( ) ) then\r\n\tconsole.log( \"Returns how screens are rotated\" );\r\nend;")]
[LuaMethod("getscreenrotation", "Returns how screens are rotated")]
[return: LuaEnumStringParam]
public string GetScreenRotation()
=> Settings.ScreenRotation.ToString();
@ -43,12 +45,13 @@ namespace BizHawk.Client.Common
[LuaMethodExample("if ( nds.getaudiobitrate( ) ) then\r\n\tconsole.log( \"Returns the audio bitrate setting\" );\r\nend;")]
[LuaMethod("getaudiobitrate", "Returns the audio bitrate setting")]
[return: LuaEnumStringParam]
public string GetAudioBitrate()
=> Settings.AudioBitrate.ToString();
[LuaMethodExample("nds.setscreenlayout( \"Vertical\" );")]
[LuaMethod("setscreenlayout", "Sets which screen layout is active")]
public void SetScreenLayout(string value)
public void SetScreenLayout([LuaEnumStringParam] string value)
{
var s = Settings;
s.ScreenLayout = (NDS.ScreenLayoutKind)Enum.Parse(typeof(NDS.ScreenLayoutKind), value, true);
@ -66,7 +69,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("nds.setscreenrotation( \"Rotate0\" );")]
[LuaMethod("setscreenrotation", "Sets how screens are rotated")]
public void SetScreenRotation(string value)
public void SetScreenRotation([LuaEnumStringParam] string value)
{
var s = Settings;
s.ScreenRotation = (NDS.ScreenRotationKind)Enum.Parse(typeof(NDS.ScreenRotationKind), value, true);
@ -84,7 +87,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("nds.setaudiobitrate( \"Auto\" );")]
[LuaMethod("setaudiobitrate", "Sets the audio bitrate setting")]
public void SetAudioBitrate(string value)
public void SetAudioBitrate([LuaEnumStringParam] string value)
{
var s = Settings;
s.AudioBitrate = (NDS.NDSSettings.AudioBitrateType)Enum.Parse(typeof(NDS.NDSSettings.AudioBitrateType), value, true);

View File

@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
using System.Linq;
using NLua;
@ -16,6 +17,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local stbizhex = bizstring.hex( -12345 );")]
[LuaMethod("hex", "Converts the number to a string representation of the hexadecimal value of the given number")]
[return: LuaASCIIStringParam]
public static string Hex(long num)
{
var hex = $"{num:X}";
@ -29,6 +31,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local stbizbin = bizstring.binary( -12345 );")]
[LuaMethod("binary", "Converts the number to a string representation of the binary value of the given number")]
[return: LuaASCIIStringParam]
public static string Binary(long num)
{
var binary = Convert.ToString(num, 2);
@ -38,6 +41,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local stbizoct = bizstring.octal( -12345 );")]
[LuaMethod("octal", "Converts the number to a string representation of the octal value of the given number")]
[return: LuaASCIIStringParam]
public static string Octal(long num)
{
var octal = Convert.ToString(num, 8);
@ -51,82 +55,89 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local stbiztri = bizstring.trim( \"Some trim string\t \" );")]
[LuaMethod("trim", "returns a string that trims whitespace on the left and right ends of the string")]
public static string Trim(string str)
{
return string.IsNullOrEmpty(str) ? null : str.Trim();
}
[return: LuaArbitraryStringParam]
public static string Trim([LuaArbitraryStringParam] string str)
=> string.IsNullOrEmpty(str) ? null : UnFixString(FixString(str).Trim());
[LuaMethodExample("local stbizrep = bizstring.replace( \"Some string\", \"Some\", \"Replaced\" );")]
[LuaMethod("replace", "Returns a string that replaces all occurrences of str2 in str1 with the value of replace")]
public static string Replace(string str, string str2, string replace)
[return: LuaArbitraryStringParam]
public static string Replace(
[LuaArbitraryStringParam] string str,
[LuaArbitraryStringParam] string str2,
[LuaArbitraryStringParam] string replace)
{
return string.IsNullOrEmpty(str)
? null
: str.Replace(str2, replace);
: UnFixString(FixString(str).Replace(FixString(str2), FixString(replace)));
}
[LuaMethodExample("local stbiztou = bizstring.toupper( \"Some string\" );")]
[LuaMethod("toupper", "Returns an uppercase version of the given string")]
public static string ToUpper(string str)
[return: LuaArbitraryStringParam]
public static string ToUpper([LuaArbitraryStringParam] string str)
{
return string.IsNullOrEmpty(str)
? null
: str.ToUpper();
: UnFixString(FixString(str).ToUpperInvariant());
}
[LuaMethodExample("local stbiztol = bizstring.tolower( \"Some string\" );")]
[LuaMethod("tolower", "Returns an lowercase version of the given string")]
public static string ToLower(string str)
[return: LuaArbitraryStringParam]
public static string ToLower([LuaArbitraryStringParam] string str)
{
return string.IsNullOrEmpty(str)
? null
: str.ToLower();
: UnFixString(FixString(str).ToLowerInvariant());
}
[LuaMethodExample("local stbizsub = bizstring.substring( \"Some string\", 6, 3 );")]
[LuaMethod("substring", "Returns a string that represents a substring of str starting at position for the specified length")]
public static string SubString(string str, int position, int length)
[return: LuaArbitraryStringParam]
public static string SubString([LuaArbitraryStringParam] string str, int position, int length)
{
return string.IsNullOrEmpty(str)
? null
: str.Substring(position, length);
: UnFixString(FixString(str).Substring(position, length));
}
[LuaMethodExample("local stbizrem = bizstring.remove( \"Some string\", 4, 5 );")]
[LuaMethod("remove", "Returns a string that represents str with the given position and count removed")]
public static string Remove(string str, int position, int count)
[return: LuaArbitraryStringParam]
public static string Remove([LuaArbitraryStringParam] string str, int position, int count)
{
return string.IsNullOrEmpty(str)
? null
: str.Remove(position, count);
: UnFixString(FixString(str).Remove(position, count));
}
[LuaMethodExample("if ( bizstring.contains( \"Some string\", \"Some\") ) then\r\n\tconsole.log( \"Returns whether or not str contains str2\" );\r\nend;")]
[LuaMethod("contains", "Returns whether or not str contains str2")]
public static bool Contains(string str, string str2)
{
return !string.IsNullOrEmpty(str) && str.Contains(str2);
}
public static bool Contains([LuaArbitraryStringParam] string str, [LuaArbitraryStringParam] string str2)
=> !string.IsNullOrEmpty(str) && str.Contains(str2); // don't bother fixing encoding, will match (or not match) regardless
[LuaMethodExample("if ( bizstring.startswith( \"Some string\", \"Some\") ) then\r\n\tconsole.log( \"Returns whether str starts with str2\" );\r\nend;")]
[LuaMethod("startswith", "Returns whether str starts with str2")]
public static bool StartsWith(string str, string str2)
{
return !string.IsNullOrEmpty(str) && str.StartsWith(str2);
}
public static bool StartsWith([LuaArbitraryStringParam] string str, [LuaArbitraryStringParam] string str2)
=> !string.IsNullOrEmpty(str) && str.StartsWith(str2); // don't bother fixing encoding, will match (or not match) regardless
[LuaMethodExample("if ( bizstring.endswith( \"Some string\", \"string\") ) then\r\n\tconsole.log( \"Returns whether str ends wth str2\" );\r\nend;")]
[LuaMethod("endswith", "Returns whether str ends wth str2")]
public static bool EndsWith(string str, string str2)
{
return !string.IsNullOrEmpty(str) && str.EndsWith(str2);
}
public static bool EndsWith([LuaArbitraryStringParam] string str, [LuaArbitraryStringParam] string str2)
=> !string.IsNullOrEmpty(str) && str.EndsWith(str2); // don't bother fixing encoding, will match (or not match) regardless
[LuaMethodExample("local nlbizspl = bizstring.split( \"Some, string\", \", \" );")]
[LuaMethod("split", "Splits str into a Lua-style array using the given separator (consecutive separators in str will NOT create empty entries in the array). If the separator is not a string exactly one char long, ',' will be used.")]
public LuaTable Split(string str, string separator)
=> string.IsNullOrEmpty(str)
[return: LuaArbitraryStringParam]
public LuaTable Split([LuaArbitraryStringParam] string str, [LuaArbitraryStringParam] string separator)
{
static char SingleOrElse(string s, char defaultValue)
=> s?.Length == 1 ? s[0] : defaultValue;
return string.IsNullOrEmpty(str)
? _th.CreateTable()
: _th.ListToTable(str.Split(new[] { separator?.Length == 1 ? separator[0] : ',' }, StringSplitOptions.RemoveEmptyEntries));
: _th.ListToTable(FixString(str).Split(new[] { SingleOrElse(FixString(separator), ',') }, StringSplitOptions.RemoveEmptyEntries)
.Select(static s => UnFixString(s)).ToList());
}
}
}

View File

@ -1,12 +1,29 @@
using System;
using System.Drawing;
using System.Globalization;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using System.Threading;
using BizHawk.Common;
using BizHawk.Common.StringExtensions;
namespace BizHawk.Client.Common
{
public abstract class LuaLibraryBase
{
[return: NotNullIfNotNull("s")]
public static string FixString(string s)
=> s is null
? null
: Encoding.UTF8.GetString(OSTailoredCode.IsUnixHost ? s.ToCharCodepointArray() : Encoding.Default.GetBytes(s)); // default is CP-1252 on Win10 in English, CP-1251 in Russian, and probably other things elsewhere, but that's what Lua is sending us so ¯\_(ツ)_/¯
[return: NotNullIfNotNull("s")]
public static string UnFixString(string s)
=> s is null
? null
: OSTailoredCode.IsUnixHost
? StringExtensions.CharCodepointsToString(Encoding.UTF8.GetBytes(s))
: Encoding.Default.GetString(Encoding.UTF8.GetBytes(s));
public PathEntryCollection PathEntries { get; set; }
protected LuaLibraryBase(IPlatformLuaLibEnv luaLibsImpl, ApiContainer apiContainer, Action<string> logOutputCallback)

View File

@ -28,6 +28,7 @@ namespace BizHawk.Client.EmuHawk
[LuaMethodExample("local stconget = console.getluafunctionslist( );")]
[LuaMethod("getluafunctionslist", "returns a list of implemented functions")]
[return: LuaASCIIStringParam]
public string GetLuaFunctionsList()
{
var list = new StringBuilder();
@ -41,29 +42,29 @@ namespace BizHawk.Client.EmuHawk
[LuaMethodExample("console.log( \"New log.\" );")]
[LuaMethod("log", "Outputs the given object to the output box on the Lua Console dialog. Note: Can accept a LuaTable")]
public void Log(params object[] outputs)
public void Log([LuaArbitraryStringParam] params object[] outputs)
{
LogWithSeparator("\t", "\n", outputs);
}
[LuaMethodExample("console.writeline( \"New log line.\" );")]
[LuaMethod("writeline", "Outputs the given object to the output box on the Lua Console dialog. Note: Can accept a LuaTable")]
public void WriteLine(params object[] outputs)
public void WriteLine([LuaArbitraryStringParam] params object[] outputs)
{
LogWithSeparator("\n", "\n", outputs);
}
[LuaMethodExample("console.write( \"New log message.\" );")]
[LuaMethod("write", "Outputs the given object to the output box on the Lua Console dialog. Note: Can accept a LuaTable")]
public void Write(params object[] outputs)
public void Write([LuaArbitraryStringParam] params object[] outputs)
{
LogWithSeparator("", "", outputs);
}
// Outputs the given object to the output box on the Lua Console dialog. Note: Can accept a LuaTable
private void LogWithSeparator(string separator, string terminator, params object[] outputs)
private void LogWithSeparator(string separator, string terminator, [LuaArbitraryStringParam] params object[] outputs)
{
static string SerializeTable(LuaTable lti)
static string SerializeTable([LuaArbitraryStringParam] LuaTable lti)
{
var keyObjs = lti.Keys;
var valueObjs = lti.Values;
@ -80,9 +81,8 @@ namespace BizHawk.Client.EmuHawk
}
return string.Concat(keyObjs.Cast<object>()
.Select((kObj, i) => $"\"{kObj}\": \"{values[i]}\"\n")
.OrderBy(s => s)
);
.Select((kObj, i) => $"\"{(kObj is string s ? FixString(s) : kObj.ToString())}\": \"{(values[i] is string s1 ? FixString(s1) : values[i].ToString())}\"\n")
.OrderBy(static s => s));
}
if (!Tools.Has<LuaConsole>())
@ -92,11 +92,14 @@ namespace BizHawk.Client.EmuHawk
var sb = new StringBuilder();
void SerializeAndWrite(object output) => sb.Append(
output is LuaTable table
? SerializeTable(table)
: output?.ToString() ?? "nil"
);
void SerializeAndWrite([LuaArbitraryStringParam] object output)
=> sb.Append(output switch
{
null => "nil",
LuaTable table => SerializeTable(table),
string s => FixString(s),
_ => output.ToString()
});
if (outputs == null)
{

View File

@ -39,10 +39,8 @@ namespace BizHawk.Client.EmuHawk
private static void SetSize(Control control, int width, int height)
=> control.Size = UIHelper.Scale(new Size(width, height));
private static void SetText(Control control, string caption)
{
control.Text = caption ?? "";
}
private static void SetText(Control control, [LuaArbitraryStringParam] string caption)
=> control.Text = FixString(caption) ?? string.Empty;
[LuaMethodExample("forms.addclick( 332, function()\r\n\tconsole.log( \"adds the given lua function as a click event to the given control\" );\r\nend );")]
[LuaMethod("addclick", "adds the given lua function as a click event to the given control")]
@ -66,7 +64,7 @@ namespace BizHawk.Client.EmuHawk
"button", "Creates a button control on the given form. The caption property will be the text value on the button. clickEvent is the name of a Lua function that will be invoked when the button is clicked. x, and y are the optional location parameters for the position of the button within the given form. The function returns the handle of the created button. Width and Height are optional, if not specified they will be a default size")]
public int Button(
int formHandle,
string caption,
[LuaArbitraryStringParam] string caption,
LuaFunction clickEvent,
int? x = null,
int? y = null,
@ -100,7 +98,7 @@ namespace BizHawk.Client.EmuHawk
[LuaMethodExample("local inforche = forms.checkbox( 333, \"Caption\", 2, 48 );")]
[LuaMethod(
"checkbox", "Creates a checkbox control on the given form. The caption property will be the text of the checkbox. x and y are the optional location parameters for the position of the checkbox within the form")]
public int Checkbox(int formHandle, string caption, int? x = null, int? y = null)
public int Checkbox(int formHandle, [LuaArbitraryStringParam] string caption, int? x = null, int? y = null)
{
var form = GetForm(formHandle);
if (form == null)
@ -170,7 +168,7 @@ namespace BizHawk.Client.EmuHawk
"dropdown", "Creates a dropdown (with a ComboBoxStyle of DropDownList) control on the given form. Dropdown items are passed via a lua table. Only the values will be pulled for the dropdown items, the keys are irrelevant. Items will be sorted alphabetically. x and y are the optional location parameters, and width and height are the optional size parameters.")]
public int Dropdown(
int formHandle,
LuaTable items,
[LuaArbitraryStringParam] LuaTable items,
int? x = null,
int? y = null,
int? width = null,
@ -182,7 +180,7 @@ namespace BizHawk.Client.EmuHawk
return 0;
}
var dropdownItems = _th.EnumerateValues<string>(items).ToList();
var dropdownItems = _th.EnumerateValues<string>(items).Select(FixString).ToList();
dropdownItems.Sort();
var dropdown = new LuaDropDown(dropdownItems);
@ -203,7 +201,8 @@ namespace BizHawk.Client.EmuHawk
[LuaMethodExample("local stforget = forms.getproperty(332, \"Property\");")]
[LuaMethod("getproperty", "returns a string representation of the value of a property of the widget at the given handle")]
public string GetProperty(int handle, string property)
[return: LuaArbitraryStringParam]
public string GetProperty(int handle, [LuaASCIIStringParam] string property)
{
try
{
@ -212,14 +211,14 @@ namespace BizHawk.Client.EmuHawk
{
if (form.Handle == ptr)
{
return form.GetType().GetProperty(property).GetValue(form, null).ToString();
return UnFixString(form.GetType().GetProperty(property).GetValue(form, null).ToString());
}
foreach (Control control in form.Controls)
{
if (control.Handle == ptr)
{
return control.GetType().GetProperty(property).GetValue(control, null).ToString();
return UnFixString(control.GetType().GetProperty(property).GetValue(control, null).ToString());
}
}
}
@ -234,6 +233,7 @@ namespace BizHawk.Client.EmuHawk
[LuaMethodExample("local stforget = forms.gettext(332);")]
[LuaMethod("gettext", "Returns the text property of a given form or control")]
[return: LuaArbitraryStringParam]
public string GetText(int handle)
{
try
@ -243,19 +243,14 @@ namespace BizHawk.Client.EmuHawk
{
if (form.Handle == ptr)
{
return form.Text;
return UnFixString(form.Text);
}
foreach (Control control in form.Controls)
{
if (control.Handle == ptr)
{
if (control is LuaDropDown dd)
{
return dd.SelectedItem.ToString();
}
return control.Text;
return UnFixString(control is LuaDropDown dd ? dd.SelectedItem.ToString() : control.Text);
}
}
}
@ -302,7 +297,7 @@ namespace BizHawk.Client.EmuHawk
"label", "Creates a label control on the given form. The caption property is the text of the label. x, and y are the optional location parameters for the position of the label within the given form. The function returns the handle of the created label. Width and Height are optional, if not specified they will be a default size.")]
public int Label(
int formHandle,
string caption,
[LuaArbitraryStringParam] string caption,
int? x = null,
int? y = null,
int? width = null,
@ -340,7 +335,11 @@ namespace BizHawk.Client.EmuHawk
[LuaMethodExample("local infornew = forms.newform( 18, 24, \"Title\", function()\r\n\tconsole.log( \"creates a new default dialog, if both width and height are specified it will create a dialog of the specified size. If title is specified it will be the caption of the dialog, else the dialog caption will be 'Lua Dialog'. The function will return an int representing the handle of the dialog created.\" );\r\nend );")]
[LuaMethod(
"newform", "creates a new default dialog, if both width and height are specified it will create a dialog of the specified size. If title is specified it will be the caption of the dialog, else the dialog caption will be 'Lua Dialog'. The function will return an int representing the handle of the dialog created.")]
public int NewForm(int? width = null, int? height = null, string title = null, LuaFunction onClose = null)
public int NewForm(
int? width = null,
int? height = null,
[LuaArbitraryStringParam] string title = null,
LuaFunction onClose = null)
{
var form = new LuaWinform(CurrentFile, WindowClosed);
_luaForms.Add(form);
@ -349,7 +348,7 @@ namespace BizHawk.Client.EmuHawk
form.ClientSize = UIHelper.Scale(new Size(width.Value, height.Value));
}
form.Text = title;
SetText(form, title);
form.MaximizeBox = false;
form.FormBorderStyle = FormBorderStyle.FixedDialog;
form.Icon = SystemIcons.Application;
@ -376,25 +375,22 @@ namespace BizHawk.Client.EmuHawk
[LuaMethodExample(@"local filename = forms.openfile(""C:\filename.bin"", ""C:\"", ""Raster Images (*.bmp;*.gif;*.jpg;*.png)|*.bmp;*.gif;*.jpg;*.png|All Files (*.*)|*.*"")")]
[LuaMethod(
"openfile", "Creates a standard openfile dialog with optional parameters for the filename, directory, and filter. The return value is the directory that the user picked. If they chose to cancel, it will return an empty string")]
public string OpenFile(string fileName = null, string initialDirectory = null, string filter = null)
[return: LuaArbitraryStringParam]
public string OpenFile(
[LuaArbitraryStringParam] string fileName = null,
[LuaArbitraryStringParam] string initialDirectory = null,
[LuaASCIIStringParam] string filter = null)
{
var openFileDialog1 = new OpenFileDialog();
if (initialDirectory != null)
{
openFileDialog1.InitialDirectory = initialDirectory;
}
if (fileName != null)
{
openFileDialog1.FileName = fileName;
}
if (initialDirectory is not null) openFileDialog1.InitialDirectory = FixString(initialDirectory);
if (fileName is not null) openFileDialog1.FileName = FixString(fileName);
openFileDialog1.AddExtension = true;
openFileDialog1.Filter = filter ?? FilesystemFilter.AllFilesEntry;
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
return openFileDialog1.FileName;
return UnFixString(openFileDialog1.FileName);
}
return "";
@ -689,11 +685,18 @@ namespace BizHawk.Client.EmuHawk
[LuaMethod(
"drawIcon",
"draws an Icon (.ico) file from the given path at the given coordinate. width and height are optional. If specified, it will resize the image accordingly")]
public void DrawIcon(int componentHandle, string path, int x, int y, int? width = null, int? height = null)
public void DrawIcon(
int componentHandle,
[LuaArbitraryStringParam] string path,
int x,
int y,
int? width = null,
int? height = null)
{
if (!File.Exists(path))
var path1 = FixString(path);
if (!File.Exists(path1))
{
LogOutputCallback($"File not found: {path}\nScript Terminated");
LogOutputCallback($"File not found: {path1}\nScript Terminated");
return;
}
try
@ -709,7 +712,7 @@ namespace BizHawk.Client.EmuHawk
foreach (var control in form.Controls.OfType<LuaPictureBox>())
{
control.DrawIcon(path, x, y, width, height);
control.DrawIcon(path1, x, y, width, height);
}
}
}
@ -725,16 +728,17 @@ namespace BizHawk.Client.EmuHawk
"draws an image file from the given path at the given coordinate. width and height are optional. If specified, it will resize the image accordingly")]
public void DrawImage(
int componentHandle,
string path,
[LuaArbitraryStringParam] string path,
int x,
int y,
int? width = null,
int? height = null,
bool cache = true)
{
if (!File.Exists(path))
var path1 = FixString(path);
if (!File.Exists(path1))
{
LogOutputCallback($"File not found: {path}\nScript Terminated");
LogOutputCallback($"File not found: {path1}\nScript Terminated");
return;
}
try
@ -750,7 +754,7 @@ namespace BizHawk.Client.EmuHawk
foreach (var control in form.Controls.OfType<LuaPictureBox>())
{
control.DrawImage(path, x, y, width, height, cache);
control.DrawImage(path1, x, y, width, height, cache);
}
}
}
@ -795,7 +799,7 @@ namespace BizHawk.Client.EmuHawk
"draws a given region of an image file from the given path at the given coordinate, and optionally with the given size")]
public void DrawImageRegion(
int componentHandle,
string path,
[LuaArbitraryStringParam] string path,
int source_x,
int source_y,
int source_width,
@ -805,9 +809,10 @@ namespace BizHawk.Client.EmuHawk
int? dest_width = null,
int? dest_height = null)
{
if (!File.Exists(path))
var path1 = FixString(path);
if (!File.Exists(path1))
{
LogOutputCallback($"File not found: {path}\nScript Terminated");
LogOutputCallback($"File not found: {path1}\nScript Terminated");
return;
}
try
@ -823,7 +828,7 @@ namespace BizHawk.Client.EmuHawk
foreach (var control in form.Controls.OfType<LuaPictureBox>())
{
control.DrawImageRegion(path, source_x, source_y, source_width, source_height, dest_x, dest_y, dest_width, dest_height);
control.DrawImageRegion(path1, source_x, source_y, source_width, source_height, dest_x, dest_y, dest_width, dest_height);
}
}
}
@ -1086,14 +1091,14 @@ namespace BizHawk.Client.EmuHawk
int componentHandle,
int x,
int y,
string message,
[LuaArbitraryStringParam] string message,
[LuaColorParam] object forecolor = null,
[LuaColorParam] object backcolor = null,
int? fontsize = null,
string fontfamily = null,
string fontstyle = null,
string horizalign = null,
string vertalign = null)
[LuaASCIIStringParam] string fontfamily = null,
[LuaEnumStringParam] string fontstyle = null,
[LuaEnumStringParam] string horizalign = null,
[LuaEnumStringParam] string vertalign = null)
{
try
{
@ -1110,7 +1115,7 @@ namespace BizHawk.Client.EmuHawk
foreach (var control in form.Controls.OfType<LuaPictureBox>())
{
control.DrawText(x, y, message, fgColor, bgColor, fontsize, fontfamily, fontstyle, horizalign, vertalign);
control.DrawText(x, y, FixString(message), fgColor, bgColor, fontsize, fontfamily, fontstyle, horizalign, vertalign);
}
}
}
@ -1128,14 +1133,14 @@ namespace BizHawk.Client.EmuHawk
int componentHandle,
int x,
int y,
string message,
[LuaArbitraryStringParam] string message,
[LuaColorParam] object forecolor = null,
[LuaColorParam] object backcolor = null,
int? fontsize = null,
string fontfamily = null,
string fontstyle = null,
string horizalign = null,
string vertalign = null)
[LuaASCIIStringParam] string fontfamily = null,
[LuaEnumStringParam] string fontstyle = null,
[LuaEnumStringParam] string horizalign = null,
[LuaEnumStringParam] string vertalign = null)
{
try
{
@ -1152,7 +1157,7 @@ namespace BizHawk.Client.EmuHawk
foreach (var control in form.Controls.OfType<LuaPictureBox>())
{
control.DrawText(x, y, message, fgColor, bgColor, fontsize, fontfamily, fontstyle, horizalign, vertalign);
control.DrawText(x, y, FixString(message), fgColor, bgColor, fontsize, fontfamily, fontstyle, horizalign, vertalign);
}
}
}
@ -1227,7 +1232,7 @@ namespace BizHawk.Client.EmuHawk
[LuaMethodExample("forms.setdropdownitems(dropdown_handle, { \"item1\", \"item2\" });")]
[LuaMethod("setdropdownitems", "Updates the item list of a dropdown menu. The optional third parameter toggles alphabetical sorting of items, pass false to skip sorting.")]
public void SetDropdownItems(int handle, LuaTable items, bool alphabetize = true)
public void SetDropdownItems(int handle, [LuaArbitraryStringParam] LuaTable items, bool alphabetize = true)
{
try
{
@ -1245,7 +1250,7 @@ namespace BizHawk.Client.EmuHawk
{
if (control is LuaDropDown ldd)
{
var dropdownItems = _th.EnumerateValues<string>(items).ToList();
var dropdownItems = _th.EnumerateValues<string>(items).Select(FixString).ToList();
if (alphabetize) dropdownItems.Sort();
ldd.SetItems(dropdownItems);
}
@ -1290,7 +1295,7 @@ namespace BizHawk.Client.EmuHawk
/// </exception>
[LuaMethodExample("forms.setproperty( 332, \"Property\", \"Property value\" );")]
[LuaMethod("setproperty", "Attempts to set the given property of the widget with the given value. Note: not all properties will be able to be represented for the control to accept")]
public void SetProperty(int handle, string property, object value)
public void SetProperty(int handle, [LuaASCIIStringParam] string property, object value)
{
// relying on exceptions for error handling here
void ParseAndSet(Control c)
@ -1357,7 +1362,7 @@ namespace BizHawk.Client.EmuHawk
[LuaMethodExample("forms.settext( 332, \"Caption\" );")]
[LuaMethod("settext", "Sets the text property of a control or form by passing in the handle of the created object")]
public void Settext(int handle, string caption)
public void Settext(int handle, [LuaArbitraryStringParam] string caption)
{
var ptr = new IntPtr(handle);
foreach (var form in _luaForms)
@ -1384,15 +1389,15 @@ namespace BizHawk.Client.EmuHawk
"textbox", "Creates a textbox control on the given form. The caption property will be the initial value of the textbox (default is empty). Width and Height are option, if not specified they will be a default size of 100, 20. Type is an optional property to restrict the textbox input. The available options are HEX, SIGNED, and UNSIGNED. Passing it null or any other value will set it to no restriction. x, and y are the optional location parameters for the position of the textbox within the given form. The function returns the handle of the created textbox. If true, the multiline will enable the standard winform multi-line property. If true, the fixedWidth options will create a fixed width font. Scrollbars is an optional property to specify which scrollbars to display. The available options are Vertical, Horizontal, Both, and None. Scrollbars are only shown on a multiline textbox")]
public int Textbox(
int formHandle,
string caption = null,
[LuaArbitraryStringParam] string caption = null,
int? width = null,
int? height = null,
string boxtype = null,
[LuaEnumStringParam] string boxtype = null,
int? x = null,
int? y = null,
bool multiline = false,
bool fixedWidth = false,
string scrollbars = null)
[LuaEnumStringParam] string scrollbars = null)
{
var form = GetForm(formHandle);
if (form == null)

View File

@ -172,7 +172,7 @@ namespace BizHawk.Client.EmuHawk
[LuaMethodExample("")]
[LuaMethod("submitinputchange", "")]
public void SubmitInputChange(int frame, string button, bool value)
public void SubmitInputChange(int frame, [LuaASCIIStringParam] string button, bool value)
{
if (Engaged())
{
@ -209,7 +209,7 @@ namespace BizHawk.Client.EmuHawk
[LuaMethodExample("")]
[LuaMethod("submitanalogchange", "")]
public void SubmitAnalogChange(int frame, string button, float value)
public void SubmitAnalogChange(int frame, [LuaASCIIStringParam] string button, float value)
{
if (Engaged())
{
@ -348,34 +348,36 @@ namespace BizHawk.Client.EmuHawk
}
[LuaMethod("addcolumn", "")]
public void AddColumn(string name, string text, int width)
public void AddColumn([LuaArbitraryStringParam] string name, [LuaArbitraryStringParam] string text, int width)
{
if (Engaged())
{
Tastudio.AddColumn(name, text, width, ColumnType.Text);
Tastudio.AddColumn(FixString(name), FixString(text), width, ColumnType.Text);
}
}
[LuaMethodExample("tastudio.setbranchtext( \"Some text\", 1 );")]
[LuaMethod("setbranchtext", "adds the given message to the existing branch, or to the branch that will be created next if branch index is not specified")]
public void SetBranchText(string text, int? index = null)
public void SetBranchText([LuaArbitraryStringParam] string text, int? index = null)
{
var text1 = FixString(text);
if (index != null)
{
var branch = Tastudio.CurrentTasMovie.Branches[index.Value];
if (branch != null)
{
branch.UserText = text;
branch.UserText = text1;
}
}
else
{
Tastudio.CurrentTasMovie.Branches.NewBranchText = text;
Tastudio.CurrentTasMovie.Branches.NewBranchText = text1;
}
}
[LuaMethodExample("local nltasget = tastudio.getbranches( );")]
[LuaMethod("getbranches", "Returns a list of the current tastudio branches. Each entry will have the Id, Frame, and Text properties of the branch")]
[return: LuaArbitraryStringParam]
public LuaTable GetBranches()
{
if (!Engaged()) return _th.CreateTable();
@ -384,14 +386,15 @@ namespace BizHawk.Client.EmuHawk
{
Id = b.Uuid.ToString(),
b.Frame,
Text = b.UserText
Text = UnFixString(b.UserText)
}),
indexFrom: 0);
}
[LuaMethodExample("local nltasget = tastudio.getbranchinput( \"97021544-2454-4483-824f-47f75e7fcb6a\", 500 );")]
[LuaMethod("getbranchinput", "Gets the controller state of the given frame with the given branch identifier")]
public LuaTable GetBranchInput(string branchId, int frame)
[return: LuaASCIIStringParam]
public LuaTable GetBranchInput([LuaASCIIStringParam] string branchId, int frame)
{
var table = _th.CreateTable();
if (!Engaged()) return table;
@ -428,6 +431,7 @@ namespace BizHawk.Client.EmuHawk
[LuaMethodExample("local sttasget = tastudio.getmarker( 500 );")]
[LuaMethod("getmarker", "returns the marker text at the given frame, or an empty string if there is no marker for the given frame")]
[return: LuaArbitraryStringParam]
public string GetMarker(int frame)
{
if (Engaged())
@ -435,7 +439,7 @@ namespace BizHawk.Client.EmuHawk
var marker = Tastudio.CurrentTasMovie.Markers.Get(frame);
if (marker != null)
{
return marker.Message;
return UnFixString(marker.Message);
}
}
@ -459,18 +463,19 @@ namespace BizHawk.Client.EmuHawk
[LuaMethodExample("tastudio.setmarker( 500, \"Some message\" );")]
[LuaMethod("setmarker", "Adds or sets a marker at the given frame, with an optional message")]
public void SetMarker(int frame, string message = null)
public void SetMarker(int frame, [LuaArbitraryStringParam] string message = null)
{
if (Engaged())
{
var message1 = FixString(message);
var marker = Tastudio.CurrentTasMovie.Markers.Get(frame);
if (marker != null)
{
marker.Message = message;
marker.Message = message1;
}
else
{
Tastudio.CurrentTasMovie.Markers.Add(frame, message);
Tastudio.CurrentTasMovie.Markers.Add(frame, message1);
Tastudio.RefreshDialog();
}
}

View File

@ -85,10 +85,8 @@ namespace BizHawk.Client.EmuHawk
[LuaMethod(
"SetTitle",
"Sets the canvas window title")]
public void SetTitle(string title)
{
Text = title;
}
public void SetTitle([LuaArbitraryStringParam] string title)
=> Text = LuaLibraryBase.FixString(title);
[LuaMethodExample(
"LuaCanvas.SetLocation( 16, 32 );")]
@ -220,11 +218,11 @@ namespace BizHawk.Client.EmuHawk
[LuaMethod(
"DrawIcon",
"draws an Icon (.ico) file from the given path at the given coordinate. width and height are optional. If specified, it will resize the image accordingly")]
public void DrawIcon(string path, int x, int y, int? width = null, int? height = null)
public void DrawIcon([LuaArbitraryStringParam] string path, int x, int y, int? width = null, int? height = null)
{
try
{
luaPictureBox.DrawIcon(path, x, y, width, height);
luaPictureBox.DrawIcon(LuaLibraryBase.FixString(path), x, y, width, height);
}
catch (Exception ex)
{
@ -237,15 +235,21 @@ namespace BizHawk.Client.EmuHawk
[LuaMethod(
"DrawImage",
"draws an image file from the given path at the given coordinate. width and height are optional. If specified, it will resize the image accordingly")]
public void DrawImage(string path, int x, int y, int? width = null, int? height = null, bool cache = true)
public void DrawImage(
[LuaArbitraryStringParam] string path,
int x,
int y,
int? width = null,
int? height = null,
bool cache = true)
{
if (!File.Exists(path))
var path1 = LuaLibraryBase.FixString(path);
if (!File.Exists(path1))
{
LogOutputCallback($"File not found: {path}\nScript Terminated");
LogOutputCallback($"File not found: {path1}\nScript Terminated");
return;
}
luaPictureBox.DrawImage(path, x, y, width, height, cache);
luaPictureBox.DrawImage(path1, x, y, width, height, cache);
}
[LuaMethodExample(
@ -264,7 +268,7 @@ namespace BizHawk.Client.EmuHawk
"DrawImageRegion",
"draws a given region of an image file from the given path at the given coordinate, and optionally with the given size")]
public void DrawImageRegion(
string path,
[LuaArbitraryStringParam] string path,
int sourceX,
int sourceY,
int sourceWidth,
@ -274,13 +278,13 @@ namespace BizHawk.Client.EmuHawk
int? destWidth = null,
int? destHeight = null)
{
if (!File.Exists(path))
var path1 = LuaLibraryBase.FixString(path);
if (!File.Exists(path1))
{
LogOutputCallback($"File not found: {path}\nScript Terminated");
LogOutputCallback($"File not found: {path1}\nScript Terminated");
return;
}
luaPictureBox.DrawImageRegion(path, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight);
luaPictureBox.DrawImageRegion(path1, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight);
}
[LuaMethodExample(
@ -403,16 +407,16 @@ namespace BizHawk.Client.EmuHawk
public void DrawString(
int x,
int y,
string message,
[LuaArbitraryStringParam] string message,
[LuaColorParam] object foreColor = null,
[LuaColorParam] object backColor = null,
int? fontSize = null,
string fontFamily = null,
string fontStyle = null,
string horizontalAlign = null,
string verticalAlign = null)
[LuaASCIIStringParam] string fontFamily = null,
[LuaEnumStringParam] string fontStyle = null,
[LuaEnumStringParam] string horizontalAlign = null,
[LuaEnumStringParam] string verticalAlign = null)
{
luaPictureBox.DrawText(x, y, message, _th.SafeParseColor(foreColor), _th.SafeParseColor(backColor), fontSize, fontFamily, fontStyle, horizontalAlign, verticalAlign);
luaPictureBox.DrawText(x, y, LuaLibraryBase.FixString(message), _th.SafeParseColor(foreColor), _th.SafeParseColor(backColor), fontSize, fontFamily, fontStyle, horizontalAlign, verticalAlign);
}
[LuaMethodExample(
@ -423,16 +427,16 @@ namespace BizHawk.Client.EmuHawk
public void DrawText(
int x,
int y,
string message,
[LuaArbitraryStringParam] string message,
[LuaColorParam] object foreColor = null,
[LuaColorParam] object backColor = null,
int? fontSize = null,
string fontFamily = null,
string fontStyle = null,
string horizontalAlign = null,
string verticalAlign = null)
[LuaASCIIStringParam] string fontFamily = null,
[LuaEnumStringParam] string fontStyle = null,
[LuaEnumStringParam] string horizontalAlign = null,
[LuaEnumStringParam] string verticalAlign = null)
{
luaPictureBox.DrawText(x, y, message, _th.SafeParseColor(foreColor), _th.SafeParseColor(backColor), fontSize, fontFamily, fontStyle, horizontalAlign, verticalAlign);
luaPictureBox.DrawText(x, y, LuaLibraryBase.FixString(message), _th.SafeParseColor(foreColor), _th.SafeParseColor(backColor), fontSize, fontFamily, fontStyle, horizontalAlign, verticalAlign);
}
@ -460,9 +464,9 @@ namespace BizHawk.Client.EmuHawk
}
[LuaMethod("save_image_to_disk", "Saves everything that's been drawn to a .png file at the given path. Relative paths are relative to the path set for \"Screenshots\" for the current system.")]
public void SaveImageToDisk(string path)
public void SaveImageToDisk([LuaArbitraryStringParam] string path)
{
luaPictureBox.Image.Save(path.MakeAbsolute(_emuLib.PathEntries.ScreenshotAbsolutePathFor(_emuLib.GetSystemId())));
luaPictureBox.Image.Save(LuaLibraryBase.FixString(path).MakeAbsolute(_emuLib.PathEntries.ScreenshotAbsolutePathFor(_emuLib.GetSystemId())));
}
}
}

View File

@ -36,7 +36,13 @@ namespace BizHawk.Client.EmuHawk
public void CallSaveStateEvent(string name) {}
public INamedLuaFunction CreateAndRegisterNamedFunction(LuaFunction function, string theEvent, Action<string> logCallback, LuaFile luaFile, string name = null) => null;
public INamedLuaFunction CreateAndRegisterNamedFunction(
LuaFunction function,
string theEvent,
Action<string> logCallback,
LuaFile luaFile,
[LuaArbitraryStringParam] string name = null)
=> null;
public NLuaTableHelper GetTableHelper() => null;

View File

@ -34,12 +34,26 @@ namespace BizHawk.Client.EmuHawk
var foundAttrs = method.GetCustomAttributes(typeof(LuaMethodAttribute), false);
if (foundAttrs.Length == 0) continue;
if (instance != null) _lua.RegisterFunction($"{name}.{((LuaMethodAttribute) foundAttrs[0]).Name}", instance, method);
Docs.Add(new LibraryFunction(
LibraryFunction libFunc = new(
name,
type.GetCustomAttributes(typeof(DescriptionAttribute), false).Cast<DescriptionAttribute>()
.Select(descAttr => descAttr.Description).FirstOrDefault() ?? string.Empty,
method
));
);
Docs.Add(libFunc);
#if DEBUG
// these don't catch object or LuaTable!
if (method.GetParameters().Any(static pi => pi.ParameterType == typeof(string)
&& !pi.CustomAttributes.Any(static a => typeof(LuaStringParamAttributeBase).IsAssignableFrom(a.AttributeType))))
{
Console.WriteLine($"Lua function {name}.{libFunc.Name} has an unclassified string param");
}
if (method.ReturnParameter!.ParameterType == typeof(string)
&& !method.ReturnParameter.CustomAttributes.Any(static a => typeof(LuaStringParamAttributeBase).IsAssignableFrom(a.AttributeType)))
{
Console.WriteLine($"Lua function {name}.{libFunc.Name} has an unclassified string return value");
}
#endif
}
}
@ -265,9 +279,14 @@ namespace BizHawk.Client.EmuHawk
_lua = new Lua();
}
public INamedLuaFunction CreateAndRegisterNamedFunction(LuaFunction function, string theEvent, Action<string> logCallback, LuaFile luaFile, string name = null)
public INamedLuaFunction CreateAndRegisterNamedFunction(
LuaFunction function,
string theEvent,
Action<string> logCallback,
LuaFile luaFile,
[LuaArbitraryStringParam] string name = null)
{
var nlf = new NamedLuaFunction(function, theEvent, logCallback, luaFile, name);
var nlf = new NamedLuaFunction(function, theEvent, logCallback, luaFile, LuaLibraryBase.FixString(name));
RegisteredFunctions.Add(nlf);
return nlf;
}

View File

@ -5,6 +5,13 @@ namespace BizHawk.Common.StringExtensions
{
public static class StringExtensions
{
public static string CharCodepointsToString(byte[] array)
{
var a = new char[array.Length];
for (var i = 0; i < array.Length; i++) a[i] = char.ConvertFromUtf32(array[i])[0];
return new(a);
}
public static bool Contains(this string haystack, string needle, StringComparison comparisonType)
=> haystack.IndexOf(needle, comparisonType) != -1;
@ -117,6 +124,13 @@ namespace BizHawk.Common.StringExtensions
return index < 0 ? null : str.Substring(0, index);
}
public static byte[] ToCharCodepointArray(this string str)
{
var a = new byte[str.Length];
for (var i = 0; i < str.Length; i++) a[i] = (byte) char.ConvertToUtf32(str, i);
return a;
}
/// <summary>
/// splits a given <paramref name="str"/> by <paramref name="delimiter"/>,
/// applies <paramref name="transform"/> to each part, then rejoins them