Merge pull request #1739 from TASVideos/lua-via-apihawk

Delegate Lua API to ApiHawk
This commit is contained in:
James Groom 2019-12-15 11:47:20 +00:00 committed by GitHub
commit 9162c8d246
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 679 additions and 1754 deletions

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace BizHawk.Client.Common
{
public sealed class APISubsetContainer : IApiContainer
public class APISubsetContainer : IApiContainer
{
public Dictionary<Type, IExternalApi> Libraries { get; set; }

View File

@ -18,6 +18,15 @@ namespace BizHawk.Client.Common
[Description("A library for interacting with the currently loaded emulator core")]
public sealed class EmuApi : IEmu
{
public EmuApi(Action<string> logCallback)
{
LogCallback = logCallback;
}
public EmuApi() : this(Console.WriteLine) {}
private readonly Action<string> LogCallback;
private static class EmuStatic
{
public static void DisplayVsync(bool enabled)
@ -37,8 +46,8 @@ namespace BizHawk.Client.Common
{
Global.Config.AutoMinimizeSkipping = enabled;
}
}
[RequiredService]
private IEmulator Emulator { get; set; }
@ -100,7 +109,7 @@ namespace BizHawk.Client.Common
}
catch (NotImplementedException)
{
Console.WriteLine($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDisassemblable.Disassemble)}()");
LogCallback($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDisassemblable.Disassemble)}()");
return null;
}
}
@ -121,7 +130,7 @@ namespace BizHawk.Client.Common
}
catch (NotImplementedException)
{
Console.WriteLine($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDebuggable.GetCpuFlagsAndRegisters)}()");
LogCallback($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDebuggable.GetCpuFlagsAndRegisters)}()");
return null;
}
}
@ -144,7 +153,7 @@ namespace BizHawk.Client.Common
}
catch (NotImplementedException)
{
Console.WriteLine($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDebuggable.GetCpuFlagsAndRegisters)}()");
LogCallback($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDebuggable.GetCpuFlagsAndRegisters)}()");
}
return table;
@ -163,7 +172,7 @@ namespace BizHawk.Client.Common
}
catch (NotImplementedException)
{
Console.WriteLine($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDebuggable.SetCpuRegister)}()");
LogCallback($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDebuggable.SetCpuRegister)}()");
}
}
@ -180,7 +189,7 @@ namespace BizHawk.Client.Common
}
catch (NotImplementedException)
{
Console.WriteLine($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDebuggable.TotalExecutedCycles)}()");
LogCallback($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDebuggable.TotalExecutedCycles)}()");
return 0;
}
@ -198,7 +207,7 @@ namespace BizHawk.Client.Common
return InputPollableCore.IsLagFrame;
}
Console.WriteLine($"Can not get lag information, {Emulator.Attributes().CoreName} does not implement {nameof(IInputPollable)}");
LogCallback($"Can not get lag information, {Emulator.Attributes().CoreName} does not implement {nameof(IInputPollable)}");
return false;
}
@ -210,7 +219,7 @@ namespace BizHawk.Client.Common
}
else
{
Console.WriteLine($"Can not set lag information, {Emulator.Attributes().CoreName} does not implement {nameof(IInputPollable)}");
LogCallback($"Can not set lag information, {Emulator.Attributes().CoreName} does not implement {nameof(IInputPollable)}");
}
}
@ -221,7 +230,7 @@ namespace BizHawk.Client.Common
return InputPollableCore.LagCount;
}
Console.WriteLine($"Can not get lag information, {Emulator.Attributes().CoreName} does not implement {nameof(IInputPollable)}");
LogCallback($"Can not get lag information, {Emulator.Attributes().CoreName} does not implement {nameof(IInputPollable)}");
return 0;
}
@ -233,7 +242,7 @@ namespace BizHawk.Client.Common
}
else
{
Console.WriteLine($"Can not set lag information, {Emulator.Attributes().CoreName} does not implement {nameof(IInputPollable)}");
LogCallback($"Can not set lag information, {Emulator.Attributes().CoreName} does not implement {nameof(IInputPollable)}");
}
}

View File

@ -7,6 +7,15 @@ namespace BizHawk.Client.Common
{
public sealed class JoypadApi : IJoypad
{
public JoypadApi(Action<string> logCallback)
{
LogCallback = logCallback;
}
public JoypadApi() : this(Console.WriteLine) {}
private readonly Action<string> LogCallback;
public Dictionary<string,dynamic> Get(int? controller = null)
{
var buttons = new Dictionary<string, dynamic>();
@ -42,7 +51,6 @@ namespace BizHawk.Client.Common
return buttons;
}
// TODO: what about float controls?
public Dictionary<string, dynamic> GetImmediate()
{
var buttons = new Dictionary<string, dynamic>();
@ -79,7 +87,7 @@ namespace BizHawk.Client.Common
}
catch (Exception)
{
Console.WriteLine($"invalid mnemonic string: {inputLogEntry}");
LogCallback($"invalid mnemonic string: {inputLogEntry}");
}
}

View File

@ -1,22 +1,24 @@
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
using BizHawk.Common.BufferExtensions;
namespace BizHawk.Client.Common
{
public sealed class MemApi : MemApiBase, IMem
public sealed class MemApi : IMem
{
private MemoryDomain _currentMemoryDomain;
private bool _isBigEndian;
public MemApi()
: base()
{
}
[RequiredService]
private IEmulator Emulator { get; set; }
protected override MemoryDomain Domain
[OptionalService]
private IMemoryDomains MemoryDomainCore { get; set; }
private MemoryDomain _currentMemoryDomain;
private MemoryDomain Domain
{
get
{
@ -33,11 +35,165 @@ namespace BizHawk.Client.Common
}
var error = $"Error: {Emulator.Attributes().CoreName} does not implement memory domains";
Console.WriteLine(error);
LogCallback(error);
throw new NotImplementedException(error);
}
}
private bool _isBigEndian;
private IMemoryDomains DomainList
{
get
{
if (MemoryDomainCore != null)
{
return MemoryDomainCore;
}
var error = $"Error: {Emulator.Attributes().CoreName} does not implement memory domains";
LogCallback(error);
throw new NotImplementedException(error);
}
}
public MemApi(Action<string> logCallback)
{
LogCallback = logCallback;
}
public MemApi() : this(Console.WriteLine) {}
private readonly Action<string> LogCallback;
private string VerifyMemoryDomain(string domain)
{
try
{
if (DomainList[domain] == null)
{
LogCallback($"Unable to find domain: {domain}, falling back to current");
return Domain.Name;
}
return domain;
}
catch // Just in case
{
LogCallback($"Unable to find domain: {domain}, falling back to current");
}
return Domain.Name;
}
private uint ReadUnsignedByte(long addr, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (addr < d.Size)
{
return d.PeekByte(addr);
}
LogCallback($"Warning: attempted read of {addr} outside the memory size of {d.Size}");
return 0;
}
private void WriteUnsignedByte(long addr, uint v, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (d.CanPoke())
{
if (addr < d.Size)
{
d.PokeByte(addr, (byte)v);
}
else
{
LogCallback($"Warning: attempted write to {addr} outside the memory size of {d.Size}");
}
}
else
{
LogCallback($"Error: the domain {d.Name} is not writable");
}
}
private static int U2S(uint u, int size)
{
var s = (int)u;
s <<= 8 * (4 - size);
s >>= 8 * (4 - size);
return s;
}
#region Endian Handling
private uint ReadUnsignedLittle(long addr, int size, string domain = null)
{
uint v = 0;
for (var i = 0; i < size; ++i)
{
v |= ReadUnsignedByte(addr + i, domain) << (8 * i);
}
return v;
}
private uint ReadUnsignedBig(long addr, int size, string domain = null)
{
uint v = 0;
for (var i = 0; i < size; ++i)
{
v |= ReadUnsignedByte(addr + i, domain) << (8 * (size - 1 - i));
}
return v;
}
private void WriteUnsignedLittle(long addr, uint v, int size, string domain = null)
{
for (var i = 0; i < size; ++i)
{
WriteUnsignedByte(addr + i, (v >> (8 * i)) & 0xFF, domain);
}
}
private void WriteUnsignedBig(long addr, uint v, int size, string domain = null)
{
for (var i = 0; i < size; ++i)
{
WriteUnsignedByte(addr + i, (v >> (8 * (size - 1 - i))) & 0xFF, domain);
}
}
private int ReadSigned(long addr, int size, string domain = null)
{
return _isBigEndian
? U2S(ReadUnsignedBig(addr, size, domain), size)
: U2S(ReadUnsignedLittle(addr, size, domain), size);
}
private uint ReadUnsigned(long addr, int size, string domain = null)
{
return _isBigEndian
? ReadUnsignedBig(addr, size, domain)
: ReadUnsignedLittle(addr, size, domain);
}
private void WriteSigned(long addr, int value, int size, string domain = null)
{
if (_isBigEndian) WriteUnsignedBig(addr, (uint)value, size, domain);
else WriteUnsignedLittle(addr, (uint)value, size, domain);
}
private void WriteUnsigned(long addr, uint value, int size, string domain = null)
{
if (_isBigEndian) WriteUnsignedBig(addr, value, size, domain);
else WriteUnsignedLittle(addr, value, size, domain);
}
#endregion
#region Unique Library Methods
public void SetBigEndian(bool enabled = true)
@ -87,12 +243,12 @@ namespace BizHawk.Client.Common
return true;
}
Console.WriteLine($"Unable to find domain: {domain}");
LogCallback($"Unable to find domain: {domain}");
return false;
}
catch // Just in case
{
Console.WriteLine($"Unable to find domain: {domain}");
LogCallback($"Unable to find domain: {domain}");
}
return false;
@ -106,13 +262,13 @@ namespace BizHawk.Client.Common
if (addr < 0 || addr >= d.Size)
{
string error = $"Address {addr} is outside the bounds of domain {d.Name}";
Console.WriteLine(error);
LogCallback(error);
throw new ArgumentOutOfRangeException(error);
}
if (addr + count > d.Size)
{
string error = $"Address {addr} + count {count} is outside the bounds of domain {d.Name}";
Console.WriteLine(error);
LogCallback(error);
throw new ArgumentOutOfRangeException(error);
}
@ -122,42 +278,12 @@ namespace BizHawk.Client.Common
data[i] = d.PeekByte(addr + i);
}
using var hasher = System.Security.Cryptography.SHA256.Create();
using var hasher = SHA256.Create();
return hasher.ComputeHash(data).BytesToHexString();
}
#endregion
#region Endian Handling
private int ReadSigned(long addr, int size, string domain = null)
{
return _isBigEndian
? ReadSignedBig(addr, size, domain)
: ReadSignedLittle(addr, size, domain);
}
private uint ReadUnsigned(long addr, int size, string domain = null)
{
return _isBigEndian
? ReadUnsignedBig(addr, size, domain)
: ReadUnsignedLittle(addr, size, domain);
}
private void WriteSigned(long addr, int value, int size, string domain = null)
{
if (_isBigEndian) WriteSignedBig(addr, value, size, domain);
else WriteSignedLittle(addr, value, size, domain);
}
private void WriteUnsigned(long addr, uint value, int size, string domain = null)
{
if (_isBigEndian) WriteUnsignedBig(addr, value, size, domain);
else WriteUnsignedLittle(addr, value, size, domain);
}
#endregion
#region Common Special and Legacy Methods
public uint ReadByte(long addr, string domain = null)
@ -170,24 +296,83 @@ namespace BizHawk.Client.Common
WriteUnsignedByte(addr, value, domain);
}
public new List<byte> ReadByteRange(long addr, int length, string domain = null)
public List<byte> ReadByteRange(long addr, int length, string domain = null)
{
return base.ReadByteRange(addr, length, domain);
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
var lastAddr = length + addr;
var list = new List<byte>();
for (; addr <= lastAddr; addr++)
{
if (addr < d.Size)
list.Add(d.PeekByte(addr));
else {
LogCallback($"Warning: Attempted read {addr} outside memory domain size of {d.Size} in {nameof(ReadByteRange)}()");
list.Add(0);
}
}
return list;
}
public new void WriteByteRange(long addr, List<byte> memoryblock, string domain = null)
public void WriteByteRange(long addr, List<byte> memoryblock, string domain = null)
{
base.WriteByteRange(addr, memoryblock, domain);
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (d.CanPoke())
{
foreach (var m in memoryblock)
{
if (addr < d.Size)
{
d.PokeByte(addr++, m);
}
else
{
LogCallback($"Warning: Attempted write {addr} outside memory domain size of {d.Size} in {nameof(WriteByteRange)}()");
}
}
}
else
{
LogCallback($"Error: the domain {d.Name} is not writable");
}
}
public float ReadFloat(long addr, string domain = null)
{
return base.ReadFloat(addr, _isBigEndian, domain);
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (addr < d.Size)
{
var val = d.PeekUint(addr, _isBigEndian);
var bytes = BitConverter.GetBytes(val);
return BitConverter.ToSingle(bytes, 0);
}
LogCallback($"Warning: Attempted read {addr} outside memory size of {d.Size}");
return 0;
}
public void WriteFloat(long addr, double value, string domain = null)
{
base.WriteFloat(addr, value, _isBigEndian, domain);
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (d.CanPoke())
{
if (addr < d.Size)
{
var dv = (float)value;
var bytes = BitConverter.GetBytes(dv);
var v = BitConverter.ToUInt32(bytes, 0);
d.PokeUint(addr, v, _isBigEndian);
}
else
{
LogCallback($"Warning: Attempted write {addr} outside memory size of {d.Size}");
}
}
else
{
LogCallback($"Error: the domain {Domain.Name} is not writable");
}
}
#endregion
@ -217,6 +402,7 @@ namespace BizHawk.Client.Common
#endregion
#region 2 Byte
public int ReadS16(long addr, string domain = null)
{
return (short)ReadSigned(addr, 2, domain);
@ -285,4 +471,4 @@ namespace BizHawk.Client.Common
#endregion
}
}
}

