From 7c6f78b4618712791c996ec581103a08f6c1c2e5 Mon Sep 17 00:00:00 2001
From: James Groom <OSSYoshiRulz@gmail.com>
Date: Wed, 20 Oct 2021 23:57:39 +1000
Subject: [PATCH] Add `save_image_to_disk` to Lua canvases (#2973)

* Pass PathEntryCollection through to Lua libs

* Pass EmulationLuaLibrary to LuaCanvas, add save function to LuaCanvas

resolves #2744
---
 src/BizHawk.Client.Common/lua/IPlatformLuaLibEnv.cs  |  2 ++
 src/BizHawk.Client.Common/lua/LuaLibraryBase.cs      |  3 +++
 src/BizHawk.Client.EmuHawk/tools/Lua/LuaCanvas.cs    | 12 ++++++++++++
 .../tools/Lua/UnixLuaLibraries.cs                    |  5 ++++-
 .../tools/Lua/Win32LuaLibraries.cs                   |  7 ++++++-
 5 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/src/BizHawk.Client.Common/lua/IPlatformLuaLibEnv.cs b/src/BizHawk.Client.Common/lua/IPlatformLuaLibEnv.cs
index 0afca8ed86..ed52a78226 100644
--- a/src/BizHawk.Client.Common/lua/IPlatformLuaLibEnv.cs
+++ b/src/BizHawk.Client.Common/lua/IPlatformLuaLibEnv.cs
@@ -17,6 +17,8 @@ namespace BizHawk.Client.Common
 
 		LuaFunctionList RegisteredFunctions { get; }
 
+		public PathEntryCollection PathEntries { get; }
+
 		LuaFileList ScriptList { get; }
 
 		void CallLoadStateEvent(string name);
diff --git a/src/BizHawk.Client.Common/lua/LuaLibraryBase.cs b/src/BizHawk.Client.Common/lua/LuaLibraryBase.cs
index 15a6cda65f..ac08460865 100644
--- a/src/BizHawk.Client.Common/lua/LuaLibraryBase.cs
+++ b/src/BizHawk.Client.Common/lua/LuaLibraryBase.cs
@@ -7,12 +7,15 @@ namespace BizHawk.Client.Common
 {
 	public abstract class LuaLibraryBase
 	{
+		public PathEntryCollection PathEntries { get; set; }
+
 		protected LuaLibraryBase(IPlatformLuaLibEnv luaLibsImpl, ApiContainer apiContainer, Action<string> logOutputCallback)
 		{
 			LogOutputCallback = logOutputCallback;
 			_luaLibsImpl = luaLibsImpl;
 			_th = _luaLibsImpl.GetTableHelper();
 			APIs = apiContainer;
+			PathEntries = _luaLibsImpl.PathEntries;
 		}
 
 		protected static LuaFile CurrentFile { get; private set; }
diff --git a/src/BizHawk.Client.EmuHawk/tools/Lua/LuaCanvas.cs b/src/BizHawk.Client.EmuHawk/tools/Lua/LuaCanvas.cs
index 7ae518e62b..431653f992 100644
--- a/src/BizHawk.Client.EmuHawk/tools/Lua/LuaCanvas.cs
+++ b/src/BizHawk.Client.EmuHawk/tools/Lua/LuaCanvas.cs
@@ -5,6 +5,8 @@ using System.Windows.Forms;
 using System.IO;
 
 using BizHawk.Client.Common;
+using BizHawk.Common.PathExtensions;
+
 using NLua;
 
 // ReSharper disable UnusedMember.Global
@@ -13,6 +15,8 @@ namespace BizHawk.Client.EmuHawk
 	[Description("Represents a canvas object returned by the gui.createcanvas() method")]
 	public sealed class LuaCanvas : Form
 	{
+		private readonly EmulationLuaLibrary _emuLib;
+
 		private readonly NLuaTableHelper _th;
 
 		private readonly Action<string> LogOutputCallback;
@@ -20,6 +24,7 @@ namespace BizHawk.Client.EmuHawk
 		private readonly LuaPictureBox luaPictureBox;
 
 		public LuaCanvas(
+			EmulationLuaLibrary emuLib,
 			int width,
 			int height,
 			int? x,
@@ -27,6 +32,7 @@ namespace BizHawk.Client.EmuHawk
 			NLuaTableHelper tableHelper,
 			Action<string> logOutputCallback)
 		{
+			_emuLib = emuLib;
 			_th = tableHelper;
 			LogOutputCallback = logOutputCallback;
 
@@ -413,5 +419,11 @@ namespace BizHawk.Client.EmuHawk
 			var position = luaPictureBox.GetMouse();
 			return position.Y;
 		}
+
+		[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)
+		{
+			luaPictureBox.Image.Save(path.MakeAbsolute(_emuLib.PathEntries.ScreenshotAbsolutePathFor(_emuLib.GetSystemId())));
+		}
 	}
 }
diff --git a/src/BizHawk.Client.EmuHawk/tools/Lua/UnixLuaLibraries.cs b/src/BizHawk.Client.EmuHawk/tools/Lua/UnixLuaLibraries.cs
index ec6aeb7cce..74dc98ff84 100644
--- a/src/BizHawk.Client.EmuHawk/tools/Lua/UnixLuaLibraries.cs
+++ b/src/BizHawk.Client.EmuHawk/tools/Lua/UnixLuaLibraries.cs
@@ -21,10 +21,13 @@ namespace BizHawk.Client.EmuHawk
 
 		public LuaFunctionList RegisteredFunctions { get; }
 
+		public PathEntryCollection PathEntries { get; }
+
 		public LuaFileList ScriptList { get; }
 
-		public UnixLuaLibraries(LuaFileList scriptList, LuaFunctionList registeredFuncList)
+		public UnixLuaLibraries(LuaFileList scriptList, LuaFunctionList registeredFuncList, PathEntryCollection pathEntries)
 		{
+			PathEntries = pathEntries;
 			RegisteredFunctions = registeredFuncList;
 			ScriptList = scriptList;
 		}
diff --git a/src/BizHawk.Client.EmuHawk/tools/Lua/Win32LuaLibraries.cs b/src/BizHawk.Client.EmuHawk/tools/Lua/Win32LuaLibraries.cs
index 6c346f7ae7..6e4bed29d4 100644
--- a/src/BizHawk.Client.EmuHawk/tools/Lua/Win32LuaLibraries.cs
+++ b/src/BizHawk.Client.EmuHawk/tools/Lua/Win32LuaLibraries.cs
@@ -49,6 +49,7 @@ namespace BizHawk.Client.EmuHawk
 			_inputManager = inputManager;
 			_mainForm = mainForm;
 			LuaWait = new AutoResetEvent(false);
+			PathEntries = config.PathEntries;
 			RegisteredFunctions = registeredFuncList;
 			ScriptList = scriptList;
 			Docs.Clear();
@@ -83,9 +84,10 @@ namespace BizHawk.Client.EmuHawk
 					}
 					else if (instance is GuiLuaLibrary guiLib)
 					{
+						// emu lib may be null now, depending on order of ReflectionCache.Types, but definitely won't be null when this is called
 						guiLib.CreateLuaCanvasCallback = (width, height, x, y) =>
 						{
-							var canvas = new LuaCanvas(width, height, x, y, _th, LogToLuaConsole);
+							var canvas = new LuaCanvas(EmulationLuaLibrary, width, height, x, y, _th, LogToLuaConsole);
 							canvas.Show();
 							return _th.ObjectToTable(canvas);
 						};
@@ -141,6 +143,8 @@ namespace BizHawk.Client.EmuHawk
 
 		private EventWaitHandle LuaWait;
 
+		public PathEntryCollection PathEntries { get; private set; }
+
 		public LuaFileList ScriptList { get; }
 
 		private static void LogToLuaConsole(object outputs) => _logToLuaConsoleCallback(new[] { outputs });
@@ -154,6 +158,7 @@ namespace BizHawk.Client.EmuHawk
 			IGameInfo game)
 		{
 			_apiContainer = ApiManager.RestartLua(newServiceProvider, LogToLuaConsole, _mainForm, _displayManager, _inputManager, _mainForm.MovieSession, _mainForm.Tools, config, emulator, game);
+			PathEntries = config.PathEntries;
 			foreach (var lib in Libraries.Values)
 			{
 				lib.APIs = _apiContainer;