2013-10-28 19:13:01 +00:00
using System ;
using System.Linq ;
2014-06-03 02:19:13 +00:00
using System.ComponentModel ;
2013-12-30 01:58:44 +00:00
2013-10-28 19:13:01 +00:00
using LuaInterface ;
2014-12-04 00:43:12 +00:00
using BizHawk.Emulation.Common ;
using BizHawk.Emulation.Common.IEmulatorExtensions ;
2014-06-03 02:19:13 +00:00
2016-08-25 19:11:23 +00:00
using BizHawk.Emulation.Cores.Nintendo.N64 ;
2013-11-01 14:51:51 +00:00
namespace BizHawk.Client.Common
2013-10-28 19:13:01 +00:00
{
2014-06-03 02:19:13 +00:00
[Description("A library for registering lua functions to emulator events.\n All events support multiple registered methods.\nAll registered event methods can be named and return a Guid when registered")]
2014-06-01 22:02:59 +00:00
public sealed class EventLuaLibrary : LuaLibraryBase
2013-10-28 19:13:01 +00:00
{
2015-01-01 20:01:37 +00:00
[OptionalService]
public IInputPollable InputPollableCore { get ; set ; }
[OptionalService]
public IDebuggable DebuggableCore { get ; set ; }
2015-01-25 22:14:58 +00:00
[RequiredService]
public IEmulator Emulator { get ; set ; }
2014-02-03 20:48:01 +00:00
private readonly LuaFunctionList _luaFunctions = new LuaFunctionList ( ) ;
2014-05-21 00:17:35 +00:00
public EventLuaLibrary ( Lua lua )
: base ( lua ) { }
public EventLuaLibrary ( Lua lua , Action < string > logOutputCallback )
: base ( lua , logOutputCallback ) { }
2013-11-01 14:51:51 +00:00
2017-04-14 19:59:01 +00:00
public override string Name = > "event" ;
2013-10-31 16:58:56 +00:00
2017-04-15 20:37:30 +00:00
#region Events Library Helpers
2013-10-28 19:13:01 +00:00
2014-05-26 03:08:16 +00:00
public void CallExitEvent ( Lua thread )
{
2017-05-17 18:18:26 +00:00
var exitCallbacks = _luaFunctions . Where ( l = > l . Lua = = thread & & l . Event = = "OnExit" ) ;
2014-05-26 03:08:16 +00:00
foreach ( var exitCallback in exitCallbacks )
{
exitCallback . Call ( ) ;
}
}
2017-05-17 18:18:26 +00:00
public LuaFunctionList RegisteredFunctions = > _luaFunctions ;
2013-10-28 19:13:01 +00:00
2013-10-31 16:58:56 +00:00
public void CallSaveStateEvent ( string name )
2013-10-28 19:13:01 +00:00
{
2017-05-17 18:18:26 +00:00
var lfs = _luaFunctions . Where ( l = > l . Event = = "OnSavestateSave" ) . ToList ( ) ;
2013-10-28 19:13:01 +00:00
if ( lfs . Any ( ) )
{
try
{
2013-11-26 01:21:24 +00:00
foreach ( var lf in lfs )
2013-10-28 19:13:01 +00:00
{
lf . Call ( name ) ;
}
}
2015-03-28 00:26:10 +00:00
catch ( Exception e )
2013-10-28 19:13:01 +00:00
{
2014-05-19 01:42:41 +00:00
Log (
2013-11-01 14:51:51 +00:00
"error running function attached by lua function event.onsavestate" +
"\nError message: " +
e . Message ) ;
2013-10-28 19:13:01 +00:00
}
}
}
2013-10-31 16:58:56 +00:00
public void CallLoadStateEvent ( string name )
2013-10-28 19:13:01 +00:00
{
2017-05-17 18:18:26 +00:00
var lfs = _luaFunctions . Where ( l = > l . Event = = "OnSavestateLoad" ) . ToList ( ) ;
2013-10-28 19:13:01 +00:00
if ( lfs . Any ( ) )
{
try
{
2013-11-26 01:21:24 +00:00
foreach ( var lf in lfs )
2013-10-28 19:13:01 +00:00
{
lf . Call ( name ) ;
}
}
2015-03-28 00:26:10 +00:00
catch ( Exception e )
2013-10-28 19:13:01 +00:00
{
2014-05-19 01:42:41 +00:00
Log (
2013-11-01 14:51:51 +00:00
"error running function attached by lua function event.onloadstate" +
"\nError message: " +
e . Message ) ;
2013-10-28 19:13:01 +00:00
}
}
}
2013-10-31 16:58:56 +00:00
public void CallFrameBeforeEvent ( )
2013-10-28 19:13:01 +00:00
{
2017-05-17 18:18:26 +00:00
var lfs = _luaFunctions . Where ( l = > l . Event = = "OnFrameStart" ) . ToList ( ) ;
2013-10-28 19:13:01 +00:00
if ( lfs . Any ( ) )
{
try
{
2013-11-26 01:21:24 +00:00
foreach ( var lf in lfs )
2013-10-28 19:13:01 +00:00
{
lf . Call ( ) ;
}
}
2015-03-28 00:26:10 +00:00
catch ( Exception e )
2013-10-28 19:13:01 +00:00
{
2014-05-19 01:42:41 +00:00
Log (
2013-11-01 14:51:51 +00:00
"error running function attached by lua function event.onframestart" +
"\nError message: " +
e . Message ) ;
2013-10-28 19:13:01 +00:00
}
}
}
2013-10-31 16:58:56 +00:00
public void CallFrameAfterEvent ( )
2013-10-28 19:13:01 +00:00
{
2017-05-17 18:18:26 +00:00
var lfs = _luaFunctions . Where ( l = > l . Event = = "OnFrameEnd" ) . ToList ( ) ;
2013-10-28 19:13:01 +00:00
if ( lfs . Any ( ) )
{
try
{
2013-11-26 01:21:24 +00:00
foreach ( var lf in lfs )
2013-10-28 19:13:01 +00:00
{
lf . Call ( ) ;
}
}
2015-03-28 00:26:10 +00:00
catch ( Exception e )
2013-10-28 19:13:01 +00:00
{
2014-05-19 01:42:41 +00:00
Log (
2013-11-01 14:51:51 +00:00
"error running function attached by lua function event.onframeend" +
"\nError message: " +
e . Message ) ;
2013-10-28 19:13:01 +00:00
}
}
}
2016-08-25 19:11:23 +00:00
private bool N64CoreTypeDynarec ( )
{
if ( Emulator is N64 )
{
if ( ( Emulator as N64 ) . GetSyncSettings ( ) . Core = = N64SyncSettings . CoreType . Dynarec )
{
Log ( "N64 Error: Memory callbacks are not implemented for Dynamic Recompiler core type\nUse Interpreter or Pure Interpreter\n" ) ;
return true ;
}
}
2017-05-17 18:18:26 +00:00
2016-08-25 19:11:23 +00:00
return false ;
}
2014-12-05 01:56:45 +00:00
private void LogMemoryCallbacksNotImplemented ( )
{
2017-05-17 18:18:26 +00:00
Log ( $"{Emulator.Attributes().CoreName} does not implement memory callbacks" ) ;
2015-01-25 22:14:58 +00:00
}
private void LogMemoryExecuteCallbacksNotImplemented ( )
{
2017-05-17 18:18:26 +00:00
Log ( $"{Emulator.Attributes().CoreName} does not implement memory execute callbacks" ) ;
2014-12-05 01:56:45 +00:00
}
2013-10-28 19:13:01 +00:00
#endregion
2014-01-25 21:37:25 +00:00
[ LuaMethodAttributes (
2017-05-09 18:19:55 +00:00
"onframeend" , "Calls the given lua function at the end of each frame, after all emulation and drawing has completed. Note: this is the default behavior of lua scripts" ) ]
2014-01-25 21:37:25 +00:00
public string OnFrameEnd ( LuaFunction luaf , string name = null )
2013-10-28 19:13:01 +00:00
{
2013-11-26 01:21:24 +00:00
var nlf = new NamedLuaFunction ( luaf , "OnFrameEnd" , LogOutputCallback , CurrentThread , name ) ;
2013-11-10 19:19:58 +00:00
_luaFunctions . Add ( nlf ) ;
2013-11-26 01:21:24 +00:00
return nlf . Guid . ToString ( ) ;
2013-10-28 19:13:01 +00:00
}
2014-01-25 21:37:25 +00:00
[ LuaMethodAttributes (
2017-05-09 18:19:55 +00:00
"onframestart" , "Calls the given lua function at the beginning of each frame before any emulation and drawing occurs" ) ]
2014-01-25 21:37:25 +00:00
public string OnFrameStart ( LuaFunction luaf , string name = null )
2013-10-28 19:13:01 +00:00
{
2013-11-26 01:21:24 +00:00
var nlf = new NamedLuaFunction ( luaf , "OnFrameStart" , LogOutputCallback , CurrentThread , name ) ;
2013-11-10 19:19:58 +00:00
_luaFunctions . Add ( nlf ) ;
2013-11-26 01:21:24 +00:00
return nlf . Guid . ToString ( ) ;
2013-10-28 19:13:01 +00:00
}
2014-01-25 21:37:25 +00:00
[ LuaMethodAttributes (
2017-05-09 18:19:55 +00:00
"oninputpoll" , "Calls the given lua function after each time the emulator core polls for input" ) ]
2015-01-01 20:01:37 +00:00
public string OnInputPoll ( LuaFunction luaf , string name = null )
2013-10-28 19:13:01 +00:00
{
2013-11-26 01:21:24 +00:00
var nlf = new NamedLuaFunction ( luaf , "OnInputPoll" , LogOutputCallback , CurrentThread , name ) ;
2013-11-10 19:19:58 +00:00
_luaFunctions . Add ( nlf ) ;
2014-12-04 00:43:12 +00:00
2015-01-01 20:01:37 +00:00
if ( InputPollableCore ! = null )
2014-12-04 00:43:12 +00:00
{
try
{
2015-01-01 20:01:37 +00:00
InputPollableCore . InputCallbacks . Add ( nlf . Callback ) ;
return nlf . Guid . ToString ( ) ;
2014-12-04 00:43:12 +00:00
}
catch ( NotImplementedException )
{
LogNotImplemented ( ) ;
2015-01-01 20:01:37 +00:00
return Guid . Empty . ToString ( ) ;
2014-12-04 00:43:12 +00:00
}
}
2017-04-14 19:59:01 +00:00
LogNotImplemented ( ) ;
return Guid . Empty . ToString ( ) ;
2014-12-04 00:43:12 +00:00
}
private void LogNotImplemented ( )
{
Log ( string . Format ( "Error: {0} does not yet implement input polling callbacks" ) ) ;
2013-10-28 19:13:01 +00:00
}
2014-01-25 21:37:25 +00:00
[ LuaMethodAttributes (
2017-05-09 18:19:55 +00:00
"onloadstate" , "Fires after a state is loaded. Receives a lua function name, and registers it to the event immediately following a successful savestate event" ) ]
2014-01-25 21:37:25 +00:00
public string OnLoadState ( LuaFunction luaf , string name = null )
2013-10-28 19:13:01 +00:00
{
2013-11-26 01:21:24 +00:00
var nlf = new NamedLuaFunction ( luaf , "OnSavestateLoad" , LogOutputCallback , CurrentThread , name ) ;
2013-11-10 19:19:58 +00:00
_luaFunctions . Add ( nlf ) ;
2013-11-26 01:21:24 +00:00
return nlf . Guid . ToString ( ) ;
2013-10-28 19:13:01 +00:00
}
2017-05-09 18:19:55 +00:00
[LuaMethodAttributes("onmemoryexecute", "Fires after the given address is executed by the core")]
2014-01-27 01:15:56 +00:00
public string OnMemoryExecute ( LuaFunction luaf , uint address , string name = null )
2013-11-12 01:51:07 +00:00
{
2015-01-01 20:01:37 +00:00
try
2014-12-05 01:56:45 +00:00
{
2015-01-25 22:14:58 +00:00
if ( DebuggableCore ! = null & & DebuggableCore . MemoryCallbacksAvailable ( ) & &
DebuggableCore . MemoryCallbacks . ExecuteCallbacksAvailable )
2015-01-01 20:01:37 +00:00
{
2016-08-25 19:11:23 +00:00
if ( N64CoreTypeDynarec ( ) )
2017-04-14 19:59:01 +00:00
{
2016-08-25 19:11:23 +00:00
return Guid . Empty . ToString ( ) ;
2017-04-14 19:59:01 +00:00
}
2016-08-25 19:11:23 +00:00
2015-01-01 20:01:37 +00:00
var nlf = new NamedLuaFunction ( luaf , "OnMemoryExecute" , LogOutputCallback , CurrentThread , name ) ;
_luaFunctions . Add ( nlf ) ;
2014-12-05 01:56:45 +00:00
2015-01-01 20:01:37 +00:00
DebuggableCore . MemoryCallbacks . Add (
2016-08-08 11:37:39 +00:00
new MemoryCallback ( MemoryCallbackType . Execute , "Lua Hook" , nlf . Callback , address , null ) ) ;
2015-01-01 20:01:37 +00:00
return nlf . Guid . ToString ( ) ;
}
2014-12-05 01:56:45 +00:00
}
2017-04-14 19:59:01 +00:00
catch ( NotImplementedException )
2014-12-05 01:56:45 +00:00
{
2015-01-25 22:14:58 +00:00
LogMemoryExecuteCallbacksNotImplemented ( ) ;
2014-12-05 01:56:45 +00:00
return Guid . Empty . ToString ( ) ;
}
2015-01-01 20:01:37 +00:00
2015-01-25 22:14:58 +00:00
LogMemoryExecuteCallbacksNotImplemented ( ) ;
2015-01-01 20:01:37 +00:00
return Guid . Empty . ToString ( ) ;
2013-11-12 01:51:07 +00:00
}
2014-01-25 21:37:25 +00:00
[ LuaMethodAttributes (
2017-05-09 18:19:55 +00:00
"onmemoryread" , "Fires after the given address is read by the core. If no address is given, it will attach to every memory read" ) ]
2014-01-27 01:15:56 +00:00
public string OnMemoryRead ( LuaFunction luaf , uint? address = null , string name = null )
2013-10-28 19:13:01 +00:00
{
2015-01-01 20:01:37 +00:00
try
2014-12-05 01:56:45 +00:00
{
2015-01-25 22:14:58 +00:00
if ( DebuggableCore ! = null & & DebuggableCore . MemoryCallbacksAvailable ( ) )
2015-01-01 20:01:37 +00:00
{
2016-08-25 19:11:23 +00:00
if ( N64CoreTypeDynarec ( ) )
2017-04-14 19:59:01 +00:00
{
2016-08-25 19:11:23 +00:00
return Guid . Empty . ToString ( ) ;
2017-04-14 19:59:01 +00:00
}
2016-08-25 19:11:23 +00:00
2015-01-01 20:01:37 +00:00
var nlf = new NamedLuaFunction ( luaf , "OnMemoryRead" , LogOutputCallback , CurrentThread , name ) ;
_luaFunctions . Add ( nlf ) ;
DebuggableCore . MemoryCallbacks . Add (
2016-08-08 11:37:39 +00:00
new MemoryCallback ( MemoryCallbackType . Read , "Lua Hook" , nlf . Callback , address , null ) ) ;
2015-01-01 20:01:37 +00:00
return nlf . Guid . ToString ( ) ;
}
2014-12-05 01:56:45 +00:00
}
2015-01-01 20:01:37 +00:00
catch ( NotImplementedException )
2014-12-05 01:56:45 +00:00
{
LogMemoryCallbacksNotImplemented ( ) ;
return Guid . Empty . ToString ( ) ;
}
2015-01-01 20:01:37 +00:00
LogMemoryCallbacksNotImplemented ( ) ;
return Guid . Empty . ToString ( ) ;
2013-10-28 19:13:01 +00:00
}
2014-01-25 21:37:25 +00:00
[ LuaMethodAttributes (
2017-05-09 18:19:55 +00:00
"onmemorywrite" , "Fires after the given address is written by the core. If no address is given, it will attach to every memory write" ) ]
2014-01-27 01:15:56 +00:00
public string OnMemoryWrite ( LuaFunction luaf , uint? address = null , string name = null )
2013-10-28 19:13:01 +00:00
{
2015-01-01 20:01:37 +00:00
try
2014-12-05 01:56:45 +00:00
{
2015-01-25 22:14:58 +00:00
if ( DebuggableCore ! = null & & DebuggableCore . MemoryCallbacksAvailable ( ) )
2015-01-01 20:01:37 +00:00
{
2016-08-25 19:11:23 +00:00
if ( N64CoreTypeDynarec ( ) )
2017-04-14 19:59:01 +00:00
{
2016-08-25 19:11:23 +00:00
return Guid . Empty . ToString ( ) ;
2017-04-14 19:59:01 +00:00
}
2016-08-25 19:11:23 +00:00
2015-01-01 20:01:37 +00:00
var nlf = new NamedLuaFunction ( luaf , "OnMemoryWrite" , LogOutputCallback , CurrentThread , name ) ;
_luaFunctions . Add ( nlf ) ;
DebuggableCore . MemoryCallbacks . Add (
2016-08-08 11:37:39 +00:00
new MemoryCallback ( MemoryCallbackType . Write , "Lua Hook" , nlf . Callback , address , null ) ) ;
2015-01-01 20:01:37 +00:00
return nlf . Guid . ToString ( ) ;
}
2014-12-05 01:56:45 +00:00
}
2015-01-01 20:01:37 +00:00
catch ( NotImplementedException )
2014-12-05 01:56:45 +00:00
{
LogMemoryCallbacksNotImplemented ( ) ;
return Guid . Empty . ToString ( ) ;
}
2015-01-01 20:01:37 +00:00
LogMemoryCallbacksNotImplemented ( ) ;
return Guid . Empty . ToString ( ) ;
2013-10-28 19:13:01 +00:00
}
2017-05-09 18:19:55 +00:00
[LuaMethodAttributes("onsavestate", "Fires after a state is saved")]
2014-01-25 21:37:25 +00:00
public string OnSaveState ( LuaFunction luaf , string name = null )
2013-10-28 19:13:01 +00:00
{
2013-11-26 01:21:24 +00:00
var nlf = new NamedLuaFunction ( luaf , "OnSavestateSave" , LogOutputCallback , CurrentThread , name ) ;
2014-05-26 03:08:16 +00:00
_luaFunctions . Add ( nlf ) ;
return nlf . Guid . ToString ( ) ;
}
2017-05-09 18:19:55 +00:00
[LuaMethodAttributes("onexit", "Fires after the calling script has stopped")]
2014-05-26 03:08:16 +00:00
public string OnExit ( LuaFunction luaf , string name = null )
{
var nlf = new NamedLuaFunction ( luaf , "OnExit" , LogOutputCallback , CurrentThread , name ) ;
2013-11-10 19:19:58 +00:00
_luaFunctions . Add ( nlf ) ;
2013-11-26 01:21:24 +00:00
return nlf . Guid . ToString ( ) ;
2013-10-28 19:13:01 +00:00
}
2014-01-25 21:37:25 +00:00
[ LuaMethodAttributes (
2017-05-09 18:19:55 +00:00
"unregisterbyid" , "Removes the registered function that matches the guid. If a function is found and remove the function will return true. If unable to find a match, the function will return false." ) ]
2014-06-02 01:05:51 +00:00
public bool UnregisterById ( string guid )
2013-10-28 19:13:01 +00:00
{
2013-11-26 01:21:24 +00:00
foreach ( var nlf in _luaFunctions . Where ( nlf = > nlf . Guid . ToString ( ) = = guid . ToString ( ) ) )
2013-10-28 19:13:01 +00:00
{
2013-12-19 01:02:50 +00:00
_luaFunctions . Remove ( nlf ) ;
2013-11-26 01:21:24 +00:00
return true ;
2013-10-28 19:13:01 +00:00
}
return false ;
}
2014-01-25 21:37:25 +00:00
[ LuaMethodAttributes (
2017-05-09 18:19:55 +00:00
"unregisterbyname" , "Removes the first registered function that matches Name. If a function is found and remove the function will return true. If unable to find a match, the function will return false." ) ]
2014-01-26 18:36:27 +00:00
public bool UnregisterByName ( string name )
2013-10-28 19:13:01 +00:00
{
2014-01-26 18:36:27 +00:00
foreach ( var nlf in _luaFunctions . Where ( nlf = > nlf . Name = = name ) )
2013-10-28 19:13:01 +00:00
{
2013-12-19 01:02:50 +00:00
_luaFunctions . Remove ( nlf ) ;
2013-11-26 01:21:24 +00:00
return true ;
2013-10-28 19:13:01 +00:00
}
return false ;
}
}
}