View File

@ -1,240 +0,0 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
namespace BizHawk.Client.Common
{
/// <summary>
/// Base class for the Memory and MainMemory plugin libraries
/// </summary>
public abstract class MemApiBase : IExternalApi
{
[RequiredService]
protected IEmulator Emulator { get; set; }
[OptionalService]
protected IMemoryDomains MemoryDomainCore { get; set; }
protected abstract MemoryDomain Domain { get; }
protected MemApiBase()
{ }
protected IMemoryDomains DomainList
{
get
{
if (MemoryDomainCore != null)
{
return MemoryDomainCore;
}
var error = $"Error: {Emulator.Attributes().CoreName} does not implement memory domains";
Console.WriteLine(error);
throw new NotImplementedException(error);
}
}
public string VerifyMemoryDomain(string domain)
{
try
{
if (DomainList[domain] == null)
{
Console.WriteLine($"Unable to find domain: {domain}, falling back to current");
return Domain.Name;
}
return domain;
}
catch // Just in case
{
Console.WriteLine($"Unable to find domain: {domain}, falling back to current");
}
return Domain.Name;
}
protected uint ReadUnsignedByte(long addr, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (addr < d.Size)
{
return d.PeekByte(addr);
}
Console.WriteLine($"Warning: attempted read of {addr} outside the memory size of {d.Size}");
return 0;
}
protected void WriteUnsignedByte(long addr, uint v, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (d.CanPoke())
{
if (addr < d.Size)
{
d.PokeByte(addr, (byte)v);
}
else
{
Console.WriteLine($"Warning: attempted write to {addr} outside the memory size of {d.Size}");
}
}
else
{
Console.WriteLine($"Error: the domain {d.Name} is not writable");
}
}
protected static int U2S(uint u, int size)
{
var s = (int)u;
s <<= 8 * (4 - size);
s >>= 8 * (4 - size);
return s;
}
protected int ReadSignedLittle(long addr, int size, string domain = null)
{
return U2S(ReadUnsignedLittle(addr, size, domain), size);
}
protected uint ReadUnsignedLittle(long addr, int size, string domain = null)
{
uint v = 0;
for (var i = 0; i < size; ++i)
{
v |= ReadUnsignedByte(addr + i, domain) << (8 * i);
}
return v;
}
protected int ReadSignedBig(long addr, int size, string domain = null)
{
return U2S(ReadUnsignedBig(addr, size, domain), size);
}
protected uint ReadUnsignedBig(long addr, int size, string domain = null)
{
uint v = 0;
for (var i = 0; i < size; ++i)
{
v |= ReadUnsignedByte(addr + i, domain) << (8 * (size - 1 - i));
}
return v;
}
protected void WriteSignedLittle(long addr, int v, int size, string domain = null)
{
WriteUnsignedLittle(addr, (uint)v, size, domain);
}
protected void WriteUnsignedLittle(long addr, uint v, int size, string domain = null)
{
for (var i = 0; i < size; ++i)
{
WriteUnsignedByte(addr + i, (v >> (8 * i)) & 0xFF, domain);
}
}
protected void WriteSignedBig(long addr, int v, int size, string domain = null)
{
WriteUnsignedBig(addr, (uint)v, size, domain);
}
protected void WriteUnsignedBig(long addr, uint v, int size, string domain = null)
{
for (var i = 0; i < size; ++i)
{
WriteUnsignedByte(addr + i, (v >> (8 * (size - 1 - i))) & 0xFF, domain);
}
}
#region public Library implementations
protected List<byte> ReadByteRange(long addr, int length, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
var lastAddr = length + addr;
var list = new List<byte>();
for (; addr <= lastAddr; addr++)
{
if (addr < d.Size)
list.Add(d.PeekByte(addr));
else {
Console.WriteLine($"Warning: Attempted read {addr} outside memory domain size of {d.Size} in {nameof(ReadByteRange)}()");
list.Add(0);
}
}
return list;
}
protected void WriteByteRange(long addr, List<byte> memoryblock, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (d.CanPoke())
{
foreach (var m in memoryblock)
{
if (addr < d.Size)
{
d.PokeByte(addr++, m);
}
else
{
Console.WriteLine($"Warning: Attempted write {addr} outside memory domain size of {d.Size} in {nameof(WriteByteRange)}()");
}
}
}
else
{
Console.WriteLine($"Error: the domain {d.Name} is not writable");
}
}
protected float ReadFloat(long addr, bool bigEndian, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (addr < d.Size)
{
var val = d.PeekUint(addr, bigEndian);
var bytes = BitConverter.GetBytes(val);
return BitConverter.ToSingle(bytes, 0);
}
Console.WriteLine($"Warning: Attempted read {addr} outside memory size of {d.Size}");
return 0;
}
protected void WriteFloat(long addr, double value, bool bigEndian, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (d.CanPoke())
{
if (addr < d.Size)
{
var dv = (float)value;
var bytes = BitConverter.GetBytes(dv);
var v = BitConverter.ToUInt32(bytes, 0);
d.PokeUint(addr, v, bigEndian);
}
else
{
Console.WriteLine($"Warning: Attempted write {addr} outside memory size of {d.Size}");
}
}
else
{
Console.WriteLine($"Error: the domain {Domain.Name} is not writable");
}
}
#endregion
}
}

View File

@ -8,8 +8,14 @@ namespace BizHawk.Client.Common
{
public sealed class MemorySaveStateApi : IMemorySaveState
{
public MemorySaveStateApi()
{ }
public MemorySaveStateApi(Action<string> logCallback)
{
LogCallback = logCallback;
}
public MemorySaveStateApi() : this(Console.WriteLine) {}
private readonly Action<string> LogCallback;
[RequiredService]
private IStatable StatableCore { get; set; }
@ -40,7 +46,7 @@ namespace BizHawk.Client.Common
}
catch
{
Console.WriteLine("Unable to find the given savestate in memory");
LogCallback("Unable to find the given savestate in memory");
}
}

View File

@ -8,6 +8,15 @@ namespace BizHawk.Client.Common
{
public sealed class MovieApi : IInputMovie
{
public MovieApi(Action<string> logCallback)
{
LogCallback = logCallback;
}
public MovieApi() : this(Console.WriteLine) {}
private readonly Action<string> LogCallback;
private static class MoviePluginStatic
{
public static string Filename()
@ -105,8 +114,6 @@ namespace BizHawk.Client.Common
}
}
public MovieApi()
{ }
public bool StartsFromSavestate()
{
@ -122,7 +129,7 @@ namespace BizHawk.Client.Common
{
if (!Global.MovieSession.Movie.IsActive)
{
Console.WriteLine("No movie loaded");
LogCallback("No movie loaded");
return null;
}
@ -131,7 +138,7 @@ namespace BizHawk.Client.Common
if (adapter == null)
{
Console.WriteLine("Can't get input of the last frame of the movie. Use the previous frame");
LogCallback("Can't get input of the last frame of the movie. Use the previous frame");
return null;
}
@ -173,7 +180,7 @@ namespace BizHawk.Client.Common
var test = new FileInfo(filename);
if (test.Exists)
{
Console.WriteLine($"File {filename} already exists, will not overwrite");
LogCallback($"File {filename} already exists, will not overwrite");
return;
}

View File

@ -2,9 +2,9 @@
{
public interface ISaveState : IExternalApi
{
void Load(string path);
void LoadSlot(int slotNum);
void Save(string path);
void SaveSlot(int slotNum);
void Load(string path, bool suppressOSD = false);
void LoadSlot(int slotNum, bool suppressOSD = false);
void Save(string path, bool suppressOSD = false);
void SaveSlot(int slotNum, bool suppressOSD = false);
}
}

View File

@ -91,7 +91,6 @@
<Compile Include="Api\Classes\GameInfoApi.cs" />
<Compile Include="Api\Classes\JoypadApi.cs" />
<Compile Include="Api\Classes\MemApi.cs" />
<Compile Include="Api\Classes\MemApiBase.cs" />
<Compile Include="Api\Classes\MemEventsApi.cs" />
<Compile Include="Api\Classes\MemorySaveStateApi.cs" />
<Compile Include="Api\Classes\MovieApi.cs" />
@ -171,7 +170,6 @@
<Compile Include="lua\LuaFunctionList.cs" />
<Compile Include="lua\LuaHelper.cs" />
<Compile Include="lua\LuaLibraryBase.cs" />
<Compile Include="lua\LuaMemoryBase.cs" />
<Compile Include="lua\LuaSandbox.cs" />
<Compile Include="lua\NamedLuaFunction.cs" />
<Compile Include="miniz\LibMiniz.cs" />

View File

@ -1,14 +1,6 @@
using System;
using System.ComponentModel;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
using BizHawk.Emulation.Cores.Nintendo.NES;
using BizHawk.Emulation.Cores.PCEngine;
using BizHawk.Emulation.Cores.Sega.MasterSystem;
using BizHawk.Emulation.Cores.WonderSwan;
using BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES;
using NLua;
// ReSharper disable UnusedMember.Global
@ -16,29 +8,8 @@ using NLua;
namespace BizHawk.Client.Common
{
[Description("A library for interacting with the currently loaded emulator core")]
public sealed class EmulatorLuaLibrary : LuaLibraryBase
public sealed class EmulatorLuaLibrary : DelegatingLuaLibrary
{
[RequiredService]
private IEmulator Emulator { get; set; }
[OptionalService]
private IDebuggable DebuggableCore { get; set; }
[OptionalService]
private IDisassemblable DisassemblableCore { get; set; }
[OptionalService]
private IMemoryDomains MemoryDomains { get; set; }
[OptionalService]
private IInputPollable InputPollableCore { get; set; }
[OptionalService]
private IRegionable RegionableCore { get; set; }
[OptionalService]
private IBoardInfo BoardInfo { get; set; }
public Action FrameAdvanceCallback { get; set; }
public Action YieldCallback { get; set; }
@ -52,10 +23,7 @@ namespace BizHawk.Client.Common
[LuaMethodExample("emu.displayvsync( true );")]
[LuaMethod("displayvsync", "Sets the display vsync property of the emulator")]
public static void DisplayVsync(bool enabled)
{
Global.Config.VSync = enabled;
}
public void DisplayVsync(bool enabled) => APIs.Emu.DisplayVsync(enabled);
[LuaMethodExample("emu.frameadvance( );")]
[LuaMethod("frameadvance", "Signals to the emulator to resume emulation. Necessary for any lua script while loop or else the emulator will freeze!")]
@ -66,269 +34,66 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local inemufra = emu.framecount( );")]
[LuaMethod("framecount", "Returns the current frame count")]
public int FrameCount()
{
return Emulator.Frame;
}
public int FrameCount() => APIs.Emu.FrameCount();
[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 = "")
{
try
{
if (DisassemblableCore == null)
{
throw new NotImplementedException();
}
int l;
MemoryDomain domain = MemoryDomains.SystemBus;
if (!string.IsNullOrEmpty(name))
{
domain = MemoryDomains[name];
}
var d = DisassemblableCore.Disassemble(domain, pc, out l);
return new { disasm = d, length = l };
}
catch (NotImplementedException)
{
Log($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDisassemblable.Disassemble)}()");
return null;
}
}
public object Disassemble(uint pc, string name = "") => APIs.Emu.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)
{
try
{
if (DebuggableCore == null)
{
throw new NotImplementedException();
}
var registers = DebuggableCore.GetCpuFlagsAndRegisters();
return registers.ContainsKey(name)
? (int)registers[name].Value
: 0;
}
catch (NotImplementedException)
{
Log($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDebuggable.GetCpuFlagsAndRegisters)}()");
return 0;
}
}
public int GetRegister(string name) => (int?) APIs.Emu.GetRegister(name) ?? 0;
[LuaMethodExample("local nlemuget = emu.getregisters( );")]
[LuaMethod("getregisters", "returns the complete set of available flags and registers for a given core")]
public LuaTable GetRegisters()
{
var result = APIs.Emu.GetRegisters();
var table = Lua.NewTable();
try
{
if (DebuggableCore == null)
{
throw new NotImplementedException();
}
foreach (var kvp in DebuggableCore.GetCpuFlagsAndRegisters())
{
table[kvp.Key] = kvp.Value.Value;
}
}
catch (NotImplementedException)
{
Log($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDebuggable.GetCpuFlagsAndRegisters)}()");
}
foreach (var kvp in result) table[kvp.Key] = kvp.Value;
return table;
}
[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)
{
try
{
if (DebuggableCore == null)
{
throw new NotImplementedException();
}
DebuggableCore.SetCpuRegister(register, value);
}
catch (NotImplementedException)
{
Log($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDebuggable.SetCpuRegister)}()");
}
}
public void SetRegister(string register, int value) => APIs.Emu.SetRegister(register, value);
[LuaMethodExample("local inemutot = emu.totalexecutedcycles( );")]
[LuaMethod("totalexecutedcycles", "gets the total number of executed cpu cycles")]
public long TotalExecutedycles()
{
try
{
if (DebuggableCore == null)
{
throw new NotImplementedException();
}
return DebuggableCore.TotalExecutedCycles;
}
catch (NotImplementedException)
{
Log($"Error: {Emulator.Attributes().CoreName} does not yet implement {nameof(IDebuggable.TotalExecutedCycles)}()");
return 0;
}
}
public long TotalExecutedycles() => APIs.Emu.TotalExecutedCycles();
[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")]
public static string GetSystemId()
{
return Global.Game.System;
}
public string GetSystemId() => APIs.Emu.GetSystemId();
[LuaMethodExample("if ( emu.islagged( ) ) then\r\n\tconsole.log( \"Returns whether or not the current frame is a lag frame\" );\r\nend;")]
[LuaMethod("islagged", "Returns whether or not the current frame is a lag frame")]
public bool IsLagged()
{
if (InputPollableCore != null)
{
return InputPollableCore.IsLagFrame;
}
Log($"Can not get lag information, {Emulator.Attributes().CoreName} does not implement {nameof(IInputPollable)}");
return false;
}
public bool IsLagged() => APIs.Emu.IsLagged();
[LuaMethodExample("emu.setislagged( true );")]
[LuaMethod("setislagged", "Sets the lag flag for the current frame. If no value is provided, it will default to true")]
public void SetIsLagged(bool value = true)
{
if (InputPollableCore != null)
{
InputPollableCore.IsLagFrame = value;
}
else
{
Log($"Can not set lag information, {Emulator.Attributes().CoreName} does not implement {nameof(IInputPollable)}");
}
}
public void SetIsLagged(bool value = true) => APIs.Emu.SetIsLagged(value);
[LuaMethodExample("local inemulag = emu.lagcount( );")]
[LuaMethod("lagcount", "Returns the current lag count")]
public int LagCount()
{
if (InputPollableCore != null)
{
return InputPollableCore.LagCount;
}
Log($"Can not get lag information, {Emulator.Attributes().CoreName} does not implement {nameof(IInputPollable)}");
return 0;
}
public int LagCount() => APIs.Emu.LagCount();
[LuaMethodExample("emu.setlagcount( 50 );")]
[LuaMethod("setlagcount", "Sets the current lag count")]
public void SetLagCount(int count)
{
if (InputPollableCore != null)
{
InputPollableCore.LagCount = count;
}
else
{
Log($"Can not set lag information, {Emulator.Attributes().CoreName} does not implement {nameof(IInputPollable)}");
}
}
public void SetLagCount(int count) => APIs.Emu.SetLagCount(count);
[LuaMethodExample("emu.limitframerate( true );")]
[LuaMethod("limitframerate", "sets the limit framerate property of the emulator")]
public static void LimitFramerate(bool enabled)
{
Global.Config.ClockThrottle = enabled;
}
public void LimitFramerate(bool enabled) => APIs.Emu.LimitFramerate(enabled);
[LuaMethodExample("emu.minimizeframeskip( true );")]
[LuaMethod("minimizeframeskip", "Sets the autominimizeframeskip value of the emulator")]
public static void MinimizeFrameskip(bool enabled)
{
Global.Config.AutoMinimizeSkipping = enabled;
}
public void MinimizeFrameskip(bool enabled) => APIs.Emu.MinimizeFrameskip(enabled);
[LuaMethodExample("emu.setrenderplanes( true, false );")]
[LuaMethod("setrenderplanes", "Toggles the drawing of sprites and background planes. Set to false or nil to disable a pane, anything else will draw them")]
public void SetRenderPlanes(params bool[] luaParam)
{
if (Emulator is NES nes)
{
// in the future, we could do something more arbitrary here.
// but this isn't any worse than the old system
var s = nes.GetSettings();
s.DispSprites = luaParam[0];
s.DispBackground = luaParam[1];
nes.PutSettings(s);
}
else if (Emulator is QuickNES quicknes)
{
var s = quicknes.GetSettings();
// this core doesn't support disabling BG
bool showSp = GetSetting(0, luaParam);
if (showSp && s.NumSprites == 0)
{
s.NumSprites = 8;
}
else if (!showSp && s.NumSprites > 0)
{
s.NumSprites = 0;
}
quicknes.PutSettings(s);
}
else if (Emulator is PCEngine pce)
{
var s = pce.GetSettings();
s.ShowOBJ1 = GetSetting(0, luaParam);
s.ShowBG1 = GetSetting(1, luaParam);
if (luaParam.Length > 2)
{
s.ShowOBJ2 = GetSetting(2, luaParam);
s.ShowBG2 = GetSetting(3, luaParam);
}
pce.PutSettings(s);
}
else if (Emulator is SMS sms)
{
var s = sms.GetSettings();
s.DispOBJ = GetSetting(0, luaParam);
s.DispBG = GetSetting(1, luaParam);
sms.PutSettings(s);
}
else if (Emulator is WonderSwan ws)
{
var s = ws.GetSettings();
s.EnableSprites = GetSetting(0, luaParam);
s.EnableFG = GetSetting(1, luaParam);
s.EnableBG = GetSetting(2, luaParam);
ws.PutSettings(s);
}
}
private static bool GetSetting(int index, bool[] settings)
{
return index >= settings.Length || settings[index];
}
public void SetRenderPlanes(params bool[] luaParam) => APIs.Emu.SetRenderPlanes(luaParam);
[LuaMethodExample("emu.yield( );")]
[LuaMethod("yield", "allows a script to run while emulation is paused and interact with the gui/main window in realtime ")]
@ -339,21 +104,11 @@ 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")]
public string GetDisplayType()
{
return RegionableCore != null
? RegionableCore.Region.ToString()
: "";
}
public string GetDisplayType() => APIs.Emu.GetDisplayType();
[LuaMethodExample("local stemuget = emu.getboardname();")]
[LuaMethod("getboardname", "returns (if available) the board name of the loaded ROM")]
public string GetBoardName()
{
return BoardInfo != null
? BoardInfo.BoardName
: "";
}
public string GetBoardName() => APIs.Emu.GetBoardName();
[LuaMethod("getluacore", "returns the name of the Lua core currently in use")]
public string GetLuaBackend()

View File

@ -7,11 +7,8 @@ using BizHawk.Emulation.Common;
// ReSharper disable UnusedAutoPropertyAccessor.Local
namespace BizHawk.Client.Common
{
public sealed class GameInfoLuaLibrary : LuaLibraryBase
public sealed class GameInfoLuaLibrary : DelegatingLuaLibrary
{
[OptionalService]
private IBoardInfo BoardInfo { get; set; }
public GameInfoLuaLibrary(Lua lua)
: base(lua) { }
@ -22,66 +19,36 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local stgamget = gameinfo.getromname( );")]
[LuaMethod("getromname", "returns the name of the currently loaded rom, if a rom is loaded")]
public string GetRomName()
{
return Global.Game?.Name ?? "";
}
public string GetRomName() => APIs.GameInfo.GetRomName();
[LuaMethodExample("local stgamget = gameinfo.getromhash( );")]
[LuaMethod("getromhash", "returns the hash of the currently loaded rom, if a rom is loaded")]
public string GetRomHash()
{
return Global.Game?.Hash ?? "";
}
public string GetRomHash() => APIs.GameInfo.GetRomHash();
[LuaMethodExample("if ( gameinfo.indatabase( ) ) then\r\n\tconsole.log( \"returns whether or not the currently loaded rom is in the game database\" );\r\nend;")]
[LuaMethod("indatabase", "returns whether or not the currently loaded rom is in the game database")]
public bool InDatabase()
{
if (Global.Game != null)
{
return !Global.Game.NotInDatabase;
}
return false;
}
public bool InDatabase() => APIs.GameInfo.InDatabase();
[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")]
public string GetStatus()
{
return Global.Game?.Status.ToString();
}
public string GetStatus() => APIs.GameInfo.GetStatus();
[LuaMethodExample("if ( gameinfo.isstatusbad( ) ) then\r\n\tconsole.log( \"returns the currently loaded rom's game database status is considered 'bad'\" );\r\nend;")]
[LuaMethod("isstatusbad", "returns the currently loaded rom's game database status is considered 'bad'")]
public bool IsStatusBad()
{
return Global.Game?.IsRomStatusBad() ?? true;
}
public bool IsStatusBad() => APIs.GameInfo.IsStatusBad();
[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")]
public string GetBoardType()
{
return BoardInfo?.BoardName ?? "";
}
public string GetBoardType() => APIs.GameInfo.GetBoardType();
[LuaMethodExample("local nlgamget = gameinfo.getoptions( );")]
[LuaMethod("getoptions", "returns the game options for the currently loaded rom. Options vary per platform")]
public LuaTable GetOptions()
{
var options = Lua.NewTable();
if (Global.Game != null)
{
foreach (var option in Global.Game.GetOptionsDict())
{
options[option.Key] = option.Value;
}
}
return options;
var dict = APIs.GameInfo.GetOptions();
var table = Lua.NewTable();
foreach (var kvp in dict) table[kvp.Key] = kvp.Value;
return table;
}
}
}

View File

@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using NLua;
// ReSharper disable UnusedMember.Global
namespace BizHawk.Client.Common
{
public sealed class JoypadLuaLibrary : LuaLibraryBase
public sealed class JoypadLuaLibrary : DelegatingLuaLibrary
{
public JoypadLuaLibrary(Lua lua)
: base(lua) { }
@ -18,169 +20,42 @@ namespace BizHawk.Client.Common
[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")]
public LuaTable Get(int? controller = null)
{
var buttons = Lua.NewTable();
var adapter = Global.AutofireStickyXORAdapter;
foreach (var button in adapter.Source.Definition.BoolButtons)
{
if (!controller.HasValue)
{
buttons[button] = adapter.IsPressed(button);
}
else if (button.Length >= 3 && button.Substring(0, 2) == $"P{controller}")
{
buttons[button.Substring(3)] = adapter.IsPressed($"P{controller} {button.Substring(3)}");
}
}
foreach (var button in adapter.Source.Definition.FloatControls)
{
if (controller == null)
{
buttons[button] = adapter.GetFloat(button);
}
else if (button.Length >= 3 && button.Substring(0, 2) == $"P{controller}")
{
buttons[button.Substring(3)] = adapter.GetFloat($"P{controller} {button.Substring(3)}");
}
}
buttons["clear"] = null;
buttons["getluafunctionslist"] = null;
buttons["output"] = null;
return buttons;
var result = APIs.Joypad.Get(controller);
var table = Lua.NewTable();
foreach (var kvp in result) table[kvp.Key] = kvp.Value;
return table;
}
// TODO: what about float controls?
[LuaMethodExample("local nljoyget = joypad.getimmediate( );")]
[LuaMethod("getimmediate", "returns a lua table of any controller buttons currently pressed by the user")]
public LuaTable GetImmediate()
{
var buttons = Lua.NewTable();
foreach (var button in Global.ActiveController.Definition.BoolButtons)
{
buttons[button] = Global.ActiveController.IsPressed(button);
}
return buttons;
var result = APIs.Joypad.GetImmediate();
var table = Lua.NewTable();
foreach (var kvp in result) table[kvp.Key] = kvp.Value;
return table;
}
[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 interpretted the same way an entry from a movie input log would be")]
public void SetFromMnemonicStr(string inputLogEntry)
{
try
{
var lg = Global.MovieSession.MovieControllerInstance();
lg.SetControllersAsMnemonic(inputLogEntry);
foreach (var button in lg.Definition.BoolButtons)
{
Global.LuaAndAdaptor.SetButton(button, lg.IsPressed(button));
}
foreach (var floatButton in lg.Definition.FloatControls)
{
Global.LuaAndAdaptor.SetFloat(floatButton, lg.GetFloat(floatButton));
}
}
catch (Exception)
{
Log($"invalid mnemonic string: {inputLogEntry}");
}
}
public void SetFromMnemonicStr(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)
{
try
{
foreach (var button in buttons.Keys)
{
var invert = false;
bool? theValue;
var theValueStr = buttons[button].ToString();
if (!string.IsNullOrWhiteSpace(theValueStr))
{
if (theValueStr.ToLower() == "false")
{
theValue = false;
}
else if (theValueStr.ToLower() == "true")
{
theValue = true;
}
else
{
invert = true;
theValue = null;
}
}
else
{
theValue = null;
}
var toPress = button.ToString();
if (controller.HasValue)
{
toPress = $"P{controller} {button}";
}
if (!invert)
{
if (theValue.HasValue) // Force
{
Global.LuaAndAdaptor.SetButton(toPress, theValue.Value);
Global.ActiveController.Overrides(Global.LuaAndAdaptor);
}
else // Unset
{
Global.LuaAndAdaptor.UnSet(toPress);
Global.ActiveController.Overrides(Global.LuaAndAdaptor);
}
}
else // Inverse
{
Global.LuaAndAdaptor.SetInverse(toPress);
Global.ActiveController.Overrides(Global.LuaAndAdaptor);
}
}
}
catch
{
/*Eat it*/
}
var dict = new Dictionary<string, bool>();
foreach (var k in buttons.Keys) dict[k.ToString()] = (bool) buttons[k];
APIs.Joypad.Set(dict, controller);
}
[LuaMethodExample("joypad.setanalog( { [ \"Tilt X\" ] = true, [ \"Tilt Y\" ] = false } );")]
[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, object controller = null)
{
try
{
foreach (var name in controls.Keys)
{
var theValueStr = controls[name].ToString();
float? theValue = null;
if (!string.IsNullOrWhiteSpace(theValueStr))
{
if (float.TryParse(theValueStr, out var f))
{
theValue = f;
}
}
Global.StickyXORAdapter.SetFloat(controller == null ? name.ToString() : $"P{controller} {name}", theValue);
}
}
catch
{
/*Eat it*/
}
var dict = new Dictionary<string, float>();
foreach (var k in controls.Keys) dict[k.ToString()] = (float) controls[k];
APIs.Joypad.SetAnalog(dict, controller);
}
}
}

View File

@ -1,16 +1,23 @@
using System;
using System.ComponentModel;
using NLua;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
using NLua;
// ReSharper disable UnusedMember.Global
namespace BizHawk.Client.Common
{
[Description("Main memory library reads and writes from the Main memory domain (the default memory domain set by any given core)")]
public sealed class MainMemoryLuaLibrary : LuaMemoryBase
public sealed class MainMemoryLuaLibrary : DelegatingLuaLibrary
{
[RequiredService]
private IEmulator Emulator { get; set; }
[OptionalService]
private IMemoryDomains MemoryDomainCore { get; set; }
public MainMemoryLuaLibrary(Lua lua)
: base(lua) { }
@ -19,7 +26,7 @@ namespace BizHawk.Client.Common
public override string Name => "mainmemory";
protected override MemoryDomain Domain
private MemoryDomain Domain
{
get
{
@ -58,44 +65,68 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local uimairea = mainmemory.readbyte( 0x100 );")]
[LuaMethod("readbyte", "gets the value from the given address as an unsigned byte")]
public uint ReadByte(int addr)
{
return ReadUnsignedByte(addr);
}
public uint ReadByte(int addr) => APIs.Mem.ReadByte(addr, Domain.Name);
[LuaMethodExample("mainmemory.writebyte( 0x100, 1000 );")]
[LuaMethod("writebyte", "Writes the given value to the given address as an unsigned byte")]
public void WriteByte(int addr, uint value)
{
WriteUnsignedByte(addr, value);
}
public void WriteByte(int addr, uint value) => APIs.Mem.WriteByte(addr, value, Domain.Name);
[LuaMethodExample("local nlmairea = mainmemory.readbyterange( 0x100, 64 );")]
[LuaMethod("readbyterange", "Reads the address range that starts from address, and is length long. Returns the result into a table of key value pairs (where the address is the key).")]
public LuaTable ReadByteRange(int addr, int length)
{
return base.ReadByteRange(addr, length);
var result = APIs.Mem.ReadByteRange(addr, length, Domain.Name);
var table = Lua.NewTable();
var count = result.Count;
for (var i = 0; i != count; i++) table[i] = result[i];
return table;
}
/// <remarks>TODO C# version requires a contiguous address range</remarks>
[LuaMethodExample("")]
[LuaMethod("writebyterange", "Writes the given values to the given addresses as unsigned bytes")]
public void WriteByteRange(LuaTable memoryblock)
{
base.WriteByteRange(memoryblock);
#if true
foreach (var addr in memoryblock.Keys) APIs.Mem.WriteByte(LuaInt(addr), (uint) memoryblock[addr], Domain.Name);
#else
var d = Domain;
if (d.CanPoke())
{
foreach (var address in memoryblock.Keys)
{
var addr = LuaInt(address);
if (addr < d.Size)
{
d.PokeByte(addr, (byte)LuaInt(memoryblock[address]));
}
else
{
Log($"Warning: Attempted write {addr} outside memory domain size of {d.Size} in writebyterange()");
}
}
}
else
{
Log($"Error: the domain {d.Name} is not writable");
}
#endif
}
[LuaMethodExample("local simairea = mainmemory.readfloat(0x100, false);")]
[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(int addr, bool bigendian)
{
return base.ReadFloat(addr, bigendian);
APIs.Mem.SetBigEndian(bigendian);
return APIs.Mem.ReadFloat(addr, Domain.Name);
}
[LuaMethodExample("mainmemory.writefloat( 0x100, 10.0, false );")]
[LuaMethod("writefloat", "Writes the given 32-bit float value to the given address and endian")]
public void WriteFloat(int addr, double value, bool bigendian)
{
base.WriteFloat(addr, value, bigendian);
APIs.Mem.SetBigEndian(bigendian);
APIs.Mem.WriteFloat(addr, value, Domain.Name);
}
#endregion
@ -104,31 +135,19 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local inmairea = mainmemory.read_s8( 0x100 );")]
[LuaMethod("read_s8", "read signed byte")]
public int ReadS8(int addr)
{
return (sbyte)ReadUnsignedByte(addr);
}
public int ReadS8(int addr) => APIs.Mem.ReadS8(addr, Domain.Name);
[LuaMethodExample("mainmemory.write_s8( 0x100, 1000 );")]
[LuaMethod("write_s8", "write signed byte")]
public void WriteS8(int addr, uint value)
{
WriteUnsignedByte(addr, value);
}
public void WriteS8(int addr, uint value) => APIs.Mem.WriteS8(addr, unchecked((int) value), Domain.Name);
[LuaMethodExample("local uimairea = mainmemory.read_u8( 0x100 );")]
[LuaMethod("read_u8", "read unsigned byte")]
public uint ReadU8(int addr)
{
return ReadUnsignedByte(addr);
}
public uint ReadU8(int addr) => APIs.Mem.ReadU8(addr, Domain.Name);
[LuaMethodExample("mainmemory.write_u8( 0x100, 1000 );")]
[LuaMethod("write_u8", "write unsigned byte")]
public void WriteU8(int addr, uint value)
{
WriteUnsignedByte(addr, value);
}
public void WriteU8(int addr, uint value) => APIs.Mem.WriteU8(addr, value, Domain.Name);
#endregion
@ -138,56 +157,64 @@ namespace BizHawk.Client.Common
[LuaMethod("read_s16_le", "read signed 2 byte value, little endian")]
public int ReadS16Little(int addr)
{
return ReadSignedLittleCore(addr, 2);
APIs.Mem.SetBigEndian(false);
return APIs.Mem.ReadS16(addr, Domain.Name);
}
[LuaMethodExample("mainmemory.write_s16_le( 0x100, -1000 );")]
[LuaMethod("write_s16_le", "write signed 2 byte value, little endian")]
public void WriteS16Little(int addr, int value)
{
WriteSignedLittle(addr, value, 2);
APIs.Mem.SetBigEndian(false);
APIs.Mem.WriteS16(addr, value, Domain.Name);
}
[LuaMethodExample("local inmairea = mainmemory.read_s16_be( 0x100 );")]
[LuaMethod("read_s16_be", "read signed 2 byte value, big endian")]
public int ReadS16Big(int addr)
{
return ReadSignedBig(addr, 2);
APIs.Mem.SetBigEndian();
return APIs.Mem.ReadS16(addr, Domain.Name);
}
[LuaMethodExample("mainmemory.write_s16_be( 0x100, -1000 );")]
[LuaMethod("write_s16_be", "write signed 2 byte value, big endian")]
public void WriteS16Big(int addr, int value)
{
WriteSignedBig(addr, value, 2);
APIs.Mem.SetBigEndian();
APIs.Mem.WriteS16(addr, value, Domain.Name);
}
[LuaMethodExample("local uimairea = mainmemory.read_u16_le( 0x100 );")]
[LuaMethod("read_u16_le", "read unsigned 2 byte value, little endian")]
public uint ReadU16Little(int addr)
{
return ReadSignedLittle(addr, 2);
APIs.Mem.SetBigEndian(false);
return APIs.Mem.ReadU16(addr, Domain.Name);
}
[LuaMethodExample("mainmemory.write_u16_le( 0x100, 1000 );")]
[LuaMethod("write_u16_le", "write unsigned 2 byte value, little endian")]
public void WriteU16Little(int addr, uint value)
{
WriteUnsignedLittle(addr, value, 2);
APIs.Mem.SetBigEndian(false);
APIs.Mem.WriteU16(addr, value, Domain.Name);
}
[LuaMethodExample("local uimairea = mainmemory.read_u16_be( 0x100 );")]
[LuaMethod("read_u16_be", "read unsigned 2 byte value, big endian")]
public uint ReadU16Big(int addr)
{
return ReadUnsignedBig(addr, 2);
APIs.Mem.SetBigEndian();
return APIs.Mem.ReadU16(addr, Domain.Name);
}
[LuaMethodExample("mainmemory.write_u16_be( 0x100, 1000 );")]
[LuaMethod("write_u16_be", "write unsigned 2 byte value, big endian")]
public void WriteU16Big(int addr, uint value)
{
WriteUnsignedBig(addr, value, 2);
APIs.Mem.SetBigEndian();
APIs.Mem.WriteU16(addr, value, Domain.Name);
}
#endregion
@ -198,56 +225,64 @@ namespace BizHawk.Client.Common
[LuaMethod("read_s24_le", "read signed 24 bit value, little endian")]
public int ReadS24Little(int addr)
{
return ReadSignedLittleCore(addr, 3);
APIs.Mem.SetBigEndian(false);
return APIs.Mem.ReadS24(addr, Domain.Name);
}
[LuaMethodExample("mainmemory.write_s24_le( 0x100, -1000 );")]
[LuaMethod("write_s24_le", "write signed 24 bit value, little endian")]
public void WriteS24Little(int addr, int value)
{
WriteSignedLittle(addr, value, 3);
APIs.Mem.SetBigEndian(false);
APIs.Mem.WriteS24(addr, value, Domain.Name);
}
[LuaMethodExample("local inmairea = mainmemory.read_s24_be( 0x100 );")]
[LuaMethod("read_s24_be", "read signed 24 bit value, big endian")]
public int ReadS24Big(int addr)
{
return ReadSignedBig(addr, 3);
APIs.Mem.SetBigEndian();
return APIs.Mem.ReadS24(addr, Domain.Name);
}
[LuaMethodExample("mainmemory.write_s24_be( 0x100, -1000 );")]
[LuaMethod("write_s24_be", "write signed 24 bit value, big endian")]
public void WriteS24Big(int addr, int value)
{
WriteSignedBig(addr, value, 3);
APIs.Mem.SetBigEndian();
APIs.Mem.WriteS24(addr, value, Domain.Name);
}
[LuaMethodExample("local uimairea = mainmemory.read_u24_le( 0x100 );")]
[LuaMethod("read_u24_le", "read unsigned 24 bit value, little endian")]
public uint ReadU24Little(int addr)
{
return ReadSignedLittle(addr, 3);
APIs.Mem.SetBigEndian(false);
return APIs.Mem.ReadU24(addr, Domain.Name);
}
[LuaMethodExample("mainmemory.write_u24_le( 0x100, 1000 );")]
[LuaMethod("write_u24_le", "write unsigned 24 bit value, little endian")]
public void WriteU24Little(int addr, uint value)
{
WriteUnsignedLittle(addr, value, 3);
APIs.Mem.SetBigEndian(false);
APIs.Mem.WriteU24(addr, value, Domain.Name);
}
[LuaMethodExample("local uimairea = mainmemory.read_u24_be( 0x100 );")]
[LuaMethod("read_u24_be", "read unsigned 24 bit value, big endian")]
public uint ReadU24Big(int addr)
{
return ReadUnsignedBig(addr, 3);
APIs.Mem.SetBigEndian();
return APIs.Mem.ReadU24(addr, Domain.Name);
}
[LuaMethodExample("mainmemory.write_u24_be( 0x100, 1000 );")]
[LuaMethod("write_u24_be", "write unsigned 24 bit value, big endian")]
public void WriteU24Big(int addr, uint value)
{
WriteUnsignedBig(addr, value, 3);
APIs.Mem.SetBigEndian();
APIs.Mem.WriteU24(addr, value, Domain.Name);
}
#endregion
@ -258,56 +293,64 @@ namespace BizHawk.Client.Common
[LuaMethod("read_s32_le", "read signed 4 byte value, little endian")]
public int ReadS32Little(int addr)
{
return ReadSignedLittleCore(addr, 4);
APIs.Mem.SetBigEndian(false);
return APIs.Mem.ReadS32(addr, Domain.Name);
}
[LuaMethodExample("mainmemory.write_s32_le( 0x100, -1000 );")]
[LuaMethod("write_s32_le", "write signed 4 byte value, little endian")]
public void WriteS32Little(int addr, int value)
{
WriteSignedLittle(addr, value, 4);
APIs.Mem.SetBigEndian(false);
APIs.Mem.WriteS32(addr, value, Domain.Name);
}
[LuaMethodExample("local inmairea = mainmemory.read_s32_be( 0x100 );")]
[LuaMethod("read_s32_be", "read signed 4 byte value, big endian")]
public int ReadS32Big(int addr)
{
return ReadSignedBig(addr, 4);
APIs.Mem.SetBigEndian();
return APIs.Mem.ReadS32(addr, Domain.Name);
}
[LuaMethodExample("mainmemory.write_s32_be( 0x100, -1000 );")]
[LuaMethod("write_s32_be", "write signed 4 byte value, big endian")]
public void WriteS32Big(int addr, int value)
{
WriteSignedBig(addr, value, 4);
APIs.Mem.SetBigEndian();
APIs.Mem.WriteS32(addr, value, Domain.Name);
}
[LuaMethodExample("local uimairea = mainmemory.read_u32_le( 0x100 );")]
[LuaMethod("read_u32_le", "read unsigned 4 byte value, little endian")]
public uint ReadU32Little(int addr)
{
return ReadSignedLittle(addr, 4);
APIs.Mem.SetBigEndian(false);
return APIs.Mem.ReadU32(addr, Domain.Name);
}
[LuaMethodExample("mainmemory.write_u32_le( 0x100, 1000 );")]
[LuaMethod("write_u32_le", "write unsigned 4 byte value, little endian")]
public void WriteU32Little(int addr, uint value)
{
WriteUnsignedLittle(addr, value, 4);
APIs.Mem.SetBigEndian(false);
APIs.Mem.WriteU32(addr, value, Domain.Name);
}
[LuaMethodExample("local uimairea = mainmemory.read_u32_be( 0x100 );")]
[LuaMethod("read_u32_be", "read unsigned 4 byte value, big endian")]
public uint ReadU32Big(int addr)
{
return ReadUnsignedBig(addr, 4);
APIs.Mem.SetBigEndian();
return APIs.Mem.ReadU32(addr, Domain.Name);
}
[LuaMethodExample("mainmemory.write_u32_be( 0x100, 1000 );")]
[LuaMethod("write_u32_be", "write unsigned 4 byte value, big endian")]
public void WriteU32Big(int addr, uint value)
{
WriteUnsignedBig(addr, value, 4);
APIs.Mem.SetBigEndian();
APIs.Mem.WriteU32(addr, value, Domain.Name);
}
#endregion

View File

@ -2,18 +2,13 @@
using System.ComponentModel;
using NLua;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
using BizHawk.Common.BufferExtensions;
// ReSharper disable UnusedMember.Global
namespace BizHawk.Client.Common
{
[Description("These functions behavior identically to the mainmemory functions but the user can set the memory domain to read and write from. The default domain is the system bus. Use getcurrentmemorydomain(), and usememorydomain() to control which domain is used. Each core has its own set of valid memory domains. Use getmemorydomainlist() to get a list of memory domains for the current core loaded.")]
public sealed class MemoryLuaLibrary : LuaMemoryBase
public sealed class MemoryLuaLibrary : DelegatingLuaLibrary
{
private MemoryDomain _currentMemoryDomain;
public MemoryLuaLibrary(Lua lua)
: base(lua)
{
@ -26,124 +21,38 @@ namespace BizHawk.Client.Common
public override string Name => "memory";
protected override MemoryDomain Domain
{
get
{
if (MemoryDomainCore != null)
{
if (_currentMemoryDomain == null)
{
_currentMemoryDomain = MemoryDomainCore.HasSystemBus
? MemoryDomainCore.SystemBus
: MemoryDomainCore.MainMemory;
}
return _currentMemoryDomain;
}
var error = $"Error: {Emulator.Attributes().CoreName} does not implement memory domains";
Log(error);
throw new NotImplementedException(error);
}
}
#region Unique Library Methods
[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")]
public LuaTable GetMemoryDomainList()
{
var result = APIs.Mem.GetMemoryDomainList();
var table = Lua.NewTable();
int i = 0;
foreach (var domain in DomainList)
{
table[i] = domain.Name;
i++;
}
var count = result.Count;
for (var i = 0; i != count; i++) table[i] = result[i];
return table;
}
[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 = "")
{
if (string.IsNullOrEmpty(name))
{
return (uint)Domain.Size;
}
return (uint)DomainList[VerifyMemoryDomain(name)].Size;
}
public uint GetMemoryDomainSize(string name = "") => APIs.Mem.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")]
public string GetCurrentMemoryDomain()
{
return Domain.Name;
}
public string GetCurrentMemoryDomain() => APIs.Mem.GetCurrentMemoryDomain();
[LuaMethodExample("local uimemget = memory.getcurrentmemorydomainsize( );")]
[LuaMethod("getcurrentmemorydomainsize", "Returns the number of bytes of the current memory domain selected by Lua. The default is Main memory")]
public uint GetCurrentMemoryDomainSize()
{
return (uint)Domain.Size;
}
public uint GetCurrentMemoryDomainSize() => APIs.Mem.GetCurrentMemoryDomainSize();
[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)
{
try
{
if (DomainList[domain] != null)
{
_currentMemoryDomain = DomainList[domain];
return true;
}
Log($"Unable to find domain: {domain}");
return false;
}
catch // Just in case
{
Log($"Unable to find domain: {domain}");
}
return false;
}
public bool UseMemoryDomain(string domain) => APIs.Mem.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(int addr, int count, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
// checks
if (addr < 0 || addr >= d.Size)
{
string error = $"Address {addr} is outside the bounds of domain {d.Name}";
Log(error);
throw new ArgumentOutOfRangeException(error);
}
if (addr + count > d.Size)
{
string error = $"Address {addr} + count {count} is outside the bounds of domain {d.Name}";
Log(error);
throw new ArgumentOutOfRangeException(error);
}
byte[] data = new byte[count];
for (int i = 0; i < count; i++)
{
data[i] = d.PeekByte(addr + i);
}
using var hasher = System.Security.Cryptography.SHA256.Create();
return hasher.ComputeHash(data).BytesToHexString();
}
public string HashRegion(int addr, int count, string domain = null) => APIs.Mem.HashRegion(addr, count, domain);
#endregion
@ -151,44 +60,68 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local uimemrea = memory.readbyte( 0x100, mainmemory.getname( ) );")]
[LuaMethod("readbyte", "gets the value from the given address as an unsigned byte")]
public uint ReadByte(int addr, string domain = null)
{
return ReadUnsignedByte(addr, domain);
}
public uint ReadByte(int addr, string domain = null) => APIs.Mem.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(int addr, uint value, string domain = null)
{
WriteUnsignedByte(addr, value, domain);
}
public void WriteByte(int addr, uint value, string domain = null) => APIs.Mem.WriteByte(addr, value, domain);
[LuaMethodExample("local nlmemrea = memory.readbyterange( 0x100, 30, mainmemory.getname( ) );")]
[LuaMethod("readbyterange", "Reads the address range that starts from address, and is length long. Returns the result into a table of key value pairs (where the address is the key).")]
public new LuaTable ReadByteRange(int addr, int length, string domain = null)
public LuaTable ReadByteRange(int addr, int length, string domain = null)
{
return base.ReadByteRange(addr, length, domain);
var result = APIs.Mem.ReadByteRange(addr, length, domain);
var table = Lua.NewTable();
var count = result.Count;
for (var i = 0; i != count; i++) table[i] = result[i];
return table;
}
/// <remarks>TODO C# version requires a contiguous address range</remarks>
[LuaMethodExample("")]
[LuaMethod("writebyterange", "Writes the given values to the given addresses as unsigned bytes")]
public new void WriteByteRange(LuaTable memoryblock, string domain = null)
public void WriteByteRange(LuaTable memoryblock, string domain = null)
{
base.WriteByteRange(memoryblock, domain);
#if true
foreach (var addr in memoryblock.Keys) APIs.Mem.WriteByte(LuaInt(addr), (uint) memoryblock[addr], domain);
#else
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (d.CanPoke())
{
foreach (var address in memoryblock.Keys)
{
var addr = LuaInt(address);
if (addr < d.Size)
{
d.PokeByte(addr, (byte)LuaInt(memoryblock[address]));
}
else
{
Log($"Warning: Attempted write {addr} outside memory domain size of {d.Size} in writebyterange()");
}
}
}
else
{
Log($"Error: the domain {d.Name} is not writable");
}
#endif
}
[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 new float ReadFloat(int addr, bool bigendian, string domain = null)
public float ReadFloat(int addr, bool bigendian, string domain = null)
{
return base.ReadFloat(addr, bigendian, domain);
APIs.Mem.SetBigEndian(bigendian);
return APIs.Mem.ReadFloat(addr, domain);
}
[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 new void WriteFloat(int addr, double value, bool bigendian, string domain = null)
public void WriteFloat(int addr, double value, bool bigendian, string domain = null)
{
base.WriteFloat(addr, value, bigendian, domain);
APIs.Mem.SetBigEndian(bigendian);
APIs.Mem.WriteFloat(addr, value, domain);
}
#endregion
@ -197,31 +130,19 @@ namespace BizHawk.Client.Common
[LuaMethodExample("local inmemrea = memory.read_s8( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_s8", "read signed byte")]
public int ReadS8(int addr, string domain = null)
{
return (sbyte)ReadUnsignedByte(addr, domain);
}
public int ReadS8(int addr, string domain = null) => APIs.Mem.ReadS8(addr, domain);
[LuaMethodExample("memory.write_s8( 0x100, 1000, mainmemory.getname( ) );")]
[LuaMethod("write_s8", "write signed byte")]
public void WriteS8(int addr, uint value, string domain = null)
{
WriteUnsignedByte(addr, value, domain);
}
public void WriteS8(int addr, uint value, string domain = null) => APIs.Mem.WriteS8(addr, unchecked((int) value), domain);
[LuaMethodExample("local uimemrea = memory.read_u8( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_u8", "read unsigned byte")]
public uint ReadU8(int addr, string domain = null)
{
return ReadUnsignedByte(addr, domain);
}
public uint ReadU8(int addr, string domain = null) => APIs.Mem.ReadU8(addr, domain);
[LuaMethodExample("memory.write_u8( 0x100, 1000, mainmemory.getname( ) );")]
[LuaMethod("write_u8", "write unsigned byte")]
public void WriteU8(int addr, uint value, string domain = null)
{
WriteUnsignedByte(addr, value, domain);
}
public void WriteU8(int addr, uint value, string domain = null) => APIs.Mem.WriteU8(addr, value, domain);
#endregion
@ -231,56 +152,64 @@ namespace BizHawk.Client.Common
[LuaMethod("read_s16_le", "read signed 2 byte value, little endian")]
public int ReadS16Little(int addr, string domain = null)
{
return ReadSignedLittleCore(addr, 2, domain);
APIs.Mem.SetBigEndian(false);
return APIs.Mem.ReadS16(addr, domain);
}
[LuaMethodExample("memory.write_s16_le( 0x100, -1000, mainmemory.getname( ) );")]
[LuaMethod("write_s16_le", "write signed 2 byte value, little endian")]
public void WriteS16Little(int addr, int value, string domain = null)
{
WriteSignedLittle(addr, value, 2, domain);
APIs.Mem.SetBigEndian(false);
APIs.Mem.WriteS16(addr, value, domain);
}
[LuaMethodExample("local inmemrea = memory.read_s16_be( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_s16_be", "read signed 2 byte value, big endian")]
public int ReadS16Big(int addr, string domain = null)
{
return ReadSignedBig(addr, 2, domain);
APIs.Mem.SetBigEndian();
return APIs.Mem.ReadS16(addr, domain);
}
[LuaMethodExample("memory.write_s16_be( 0x100, -1000, mainmemory.getname( ) );")]
[LuaMethod("write_s16_be", "write signed 2 byte value, big endian")]
public void WriteS16Big(int addr, int value, string domain = null)
{
WriteSignedBig(addr, value, 2, domain);
APIs.Mem.SetBigEndian();
APIs.Mem.WriteS16(addr, value, domain);
}
[LuaMethodExample("local uimemrea = memory.read_u16_le( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_u16_le", "read unsigned 2 byte value, little endian")]
public uint ReadU16Little(int addr, string domain = null)
{
return ReadUnsignedLittle(addr, 2, domain);
APIs.Mem.SetBigEndian(false);
return APIs.Mem.ReadU16(addr, domain);
}
[LuaMethodExample("memory.write_u16_le( 0x100, 1000, mainmemory.getname( ) );")]
[LuaMethod("write_u16_le", "write unsigned 2 byte value, little endian")]
public void WriteU16Little(int addr, uint value, string domain = null)
{
WriteUnsignedLittle(addr, value, 2, domain);
APIs.Mem.SetBigEndian(false);
APIs.Mem.WriteU16(addr, value, domain);
}
[LuaMethodExample("local uimemrea = memory.read_u16_be( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_u16_be", "read unsigned 2 byte value, big endian")]
public uint ReadU16Big(int addr, string domain = null)
{
return ReadUnsignedBig(addr, 2, domain);
APIs.Mem.SetBigEndian();
return APIs.Mem.ReadU16(addr, domain);
}
[LuaMethodExample("memory.write_u16_be( 0x100, 1000, mainmemory.getname( ) );")]
[LuaMethod("write_u16_be", "write unsigned 2 byte value, big endian")]
public void WriteU16Big(int addr, uint value, string domain = null)
{
WriteUnsignedBig(addr, value, 2, domain);
APIs.Mem.SetBigEndian();
APIs.Mem.WriteU16(addr, value, domain);
}
#endregion
@ -291,56 +220,64 @@ namespace BizHawk.Client.Common
[LuaMethod("read_s24_le", "read signed 24 bit value, little endian")]
public int ReadS24Little(int addr, string domain = null)
{
return ReadSignedLittleCore(addr, 3, domain);
APIs.Mem.SetBigEndian(false);
return APIs.Mem.ReadS24(addr, domain);
}
[LuaMethodExample("memory.write_s24_le( 0x100, -1000, mainmemory.getname( ) );")]
[LuaMethod("write_s24_le", "write signed 24 bit value, little endian")]
public void WriteS24Little(int addr, int value, string domain = null)
{
WriteSignedLittle(addr, value, 3, domain);
APIs.Mem.SetBigEndian(false);
APIs.Mem.WriteS24(addr, value, domain);
}
[LuaMethodExample("local inmemrea = memory.read_s24_be( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_s24_be", "read signed 24 bit value, big endian")]
public int ReadS24Big(int addr, string domain = null)
{
return ReadSignedBig(addr, 3, domain);
APIs.Mem.SetBigEndian();
return APIs.Mem.ReadS24(addr, domain);
}
[LuaMethodExample("memory.write_s24_be( 0x100, -1000, mainmemory.getname( ) );")]
[LuaMethod("write_s24_be", "write signed 24 bit value, big endian")]
public void WriteS24Big(int addr, int value, string domain = null)
{
WriteSignedBig(addr, value, 3, domain);
APIs.Mem.SetBigEndian();
APIs.Mem.WriteS24(addr, value, domain);
}
[LuaMethodExample("local uimemrea = memory.read_u24_le( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_u24_le", "read unsigned 24 bit value, little endian")]
public uint ReadU24Little(int addr, string domain = null)
{
return ReadUnsignedLittle(addr, 3, domain);
APIs.Mem.SetBigEndian(false);
return APIs.Mem.ReadU24(addr, domain);
}
[LuaMethodExample("memory.write_u24_le( 0x100, 1000, mainmemory.getname( ) );")]
[LuaMethod("write_u24_le", "write unsigned 24 bit value, little endian")]
public void WriteU24Little(int addr, uint value, string domain = null)
{
WriteUnsignedLittle(addr, value, 3, domain);
APIs.Mem.SetBigEndian(false);
APIs.Mem.WriteU24(addr, value, domain);
}
[LuaMethodExample("local uimemrea = memory.read_u24_be( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_u24_be", "read unsigned 24 bit value, big endian")]
public uint ReadU24Big(int addr, string domain = null)
{
return ReadUnsignedBig(addr, 3, domain);
APIs.Mem.SetBigEndian();
return APIs.Mem.ReadU24(addr, domain);
}
[LuaMethodExample("memory.write_u24_be( 0x100, 1000, mainmemory.getname( ) );")]
[LuaMethod("write_u24_be", "write unsigned 24 bit value, big endian")]
public void WriteU24Big(int addr, uint value, string domain = null)
{
WriteUnsignedBig(addr, value, 3, domain);
APIs.Mem.SetBigEndian();
APIs.Mem.WriteU24(addr, value, domain);
}
#endregion
@ -351,56 +288,64 @@ namespace BizHawk.Client.Common
[LuaMethod("read_s32_le", "read signed 4 byte value, little endian")]
public int ReadS32Little(int addr, string domain = null)
{
return ReadSignedLittleCore(addr, 4, domain);
APIs.Mem.SetBigEndian(false);
return APIs.Mem.ReadS32(addr, domain);
}
[LuaMethodExample("memory.write_s32_le( 0x100, -1000, mainmemory.getname( ) );")]
[LuaMethod("write_s32_le", "write signed 4 byte value, little endian")]
public void WriteS32Little(int addr, int value, string domain = null)
{
WriteSignedLittle(addr, value, 4, domain);
APIs.Mem.SetBigEndian(false);
APIs.Mem.WriteS32(addr, value, domain);
}
[LuaMethodExample("local inmemrea = memory.read_s32_be( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_s32_be", "read signed 4 byte value, big endian")]
public int ReadS32Big(int addr, string domain = null)
{
return ReadSignedBig(addr, 4, domain);
APIs.Mem.SetBigEndian();
return APIs.Mem.ReadS32(addr, domain);
}
[LuaMethodExample("memory.write_s32_be( 0x100, -1000, mainmemory.getname( ) );")]
[LuaMethod("write_s32_be", "write signed 4 byte value, big endian")]
public void WriteS32Big(int addr, int value, string domain = null)
{
WriteSignedBig(addr, value, 4, domain);
APIs.Mem.SetBigEndian();
APIs.Mem.WriteS32(addr, value, domain);
}
[LuaMethodExample("local uimemrea = memory.read_u32_le( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_u32_le", "read unsigned 4 byte value, little endian")]
public uint ReadU32Little(int addr, string domain = null)
{
return ReadUnsignedLittle(addr, 4, domain);
APIs.Mem.SetBigEndian(false);
return APIs.Mem.ReadU32(addr, domain);
}
[LuaMethodExample("memory.write_u32_le( 0x100, 1000, mainmemory.getname( ) );")]
[LuaMethod("write_u32_le", "write unsigned 4 byte value, little endian")]
public void WriteU32Little(int addr, uint value, string domain = null)
{
WriteUnsignedLittle(addr, value, 4, domain);
APIs.Mem.SetBigEndian(false);
APIs.Mem.WriteU32(addr, value, domain);
}
[LuaMethodExample("local uimemrea = memory.read_u32_be( 0x100, mainmemory.getname( ) );")]
[LuaMethod("read_u32_be", "read unsigned 4 byte value, big endian")]
public uint ReadU32Big(int addr, string domain = null)
{
return ReadUnsignedBig(addr, 4, domain);
APIs.Mem.SetBigEndian();
return APIs.Mem.ReadU32(addr, domain);
}
[LuaMethodExample("memory.write_u32_be( 0x100, 1000, mainmemory.getname( ) );")]
[LuaMethod("write_u32_be", "write unsigned 4 byte value, big endian")]
public void WriteU32Big(int addr, uint value, string domain = null)
{
WriteUnsignedBig(addr, value, 4, domain);
APIs.Mem.SetBigEndian();
APIs.Mem.WriteU32(addr, value, domain);
}
#endregion

View File

@ -10,7 +10,7 @@ using BizHawk.Emulation.Common;
// ReSharper disable UnusedAutoPropertyAccessor.Local
namespace BizHawk.Client.Common
{
public sealed class MemorySavestateEmuLuaLibrary : LuaLibraryBase
public sealed class MemorySavestateEmuLuaLibrary : DelegatingLuaLibrary
{
public MemorySavestateEmuLuaLibrary(Lua lua)
: base(lua) { }
@ -20,56 +20,20 @@ namespace BizHawk.Client.Common
public override string Name => "memorysavestate";
[RequiredService]
private IStatable StatableCore { get; set; }
private readonly Dictionary<Guid, byte[]> _memorySavestates = new Dictionary<Guid, byte[]>();
[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")]
public string SaveCoreStateToMemory()
{
var guid = Guid.NewGuid();
var bytes = (byte[])StatableCore.SaveStateBinary().Clone();
_memorySavestates.Add(guid, bytes);
return guid.ToString();
}
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)
{
var guid = new Guid(identifier);
try
{
var state = _memorySavestates[guid];
using var ms = new MemoryStream(state);
using var br = new BinaryReader(ms);
StatableCore.LoadStateBinary(br);
}
catch
{
Log("Unable to find the given savestate in memory");
}
}
public void LoadCoreStateFromMemory(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)
{
var guid = new Guid(identifier);
_memorySavestates.Remove(guid);
}
public void DeleteState(string identifier) => APIs.MemorySaveState.DeleteState(identifier);
[LuaMethodExample("memorysavestate.clearstatesfrommemory( );")]
[LuaMethod("clearstatesfrommemory", "clears all savestates stored in memory")]
public void ClearInMemoryStates()
{
_memorySavestates.Clear();
}
public void ClearInMemoryStates() => APIs.MemorySaveState.ClearInMemoryStates();
}
}

View File

@ -1,11 +1,11 @@
using System;
using System.IO;
using NLua;
// ReSharper disable UnusedMember.Global
namespace BizHawk.Client.Common
{
public sealed class MovieLuaLibrary : LuaLibraryBase
public sealed class MovieLuaLibrary : DelegatingLuaLibrary
{
public MovieLuaLibrary(Lua lua)
: base(lua) { }
@ -17,253 +17,108 @@ namespace BizHawk.Client.Common
[LuaMethodExample("if ( movie.startsfromsavestate( ) ) then\r\n\tconsole.log( \"Returns whether or not the movie is a savestate-anchored movie\" );\r\nend;")]
[LuaMethod("startsfromsavestate", "Returns whether or not the movie is a savestate-anchored movie")]
public bool StartsFromSavestate()
{
return Global.MovieSession.Movie.IsActive && Global.MovieSession.Movie.StartsFromSavestate;
}
public bool StartsFromSavestate() => APIs.Movie.StartsFromSavestate();
[LuaMethodExample("if ( movie.startsfromsaveram( ) ) then\r\n\tconsole.log( \"Returns whether or not the movie is a saveram-anchored movie\" );\r\nend;")]
[LuaMethod("startsfromsaveram", "Returns whether or not the movie is a saveram-anchored movie")]
public bool StartsFromSaveram()
{
return Global.MovieSession.Movie.IsActive && Global.MovieSession.Movie.StartsFromSaveRam;
}
public bool StartsFromSaveram() => APIs.Movie.StartsFromSaveram();
[LuaMethodExample("local stmovfil = movie.filename( );")]
[LuaMethod("filename", "Returns the file name including path of the currently loaded movie")]
public static string Filename()
{
return Global.MovieSession.Movie.Filename;
}
public string Filename() => 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")]
public LuaTable GetInput(int frame)
{
if (!Global.MovieSession.Movie.IsActive)
{
Log("No movie loaded");
return null;
}
var input = Lua.NewTable();
var adapter = Global.MovieSession.Movie.GetInputState(frame);
if (adapter == null)
{
Log("Can't get input of the last frame of the movie. Use the previous frame");
return null;
}
foreach (var button in adapter.Definition.BoolButtons)
{
input[button] = adapter.IsPressed(button);
}
foreach (var button in adapter.Definition.FloatControls)
{
input[button] = adapter.GetFloat(button);
}
return input;
var result = APIs.Movie.GetInput(frame);
var table = Lua.NewTable();
foreach (var kvp in result) table[kvp.Key] = kvp.Value;
return table;
}
[LuaMethodExample("local stmovget = movie.getinputasmnemonic( 500 );")]
[LuaMethod("getinputasmnemonic", "Returns the input of a given frame of the loaded movie in a raw inputlog string")]
public string GetInputAsMnemonic(int frame)
{
if (Global.MovieSession.Movie.IsActive && frame < Global.MovieSession.Movie.InputLogLength)
{
var lg = Global.MovieSession.LogGeneratorInstance();
lg.SetSource(Global.MovieSession.Movie.GetInputState(frame));
return lg.GenerateLogEntry();
}
return "";
}
public string GetInputAsMnemonic(int frame) => APIs.Movie.GetInputAsMnemonic(frame);
[LuaMethodExample("if ( movie.getreadonly( ) ) then\r\n\tconsole.log( \"Returns true if the movie is in read-only mode, false if in read+write\" );\r\nend;")]
[LuaMethod("getreadonly", "Returns true if the movie is in read-only mode, false if in read+write")]
public static bool GetReadOnly()
{
return Global.MovieSession.ReadOnly;
}
public bool GetReadOnly() => APIs.Movie.GetReadOnly();
[LuaMethodExample("local ulmovget = movie.getrerecordcount();")]
[LuaMethod("getrerecordcount", "Gets the rerecord count of the current movie.")]
public static ulong GetRerecordCount()
{
return Global.MovieSession.Movie.Rerecords;
}
public ulong GetRerecordCount() => APIs.Movie.GetRerecordCount();
[LuaMethodExample("if ( movie.getrerecordcounting( ) ) then\r\n\tconsole.log( \"Returns whether or not the current movie is incrementing rerecords on loadstate\" );\r\nend;")]
[LuaMethod("getrerecordcounting", "Returns whether or not the current movie is incrementing rerecords on loadstate")]
public static bool GetRerecordCounting()
{
return Global.MovieSession.Movie.IsCountingRerecords;
}
public bool GetRerecordCounting() => APIs.Movie.GetRerecordCounting();
[LuaMethodExample("if ( movie.isloaded( ) ) then\r\n\tconsole.log( \"Returns true if a movie is loaded in memory ( play, record, or finished modes ), false if not ( inactive mode )\" );\r\nend;")]
[LuaMethod("isloaded", "Returns true if a movie is loaded in memory (play, record, or finished modes), false if not (inactive mode)")]
public static bool IsLoaded()
{
return Global.MovieSession.Movie.IsActive;
}
public bool IsLoaded() => APIs.Movie.IsLoaded();
[LuaMethodExample("local domovlen = movie.length( );")]
[LuaMethod("length", "Returns the total number of frames of the loaded movie")]
public static double Length()
{
return Global.MovieSession.Movie.FrameCount;
}
public double Length() => APIs.Movie.Length();
[LuaMethodExample("local stmovmod = movie.mode( );")]
[LuaMethod("mode", "Returns the mode of the current movie. Possible modes: \"PLAY\", \"RECORD\", \"FINISHED\", \"INACTIVE\"")]
public static string Mode()
{
if (Global.MovieSession.Movie.IsFinished)
{
return "FINISHED";
}
if (Global.MovieSession.Movie.IsPlaying)
{
return "PLAY";
}
if (Global.MovieSession.Movie.IsRecording)
{
return "RECORD";
}
return "INACTIVE";
}
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 = "")
{
if (!Global.MovieSession.Movie.IsActive)
{
return;
}
if (!string.IsNullOrEmpty(filename))
{
filename += $".{Global.MovieSession.Movie.PreferredExtension}";
var test = new FileInfo(filename);
if (test.Exists)
{
Log($"File {filename} already exists, will not overwrite");
return;
}
Global.MovieSession.Movie.Filename = filename;
}
Global.MovieSession.Movie.Save();
}
public void Save(string filename = "") => APIs.Movie.Save();
[LuaMethodExample("movie.setreadonly( false );")]
[LuaMethod("setreadonly", "Sets the read-only state to the given value. true for read only, false for read+write")]
public static void SetReadOnly(bool readOnly)
{
Global.MovieSession.ReadOnly = readOnly;
}
public void SetReadOnly(bool readOnly) => APIs.Movie.SetReadOnly(readOnly);
[LuaMethodExample("movie.setrerecordcount( 20.0 );")]
[LuaMethod("setrerecordcount", "Sets the rerecord count of the current movie.")]
public static void SetRerecordCount(double count)
{
// Lua numbers are always double, integer precision holds up
// to 53 bits, so throw an error if it's bigger than that.
const double precisionLimit = 9007199254740992d;
if (count > precisionLimit)
{
throw new Exception("Rerecord count exceeds Lua integer precision.");
}
Global.MovieSession.Movie.Rerecords = (ulong)count;
}
public void SetRerecordCount(double count) => APIs.Movie.SetRerecordCount(count);
[LuaMethodExample("movie.setrerecordcounting( true );")]
[LuaMethod("setrerecordcounting", "Sets whether or not the current movie will increment the rerecord counter on loadstate")]
public static void SetRerecordCounting(bool counting)
{
Global.MovieSession.Movie.IsCountingRerecords = counting;
}
public void SetRerecordCounting(bool counting) => APIs.Movie.SetRerecordCounting(counting);
[LuaMethodExample("movie.stop( );")]
[LuaMethod("stop", "Stops the current movie")]
public static void Stop()
{
Global.MovieSession.Movie.Stop();
}
public void Stop() => APIs.Movie.Stop();
[LuaMethodExample("local domovget = movie.getfps( );")]
[LuaMethod("getfps", "If a movie is loaded, gets the frames per second used by the movie to determine the movie length time")]
public static double GetFps()
{
if (Global.MovieSession.Movie.IsActive)
{
var movie = Global.MovieSession.Movie;
var system = movie.HeaderEntries[HeaderKeys.PLATFORM];
var pal = movie.HeaderEntries.ContainsKey(HeaderKeys.PAL)
&& movie.HeaderEntries[HeaderKeys.PAL] == "1";
return new PlatformFrameRates()[system, pal];
}
return 0.0;
}
public double GetFps() => APIs.Movie.GetFps();
[LuaMethodExample("local nlmovget = movie.getheader( );")]
[LuaMethod("getheader", "If a movie is active, will return the movie header as a lua table")]
public LuaTable GetHeader()
{
var luaTable = Lua.NewTable();
if (Global.MovieSession.Movie.IsActive)
{
foreach (var kvp in Global.MovieSession.Movie.HeaderEntries)
{
luaTable[kvp.Key] = kvp.Value;
}
}
return luaTable;
var result = APIs.Movie.GetHeader();
var table = Lua.NewTable();
foreach (var kvp in result) table[kvp.Key] = kvp.Value;
return table;
}
[LuaMethodExample("local nlmovget = movie.getcomments( );")]
[LuaMethod("getcomments", "If a movie is active, will return the movie comments as a lua table")]
public LuaTable GetComments()
{
var luaTable = Lua.NewTable();
if (Global.MovieSession.Movie.IsActive)
{
for (int i = 0; i < Global.MovieSession.Movie.Comments.Count; i++)
{
luaTable[i] = Global.MovieSession.Movie.Comments[i];
}
}
return luaTable;
var result = APIs.Movie.GetComments();
var table = Lua.NewTable();
var count = result.Count;
for (var i = 0; i != count; i++) table[i] = result[i];
return table;
}
[LuaMethodExample("local nlmovget = movie.getsubtitles( );")]
[LuaMethod("getsubtitles", "If a movie is active, will return the movie subtitles as a lua table")]
public LuaTable GetSubtitles()
{
var luaTable = Lua.NewTable();
if (Global.MovieSession.Movie.IsActive)
{
for (int i = 0; i < Global.MovieSession.Movie.Subtitles.Count; i++)
{
luaTable[i] = Global.MovieSession.Movie.Subtitles[i].ToString();
}
}
return luaTable;
var result = APIs.Movie.GetSubtitles();
var table = Lua.NewTable();
var count = result.Count;
for (var i = 0; i != count; i++) table[i] = result[i];
return table;
}
}
}

View File

@ -1,14 +1,14 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.SQLite;
using NLua;
// ReSharper disable UnusedMember.Global
namespace BizHawk.Client.Common
{
[Description("A library for performing SQLite operations.")]
public sealed class SqlLuaLibrary : LuaLibraryBase
public sealed class SqlLuaLibrary : DelegatingLuaLibrary
{
public SqlLuaLibrary(Lua lua)
: base(lua) { }
@ -18,131 +18,32 @@ namespace BizHawk.Client.Common
public override string Name => "SQL";
SQLiteConnection _mDBConnection;
[LuaMethodExample("local stSQLcre = SQL.createdatabase( \"eg_db\" );")]
[LuaMethod("createdatabase", "Creates a SQLite Database. Name should end with .db")]
public string CreateDatabase(string name)
{
try
{
SQLiteConnection.CreateFile(name);
return "Database Created Successfully";
}
catch (SQLiteException sqlEx)
{
return sqlEx.Message;
}
}
public string CreateDatabase(string name) => APIs.Sql.CreateDatabase(name);
[LuaMethodExample("local stSQLope = SQL.opendatabase( \"eg_db\" );")]
[LuaMethod("opendatabase", "Opens a SQLite database. Name should end with .db")]
public string OpenDatabase(string name)
{
try
{
var connBuilder = new SQLiteConnectionStringBuilder
{
DataSource = name,
Version = 3,
JournalMode = SQLiteJournalModeEnum.Wal, // Allows for reads and writes to happen at the same time
DefaultIsolationLevel = System.Data.IsolationLevel.ReadCommitted, // This only helps make the database lock left. May be pointless now
SyncMode = SynchronizationModes.Off, // This shortens the delay for do synchronous calls.
};
_mDBConnection = new SQLiteConnection(connBuilder.ToString());
_mDBConnection.Open();
_mDBConnection.Close();
return "Database Opened Successfully";
}
catch (SQLiteException sqlEx)
{
return sqlEx.Message;
}
}
public string OpenDatabase(string name) => APIs.Sql.OpenDatabase(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 = "")
{
if (query == "")
{
return "query is empty";
}
try
{
_mDBConnection.Open();
string sql = query;
SQLiteCommand command = new SQLiteCommand(sql, _mDBConnection);
command.ExecuteNonQuery();
_mDBConnection.Close();
return "Command ran successfully";
}
catch (NullReferenceException)
{
return "Database not open.";
}
catch (SQLiteException sqlEx)
{
_mDBConnection.Close();
return sqlEx.Message;
}
}
public string WriteCommand(string query = "") => APIs.Sql.WriteCommand(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 dynamic ReadCommand(string query = "")
{
if (query == "")
{
return "query is empty";
}
try
var result = APIs.Sql.ReadCommand(query);
if (result is Dictionary<string, object> dict)
{
var table = Lua.NewTable();
_mDBConnection.Open();
string sql = $"PRAGMA read_uncommitted =1;{query}";
using var command = new SQLiteCommand(sql, _mDBConnection);
SQLiteDataReader reader = command.ExecuteReader();
bool rows = reader.HasRows;
long rowCount = 0;
var columns = new List<string>();
for (int i = 0; i < reader.FieldCount; ++i) //Add all column names into list
{
columns.Add(reader.GetName(i));
}
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; ++i)
{
table[$"{columns[i]} {rowCount}"] = reader.GetValue(i);
}
rowCount += 1;
}
reader.Close();
_mDBConnection.Close();
if (rows == false)
{
return "No rows found";
}
foreach (var kvp in dict) table[kvp.Key] = kvp.Value;
return table;
}
catch (NullReferenceException)
{
return "Database not opened.";
}
catch (SQLiteException sqlEx)
{
_mDBConnection.Close();
return sqlEx.Message;
}
return result;
}
}
}

View File

@ -7,7 +7,7 @@ using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
{
[Description("A library for setting and retrieving dynamic data that will be saved and loaded with savestates")]
public sealed class UserDataLibrary : LuaLibraryBase
public sealed class UserDataLibrary : DelegatingLuaLibrary
{
public UserDataLibrary(Lua lua)
: base(lua) { }
@ -19,48 +19,22 @@ namespace BizHawk.Client.EmuHawk
[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)
{
if (value != null)
{
var t = value.GetType();
if (!t.IsPrimitive && t != typeof(string))
{
throw new InvalidOperationException("Invalid type for userdata");
}
}
Global.UserBag[name] = value;
}
public void Set(string name, object value) => APIs.UserData.Set(name, 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)
{
return Global.UserBag.ContainsKey(key)
? Global.UserBag[key]
: null;
}
public object Get(string key) => APIs.UserData.Get(key);
[LuaMethodExample("userdata.clear( );")]
[LuaMethod("clear", "clears all user data")]
public void Clear()
{
Global.UserBag.Clear();
}
public void Clear() => APIs.UserData.Clear();
[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)
{
return Global.UserBag.Remove(key);
}
public bool Remove(string key) => APIs.UserData.Remove(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)
{
return Global.UserBag.ContainsKey(key);
}
public bool ContainsKey(string key) => APIs.UserData.ContainsKey(key);
}
}

View File

@ -1,259 +0,0 @@
using System;
using NLua;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
namespace BizHawk.Client.Common
{
/// <summary>
/// Base class for the Memory and MainMemory lua libraries
/// </summary>
public abstract class LuaMemoryBase : LuaLibraryBase
{
[RequiredService]
protected IEmulator Emulator { get; set; }
[OptionalService]
protected IMemoryDomains MemoryDomainCore { get; set; }
protected LuaMemoryBase(Lua lua)
: base(lua) { }
protected LuaMemoryBase(Lua lua, Action<string> logOutputCallback)
: base(lua, logOutputCallback) { }
protected abstract MemoryDomain Domain { get; }
protected IMemoryDomains DomainList
{
get
{
if (MemoryDomainCore != null)
{
return MemoryDomainCore;
}
var error = $"Error: {Emulator.Attributes().CoreName} does not implement memory domains";
Log(error);
throw new NotImplementedException(error);
}
}
public string VerifyMemoryDomain(string domain)
{
try
{
if (DomainList[domain] == null)
{
Log($"Unable to find domain: {domain}, falling back to current");
return Domain.Name;
}
return domain;
}
catch // Just in case
{
Log($"Unable to find domain: {domain}, falling back to current");
}
return Domain.Name;
}
protected uint ReadUnsignedByte(int addr, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (addr < d.Size)
{
return d.PeekByte(addr);
}
Log($"Warning: attempted read of {addr} outside the memory size of {d.Size}");
return 0;
}
protected void WriteUnsignedByte(int addr, uint v, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (d.CanPoke())
{
if (addr < Domain.Size)
{
d.PokeByte(addr, (byte)v);
}
else
{
Log($"Warning: attempted write to {addr} outside the memory size of {d.Size}");
}
}
else
{
Log($"Error: the domain {d.Name} is not writable");
}
}
protected static int U2S(uint u, int size)
{
var s = (int)u;
s <<= 8 * (4 - size);
s >>= 8 * (4 - size);
return s;
}
protected int ReadSignedLittleCore(int addr, int size, string domain = null)
{
return U2S(ReadUnsignedLittle(addr, size, domain), size);
}
protected uint ReadUnsignedLittle(int addr, int size, string domain = null)
{
uint v = 0;
for (var i = 0; i < size; ++i)
{
v |= ReadUnsignedByte(addr + i, domain) << (8 * i);
}
return v;
}
protected int ReadSignedBig(int addr, int size, string domain = null)
{
return U2S(ReadUnsignedBig(addr, size, domain), size);
}
protected uint ReadUnsignedBig(int addr, int size, string domain = null)
{
uint v = 0;
for (var i = 0; i < size; ++i)
{
v |= ReadUnsignedByte(addr + i, domain) << (8 * (size - 1 - i));
}
return v;
}
protected void WriteSignedLittle(int addr, int v, int size, string domain = null)
{
WriteUnsignedLittle(addr, (uint)v, size, domain);
}
protected void WriteUnsignedLittle(int addr, uint v, int size, string domain = null)
{
for (var i = 0; i < size; ++i)
{
WriteUnsignedByte(addr + i, (v >> (8 * i)) & 0xFF, domain);
}
}
protected void WriteSignedBig(int addr, int v, int size, string domain = null)
{
WriteUnsignedBig(addr, (uint)v, size, domain);
}
protected void WriteUnsignedBig(int addr, uint v, int size, string domain = null)
{
for (var i = 0; i < size; ++i)
{
WriteUnsignedByte(addr + i, (v >> (8 * (size - 1 - i))) & 0xFF, domain);
}
}
protected uint ReadSignedLittle(int addr, int size) // only used by mainmemory, so no domain can be passed
{
uint v = 0;
for (var i = 0; i < size; ++i)
{
v |= ReadUnsignedByte(addr + i) << (8 * i);
}
return v;
}
#region public Library implementations
protected LuaTable ReadByteRange(int addr, int length, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
var lastAddr = length + addr;
var table = Lua.NewTable();
if (lastAddr <= d.Size)
{
for (var i = 0; i < length; i++)
{
int a = addr + i;
var v = d.PeekByte(a);
table[i] = v;
}
}
else
{
Log($"Warning: Attempted read {lastAddr} outside memory domain size of {d.Size} in readbyterange()");
}
return table;
}
protected void WriteByteRange(LuaTable memoryblock, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (d.CanPoke())
{
foreach (var address in memoryblock.Keys)
{
var addr = LuaInt(address);
if (addr < d.Size)
{
d.PokeByte(addr, (byte)LuaInt(memoryblock[address]));
}
else
{
Log($"Warning: Attempted write {addr} outside memory domain size of {d.Size} in writebyterange()");
}
}
}
else
{
Log($"Error: the domain {d.Name} is not writable");
}
}
protected float ReadFloat(int addr, bool bigEndian, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (addr < d.Size)
{
var val = d.PeekUint(addr, bigEndian);
var bytes = BitConverter.GetBytes(val);
return BitConverter.ToSingle(bytes, 0);
}
Log($"Warning: Attempted read {addr} outside memory size of {d.Size}");
return 0;
}
protected void WriteFloat(int addr, double value, bool bigEndian, string domain = null)
{
var d = string.IsNullOrEmpty(domain) ? Domain : DomainList[VerifyMemoryDomain(domain)];
if (d.CanPoke())
{
if (addr < d.Size)
{
var dv = (float)value;
var bytes = BitConverter.GetBytes(dv);
var v = BitConverter.ToUInt32(bytes, 0);
d.PokeUint(addr, v, bigEndian);
}
else
{
Log($"Warning: Attempted write {addr} outside memory size of {d.Size}");
}
}
else
{
Log($"Error: the domain {Domain.Name} is not writable");
}
}
#endregion
}
}

View File

@ -1,33 +1,18 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using BizHawk.Client.ApiHawk;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
{
public sealed class ApiContainer : IApiContainer
public sealed class ApiContainer : APISubsetContainer
{
public IComm Comm => (IComm)Libraries[typeof(CommApi)];
public IEmu Emu => (IEmu)Libraries[typeof(EmuApi)];
public IGameInfo GameInfo => (IGameInfo)Libraries[typeof(GameInfoApi)];
public IGui Gui => (IGui)Libraries[typeof(GuiApi)];
public IInput Input => (IInput)Libraries[typeof(InputApi)];
public IJoypad Joypad => (IJoypad)Libraries[typeof(JoypadApi)];
public IMem Mem => (IMem)Libraries[typeof(MemApi)];
public IMemEvents MemEvents => (IMemEvents)Libraries[typeof(MemEventsApi)];
public IMemorySaveState MemorySaveState => (IMemorySaveState)Libraries[typeof(MemorySaveStateApi)];
public IInputMovie Movie => (IInputMovie)Libraries[typeof(MovieApi)];
public ISaveState SaveState => (ISaveState)Libraries[typeof(SaveStateApi)];
public ISql Sql => (ISql)Libraries[typeof(SqlApi)];
public ITool Tool => (ITool)Libraries[typeof(ToolApi)];
public IUserData UserData => (IUserData)Libraries[typeof(UserDataApi)];
public Dictionary<Type, IExternalApi> Libraries { get; set; }
public ApiContainer(Dictionary<Type, IExternalApi> libs)
{
Libraries = libs;
}
public IComm Comm => (IComm) Libraries[typeof(CommApi)];
public IGui Gui => (IGui) Libraries[typeof(GuiApi)];
public IInput Input => (IInput) Libraries[typeof(InputApi)];
public ISaveState SaveState => (ISaveState) Libraries[typeof(SaveStateApi)];
public ITool Tool => (ITool) Libraries[typeof(ToolApi)];
public ApiContainer(Dictionary<Type, IExternalApi> libs) : base(libs) {}
}
}

View File

@ -9,39 +9,45 @@ namespace BizHawk.Client.EmuHawk
{
public sealed class SaveStateApi : ISaveState
{
public SaveStateApi() : base()
{ }
public SaveStateApi(Action<string> logCallback)
{
LogCallback = logCallback;
}
public void Load(string path)
public SaveStateApi() : this(Console.WriteLine) {}
private readonly Action<string> LogCallback;
public void Load(string path, bool suppressOSD)
{
if (!File.Exists(path))
{
Console.WriteLine($"could not find file: {path}");
LogCallback($"could not find file: {path}");
}
else
{
GlobalWin.MainForm.LoadState(path, Path.GetFileName(path), true);
GlobalWin.MainForm.LoadState(path, Path.GetFileName(path), true, suppressOSD);
}
}
public void LoadSlot(int slotNum)
public void LoadSlot(int slotNum, bool suppressOSD)
{
if (slotNum >= 0 && slotNum <= 9)
{
GlobalWin.MainForm.LoadQuickSave($"QuickSave{slotNum}", true);
GlobalWin.MainForm.LoadQuickSave($"QuickSave{slotNum}", true, suppressOSD);
}
}
public void Save(string path)
public void Save(string path, bool suppressOSD)
{
GlobalWin.MainForm.SaveState(path, path, true);
GlobalWin.MainForm.SaveState(path, path, true, suppressOSD);
}
public void SaveSlot(int slotNum)
public void SaveSlot(int slotNum, bool suppressOSD)
{
if (slotNum >= 0 && slotNum <= 9)
{
GlobalWin.MainForm.SaveQuickSave($"QuickSave{slotNum}");
GlobalWin.MainForm.SaveQuickSave($"QuickSave{slotNum}", true, suppressOSD);
}
}
}

View File

@ -917,6 +917,7 @@
<Compile Include="tools\HexEditor\HexFind.Designer.cs">
<DependentUpon>HexFind.cs</DependentUpon>
</Compile>
<Compile Include="tools\Lua\Libraries\DelegatingLuaLibraryEmu.cs" />
<Compile Include="tools\Lua\Libraries\EmuLuaLibrary.Client.cs" />
<Compile Include="tools\Lua\Libraries\EmuLuaLibrary.Communication.cs" />
<Compile Include="tools\Lua\Libraries\EmuLuaLibrary.Console.cs" />

View File

@ -0,0 +1,18 @@
using System;
using NLua;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
{
/// <summary>As <see cref="DelegatingLuaLibrary"/>, but also includes EmuHawk APIs via a <see cref="APIContainer"/>.</summary>
public abstract class DelegatingLuaLibraryEmu : DelegatingLuaLibrary
{
protected DelegatingLuaLibraryEmu(Lua lua) : base(lua) {}
protected DelegatingLuaLibraryEmu(Lua lua, Action<string> logOutputCallback) : base(lua, logOutputCallback) {}
public new ApiContainer APIs { protected get; set; }
}
}

View File

@ -15,7 +15,7 @@ using System.Diagnostics;
namespace BizHawk.Client.EmuHawk
{
[Description("A library for manipulating the EmuHawk client UI")]
public sealed class EmuHawkLuaLibrary : LuaLibraryBase
public sealed class EmuHawkLuaLibrary : DelegatingLuaLibraryEmu
{
[RequiredService]
private IEmulator Emulator { get; set; }
@ -225,31 +225,19 @@ namespace BizHawk.Client.EmuHawk
[LuaMethodExample("client.opencheats( );")]
[LuaMethod("opencheats", "opens the Cheats dialog")]
public static void OpenCheats()
{
GlobalWin.Tools.Load<Cheats>();
}
public void OpenCheats() => APIs.Tool.OpenCheats();
[LuaMethodExample("client.openhexeditor( );")]
[LuaMethod("openhexeditor", "opens the Hex Editor dialog")]
public static void OpenHexEditor()
{
GlobalWin.Tools.Load<HexEditor>();
}
public void OpenHexEditor() => APIs.Tool.OpenHexEditor();
[LuaMethodExample("client.openramwatch( );")]
[LuaMethod("openramwatch", "opens the RAM Watch dialog")]
public static void OpenRamWatch()
{
GlobalWin.Tools.LoadRamWatch(loadDialog: true);
}
public void OpenRamWatch() => APIs.Tool.OpenRamWatch();
[LuaMethodExample("client.openramsearch( );")]
[LuaMethod("openramsearch", "opens the RAM Search dialog")]
public static void OpenRamSearch()
{
GlobalWin.Tools.Load<RamSearch>();
}
public void OpenRamSearch() => APIs.Tool.OpenRamSearch();
[LuaMethodExample("client.openrom( \"C:\\\" );")]
[LuaMethod("openrom", "opens the Open ROM dialog")]
@ -261,24 +249,15 @@ namespace BizHawk.Client.EmuHawk
[LuaMethodExample("client.opentasstudio( );")]
[LuaMethod("opentasstudio", "opens the TAStudio dialog")]
public static void OpenTasStudio()
{
GlobalWin.Tools.Load<TAStudio>();
}
public void OpenTasStudio() => APIs.Tool.OpenTasStudio();
[LuaMethodExample("client.opentoolbox( );")]
[LuaMethod("opentoolbox", "opens the Toolbox Dialog")]
public static void OpenToolBox()
{
GlobalWin.Tools.Load<ToolBox>();
}
public void OpenToolBox() => APIs.Tool.OpenToolBox();
[LuaMethodExample("client.opentracelogger( );")]
[LuaMethod("opentracelogger", "opens the tracelogger if it is available for the given core")]
public static void OpenTraceLogger()
{
GlobalWin.Tools.Load<TraceLogger>();
}
public void OpenTraceLogger() => APIs.Tool.OpenTraceLogger();
[LuaMethodExample("client.pause( );")]
[LuaMethod("pause", "Pauses the emulator")]
@ -465,38 +444,16 @@ namespace BizHawk.Client.EmuHawk
[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)
{
var toolType = ReflectionUtil.GetTypeByName(name)
.FirstOrDefault(x => typeof(IToolForm).IsAssignableFrom(x) && !x.IsInterface);
if (toolType != null)
{
GlobalWin.Tools.Load(toolType);
}
var selectedTool = GlobalWin.Tools.AvailableTools
.FirstOrDefault(tool => tool.GetType().Name.ToLower() == name.ToLower());
if (selectedTool != null)
{
return LuaHelper.ToLuaTable(Lua, selectedTool);
}
return null;
var selectedTool = APIs.Tool.GetTool(name);
return selectedTool == null ? null : LuaHelper.ToLuaTable(Lua, selectedTool);
}
[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)
{
var possibleTypes = ReflectionUtil.GetTypeByName(name);
if (possibleTypes.Any())
{
var instance = Activator.CreateInstance(possibleTypes.First());
return LuaHelper.ToLuaTable(Lua, instance);
}
return null;
var instance = APIs.Tool.GetTool(name);
return instance == null ? null : LuaHelper.ToLuaTable(Lua, instance);
}
[LuaMethodExample("client.displaymessages( true );")]

View File

@ -1,13 +1,12 @@
using System;
using System.Linq;
using System.Windows.Forms;
using NLua;
using BizHawk.Client.Common;
using NLua;
namespace BizHawk.Client.EmuHawk
{
public sealed class InputLuaLibrary : LuaLibraryBase
public sealed class InputLuaLibrary : DelegatingLuaLibraryEmu
{
public InputLuaLibrary(Lua lua)
: base(lua) { }
@ -21,32 +20,20 @@ namespace BizHawk.Client.EmuHawk
[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.")]
public LuaTable Get()
{
var buttons = Lua.NewTable();
foreach (var kvp in Global.ControllerInputCoalescer.BoolButtons().Where(kvp => kvp.Value))
{
buttons[kvp.Key] = true;
}
return buttons;
var result = APIs.Input.Get();
var table = Lua.NewTable();
foreach (var kvp in result) table[kvp.Key] = kvp.Value;
return table;
}
[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.")]
public LuaTable GetMouse()
{
var buttons = Lua.NewTable();
// TODO - need to specify whether in "emu" or "native" coordinate space.
var p = GlobalWin.DisplayManager.UntransformPoint(Control.MousePosition);
buttons["X"] = p.X;
buttons["Y"] = p.Y;
buttons[MouseButtons.Left.ToString()] = (Control.MouseButtons & MouseButtons.Left) != 0;
buttons[MouseButtons.Middle.ToString()] = (Control.MouseButtons & MouseButtons.Middle) != 0;
buttons[MouseButtons.Right.ToString()] = (Control.MouseButtons & MouseButtons.Right) != 0;
buttons[MouseButtons.XButton1.ToString()] = (Control.MouseButtons & MouseButtons.XButton1) != 0;
buttons[MouseButtons.XButton2.ToString()] = (Control.MouseButtons & MouseButtons.XButton2) != 0;
buttons["Wheel"] = GlobalWin.MainForm.MouseWheelTracker;
return buttons;
var result = APIs.Input.GetMouse();
var table = Lua.NewTable();
foreach (var kvp in result) table[kvp.Key] = kvp.Value;
return table;
}
}
}

View File

@ -1,13 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using NLua;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
{
public sealed class SavestateLuaLibrary : LuaLibraryBase
public sealed class SavestateLuaLibrary : DelegatingLuaLibraryEmu
{
public SavestateLuaLibrary(Lua lua)
: base(lua) { }
@ -19,43 +18,18 @@ namespace BizHawk.Client.EmuHawk
[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)
{
if (!File.Exists(path))
{
Log($"could not find file: {path}");
}
else
{
GlobalWin.MainForm.LoadState(path, Path.GetFileName(path), true, suppressOSD);
}
}
public void Load(string path, bool suppressOSD = false) => APIs.SaveState.Load(path, suppressOSD);
[LuaMethodExample("savestate.loadslot( 7 );")]
[LuaMethod("loadslot", "Loads the savestate at the given slot number (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.")]
public void LoadSlot(int slotNum, bool suppressOSD = false)
{
if (slotNum >= 0 && slotNum <= 9)
{
GlobalWin.MainForm.LoadQuickSave($"QuickSave{slotNum}", true, suppressOSD);
}
}
public void LoadSlot(int slotNum, bool suppressOSD = false) => APIs.SaveState.LoadSlot(slotNum, suppressOSD);
[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)
{
GlobalWin.MainForm.SaveState(path, path, true, suppressOSD);
}
public void Save(string path, bool suppressOSD = false) => APIs.SaveState.Save(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.")]
public void SaveSlot(int slotNum, bool suppressOSD = false)
{
if (slotNum >= 0 && slotNum <= 9)
{
GlobalWin.MainForm.SaveQuickSave($"QuickSave{slotNum}", true, suppressOSD);
}
}
public void SaveSlot(int slotNum, bool suppressOSD = false) => APIs.SaveState.SaveSlot(slotNum, suppressOSD);
}
}

View File

@ -24,13 +24,13 @@ namespace BizHawk.Client.EmuHawk
public EmuLuaLibrary(IEmulatorServiceProvider serviceProvider)
: this()
{
static APISubsetContainer InitApiHawkContainerInstance(IEmulatorServiceProvider sp, Action<string> logCallback)
static ApiContainer InitApiHawkContainerInstance(IEmulatorServiceProvider sp, Action<string> logCallback)
{
var ctorParamTypes = new[] { typeof(Action<string>) };
var ctorParams = new object[] { logCallback };
var libDict = new Dictionary<Type, IExternalApi>();
foreach (var api in Assembly.Load("BizHawk.Client.ApiHawk").GetTypes()
.Concat(Assembly.GetAssembly(typeof(APISubsetContainer)).GetTypes())
foreach (var api in Assembly.GetAssembly(typeof(EmuApi)).GetTypes()
.Concat(Assembly.GetAssembly(typeof(ToolApi)).GetTypes())
.Where(t => t.IsSealed && typeof(IExternalApi).IsAssignableFrom(t) && ServiceInjector.IsAvailable(sp, t)))
{
var ctorWithParams = api.GetConstructor(ctorParamTypes);
@ -38,7 +38,7 @@ namespace BizHawk.Client.EmuHawk
ServiceInjector.UpdateServices(sp, instance);
libDict.Add(api, instance);
}
return ApiHawkContainerInstance = new APISubsetContainer(libDict);
return ApiHawkContainerInstance = new ApiContainer(libDict);
}
LuaWait = new AutoResetEvent(false);
@ -73,8 +73,11 @@ namespace BizHawk.Client.EmuHawk
instance.LuaRegister(lib, Docs);
instance.LogOutputCallback = ConsoleLuaLibrary.LogOutput;
ServiceInjector.UpdateServices(serviceProvider, instance);
if (instance is DelegatingLuaLibrary dlgInstance)
dlgInstance.APIs = ApiHawkContainerInstance ?? InitApiHawkContainerInstance(serviceProvider, ConsoleLuaLibrary.LogOutput);
ApiHawkContainerInstance ??= InitApiHawkContainerInstance(serviceProvider, ConsoleLuaLibrary.LogOutput);
if (instance is DelegatingLuaLibraryEmu dlgInstanceEmu) dlgInstanceEmu.APIs = ApiHawkContainerInstance; // this is necessary as the property has the `new` modifier
else if (instance is DelegatingLuaLibrary dlgInstance) dlgInstance.APIs = ApiHawkContainerInstance;
Libraries.Add(lib, instance);
}
}
@ -98,7 +101,7 @@ namespace BizHawk.Client.EmuHawk
}
/// <remarks>lazily instantiated</remarks>
private static APISubsetContainer ApiHawkContainerInstance;
private static ApiContainer ApiHawkContainerInstance;
private Lua _lua = new Lua();
private Lua _currThread;