2021-01-16 19:40:28 +00:00
# if defined(__linux__) || defined(__unix__)
2020-10-22 01:21:49 +00:00
# include <stdlib.h>
2008-11-02 19:10:53 +00:00
# include <unistd.h>
2010-05-01 18:48:42 +00:00
# define SetCurrentDir chdir
2008-11-02 19:10:53 +00:00
# include <sys/types.h>
# include <sys/wait.h>
2020-10-22 01:21:49 +00:00
# include <libgen.h>
2020-10-22 01:40:27 +00:00
# elif __APPLE__
2020-10-22 01:52:32 +00:00
# include <stdlib.h>
2020-10-31 20:08:22 +00:00
# include <unistd.h>
2020-10-22 01:40:27 +00:00
# include <libgen.h>
2020-10-22 01:52:32 +00:00
# include <mach-o/dyld.h>
2020-10-31 20:12:40 +00:00
# define SetCurrentDir chdir
2008-11-02 19:10:53 +00:00
# endif
2010-05-01 18:48:42 +00:00
# ifdef WIN32
2021-02-07 17:23:31 +00:00
# include <Windows.h>
2010-05-01 18:48:42 +00:00
# include <direct.h>
# define SetCurrentDir _chdir
# endif
2008-11-02 19:10:53 +00:00
# include "types.h"
# include "fceu.h"
2018-11-25 17:25:55 +00:00
# include "file.h"
2008-11-02 19:10:53 +00:00
# include "video.h"
2013-05-26 15:18:30 +00:00
# include "debug.h"
2011-02-26 15:20:30 +00:00
# include "sound.h"
2008-11-02 19:10:53 +00:00
# include "drawing.h"
# include "state.h"
# include "movie.h"
# include "driver.h"
# include "cheat.h"
2009-10-11 08:50:09 +00:00
# include "x6502.h"
2016-12-22 15:15:38 +00:00
# include "ppu.h"
2008-11-02 19:10:53 +00:00
# include "utils/xstring.h"
# include "utils/memory.h"
2015-06-23 21:04:31 +00:00
# include "utils/crc32.h"
2008-11-02 19:10:53 +00:00
# include "fceulua.h"
2018-11-25 17:25:55 +00:00
extern char FileBase [ ] ;
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-08 18:19:49 +00:00
# include "drivers/win/common.h"
2016-07-03 12:01:17 +00:00
# include "drivers/win/main.h"
2012-02-23 19:47:41 +00:00
# include "drivers/win/taseditor/selection.h"
2012-08-25 19:59:07 +00:00
# include "drivers/win/taseditor/laglog.h"
2012-01-29 22:33:23 +00:00
# include "drivers/win/taseditor/markers.h"
# include "drivers/win/taseditor/snapshot.h"
2012-01-12 20:15:23 +00:00
# include "drivers/win/taseditor/taseditor_lua.h"
2017-02-23 21:29:43 +00:00
# include "drivers/win/cdlogger.h"
2021-11-04 11:01:33 +00:00
extern TASEDITOR_LUA taseditor_lua ;
2008-11-02 19:10:53 +00:00
# endif
2020-10-22 00:45:50 +00:00
# ifdef __SDL__
# ifdef __QT_DRIVER__
2022-08-22 02:43:35 +00:00
# include "drivers/Qt/sdl.h"
# include "drivers/Qt/main.h"
# include "drivers/Qt/input.h"
2020-10-22 00:45:50 +00:00
# include "drivers/Qt/fceuWrapper.h"
2021-10-24 21:58:45 +00:00
# include "drivers/Qt/TasEditor/selection.h"
# include "drivers/Qt/TasEditor/laglog.h"
# include "drivers/Qt/TasEditor/markers.h"
# include "drivers/Qt/TasEditor/snapshot.h"
# include "drivers/Qt/TasEditor/taseditor_lua.h"
extern TASEDITOR_LUA * taseditor_lua ;
2020-10-22 00:45:50 +00:00
# else
int LoadGame ( const char * path , bool silent = false ) ;
int reloadLastGame ( void ) ;
2020-12-12 11:59:07 +00:00
void fceuWrapperRequestAppExit ( void ) ;
2020-10-22 00:45:50 +00:00
# endif
# endif
2013-04-13 02:52:13 +00:00
# include <cstdio>
# include <cstdlib>
# include <cstring>
# include <cctype>
# include <cassert>
# include <cstdlib>
# include <cmath>
# include <zlib.h>
# include <vector>
# include <map>
# include <string>
# include <algorithm>
2013-09-15 11:35:14 +00:00
# include <bitset>
2013-04-13 02:52:13 +00:00
2013-04-13 23:17:46 +00:00
# include "x6502abbrev.h"
2013-03-20 22:17:14 +00:00
bool CheckLua ( )
2013-03-20 14:46:55 +00:00
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2013-03-20 14:46:55 +00:00
HMODULE mod = LoadLibrary ( " lua51.dll " ) ;
if ( ! mod )
{
return false ;
}
FreeLibrary ( mod ) ;
return true ;
# else
return true ;
# endif
}
2013-03-20 22:17:14 +00:00
bool DemandLua ( )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2013-03-20 22:17:14 +00:00
if ( ! CheckLua ( ) )
{
MessageBox ( NULL , " lua51.dll was not found. Please get it into your PATH or in the same directory as fceux.exe " , " FCEUX " , MB_OK | MB_ICONERROR ) ;
return false ;
}
return true ;
# else
return true ;
# endif
}
2009-10-31 12:07:26 +00:00
extern " C "
{
# include <lua.h>
# include <lauxlib.h>
# include <lualib.h>
2009-11-03 06:11:55 +00:00
# ifdef WIN32
2013-07-08 23:00:57 +00:00
# include <lstate.h>
2009-11-03 06:11:55 +00:00
int iuplua_open ( lua_State * L ) ;
int iupcontrolslua_open ( lua_State * L ) ;
2011-10-31 19:11:31 +00:00
int luaopen_winapi ( lua_State * L ) ;
2010-06-10 06:13:51 +00:00
2018-12-16 18:20:38 +00:00
int imlua_open ( lua_State * L ) ;
int cdlua_open ( lua_State * L ) ;
int cdluaim_open ( lua_State * L ) ;
2010-06-10 06:13:51 +00:00
//luasocket
int luaopen_socket_core ( lua_State * L ) ;
int luaopen_mime_core ( lua_State * L ) ;
2009-11-03 06:11:55 +00:00
# endif
2009-10-31 12:07:26 +00:00
}
2008-11-02 19:10:53 +00:00
# ifndef TRUE
# define TRUE 1
# define FALSE 0
# endif
2009-10-19 03:09:25 +00:00
# ifndef _MSC_VER
2009-10-12 19:28:07 +00:00
# define stricmp strcasecmp
# define strnicmp strncasecmp
2012-10-21 16:40:04 +00:00
2009-10-12 19:28:07 +00:00
# ifdef __GNUC__
# define __forceinline __attribute__ ((always_inline))
2012-10-21 16:40:04 +00:00
# else
# define __forceinline
2009-10-12 19:28:07 +00:00
# endif
2009-10-12 19:05:23 +00:00
# endif
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2008-12-28 01:36:02 +00:00
extern void AddRecentLuaFile ( const char * filename ) ;
# endif
2012-02-25 20:54:13 +00:00
extern bool turbo ;
2014-03-15 22:04:04 +00:00
extern int32 fps_scale ;
2012-02-25 20:54:13 +00:00
2008-11-02 19:10:53 +00:00
struct LuaSaveState {
std : : string filename ;
2010-05-17 21:02:38 +00:00
EMUFILE_MEMORY * data ;
2008-11-02 19:10:53 +00:00
bool anonymous , persisted ;
LuaSaveState ( )
2012-10-21 16:40:04 +00:00
: data ( 0 )
2008-11-02 19:10:53 +00:00
, anonymous ( false )
, persisted ( false )
{ }
~ LuaSaveState ( ) {
if ( data ) delete data ;
}
void persist ( ) {
persisted = true ;
FILE * outf = fopen ( filename . c_str ( ) , " wb " ) ;
fwrite ( data - > buf ( ) , 1 , data - > size ( ) , outf ) ;
fclose ( outf ) ;
}
void ensureLoad ( ) {
if ( data ) return ;
persisted = true ;
FILE * inf = fopen ( filename . c_str ( ) , " rb " ) ;
fseek ( inf , 0 , SEEK_END ) ;
int len = ftell ( inf ) ;
fseek ( inf , 0 , SEEK_SET ) ;
2010-05-17 21:02:38 +00:00
data = new EMUFILE_MEMORY ( len ) ;
2008-11-02 19:10:53 +00:00
fread ( data - > buf ( ) , 1 , len , inf ) ;
fclose ( inf ) ;
}
} ;
2019-07-23 19:12:03 +00:00
static void ( * info_print ) ( intptr_t uid , const char * str ) ;
static void ( * info_onstart ) ( intptr_t uid ) ;
static void ( * info_onstop ) ( intptr_t uid ) ;
static intptr_t info_uid ;
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2009-10-19 03:01:26 +00:00
extern HWND LuaConsoleHWnd ;
extern INT_PTR CALLBACK DlgLuaScriptDialog ( HWND hDlg , UINT msg , WPARAM wParam , LPARAM lParam ) ;
2020-07-20 01:15:56 +00:00
void TaseditorDisableManualFunctionIfNeeded ( ) ;
2020-10-31 16:24:52 +00:00
# else
int LuaKillMessageBox ( void ) ;
2020-10-31 16:40:31 +00:00
# ifdef __linux__
2020-11-29 03:38:57 +00:00
# ifndef __THROWNL
# define __THROWNL throw () // Build fix Alpine Linux libc
# endif
2020-10-31 16:24:52 +00:00
int LuaPrintfToWindowConsole ( const char * __restrict format , . . . )
__THROWNL __attribute__ ( ( __format__ ( __printf__ , 1 , 2 ) ) ) ;
2020-10-31 16:40:31 +00:00
# else
2021-02-07 17:23:31 +00:00
# ifdef WIN32
int LuaPrintfToWindowConsole ( _In_z_ _Printf_format_string_ const char * format , . . . ) ;
# else
2020-10-31 16:40:31 +00:00
int LuaPrintfToWindowConsole ( const char * __restrict format , . . . ) throw ( ) ;
# endif
2021-02-07 17:23:31 +00:00
# endif
2020-07-20 01:15:56 +00:00
# endif
2019-07-23 19:12:03 +00:00
extern void PrintToWindowConsole ( intptr_t hDlgAsInt , const char * str ) ;
extern void WinLuaOnStart ( intptr_t hDlgAsInt ) ;
extern void WinLuaOnStop ( intptr_t hDlgAsInt ) ;
2009-10-19 03:01:26 +00:00
2008-11-02 19:10:53 +00:00
static lua_State * L ;
2010-05-01 16:42:06 +00:00
static int luaexiterrorcount = 8 ;
2008-11-02 19:10:53 +00:00
// Are we running any code right now?
static char * luaScriptName = NULL ;
// Are we running any code right now?
2008-12-08 18:17:28 +00:00
int luaRunning = FALSE ;
2008-11-02 19:10:53 +00:00
// True at the frame boundary, false otherwise.
static int frameBoundary = FALSE ;
// The execution speed we're running at.
static enum { SPEED_NORMAL , SPEED_NOTHROTTLE , SPEED_TURBO , SPEED_MAXIMUM } speedmode = SPEED_NORMAL ;
// Rerecord count skip mode
static int skipRerecords = FALSE ;
// Used by the registry to find our functions
static const char * frameAdvanceThread = " FCEU.FrameAdvance " ;
static const char * guiCallbackTable = " FCEU.GUI " ;
// True if there's a thread waiting to run after a run of frame-advance.
static int frameAdvanceWaiting = FALSE ;
// We save our pause status in the case of a natural death.
static int wasPaused = FALSE ;
2009-10-12 07:32:31 +00:00
// Transparency strength. 255=opaque, 0=so transparent it's invisible
static int transparencyModifier = 255 ;
2008-11-02 19:10:53 +00:00
2019-12-15 20:31:09 +00:00
// Our zapper.
static int luazapperx = - 1 ;
static int luazappery = - 1 ;
static int luazapperfire = - 1 ;
2008-11-02 19:10:53 +00:00
// Our joypads.
2009-09-15 16:39:49 +00:00
static uint8 luajoypads1 [ 4 ] = { 0xFF , 0xFF , 0xFF , 0xFF } ; //x1
static uint8 luajoypads2 [ 4 ] = { 0x00 , 0x00 , 0x00 , 0x00 } ; //0x
/* Crazy logic stuff.
11 - true 01 - pass - through ( default )
00 - false 10 - invert */
2008-11-02 19:10:53 +00:00
static enum { GUI_USED_SINCE_LAST_DISPLAY , GUI_USED_SINCE_LAST_FRAME , GUI_CLEAR } gui_used = GUI_CLEAR ;
static uint8 * gui_data = NULL ;
static int gui_saw_current_palette = FALSE ;
// Protects Lua calls from going nuts.
// We set this to a big number like 1000 and decrement it
// over time. The script gets knifed once this reaches zero.
static int numTries ;
2009-10-11 08:34:10 +00:00
// number of registered memory functions (1 per hooked byte)
static unsigned int numMemHooks ;
2008-11-02 19:10:53 +00:00
// Look in fceu.h for macros named like JOY_UP to determine the order.
static const char * button_mappings [ ] = {
" A " , " B " , " select " , " start " , " up " , " down " , " left " , " right "
} ;
2009-10-19 03:01:26 +00:00
# ifdef _MSC_VER
# define snprintf _snprintf
# define vscprintf _vscprintf
# else
# define stricmp strcasecmp
# define strnicmp strncasecmp
# endif
2009-10-08 05:02:22 +00:00
static const char * luaCallIDStrings [ ] =
{
" CALL_BEFOREEMULATION " ,
" CALL_AFTEREMULATION " ,
" CALL_BEFOREEXIT " ,
2009-10-25 03:02:00 +00:00
" CALL_BEFORESAVE " ,
" CALL_AFTERLOAD " ,
2012-01-08 18:19:49 +00:00
" CALL_TASEDITOR_AUTO " ,
" CALL_TASEDITOR_MANUAL " ,
2009-10-08 05:02:22 +00:00
} ;
2009-10-12 19:04:28 +00:00
//make sure we have the right number of strings
CTASSERT ( sizeof ( luaCallIDStrings ) / sizeof ( * luaCallIDStrings ) = = LUACALL_COUNT )
2009-10-11 08:34:10 +00:00
static const char * luaMemHookTypeStrings [ ] =
{
" MEMHOOK_WRITE " ,
" MEMHOOK_READ " ,
" MEMHOOK_EXEC " ,
" MEMHOOK_WRITE_SUB " ,
" MEMHOOK_READ_SUB " ,
" MEMHOOK_EXEC_SUB " ,
} ;
2009-10-12 19:04:28 +00:00
//make sure we have the right number of strings
CTASSERT ( sizeof ( luaMemHookTypeStrings ) / sizeof ( * luaMemHookTypeStrings ) = = LUAMEMHOOK_COUNT )
2009-10-08 05:02:22 +00:00
2009-10-19 03:01:26 +00:00
static char * rawToCString ( lua_State * L , int idx = 0 ) ;
static const char * toCString ( lua_State * L , int idx = 0 ) ;
2020-12-12 10:38:11 +00:00
static int exitScheduled = FALSE ;
2008-11-02 19:10:53 +00:00
/**
* Resets emulator speed / pause states after script exit .
*/
2012-10-21 16:40:04 +00:00
static void FCEU_LuaOnStop ( )
2012-09-23 12:45:28 +00:00
{
2008-11-02 19:10:53 +00:00
luaRunning = FALSE ;
2019-12-15 20:31:09 +00:00
luazapperx = - 1 ;
luazappery = - 1 ;
luazapperfire = - 1 ;
2009-09-15 16:39:49 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
luajoypads1 [ i ] = 0xFF ; // Set these back to pass-through
luajoypads2 [ i ] = 0x00 ;
}
2008-11-02 19:10:53 +00:00
gui_used = GUI_CLEAR ;
2010-04-07 21:45:06 +00:00
//if (wasPaused && !FCEUI_EmulationPaused())
// FCEUI_ToggleEmulationPause();
2014-11-22 01:44:51 +00:00
//zero 21-nov-2014 - this variable doesnt exist outside windows so it cant have this feature
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2014-03-15 22:04:04 +00:00
if ( fps_scale ! = 256 ) //thanks, we already know it's on normal speed
FCEUD_SetEmulationSpeed ( EMUSPEED_NORMAL ) ; //TODO: Ideally lua returns the speed to the speed the user set before running the script
2009-03-14 21:00:28 +00:00
//rather than returning it to normal, and turbo off. Perhaps some flags and a FCEUD_GetEmulationSpeed function
2014-11-22 01:44:51 +00:00
# endif
2012-02-25 20:54:13 +00:00
turbo = false ;
//FCEUD_TurboOff();
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-07-19 19:40:40 +00:00
TaseditorDisableManualFunctionIfNeeded ( ) ;
2012-01-08 18:19:49 +00:00
# endif
2008-11-02 19:10:53 +00:00
}
/**
* Asks Lua if it wants control of the emulator ' s speed .
* Returns 0 if no , 1 if yes . If yes , caller should also
* consult FCEU_LuaFrameSkip ( ) .
*/
int FCEU_LuaSpeed ( ) {
if ( ! L | | ! luaRunning )
return 0 ;
//printf("%d\n", speedmode);
switch ( speedmode ) {
case SPEED_NOTHROTTLE :
case SPEED_TURBO :
case SPEED_MAXIMUM :
return 1 ;
case SPEED_NORMAL :
default :
return 0 ;
}
}
/**
* Asks Lua if it wants control whether this frame is skipped .
* Returns 0 if no , 1 if frame should be skipped , - 1 if it should not be .
*/
2019-01-28 02:32:22 +00:00
int FCEU_LuaFrameskip ( ) {
2008-11-02 19:10:53 +00:00
if ( ! L | | ! luaRunning )
return 0 ;
switch ( speedmode ) {
case SPEED_NORMAL :
return 0 ;
case SPEED_NOTHROTTLE :
return - 1 ;
case SPEED_TURBO :
return 0 ;
case SPEED_MAXIMUM :
return 1 ;
}
return 0 ;
}
2008-11-21 15:07:09 +00:00
/**
* Toggle certain rendering planes
2009-10-11 04:39:05 +00:00
* emu . setrenderingplanes ( sprites , background )
2008-11-21 15:07:09 +00:00
* Accepts two ( lua ) boolean values and acts accordingly
*/
2009-10-11 04:39:05 +00:00
static int emu_setrenderplanes ( lua_State * L ) {
2008-11-21 15:07:09 +00:00
bool sprites = ( lua_toboolean ( L , 1 ) = = 1 ) ;
bool background = ( lua_toboolean ( L , 2 ) = = 1 ) ;
FCEUI_SetRenderPlanes ( sprites , background ) ;
return 0 ;
}
2008-11-02 19:10:53 +00:00
///////////////////////////
2009-10-11 04:39:05 +00:00
// emu.speedmode(string mode)
2008-11-02 19:10:53 +00:00
//
// Takes control of the emulation speed
// of the system. Normal is normal speed (60fps, 50 for PAL),
// nothrottle disables speed control but renders every frame,
// turbo renders only a few frames in order to speed up emulation,
// maximum renders no frames
// TODO: better enforcement, done in the same way as basicbot...
2009-10-11 04:39:05 +00:00
static int emu_speedmode ( lua_State * L ) {
2008-11-02 19:10:53 +00:00
const char * mode = luaL_checkstring ( L , 1 ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
if ( strcasecmp ( mode , " normal " ) = = 0 ) {
speedmode = SPEED_NORMAL ;
} else if ( strcasecmp ( mode , " nothrottle " ) = = 0 ) {
speedmode = SPEED_NOTHROTTLE ;
} else if ( strcasecmp ( mode , " turbo " ) = = 0 ) {
speedmode = SPEED_TURBO ;
} else if ( strcasecmp ( mode , " maximum " ) = = 0 ) {
speedmode = SPEED_MAXIMUM ;
} else
2009-10-11 04:39:05 +00:00
luaL_error ( L , " Invalid mode %s to emu.speedmode " , mode ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
//printf("new speed mode: %d\n", speedmode);
2018-11-16 02:49:19 +00:00
if ( speedmode = = SPEED_NORMAL )
2010-06-12 17:12:46 +00:00
{
2009-02-23 15:35:21 +00:00
FCEUD_SetEmulationSpeed ( EMUSPEED_NORMAL ) ;
2010-06-12 17:12:46 +00:00
FCEUD_TurboOff ( ) ;
}
2018-11-16 02:49:19 +00:00
else if ( speedmode = = SPEED_TURBO ) //adelikat: Making turbo actually use turbo.
2009-02-23 16:36:03 +00:00
FCEUD_TurboOn ( ) ; //Turbo and max speed are two different results. Turbo employs frame skipping and sound bypassing if mute turbo option is enabled.
//This makes it faster but with frame skipping. Therefore, maximum is still a useful feature, in case the user is recording an avi or making screenshots (or something else that needs all frames)
2012-10-21 16:40:04 +00:00
else
2009-02-23 15:35:21 +00:00
FCEUD_SetEmulationSpeed ( EMUSPEED_FASTEST ) ; //TODO: Make nothrottle turn off throttle, or remove the option
2008-11-02 19:10:53 +00:00
return 0 ;
}
2009-10-11 04:39:05 +00:00
// emu.poweron()
2009-03-28 13:38:44 +00:00
//
// Executes a power cycle
2009-10-11 04:39:05 +00:00
static int emu_poweron ( lua_State * L ) {
2009-03-28 13:38:44 +00:00
if ( GameInfo )
FCEUI_PowerNES ( ) ;
2012-10-21 16:40:04 +00:00
2009-03-28 13:38:44 +00:00
return 0 ;
}
2017-05-15 05:09:13 +00:00
static int emu_debuggerloop ( lua_State * L ) {
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2017-05-15 05:09:13 +00:00
extern void win_debuggerLoop ( ) ;
win_debuggerLoop ( ) ;
# endif
return 0 ;
}
2017-05-15 17:21:58 +00:00
static int emu_debuggerloopstep ( lua_State * L ) {
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2017-05-15 17:21:58 +00:00
extern void win_debuggerLoopStep ( ) ;
win_debuggerLoopStep ( ) ;
# endif
return 0 ;
}
2009-10-11 04:39:05 +00:00
// emu.softreset()
2009-03-28 13:38:44 +00:00
//
// Executes a power cycle
2009-10-11 04:39:05 +00:00
static int emu_softreset ( lua_State * L ) {
2009-03-28 13:38:44 +00:00
if ( GameInfo )
FCEUI_ResetNES ( ) ;
2012-10-21 16:40:04 +00:00
2009-03-28 13:38:44 +00:00
return 0 ;
}
2008-11-02 19:10:53 +00:00
2009-10-11 04:39:05 +00:00
// emu.frameadvance()
2008-11-02 19:10:53 +00:00
//
// Executes a frame advance. Occurs by yielding the coroutine, then re-running
// when we break out.
2009-10-11 04:39:05 +00:00
static int emu_frameadvance ( lua_State * L ) {
2008-11-02 19:10:53 +00:00
// We're going to sleep for a frame-advance. Take notes.
2012-10-21 16:40:04 +00:00
if ( frameAdvanceWaiting )
2009-10-11 04:39:05 +00:00
return luaL_error ( L , " can't call emu.frameadvance() from here " ) ;
2008-11-02 19:10:53 +00:00
frameAdvanceWaiting = TRUE ;
2012-10-21 16:40:04 +00:00
// Now we can yield to the main
2008-11-02 19:10:53 +00:00
return lua_yield ( L , 0 ) ;
// It's actually rather disappointing...
}
2012-02-01 22:02:26 +00:00
// bool emu.paused()
2012-01-08 18:19:49 +00:00
static int emu_paused ( lua_State * L )
{
lua_pushboolean ( L , FCEUI_EmulationPaused ( ) ! = 0 ) ;
return 1 ;
}
2008-11-02 19:10:53 +00:00
2009-10-11 04:39:05 +00:00
// emu.pause()
2008-11-02 19:10:53 +00:00
//
2012-05-01 21:05:35 +00:00
// Pauses the emulator. Returns immediately.
static int emu_pause ( lua_State * L )
{
2008-11-02 19:10:53 +00:00
if ( ! FCEUI_EmulationPaused ( ) )
FCEUI_ToggleEmulationPause ( ) ;
2012-05-01 21:05:35 +00:00
return 0 ;
2008-11-02 19:10:53 +00:00
}
2009-10-11 04:39:05 +00:00
//emu.unpause()
2009-08-16 15:27:17 +00:00
//
2012-05-01 21:05:35 +00:00
// Unpauses the emulator. Returns immediately.
static int emu_unpause ( lua_State * L )
{
2009-08-16 15:27:17 +00:00
if ( FCEUI_EmulationPaused ( ) )
FCEUI_ToggleEmulationPause ( ) ;
2012-05-01 21:05:35 +00:00
return 0 ;
2009-08-16 15:27:17 +00:00
}
2008-11-02 19:10:53 +00:00
2009-10-11 04:39:05 +00:00
// emu.message(string msg)
2008-11-02 19:10:53 +00:00
//
// Displays the given message on the screen.
2009-10-11 04:39:05 +00:00
static int emu_message ( lua_State * L ) {
2008-11-02 19:10:53 +00:00
const char * msg = luaL_checkstring ( L , 1 ) ;
2010-05-16 04:18:49 +00:00
FCEU_DispMessage ( " %s " , 0 , msg ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
return 0 ;
2016-07-03 12:01:17 +00:00
}
// emu.getdir()
//
// Returns the path of fceux.exe as a string.
static int emu_getdir ( lua_State * L ) {
# ifdef WIN32
2021-04-12 02:11:31 +00:00
char fullPath [ 2048 ] ;
char driveLetter [ 3 ] ;
char directory [ 2048 ] ;
char finalPath [ 2048 ] ;
2016-07-03 12:01:17 +00:00
2021-04-12 10:27:04 +00:00
GetModuleFileNameA ( NULL , fullPath , 2048 ) ;
2016-07-03 12:01:17 +00:00
_splitpath ( fullPath , driveLetter , directory , NULL , NULL ) ;
snprintf ( finalPath , sizeof ( finalPath ) , " %s%s " , driveLetter , directory ) ;
lua_pushstring ( L , finalPath ) ;
return 1 ;
2020-10-22 01:21:49 +00:00
# elif __linux__
char exePath [ 2048 ] ;
2021-07-10 18:17:42 +00:00
ssize_t count = : : readlink ( " /proc/self/exe " , exePath , sizeof ( exePath ) - 1 ) ;
2020-10-22 01:21:49 +00:00
if ( count > 0 )
{
char * dir ;
exePath [ count ] = 0 ;
//printf("EXE Path: '%s' \n", exePath );
dir = : : dirname ( exePath ) ;
if ( dir )
{
//printf("DIR Path: '%s' \n", dir );
lua_pushstring ( L , dir ) ;
return 1 ;
}
}
# elif __APPLE__
char exePath [ 2048 ] ;
uint32_t bufSize = sizeof ( exePath ) ;
int result = _NSGetExecutablePath ( exePath , & bufSize ) ;
if ( result = = 0 )
{
2020-10-22 01:40:27 +00:00
char * dir ;
2021-07-10 18:17:42 +00:00
exePath [ sizeof ( exePath ) - 1 ] = 0 ;
2020-10-22 01:21:49 +00:00
//printf("EXE Path: '%s' \n", exePath );
2020-10-22 01:40:27 +00:00
dir = : : dirname ( exePath ) ;
2020-10-22 01:21:49 +00:00
2020-10-22 01:40:27 +00:00
if ( dir )
2020-10-22 01:21:49 +00:00
{
2020-10-22 01:40:27 +00:00
//printf("DIR Path: '%s' \n", dir );
lua_pushstring ( L , dir ) ;
2020-10-22 01:21:49 +00:00
return 1 ;
}
}
2016-07-03 12:01:17 +00:00
# endif
2020-06-20 03:58:12 +00:00
return 0 ;
2016-07-03 12:01:17 +00:00
}
2008-11-02 19:10:53 +00:00
2017-04-19 05:37:19 +00:00
extern void ReloadRom ( void ) ;
2020-10-22 00:45:50 +00:00
2021-11-16 05:10:34 +00:00
//#ifdef __QT_DRIVER__
//static int emu_wait_for_rom_load(lua_State *L)
//{
// fceuWrapperUnLock();
// printf("Waiting for ROM\n");
// #ifdef WIN32
// msleep(1000);
// #else
// usleep(1000000);
// #endif
// fceuWrapperLock();
//
// return 0;
//}
//#endif
2017-04-19 05:37:19 +00:00
2016-07-03 12:01:17 +00:00
// emu.loadrom(string filename)
//
// Loads the rom from the directory relative to the lua script or from the absolute path.
2020-01-30 23:26:52 +00:00
// If the rom can't be loaded, loads the most recent one.
2020-10-22 00:45:50 +00:00
static int emu_loadrom ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2017-04-19 05:37:19 +00:00
const char * str = lua_tostring ( L , 1 ) ;
//special case: reload rom
2020-11-02 02:45:36 +00:00
if ( ! str ) {
2017-04-19 05:37:19 +00:00
ReloadRom ( ) ;
return 0 ;
}
2016-07-03 12:01:17 +00:00
const char * nameo2 = luaL_checkstring ( L , 1 ) ;
char nameo [ 2048 ] ;
strncpy ( nameo , nameo2 , sizeof ( nameo ) ) ;
if ( ! ALoad ( nameo ) ) {
extern void LoadRecentRom ( int slot ) ;
LoadRecentRom ( 0 ) ;
2020-11-02 02:45:36 +00:00
}
if ( GameInfo )
{
//printf("Currently Loaded ROM: '%s'\n", GameInfo->filename );
lua_pushstring ( L , GameInfo - > filename ) ;
2016-07-03 12:01:17 +00:00
return 1 ;
}
2020-11-02 02:45:36 +00:00
return 0 ;
2021-11-16 05:10:34 +00:00
# elif defined(__QT_DRIVER__)
2020-10-21 20:05:26 +00:00
const char * nameo2 = luaL_checkstring ( L , 1 ) ;
char nameo [ 2048 ] ;
2020-10-31 20:08:22 +00:00
2021-11-16 05:10:34 +00:00
strncpy ( nameo , nameo2 , sizeof ( nameo ) ) ;
nameo [ sizeof ( nameo ) - 1 ] = 0 ;
LoadGameFromLua ( nameo ) ;
2021-02-07 17:23:31 +00:00
2021-11-16 05:10:34 +00:00
//lua_cpcall(L, emu_wait_for_rom_load, NULL);
2020-11-02 02:45:36 +00:00
//printf("Attempting to Load ROM: '%s'\n", nameo );
2021-11-16 05:10:34 +00:00
//if (!LoadGame(nameo, true))
//{
// //printf("Failed to Load ROM: '%s'\n", nameo );
// reloadLastGame();
//}
2020-11-02 02:45:36 +00:00
if ( GameInfo )
{
//printf("Currently Loaded ROM: '%s'\n", GameInfo->filename );
lua_pushstring ( L , GameInfo - > filename ) ;
2020-10-21 20:05:26 +00:00
return 1 ;
2020-11-02 02:45:36 +00:00
} else {
return 0 ;
2020-10-21 20:05:26 +00:00
}
2016-07-03 12:01:17 +00:00
# endif
2020-11-02 02:45:36 +00:00
return 0 ;
2008-11-02 19:10:53 +00:00
}
2020-12-12 10:38:11 +00:00
// emu.exit()
//
// Closes the fceux
static int emu_exit ( lua_State * L ) {
exitScheduled = TRUE ;
return 0 ;
}
2008-11-02 19:10:53 +00:00
2009-10-11 04:39:05 +00:00
static int emu_registerbefore ( lua_State * L ) {
2009-10-08 05:02:22 +00:00
if ( ! lua_isnil ( L , 1 ) )
luaL_checktype ( L , 1 , LUA_TFUNCTION ) ;
lua_settop ( L , 1 ) ;
lua_getfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_BEFOREEMULATION ] ) ;
lua_insert ( L , 1 ) ;
lua_setfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_BEFOREEMULATION ] ) ;
//StopScriptIfFinished(luaStateToUIDMap[L]);
return 1 ;
}
2009-10-11 04:39:05 +00:00
static int emu_registerafter ( lua_State * L ) {
2009-10-08 05:02:22 +00:00
if ( ! lua_isnil ( L , 1 ) )
luaL_checktype ( L , 1 , LUA_TFUNCTION ) ;
lua_settop ( L , 1 ) ;
lua_getfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_AFTEREMULATION ] ) ;
lua_insert ( L , 1 ) ;
lua_setfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_AFTEREMULATION ] ) ;
//StopScriptIfFinished(luaStateToUIDMap[L]);
return 1 ;
}
2009-10-11 04:39:05 +00:00
static int emu_registerexit ( lua_State * L ) {
2009-10-08 05:02:22 +00:00
if ( ! lua_isnil ( L , 1 ) )
luaL_checktype ( L , 1 , LUA_TFUNCTION ) ;
lua_settop ( L , 1 ) ;
lua_getfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_BEFOREEXIT ] ) ;
lua_insert ( L , 1 ) ;
lua_setfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_BEFOREEXIT ] ) ;
//StopScriptIfFinished(luaStateToUIDMap[L]);
return 1 ;
}
2010-05-08 20:34:27 +00:00
static int emu_addgamegenie ( lua_State * L ) {
2012-10-21 16:40:04 +00:00
2010-05-08 20:34:27 +00:00
const char * msg = luaL_checkstring ( L , 1 ) ;
// Add a Game Genie code if it hasn't already been added
int GGaddr , GGcomp , GGval ;
int i = 0 ;
uint32 Caddr ;
uint8 Cval ;
int Ccompare , Ctype ;
2012-10-21 16:40:04 +00:00
2010-05-08 20:34:27 +00:00
if ( ! FCEUI_DecodeGG ( msg , & GGaddr , & GGval , & GGcomp ) ) {
2010-05-10 07:20:55 +00:00
luaL_error ( L , " Failed to decode game genie code " ) ;
lua_pushboolean ( L , false ) ;
return 1 ;
2010-05-08 20:34:27 +00:00
}
while ( FCEUI_GetCheat ( i , NULL , & Caddr , & Cval , & Ccompare , NULL , & Ctype ) ) {
if ( ( GGaddr = = Caddr ) & & ( GGval = = Cval ) & & ( GGcomp = = Ccompare ) & & ( Ctype = = 1 ) ) {
// Already Added, so consider it a success
2010-05-10 07:20:55 +00:00
lua_pushboolean ( L , true ) ;
2010-05-08 20:34:27 +00:00
return 1 ;
}
i = i + 1 ;
}
2012-10-21 16:40:04 +00:00
2010-05-08 20:34:27 +00:00
if ( FCEUI_AddCheat ( msg , GGaddr , GGval , GGcomp , 1 ) ) {
// Code was added
// Can't manage the display update the way I want, so I won't bother with it
// UpdateCheatsAdded();
2010-05-10 07:20:55 +00:00
lua_pushboolean ( L , true ) ;
2010-05-08 20:34:27 +00:00
return 1 ;
} else {
// Code didn't get added
2010-05-10 07:20:55 +00:00
lua_pushboolean ( L , false ) ;
return 1 ;
2010-05-08 20:34:27 +00:00
}
}
static int emu_delgamegenie ( lua_State * L ) {
2012-10-21 16:40:04 +00:00
2010-05-08 20:34:27 +00:00
const char * msg = luaL_checkstring ( L , 1 ) ;
// Remove a Game Genie code. Very restrictive about deleted code.
int GGaddr , GGcomp , GGval ;
uint32 i = 0 ;
2022-08-19 16:37:05 +00:00
std : : string Cname ;
2010-05-08 20:34:27 +00:00
uint32 Caddr ;
uint8 Cval ;
int Ccompare , Ctype ;
2012-10-21 16:40:04 +00:00
2010-05-08 20:34:27 +00:00
if ( ! FCEUI_DecodeGG ( msg , & GGaddr , & GGval , & GGcomp ) ) {
2010-05-10 07:20:55 +00:00
luaL_error ( L , " Failed to decode game genie code " ) ;
lua_pushboolean ( L , false ) ;
return 1 ;
2010-05-08 20:34:27 +00:00
}
while ( FCEUI_GetCheat ( i , & Cname , & Caddr , & Cval , & Ccompare , NULL , & Ctype ) ) {
2022-08-19 16:37:05 +00:00
if ( ( Cname = = msg ) & & ( GGaddr = = Caddr ) & & ( GGval = = Cval ) & & ( GGcomp = = Ccompare ) & & ( Ctype = = 1 ) ) {
2010-05-08 20:34:27 +00:00
// Delete cheat code
2010-05-10 07:20:55 +00:00
if ( FCEUI_DelCheat ( i ) ) {
lua_pushboolean ( L , true ) ;
2010-05-08 20:34:27 +00:00
return 1 ;
2010-05-10 07:20:55 +00:00
}
else {
lua_pushboolean ( L , false ) ;
return 1 ;
}
2010-05-08 20:34:27 +00:00
}
i = i + 1 ;
}
// Cheat didn't exist, so it's not an error
2010-05-10 07:20:55 +00:00
lua_pushboolean ( L , true ) ;
2010-05-08 20:34:27 +00:00
return 1 ;
}
2009-10-08 05:02:22 +00:00
2009-10-25 03:02:00 +00:00
// can't remember what the best way of doing this is...
# if defined(i386) || defined(__i386) || defined(__i386__) || defined(M_I86) || defined(_M_IX86) || defined(WIN32)
# define IS_LITTLE_ENDIAN
# endif
// push a value's bytes onto the output stack
template < typename T >
void PushBinaryItem ( T item , std : : vector < unsigned char > & output )
{
unsigned char * buf = ( unsigned char * ) & item ;
# ifdef IS_LITTLE_ENDIAN
for ( int i = sizeof ( T ) ; i ; i - - )
output . push_back ( * buf + + ) ;
# else
int vecsize = output . size ( ) ;
for ( int i = sizeof ( T ) ; i ; i - - )
output . insert ( output . begin ( ) + vecsize , * buf + + ) ;
# endif
}
// read a value from the byte stream and advance the stream by its size
template < typename T >
T AdvanceByteStream ( const unsigned char * & data , unsigned int & remaining )
{
# ifdef IS_LITTLE_ENDIAN
T rv = * ( T * ) data ;
data + = sizeof ( T ) ;
# else
T rv ; unsigned char * rvptr = ( unsigned char * ) & rv ;
for ( int i = sizeof ( T ) - 1 ; i > = 0 ; i - - )
rvptr [ i ] = * data + + ;
# endif
remaining - = sizeof ( T ) ;
return rv ;
}
// advance the byte stream by a certain size without reading a value
void AdvanceByteStream ( const unsigned char * & data , unsigned int & remaining , int amount )
{
data + = amount ;
remaining - = amount ;
}
# define LUAEXT_TLONG 30 // 0x1E // 4-byte signed integer
# define LUAEXT_TUSHORT 31 // 0x1F // 2-byte unsigned integer
# define LUAEXT_TSHORT 32 // 0x20 // 2-byte signed integer
# define LUAEXT_TBYTE 33 // 0x21 // 1-byte unsigned integer
# define LUAEXT_TNILS 34 // 0x22 // multiple nils represented by a 4-byte integer (warning: becomes multiple stack entities)
# define LUAEXT_TTABLE 0x40 // 0x40 through 0x4F // tables of different sizes:
# define LUAEXT_BITS_1A 0x01 // size of array part fits in a 1-byte unsigned integer
# define LUAEXT_BITS_2A 0x02 // size of array part fits in a 2-byte unsigned integer
# define LUAEXT_BITS_4A 0x03 // size of array part fits in a 4-byte unsigned integer
# define LUAEXT_BITS_1H 0x04 // size of hash part fits in a 1-byte unsigned integer
# define LUAEXT_BITS_2H 0x08 // size of hash part fits in a 2-byte unsigned integer
# define LUAEXT_BITS_4H 0x0C // size of hash part fits in a 4-byte unsigned integer
# define BITMATCH(x,y) (((x) & (y)) == (y))
static void PushNils ( std : : vector < unsigned char > & output , int & nilcount )
{
int count = nilcount ;
nilcount = 0 ;
static const int minNilsWorthEncoding = 6 ; // because a LUAEXT_TNILS entry is 5 bytes
if ( count < minNilsWorthEncoding )
{
for ( int i = 0 ; i < count ; i + + )
output . push_back ( LUA_TNIL ) ;
}
else
{
output . push_back ( LUAEXT_TNILS ) ;
PushBinaryItem < uint32 > ( count , output ) ;
}
}
static std : : vector < const void * > s_tableAddressStack ; // prevents infinite recursion of a table within a table (when cycle is found, print something like table:parent)
static std : : vector < const void * > s_metacallStack ; // prevents infinite recursion if something's __tostring returns another table that contains that something (when cycle is found, print the inner result without using __tostring)
static void LuaStackToBinaryConverter ( lua_State * L , int i , std : : vector < unsigned char > & output )
{
int type = lua_type ( L , i ) ;
// the first byte of every serialized item says what Lua type it is
output . push_back ( type & 0xFF ) ;
switch ( type )
{
default :
{
char errmsg [ 1024 ] ;
sprintf ( errmsg , " values of type \" %s \" are not allowed to be returned from registered save functions. \r \n " , luaL_typename ( L , i ) ) ;
if ( info_print )
info_print ( info_uid , errmsg ) ;
else
puts ( errmsg ) ;
}
break ;
case LUA_TNIL :
// no information necessary beyond the type
break ;
case LUA_TBOOLEAN :
// serialize as 0 or 1
output . push_back ( lua_toboolean ( L , i ) ) ;
break ;
case LUA_TSTRING :
// serialize as a 0-terminated string of characters
{
const char * str = lua_tostring ( L , i ) ;
while ( * str )
output . push_back ( * str + + ) ;
output . push_back ( ' \0 ' ) ;
}
break ;
case LUA_TNUMBER :
{
double num = ( double ) lua_tonumber ( L , i ) ;
int32 inum = ( int32 ) lua_tointeger ( L , i ) ;
if ( num ! = inum )
{
PushBinaryItem ( num , output ) ;
}
else
{
if ( ( inum & ~ 0xFF ) = = 0 )
type = LUAEXT_TBYTE ;
else if ( ( uint16 ) ( inum & 0xFFFF ) = = inum )
type = LUAEXT_TUSHORT ;
else if ( ( int16 ) ( inum & 0xFFFF ) = = inum )
type = LUAEXT_TSHORT ;
else
type = LUAEXT_TLONG ;
output . back ( ) = type ;
switch ( type )
{
case LUAEXT_TLONG :
PushBinaryItem < int32 > ( static_cast < int32 > ( inum ) , output ) ;
break ;
case LUAEXT_TUSHORT :
PushBinaryItem < uint16 > ( static_cast < uint16 > ( inum ) , output ) ;
break ;
case LUAEXT_TSHORT :
PushBinaryItem < int16 > ( static_cast < int16 > ( inum ) , output ) ;
break ;
case LUAEXT_TBYTE :
output . push_back ( static_cast < uint8 > ( inum ) ) ;
break ;
}
}
}
break ;
case LUA_TTABLE :
// serialize as a type that describes how many bytes are used for storing the counts,
// followed by the number of array entries if any, then the number of hash entries if any,
// then a Lua value per array entry, then a (key,value) pair of Lua values per hashed entry
// note that the structure of table references are not faithfully serialized (yet)
{
int outputTypeIndex = output . size ( ) - 1 ;
int arraySize = 0 ;
int hashSize = 0 ;
if ( lua_checkstack ( L , 4 ) & & std : : find ( s_tableAddressStack . begin ( ) , s_tableAddressStack . end ( ) , lua_topointer ( L , i ) ) = = s_tableAddressStack . end ( ) )
{
s_tableAddressStack . push_back ( lua_topointer ( L , i ) ) ;
struct Scope { ~ Scope ( ) { s_tableAddressStack . pop_back ( ) ; } } scope ;
bool wasnil = false ;
int nilcount = 0 ;
arraySize = lua_objlen ( L , i ) ;
int arrayValIndex = lua_gettop ( L ) + 1 ;
for ( int j = 1 ; j < = arraySize ; j + + )
{
lua_rawgeti ( L , i , j ) ;
bool isnil = lua_isnil ( L , arrayValIndex ) ;
if ( isnil )
nilcount + + ;
else
{
if ( wasnil )
PushNils ( output , nilcount ) ;
LuaStackToBinaryConverter ( L , arrayValIndex , output ) ;
}
lua_pop ( L , 1 ) ;
wasnil = isnil ;
}
if ( wasnil )
PushNils ( output , nilcount ) ;
if ( arraySize )
lua_pushinteger ( L , arraySize ) ; // before first key
else
lua_pushnil ( L ) ; // before first key
int keyIndex = lua_gettop ( L ) ;
int valueIndex = keyIndex + 1 ;
while ( lua_next ( L , i ) )
{
// assert(lua_type(L, keyIndex) && "nil key in Lua table, impossible");
// assert(lua_type(L, valueIndex) && "nil value in Lua table, impossible");
LuaStackToBinaryConverter ( L , keyIndex , output ) ;
LuaStackToBinaryConverter ( L , valueIndex , output ) ;
lua_pop ( L , 1 ) ;
hashSize + + ;
}
}
int outputType = LUAEXT_TTABLE ;
if ( arraySize & 0xFFFF0000 )
outputType | = LUAEXT_BITS_4A ;
else if ( arraySize & 0xFF00 )
outputType | = LUAEXT_BITS_2A ;
else if ( arraySize & 0xFF )
outputType | = LUAEXT_BITS_1A ;
if ( hashSize & 0xFFFF0000 )
outputType | = LUAEXT_BITS_4H ;
else if ( hashSize & 0xFF00 )
outputType | = LUAEXT_BITS_2H ;
else if ( hashSize & 0xFF )
outputType | = LUAEXT_BITS_1H ;
output [ outputTypeIndex ] = outputType ;
int insertIndex = outputTypeIndex ;
if ( BITMATCH ( outputType , LUAEXT_BITS_4A ) | | BITMATCH ( outputType , LUAEXT_BITS_2A ) | | BITMATCH ( outputType , LUAEXT_BITS_1A ) )
output . insert ( output . begin ( ) + ( + + insertIndex ) , arraySize & 0xFF ) ;
if ( BITMATCH ( outputType , LUAEXT_BITS_4A ) | | BITMATCH ( outputType , LUAEXT_BITS_2A ) )
output . insert ( output . begin ( ) + ( + + insertIndex ) , ( arraySize & 0xFF00 ) > > 8 ) ;
if ( BITMATCH ( outputType , LUAEXT_BITS_4A ) )
output . insert ( output . begin ( ) + ( + + insertIndex ) , ( arraySize & 0x00FF0000 ) > > 16 ) ,
output . insert ( output . begin ( ) + ( + + insertIndex ) , ( arraySize & 0xFF000000 ) > > 24 ) ;
if ( BITMATCH ( outputType , LUAEXT_BITS_4H ) | | BITMATCH ( outputType , LUAEXT_BITS_2H ) | | BITMATCH ( outputType , LUAEXT_BITS_1H ) )
output . insert ( output . begin ( ) + ( + + insertIndex ) , hashSize & 0xFF ) ;
if ( BITMATCH ( outputType , LUAEXT_BITS_4H ) | | BITMATCH ( outputType , LUAEXT_BITS_2H ) )
output . insert ( output . begin ( ) + ( + + insertIndex ) , ( hashSize & 0xFF00 ) > > 8 ) ;
if ( BITMATCH ( outputType , LUAEXT_BITS_4H ) )
output . insert ( output . begin ( ) + ( + + insertIndex ) , ( hashSize & 0x00FF0000 ) > > 16 ) ,
output . insert ( output . begin ( ) + ( + + insertIndex ) , ( hashSize & 0xFF000000 ) > > 24 ) ;
} break ;
}
}
// complements LuaStackToBinaryConverter
void BinaryToLuaStackConverter ( lua_State * L , const unsigned char * & data , unsigned int & remaining )
{
// assert(s_dbg_dataSize - (data - s_dbg_dataStart) == remaining);
unsigned char type = AdvanceByteStream < unsigned char > ( data , remaining ) ;
switch ( type )
{
default :
{
char errmsg [ 1024 ] ;
if ( type < = 10 & & type ! = LUA_TTABLE )
sprintf ( errmsg , " values of type \" %s \" are not allowed to be loaded into registered load functions. The save state's Lua save data file might be corrupted. \r \n " , lua_typename ( L , type ) ) ;
else
sprintf ( errmsg , " The save state's Lua save data file seems to be corrupted. \r \n " ) ;
if ( info_print )
info_print ( info_uid , errmsg ) ;
else
puts ( errmsg ) ;
}
break ;
case LUA_TNIL :
lua_pushnil ( L ) ;
break ;
case LUA_TBOOLEAN :
lua_pushboolean ( L , AdvanceByteStream < uint8 > ( data , remaining ) ) ;
break ;
case LUA_TSTRING :
lua_pushstring ( L , ( const char * ) data ) ;
AdvanceByteStream ( data , remaining , strlen ( ( const char * ) data ) + 1 ) ;
break ;
case LUA_TNUMBER :
lua_pushnumber ( L , AdvanceByteStream < double > ( data , remaining ) ) ;
break ;
case LUAEXT_TLONG :
lua_pushinteger ( L , AdvanceByteStream < int32 > ( data , remaining ) ) ;
break ;
case LUAEXT_TUSHORT :
lua_pushinteger ( L , AdvanceByteStream < uint16 > ( data , remaining ) ) ;
break ;
case LUAEXT_TSHORT :
lua_pushinteger ( L , AdvanceByteStream < int16 > ( data , remaining ) ) ;
break ;
case LUAEXT_TBYTE :
lua_pushinteger ( L , AdvanceByteStream < uint8 > ( data , remaining ) ) ;
break ;
case LUAEXT_TTABLE :
case LUAEXT_TTABLE | LUAEXT_BITS_1A :
case LUAEXT_TTABLE | LUAEXT_BITS_2A :
case LUAEXT_TTABLE | LUAEXT_BITS_4A :
case LUAEXT_TTABLE | LUAEXT_BITS_1H :
case LUAEXT_TTABLE | LUAEXT_BITS_2H :
case LUAEXT_TTABLE | LUAEXT_BITS_4H :
case LUAEXT_TTABLE | LUAEXT_BITS_1A | LUAEXT_BITS_1H :
case LUAEXT_TTABLE | LUAEXT_BITS_2A | LUAEXT_BITS_1H :
case LUAEXT_TTABLE | LUAEXT_BITS_4A | LUAEXT_BITS_1H :
case LUAEXT_TTABLE | LUAEXT_BITS_1A | LUAEXT_BITS_2H :
case LUAEXT_TTABLE | LUAEXT_BITS_2A | LUAEXT_BITS_2H :
case LUAEXT_TTABLE | LUAEXT_BITS_4A | LUAEXT_BITS_2H :
case LUAEXT_TTABLE | LUAEXT_BITS_1A | LUAEXT_BITS_4H :
case LUAEXT_TTABLE | LUAEXT_BITS_2A | LUAEXT_BITS_4H :
case LUAEXT_TTABLE | LUAEXT_BITS_4A | LUAEXT_BITS_4H :
{
unsigned int arraySize = 0 ;
if ( BITMATCH ( type , LUAEXT_BITS_4A ) | | BITMATCH ( type , LUAEXT_BITS_2A ) | | BITMATCH ( type , LUAEXT_BITS_1A ) )
arraySize | = AdvanceByteStream < uint8 > ( data , remaining ) ;
if ( BITMATCH ( type , LUAEXT_BITS_4A ) | | BITMATCH ( type , LUAEXT_BITS_2A ) )
arraySize | = ( ( uint16 ) AdvanceByteStream < uint8 > ( data , remaining ) ) < < 8 ;
if ( BITMATCH ( type , LUAEXT_BITS_4A ) )
arraySize | = ( ( uint32 ) AdvanceByteStream < uint8 > ( data , remaining ) ) < < 16 ,
arraySize | = ( ( uint32 ) AdvanceByteStream < uint8 > ( data , remaining ) ) < < 24 ;
unsigned int hashSize = 0 ;
if ( BITMATCH ( type , LUAEXT_BITS_4H ) | | BITMATCH ( type , LUAEXT_BITS_2H ) | | BITMATCH ( type , LUAEXT_BITS_1H ) )
hashSize | = AdvanceByteStream < uint8 > ( data , remaining ) ;
if ( BITMATCH ( type , LUAEXT_BITS_4H ) | | BITMATCH ( type , LUAEXT_BITS_2H ) )
hashSize | = ( ( uint16 ) AdvanceByteStream < uint8 > ( data , remaining ) ) < < 8 ;
if ( BITMATCH ( type , LUAEXT_BITS_4H ) )
hashSize | = ( ( uint32 ) AdvanceByteStream < uint8 > ( data , remaining ) ) < < 16 ,
hashSize | = ( ( uint32 ) AdvanceByteStream < uint8 > ( data , remaining ) ) < < 24 ;
lua_checkstack ( L , 8 ) ;
lua_createtable ( L , arraySize , hashSize ) ;
unsigned int n = 1 ;
while ( n < = arraySize )
{
if ( * data = = LUAEXT_TNILS )
{
AdvanceByteStream ( data , remaining , 1 ) ;
n + = AdvanceByteStream < uint32 > ( data , remaining ) ;
}
else
{
BinaryToLuaStackConverter ( L , data , remaining ) ; // push value
lua_rawseti ( L , - 2 , n ) ; // table[n] = value
n + + ;
}
}
for ( unsigned int h = 1 ; h < = hashSize ; h + + )
{
BinaryToLuaStackConverter ( L , data , remaining ) ; // push key
BinaryToLuaStackConverter ( L , data , remaining ) ; // push value
lua_rawset ( L , - 3 ) ; // table[key] = value
}
}
break ;
}
}
static const unsigned char luaBinaryMajorVersion = 9 ;
static const unsigned char luaBinaryMinorVersion = 1 ;
unsigned char * LuaStackToBinary ( lua_State * L , unsigned int & size )
{
int n = lua_gettop ( L ) ;
if ( n = = 0 )
return NULL ;
std : : vector < unsigned char > output ;
output . push_back ( luaBinaryMajorVersion ) ;
output . push_back ( luaBinaryMinorVersion ) ;
for ( int i = 1 ; i < = n ; i + + )
LuaStackToBinaryConverter ( L , i , output ) ;
unsigned char * rv = new unsigned char [ output . size ( ) ] ;
memcpy ( rv , & output . front ( ) , output . size ( ) ) ;
size = output . size ( ) ;
return rv ;
}
void BinaryToLuaStack ( lua_State * L , const unsigned char * data , unsigned int size , unsigned int itemsToLoad )
{
unsigned char major = * data + + ;
unsigned char minor = * data + + ;
size - = 2 ;
if ( luaBinaryMajorVersion ! = major | | luaBinaryMinorVersion ! = minor )
return ;
while ( size > 0 & & itemsToLoad > 0 )
{
BinaryToLuaStackConverter ( L , data , size ) ;
itemsToLoad - - ;
}
}
// saves Lua stack into a record and pops it
void LuaSaveData : : SaveRecord ( lua_State * L , unsigned int key )
{
if ( ! L )
return ;
Record * cur = new Record ( ) ;
cur - > key = key ;
cur - > data = LuaStackToBinary ( L , cur - > size ) ;
cur - > next = NULL ;
lua_settop ( L , 0 ) ;
if ( cur - > size < = 0 )
{
delete cur ;
return ;
}
Record * last = recordList ;
while ( last & & last - > next )
last = last - > next ;
if ( last )
last - > next = cur ;
else
recordList = cur ;
}
// pushes a record's data onto the Lua stack
void LuaSaveData : : LoadRecord ( struct lua_State * L , unsigned int key , unsigned int itemsToLoad ) const
{
if ( ! L )
return ;
Record * cur = recordList ;
while ( cur )
{
if ( cur - > key = = key )
{
// s_dbg_dataStart = cur->data;
// s_dbg_dataSize = cur->size;
BinaryToLuaStack ( L , cur - > data , cur - > size , itemsToLoad ) ;
return ;
}
cur = cur - > next ;
}
}
// saves part of the Lua stack (at the given index) into a record and does NOT pop anything
void LuaSaveData : : SaveRecordPartial ( struct lua_State * L , unsigned int key , int idx )
{
if ( ! L )
return ;
if ( idx < 0 )
idx + = lua_gettop ( L ) + 1 ;
Record * cur = new Record ( ) ;
cur - > key = key ;
cur - > next = NULL ;
if ( idx < = lua_gettop ( L ) )
{
std : : vector < unsigned char > output ;
output . push_back ( luaBinaryMajorVersion ) ;
output . push_back ( luaBinaryMinorVersion ) ;
LuaStackToBinaryConverter ( L , idx , output ) ;
unsigned char * rv = new unsigned char [ output . size ( ) ] ;
memcpy ( rv , & output . front ( ) , output . size ( ) ) ;
cur - > size = output . size ( ) ;
cur - > data = rv ;
}
if ( cur - > size < = 0 )
{
delete cur ;
return ;
}
Record * last = recordList ;
while ( last & & last - > next )
last = last - > next ;
if ( last )
last - > next = cur ;
else
recordList = cur ;
}
void fwriteint ( unsigned int value , FILE * file )
{
for ( int i = 0 ; i < 4 ; i + + )
{
int w = value & 0xFF ;
fwrite ( & w , 1 , 1 , file ) ;
value > > = 8 ;
}
}
void freadint ( unsigned int & value , FILE * file )
{
int rv = 0 ;
for ( int i = 0 ; i < 4 ; i + + )
{
int r = 0 ;
fread ( & r , 1 , 1 , file ) ;
rv | = r < < ( i * 8 ) ;
}
value = rv ;
}
// writes all records to an already-open file
void LuaSaveData : : ExportRecords ( void * fileV ) const
{
FILE * file = ( FILE * ) fileV ;
if ( ! file )
return ;
Record * cur = recordList ;
while ( cur )
{
fwriteint ( cur - > key , file ) ;
fwriteint ( cur - > size , file ) ;
fwrite ( cur - > data , cur - > size , 1 , file ) ;
cur = cur - > next ;
}
}
// reads records from an already-open file
void LuaSaveData : : ImportRecords ( void * fileV )
{
FILE * file = ( FILE * ) fileV ;
if ( ! file )
return ;
ClearRecords ( ) ;
Record rec ;
Record * cur = & rec ;
Record * last = NULL ;
while ( 1 )
{
freadint ( cur - > key , file ) ;
freadint ( cur - > size , file ) ;
if ( feof ( file ) | | ferror ( file ) )
break ;
cur - > data = new unsigned char [ cur - > size ] ;
fread ( cur - > data , cur - > size , 1 , file ) ;
Record * next = new Record ( ) ;
memcpy ( next , cur , sizeof ( Record ) ) ;
next - > next = NULL ;
if ( last )
last - > next = next ;
else
recordList = next ;
last = next ;
}
}
void LuaSaveData : : ClearRecords ( )
{
Record * cur = recordList ;
while ( cur )
{
Record * del = cur ;
cur = cur - > next ;
delete [ ] del - > data ;
delete del ;
}
recordList = NULL ;
}
void CallRegisteredLuaSaveFunctions ( int savestateNumber , LuaSaveData & saveData )
{
//lua_State* L = FCEU_GetLuaState();
if ( L )
{
lua_settop ( L , 0 ) ;
lua_getfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_BEFORESAVE ] ) ;
2012-10-21 16:40:04 +00:00
2009-10-25 03:02:00 +00:00
if ( lua_isfunction ( L , - 1 ) )
{
lua_pushinteger ( L , savestateNumber ) ;
int ret = lua_pcall ( L , 1 , LUA_MULTRET , 0 ) ;
if ( ret ! = 0 ) {
// This is grounds for trashing the function
lua_pushnil ( L ) ;
lua_setfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_BEFORESAVE ] ) ;
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2009-10-25 03:02:00 +00:00
MessageBox ( hAppWnd , lua_tostring ( L , - 1 ) , " Lua Error in SAVE function " , MB_OK ) ;
# else
2020-10-31 16:24:52 +00:00
LuaPrintfToWindowConsole ( " Lua error in registersave function: %s \n " , lua_tostring ( L , - 1 ) ) ;
2009-10-25 03:02:00 +00:00
fprintf ( stderr , " Lua error in registersave function: %s \n " , lua_tostring ( L , - 1 ) ) ;
# endif
}
saveData . SaveRecord ( L , LUA_DATARECORDKEY ) ;
}
else
{
lua_pop ( L , 1 ) ;
}
}
}
void CallRegisteredLuaLoadFunctions ( int savestateNumber , const LuaSaveData & saveData )
{
//lua_State* L = FCEU_GetLuaState();
if ( L )
{
lua_settop ( L , 0 ) ;
lua_getfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_AFTERLOAD ] ) ;
2012-10-21 16:40:04 +00:00
2009-10-25 03:02:00 +00:00
if ( lua_isfunction ( L , - 1 ) )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2009-10-25 03:02:00 +00:00
// since the scriptdata can be very expensive to load
// (e.g. the registered save function returned some huge tables)
// check the number of parameters the registered load function expects
// and don't bother loading the parameters it wouldn't receive anyway
int numParamsExpected = ( L - > top - 1 ) - > value . gc - > cl . l . p - > numparams ; // NOTE: if this line crashes, that means your Lua headers are out of sync with your Lua lib
if ( numParamsExpected ) numParamsExpected - - ; // minus one for the savestate number we always pass in
int prevGarbage = lua_gc ( L , LUA_GCCOUNT , 0 ) ;
lua_pushinteger ( L , savestateNumber ) ;
saveData . LoadRecord ( L , LUA_DATARECORDKEY , numParamsExpected ) ;
2013-07-08 23:00:57 +00:00
# else
int prevGarbage = lua_gc ( L , LUA_GCCOUNT , 0 ) ;
lua_pushinteger ( L , savestateNumber ) ;
saveData . LoadRecord ( L , LUA_DATARECORDKEY , ( unsigned int ) - 1 ) ;
# endif
2009-10-25 03:02:00 +00:00
int n = lua_gettop ( L ) - 1 ;
int ret = lua_pcall ( L , n , 0 , 0 ) ;
if ( ret ! = 0 ) {
// This is grounds for trashing the function
lua_pushnil ( L ) ;
lua_setfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_AFTERLOAD ] ) ;
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2009-10-25 03:02:00 +00:00
MessageBox ( hAppWnd , lua_tostring ( L , - 1 ) , " Lua Error in LOAD function " , MB_OK ) ;
# else
2020-10-31 16:24:52 +00:00
LuaPrintfToWindowConsole ( " Lua error in registerload function: %s \n " , lua_tostring ( L , - 1 ) ) ;
2009-10-25 03:02:00 +00:00
fprintf ( stderr , " Lua error in registerload function: %s \n " , lua_tostring ( L , - 1 ) ) ;
# endif
}
else
{
int newGarbage = lua_gc ( L , LUA_GCCOUNT , 0 ) ;
if ( newGarbage - prevGarbage > 50 )
{
// now seems to be a very good time to run the garbage collector
// it might take a while now but that's better than taking 10 whiles 9 loads from now
lua_gc ( L , LUA_GCCOLLECT , 0 ) ;
}
}
}
else
{
lua_pop ( L , 1 ) ;
}
}
}
2009-10-08 05:02:22 +00:00
2018-11-25 17:25:55 +00:00
// rom.getfilename()
//
// Base filename of the currently loaded ROM, or nil if none is loaded
static int rom_getfilename ( lua_State * L ) {
if ( GameInfo ) lua_pushstring ( L , FileBase ) ;
else lua_pushnil ( L ) ;
return 1 ;
}
static int rom_gethash ( lua_State * L ) {
const char * type = luaL_checkstring ( L , 1 ) ;
2018-11-29 03:39:29 +00:00
MD5DATA md5hash = GameInfo - > MD5 ;
if ( ! type ) lua_pushstring ( L , " " ) ;
else if ( ! stricmp ( type , " md5 " ) ) lua_pushstring ( L , md5_asciistr ( md5hash ) ) ;
else if ( ! stricmp ( type , " base64 " ) ) lua_pushstring ( L , BytesToString ( md5hash . data , MD5DATA : : size ) . c_str ( ) ) ;
else lua_pushstring ( L , " " ) ;
2018-11-25 17:25:55 +00:00
return 1 ;
}
2013-09-15 18:37:11 +00:00
static int rom_readbyte ( lua_State * L ) {
lua_pushinteger ( L , FCEU_ReadRomByte ( luaL_checkinteger ( L , 1 ) ) ) ;
return 1 ;
}
static int rom_readbytesigned ( lua_State * L ) {
lua_pushinteger ( L , ( signed char ) FCEU_ReadRomByte ( luaL_checkinteger ( L , 1 ) ) ) ;
return 1 ;
}
2017-02-23 21:29:43 +00:00
static int rom_readbyterange ( lua_State * L ) {
int range_start = luaL_checkinteger ( L , 1 ) ;
int range_size = luaL_checkinteger ( L , 2 ) ;
if ( range_size < 0 )
return 0 ;
char * buf = ( char * ) alloca ( range_size ) ;
for ( int i = 0 ; i < range_size ; i + + ) {
buf [ i ] = FCEU_ReadRomByte ( range_start + i ) ;
}
lua_pushlstring ( L , buf , range_size ) ;
return 1 ;
}
2015-06-23 21:04:31 +00:00
// doesn't keep backups to allow maximum speed (for automatic rom corruptors and stuff)
// keeping them might be an option though, just need to use memview's ApplyPatch()
// that'd also highlight the edits in hex editor
2018-11-15 00:44:18 +00:00
static int rom_writebyte ( lua_State * L )
2015-06-23 21:04:31 +00:00
{
uint32 address = luaL_checkinteger ( L , 1 ) ;
if ( address < 16 )
luaL_error ( L , " rom.writebyte() can't edit the ROM header. " ) ;
else
FCEU_WriteRomByte ( address , luaL_checkinteger ( L , 2 ) ) ;
return 1 ;
}
2013-09-15 18:37:11 +00:00
static int memory_readbyte ( lua_State * L ) {
2016-12-13 18:13:46 +00:00
lua_pushinteger ( L , GetMem ( luaL_checkinteger ( L , 1 ) ) ) ;
2013-09-15 18:37:11 +00:00
return 1 ;
}
static int memory_readbytesigned ( lua_State * L ) {
2016-12-13 18:13:46 +00:00
signed char c = ( signed char ) GetMem ( luaL_checkinteger ( L , 1 ) ) ;
2013-09-15 18:37:11 +00:00
lua_pushinteger ( L , c ) ;
return 1 ;
}
2013-09-16 16:04:11 +00:00
static int GetWord ( lua_State * L , bool isSigned )
{
2013-09-19 20:51:41 +00:00
// little endian, unless the high byte address is specified as a 2nd parameter
uint16 addressLow = luaL_checkinteger ( L , 1 ) ;
uint16 addressHigh = addressLow + 1 ;
if ( lua_type ( L , 2 ) = = LUA_TNUMBER )
addressHigh = luaL_checkinteger ( L , 2 ) ;
2016-12-13 18:13:46 +00:00
uint16 result = GetMem ( addressLow ) | ( GetMem ( addressHigh ) < < 8 ) ;
2013-09-16 16:04:11 +00:00
return isSigned ? ( int16 ) result : result ;
2013-09-15 18:37:11 +00:00
}
2013-09-16 16:04:11 +00:00
static int memory_readword ( lua_State * L )
{
2013-09-15 18:37:11 +00:00
lua_pushinteger ( L , GetWord ( L , false ) ) ;
return 1 ;
}
static int memory_readwordsigned ( lua_State * L ) {
lua_pushinteger ( L , GetWord ( L , true ) ) ;
return 1 ;
}
static int memory_writebyte ( lua_State * L ) {
2019-11-23 04:29:25 +00:00
uint32 A = luaL_checkinteger ( L , 1 ) ;
uint8 V = luaL_checkinteger ( L , 2 ) ;
if ( A < 0x10000 )
BWrite [ A ] ( A , V ) ;
return 0 ;
}
static int legacymemory_writebyte ( lua_State * L ) {
2013-09-15 18:37:11 +00:00
FCEU_CheatSetByte ( luaL_checkinteger ( L , 1 ) , luaL_checkinteger ( L , 2 ) ) ;
return 0 ;
}
2008-11-02 19:10:53 +00:00
static int memory_readbyterange ( lua_State * L ) {
int range_start = luaL_checkinteger ( L , 1 ) ;
int range_size = luaL_checkinteger ( L , 2 ) ;
if ( range_size < 0 )
return 0 ;
char * buf = ( char * ) alloca ( range_size ) ;
for ( int i = 0 ; i < range_size ; i + + ) {
2016-12-13 18:13:46 +00:00
buf [ i ] = GetMem ( range_start + i ) ;
2008-11-02 19:10:53 +00:00
}
lua_pushlstring ( L , buf , range_size ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
return 1 ;
}
2016-12-22 15:15:38 +00:00
static int ppu_readbyte ( lua_State * L ) {
lua_pushinteger ( L , FFCEUX_PPURead ( luaL_checkinteger ( L , 1 ) ) ) ;
return 1 ;
}
static int ppu_readbyterange ( lua_State * L ) {
int range_start = luaL_checkinteger ( L , 1 ) ;
int range_size = luaL_checkinteger ( L , 2 ) ;
if ( range_size < 0 )
return 0 ;
char * buf = ( char * ) alloca ( range_size ) ;
for ( int i = 0 ; i < range_size ; i + + ) {
buf [ i ] = FFCEUX_PPURead ( range_start + i ) ;
}
lua_pushlstring ( L , buf , range_size ) ;
return 1 ;
}
2009-10-19 03:01:26 +00:00
static inline bool isalphaorunderscore ( char c )
{
return isalpha ( c ) | | c = = ' _ ' ;
}
# define APPENDPRINT { int _n = snprintf(ptr, remaining,
# define END ); if(_n >= 0) { ptr += _n; remaining -= _n; } else { remaining = 0; } }
static void toCStringConverter ( lua_State * L , int i , char * & ptr , int & remaining )
{
if ( remaining < = 0 )
return ;
const char * str = ptr ; // for debugging
// if there is a __tostring metamethod then call it
int usedMeta = luaL_callmeta ( L , i , " __tostring " ) ;
if ( usedMeta )
{
std : : vector < const void * > : : const_iterator foundCycleIter = std : : find ( s_metacallStack . begin ( ) , s_metacallStack . end ( ) , lua_topointer ( L , i ) ) ;
if ( foundCycleIter ! = s_metacallStack . end ( ) )
{
lua_pop ( L , 1 ) ;
usedMeta = false ;
}
else
{
s_metacallStack . push_back ( lua_topointer ( L , i ) ) ;
i = lua_gettop ( L ) ;
}
}
switch ( lua_type ( L , i ) )
{
case LUA_TNONE : break ;
case LUA_TNIL : APPENDPRINT " nil " END break ;
case LUA_TBOOLEAN : APPENDPRINT lua_toboolean ( L , i ) ? " true " : " false " END break ;
case LUA_TSTRING : APPENDPRINT " %s " , lua_tostring ( L , i ) END break ;
2016-12-12 22:25:41 +00:00
case LUA_TNUMBER : APPENDPRINT " %.12g " , lua_tonumber ( L , i ) END break ;
2012-10-21 16:40:04 +00:00
case LUA_TFUNCTION :
2009-10-19 03:01:26 +00:00
/*if((L->base + i-1)->value.gc->cl.c.isC)
{
//lua_CFunction func = lua_tocfunction(L, i);
//std::map<lua_CFunction, const char*>::iterator iter = s_cFuncInfoMap.find(func);
//if(iter == s_cFuncInfoMap.end())
goto defcase ;
2012-10-21 16:40:04 +00:00
//APPENDPRINT "function(%s)", iter->second END
2009-10-19 03:01:26 +00:00
}
else
{
2012-10-21 16:40:04 +00:00
APPENDPRINT " function( " END
2009-10-19 03:01:26 +00:00
Proto * p = ( L - > base + i - 1 ) - > value . gc - > cl . l . p ;
int numParams = p - > numparams + ( p - > is_vararg ? 1 : 0 ) ;
for ( int n = 0 ; n < p - > numparams ; n + + )
{
2012-10-21 16:40:04 +00:00
APPENDPRINT " %s " , getstr ( p - > locvars [ n ] . varname ) END
2009-10-19 03:01:26 +00:00
if ( n ! = numParams - 1 )
APPENDPRINT " , " END
}
if ( p - > is_vararg )
APPENDPRINT " ... " END
APPENDPRINT " ) " END
} */
goto defcase ;
break ;
defcase : default : APPENDPRINT " %s:%p " , luaL_typename ( L , i ) , lua_topointer ( L , i ) END break ;
case LUA_TTABLE :
{
// first make sure there's enough stack space
if ( ! lua_checkstack ( L , 4 ) )
{
// note that even if lua_checkstack never returns false,
// that doesn't mean we didn't need to call it,
// because calling it retrieves stack space past LUA_MINSTACK
goto defcase ;
}
std : : vector < const void * > : : const_iterator foundCycleIter = std : : find ( s_tableAddressStack . begin ( ) , s_tableAddressStack . end ( ) , lua_topointer ( L , i ) ) ;
if ( foundCycleIter ! = s_tableAddressStack . end ( ) )
{
int parentNum = s_tableAddressStack . end ( ) - foundCycleIter ;
if ( parentNum > 1 )
APPENDPRINT " %s:parent^%d " , luaL_typename ( L , i ) , parentNum END
else
APPENDPRINT " %s:parent " , luaL_typename ( L , i ) END
}
else
{
s_tableAddressStack . push_back ( lua_topointer ( L , i ) ) ;
struct Scope { ~ Scope ( ) { s_tableAddressStack . pop_back ( ) ; } } scope ;
APPENDPRINT " { " END
lua_pushnil ( L ) ; // first key
int keyIndex = lua_gettop ( L ) ;
int valueIndex = keyIndex + 1 ;
bool first = true ;
bool skipKey = true ; // true if we're still in the "array part" of the table
lua_Number arrayIndex = ( lua_Number ) 0 ;
while ( lua_next ( L , i ) )
{
if ( first )
first = false ;
else
APPENDPRINT " , " END
if ( skipKey )
{
arrayIndex + = ( lua_Number ) 1 ;
bool keyIsNumber = ( lua_type ( L , keyIndex ) = = LUA_TNUMBER ) ;
skipKey = keyIsNumber & & ( lua_tonumber ( L , keyIndex ) = = arrayIndex ) ;
}
if ( ! skipKey )
{
bool keyIsString = ( lua_type ( L , keyIndex ) = = LUA_TSTRING ) ;
bool invalidLuaIdentifier = ( ! keyIsString | | ! isalphaorunderscore ( * lua_tostring ( L , keyIndex ) ) ) ;
if ( invalidLuaIdentifier )
if ( keyIsString )
APPENDPRINT " [' " END
else
APPENDPRINT " [ " END
toCStringConverter ( L , keyIndex , ptr , remaining ) ; // key
if ( invalidLuaIdentifier )
if ( keyIsString )
APPENDPRINT " ']= " END
else
APPENDPRINT " ]= " END
else
APPENDPRINT " = " END
}
bool valueIsString = ( lua_type ( L , valueIndex ) = = LUA_TSTRING ) ;
if ( valueIsString )
APPENDPRINT " ' " END
toCStringConverter ( L , valueIndex , ptr , remaining ) ; // value
if ( valueIsString )
APPENDPRINT " ' " END
lua_pop ( L , 1 ) ;
if ( remaining < = 0 )
{
lua_settop ( L , keyIndex - 1 ) ; // stack might not be clean yet if we're breaking early
break ;
}
}
APPENDPRINT " } " END
}
} break ;
}
if ( usedMeta )
{
s_metacallStack . pop_back ( ) ;
lua_pop ( L , 1 ) ;
}
}
static const int s_tempStrMaxLen = 64 * 1024 ;
static char s_tempStr [ s_tempStrMaxLen ] ;
static char * rawToCString ( lua_State * L , int idx )
{
int a = idx > 0 ? idx : 1 ;
int n = idx > 0 ? idx : lua_gettop ( L ) ;
char * ptr = s_tempStr ;
* ptr = 0 ;
int remaining = s_tempStrMaxLen ;
for ( int i = a ; i < = n ; i + + )
{
toCStringConverter ( L , i , ptr , remaining ) ;
if ( i ! = n )
APPENDPRINT " " END
}
if ( remaining < 3 )
{
while ( remaining < 6 )
remaining + + , ptr - - ;
APPENDPRINT " ... " END
}
APPENDPRINT " \r \n " END
// the trailing newline is so print() can avoid having to do wasteful things to print its newline
// (string copying would be wasteful and calling info.print() twice can be extremely slow)
// at the cost of functions that don't want the newline needing to trim off the last two characters
// (which is a very fast operation and thus acceptable in this case)
return s_tempStr ;
}
# undef APPENDPRINT
# undef END
// replacement for luaB_tostring() that is able to show the contents of tables (and formats numbers better, and show function prototypes)
// can be called directly from lua via tostring(), assuming tostring hasn't been reassigned
static int tostring ( lua_State * L )
{
char * str = rawToCString ( L ) ;
str [ strlen ( str ) - 2 ] = 0 ; // hack: trim off the \r\n (which is there to simplify the print function's task)
lua_pushstring ( L , str ) ;
return 1 ;
}
2013-09-15 18:37:11 +00:00
// tobitstring(int value)
//
// Converts byte to binary string
static int tobitstring ( lua_State * L )
{
std : : bitset < 8 > bits ( luaL_checkinteger ( L , 1 ) ) ;
std : : string temp = bits . to_string ( ) . insert ( 4 , " " ) ;
const char * result = temp . c_str ( ) ;
lua_pushstring ( L , result ) ;
return 1 ;
}
2009-10-19 03:01:26 +00:00
// like rawToCString, but will check if the global Lua function tostring()
// has been replaced with a custom function, and call that instead if so
static const char * toCString ( lua_State * L , int idx )
{
int a = idx > 0 ? idx : 1 ;
int n = idx > 0 ? idx : lua_gettop ( L ) ;
lua_getglobal ( L , " tostring " ) ;
lua_CFunction cf = lua_tocfunction ( L , - 1 ) ;
if ( cf = = tostring ) // optimization: if using our own C tostring function, we can bypass the call through Lua and all the string object allocation that would entail
{
lua_pop ( L , 1 ) ;
return rawToCString ( L , idx ) ;
}
else // if the user overrided the tostring function, we have to actually call it and store the temporarily allocated string it returns
{
lua_pushstring ( L , " " ) ;
for ( int i = a ; i < = n ; i + + ) {
lua_pushvalue ( L , - 2 ) ; // function to be called
lua_pushvalue ( L , i ) ; // value to print
lua_call ( L , 1 , 1 ) ;
if ( lua_tostring ( L , - 1 ) = = NULL )
luaL_error ( L , LUA_QL ( " tostring " ) " must return a string to " LUA_QL ( " print " ) ) ;
lua_pushstring ( L , ( i < n ) ? " " : " \r \n " ) ;
lua_concat ( L , 3 ) ;
}
const char * str = lua_tostring ( L , - 1 ) ;
strncpy ( s_tempStr , str , s_tempStrMaxLen ) ;
s_tempStr [ s_tempStrMaxLen - 1 ] = 0 ;
lua_pop ( L , 2 ) ;
return s_tempStr ;
}
}
// replacement for luaB_print() that goes to the appropriate textbox instead of stdout
static int print ( lua_State * L )
{
2018-11-24 17:25:29 +00:00
if ( info_print ) {
const char * str = toCString ( L ) ;
2009-10-19 03:01:26 +00:00
2018-11-24 17:25:29 +00:00
int uid = info_uid ; //luaStateToUIDMap[L->l_G->mainthread];
//LuaContextInfo& info = GetCurrentInfo();
2009-10-19 03:01:26 +00:00
info_print ( uid , str ) ;
2018-11-24 17:25:29 +00:00
}
else {
char * str = rawToCString ( L ) ;
str [ strlen ( str ) - 2 ] = 0 ; // *NIX need no extra \r\n BS
2009-10-19 03:01:26 +00:00
puts ( str ) ;
2018-11-24 17:25:29 +00:00
}
2009-10-19 03:01:26 +00:00
//worry(L, 100);
return 0 ;
}
2015-06-23 21:04:31 +00:00
// gethash()
//
// Returns the crc32 hashsum of an arbitrary buffer
static int gethash ( lua_State * L ) {
uint8 * buffer = ( uint8 * ) luaL_checkstring ( L , 1 ) ;
int size = luaL_checkinteger ( L , 2 ) ;
int hash = CalcCRC32 ( 0 , buffer , size ) ;
lua_pushinteger ( L , hash ) ;
return 1 ;
}
2009-10-19 03:01:26 +00:00
// provides an easy way to copy a table from Lua
// (simple assignment only makes an alias, but sometimes an independent table is desired)
// currently this function only performs a shallow copy,
// but I think it should be changed to do a deep copy (possibly of configurable depth?)
// that maintains the internal table reference structure
static int copytable ( lua_State * L )
{
int origIndex = 1 ; // we only care about the first argument
int origType = lua_type ( L , origIndex ) ;
if ( origType = = LUA_TNIL )
{
lua_pushnil ( L ) ;
return 1 ;
}
if ( origType ! = LUA_TTABLE )
{
luaL_typerror ( L , 1 , lua_typename ( L , LUA_TTABLE ) ) ;
lua_pushnil ( L ) ;
return 1 ;
}
2012-10-21 16:40:04 +00:00
2009-10-19 03:01:26 +00:00
lua_createtable ( L , lua_objlen ( L , 1 ) , 0 ) ;
int copyIndex = lua_gettop ( L ) ;
lua_pushnil ( L ) ; // first key
int keyIndex = lua_gettop ( L ) ;
int valueIndex = keyIndex + 1 ;
while ( lua_next ( L , origIndex ) )
{
lua_pushvalue ( L , keyIndex ) ;
lua_pushvalue ( L , valueIndex ) ;
lua_rawset ( L , copyIndex ) ; // copytable[key] = value
lua_pop ( L , 1 ) ;
}
// copy the reference to the metatable as well, if any
if ( lua_getmetatable ( L , origIndex ) )
lua_setmetatable ( L , copyIndex ) ;
return 1 ; // return the new table
}
// because print traditionally shows the address of tables,
// and the print function I provide instead shows the contents of tables,
// I also provide this function
// (otherwise there would be no way to see a table's address, AFAICT)
static int addressof ( lua_State * L )
{
const void * ptr = lua_topointer ( L , - 1 ) ;
lua_pushinteger ( L , ( lua_Integer ) ptr ) ;
return 1 ;
}
2009-10-11 08:50:09 +00:00
struct registerPointerMap
{
const char * registerName ;
unsigned int * pointer ;
int dataSize ;
} ;
# define RPM_ENTRY(name,var) {name, (unsigned int*)&var, sizeof(var)},
registerPointerMap regPointerMap [ ] = {
2009-10-19 04:07:30 +00:00
RPM_ENTRY ( " pc " , _PC )
2009-10-11 08:50:09 +00:00
RPM_ENTRY ( " a " , _A )
RPM_ENTRY ( " x " , _X )
RPM_ENTRY ( " y " , _Y )
2009-10-19 04:07:30 +00:00
RPM_ENTRY ( " s " , _S )
RPM_ENTRY ( " p " , _P )
2009-10-11 08:50:09 +00:00
{ }
} ;
struct cpuToRegisterMap
{
const char * cpuName ;
registerPointerMap * rpmap ;
}
cpuToRegisterMaps [ ] =
{
{ " " , regPointerMap } ,
} ;
//DEFINE_LUA_FUNCTION(memory_getregister, "cpu_dot_registername_string")
static int memory_getregister ( lua_State * L )
{
const char * qualifiedRegisterName = luaL_checkstring ( L , 1 ) ;
lua_settop ( L , 0 ) ;
for ( int cpu = 0 ; cpu < sizeof ( cpuToRegisterMaps ) / sizeof ( * cpuToRegisterMaps ) ; cpu + + )
{
cpuToRegisterMap ctrm = cpuToRegisterMaps [ cpu ] ;
int cpuNameLen = strlen ( ctrm . cpuName ) ;
if ( ! strnicmp ( qualifiedRegisterName , ctrm . cpuName , cpuNameLen ) )
{
qualifiedRegisterName + = cpuNameLen ;
for ( int reg = 0 ; ctrm . rpmap [ reg ] . dataSize ; reg + + )
{
registerPointerMap rpm = ctrm . rpmap [ reg ] ;
if ( ! stricmp ( qualifiedRegisterName , rpm . registerName ) )
{
switch ( rpm . dataSize )
{ default :
case 1 : lua_pushinteger ( L , * ( unsigned char * ) rpm . pointer ) ; break ;
case 2 : lua_pushinteger ( L , * ( unsigned short * ) rpm . pointer ) ; break ;
case 4 : lua_pushinteger ( L , * ( unsigned long * ) rpm . pointer ) ; break ;
}
return 1 ;
}
}
lua_pushnil ( L ) ;
return 1 ;
}
}
lua_pushnil ( L ) ;
return 1 ;
}
//DEFINE_LUA_FUNCTION(memory_setregister, "cpu_dot_registername_string,value")
static int memory_setregister ( lua_State * L )
{
const char * qualifiedRegisterName = luaL_checkstring ( L , 1 ) ;
unsigned long value = ( unsigned long ) ( luaL_checkinteger ( L , 2 ) ) ;
lua_settop ( L , 0 ) ;
for ( int cpu = 0 ; cpu < sizeof ( cpuToRegisterMaps ) / sizeof ( * cpuToRegisterMaps ) ; cpu + + )
{
cpuToRegisterMap ctrm = cpuToRegisterMaps [ cpu ] ;
int cpuNameLen = strlen ( ctrm . cpuName ) ;
if ( ! strnicmp ( qualifiedRegisterName , ctrm . cpuName , cpuNameLen ) )
{
qualifiedRegisterName + = cpuNameLen ;
for ( int reg = 0 ; ctrm . rpmap [ reg ] . dataSize ; reg + + )
{
registerPointerMap rpm = ctrm . rpmap [ reg ] ;
if ( ! stricmp ( qualifiedRegisterName , rpm . registerName ) )
{
switch ( rpm . dataSize )
{ default :
case 1 : * ( unsigned char * ) rpm . pointer = ( unsigned char ) ( value & 0xFF ) ; break ;
case 2 : * ( unsigned short * ) rpm . pointer = ( unsigned short ) ( value & 0xFFFF ) ; break ;
case 4 : * ( unsigned long * ) rpm . pointer = value ; break ;
}
return 0 ;
}
}
return 0 ;
}
}
return 0 ;
}
2018-11-15 00:44:18 +00:00
// Forces a stack trace and returns the string
static const char * CallLuaTraceback ( lua_State * L ) {
lua_getfield ( L , LUA_GLOBALSINDEX , " debug " ) ;
if ( ! lua_istable ( L , - 1 ) ) {
lua_pop ( L , 1 ) ;
return " " ;
}
lua_getfield ( L , - 1 , " traceback " ) ;
if ( ! lua_isfunction ( L , - 1 ) ) {
lua_pop ( L , 2 ) ;
return " " ;
}
lua_pushvalue ( L , 1 ) ;
lua_call ( L , 1 , 1 ) ;
return lua_tostring ( L , - 1 ) ;
}
2008-11-02 19:10:53 +00:00
2009-10-11 08:34:10 +00:00
void HandleCallbackError ( lua_State * L )
{
//if(L->errfunc || L->errorJmp)
// luaL_error(L, "%s", lua_tostring(L,-1));
//else
{
2018-11-15 00:44:18 +00:00
const char * trace = CallLuaTraceback ( L ) ;
2009-10-11 08:34:10 +00:00
lua_pushnil ( L ) ;
lua_setfield ( L , LUA_REGISTRYINDEX , guiCallbackTable ) ;
2018-11-15 00:44:18 +00:00
char errmsg [ 2048 ] ;
sprintf ( errmsg , " %s \n %s " , lua_tostring ( L , - 1 ) , trace ) ;
2009-10-11 08:34:10 +00:00
// Error?
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2018-11-15 00:44:18 +00:00
MessageBox ( hAppWnd , errmsg , " Lua run error " , MB_OK | MB_ICONSTOP ) ;
2009-10-11 08:34:10 +00:00
# else
2020-10-31 16:24:52 +00:00
LuaPrintfToWindowConsole ( " Lua thread bombed out: %s \n " , errmsg ) ;
2018-11-15 00:44:18 +00:00
fprintf ( stderr , " Lua thread bombed out: %s \n " , errmsg ) ;
2009-10-11 08:34:10 +00:00
# endif
FCEU_LuaStop ( ) ;
}
}
// the purpose of this structure is to provide a way of
// QUICKLY determining whether a memory address range has a hook associated with it,
// with a bias toward fast rejection because the majority of addresses will not be hooked.
// (it must not use any part of Lua or perform any per-script operations,
// otherwise it would definitely be too slow.)
// calculating the regions when a hook is added/removed may be slow,
// but this is an intentional tradeoff to obtain a high speed of checking during later execution
struct TieredRegion
{
template < unsigned int maxGap >
struct Region
{
struct Island
{
unsigned int start ;
unsigned int end ;
2009-12-11 03:07:34 +00:00
# ifdef NEED_MINGW_HACKS
bool Contains ( unsigned int address , int size ) const { return address < end & & address + size > start ; }
# else
2009-10-11 08:34:10 +00:00
__forceinline bool Contains ( unsigned int address , int size ) const { return address < end & & address + size > start ; }
2009-12-11 03:07:34 +00:00
# endif
2009-10-11 08:34:10 +00:00
} ;
std : : vector < Island > islands ;
void Calculate ( const std : : vector < unsigned int > & bytes )
{
islands . clear ( ) ;
unsigned int lastEnd = ~ 0 ;
std : : vector < unsigned int > : : const_iterator iter = bytes . begin ( ) ;
std : : vector < unsigned int > : : const_iterator end = bytes . end ( ) ;
for ( ; iter ! = end ; + + iter )
{
unsigned int addr = * iter ;
if ( addr < lastEnd | | addr > lastEnd + ( long long ) maxGap )
{
islands . push_back ( Island ( ) ) ;
islands . back ( ) . start = addr ;
}
islands . back ( ) . end = addr + 1 ;
lastEnd = addr + 1 ;
}
}
bool Contains ( unsigned int address , int size ) const
{
2018-11-16 02:49:19 +00:00
for ( size_t i = 0 ; i ! = islands . size ( ) ; + + i )
{
if ( islands [ i ] . Contains ( address , size ) )
return true ;
}
2009-10-11 08:34:10 +00:00
return false ;
}
} ;
Region < 0xFFFFFFFF > broad ;
Region < 0x1000 > mid ;
Region < 0 > narrow ;
void Calculate ( std : : vector < unsigned int > & bytes )
{
std : : sort ( bytes . begin ( ) , bytes . end ( ) ) ;
broad . Calculate ( bytes ) ;
mid . Calculate ( bytes ) ;
narrow . Calculate ( bytes ) ;
}
TieredRegion ( )
{
2018-11-16 02:49:19 +00:00
std : : vector < unsigned int > temp ;
2009-10-12 19:04:28 +00:00
Calculate ( temp ) ;
2009-10-11 08:34:10 +00:00
}
__forceinline int NotEmpty ( )
{
return broad . islands . size ( ) ;
}
// note: it is illegal to call this if NotEmpty() returns 0
__forceinline bool Contains ( unsigned int address , int size )
{
return broad . islands [ 0 ] . Contains ( address , size ) & &
mid . Contains ( address , size ) & &
narrow . Contains ( address , size ) ;
}
} ;
TieredRegion hookedRegions [ LUAMEMHOOK_COUNT ] ;
static void CalculateMemHookRegions ( LuaMemHookType hookType )
{
std : : vector < unsigned int > hookedBytes ;
// std::map<int, LuaContextInfo*>::iterator iter = luaContextInfo.begin();
// std::map<int, LuaContextInfo*>::iterator end = luaContextInfo.end();
// while(iter != end)
// {
// LuaContextInfo& info = *iter->second;
if ( /*info.*/ numMemHooks )
{
// lua_State* L = info.L;
if ( L )
{
lua_settop ( L , 0 ) ;
lua_getfield ( L , LUA_REGISTRYINDEX , luaMemHookTypeStrings [ hookType ] ) ;
lua_pushnil ( L ) ;
while ( lua_next ( L , - 2 ) )
{
if ( lua_isfunction ( L , - 1 ) )
{
unsigned int addr = lua_tointeger ( L , - 2 ) ;
hookedBytes . push_back ( addr ) ;
}
lua_pop ( L , 1 ) ;
}
lua_settop ( L , 0 ) ;
}
}
// ++iter;
// }
hookedRegions [ hookType ] . Calculate ( hookedBytes ) ;
}
static void CallRegisteredLuaMemHook_LuaMatch ( unsigned int address , int size , unsigned int value , LuaMemHookType hookType )
{
// std::map<int, LuaContextInfo*>::iterator iter = luaContextInfo.begin();
// std::map<int, LuaContextInfo*>::iterator end = luaContextInfo.end();
// while(iter != end)
// {
// LuaContextInfo& info = *iter->second;
if ( /*info.*/ numMemHooks )
{
// lua_State* L = info.L;
if ( L /* && !info.panic*/ )
{
# ifdef USE_INFO_STACK
infoStack . insert ( infoStack . begin ( ) , & info ) ;
struct Scope { ~ Scope ( ) { infoStack . erase ( infoStack . begin ( ) ) ; } } scope ;
# endif
lua_settop ( L , 0 ) ;
lua_getfield ( L , LUA_REGISTRYINDEX , luaMemHookTypeStrings [ hookType ] ) ;
for ( int i = address ; i ! = address + size ; i + + )
{
lua_rawgeti ( L , - 1 , i ) ;
if ( lua_isfunction ( L , - 1 ) )
{
bool wasRunning = ( luaRunning ! = 0 ) /*info.running*/ ;
luaRunning /*info.running*/ = true ;
//RefreshScriptSpeedStatus();
lua_pushinteger ( L , address ) ;
lua_pushinteger ( L , size ) ;
2016-08-23 08:46:22 +00:00
lua_pushinteger ( L , value ) ;
int errorcode = lua_pcall ( L , 3 , 0 , 0 ) ;
2009-10-11 08:34:10 +00:00
luaRunning /*info.running*/ = wasRunning ;
//RefreshScriptSpeedStatus();
if ( errorcode )
{
HandleCallbackError ( L ) ;
//int uid = iter->first;
//HandleCallbackError(L,info,uid,true);
}
break ;
}
else
{
lua_pop ( L , 1 ) ;
}
}
lua_settop ( L , 0 ) ;
}
}
// ++iter;
// }
}
void CallRegisteredLuaMemHook ( unsigned int address , int size , unsigned int value , LuaMemHookType hookType )
{
// performance critical! (called VERY frequently)
// I suggest timing a large number of calls to this function in Release if you change anything in here,
// before and after, because even the most innocent change can make it become 30% to 400% slower.
// a good amount to test is: 100000000 calls with no hook set, and another 100000000 with a hook set.
// (on my system that consistently took 200 ms total in the former case and 350 ms total in the latter case)
if ( hookedRegions [ hookType ] . NotEmpty ( ) )
{
//if((hookType <= LUAMEMHOOK_EXEC) && (address >= 0xE00000))
// address |= 0xFF0000; // account for mirroring of RAM
if ( hookedRegions [ hookType ] . Contains ( address , size ) )
CallRegisteredLuaMemHook_LuaMatch ( address , size , value , hookType ) ; // something has hooked this specific address
}
}
void CallRegisteredLuaFunctions ( LuaCallID calltype )
{
assert ( ( unsigned int ) calltype < ( unsigned int ) LUACALL_COUNT ) ;
const char * idstring = luaCallIDStrings [ calltype ] ;
if ( ! L )
return ;
lua_settop ( L , 0 ) ;
lua_getfield ( L , LUA_REGISTRYINDEX , idstring ) ;
int errorcode = 0 ;
if ( lua_isfunction ( L , - 1 ) )
{
errorcode = lua_pcall ( L , 0 , 0 , 0 ) ;
if ( errorcode )
HandleCallbackError ( L ) ;
}
else
{
lua_pop ( L , 1 ) ;
}
}
2011-10-14 17:33:58 +00:00
void ForceExecuteLuaFrameFunctions ( )
{
FCEU_LuaFrameBoundary ( ) ;
CallRegisteredLuaFunctions ( LUACALL_BEFOREEMULATION ) ;
CallRegisteredLuaFunctions ( LUACALL_AFTEREMULATION ) ;
}
2012-01-08 18:19:49 +00:00
void TaseditorAutoFunction ( )
{
CallRegisteredLuaFunctions ( LUACALL_TASEDITOR_AUTO ) ;
}
void TaseditorManualFunction ( )
{
CallRegisteredLuaFunctions ( LUACALL_TASEDITOR_MANUAL ) ;
}
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-07-19 19:40:40 +00:00
void TaseditorDisableManualFunctionIfNeeded ( )
2012-01-08 18:19:49 +00:00
{
if ( L )
{
// check if LUACALL_TASEDITOR_MANUAL function is not nil
lua_getfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_TASEDITOR_MANUAL ] ) ;
2012-07-19 19:40:40 +00:00
if ( ! lua_isfunction ( L , - 1 ) )
2013-03-04 16:58:54 +00:00
taseditor_lua . disableRunFunction ( ) ;
2012-01-08 18:19:49 +00:00
lua_pop ( L , 1 ) ;
2013-03-04 16:58:54 +00:00
} else taseditor_lua . disableRunFunction ( ) ;
2012-01-08 18:19:49 +00:00
}
2021-10-24 21:58:45 +00:00
# elif __QT_DRIVER__
void TaseditorDisableManualFunctionIfNeeded ( )
{
if ( L )
{
// check if LUACALL_TASEDITOR_MANUAL function is not nil
lua_getfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_TASEDITOR_MANUAL ] ) ;
if ( ! lua_isfunction ( L , - 1 ) )
taseditor_lua - > disableRunFunction ( ) ;
lua_pop ( L , 1 ) ;
} else taseditor_lua - > disableRunFunction ( ) ;
}
2012-01-12 19:56:17 +00:00
# endif
2012-01-08 18:19:49 +00:00
2009-10-11 08:34:10 +00:00
static int memory_registerHook ( lua_State * L , LuaMemHookType hookType , int defaultSize )
{
// get first argument: address
unsigned int addr = luaL_checkinteger ( L , 1 ) ;
2009-10-15 01:12:57 +00:00
//if((addr & ~0xFFFFFF) == ~0xFFFFFF)
// addr &= 0xFFFFFF;
2009-10-11 08:34:10 +00:00
// get optional second argument: size
int size = defaultSize ;
int funcIdx = 2 ;
if ( lua_isnumber ( L , 2 ) )
{
size = luaL_checkinteger ( L , 2 ) ;
if ( size < 0 )
{
size = - size ;
addr - = size ;
}
funcIdx + + ;
}
2008-11-02 19:10:53 +00:00
2009-10-11 08:34:10 +00:00
// check last argument: callback function
bool clearing = lua_isnil ( L , funcIdx ) ;
if ( ! clearing )
luaL_checktype ( L , funcIdx , LUA_TFUNCTION ) ;
lua_settop ( L , funcIdx ) ;
2008-11-02 19:10:53 +00:00
2009-10-11 08:34:10 +00:00
// get the address-to-callback table for this hook type of the current script
lua_getfield ( L , LUA_REGISTRYINDEX , luaMemHookTypeStrings [ hookType ] ) ;
// count how many callback functions we'll be displacing
int numFuncsAfter = clearing ? 0 : size ;
int numFuncsBefore = 0 ;
for ( unsigned int i = addr ; i ! = addr + size ; i + + )
{
lua_rawgeti ( L , - 1 , i ) ;
if ( lua_isfunction ( L , - 1 ) )
numFuncsBefore + + ;
lua_pop ( L , 1 ) ;
}
// put the callback function in the address slots
for ( unsigned int i = addr ; i ! = addr + size ; i + + )
{
lua_pushvalue ( L , - 2 ) ;
lua_rawseti ( L , - 2 , i ) ;
}
// adjust the count of active hooks
//LuaContextInfo& info = GetCurrentInfo();
/*info.*/ numMemHooks + = numFuncsAfter - numFuncsBefore ;
// re-cache regions of hooked memory across all scripts
CalculateMemHookRegions ( hookType ) ;
//StopScriptIfFinished(luaStateToUIDMap[L]);
2008-11-02 19:10:53 +00:00
return 0 ;
}
2009-10-11 08:34:10 +00:00
LuaMemHookType MatchHookTypeToCPU ( lua_State * L , LuaMemHookType hookType )
{
int cpuID = 0 ;
int cpunameIndex = 0 ;
if ( lua_type ( L , 2 ) = = LUA_TSTRING )
cpunameIndex = 2 ;
else if ( lua_type ( L , 3 ) = = LUA_TSTRING )
cpunameIndex = 3 ;
if ( cpunameIndex )
{
const char * cpuName = lua_tostring ( L , cpunameIndex ) ;
if ( ! stricmp ( cpuName , " sub " ) )
cpuID = 1 ;
lua_remove ( L , cpunameIndex ) ;
}
switch ( cpuID )
{
case 0 :
return hookType ;
case 1 :
switch ( hookType )
{
case LUAMEMHOOK_WRITE : return LUAMEMHOOK_WRITE_SUB ;
case LUAMEMHOOK_READ : return LUAMEMHOOK_READ_SUB ;
case LUAMEMHOOK_EXEC : return LUAMEMHOOK_EXEC_SUB ;
}
}
return hookType ;
}
static int memory_registerwrite ( lua_State * L )
{
return memory_registerHook ( L , MatchHookTypeToCPU ( L , LUAMEMHOOK_WRITE ) , 1 ) ;
}
static int memory_registerread ( lua_State * L )
{
return memory_registerHook ( L , MatchHookTypeToCPU ( L , LUAMEMHOOK_READ ) , 1 ) ;
}
static int memory_registerexec ( lua_State * L )
{
2009-10-15 01:12:57 +00:00
return memory_registerHook ( L , MatchHookTypeToCPU ( L , LUAMEMHOOK_EXEC ) , 1 ) ;
2009-10-11 08:34:10 +00:00
}
2009-03-08 22:36:38 +00:00
//adelikat: table pulled from GENS. credz nitsuja!
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2009-03-08 22:36:38 +00:00
const char * s_keyToName [ 256 ] =
{
NULL ,
" leftclick " ,
" rightclick " ,
NULL ,
" middleclick " ,
NULL ,
NULL ,
NULL ,
" backspace " ,
" tab " ,
NULL ,
NULL ,
NULL ,
" enter " ,
NULL ,
NULL ,
" shift " , // 0x10
" control " ,
" alt " ,
" pause " ,
" capslock " ,
NULL ,
NULL ,
NULL ,
NULL ,
NULL ,
NULL ,
" escape " ,
NULL ,
NULL ,
NULL ,
NULL ,
" space " , // 0x20
" pageup " ,
" pagedown " ,
" end " ,
" home " ,
" left " ,
" up " ,
" right " ,
" down " ,
NULL ,
NULL ,
NULL ,
NULL ,
" insert " ,
" delete " ,
NULL ,
" 0 " , " 1 " , " 2 " , " 3 " , " 4 " , " 5 " , " 6 " , " 7 " , " 8 " , " 9 " ,
NULL , NULL , NULL , NULL , NULL , NULL , NULL ,
" A " , " B " , " C " , " D " , " E " , " F " , " G " , " H " , " I " , " J " ,
" K " , " L " , " M " , " N " , " O " , " P " , " Q " , " R " , " S " , " T " ,
" U " , " V " , " W " , " X " , " Y " , " Z " ,
NULL ,
NULL ,
NULL ,
NULL ,
NULL ,
" numpad0 " , " numpad1 " , " numpad2 " , " numpad3 " , " numpad4 " , " numpad5 " , " numpad6 " , " numpad7 " , " numpad8 " , " numpad9 " ,
" numpad* " , " numpad+ " ,
NULL ,
" numpad- " , " numpad. " , " numpad/ " ,
" F1 " , " F2 " , " F3 " , " F4 " , " F5 " , " F6 " , " F7 " , " F8 " , " F9 " , " F10 " , " F11 " , " F12 " ,
" F13 " , " F14 " , " F15 " , " F16 " , " F17 " , " F18 " , " F19 " , " F20 " , " F21 " , " F22 " , " F23 " , " F24 " ,
NULL ,
NULL ,
NULL ,
NULL ,
NULL ,
NULL ,
NULL ,
NULL ,
" numlock " ,
" scrolllock " ,
NULL , // 0x92
NULL , NULL , NULL , NULL , NULL , NULL , NULL , NULL , NULL , NULL , NULL ,
NULL , NULL , NULL , NULL , NULL , NULL , NULL , NULL , NULL , NULL ,
NULL , NULL , NULL , NULL , NULL , NULL , NULL , NULL , NULL ,
NULL , NULL , NULL , NULL , NULL , NULL , NULL , NULL ,
NULL , // 0xB9
" semicolon " ,
" plus " ,
" comma " ,
" minus " ,
" period " ,
" slash " ,
" tilde " ,
NULL , // 0xC1
NULL , NULL , NULL , NULL , NULL , NULL , NULL , NULL , NULL ,
NULL , NULL , NULL , NULL , NULL , NULL , NULL , NULL ,
NULL , NULL , NULL , NULL , NULL , NULL , NULL ,
NULL , // 0xDA
" leftbracket " ,
" backslash " ,
" rightbracket " ,
" quote " ,
} ;
# endif
//adelikat - the code for the keys is copied directly from GENS. Props to nitsuja
// the code for the mouse is simply the same code from zapper.get
// input.get()
// takes no input, returns a lua table of entries representing the current input state,
// independent of the joypad buttons the emulated game thinks are pressed
// for example:
// if the user is holding the W key and the left mouse button
// and has the mouse at the bottom-right corner of the game screen,
// then this would return {W=true, leftclick=true, xmouse=319, ymouse=223}
static int input_get ( lua_State * L ) {
lua_newtable ( L ) ;
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2009-03-08 22:36:38 +00:00
// keyboard and mouse button status
{
extern int EnableBackgroundInput ;
unsigned char keys [ 256 ] ;
if ( ! EnableBackgroundInput )
{
if ( GetKeyboardState ( keys ) )
{
for ( int i = 1 ; i < 255 ; i + + )
{
int mask = ( i = = VK_CAPITAL | | i = = VK_NUMLOCK | | i = = VK_SCROLL ) ? 0x01 : 0x80 ;
if ( keys [ i ] & mask )
{
2018-12-17 20:11:30 +00:00
//ignore mouse buttons if the main window isn't focused
if ( i = = 1 | | i = = 2 )
if ( GetForegroundWindow ( ) ! = hAppWnd )
continue ;
2009-03-08 22:36:38 +00:00
const char * name = s_keyToName [ i ] ;
if ( name )
{
lua_pushboolean ( L , true ) ;
lua_setfield ( L , - 2 , name ) ;
}
}
}
}
}
else // use a slightly different method that will detect background input:
{
for ( int i = 1 ; i < 255 ; i + + )
{
const char * name = s_keyToName [ i ] ;
if ( name )
{
int active ;
2018-12-17 20:11:30 +00:00
//ignore mouse buttons if the main window isn't focused
if ( i = = 1 | | i = = 2 )
if ( GetForegroundWindow ( ) ! = hAppWnd )
continue ;
2009-03-08 22:36:38 +00:00
if ( i = = VK_CAPITAL | | i = = VK_NUMLOCK | | i = = VK_SCROLL )
active = GetKeyState ( i ) & 0x01 ;
else
active = GetAsyncKeyState ( i ) & 0x8000 ;
if ( active )
{
lua_pushboolean ( L , true ) ;
lua_setfield ( L , - 2 , name ) ;
}
}
}
}
}
2022-08-22 02:43:35 +00:00
# elif defined(__QT_DRIVER__)
// Qt/SDL
{
const uint8_t * keyBuf = QtSDL_getKeyboardState ( nullptr ) ;
if ( keyBuf )
{
int i = 0 ;
char keyName [ 64 ] ;
const char * keyOut = nullptr ;
for ( int i = 0 ; i < SDL_NUM_SCANCODES ; i + + )
{
if ( keyBuf [ i ] )
{
SDL_Keycode k = SDL_GetKeyFromScancode ( static_cast < SDL_Scancode > ( i ) ) ;
const char * name = SDL_GetKeyName ( k ) ;
//printf("Key:%i '%s'\n", i, name);
if ( isalpha ( name [ 0 ] ) | | isdigit ( name [ 0 ] ) )
{ // If name starts with letters or number, copy name without spaces
int ii = 0 , jj = 0 ;
while ( name [ ii ] ! = 0 )
{
if ( isalpha ( name [ ii ] ) | | isdigit ( name [ ii ] ) | | ( name [ ii ] = = ' _ ' ) )
{
keyName [ jj ] = name [ ii ] ; jj + + ;
}
ii + + ;
}
keyName [ jj ] = 0 ;
keyOut = keyName ;
}
else
{ // Handle special char names
switch ( name [ 0 ] )
{
case ' [ ' :
keyOut = " LeftBracket " ;
break ;
case ' ] ' :
keyOut = " RightBracket " ;
break ;
case ' { ' :
keyOut = " LeftBrace " ;
break ;
case ' } ' :
keyOut = " RightBrace " ;
break ;
case ' , ' :
keyOut = " Comma " ;
break ;
case ' . ' :
keyOut = " Period " ;
break ;
case ' ~ ' :
keyOut = " Tilde " ;
break ;
case ' ` ' :
keyOut = " Backtick " ;
break ;
case ' | ' :
keyOut = " VerticalBar " ;
break ;
case ' / ' :
keyOut = " Slash " ;
break ;
case ' \\ ' :
keyOut = " BackSlash " ;
break ;
case ' + ' :
keyOut = " Plus " ;
break ;
case ' = ' :
keyOut = " Equals " ;
break ;
case ' _ ' :
keyOut = " Underscore " ;
break ;
case ' - ' :
keyOut = " Minus " ;
break ;
case ' ; ' :
keyOut = " SemiColon " ;
break ;
case ' : ' :
keyOut = " Colon " ;
break ;
case ' \' ' :
case ' \" ' :
keyOut = " Quote " ;
break ;
default :
keyOut = name ;
break ;
}
}
lua_pushboolean ( L , true ) ;
lua_setfield ( L , - 2 , keyOut ) ;
}
}
}
}
2010-07-18 02:45:17 +00:00
# else
//SDL TODO: implement this for keyboard!!
# endif
2009-03-08 22:36:38 +00:00
// mouse position in game screen pixel coordinates
2012-10-21 16:40:04 +00:00
2009-03-08 22:36:38 +00:00
extern void GetMouseData ( uint32 ( & md ) [ 3 ] ) ;
uint32 MouseData [ 3 ] ;
GetMouseData ( MouseData ) ;
int x = MouseData [ 0 ] ;
int y = MouseData [ 1 ] ;
int click = MouseData [ 2 ] ; ///adelikat TODO: remove the ability to store the value 2? Since 2 is right-clicking and not part of zapper input and is used for context menus
lua_pushinteger ( L , x ) ;
2009-03-21 23:26:04 +00:00
lua_setfield ( L , - 2 , " xmouse " ) ;
2009-03-08 22:36:38 +00:00
lua_pushinteger ( L , y ) ;
2009-03-21 23:26:04 +00:00
lua_setfield ( L , - 2 , " ymouse " ) ;
2009-03-08 22:36:38 +00:00
lua_pushinteger ( L , click ) ;
2012-10-21 16:40:04 +00:00
lua_setfield ( L , - 2 , " click " ) ;
2009-03-08 22:36:38 +00:00
return 1 ;
}
2008-11-02 19:10:53 +00:00
2012-10-21 16:40:04 +00:00
// table zapper.read
2009-03-08 18:10:07 +00:00
//int which unecessary because zapper is always controller 2
//Reads the zapper coordinates and a click value (1 if clicked, 0 if not, 2 if right click (but this is not used for zapper input)
static int zapper_read ( lua_State * L ) {
2012-10-21 16:40:04 +00:00
2009-03-08 18:10:07 +00:00
lua_newtable ( L ) ;
2010-04-08 20:44:59 +00:00
int z = 0 ;
2010-03-28 04:47:02 +00:00
extern void GetMouseData ( uint32 ( & md ) [ 3 ] ) ; //adelikat: shouldn't this be ifdef'ed for Win32?
int x , y , click ;
2010-05-12 15:31:24 +00:00
if ( FCEUMOV_Mode ( MOVIEMODE_PLAY ) )
2010-03-28 04:47:02 +00:00
{
2010-04-08 20:44:59 +00:00
if ( ! currFrameCounter )
z = 0 ;
else
z = currFrameCounter - 1 ;
x = currMovieData . records [ z ] . zappers [ 1 ] . x ; //adelikat: Used hardcoded port 1 since as far as I know, only port 1 is valid for zappers
y = currMovieData . records [ z ] . zappers [ 1 ] . y ;
click = currMovieData . records [ z ] . zappers [ 1 ] . b ;
2010-03-28 04:47:02 +00:00
}
else
{
uint32 MouseData [ 3 ] ;
GetMouseData ( MouseData ) ;
x = MouseData [ 0 ] ;
y = MouseData [ 1 ] ;
2012-10-21 16:40:04 +00:00
click = MouseData [ 2 ] ;
2010-03-28 04:47:02 +00:00
if ( click > 1 )
click = 1 ; //adelikat: This is zapper.read() thus should only give valid zapper input (instead of simply mouse input
}
2009-03-08 18:10:07 +00:00
lua_pushinteger ( L , x ) ;
2010-03-28 04:47:02 +00:00
lua_setfield ( L , - 2 , " x " ) ;
2009-03-08 18:10:07 +00:00
lua_pushinteger ( L , y ) ;
2010-03-28 04:47:02 +00:00
lua_setfield ( L , - 2 , " y " ) ;
2009-03-08 18:10:07 +00:00
lua_pushinteger ( L , click ) ;
2012-10-21 16:40:04 +00:00
lua_setfield ( L , - 2 , " fire " ) ;
2009-03-08 18:10:07 +00:00
return 1 ;
}
2019-12-15 20:31:09 +00:00
// zapper.set(table state)
//
// Sets the zapper state for the next frame advance.
static int zapper_set ( lua_State * L ) {
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
luazapperx = - 1 ;
luazappery = - 1 ;
luazapperfire = - 1 ;
2009-03-08 18:10:07 +00:00
2019-12-15 20:31:09 +00:00
lua_getfield ( L , 1 , " x " ) ;
if ( ! lua_isnil ( L , - 1 ) ) luazapperx = lua_tointeger ( L , - 1 ) ;
lua_pop ( L , 1 ) ;
lua_getfield ( L , 1 , " y " ) ;
if ( ! lua_isnil ( L , - 1 ) ) luazappery = lua_tointeger ( L , - 1 ) ;
lua_pop ( L , 1 ) ;
lua_getfield ( L , 1 , " fire " ) ;
if ( ! lua_isnil ( L , - 1 ) )
{
if ( lua_toboolean ( L , - 1 ) ) // True or string
luazapperfire = 1 ;
if ( lua_toboolean ( L , - 1 ) = = 0 | | lua_isstring ( L , - 1 ) ) // False or string
luazapperfire = 0 ;
}
lua_pop ( L , 1 ) ;
return 0 ;
}
2009-03-08 18:10:07 +00:00
2008-11-02 19:10:53 +00:00
// table joypad.read(int which = 1)
//
// Reads the joypads as inputted by the user.
// TODO: Don't read in *everything*...
2009-11-08 12:15:25 +00:00
static int joy_get_internal ( lua_State * L , bool reportUp , bool reportDown ) {
2008-11-02 19:10:53 +00:00
// Reads the joypads as inputted by the user
int which = luaL_checkinteger ( L , 1 ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
if ( which < 1 | | which > 4 ) {
2009-01-01 15:04:05 +00:00
luaL_error ( L , " Invalid input port (valid range 1-4, specified %d) " , which ) ;
2008-11-02 19:10:53 +00:00
}
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// Use the OS-specific code to do the reading.
extern SFORMAT FCEUCTRL_STATEINFO [ ] ;
uint8 buttons = ( ( uint8 * ) FCEUCTRL_STATEINFO [ 1 ] . v ) [ which - 1 ] ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
lua_newtable ( L ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
int i ;
for ( i = 0 ; i < 8 ; i + + ) {
2009-11-08 12:15:25 +00:00
bool pressed = ( buttons & ( 1 < < i ) ) ! = 0 ;
if ( ( pressed & & reportDown ) | | ( ! pressed & & reportUp ) ) {
lua_pushboolean ( L , pressed ) ;
2008-11-02 19:10:53 +00:00
lua_setfield ( L , - 2 , button_mappings [ i ] ) ;
}
}
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
return 1 ;
}
2009-11-08 12:15:25 +00:00
// joypad.get(which)
// returns a table of every game button,
// true meaning currently-held and false meaning not-currently-held
// (as of last frame boundary)
// this WILL read input from a currently-playing movie
static int joypad_get ( lua_State * L )
{
return joy_get_internal ( L , true , true ) ;
}
// joypad.getdown(which)
// returns a table of every game button that is currently held
static int joypad_getdown ( lua_State * L )
{
return joy_get_internal ( L , false , true ) ;
}
// joypad.getup(which)
// returns a table of every game button that is not currently held
static int joypad_getup ( lua_State * L )
{
return joy_get_internal ( L , true , false ) ;
}
2008-11-02 19:10:53 +00:00
2012-02-01 22:02:26 +00:00
// table joypad.getimmediate(int which)
// Reads immediate state of joypads (at the moment of calling)
static int joypad_getimmediate ( lua_State * L )
{
int which = luaL_checkinteger ( L , 1 ) ;
if ( which < 1 | | which > 4 )
{
luaL_error ( L , " Invalid input port (valid range 1-4, specified %d) " , which ) ;
}
// Currently only supports Windows, sorry...
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2014-11-22 01:23:19 +00:00
extern uint32 GetGamepadPressedImmediate ( ) ;
uint8 buttons = GetGamepadPressedImmediate ( ) > > ( ( which - 1 ) * 8 ) ;
2012-10-21 16:40:04 +00:00
2012-02-01 22:02:26 +00:00
lua_newtable ( L ) ;
for ( int i = 0 ; i < 8 ; + + i )
{
lua_pushboolean ( L , ( buttons & ( 1 < < i ) ) ! = 0 ) ;
lua_setfield ( L , - 2 , button_mappings [ i ] ) ;
}
# else
lua_pushnil ( L ) ;
# endif
return 1 ;
}
2008-11-02 19:10:53 +00:00
2009-03-14 21:00:28 +00:00
// joypad.set(int which, table buttons)
2008-11-02 19:10:53 +00:00
//
// Sets the given buttons to be pressed during the next
2012-10-21 16:40:04 +00:00
// frame advance. The table should have the right
2008-11-02 19:10:53 +00:00
// keys (no pun intended) set.
2009-09-15 16:39:49 +00:00
/*FatRatKnight: I changed some of the logic.
2009-09-15 18:20:10 +00:00
Now with 4 options ! */
2008-11-02 19:10:53 +00:00
static int joypad_set ( lua_State * L ) {
// Which joypad we're tampering with
int which = luaL_checkinteger ( L , 1 ) ;
if ( which < 1 | | which > 4 ) {
luaL_error ( L , " Invalid output port (valid range 1-4, specified %d) " , which ) ;
}
// And the table of buttons.
luaL_checktype ( L , 2 , LUA_TTABLE ) ;
// Set up for taking control of the indicated controller
2009-09-15 16:39:49 +00:00
luajoypads1 [ which - 1 ] = 0xFF ; // .1 Reset right bit
luajoypads2 [ which - 1 ] = 0x00 ; // 0. Reset left bit
2009-03-14 21:00:28 +00:00
2008-11-02 19:10:53 +00:00
int i ;
for ( i = 0 ; i < 8 ; i + + ) {
lua_getfield ( L , 2 , button_mappings [ i ] ) ;
2012-10-21 16:40:04 +00:00
2009-03-14 21:00:28 +00:00
//Button is not nil, so find out if it is true/false
2012-10-21 16:40:04 +00:00
if ( ! lua_isnil ( L , - 1 ) )
2009-03-14 21:00:28 +00:00
{
2009-09-15 18:20:10 +00:00
if ( lua_toboolean ( L , - 1 ) ) //True or string
2009-09-15 16:39:49 +00:00
luajoypads2 [ which - 1 ] | = 1 < < i ;
2009-09-15 18:20:10 +00:00
if ( lua_toboolean ( L , - 1 ) = = 0 | | lua_isstring ( L , - 1 ) ) //False or string
2009-09-15 16:39:49 +00:00
luajoypads1 [ which - 1 ] & = ~ ( 1 < < i ) ;
2009-03-14 21:00:28 +00:00
}
2009-09-15 16:39:49 +00:00
2009-03-14 21:00:28 +00:00
else
{
}
2008-11-02 19:10:53 +00:00
lua_pop ( L , 1 ) ;
}
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
return 0 ;
}
2009-10-25 03:02:00 +00:00
// Helper function to convert a savestate object to the filename it represents.
static const char * savestateobj2filename ( lua_State * L , int offset ) {
2012-10-21 16:40:04 +00:00
2009-10-25 03:02:00 +00:00
// First we get the metatable of the indicated object
int result = lua_getmetatable ( L , offset ) ;
2008-11-02 19:10:53 +00:00
2009-10-25 03:02:00 +00:00
if ( ! result )
luaL_error ( L , " object not a savestate object " ) ;
2012-10-21 16:40:04 +00:00
2009-10-25 03:02:00 +00:00
// Also check that the type entry is set
lua_getfield ( L , - 1 , " __metatable " ) ;
if ( strcmp ( lua_tostring ( L , - 1 ) , " FCEU Savestate " ) ! = 0 )
luaL_error ( L , " object not a savestate object " ) ;
lua_pop ( L , 1 ) ;
2012-10-21 16:40:04 +00:00
2009-10-25 03:02:00 +00:00
// Now, get the field we want
lua_getfield ( L , - 1 , " filename " ) ;
2012-10-21 16:40:04 +00:00
2009-10-25 03:02:00 +00:00
// Return it
return lua_tostring ( L , - 1 ) ;
}
2008-11-02 19:10:53 +00:00
// Helper function for garbage collection.
static int savestate_gc ( lua_State * L ) {
2008-08-18 03:11:42 +00:00
LuaSaveState * ss = ( LuaSaveState * ) lua_touserdata ( L , 1 ) ;
if ( ss - > persisted & & ss - > anonymous )
remove ( ss - > filename . c_str ( ) ) ;
ss - > ~ LuaSaveState ( ) ;
2008-11-02 19:10:53 +00:00
//// The object we're collecting is on top of the stack
//lua_getmetatable(L,1);
//
//// Get the filename
//const char *filename;
//lua_getfield(L, -1, "filename");
//filename = lua_tostring(L,-1);
//// Delete the file
//remove(filename);
//
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// We exit, and the garbage collector takes care of the rest.
return 0 ;
}
2010-05-09 02:02:20 +00:00
// Referenced by:
2018-07-02 20:18:36 +00:00
// savestate.create(var which = nil)
2010-05-09 02:02:20 +00:00
// savestate.object(int which = nil)
2008-11-02 19:10:53 +00:00
//
// Creates an object used for savestates.
// The object can be associated with a player-accessible savestate
// ("which" between 1 and 10) or not (which == nil).
2018-07-02 20:18:36 +00:00
// If "which" is a string it's interpreted as a filesystem path
2010-05-09 02:02:20 +00:00
static int savestate_create_aliased ( lua_State * L , bool newnumbering ) {
2008-11-02 19:10:53 +00:00
int which = - 1 ;
2018-07-02 20:18:36 +00:00
const char * path = NULL ;
bool hasArg = false ;
2019-03-18 04:26:26 +00:00
if ( lua_gettop ( L ) > = 1 )
{
2018-07-02 20:18:36 +00:00
hasArg = true ;
2019-03-18 04:26:26 +00:00
if ( lua_isnumber ( L , 1 ) )
{
which = luaL_checkinteger ( L , 1 ) ;
2018-07-02 20:18:36 +00:00
if ( which < 1 | | which > 10 ) {
luaL_error ( L , " invalid player's savestate %d " , which ) ;
}
2008-11-02 19:10:53 +00:00
}
2019-03-18 04:26:26 +00:00
else
{
path = luaL_checkstring ( L , 1 ) ;
}
2008-11-02 19:10:53 +00:00
}
//lets use lua to allocate the memory, since it is effectively a memory pool.
LuaSaveState * ss = new ( lua_newuserdata ( L , sizeof ( LuaSaveState ) ) ) LuaSaveState ( ) ;
if ( which > 0 ) {
// Find an appropriate filename. This is OS specific, unfortunately.
// So I turned the filename selection code into my bitch. :)
2010-05-09 02:02:20 +00:00
// Numbers are 0 through 9.
if ( newnumbering ) //1-9, 10 = 0. QWERTY style.
2019-03-18 04:26:26 +00:00
ss - > filename = FCEU_MakeFName ( FCEUMKF_STATE , ( which % 10 ) , 0 ) ;
2010-05-09 02:02:20 +00:00
else // Note: Windows Slots 1-10 = Which 2-10, 1
2019-03-18 04:26:26 +00:00
ss - > filename = FCEU_MakeFName ( FCEUMKF_STATE , which - 1 , 0 ) ;
2010-05-09 02:02:20 +00:00
// Only ensure load if the file exists
// Also makes it persistent, but files are like that
if ( CheckFileExists ( ss - > filename . c_str ( ) ) )
ss - > ensureLoad ( ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
}
else {
//char tempbuf[100] = "snluaXXXXXX";
//filename = mktemp(tempbuf);
//doesnt work -^
2012-10-21 16:40:04 +00:00
2018-07-02 20:18:36 +00:00
if ( hasArg )
{
ss - > filename = path ;
2018-11-15 00:44:18 +00:00
2018-07-02 20:18:36 +00:00
EMUFILE_FILE inf ( path , " rb " ) ;
if ( ! inf . fail ( ) )
ss - > data = ( EMUFILE_MEMORY * ) inf . memwrap ( ) ;
}
2018-11-15 00:44:18 +00:00
else
2018-07-02 20:18:36 +00:00
{
char * tmp = tempnam ( NULL , " snlua " ) ;
ss - > filename = tmp ;
free ( tmp ) ;
ss - > anonymous = true ;
}
2008-11-02 19:10:53 +00:00
}
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// The metatable we use, protected from Lua and contains garbage collection info and stuff.
lua_newtable ( L ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
//// First, we must protect it
lua_pushstring ( L , " FCEU Savestate " ) ;
lua_setfield ( L , - 2 , " __metatable " ) ;
//
//
//// Now we need to save the file itself.
//lua_pushstring(L, filename.c_str());
//lua_setfield(L, -2, "filename");
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// If it's an anonymous savestate, we must delete the file from disk should it be gargage collected
//if (which < 0) {
lua_pushcfunction ( L , savestate_gc ) ;
lua_setfield ( L , - 2 , " __gc " ) ;
//}
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// Set the metatable
lua_setmetatable ( L , - 2 ) ;
// Awesome. Return the object
return 1 ;
}
2010-05-09 02:02:20 +00:00
// object savestate.object(int which = nil)
//
// Creates an object used for savestates.
// The object can be associated with a player-accessible savestate
// ("which" between 1 and 10) or not (which == nil).
// Uses more windows-friendly slot numbering
static int savestate_object ( lua_State * L ) {
// New Save Slot Numbers:
// 1-9 refer to 1-9, 10 refers to 0. QWERTY style.
return savestate_create_aliased ( L , true ) ;
}
2018-07-02 20:18:36 +00:00
// object savestate.create(var which = nil)
2010-05-09 02:02:20 +00:00
//
// Creates an object used for savestates.
// The object can be associated with a player-accessible savestate
// ("which" between 1 and 10) or not (which == nil).
// Uses original slot numbering
2018-07-02 20:18:36 +00:00
// If "which" is a string it's interpreted as a string filesystem path
2010-05-09 02:02:20 +00:00
static int savestate_create ( lua_State * L ) {
// Original Save Slot Numbers:
// 1-10, 1 refers to slot 0, 2-10 refer to 1-9
return savestate_create_aliased ( L , false ) ;
}
2008-11-02 19:10:53 +00:00
// savestate.save(object state)
//
// Saves a state to the given object.
static int savestate_save ( lua_State * L ) {
//char *filename = savestateobj2filename(L,1);
LuaSaveState * ss = ( LuaSaveState * ) lua_touserdata ( L , 1 ) ;
2010-05-09 02:02:20 +00:00
if ( ! ss ) {
luaL_error ( L , " Invalid savestate.save object " ) ;
return 0 ;
}
2008-11-02 19:10:53 +00:00
if ( ss - > data ) delete ss - > data ;
2010-05-17 21:02:38 +00:00
ss - > data = new EMUFILE_MEMORY ( ) ;
2008-11-02 19:10:53 +00:00
// printf("saving %s\n", filename);
// Save states are very expensive. They take time.
numTries - - ;
FCEUSS_SaveMS ( ss - > data , Z_NO_COMPRESSION ) ;
2010-05-17 21:02:38 +00:00
ss - > data - > fseek ( 0 , SEEK_SET ) ;
2008-11-02 19:10:53 +00:00
return 0 ;
}
static int savestate_persist ( lua_State * L ) {
LuaSaveState * ss = ( LuaSaveState * ) lua_touserdata ( L , 1 ) ;
ss - > persist ( ) ;
return 0 ;
}
// savestate.load(object state)
//
// Loads the given state
static int savestate_load ( lua_State * L ) {
//char *filename = savestateobj2filename(L,1);
LuaSaveState * ss = ( LuaSaveState * ) lua_touserdata ( L , 1 ) ;
2010-05-09 02:02:20 +00:00
if ( ! ss ) {
luaL_error ( L , " Invalid savestate.load object " ) ;
return 0 ;
}
2008-11-02 19:10:53 +00:00
numTries - - ;
2010-05-09 02:02:20 +00:00
/*if (!ss->data) {
luaL_error ( L , " Invalid savestate.load data " ) ;
return 0 ;
} */
if ( FCEUSS_LoadFP ( ss - > data , SSLOADPARAM_NOBACKUP ) )
2010-05-17 21:02:38 +00:00
ss - > data - > fseek ( 0 , SEEK_SET ) ;
2010-05-09 02:02:20 +00:00
2008-11-02 19:10:53 +00:00
return 0 ;
}
2009-10-25 03:02:00 +00:00
static int savestate_registersave ( lua_State * L ) {
2012-10-21 16:40:04 +00:00
2009-10-25 03:02:00 +00:00
lua_settop ( L , 1 ) ;
if ( ! lua_isnil ( L , 1 ) )
luaL_checktype ( L , 1 , LUA_TFUNCTION ) ;
lua_getfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_BEFORESAVE ] ) ;
lua_pushvalue ( L , 1 ) ;
lua_setfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_BEFORESAVE ] ) ;
//StopScriptIfFinished(luaStateToUIDMap[L]);
return 1 ;
}
static int savestate_registerload ( lua_State * L ) {
lua_settop ( L , 1 ) ;
if ( ! lua_isnil ( L , 1 ) )
luaL_checktype ( L , 1 , LUA_TFUNCTION ) ;
lua_getfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_AFTERLOAD ] ) ;
lua_pushvalue ( L , 1 ) ;
lua_setfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_AFTERLOAD ] ) ;
//StopScriptIfFinished(luaStateToUIDMap[L]);
return 1 ;
}
static int savestate_loadscriptdata ( lua_State * L ) {
const char * filename = savestateobj2filename ( L , 1 ) ;
{
LuaSaveData saveData ;
char luaSaveFilename [ 512 ] ;
strncpy ( luaSaveFilename , filename , 512 ) ;
luaSaveFilename [ 512 - ( 1 + 7 /*strlen(".luasav")*/ ) ] = ' \0 ' ;
strcat ( luaSaveFilename , " .luasav " ) ;
FILE * luaSaveFile = fopen ( luaSaveFilename , " rb " ) ;
if ( luaSaveFile )
{
saveData . ImportRecords ( luaSaveFile ) ;
fclose ( luaSaveFile ) ;
lua_settop ( L , 0 ) ;
saveData . LoadRecord ( L , LUA_DATARECORDKEY , ( unsigned int ) - 1 ) ;
return lua_gettop ( L ) ;
}
}
return 0 ;
}
2008-11-02 19:10:53 +00:00
2009-10-11 05:09:17 +00:00
// int emu.framecount()
2008-11-02 19:10:53 +00:00
//
2009-10-11 05:09:17 +00:00
// Gets the frame counter
int emu_framecount ( lua_State * L ) {
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
lua_pushinteger ( L , FCEUMOV_GetFrame ( ) ) ;
return 1 ;
}
2013-09-15 11:35:14 +00:00
// int emu.lagcount()
2009-03-07 19:26:07 +00:00
//
2013-09-15 11:35:14 +00:00
// Gets the current lag count
2009-10-11 04:39:05 +00:00
int emu_lagcount ( lua_State * L ) {
2009-03-07 19:26:07 +00:00
lua_pushinteger ( L , FCEUI_GetLagCount ( ) ) ;
return 1 ;
}
2013-09-15 11:35:14 +00:00
// emu.lagged()
2009-03-15 15:29:35 +00:00
//
2013-09-15 11:35:14 +00:00
// Returns true if the game is currently on a lag frame
2009-10-11 04:39:05 +00:00
int emu_lagged ( lua_State * L ) {
2009-03-08 19:14:37 +00:00
bool Lag_Frame = FCEUI_GetLagged ( ) ;
lua_pushboolean ( L , Lag_Frame ) ;
return 1 ;
}
2009-03-07 19:26:07 +00:00
2013-09-15 11:35:14 +00:00
// emu.setlagflag(bool value)
2012-09-13 19:48:14 +00:00
//
2013-09-15 11:35:14 +00:00
// Returns true if the game is currently on a lag frame
2012-09-13 19:48:14 +00:00
int emu_setlagflag ( lua_State * L )
{
FCEUI_SetLagFlag ( lua_toboolean ( L , 1 ) = = 1 ) ;
return 0 ;
}
2009-11-08 12:15:25 +00:00
// boolean emu.emulating()
int emu_emulating ( lua_State * L ) {
lua_pushboolean ( L , GameInfo ! = NULL ) ;
return 1 ;
}
2008-11-02 19:10:53 +00:00
// string movie.mode()
//
2013-09-15 11:35:14 +00:00
// Returns "taseditor", "record", "playback", "finished" or nil
2011-12-25 21:54:31 +00:00
int movie_mode ( lua_State * L )
{
2012-01-12 19:56:17 +00:00
if ( FCEUMOV_Mode ( MOVIEMODE_TASEDITOR ) )
2011-12-25 21:54:31 +00:00
lua_pushstring ( L , " taseditor " ) ;
else if ( FCEUMOV_IsRecording ( ) )
2008-11-02 19:10:53 +00:00
lua_pushstring ( L , " record " ) ;
2010-05-12 15:31:24 +00:00
else if ( FCEUMOV_IsFinished ( ) )
2011-12-25 21:54:31 +00:00
lua_pushstring ( L , " finished " ) ; //Note: this comes before playback since playback checks for finished as well
2008-11-02 19:10:53 +00:00
else if ( FCEUMOV_IsPlaying ( ) )
lua_pushstring ( L , " playback " ) ;
else
lua_pushnil ( L ) ;
return 1 ;
}
static int movie_rerecordcounting ( lua_State * L ) {
if ( lua_gettop ( L ) = = 0 )
luaL_error ( L , " no parameters specified " ) ;
skipRerecords = lua_toboolean ( L , 1 ) ;
return 0 ;
}
// movie.stop()
//
// Stops movie playback/recording. Bombs out if movie is not running.
static int movie_stop ( lua_State * L ) {
if ( ! FCEUMOV_IsRecording ( ) & & ! FCEUMOV_IsPlaying ( ) )
luaL_error ( L , " no movie " ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
FCEUI_StopMovie ( ) ;
return 0 ;
}
2009-03-15 15:29:35 +00:00
// movie.active()
//
2009-03-15 21:12:15 +00:00
//returns a bool value is there is a movie currently open
2009-10-11 05:29:41 +00:00
int movie_isactive ( lua_State * L ) {
2009-03-15 15:29:35 +00:00
bool movieactive = ( FCEUMOV_IsRecording ( ) | | FCEUMOV_IsPlaying ( ) ) ;
lua_pushboolean ( L , movieactive ) ;
return 1 ;
}
2009-10-11 05:29:41 +00:00
// movie.recording()
int movie_isrecording ( lua_State * L ) {
lua_pushboolean ( L , FCEUMOV_IsRecording ( ) ) ;
return 1 ;
}
// movie.playing()
int movie_isplaying ( lua_State * L ) {
lua_pushboolean ( L , FCEUMOV_IsPlaying ( ) ) ;
return 1 ;
}
2009-03-15 21:12:15 +00:00
//movie.rerecordcount()
//
//returns the rerecord count of the current movie
static int movie_rerecordcount ( lua_State * L ) {
2012-01-29 22:33:23 +00:00
if ( ! FCEUMOV_IsRecording ( ) & & ! FCEUMOV_IsPlaying ( ) & & ! FCEUMOV_Mode ( MOVIEMODE_TASEDITOR ) )
2009-03-15 21:12:15 +00:00
luaL_error ( L , " No movie loaded. " ) ;
lua_pushinteger ( L , FCEUI_GetMovieRerecordCount ( ) ) ;
2012-10-21 16:40:04 +00:00
2009-03-15 21:12:15 +00:00
return 1 ;
}
//movie.length()
//
//returns an int value representing the total length of the current movie loaded
2009-10-11 05:29:41 +00:00
static int movie_getlength ( lua_State * L ) {
2012-01-29 22:33:23 +00:00
if ( ! FCEUMOV_IsRecording ( ) & & ! FCEUMOV_IsPlaying ( ) & & ! FCEUMOV_Mode ( MOVIEMODE_TASEDITOR ) )
2009-03-15 21:12:15 +00:00
luaL_error ( L , " No movie loaded. " ) ;
lua_pushinteger ( L , FCEUI_GetMovieLength ( ) ) ;
return 1 ;
}
2009-10-11 05:09:17 +00:00
//movie.readonly
2009-03-15 21:36:01 +00:00
//
2009-10-11 05:09:17 +00:00
//returns true is emulator is in read-only mode, false if it is in read+write
static int movie_getreadonly ( lua_State * L ) {
2009-03-15 21:36:01 +00:00
lua_pushboolean ( L , FCEUI_GetMovieToggleReadOnly ( ) ) ;
return 1 ;
}
2009-10-11 05:09:17 +00:00
//movie.setreadonly
2009-03-15 23:46:57 +00:00
//
//Sets readonly / read+write status
2009-10-11 05:09:17 +00:00
static int movie_setreadonly ( lua_State * L ) {
2009-03-15 23:21:52 +00:00
bool which = ( lua_toboolean ( L , 1 ) = = 1 ) ;
FCEUI_SetMovieToggleReadOnly ( which ) ;
2012-10-21 16:40:04 +00:00
2009-03-15 23:21:52 +00:00
return 0 ;
}
2009-03-15 23:46:57 +00:00
//movie.getname
//
//returns the filename of the movie loaded
static int movie_getname ( lua_State * L ) {
2012-01-29 22:33:23 +00:00
if ( ! FCEUMOV_IsRecording ( ) & & ! FCEUMOV_IsPlaying ( ) & & ! FCEUMOV_Mode ( MOVIEMODE_TASEDITOR ) )
2009-03-15 23:46:57 +00:00
luaL_error ( L , " No movie loaded. " ) ;
2012-10-21 16:40:04 +00:00
2009-03-15 23:46:57 +00:00
std : : string name = FCEUI_GetMovieName ( ) ;
lua_pushstring ( L , name . c_str ( ) ) ;
return 1 ;
}
2010-05-29 04:06:49 +00:00
//movie.getfilename
//
//returns the filename of movie loaded with no path
static int movie_getfilename ( lua_State * L ) {
2012-10-21 16:40:04 +00:00
2012-01-29 22:33:23 +00:00
if ( ! FCEUMOV_IsRecording ( ) & & ! FCEUMOV_IsPlaying ( ) & & ! FCEUMOV_Mode ( MOVIEMODE_TASEDITOR ) )
2010-05-29 04:06:49 +00:00
luaL_error ( L , " No movie loaded. " ) ;
2012-10-21 16:40:04 +00:00
2010-05-29 04:06:49 +00:00
std : : string name = FCEUI_GetMovieName ( ) ;
int x = name . find_last_of ( " / \\ " ) + 1 ;
if ( x )
name = name . substr ( x , name . length ( ) - x ) ;
lua_pushstring ( L , name . c_str ( ) ) ;
return 1 ;
}
2009-10-11 05:29:41 +00:00
//movie.replay
2009-03-15 23:55:07 +00:00
//
//calls the play movie from beginning function
2009-10-11 05:29:41 +00:00
static int movie_replay ( lua_State * L ) {
2009-03-15 23:55:07 +00:00
FCEUI_MoviePlayFromBeginning ( ) ;
return 0 ;
}
2018-11-29 03:44:45 +00:00
// bool movie.play(string filename, [bool read_only, [int pauseframe]])
//
// Loads and plays a movie.
int movie_playback ( lua_State * L ) {
int arg_count = lua_gettop ( L ) ;
if ( arg_count = = 0 ) {
luaL_error ( L , " no parameters specified " ) ;
return 0 ;
}
const char * filename = luaL_checkstring ( L , 1 ) ;
if ( filename = = NULL ) {
luaL_error ( L , " Filename required " ) ;
return 0 ;
}
bool read_only = arg_count > = 2 ? ( lua_toboolean ( L , 2 ) = = 1 ) : 0 ;
int pauseframe = arg_count > = 3 ? lua_tointeger ( L , 3 ) : 0 ;
if ( pauseframe < 0 ) pauseframe = 0 ;
// Load it!
bool loaded = FCEUI_LoadMovie ( filename , read_only , pauseframe ) ;
lua_pushboolean ( L , loaded ) ;
return 1 ;
}
// bool movie.record(string filename, [int save_type, [string author]])
//
// Saves and records a movie.
int movie_record ( lua_State * L ) {
int arg_count = lua_gettop ( L ) ;
if ( arg_count = = 0 ) {
luaL_error ( L , " no parameters specified " ) ;
return 0 ;
}
const char * filename = luaL_checkstring ( L , 1 ) ;
if ( filename = = NULL ) {
luaL_error ( L , " Filename required " ) ;
return 0 ;
}
// No need to use the full functionality of the enum
int save_type = arg_count > = 2 ? lua_tointeger ( L , 2 ) : 0 ;
EMOVIE_FLAG flags ;
if ( save_type = = 1 ) flags = MOVIE_FLAG_NONE ; // from savestate
else if ( save_type = = 2 ) flags = MOVIE_FLAG_FROM_SAVERAM ;
else flags = MOVIE_FLAG_FROM_POWERON ;
// XXX: Assuming UTF-8 strings in Lua
std : : wstring author =
arg_count > = 3 ?
mbstowcs ( ( std : : string ) luaL_checkstring ( L , 3 ) ) : L " "
;
// Save it!
FCEUI_SaveMovie ( filename , flags , author ) ;
lua_pushboolean ( L , 1 ) ;
return 1 ;
}
2010-04-21 03:24:14 +00:00
//movie.ispoweron
//
//If movie is recorded from power-on
static int movie_ispoweron ( lua_State * L ) {
2010-04-21 21:10:17 +00:00
if ( FCEUMOV_IsRecording ( ) | | FCEUMOV_IsPlaying ( ) ) {
return FCEUMOV_FromPoweron ( ) ;
}
2010-04-21 03:24:14 +00:00
else
return 0 ;
}
//movie.isfromsavestate()
//
//If movie is recorded from a savestate
static int movie_isfromsavestate ( lua_State * L ) {
2010-04-21 21:10:17 +00:00
if ( FCEUMOV_IsRecording ( ) | | FCEUMOV_IsPlaying ( ) ) {
return ! FCEUMOV_FromPoweron ( ) ;
}
2010-04-21 03:24:14 +00:00
else
return 0 ;
}
2009-10-12 07:32:31 +00:00
# define LUA_SCREEN_WIDTH 256
2010-12-18 01:07:10 +00:00
# define LUA_SCREEN_HEIGHT 240
2009-10-12 07:32:31 +00:00
2008-11-02 19:10:53 +00:00
// Common code by the gui library: make sure the screen array is ready
static void gui_prepare ( ) {
if ( ! gui_data )
2010-09-19 00:00:38 +00:00
gui_data = ( uint8 * ) FCEU_dmalloc ( LUA_SCREEN_WIDTH * LUA_SCREEN_HEIGHT * 4 ) ;
2008-11-02 19:10:53 +00:00
if ( gui_used ! = GUI_USED_SINCE_LAST_DISPLAY )
2009-10-12 07:32:31 +00:00
memset ( gui_data , 0 , LUA_SCREEN_WIDTH * LUA_SCREEN_HEIGHT * 4 ) ;
2008-11-02 19:10:53 +00:00
gui_used = GUI_USED_SINCE_LAST_DISPLAY ;
}
2009-10-12 07:32:31 +00:00
// pixform for lua graphics
# define BUILD_PIXEL_ARGB8888(A,R,G,B) (((int) (A) << 24) | ((int) (R) << 16) | ((int) (G) << 8) | (int) (B))
# define DECOMPOSE_PIXEL_ARGB8888(PIX,A,R,G,B) { (A) = ((PIX) >> 24) & 0xff; (R) = ((PIX) >> 16) & 0xff; (G) = ((PIX) >> 8) & 0xff; (B) = (PIX) & 0xff; }
# define LUA_BUILD_PIXEL BUILD_PIXEL_ARGB8888
# define LUA_DECOMPOSE_PIXEL DECOMPOSE_PIXEL_ARGB8888
2009-11-08 12:15:25 +00:00
# define LUA_PIXEL_A(PIX) (((PIX) >> 24) & 0xff)
# define LUA_PIXEL_R(PIX) (((PIX) >> 16) & 0xff)
# define LUA_PIXEL_G(PIX) (((PIX) >> 8) & 0xff)
# define LUA_PIXEL_B(PIX) ((PIX) & 0xff)
2008-11-02 19:10:53 +00:00
2021-11-19 02:55:31 +00:00
namespace fceu
{
template < class T > static void swap ( T & one , T & two ) {
T temp = one ;
one = two ;
two = temp ;
}
}
2009-10-12 07:32:31 +00:00
// write a pixel to buffer
static inline void blend32 ( uint32 * dstPixel , uint32 colour )
{
uint8 * dst = ( uint8 * ) dstPixel ;
int a , r , g , b ;
LUA_DECOMPOSE_PIXEL ( colour , a , r , g , b ) ;
if ( a = = 255 | | dst [ 3 ] = = 0 ) {
// direct copy
* ( uint32 * ) ( dst ) = colour ;
}
else if ( a = = 0 ) {
// do not copy
}
else {
// alpha-blending
int a_dst = ( ( 255 - a ) * dst [ 3 ] + 128 ) / 255 ;
int a_new = a + a_dst ;
dst [ 0 ] = ( uint8 ) ( ( ( dst [ 0 ] * a_dst + b * a ) + ( a_new / 2 ) ) / a_new ) ;
dst [ 1 ] = ( uint8 ) ( ( ( dst [ 1 ] * a_dst + g * a ) + ( a_new / 2 ) ) / a_new ) ;
dst [ 2 ] = ( uint8 ) ( ( ( dst [ 2 ] * a_dst + r * a ) + ( a_new / 2 ) ) / a_new ) ;
dst [ 3 ] = ( uint8 ) a_new ;
}
}
// check if a pixel is in the lua canvas
static inline bool gui_check_boundary ( int x , int y ) {
return ! ( x < 0 | | x > = LUA_SCREEN_WIDTH | | y < 0 | | y > = LUA_SCREEN_HEIGHT ) ;
}
// write a pixel to gui_data (do not check boundaries for speedup)
static inline void gui_drawpixel_fast ( int x , int y , uint32 colour ) {
//gui_prepare();
blend32 ( ( uint32 * ) & gui_data [ ( y * LUA_SCREEN_WIDTH + x ) * 4 ] , colour ) ;
}
// write a pixel to gui_data (check boundaries)
static inline void gui_drawpixel_internal ( int x , int y , uint32 colour ) {
//gui_prepare();
if ( gui_check_boundary ( x , y ) )
gui_drawpixel_fast ( x , y , colour ) ;
}
// draw a line on gui_data (checks boundaries)
static void gui_drawline_internal ( int x1 , int y1 , int x2 , int y2 , bool lastPixel , uint32 colour ) {
//gui_prepare();
// Note: New version of Bresenham's Line Algorithm
// http://groups.google.co.jp/group/rec.games.roguelike.development/browse_thread/thread/345f4c42c3b25858/29e07a3af3a450e6?show_docid=29e07a3af3a450e6
int swappedx = 0 ;
int swappedy = 0 ;
int xtemp = x1 - x2 ;
int ytemp = y1 - y2 ;
if ( xtemp = = 0 & & ytemp = = 0 ) {
gui_drawpixel_internal ( x1 , y1 , colour ) ;
return ;
}
if ( xtemp < 0 ) {
xtemp = - xtemp ;
swappedx = 1 ;
}
if ( ytemp < 0 ) {
ytemp = - ytemp ;
swappedy = 1 ;
}
int delta_x = xtemp < < 1 ;
int delta_y = ytemp < < 1 ;
signed char ix = x1 > x2 ? 1 : - 1 ;
signed char iy = y1 > y2 ? 1 : - 1 ;
if ( lastPixel )
gui_drawpixel_internal ( x2 , y2 , colour ) ;
if ( delta_x > = delta_y ) {
int error = delta_y - ( delta_x > > 1 ) ;
while ( x2 ! = x1 ) {
if ( error = = 0 & & ! swappedx )
gui_drawpixel_internal ( x2 + ix , y2 , colour ) ;
if ( error > = 0 ) {
if ( error | | ( ix > 0 ) ) {
y2 + = iy ;
error - = delta_x ;
}
}
x2 + = ix ;
gui_drawpixel_internal ( x2 , y2 , colour ) ;
if ( error = = 0 & & swappedx )
gui_drawpixel_internal ( x2 , y2 + iy , colour ) ;
error + = delta_y ;
}
}
else {
int error = delta_x - ( delta_y > > 1 ) ;
while ( y2 ! = y1 ) {
if ( error = = 0 & & ! swappedy )
gui_drawpixel_internal ( x2 , y2 + iy , colour ) ;
if ( error > = 0 ) {
if ( error | | ( iy > 0 ) ) {
x2 + = ix ;
error - = delta_y ;
}
}
y2 + = iy ;
gui_drawpixel_internal ( x2 , y2 , colour ) ;
if ( error = = 0 & & swappedy )
gui_drawpixel_internal ( x2 + ix , y2 , colour ) ;
error + = delta_x ;
}
}
}
2019-01-03 12:38:33 +00:00
// draw a rect on gui_data
static void gui_drawbox_internal ( int x1 , int y1 , int x2 , int y2 , uint32 colour ) {
if ( x1 > x2 )
2021-11-19 02:55:31 +00:00
fceu : : swap < int > ( x1 , x2 ) ;
2019-01-03 12:38:33 +00:00
if ( y1 > y2 )
2021-11-19 02:55:31 +00:00
fceu : : swap < int > ( y1 , y2 ) ;
2019-01-03 12:38:33 +00:00
if ( x1 < 0 )
x1 = - 1 ;
if ( y1 < 0 )
y1 = - 1 ;
if ( x2 > = LUA_SCREEN_WIDTH )
x2 = LUA_SCREEN_WIDTH ;
if ( y2 > = LUA_SCREEN_HEIGHT )
y2 = LUA_SCREEN_HEIGHT ;
//gui_prepare();
gui_drawline_internal ( x1 , y1 , x2 , y1 , true , colour ) ;
gui_drawline_internal ( x1 , y2 , x2 , y2 , true , colour ) ;
gui_drawline_internal ( x1 , y1 , x1 , y2 , true , colour ) ;
gui_drawline_internal ( x2 , y1 , x2 , y2 , true , colour ) ;
}
2009-11-08 12:15:25 +00:00
// draw fill rect on gui_data
static void gui_fillbox_internal ( int x1 , int y1 , int x2 , int y2 , uint32 colour )
{
if ( x1 > x2 )
2009-11-08 17:13:27 +00:00
std : : swap ( x1 , x2 ) ;
2009-11-08 12:15:25 +00:00
if ( y1 > y2 )
2009-11-08 17:13:27 +00:00
std : : swap ( y1 , y2 ) ;
2009-11-08 12:15:25 +00:00
if ( x1 < 0 )
x1 = 0 ;
if ( y1 < 0 )
y1 = 0 ;
if ( x2 > = LUA_SCREEN_WIDTH )
x2 = LUA_SCREEN_WIDTH - 1 ;
if ( y2 > = LUA_SCREEN_HEIGHT )
y2 = LUA_SCREEN_HEIGHT - 1 ;
//gui_prepare();
int ix , iy ;
for ( iy = y1 ; iy < = y2 ; iy + + )
{
for ( ix = x1 ; ix < = x2 ; ix + + )
{
gui_drawpixel_fast ( ix , iy , colour ) ;
}
}
}
2009-10-12 07:32:31 +00:00
enum
{
GUI_COLOUR_CLEAR
/*
, GUI_COLOUR_WHITE , GUI_COLOUR_BLACK , GUI_COLOUR_GREY
, GUI_COLOUR_RED , GUI_COLOUR_GREEN , GUI_COLOUR_BLUE
*/
} ;
2008-11-02 19:10:53 +00:00
/**
* Returns an index approximating an RGB colour .
* TODO : This is easily improvable in terms of speed and probably
* quality of matches . ( gd overlay & transparency code call it a lot . )
* With effort we could also cheat and map indices 0x08 . . 0x3F
* ourselves .
*/
static uint8 gui_colour_rgb ( uint8 r , uint8 g , uint8 b ) {
static uint8 index_lookup [ 1 < < ( 3 + 3 + 3 ) ] ;
2009-10-12 07:32:31 +00:00
int k ;
2008-11-02 19:10:53 +00:00
if ( ! gui_saw_current_palette )
{
memset ( index_lookup , GUI_COLOUR_CLEAR , sizeof ( index_lookup ) ) ;
gui_saw_current_palette = TRUE ;
}
k = ( ( r & 0xE0 ) < < 1 ) | ( ( g & 0xE0 ) > > 2 ) | ( ( b & 0xE0 ) > > 5 ) ;
uint16 test , best = GUI_COLOUR_CLEAR ;
uint32 best_score = 0xffffffffu , test_score ;
if ( index_lookup [ k ] ! = GUI_COLOUR_CLEAR ) return index_lookup [ k ] ;
for ( test = 0 ; test < 0xff ; test + + )
{
uint8 tr , tg , tb ;
if ( test = = GUI_COLOUR_CLEAR ) continue ;
FCEUD_GetPalette ( test , & tr , & tg , & tb ) ;
test_score = abs ( r - tr ) * 66 +
abs ( g - tg ) * 129 +
abs ( b - tb ) * 25 ;
if ( test_score < best_score ) best_score = test_score , best = test ;
}
index_lookup [ k ] = best ;
return best ;
}
void FCEU_LuaUpdatePalette ( )
{
2009-10-12 07:32:31 +00:00
gui_saw_current_palette = FALSE ;
}
// Helper for a simple hex parser
static int hex2int ( lua_State * L , char c ) {
if ( c > = ' 0 ' & & c < = ' 9 ' )
return c - ' 0 ' ;
if ( c > = ' a ' & & c < = ' f ' )
return c - ' a ' + 10 ;
if ( c > = ' A ' & & c < = ' F ' )
return c - ' A ' + 10 ;
return luaL_error ( L , " invalid hex in colour " ) ;
2008-11-02 19:10:53 +00:00
}
2009-10-12 07:32:31 +00:00
static const struct ColorMapping
{
const char * name ;
2013-04-13 02:52:13 +00:00
unsigned int value ;
2009-10-12 07:32:31 +00:00
}
s_colorMapping [ ] =
{
{ " white " , 0xFFFFFFFF } ,
{ " black " , 0x000000FF } ,
{ " clear " , 0x00000000 } ,
{ " gray " , 0x7F7F7FFF } ,
{ " grey " , 0x7F7F7FFF } ,
{ " red " , 0xFF0000FF } ,
{ " orange " , 0xFF7F00FF } ,
{ " yellow " , 0xFFFF00FF } ,
{ " chartreuse " , 0x7FFF00FF } ,
{ " green " , 0x00FF00FF } ,
{ " teal " , 0x00FF7FFF } ,
{ " cyan " , 0x00FFFFFF } ,
{ " blue " , 0x0000FFFF } ,
{ " purple " , 0x7F00FFFF } ,
{ " magenta " , 0xFF00FFFF } ,
} ;
2008-11-02 19:10:53 +00:00
/**
* Converts an integer or a string on the stack at the given
2009-10-12 07:32:31 +00:00
* offset to a RGB32 colour . Several encodings are supported .
* The user may construct their own RGB value , given a simple colour name ,
* or an HTML - style " #09abcd " colour . 16 bit reduction doesn ' t occur at this time .
2010-05-15 05:03:53 +00:00
* NES palettes added with notation " P00 " to " P3F " . " P40 " to " P7F " denote LUA palettes .
2008-11-02 19:10:53 +00:00
*/
2009-10-12 07:32:31 +00:00
static inline bool str2colour ( uint32 * colour , lua_State * L , const char * str ) {
if ( str [ 0 ] = = ' # ' ) {
int color ;
sscanf ( str + 1 , " %X " , & color ) ;
int len = strlen ( str + 1 ) ;
2021-02-07 17:23:31 +00:00
int missing = std : : max < int > ( 0 , 8 - len ) ;
2009-10-12 07:32:31 +00:00
color < < = missing < < 2 ;
if ( missing > = 2 ) color | = 0xFF ;
* colour = color ;
return true ;
}
2010-05-15 05:03:53 +00:00
else if ( str [ 0 ] = = ' P ' ) {
uint8 palette ;
uint8 tr , tg , tb ;
if ( strlen ( str + 1 ) = = 2 ) {
palette = ( ( hex2int ( L , str [ 1 ] ) * 0x10 ) + hex2int ( L , str [ 2 ] ) ) ;
} else if ( strlen ( str + 1 ) = = 1 ) {
palette = ( hex2int ( L , str [ 1 ] ) ) ;
} else {
luaL_error ( L , " palettes are defined with P## hex notion " ) ;
return false ;
}
if ( palette > 0x7F ) {
luaL_error ( L , " palettes range from P00 to P7F " ) ;
return false ;
}
2012-10-21 16:40:04 +00:00
2010-05-15 05:03:53 +00:00
FCEUD_GetPalette ( palette + 0x80 , & tr , & tg , & tb ) ;
// Feeding it RGBA, because it will spit out the right value for me
* colour = LUA_BUILD_PIXEL ( tr , tg , tb , 0xFF ) ;
return true ;
}
2009-10-12 07:32:31 +00:00
else {
if ( ! strnicmp ( str , " rand " , 4 ) ) {
* colour = ( ( rand ( ) * 255 / RAND_MAX ) < < 8 ) | ( ( rand ( ) * 255 / RAND_MAX ) < < 16 ) | ( ( rand ( ) * 255 / RAND_MAX ) < < 24 ) | 0xFF ;
return true ;
}
for ( int i = 0 ; i < sizeof ( s_colorMapping ) / sizeof ( * s_colorMapping ) ; i + + ) {
if ( ! stricmp ( str , s_colorMapping [ i ] . name ) ) {
* colour = s_colorMapping [ i ] . value ;
return true ;
}
}
}
return false ;
}
static inline uint32 gui_getcolour_wrapped ( lua_State * L , int offset , bool hasDefaultValue , uint32 defaultColour ) {
2008-11-02 19:10:53 +00:00
switch ( lua_type ( L , offset ) ) {
case LUA_TSTRING :
{
const char * str = lua_tostring ( L , offset ) ;
2009-10-12 07:32:31 +00:00
uint32 colour ;
if ( str2colour ( & colour , L , str ) )
return colour ;
else {
if ( hasDefaultValue )
return defaultColour ;
else
return luaL_error ( L , " unknown colour %s " , str ) ;
}
2008-11-02 19:10:53 +00:00
}
case LUA_TNUMBER :
2009-10-12 07:32:31 +00:00
{
2016-08-21 20:12:49 +00:00
const char * str = lua_tostring ( L , offset ) ;
return ( uint32 ) strtod ( str , NULL ) ;
2009-10-12 07:32:31 +00:00
}
2009-11-08 12:15:25 +00:00
case LUA_TTABLE :
{
int color = 0xFF ;
lua_pushnil ( L ) ; // first key
int keyIndex = lua_gettop ( L ) ;
int valueIndex = keyIndex + 1 ;
bool first = true ;
while ( lua_next ( L , offset ) )
{
bool keyIsString = ( lua_type ( L , keyIndex ) = = LUA_TSTRING ) ;
bool keyIsNumber = ( lua_type ( L , keyIndex ) = = LUA_TNUMBER ) ;
int key = keyIsString ? tolower ( * lua_tostring ( L , keyIndex ) ) : ( keyIsNumber ? lua_tointeger ( L , keyIndex ) : 0 ) ;
int value = lua_tointeger ( L , valueIndex ) ;
if ( value < 0 ) value = 0 ;
if ( value > 255 ) value = 255 ;
switch ( key )
{
case 1 : case ' r ' : color | = value < < 24 ; break ;
case 2 : case ' g ' : color | = value < < 16 ; break ;
case 3 : case ' b ' : color | = value < < 8 ; break ;
case 4 : case ' a ' : color = ( color & ~ 0xFF ) | value ; break ;
}
lua_pop ( L , 1 ) ;
}
return color ;
} break ;
case LUA_TFUNCTION :
luaL_error ( L , " invalid colour " ) ; // NYI
return 0 ;
2008-11-02 19:10:53 +00:00
default :
2009-10-12 07:32:31 +00:00
if ( hasDefaultValue )
return defaultColour ;
else
return luaL_error ( L , " invalid colour " ) ;
2008-11-02 19:10:53 +00:00
}
2009-10-12 07:32:31 +00:00
}
static uint32 gui_getcolour ( lua_State * L , int offset ) {
uint32 colour ;
int a , r , g , b ;
2008-11-02 19:10:53 +00:00
2009-10-12 07:32:31 +00:00
colour = gui_getcolour_wrapped ( L , offset , false , 0 ) ;
a = ( ( colour & 0xff ) * transparencyModifier ) / 255 ;
if ( a > 255 ) a = 255 ;
b = ( colour > > 8 ) & 0xff ;
g = ( colour > > 16 ) & 0xff ;
r = ( colour > > 24 ) & 0xff ;
return LUA_BUILD_PIXEL ( a , r , g , b ) ;
2008-11-02 19:10:53 +00:00
}
2009-10-12 07:32:31 +00:00
static uint32 gui_optcolour ( lua_State * L , int offset , uint32 defaultColour ) {
uint32 colour ;
int a , r , g , b ;
uint8 defA , defB , defG , defR ;
2008-11-02 19:10:53 +00:00
2009-10-12 07:32:31 +00:00
LUA_DECOMPOSE_PIXEL ( defaultColour , defA , defR , defG , defB ) ;
defaultColour = ( defR < < 24 ) | ( defG < < 16 ) | ( defB < < 8 ) | defA ;
colour = gui_getcolour_wrapped ( L , offset , true , defaultColour ) ;
a = ( ( colour & 0xff ) * transparencyModifier ) / 255 ;
if ( a > 255 ) a = 255 ;
b = ( colour > > 8 ) & 0xff ;
g = ( colour > > 16 ) & 0xff ;
r = ( colour > > 24 ) & 0xff ;
return LUA_BUILD_PIXEL ( a , r , g , b ) ;
2008-11-02 19:10:53 +00:00
}
2009-10-11 05:09:17 +00:00
// gui.pixel(x,y,colour)
static int gui_pixel ( lua_State * L ) {
2008-11-02 19:10:53 +00:00
int x = luaL_checkinteger ( L , 1 ) ;
int y = luaL_checkinteger ( L , 2 ) ;
2009-10-12 07:32:31 +00:00
uint32 colour = gui_getcolour ( L , 3 ) ;
2008-11-02 19:10:53 +00:00
2009-10-12 07:32:31 +00:00
// if (!gui_check_boundary(x, y))
// luaL_error(L,"bad coordinates");
2008-11-02 19:10:53 +00:00
gui_prepare ( ) ;
2009-10-12 07:32:31 +00:00
gui_drawpixel_internal ( x , y , colour ) ;
2008-11-02 19:10:53 +00:00
return 0 ;
}
2012-10-21 16:40:04 +00:00
// Usage:
2010-05-10 07:20:55 +00:00
// local r,g,b,a = gui.getpixel(255, 223)
// Gets the LUA set pixel color
static int gui_getpixel ( lua_State * L ) {
2012-10-21 16:40:04 +00:00
2010-05-10 07:20:55 +00:00
int x = luaL_checkinteger ( L , 1 ) ;
int y = luaL_checkinteger ( L , 2 ) ;
int r , g , b , a ;
if ( ! gui_check_boundary ( x , y ) )
luaL_error ( L , " bad coordinates. Use 0-%d x 0-%d " , LUA_SCREEN_WIDTH - 1 , LUA_SCREEN_HEIGHT - 1 ) ;
if ( ! gui_data ) {
// Return all 0s, including for alpha.
// If alpha == 0, there was no color data for that spot
lua_pushinteger ( L , 0 ) ;
lua_pushinteger ( L , 0 ) ;
lua_pushinteger ( L , 0 ) ;
lua_pushinteger ( L , 0 ) ;
return 4 ;
}
2012-10-21 16:40:04 +00:00
2010-05-10 07:20:55 +00:00
//uint8 *dst = (uint8*) &gui_data[(y*LUA_SCREEN_WIDTH+x)*4];
2012-10-21 16:40:04 +00:00
2010-05-10 07:20:55 +00:00
//uint32 color = *(uint32*) &gui_data[(y*LUA_SCREEN_WIDTH+x)*4];
LUA_DECOMPOSE_PIXEL ( * ( uint32 * ) & gui_data [ ( y * LUA_SCREEN_WIDTH + x ) * 4 ] , a , r , g , b ) ;
lua_pushinteger ( L , r ) ;
lua_pushinteger ( L , g ) ;
lua_pushinteger ( L , b ) ;
lua_pushinteger ( L , a ) ;
return 4 ;
}
2012-10-21 16:40:04 +00:00
// Usage:
2010-05-10 07:20:55 +00:00
// local r,g,b,palette = gui.getpixel(255, 255)
// Gets the screen pixel color
// Palette will be 254 on error
static int emu_getscreenpixel ( lua_State * L ) {
2012-10-21 16:40:04 +00:00
2010-05-10 07:20:55 +00:00
int x = luaL_checkinteger ( L , 1 ) ;
int y = luaL_checkinteger ( L , 2 ) ;
2010-05-10 16:52:27 +00:00
bool getemuscreen = ( lua_toboolean ( L , 3 ) = = 1 ) ;
2010-05-10 07:20:55 +00:00
int r , g , b ;
int palette ;
if ( ( ( x < 0 ) | | ( x > 255 ) ) | | ( ( y < 0 ) | | ( y > 239 ) ) ) {
luaL_error ( L , " bad coordinates. Use 0-255 x 0-239 " ) ;
lua_pushinteger ( L , 0 ) ;
lua_pushinteger ( L , 0 ) ;
lua_pushinteger ( L , 0 ) ;
lua_pushinteger ( L , 254 ) ;
return 4 ;
}
2012-10-21 16:40:04 +00:00
2010-05-10 07:20:55 +00:00
if ( ! XBuf ) {
lua_pushinteger ( L , 0 ) ;
lua_pushinteger ( L , 0 ) ;
lua_pushinteger ( L , 0 ) ;
lua_pushinteger ( L , 254 ) ;
return 4 ;
}
2012-10-21 16:40:04 +00:00
2010-05-10 16:52:27 +00:00
uint32 pixelinfo = GetScreenPixel ( x , y , getemuscreen ) ;
2010-05-10 07:20:55 +00:00
LUA_DECOMPOSE_PIXEL ( pixelinfo , palette , r , g , b ) ;
2010-05-10 16:52:27 +00:00
palette = GetScreenPixelPalette ( x , y , getemuscreen ) ;
2010-05-10 07:20:55 +00:00
lua_pushinteger ( L , r ) ;
lua_pushinteger ( L , g ) ;
lua_pushinteger ( L , b ) ;
lua_pushinteger ( L , palette ) ;
return 4 ;
}
2009-11-08 12:15:25 +00:00
// gui.line(x1,y1,x2,y2,color,skipFirst)
2009-10-11 05:09:17 +00:00
static int gui_line ( lua_State * L ) {
2008-11-02 19:10:53 +00:00
int x1 , y1 , x2 , y2 ;
2009-11-08 12:15:25 +00:00
uint32 color ;
2008-11-02 19:10:53 +00:00
x1 = luaL_checkinteger ( L , 1 ) ;
y1 = luaL_checkinteger ( L , 2 ) ;
x2 = luaL_checkinteger ( L , 3 ) ;
y2 = luaL_checkinteger ( L , 4 ) ;
2009-11-08 12:15:25 +00:00
color = gui_optcolour ( L , 5 , LUA_BUILD_PIXEL ( 255 , 255 , 255 , 255 ) ) ;
int skipFirst = lua_toboolean ( L , 6 ) ;
2008-11-02 19:10:53 +00:00
gui_prepare ( ) ;
2009-11-08 12:15:25 +00:00
gui_drawline_internal ( x2 , y2 , x1 , y1 , ! skipFirst , color ) ;
2008-11-02 19:10:53 +00:00
return 0 ;
}
2009-11-08 12:15:25 +00:00
// gui.box(x1, y1, x2, y2, fillcolor, outlinecolor)
2009-10-11 05:09:17 +00:00
static int gui_box ( lua_State * L ) {
2008-11-02 19:10:53 +00:00
2009-11-08 12:15:25 +00:00
int x1 , y1 , x2 , y2 ;
uint32 fillcolor ;
uint32 outlinecolor ;
x1 = luaL_checkinteger ( L , 1 ) ;
y1 = luaL_checkinteger ( L , 2 ) ;
x2 = luaL_checkinteger ( L , 3 ) ;
y2 = luaL_checkinteger ( L , 4 ) ;
fillcolor = gui_optcolour ( L , 5 , LUA_BUILD_PIXEL ( 63 , 255 , 255 , 255 ) ) ;
outlinecolor = gui_optcolour ( L , 6 , LUA_BUILD_PIXEL ( 255 , LUA_PIXEL_R ( fillcolor ) , LUA_PIXEL_G ( fillcolor ) , LUA_PIXEL_B ( fillcolor ) ) ) ;
2012-10-21 16:40:04 +00:00
if ( x1 > x2 )
2009-11-08 17:13:27 +00:00
std : : swap ( x1 , x2 ) ;
2012-10-21 16:40:04 +00:00
if ( y1 > y2 )
2009-11-08 17:13:27 +00:00
std : : swap ( y1 , y2 ) ;
2009-11-08 12:15:25 +00:00
gui_prepare ( ) ;
gui_drawbox_internal ( x1 , y1 , x2 , y2 , outlinecolor ) ;
if ( ( x2 - x1 ) > = 2 & & ( y2 - y1 ) > = 2 )
gui_fillbox_internal ( x1 + 1 , y1 + 1 , x2 - 1 , y2 - 1 , fillcolor ) ;
return 0 ;
}
// (old) gui.box(x1, y1, x2, y2, color)
static int gui_box_old ( lua_State * L ) {
2008-11-02 19:10:53 +00:00
int x1 , y1 , x2 , y2 ;
2009-10-12 07:32:31 +00:00
uint32 colour ;
2008-11-02 19:10:53 +00:00
x1 = luaL_checkinteger ( L , 1 ) ;
y1 = luaL_checkinteger ( L , 2 ) ;
x2 = luaL_checkinteger ( L , 3 ) ;
y2 = luaL_checkinteger ( L , 4 ) ;
colour = gui_getcolour ( L , 5 ) ;
2009-10-12 07:32:31 +00:00
// if (!gui_check_boundary(x1, y1))
// luaL_error(L,"bad coordinates");
//
// if (!gui_check_boundary(x2, y2))
// luaL_error(L,"bad coordinates");
2008-11-02 19:10:53 +00:00
gui_prepare ( ) ;
2009-10-12 07:32:31 +00:00
gui_drawbox_internal ( x1 , y1 , x2 , y2 , colour ) ;
2008-11-02 19:10:53 +00:00
return 0 ;
}
2009-11-08 12:15:25 +00:00
static int gui_parsecolor ( lua_State * L )
{
int r , g , b , a ;
uint32 color = gui_getcolour ( L , 1 ) ;
LUA_DECOMPOSE_PIXEL ( color , a , r , g , b ) ;
lua_pushinteger ( L , r ) ;
lua_pushinteger ( L , g ) ;
lua_pushinteger ( L , b ) ;
lua_pushinteger ( L , a ) ;
return 4 ;
}
2008-11-02 19:10:53 +00:00
2010-06-07 16:38:47 +00:00
// gui.savescreenshotas()
//
// Causes FCEUX to write a screenshot to a file based on a received filename, caution: will overwrite existing screenshot files
//
// Unconditionally retrns 1; any failure in taking a screenshot would be reported on-screen
2012-10-21 16:40:04 +00:00
// from the function ReallySnap().
2010-06-07 16:38:47 +00:00
static int gui_savescreenshotas ( lua_State * L ) {
const char * name = NULL ;
size_t l ;
name = luaL_checklstring ( L , 1 , & l ) ;
lua_pushstring ( L , name ) ;
if ( name )
FCEUI_SetSnapshotAsName ( name ) ;
else
luaL_error ( L , " gui.savesnapshotas must have a string parameter " ) ;
FCEUI_SaveSnapshotAs ( ) ;
return 1 ;
}
2010-02-02 22:12:07 +00:00
// gui.savescreenshot()
//
2012-10-21 16:40:04 +00:00
// Causes FCEUX to write a screenshot to a file as if the user pressed the associated hotkey.
2010-02-02 22:12:07 +00:00
//
// Unconditionally retrns 1; any failure in taking a screenshot would be reported on-screen
2012-10-21 16:40:04 +00:00
// from the function ReallySnap().
2010-02-02 22:12:07 +00:00
static int gui_savescreenshot ( lua_State * L ) {
FCEUI_SaveSnapshot ( ) ;
return 1 ;
}
2018-11-16 02:55:08 +00:00
// gui.gdscreenshot(getemuscreen)
2009-10-12 07:32:31 +00:00
//
// Returns a screen shot as a string in gd's v1 file format.
// This allows us to make screen shots available without gd installed locally.
// Users can also just grab pixels via substring selection.
//
// I think... Does lua support grabbing byte values from a string? // yes, string.byte(str,offset)
// Well, either way, just install gd and do what you like with it.
// It really is easier that way.
// example: gd.createFromGdStr(gui.gdscreenshot()):png("outputimage.png")
2008-11-02 19:10:53 +00:00
static int gui_gdscreenshot ( lua_State * L ) {
2018-11-16 02:55:08 +00:00
bool getemuscreen = ( lua_toboolean ( L , 1 ) = = 1 ) ;
2008-11-02 19:10:53 +00:00
2009-10-12 07:32:31 +00:00
int width = LUA_SCREEN_WIDTH ;
int height = LUA_SCREEN_HEIGHT ;
int size = 11 + width * height * 4 ;
char * str = new char [ size + 1 ] ;
str [ size ] = 0 ;
unsigned char * ptr = ( unsigned char * ) str ;
// GD format header for truecolor image (11 bytes)
* ptr + + = ( 65534 > > 8 ) & 0xFF ;
* ptr + + = ( 65534 ) & 0xFF ;
* ptr + + = ( width > > 8 ) & 0xFF ;
* ptr + + = ( width ) & 0xFF ;
2015-06-23 21:04:31 +00:00
* ptr + + = ( height > > 8 ) & 0xFF ;
* ptr + + = ( height ) & 0xFF ;
2009-10-12 07:32:31 +00:00
* ptr + + = 1 ;
* ptr + + = 255 ;
* ptr + + = 255 ;
* ptr + + = 255 ;
* ptr + + = 255 ;
2018-11-16 02:55:08 +00:00
uint8 * scrBuf = getemuscreen ? XBackBuf : XBuf ;
2009-10-12 07:32:31 +00:00
for ( int y = 0 ; y < height ; y + + ) {
for ( int x = 0 ; x < width ; x + + ) {
2018-11-16 02:55:08 +00:00
uint8 index = scrBuf [ ( y ) * 256 + x ] ;
2008-11-02 19:10:53 +00:00
// Write A,R,G,B (alpha=0 for us):
2009-10-12 07:32:31 +00:00
* ptr = 0 ;
FCEUD_GetPalette ( index , ptr + 1 , ptr + 2 , ptr + 3 ) ;
ptr + = 4 ;
2008-11-02 19:10:53 +00:00
}
}
2009-10-12 07:32:31 +00:00
lua_pushlstring ( L , str , size ) ;
delete [ ] str ;
2008-11-02 19:10:53 +00:00
return 1 ;
}
2009-10-12 07:32:31 +00:00
// gui.opacity(number alphaValue)
// sets the transparency of subsequent draw calls
// 0.0 is completely transparent, 1.0 is completely opaque
// non-integer values are supported and meaningful, as are values greater than 1.0
// it is not necessary to use this function to get transparency (or the less-recommended gui.transparency() either),
// because you can provide an alpha value in the color argument of each draw call.
// however, it can be convenient to be able to globally modify the drawing transparency
static int gui_setopacity ( lua_State * L ) {
double opacF = luaL_checknumber ( L , 1 ) ;
transparencyModifier = ( int ) ( opacF * 255 ) ;
if ( transparencyModifier < 0 )
transparencyModifier = 0 ;
return 0 ;
}
2008-11-02 19:10:53 +00:00
// gui.transparency(int strength)
//
2012-10-21 16:40:04 +00:00
// 0 = solid,
2008-11-02 19:10:53 +00:00
static int gui_transparency ( lua_State * L ) {
2009-10-12 07:32:31 +00:00
double trans = luaL_checknumber ( L , 1 ) ;
transparencyModifier = ( int ) ( ( 4.0 - trans ) / 4.0 * 255 ) ;
if ( transparencyModifier < 0 )
transparencyModifier = 0 ;
2008-11-02 19:10:53 +00:00
return 0 ;
}
2009-10-12 07:32:31 +00:00
static const uint32 Small_Font_Data [ ] =
{
2012-10-21 16:40:04 +00:00
0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , // 32
2009-10-12 07:32:31 +00:00
0x00000000 , 0x00000300 , 0x00000400 , 0x00000500 , 0x00000000 , 0x00000700 , 0x00000000 , // 33 !
0x00000000 , 0x00040002 , 0x00050003 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , // 34 "
0x00000000 , 0x00040002 , 0x00050403 , 0x00060004 , 0x00070605 , 0x00080006 , 0x00000000 , // 35 #
0x00000000 , 0x00040300 , 0x00000403 , 0x00000500 , 0x00070600 , 0x00000706 , 0x00000000 , // 36 $
0x00000000 , 0x00000002 , 0x00050000 , 0x00000500 , 0x00000005 , 0x00080000 , 0x00000000 , // 37 %
0x00000000 , 0x00000300 , 0x00050003 , 0x00000500 , 0x00070005 , 0x00080700 , 0x00000000 , // 38 &
0x00000000 , 0x00000300 , 0x00000400 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , // 39 '
0x00000000 , 0x00000300 , 0x00000003 , 0x00000004 , 0x00000005 , 0x00000700 , 0x00000000 , // 40 (
0x00000000 , 0x00000300 , 0x00050000 , 0x00060000 , 0x00070000 , 0x00000700 , 0x00000000 , // 41 )
0x00000000 , 0x00000000 , 0x00000400 , 0x00060504 , 0x00000600 , 0x00080006 , 0x00000000 , // 42 *
0x00000000 , 0x00000000 , 0x00000400 , 0x00060504 , 0x00000600 , 0x00000000 , 0x00000000 , // 43 +
0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000600 , 0x00000700 , 0x00000007 , // 44 ,
0x00000000 , 0x00000000 , 0x00000000 , 0x00060504 , 0x00000000 , 0x00000000 , 0x00000000 , // 45 -
0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000700 , 0x00000000 , // 46 .
0x00030000 , 0x00040000 , 0x00000400 , 0x00000500 , 0x00000005 , 0x00000006 , 0x00000000 , // 47 /
0x00000000 , 0x00000300 , 0x00050003 , 0x00060004 , 0x00070005 , 0x00000700 , 0x00000000 , // 48 0
0x00000000 , 0x00000300 , 0x00000403 , 0x00000500 , 0x00000600 , 0x00000700 , 0x00000000 , // 49 1
0x00000000 , 0x00000302 , 0x00050000 , 0x00000500 , 0x00000005 , 0x00080706 , 0x00000000 , // 50 2
0x00000000 , 0x00000302 , 0x00050000 , 0x00000504 , 0x00070000 , 0x00000706 , 0x00000000 , // 51 3
0x00000000 , 0x00000300 , 0x00000003 , 0x00060004 , 0x00070605 , 0x00080000 , 0x00000000 , // 52 4
0x00000000 , 0x00040302 , 0x00000003 , 0x00000504 , 0x00070000 , 0x00000706 , 0x00000000 , // 53 5
0x00000000 , 0x00000300 , 0x00000003 , 0x00000504 , 0x00070005 , 0x00000700 , 0x00000000 , // 54 6
0x00000000 , 0x00040302 , 0x00050000 , 0x00000500 , 0x00000600 , 0x00000700 , 0x00000000 , // 55 7
0x00000000 , 0x00000300 , 0x00050003 , 0x00000500 , 0x00070005 , 0x00000700 , 0x00000000 , // 56 8
0x00000000 , 0x00000300 , 0x00050003 , 0x00060500 , 0x00070000 , 0x00000700 , 0x00000000 , // 57 9
0x00000000 , 0x00000000 , 0x00000400 , 0x00000000 , 0x00000000 , 0x00000700 , 0x00000000 , // 58 :
0x00000000 , 0x00000000 , 0x00000000 , 0x00000500 , 0x00000000 , 0x00000700 , 0x00000007 , // 59 ;
0x00000000 , 0x00040000 , 0x00000400 , 0x00000004 , 0x00000600 , 0x00080000 , 0x00000000 , // 60 <
0x00000000 , 0x00000000 , 0x00050403 , 0x00000000 , 0x00070605 , 0x00000000 , 0x00000000 , // 61 =
0x00000000 , 0x00000002 , 0x00000400 , 0x00060000 , 0x00000600 , 0x00000006 , 0x00000000 , // 62 >
0x00000000 , 0x00000302 , 0x00050000 , 0x00000500 , 0x00000000 , 0x00000700 , 0x00000000 , // 63 ?
0x00000000 , 0x00000300 , 0x00050400 , 0x00060004 , 0x00070600 , 0x00000000 , 0x00000000 , // 64 @
0x00000000 , 0x00000300 , 0x00050003 , 0x00060504 , 0x00070005 , 0x00080006 , 0x00000000 , // 65 A
0x00000000 , 0x00000302 , 0x00050003 , 0x00000504 , 0x00070005 , 0x00000706 , 0x00000000 , // 66 B
0x00000000 , 0x00040300 , 0x00000003 , 0x00000004 , 0x00000005 , 0x00080700 , 0x00000000 , // 67 C
0x00000000 , 0x00000302 , 0x00050003 , 0x00060004 , 0x00070005 , 0x00000706 , 0x00000000 , // 68 D
0x00000000 , 0x00040302 , 0x00000003 , 0x00000504 , 0x00000005 , 0x00080706 , 0x00000000 , // 69 E
0x00000000 , 0x00040302 , 0x00000003 , 0x00000504 , 0x00000005 , 0x00000006 , 0x00000000 , // 70 F
0x00000000 , 0x00040300 , 0x00000003 , 0x00060004 , 0x00070005 , 0x00080700 , 0x00000000 , // 71 G
0x00000000 , 0x00040002 , 0x00050003 , 0x00060504 , 0x00070005 , 0x00080006 , 0x00000000 , // 72 H
0x00000000 , 0x00000300 , 0x00000400 , 0x00000500 , 0x00000600 , 0x00000700 , 0x00000000 , // 73 I
0x00000000 , 0x00040000 , 0x00050000 , 0x00060000 , 0x00070005 , 0x00000700 , 0x00000000 , // 74 J
0x00000000 , 0x00040002 , 0x00050003 , 0x00000504 , 0x00070005 , 0x00080006 , 0x00000000 , // 75 K
0x00000000 , 0x00000002 , 0x00000003 , 0x00000004 , 0x00000005 , 0x00080706 , 0x00000000 , // 76 l
0x00000000 , 0x00040002 , 0x00050403 , 0x00060004 , 0x00070005 , 0x00080006 , 0x00000000 , // 77 M
0x00000000 , 0x00000302 , 0x00050003 , 0x00060004 , 0x00070005 , 0x00080006 , 0x00000000 , // 78 N
0x00000000 , 0x00040302 , 0x00050003 , 0x00060004 , 0x00070005 , 0x00080706 , 0x00000000 , // 79 O
0x00000000 , 0x00000302 , 0x00050003 , 0x00000504 , 0x00000005 , 0x00000006 , 0x00000000 , // 80 P
0x00000000 , 0x00040302 , 0x00050003 , 0x00060004 , 0x00070005 , 0x00080706 , 0x00090000 , // 81 Q
0x00000000 , 0x00000302 , 0x00050003 , 0x00000504 , 0x00070005 , 0x00080006 , 0x00000000 , // 82 R
0x00000000 , 0x00040300 , 0x00000003 , 0x00000500 , 0x00070000 , 0x00000706 , 0x00000000 , // 83 S
0x00000000 , 0x00040302 , 0x00000400 , 0x00000500 , 0x00000600 , 0x00000700 , 0x00000000 , // 84 T
0x00000000 , 0x00040002 , 0x00050003 , 0x00060004 , 0x00070005 , 0x00080706 , 0x00000000 , // 85 U
0x00000000 , 0x00040002 , 0x00050003 , 0x00060004 , 0x00000600 , 0x00000700 , 0x00000000 , // 86 V
0x00000000 , 0x00040002 , 0x00050003 , 0x00060004 , 0x00070605 , 0x00080006 , 0x00000000 , // 87 W
0x00000000 , 0x00040002 , 0x00050003 , 0x00000500 , 0x00070005 , 0x00080006 , 0x00000000 , // 88 X
0x00000000 , 0x00040002 , 0x00050003 , 0x00000500 , 0x00000600 , 0x00000700 , 0x00000000 , // 89 Y
0x00000000 , 0x00040302 , 0x00050000 , 0x00000500 , 0x00000005 , 0x00080706 , 0x00000000 , // 90 Z
0x00000000 , 0x00040300 , 0x00000400 , 0x00000500 , 0x00000600 , 0x00080700 , 0x00000000 , // 91 [
0x00000000 , 0x00000002 , 0x00000400 , 0x00000500 , 0x00070000 , 0x00080000 , 0x00000000 , // 92 '\'
0x00000000 , 0x00000302 , 0x00000400 , 0x00000500 , 0x00000600 , 0x00000706 , 0x00000000 , // 93 ]
0x00000000 , 0x00000300 , 0x00050003 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , // 94 ^
0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00080706 , 0x00000000 , // 95 _
0x00000000 , 0x00000002 , 0x00000400 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , // 96 `
0x00000000 , 0x00000000 , 0x00050400 , 0x00060004 , 0x00070005 , 0x00080700 , 0x00000000 , // 97 a
0x00000000 , 0x00000002 , 0x00000003 , 0x00000504 , 0x00070005 , 0x00000706 , 0x00000000 , // 98 b
0x00000000 , 0x00000000 , 0x00050400 , 0x00000004 , 0x00000005 , 0x00080700 , 0x00000000 , // 99 c
0x00000000 , 0x00040000 , 0x00050000 , 0x00060500 , 0x00070005 , 0x00080700 , 0x00000000 , // 100 d
0x00000000 , 0x00000000 , 0x00050400 , 0x00060504 , 0x00000005 , 0x00080700 , 0x00000000 , // 101 e
0x00000000 , 0x00040300 , 0x00000003 , 0x00000504 , 0x00000005 , 0x00000006 , 0x00000000 , // 102 f
0x00000000 , 0x00000000 , 0x00050400 , 0x00060004 , 0x00070600 , 0x00080000 , 0x00000807 , // 103 g
0x00000000 , 0x00000002 , 0x00000003 , 0x00000504 , 0x00070005 , 0x00080006 , 0x00000000 , // 104 h
0x00000000 , 0x00000300 , 0x00000000 , 0x00000500 , 0x00000600 , 0x00000700 , 0x00000000 , // 105 i
0x00000000 , 0x00000300 , 0x00000000 , 0x00000500 , 0x00000600 , 0x00000700 , 0x00000007 , // 106 j
0x00000000 , 0x00000002 , 0x00000003 , 0x00060004 , 0x00000605 , 0x00080006 , 0x00000000 , // 107 k
0x00000000 , 0x00000300 , 0x00000400 , 0x00000500 , 0x00000600 , 0x00080000 , 0x00000000 , // 108 l
0x00000000 , 0x00000000 , 0x00050003 , 0x00060504 , 0x00070005 , 0x00080006 , 0x00000000 , // 109 m
0x00000000 , 0x00000000 , 0x00000403 , 0x00060004 , 0x00070005 , 0x00080006 , 0x00000000 , // 110 n
0x00000000 , 0x00000000 , 0x00000400 , 0x00060004 , 0x00070005 , 0x00000700 , 0x00000000 , // 111 o
0x00000000 , 0x00000000 , 0x00000400 , 0x00060004 , 0x00000605 , 0x00000006 , 0x00000007 , // 112 p
0x00000000 , 0x00000000 , 0x00000400 , 0x00060004 , 0x00070600 , 0x00080000 , 0x00090000 , // 113 q
0x00000000 , 0x00000000 , 0x00050003 , 0x00000504 , 0x00000005 , 0x00000006 , 0x00000000 , // 114 r
0x00000000 , 0x00000000 , 0x00050400 , 0x00000004 , 0x00070600 , 0x00000706 , 0x00000000 , // 115 s
0x00000000 , 0x00000300 , 0x00050403 , 0x00000500 , 0x00000600 , 0x00080000 , 0x00000000 , // 116 t
0x00000000 , 0x00000000 , 0x00050003 , 0x00060004 , 0x00070005 , 0x00080700 , 0x00000000 , // 117 u
0x00000000 , 0x00000000 , 0x00050003 , 0x00060004 , 0x00070005 , 0x00000700 , 0x00000000 , // 118 v
0x00000000 , 0x00000000 , 0x00050003 , 0x00060004 , 0x00070605 , 0x00080006 , 0x00000000 , // 119 w
0x00000000 , 0x00000000 , 0x00050003 , 0x00000500 , 0x00070005 , 0x00080006 , 0x00000000 , // 120 x
0x00000000 , 0x00000000 , 0x00050003 , 0x00060004 , 0x00000600 , 0x00000700 , 0x00000007 , // 121 y
0x00000000 , 0x00000000 , 0x00050403 , 0x00000500 , 0x00000005 , 0x00080706 , 0x00000000 , // 122 z
0x00000000 , 0x00040300 , 0x00000400 , 0x00000504 , 0x00000600 , 0x00080700 , 0x00000000 , // 123 {
0x00000000 , 0x00000300 , 0x00000400 , 0x00000000 , 0x00000600 , 0x00000700 , 0x00000000 , // 124 |
0x00000000 , 0x00000302 , 0x00000400 , 0x00060500 , 0x00000600 , 0x00000706 , 0x00000000 , // 125 }
0x00000000 , 0x00000302 , 0x00050000 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , // 126 ~
0x00000000 , 0x00000000 , 0x00000400 , 0x00060004 , 0x00070605 , 0x00000000 , 0x00000000 , // 127
0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 ,
} ;
static void PutTextInternal ( const char * str , int len , short x , short y , int color , int backcolor )
{
int Opac = ( color > > 24 ) & 0xFF ;
int backOpac = ( backcolor > > 24 ) & 0xFF ;
int origX = x ;
if ( ! Opac & & ! backOpac )
return ;
while ( * str & & len & & y < LUA_SCREEN_HEIGHT )
{
int c = * str + + ;
while ( x > LUA_SCREEN_WIDTH & & c ! = ' \n ' ) {
c = * str ;
if ( c = = ' \0 ' )
break ;
str + + ;
}
if ( c = = ' \n ' )
{
x = origX ;
y + = 8 ;
continue ;
}
else if ( c = = ' \t ' ) // just in case
{
const int tabSpace = 8 ;
x + = ( tabSpace - ( ( ( x - origX ) / 4 ) % tabSpace ) ) * 4 ;
continue ;
}
if ( ( unsigned int ) ( c - 32 ) > = 96 )
continue ;
const unsigned char * Cur_Glyph = ( const unsigned char * ) & Small_Font_Data + ( c - 32 ) * 7 * 4 ;
for ( int y2 = 0 ; y2 < 8 ; y2 + + )
{
unsigned int glyphLine = * ( ( unsigned int * ) Cur_Glyph + y2 ) ;
for ( int x2 = - 1 ; x2 < 4 ; x2 + + )
{
int shift = x2 < < 3 ;
int mask = 0xFF < < shift ;
int intensity = ( glyphLine & mask ) > > shift ;
if ( intensity & & x2 > = 0 & & y2 < 7 )
{
//int xdraw = std::max(0,std::min(LUA_SCREEN_WIDTH - 1,x+x2));
//int ydraw = std::max(0,std::min(LUA_SCREEN_HEIGHT - 1,y+y2));
//gui_drawpixel_fast(xdraw, ydraw, color);
gui_drawpixel_internal ( x + x2 , y + y2 , color ) ;
}
else if ( backOpac )
{
2021-02-07 17:23:31 +00:00
for ( int y3 = std : : max < int > ( 0 , y2 - 1 ) ; y3 < = std : : min < int > ( 6 , y2 + 1 ) ; y3 + + )
2009-10-12 07:32:31 +00:00
{
unsigned int glyphLine = * ( ( unsigned int * ) Cur_Glyph + y3 ) ;
2021-02-07 17:23:31 +00:00
for ( int x3 = std : : max < int > ( 0 , x2 - 1 ) ; x3 < = std : : min < int > ( 3 , x2 + 1 ) ; x3 + + )
2009-10-12 07:32:31 +00:00
{
int shift = x3 < < 3 ;
int mask = 0xFF < < shift ;
intensity | = ( glyphLine & mask ) > > shift ;
if ( intensity )
goto draw_outline ; // speedup?
}
}
draw_outline :
if ( intensity )
{
//int xdraw = std::max(0,std::min(LUA_SCREEN_WIDTH - 1,x+x2));
//int ydraw = std::max(0,std::min(LUA_SCREEN_HEIGHT - 1,y+y2));
//gui_drawpixel_fast(xdraw, ydraw, backcolor);
gui_drawpixel_internal ( x + x2 , y + y2 , backcolor ) ;
}
}
}
}
x + = 4 ;
len - - ;
}
}
static int strlinelen ( const char * string )
{
const char * s = string ;
while ( * s & & * s ! = ' \n ' )
s + + ;
if ( * s )
s + + ;
return s - string ;
}
static void LuaDisplayString ( const char * string , int y , int x , uint32 color , uint32 outlineColor )
{
if ( ! string )
return ;
gui_prepare ( ) ;
PutTextInternal ( string , strlen ( string ) , x , y , color , outlineColor ) ;
/*
const char * ptr = string ;
while ( * ptr & & y < LUA_SCREEN_HEIGHT )
{
int len = strlinelen ( ptr ) ;
int skip = 0 ;
if ( len < 1 ) len = 1 ;
// break up the line if it's too long to display otherwise
if ( len > 63 )
{
len = 63 ;
const char * ptr2 = ptr + len - 1 ;
for ( int j = len - 1 ; j ; j - - , ptr2 - - )
{
if ( * ptr2 = = ' ' | | * ptr2 = = ' \t ' )
{
len = j ;
skip = 1 ;
break ;
}
}
}
int xl = 0 ;
int yl = 0 ;
int xh = ( LUA_SCREEN_WIDTH - 1 - 1 ) - 4 * len ;
int yh = LUA_SCREEN_HEIGHT - 1 ;
int x2 = std : : min ( std : : max ( x , xl ) , xh ) ;
int y2 = std : : min ( std : : max ( y , yl ) , yh ) ;
PutTextInternal ( ptr , len , x2 , y2 , color , outlineColor ) ;
ptr + = len + skip ;
y + = 8 ;
}
*/
}
static uint8 FCEUFont [ 792 ] =
{
2013-01-15 12:53:04 +00:00
6 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 0x20 - Spacebar
2009-10-12 07:32:31 +00:00
3 , 64 , 64 , 64 , 64 , 64 , 0 , 64 ,
5 , 80 , 80 , 80 , 0 , 0 , 0 , 0 ,
6 , 80 , 80 , 248 , 80 , 248 , 80 , 80 ,
6 , 32 , 120 , 160 , 112 , 40 , 240 , 32 ,
6 , 64 , 168 , 80 , 32 , 80 , 168 , 16 ,
6 , 96 , 144 , 160 , 64 , 168 , 144 , 104 ,
3 , 64 , 64 , 0 , 0 , 0 , 0 , 0 ,
4 , 32 , 64 , 64 , 64 , 64 , 64 , 32 ,
4 , 64 , 32 , 32 , 32 , 32 , 32 , 64 ,
6 , 0 , 80 , 32 , 248 , 32 , 80 , 0 ,
6 , 0 , 32 , 32 , 248 , 32 , 32 , 0 ,
3 , 0 , 0 , 0 , 0 , 0 , 64 , 128 ,
5 , 0 , 0 , 0 , 240 , 0 , 0 , 0 ,
3 , 0 , 0 , 0 , 0 , 0 , 0 , 64 ,
5 , 16 , 16 , 32 , 32 , 32 , 64 , 64 ,
2013-01-15 12:53:04 +00:00
6 , 112 , 136 , 136 , 136 , 136 , 136 , 112 , // 0x30 - 0
2009-10-12 07:32:31 +00:00
6 , 32 , 96 , 32 , 32 , 32 , 32 , 32 ,
6 , 112 , 136 , 8 , 48 , 64 , 128 , 248 ,
6 , 112 , 136 , 8 , 48 , 8 , 136 , 112 ,
6 , 16 , 48 , 80 , 144 , 248 , 16 , 16 ,
6 , 248 , 128 , 128 , 240 , 8 , 8 , 240 ,
6 , 48 , 64 , 128 , 240 , 136 , 136 , 112 ,
6 , 248 , 8 , 16 , 16 , 32 , 32 , 32 ,
6 , 112 , 136 , 136 , 112 , 136 , 136 , 112 ,
6 , 112 , 136 , 136 , 120 , 8 , 16 , 96 ,
3 , 0 , 0 , 64 , 0 , 0 , 64 , 0 ,
3 , 0 , 0 , 64 , 0 , 0 , 64 , 128 ,
4 , 0 , 32 , 64 , 128 , 64 , 32 , 0 ,
5 , 0 , 0 , 240 , 0 , 240 , 0 , 0 ,
4 , 0 , 128 , 64 , 32 , 64 , 128 , 0 ,
2013-01-15 12:53:04 +00:00
6 , 112 , 136 , 8 , 16 , 32 , 0 , 32 , // 0x3F - ?
6 , 112 , 136 , 136 , 184 , 176 , 128 , 112 , // 0x40 - @
6 , 112 , 136 , 136 , 248 , 136 , 136 , 136 , // 0x41 - A
2009-10-12 07:32:31 +00:00
6 , 240 , 136 , 136 , 240 , 136 , 136 , 240 ,
6 , 112 , 136 , 128 , 128 , 128 , 136 , 112 ,
6 , 224 , 144 , 136 , 136 , 136 , 144 , 224 ,
6 , 248 , 128 , 128 , 240 , 128 , 128 , 248 ,
6 , 248 , 128 , 128 , 240 , 128 , 128 , 128 ,
6 , 112 , 136 , 128 , 184 , 136 , 136 , 120 ,
6 , 136 , 136 , 136 , 248 , 136 , 136 , 136 ,
4 , 224 , 64 , 64 , 64 , 64 , 64 , 224 ,
6 , 8 , 8 , 8 , 8 , 8 , 136 , 112 ,
6 , 136 , 144 , 160 , 192 , 160 , 144 , 136 ,
6 , 128 , 128 , 128 , 128 , 128 , 128 , 248 ,
6 , 136 , 216 , 168 , 168 , 136 , 136 , 136 ,
6 , 136 , 136 , 200 , 168 , 152 , 136 , 136 ,
7 , 48 , 72 , 132 , 132 , 132 , 72 , 48 ,
6 , 240 , 136 , 136 , 240 , 128 , 128 , 128 ,
6 , 112 , 136 , 136 , 136 , 168 , 144 , 104 ,
6 , 240 , 136 , 136 , 240 , 144 , 136 , 136 ,
6 , 112 , 136 , 128 , 112 , 8 , 136 , 112 ,
6 , 248 , 32 , 32 , 32 , 32 , 32 , 32 ,
6 , 136 , 136 , 136 , 136 , 136 , 136 , 112 ,
6 , 136 , 136 , 136 , 80 , 80 , 32 , 32 ,
6 , 136 , 136 , 136 , 136 , 168 , 168 , 80 ,
6 , 136 , 136 , 80 , 32 , 80 , 136 , 136 ,
6 , 136 , 136 , 80 , 32 , 32 , 32 , 32 ,
6 , 248 , 8 , 16 , 32 , 64 , 128 , 248 ,
3 , 192 , 128 , 128 , 128 , 128 , 128 , 192 ,
5 , 64 , 64 , 32 , 32 , 32 , 16 , 16 ,
3 , 192 , 64 , 64 , 64 , 64 , 64 , 192 ,
4 , 64 , 160 , 0 , 0 , 0 , 0 , 0 ,
6 , 0 , 0 , 0 , 0 , 0 , 0 , 248 ,
3 , 128 , 64 , 0 , 0 , 0 , 0 , 0 ,
2013-01-15 12:53:04 +00:00
5 , 0 , 0 , 96 , 16 , 112 , 144 , 112 , // 0x61 - a
2009-10-12 07:32:31 +00:00
5 , 128 , 128 , 224 , 144 , 144 , 144 , 224 ,
5 , 0 , 0 , 112 , 128 , 128 , 128 , 112 ,
5 , 16 , 16 , 112 , 144 , 144 , 144 , 112 ,
5 , 0 , 0 , 96 , 144 , 240 , 128 , 112 ,
5 , 48 , 64 , 224 , 64 , 64 , 64 , 64 ,
5 , 0 , 112 , 144 , 144 , 112 , 16 , 224 ,
5 , 128 , 128 , 224 , 144 , 144 , 144 , 144 ,
2 , 128 , 0 , 128 , 128 , 128 , 128 , 128 ,
4 , 32 , 0 , 32 , 32 , 32 , 32 , 192 ,
5 , 128 , 128 , 144 , 160 , 192 , 160 , 144 ,
2 , 128 , 128 , 128 , 128 , 128 , 128 , 128 ,
6 , 0 , 0 , 208 , 168 , 168 , 168 , 168 ,
5 , 0 , 0 , 224 , 144 , 144 , 144 , 144 ,
5 , 0 , 0 , 96 , 144 , 144 , 144 , 96 ,
5 , 0 , 0 , 224 , 144 , 144 , 224 , 128 ,
5 , 0 , 0 , 112 , 144 , 144 , 112 , 16 ,
5 , 0 , 0 , 176 , 192 , 128 , 128 , 128 ,
5 , 0 , 0 , 112 , 128 , 96 , 16 , 224 ,
4 , 64 , 64 , 224 , 64 , 64 , 64 , 32 ,
5 , 0 , 0 , 144 , 144 , 144 , 144 , 112 ,
5 , 0 , 0 , 144 , 144 , 144 , 160 , 192 ,
6 , 0 , 0 , 136 , 136 , 168 , 168 , 80 ,
5 , 0 , 0 , 144 , 144 , 96 , 144 , 144 ,
5 , 0 , 144 , 144 , 144 , 112 , 16 , 96 ,
5 , 0 , 0 , 240 , 32 , 64 , 128 , 240 ,
4 , 32 , 64 , 64 , 128 , 64 , 64 , 32 ,
3 , 64 , 64 , 64 , 64 , 64 , 64 , 64 ,
4 , 128 , 64 , 64 , 32 , 64 , 64 , 128 ,
6 , 0 , 104 , 176 , 0 , 0 , 0 , 0
} ;
static int FixJoedChar ( uint8 ch )
{
int c = ch ; c - = 32 ;
return ( c < 0 | | c > 98 ) ? 0 : c ;
}
static int JoedCharWidth ( uint8 ch )
{
return FCEUFont [ FixJoedChar ( ch ) * 8 ] ;
}
2010-06-06 22:50:46 +00:00
void LuaDrawTextTransWH ( const char * str , size_t l , int & x , int y , uint32 color , uint32 backcolor )
2009-10-12 07:32:31 +00:00
{
int Opac = ( color > > 24 ) & 0xFF ;
int backOpac = ( backcolor > > 24 ) & 0xFF ;
int origX = x ;
if ( ! Opac & & ! backOpac )
return ;
2010-04-08 00:46:56 +00:00
size_t len = l ;
2021-02-07 17:23:31 +00:00
int defaultAlpha = std : : max < int > ( 0 , std : : min < int > ( transparencyModifier , 255 ) ) ;
2010-04-08 00:46:56 +00:00
int diffx ;
2021-02-07 17:23:31 +00:00
int diffy = std : : max < int > ( 0 , std : : min < int > ( 7 , LUA_SCREEN_HEIGHT - y ) ) ;
2009-10-12 07:32:31 +00:00
while ( * str & & len & & y < LUA_SCREEN_HEIGHT )
{
int c = * str + + ;
2010-04-08 00:46:56 +00:00
while ( x > = LUA_SCREEN_WIDTH & & c ! = ' \n ' ) {
2009-10-12 07:32:31 +00:00
c = * str ;
if ( c = = ' \0 ' )
break ;
str + + ;
2010-04-08 00:46:56 +00:00
if ( ! ( - - len ) )
break ;
2009-10-12 07:32:31 +00:00
}
if ( c = = ' \n ' )
{
x = origX ;
y + = 8 ;
2021-02-07 17:23:31 +00:00
diffy = std : : max < int > ( 0 , std : : min < int > ( 7 , LUA_SCREEN_HEIGHT - y ) ) ;
2009-10-12 07:32:31 +00:00
continue ;
}
else if ( c = = ' \t ' ) // just in case
{
const int tabSpace = 8 ;
x + = ( tabSpace - ( ( ( x - origX ) / 8 ) % tabSpace ) ) * 8 ;
continue ;
}
2021-02-07 17:23:31 +00:00
diffx = std : : max < int > ( 0 , std : : min < int > ( 7 , LUA_SCREEN_WIDTH - x ) ) ;
2009-10-12 07:32:31 +00:00
int ch = FixJoedChar ( c ) ;
2021-02-07 17:23:31 +00:00
int wid = std : : min < int > ( diffx , JoedCharWidth ( c ) ) ;
2009-10-12 07:32:31 +00:00
2010-04-08 00:46:56 +00:00
for ( int y2 = 0 ; y2 < diffy ; y2 + + )
2009-10-12 07:32:31 +00:00
{
uint8 d = FCEUFont [ ch * 8 + 1 + y2 ] ;
for ( int x2 = 0 ; x2 < wid ; x2 + + )
{
int c = ( d > > ( 7 - x2 ) ) & 1 ;
if ( c )
gui_drawpixel_internal ( x + x2 , y + y2 , color ) ;
else
gui_drawpixel_internal ( x + x2 , y + y2 , backcolor ) ;
}
}
2018-11-15 00:44:18 +00:00
2016-01-05 11:07:58 +00:00
// halo
if ( diffy > = 7 )
for ( int x2 = - 1 ; x2 < wid ; x2 + + )
{
gui_drawpixel_internal ( x + x2 , y - 1 , backcolor ) ;
gui_drawpixel_internal ( x + x2 , y + 7 , backcolor ) ;
}
if ( x = = origX )
for ( int y2 = 0 ; y2 < diffy ; y2 + + )
gui_drawpixel_internal ( x - 1 , y + y2 , backcolor ) ;
2009-10-12 07:32:31 +00:00
x + = wid ;
len - - ;
}
}
2008-11-02 19:10:53 +00:00
// gui.text(int x, int y, string msg)
//
// Displays the given text on the screen, using the same font and techniques as the
// main HUD.
static int gui_text ( lua_State * L ) {
2009-10-12 07:32:31 +00:00
extern int font_height ;
2008-11-02 19:10:53 +00:00
const char * msg ;
int x , y ;
2010-04-08 00:46:56 +00:00
size_t l ;
2008-11-02 19:10:53 +00:00
x = luaL_checkinteger ( L , 1 ) ;
y = luaL_checkinteger ( L , 2 ) ;
2010-04-08 00:46:56 +00:00
msg = luaL_checklstring ( L , 3 , & l ) ;
2008-11-02 19:10:53 +00:00
2010-04-07 23:56:15 +00:00
//if (x < 0 || x >= LUA_SCREEN_WIDTH || y < 0 || y >= (LUA_SCREEN_HEIGHT - font_height))
// luaL_error(L,"bad coordinates");
2009-10-12 07:32:31 +00:00
#if 0
uint32 colour = gui_optcolour ( L , 4 , LUA_BUILD_PIXEL ( 255 , 255 , 255 , 255 ) ) ;
uint32 borderColour = gui_optcolour ( L , 5 , LUA_BUILD_PIXEL ( 255 , 0 , 0 , 0 ) ) ;
2008-11-02 19:10:53 +00:00
gui_prepare ( ) ;
2009-10-12 07:32:31 +00:00
LuaDisplayString ( msg , y , x , colour , borderColour ) ;
# else
uint32 color = gui_optcolour ( L , 4 , LUA_BUILD_PIXEL ( 255 , 255 , 255 , 255 ) ) ;
uint32 bgcolor = gui_optcolour ( L , 5 , LUA_BUILD_PIXEL ( 255 , 27 , 18 , 105 ) ) ;
2008-11-02 19:10:53 +00:00
2009-10-12 07:32:31 +00:00
gui_prepare ( ) ;
2010-04-08 00:46:56 +00:00
LuaDrawTextTransWH ( msg , l , x , y , color , bgcolor ) ;
2010-06-06 22:50:46 +00:00
2018-11-16 02:49:19 +00:00
lua_pushinteger ( L , x ) ;
2009-10-12 07:32:31 +00:00
# endif
2010-06-06 22:50:46 +00:00
return 1 ;
2008-11-02 19:10:53 +00:00
}
2009-10-12 07:32:31 +00:00
// gui.gdoverlay([int dx=0, int dy=0,] string str [, sx=0, sy=0, sw, sh] [, float alphamul=1.0])
2008-11-02 19:10:53 +00:00
//
// Overlays the given image on the screen.
2009-10-12 07:32:31 +00:00
// example: gui.gdoverlay(gd.createFromPng("myimage.png"):gdStr())
2008-11-02 19:10:53 +00:00
static int gui_gdoverlay ( lua_State * L ) {
2009-10-12 07:32:31 +00:00
int argCount = lua_gettop ( L ) ;
2008-11-02 19:10:53 +00:00
2009-10-12 07:32:31 +00:00
int xStartDst = 0 ;
int yStartDst = 0 ;
int xStartSrc = 0 ;
int yStartSrc = 0 ;
int index = 1 ;
if ( lua_type ( L , index ) = = LUA_TNUMBER )
{
xStartDst = lua_tointeger ( L , index + + ) ;
if ( lua_type ( L , index ) = = LUA_TNUMBER )
yStartDst = lua_tointeger ( L , index + + ) ;
}
luaL_checktype ( L , index , LUA_TSTRING ) ;
const unsigned char * ptr = ( const unsigned char * ) lua_tostring ( L , index + + ) ;
if ( ptr [ 0 ] ! = 255 | | ( ptr [ 1 ] ! = 254 & & ptr [ 1 ] ! = 255 ) )
2008-11-02 19:10:53 +00:00
luaL_error ( L , " bad image data " ) ;
2009-10-12 07:32:31 +00:00
bool trueColor = ( ptr [ 1 ] = = 254 ) ;
ptr + = 2 ;
int imgwidth = * ptr + + < < 8 ;
imgwidth | = * ptr + + ;
int width = imgwidth ;
int imgheight = * ptr + + < < 8 ;
imgheight | = * ptr + + ;
int height = imgheight ;
if ( ( ! trueColor & & * ptr ) | | ( trueColor & & ! * ptr ) )
2008-11-02 19:10:53 +00:00
luaL_error ( L , " bad image data " ) ;
2009-10-12 07:32:31 +00:00
ptr + + ;
int pitch = imgwidth * ( trueColor ? 4 : 1 ) ;
if ( ( argCount - index + 1 ) > = 4 ) {
xStartSrc = luaL_checkinteger ( L , index + + ) ;
yStartSrc = luaL_checkinteger ( L , index + + ) ;
width = luaL_checkinteger ( L , index + + ) ;
height = luaL_checkinteger ( L , index + + ) ;
}
2008-11-02 19:10:53 +00:00
2009-10-12 07:32:31 +00:00
int alphaMul = transparencyModifier ;
if ( lua_isnumber ( L , index ) )
alphaMul = ( int ) ( alphaMul * lua_tonumber ( L , index + + ) ) ;
if ( alphaMul < = 0 )
return 0 ;
2008-11-02 19:10:53 +00:00
2009-10-12 07:32:31 +00:00
// since there aren't that many possible opacity levels,
// do the opacity modification calculations beforehand instead of per pixel
int opacMap [ 256 ] ;
for ( int i = 0 ; i < 128 ; i + + )
{
int opac = 255 - ( ( i < < 1 ) | ( i & 1 ) ) ; // gdAlphaMax = 127, not 255
opac = ( opac * alphaMul ) / 255 ;
if ( opac < 0 ) opac = 0 ;
if ( opac > 255 ) opac = 255 ;
opacMap [ i ] = opac ;
}
for ( int i = 128 ; i < 256 ; i + + )
opacMap [ i ] = 0 ; // what should we do for them, actually?
2008-11-02 19:10:53 +00:00
2009-10-12 07:32:31 +00:00
int colorsTotal = 0 ;
if ( ! trueColor ) {
colorsTotal = * ptr + + < < 8 ;
colorsTotal | = * ptr + + ;
}
int transparent = * ptr + + < < 24 ;
transparent | = * ptr + + < < 16 ;
transparent | = * ptr + + < < 8 ;
transparent | = * ptr + + ;
struct { uint8 r , g , b , a ; } pal [ 256 ] ;
if ( ! trueColor ) for ( int i = 0 ; i < 256 ; i + + ) {
pal [ i ] . r = * ptr + + ;
pal [ i ] . g = * ptr + + ;
pal [ i ] . b = * ptr + + ;
pal [ i ] . a = opacMap [ * ptr + + ] ;
}
2008-11-02 19:10:53 +00:00
2009-10-12 07:32:31 +00:00
// some of clippings
if ( xStartSrc < 0 ) {
width + = xStartSrc ;
xStartDst - = xStartSrc ;
xStartSrc = 0 ;
2008-11-02 19:10:53 +00:00
}
2009-10-12 07:32:31 +00:00
if ( yStartSrc < 0 ) {
height + = yStartSrc ;
yStartDst - = yStartSrc ;
yStartSrc = 0 ;
}
if ( xStartSrc + width > = imgwidth )
width = imgwidth - xStartSrc ;
if ( yStartSrc + height > = imgheight )
height = imgheight - yStartSrc ;
if ( xStartDst < 0 ) {
width + = xStartDst ;
if ( width < = 0 )
return 0 ;
xStartSrc = - xStartDst ;
xStartDst = 0 ;
}
if ( yStartDst < 0 ) {
height + = yStartDst ;
if ( height < = 0 )
return 0 ;
yStartSrc = - yStartDst ;
yStartDst = 0 ;
}
if ( xStartDst + width > = LUA_SCREEN_WIDTH )
width = LUA_SCREEN_WIDTH - xStartDst ;
if ( yStartDst + height > = LUA_SCREEN_HEIGHT )
height = LUA_SCREEN_HEIGHT - yStartDst ;
if ( width < = 0 | | height < = 0 )
return 0 ; // out of screen or invalid size
gui_prepare ( ) ;
const uint8 * pix = ( const uint8 * ) ( & ptr [ yStartSrc * pitch + ( xStartSrc * ( trueColor ? 4 : 1 ) ) ] ) ;
int bytesToNextLine = pitch - ( width * ( trueColor ? 4 : 1 ) ) ;
if ( trueColor )
for ( int y = yStartDst ; y < height + yStartDst & & y < LUA_SCREEN_HEIGHT ; y + + , pix + = bytesToNextLine ) {
for ( int x = xStartDst ; x < width + xStartDst & & x < LUA_SCREEN_WIDTH ; x + + , pix + = 4 ) {
gui_drawpixel_fast ( x , y , LUA_BUILD_PIXEL ( opacMap [ pix [ 0 ] ] , pix [ 1 ] , pix [ 2 ] , pix [ 3 ] ) ) ;
}
}
else
for ( int y = yStartDst ; y < height + yStartDst & & y < LUA_SCREEN_HEIGHT ; y + + , pix + = bytesToNextLine ) {
for ( int x = xStartDst ; x < width + xStartDst & & x < LUA_SCREEN_WIDTH ; x + + , pix + + ) {
gui_drawpixel_fast ( x , y , LUA_BUILD_PIXEL ( pal [ * pix ] . a , pal [ * pix ] . r , pal [ * pix ] . g , pal [ * pix ] . b ) ) ;
}
}
2008-11-02 19:10:53 +00:00
return 0 ;
}
// function gui.register(function f)
//
// This function will be called just before a graphical update.
// More complicated, but doesn't suffer any frame delays.
// Nil will be accepted in place of a function to erase
// a previously registered function, and the previous function
// (if any) is returned, or nil if none.
static int gui_register ( lua_State * L ) {
// We'll do this straight up.
// First set up the stack.
lua_settop ( L , 1 ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// Verify the validity of the entry
if ( ! lua_isnil ( L , 1 ) )
luaL_checktype ( L , 1 , LUA_TFUNCTION ) ;
// Get the old value
lua_getfield ( L , LUA_REGISTRYINDEX , guiCallbackTable ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// Save the new value
lua_pushvalue ( L , 1 ) ;
lua_setfield ( L , LUA_REGISTRYINDEX , guiCallbackTable ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// The old value is on top of the stack. Return it.
return 1 ;
}
2012-02-01 22:02:26 +00:00
// table sound.get()
2011-02-26 15:20:30 +00:00
static int sound_get ( lua_State * L )
2012-10-21 16:40:04 +00:00
{
2011-02-26 15:20:30 +00:00
extern ENVUNIT EnvUnits [ 3 ] ;
extern int CheckFreq ( uint32 cf , uint8 sr ) ;
extern int32 curfreq [ 2 ] ;
extern uint8 PSG [ 0x10 ] ;
2012-10-21 16:40:04 +00:00
extern int32 lengthcount [ 4 ] ;
2011-02-26 15:20:30 +00:00
extern uint8 TriCount ;
2016-06-05 09:35:27 +00:00
extern const uint32 NoiseFreqTableNTSC [ 0x10 ] ;
extern const uint32 NoiseFreqTablePAL [ 0x10 ] ;
2011-02-26 15:20:30 +00:00
extern int32 DMCPeriod ;
extern uint8 DMCAddressLatch , DMCSizeLatch ;
extern uint8 DMCFormat ;
extern char DMCHaveSample ;
2011-02-27 11:21:34 +00:00
extern uint8 InitialRawDALatch ;
2011-02-26 15:20:30 +00:00
int freqReg ;
double freq ;
2016-06-05 09:35:27 +00:00
bool shortMode ;
2011-02-26 15:20:30 +00:00
lua_newtable ( L ) ;
// rp2a03 start
lua_newtable ( L ) ;
// rp2a03 info setup
double nesVolumes [ 3 ] ;
for ( int i = 0 ; i < 3 ; i + + )
{
if ( ( EnvUnits [ i ] . Mode & 1 ) ! = 0 )
nesVolumes [ i ] = EnvUnits [ i ] . Speed ;
else
nesVolumes [ i ] = EnvUnits [ i ] . decvolume ;
nesVolumes [ i ] / = 15.0 ;
}
// rp2a03/square1
lua_newtable ( L ) ;
if ( ( curfreq [ 0 ] < 8 | | curfreq [ 0 ] > 0x7ff ) | |
( CheckFreq ( curfreq [ 0 ] , PSG [ 1 ] ) = = 0 ) | |
( lengthcount [ 0 ] = = 0 ) )
lua_pushnumber ( L , 0.0 ) ;
else
lua_pushnumber ( L , nesVolumes [ 0 ] ) ;
lua_setfield ( L , - 2 , " volume " ) ;
2016-06-05 09:35:27 +00:00
freq = ( ( PAL ? PAL_CPU : NTSC_CPU ) / 16.0 ) / ( curfreq [ 0 ] + 1 ) ;
2011-02-26 15:20:30 +00:00
lua_pushnumber ( L , freq ) ;
lua_setfield ( L , - 2 , " frequency " ) ;
lua_pushnumber ( L , ( log ( freq / 440.0 ) * 12 / log ( 2.0 ) ) + 69 ) ;
lua_setfield ( L , - 2 , " midikey " ) ;
lua_pushinteger ( L , ( PSG [ 0 ] & 0xC0 ) > > 6 ) ;
lua_setfield ( L , - 2 , " duty " ) ;
2011-03-06 09:01:45 +00:00
lua_newtable ( L ) ;
lua_pushinteger ( L , curfreq [ 0 ] ) ;
lua_setfield ( L , - 2 , " frequency " ) ;
lua_setfield ( L , - 2 , " regs " ) ;
2011-02-26 15:20:30 +00:00
lua_setfield ( L , - 2 , " square1 " ) ;
// rp2a03/square2
lua_newtable ( L ) ;
if ( ( curfreq [ 1 ] < 8 | | curfreq [ 1 ] > 0x7ff ) | |
( CheckFreq ( curfreq [ 1 ] , PSG [ 5 ] ) = = 0 ) | |
( lengthcount [ 1 ] = = 0 ) )
lua_pushnumber ( L , 0.0 ) ;
else
lua_pushnumber ( L , nesVolumes [ 1 ] ) ;
lua_setfield ( L , - 2 , " volume " ) ;
2016-06-05 09:35:27 +00:00
freq = ( ( PAL ? PAL_CPU : NTSC_CPU ) / 16.0 ) / ( curfreq [ 1 ] + 1 ) ;
2011-02-26 15:20:30 +00:00
lua_pushnumber ( L , freq ) ;
lua_setfield ( L , - 2 , " frequency " ) ;
lua_pushnumber ( L , ( log ( freq / 440.0 ) * 12 / log ( 2.0 ) ) + 69 ) ;
lua_setfield ( L , - 2 , " midikey " ) ;
lua_pushinteger ( L , ( PSG [ 4 ] & 0xC0 ) > > 6 ) ;
lua_setfield ( L , - 2 , " duty " ) ;
2011-03-06 09:01:45 +00:00
lua_newtable ( L ) ;
lua_pushinteger ( L , curfreq [ 1 ] ) ;
lua_setfield ( L , - 2 , " frequency " ) ;
lua_setfield ( L , - 2 , " regs " ) ;
2011-02-26 15:20:30 +00:00
lua_setfield ( L , - 2 , " square2 " ) ;
// rp2a03/triangle
lua_newtable ( L ) ;
if ( lengthcount [ 2 ] = = 0 | | TriCount = = 0 )
lua_pushnumber ( L , 0.0 ) ;
else
lua_pushnumber ( L , 1.0 ) ;
lua_setfield ( L , - 2 , " volume " ) ;
freqReg = PSG [ 0xa ] | ( ( PSG [ 0xb ] & 7 ) < < 8 ) ;
2016-06-05 09:35:27 +00:00
freq = ( ( PAL ? PAL_CPU : NTSC_CPU ) / 32.0 ) / ( freqReg + 1 ) ;
2011-02-26 15:20:30 +00:00
lua_pushnumber ( L , freq ) ;
lua_setfield ( L , - 2 , " frequency " ) ;
lua_pushnumber ( L , ( log ( freq / 440.0 ) * 12 / log ( 2.0 ) ) + 69 ) ;
lua_setfield ( L , - 2 , " midikey " ) ;
2011-03-06 09:01:45 +00:00
lua_newtable ( L ) ;
lua_pushinteger ( L , freqReg ) ;
lua_setfield ( L , - 2 , " frequency " ) ;
lua_setfield ( L , - 2 , " regs " ) ;
2011-02-26 15:20:30 +00:00
lua_setfield ( L , - 2 , " triangle " ) ;
// rp2a03/noise
lua_newtable ( L ) ;
if ( lengthcount [ 3 ] = = 0 )
lua_pushnumber ( L , 0.0 ) ;
else
lua_pushnumber ( L , nesVolumes [ 2 ] ) ;
lua_setfield ( L , - 2 , " volume " ) ;
freqReg = PSG [ 0xE ] & 0xF ;
2016-06-05 09:35:27 +00:00
shortMode = ( ( PSG [ 0xE ] & 0x80 ) ! = 0 ) ;
lua_pushboolean ( L , shortMode ) ;
2011-02-26 15:20:30 +00:00
lua_setfield ( L , - 2 , " short " ) ;
2016-06-05 09:35:27 +00:00
freq = PAL ? PAL_CPU / NoiseFreqTablePAL [ freqReg ] : NTSC_CPU / NoiseFreqTableNTSC [ freqReg ] ; // rate
if ( shortMode )
freq / = 93.0 ; // pitch
2011-02-26 15:20:30 +00:00
lua_pushnumber ( L , freq ) ;
lua_setfield ( L , - 2 , " frequency " ) ;
lua_pushnumber ( L , ( log ( freq / 440.0 ) * 12 / log ( 2.0 ) ) + 69 ) ;
lua_setfield ( L , - 2 , " midikey " ) ;
2011-03-06 09:01:45 +00:00
lua_newtable ( L ) ;
lua_pushinteger ( L , freqReg ) ;
lua_setfield ( L , - 2 , " frequency " ) ;
lua_setfield ( L , - 2 , " regs " ) ;
2011-02-26 15:20:30 +00:00
lua_setfield ( L , - 2 , " noise " ) ;
// rp2a03/dpcm
lua_newtable ( L ) ;
if ( DMCHaveSample = = 0 )
lua_pushnumber ( L , 0.0 ) ;
else
lua_pushnumber ( L , 1.0 ) ;
lua_setfield ( L , - 2 , " volume " ) ;
2016-06-05 09:35:27 +00:00
freq = ( PAL ? PAL_CPU : NTSC_CPU ) / DMCPeriod ; // rate
2011-02-26 15:20:30 +00:00
lua_pushnumber ( L , freq ) ;
lua_setfield ( L , - 2 , " frequency " ) ;
lua_pushnumber ( L , ( log ( freq / 440.0 ) * 12 / log ( 2.0 ) ) + 69 ) ;
lua_setfield ( L , - 2 , " midikey " ) ;
lua_pushinteger ( L , 0xC000 + ( DMCAddressLatch < < 6 ) ) ;
lua_setfield ( L , - 2 , " dmcaddress " ) ;
lua_pushinteger ( L , ( DMCSizeLatch < < 4 ) + 1 ) ;
lua_setfield ( L , - 2 , " dmcsize " ) ;
2011-02-27 11:21:34 +00:00
lua_pushboolean ( L , DMCFormat & 0x40 ) ;
lua_setfield ( L , - 2 , " dmcloop " ) ;
lua_pushinteger ( L , InitialRawDALatch ) ;
lua_setfield ( L , - 2 , " dmcseed " ) ;
2011-03-06 09:01:45 +00:00
lua_newtable ( L ) ;
lua_pushinteger ( L , DMCFormat & 0xF ) ;
lua_setfield ( L , - 2 , " frequency " ) ;
lua_setfield ( L , - 2 , " regs " ) ;
2011-02-26 15:20:30 +00:00
lua_setfield ( L , - 2 , " dpcm " ) ;
// rp2a03 end
lua_setfield ( L , - 2 , " rp2a03 " ) ;
return 1 ;
}
2013-05-26 15:18:30 +00:00
// Debugger functions library
// debugger.hitbreakpoint()
static int debugger_hitbreakpoint ( lua_State * L )
{
break_asap = true ;
return 0 ;
}
// debugger.getcyclescount()
static int debugger_getcyclescount ( lua_State * L )
{
2013-09-05 14:53:07 +00:00
int64 counter_value = timestampbase + ( uint64 ) timestamp - total_cycles_base ;
if ( counter_value < 0 ) // sanity check
{
ResetDebugStatisticsCounters ( ) ;
counter_value = 0 ;
}
lua_pushinteger ( L , counter_value ) ;
2013-05-26 15:18:30 +00:00
return 1 ;
}
// debugger.getinstructionscount()
static int debugger_getinstructionscount ( lua_State * L )
{
lua_pushinteger ( L , total_instructions ) ;
return 1 ;
}
// debugger.resetcyclescount()
static int debugger_resetcyclescount ( lua_State * L )
{
ResetCyclesCounter ( ) ;
return 0 ;
}
// debugger.resetinstructionscount()
static int debugger_resetinstructionscount ( lua_State * L )
{
ResetInstructionsCounter ( ) ;
return 0 ;
}
2011-12-25 21:54:31 +00:00
// TAS Editor functions library
2012-01-08 18:19:49 +00:00
// bool taseditor.registerauto()
static int taseditor_registerauto ( lua_State * L )
{
if ( ! lua_isnil ( L , 1 ) )
luaL_checktype ( L , 1 , LUA_TFUNCTION ) ;
lua_settop ( L , 1 ) ;
lua_getfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_TASEDITOR_AUTO ] ) ;
lua_insert ( L , 1 ) ;
lua_setfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_TASEDITOR_AUTO ] ) ;
//StopScriptIfFinished(luaStateToUIDMap[L]);
return 1 ;
}
2012-07-19 19:40:40 +00:00
// bool taseditor.registermanual(string caption)
2012-01-08 18:19:49 +00:00
static int taseditor_registermanual ( lua_State * L )
{
if ( ! lua_isnil ( L , 1 ) )
luaL_checktype ( L , 1 , LUA_TFUNCTION ) ;
2012-07-19 19:40:40 +00:00
const char * caption = NULL ;
if ( ! lua_isnil ( L , 2 ) )
caption = lua_tostring ( L , 2 ) ;
2012-01-08 18:19:49 +00:00
lua_settop ( L , 1 ) ;
lua_getfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_TASEDITOR_MANUAL ] ) ;
lua_insert ( L , 1 ) ;
lua_setfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_TASEDITOR_MANUAL ] ) ;
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2013-03-04 16:58:54 +00:00
taseditor_lua . enableRunFunction ( caption ) ;
2012-01-08 18:19:49 +00:00
# endif
return 1 ;
}
2011-12-25 21:54:31 +00:00
// bool taseditor.engaged()
static int taseditor_engaged ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-08 18:19:49 +00:00
lua_pushboolean ( L , taseditor_lua . engaged ( ) ) ;
# else
lua_pushboolean ( L , false ) ;
# endif
2011-12-25 21:54:31 +00:00
return 1 ;
}
// bool taseditor.markedframe(int frame)
static int taseditor_markedframe ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-08 18:19:49 +00:00
lua_pushboolean ( L , taseditor_lua . markedframe ( luaL_checkinteger ( L , 1 ) ) ) ;
# else
lua_pushboolean ( L , false ) ;
2011-12-25 21:54:31 +00:00
# endif
return 1 ;
}
// int taseditor.getmarker(int frame)
static int taseditor_getmarker ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-08 18:19:49 +00:00
lua_pushinteger ( L , taseditor_lua . getmarker ( luaL_checkinteger ( L , 1 ) ) ) ;
# else
lua_pushinteger ( L , - 1 ) ;
2011-12-25 21:54:31 +00:00
# endif
return 1 ;
}
// int taseditor.setmarker(int frame)
static int taseditor_setmarker ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-08 18:19:49 +00:00
lua_pushinteger ( L , taseditor_lua . setmarker ( luaL_checkinteger ( L , 1 ) ) ) ;
# else
lua_pushinteger ( L , - 1 ) ;
2011-12-25 21:54:31 +00:00
# endif
return 1 ;
}
2012-03-17 15:59:35 +00:00
// taseditor.removemarker(int frame)
static int taseditor_removemarker ( lua_State * L )
2011-12-25 21:54:31 +00:00
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-03-17 15:59:35 +00:00
taseditor_lua . removemarker ( luaL_checkinteger ( L , 1 ) ) ;
2011-12-25 21:54:31 +00:00
# endif
return 0 ;
}
// string taseditor.getnote(int index)
static int taseditor_getnote ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-08 18:19:49 +00:00
lua_pushstring ( L , taseditor_lua . getnote ( luaL_checkinteger ( L , 1 ) ) ) ;
# else
lua_pushnil ( L ) ;
2011-12-25 21:54:31 +00:00
# endif
return 1 ;
}
// taseditor.setnote(int index, string newtext)
static int taseditor_setnote ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-08 18:19:49 +00:00
taseditor_lua . setnote ( luaL_checkinteger ( L , 1 ) , luaL_checkstring ( L , 2 ) ) ;
2011-12-25 21:54:31 +00:00
# endif
return 0 ;
}
// int taseditor.getcurrentbranch()
static int taseditor_getcurrentbranch ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-08 18:19:49 +00:00
lua_pushinteger ( L , taseditor_lua . getcurrentbranch ( ) ) ;
# else
lua_pushinteger ( L , - 1 ) ;
2011-12-25 21:54:31 +00:00
# endif
return 1 ;
}
// string taseditor.getrecordermode()
static int taseditor_getrecordermode ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-08 18:19:49 +00:00
lua_pushstring ( L , taseditor_lua . getrecordermode ( ) ) ;
# else
lua_pushnil ( L ) ;
2011-12-25 21:54:31 +00:00
# endif
return 1 ;
}
2012-02-01 22:02:26 +00:00
// int taseditor.getsuperimpose()
static int taseditor_getsuperimpose ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-02-01 22:02:26 +00:00
lua_pushinteger ( L , taseditor_lua . getsuperimpose ( ) ) ;
# else
lua_pushinteger ( L , - 1 ) ;
# endif
return 1 ;
}
2012-01-29 22:33:23 +00:00
// int taseditor.getlostplayback()
static int taseditor_getlostplayback ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-29 22:33:23 +00:00
lua_pushinteger ( L , taseditor_lua . getlostplayback ( ) ) ;
# else
lua_pushinteger ( L , - 1 ) ;
# endif
return 1 ;
}
2011-12-25 21:54:31 +00:00
// int taseditor.getplaybacktarget()
static int taseditor_getplaybacktarget ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-08 18:19:49 +00:00
lua_pushinteger ( L , taseditor_lua . getplaybacktarget ( ) ) ;
# else
lua_pushinteger ( L , - 1 ) ;
2011-12-25 21:54:31 +00:00
# endif
return 1 ;
}
2012-01-08 18:19:49 +00:00
// taseditor.setplayback(int frame)
static int taseditor_setplayback ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-08 18:19:49 +00:00
taseditor_lua . setplayback ( luaL_checkinteger ( L , 1 ) ) ;
# endif
return 0 ;
}
// taseditor.stopseeking()
static int taseditor_stopseeking ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-08 18:19:49 +00:00
taseditor_lua . stopseeking ( ) ;
# endif
return 0 ;
}
2012-01-29 22:33:23 +00:00
// table taseditor.getselection()
static int taseditor_getselection ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-29 22:33:23 +00:00
// create temp vector and provide its reference to TAS Editor for filling the vector with data
std : : vector < int > cur_set ;
taseditor_lua . getselection ( cur_set ) ;
int size = cur_set . size ( ) ;
if ( size )
{
lua_createtable ( L , size , 0 ) ;
for ( int i = 0 ; i < size ; + + i )
{
lua_pushinteger ( L , cur_set [ i ] ) ;
lua_rawseti ( L , - 2 , i + 1 ) ;
}
} else
{
lua_pushnil ( L ) ;
}
# else
lua_pushnil ( L ) ;
# endif
return 1 ;
}
// taseditor.setselection(table new_set)
static int taseditor_setselection ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-29 22:33:23 +00:00
std : : vector < int > cur_set ;
// retrieve new_set data from table to vector
if ( ! lua_isnil ( L , 1 ) )
{
luaL_checktype ( L , 1 , LUA_TTABLE ) ;
int max_index = luaL_getn ( L , 1 ) ;
int i = 1 ;
while ( i < = max_index )
{
lua_rawgeti ( L , 1 , i ) ;
cur_set . push_back ( lua_tonumber ( L , - 1 ) ) ;
lua_pop ( L , 1 ) ;
i + + ;
}
}
// and provide its reference to TAS Editor for changing selection
taseditor_lua . setselection ( cur_set ) ;
# endif
return 0 ;
}
// int taseditor.getinput(int frame, int joypad)
static int taseditor_getinput ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-29 22:33:23 +00:00
lua_pushinteger ( L , taseditor_lua . getinput ( luaL_checkinteger ( L , 1 ) , luaL_checkinteger ( L , 2 ) ) ) ;
# else
lua_pushinteger ( L , - 1 ) ;
# endif
return 1 ;
}
// taseditor.submitinputchange(int frame, int joypad, int input)
static int taseditor_submitinputchange ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-29 22:33:23 +00:00
taseditor_lua . submitinputchange ( luaL_checkinteger ( L , 1 ) , luaL_checkinteger ( L , 2 ) , luaL_checkinteger ( L , 3 ) ) ;
# endif
return 0 ;
}
// taseditor.submitinsertframes(int frame, int joypad, int input)
static int taseditor_submitinsertframes ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-29 22:33:23 +00:00
taseditor_lua . submitinsertframes ( luaL_checkinteger ( L , 1 ) , luaL_checkinteger ( L , 2 ) ) ;
# endif
return 0 ;
}
// taseditor.submitdeleteframes(int frame, int joypad, int input)
static int taseditor_submitdeleteframes ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-29 22:33:23 +00:00
taseditor_lua . submitdeleteframes ( luaL_checkinteger ( L , 1 ) , luaL_checkinteger ( L , 2 ) ) ;
# endif
return 0 ;
}
2012-01-08 18:19:49 +00:00
2012-01-29 22:33:23 +00:00
// int taseditor.applyinputchanges([string name])
static int taseditor_applyinputchanges ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-29 22:33:23 +00:00
if ( lua_isnil ( L , 1 ) )
{
lua_pushinteger ( L , taseditor_lua . applyinputchanges ( " " ) ) ;
} else
{
const char * name = lua_tostring ( L , 1 ) ;
if ( name )
lua_pushinteger ( L , taseditor_lua . applyinputchanges ( name ) ) ;
else
lua_pushinteger ( L , taseditor_lua . applyinputchanges ( " " ) ) ;
}
# else
lua_pushinteger ( L , - 1 ) ;
# endif
return 1 ;
}
2012-01-08 18:19:49 +00:00
2012-01-29 22:33:23 +00:00
// taseditor.clearinputchanges()
static int taseditor_clearinputchanges ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2012-01-29 22:33:23 +00:00
taseditor_lua . clearinputchanges ( ) ;
# endif
return 0 ;
}
2012-01-08 18:19:49 +00:00
2017-02-23 21:29:43 +00:00
// CDLog functions library
static int cdl_loadcdlog ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2017-02-23 21:29:43 +00:00
const char * nameo = luaL_checkstring ( L , 1 ) ;
lua_pushinteger ( L , LoadCDLog ( nameo ) ) ;
# else
lua_pushinteger ( L , 0 ) ;
# endif
return 1 ;
}
static int cdl_loadfile ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2017-02-23 21:29:43 +00:00
LoadCDLogFile ( ) ;
# endif
return 0 ;
}
static int cdl_savecdlogfile ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2017-02-23 21:29:43 +00:00
SaveCDLogFile ( ) ;
# endif
return 0 ;
}
static int cdl_savecdlogfileas ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2017-02-23 21:29:43 +00:00
const char * nameo = luaL_checkstring ( L , 1 ) ;
RenameCDLog ( nameo ) ;
SaveCDLogFile ( ) ;
# endif
return 0 ;
}
static int cdl_docdlogger ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2017-02-23 21:29:43 +00:00
lua_pushinteger ( L , DoCDLogger ( ) ) ;
# else
lua_pushinteger ( L , 0 ) ;
# endif
return 1 ;
}
static int cdl_pausecdlogging ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2017-02-23 21:29:43 +00:00
lua_pushinteger ( L , PauseCDLogging ( ) ) ;
# else
lua_pushinteger ( L , 0 ) ;
# endif
return 1 ;
}
static int cdl_startcdlogging ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2017-02-23 21:29:43 +00:00
StartCDLogging ( ) ;
# endif
return 0 ;
}
static int cdl_resetcdlog ( lua_State * L )
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2017-02-23 21:29:43 +00:00
ResetCDLog ( ) ;
# endif
return 0 ;
}
2012-01-08 18:19:49 +00:00
2009-10-12 22:30:06 +00:00
static int doPopup ( lua_State * L , const char * deftype , const char * deficon ) {
const char * str = luaL_checkstring ( L , 1 ) ;
const char * type = lua_type ( L , 2 ) = = LUA_TSTRING ? lua_tostring ( L , 2 ) : deftype ;
const char * icon = lua_type ( L , 3 ) = = LUA_TSTRING ? lua_tostring ( L , 3 ) : deficon ;
2008-11-02 19:10:53 +00:00
2009-10-12 22:30:06 +00:00
int itype = - 1 , iters = 0 ;
while ( itype = = - 1 & & iters + + < 2 )
{
if ( ! stricmp ( type , " ok " ) ) itype = 0 ;
else if ( ! stricmp ( type , " yesno " ) ) itype = 1 ;
else if ( ! stricmp ( type , " yesnocancel " ) ) itype = 2 ;
else if ( ! stricmp ( type , " okcancel " ) ) itype = 3 ;
else if ( ! stricmp ( type , " abortretryignore " ) ) itype = 4 ;
else type = deftype ;
}
assert ( itype > = 0 & & itype < = 4 ) ;
if ( ! ( itype > = 0 & & itype < = 4 ) ) itype = 0 ;
int iicon = - 1 ; iters = 0 ;
while ( iicon = = - 1 & & iters + + < 2 )
{
if ( ! stricmp ( icon , " message " ) | | ! stricmp ( icon , " notice " ) ) iicon = 0 ;
else if ( ! stricmp ( icon , " question " ) ) iicon = 1 ;
else if ( ! stricmp ( icon , " warning " ) ) iicon = 2 ;
else if ( ! stricmp ( icon , " error " ) ) iicon = 3 ;
else icon = deficon ;
}
assert ( iicon > = 0 & & iicon < = 3 ) ;
if ( ! ( iicon > = 0 & & iicon < = 3 ) ) iicon = 0 ;
2008-11-02 19:10:53 +00:00
2009-10-12 22:30:06 +00:00
static const char * const titles [ ] = { " Notice " , " Question " , " Warning " , " Error " } ;
const char * answer = " ok " ;
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2009-10-12 22:30:06 +00:00
static const int etypes [ ] = { MB_OK , MB_YESNO , MB_YESNOCANCEL , MB_OKCANCEL , MB_ABORTRETRYIGNORE } ;
static const int eicons [ ] = { MB_ICONINFORMATION , MB_ICONQUESTION , MB_ICONWARNING , MB_ICONERROR } ;
//StopSound(); //mbg merge 7/27/08
int ianswer = MessageBox ( hAppWnd , str , titles [ iicon ] , etypes [ itype ] | eicons [ iicon ] ) ;
switch ( ianswer )
{
case IDOK : answer = " ok " ; break ;
case IDCANCEL : answer = " cancel " ; break ;
case IDABORT : answer = " abort " ; break ;
case IDRETRY : answer = " retry " ; break ;
case IDIGNORE : answer = " ignore " ; break ;
case IDYES : answer = " yes " ; break ;
case IDNO : answer = " no " ; break ;
2008-11-02 19:10:53 +00:00
}
2009-10-12 22:30:06 +00:00
lua_pushstring ( L , answer ) ;
return 1 ;
2008-11-02 19:10:53 +00:00
# else
2020-06-20 03:58:12 +00:00
const char * t ;
2008-11-02 19:10:53 +00:00
# ifdef __linux
int pid ; // appease compiler
// Before doing any work, verify the correctness of the parameters.
if ( strcmp ( type , " ok " ) = = 0 )
t = " OK:100 " ;
else if ( strcmp ( type , " yesno " ) = = 0 )
t = " Yes:100,No:101 " ;
else if ( strcmp ( type , " yesnocancel " ) = = 0 )
t = " Yes:100,No:101,Cancel:102 " ;
else
return luaL_error ( L , " invalid popup type \" %s \" " , type ) ;
// Can we find a copy of xmessage? Search the path.
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
char * path = strdup ( getenv ( " PATH " ) ) ;
char * current = path ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
char * colon ;
int found = 0 ;
while ( current ) {
colon = strchr ( current , ' : ' ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// Clip off the colon.
* colon + + = 0 ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
int len = strlen ( current ) ;
2010-09-19 00:00:38 +00:00
char * filename = ( char * ) FCEU_dmalloc ( len + 12 ) ; // always give excess
2008-11-02 19:10:53 +00:00
snprintf ( filename , len + 12 , " %s/xmessage " , current ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
if ( access ( filename , X_OK ) = = 0 ) {
free ( filename ) ;
found = 1 ;
break ;
}
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// Failed, move on.
current = colon ;
free ( filename ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
}
free ( path ) ;
// We've found it?
if ( ! found )
goto use_console ;
pid = fork ( ) ;
if ( pid = = 0 ) { // I'm the virgin sacrifice
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// I'm gonna be dead in a matter of microseconds anyways, so wasted memory doesn't matter to me.
// Go ahead and abuse strdup.
2020-06-20 03:58:12 +00:00
const char * parameters [ ] = { " xmessage " , " -buttons " , t , strdup ( str ) , NULL } ;
2008-11-02 19:10:53 +00:00
2020-06-20 03:58:12 +00:00
execvp ( " xmessage " , ( char * const * ) parameters ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// Aw shitty
perror ( " exec xmessage " ) ;
exit ( 1 ) ;
}
else if ( pid < 0 ) // something went wrong!!! Oh hell... use the console
goto use_console ;
else {
// We're the parent. Watch for the child.
int r ;
int res = waitpid ( pid , & r , 0 ) ;
if ( res < 0 ) // wtf?
goto use_console ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// The return value gets copmlicated...
if ( ! WIFEXITED ( r ) ) {
luaL_error ( L , " don't screw with my xmessage process! " ) ;
}
r = WEXITSTATUS ( r ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// We assume it's worked.
if ( r = = 0 )
{
return 0 ; // no parameters for an OK
}
if ( r = = 100 ) {
lua_pushstring ( L , " yes " ) ;
return 1 ;
}
if ( r = = 101 ) {
lua_pushstring ( L , " no " ) ;
return 1 ;
}
if ( r = = 102 ) {
lua_pushstring ( L , " cancel " ) ;
return 1 ;
}
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// Wtf?
return luaL_error ( L , " popup failed due to unknown results involving xmessage (%d) " , r) ;
}
use_console :
# endif
// All else has failed
if ( strcmp ( type , " ok " ) = = 0 )
t = " " ;
else if ( strcmp ( type , " yesno " ) = = 0 )
t = " yn " ;
else if ( strcmp ( type , " yesnocancel " ) = = 0 )
t = " ync " ;
else
return luaL_error ( L , " invalid popup type \" %s \" " , type ) ;
2009-10-12 22:30:06 +00:00
fprintf ( stderr , " Lua Message: %s \n " , str ) ;
2008-11-02 19:10:53 +00:00
2009-10-12 22:30:06 +00:00
while ( true ) {
2008-11-02 19:10:53 +00:00
char buffer [ 64 ] ;
// We don't want parameters
if ( ! t [ 0 ] ) {
fprintf ( stderr , " [Press Enter] " ) ;
fgets ( buffer , sizeof ( buffer ) , stdin ) ;
// We're done
return 0 ;
}
fprintf ( stderr , " (%s): " , t ) ;
fgets ( buffer , sizeof ( buffer ) , stdin ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// Check if the option is in the list
if ( strchr ( t , tolower ( buffer [ 0 ] ) ) ) {
switch ( tolower ( buffer [ 0 ] ) ) {
case ' y ' :
lua_pushstring ( L , " yes " ) ;
return 1 ;
case ' n ' :
lua_pushstring ( L , " no " ) ;
return 1 ;
case ' c ' :
lua_pushstring ( L , " cancel " ) ;
return 1 ;
default :
luaL_error ( L , " internal logic error in console based prompts for gui.popup " ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
}
}
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// We fell through, so we assume the user answered wrong and prompt again.
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
}
// Nothing here, since the only way out is in the loop.
# endif
}
2011-02-27 02:20:01 +00:00
static int doOpenFilePopup ( lua_State * L , bool saveFile ) {
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2011-02-27 02:20:01 +00:00
char filename [ PATH_MAX ] ;
OPENFILENAME ofn ;
ZeroMemory ( & ofn , sizeof ( OPENFILENAME ) ) ;
ofn . lStructSize = sizeof ( OPENFILENAME ) ;
ofn . hwndOwner = hAppWnd ;
ofn . lpstrFilter = TEXT ( " All files (*.*) \0 *.* \0 \0 " ) ;
ofn . nFilterIndex = 0 ;
filename [ 0 ] = TEXT ( ' \0 ' ) ;
ofn . lpstrFile = filename ;
ofn . nMaxFile = PATH_MAX ;
ofn . Flags = OFN_NOCHANGEDIR | ( saveFile ? OFN_OVERWRITEPROMPT : OFN_FILEMUSTEXIST ) ;
BOOL bResult = saveFile ? GetSaveFileName ( & ofn ) : GetOpenFileName ( & ofn ) ;
lua_newtable ( L ) ;
if ( bResult )
{
lua_pushstring ( L , filename ) ;
lua_rawseti ( L , - 2 , 1 ) ;
}
# else
// TODO: more sophisticated interface
char filename [ PATH_MAX ] ;
printf ( " Enter %s filename: " , saveFile ? " save " : " open " ) ;
fgets ( filename , PATH_MAX , stdin ) ;
lua_newtable ( L ) ;
lua_pushstring ( L , filename ) ;
lua_rawseti ( L , - 2 , 1 ) ;
# endif
return 1 ;
}
2009-10-12 22:30:06 +00:00
// string gui.popup(string message, string type = "ok", string icon = "message")
// string input.popup(string message, string type = "yesno", string icon = "question")
static int gui_popup ( lua_State * L )
{
return doPopup ( L , " ok " , " message " ) ;
}
static int input_popup ( lua_State * L )
{
return doPopup ( L , " yesno " , " question " ) ;
}
2011-02-27 02:20:01 +00:00
static int input_openfilepopup ( lua_State * L )
{
return doOpenFilePopup ( L , false ) ;
}
static int input_savefilepopup ( lua_State * L )
{
return doOpenFilePopup ( L , true ) ;
}
2009-10-08 03:45:47 +00:00
// the following bit operations are ported from LuaBitOp 1.0.1,
// because it can handle the sign bit (bit 31) correctly.
/*
* * Lua BitOp - - a bit operations library for Lua 5.1 .
* * http : //bitop.luajit.org/
* *
* * Copyright ( C ) 2008 - 2009 Mike Pall . All rights reserved .
* *
* * Permission is hereby granted , free of charge , to any person obtaining
* * a copy of this software and associated documentation files ( the
* * " Software " ) , to deal in the Software without restriction , including
* * without limitation the rights to use , copy , modify , merge , publish ,
* * distribute , sublicense , and / or sell copies of the Software , and to
* * permit persons to whom the Software is furnished to do so , subject to
* * the following conditions :
* *
* * The above copyright notice and this permission notice shall be
* * included in all copies or substantial portions of the Software .
* *
* * THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* * EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* * MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .
* * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* * CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT ,
* * TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION WITH THE
* * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
* *
* * [ MIT license : http : //www.opensource.org/licenses/mit-license.php ]
*/
2008-11-02 19:10:53 +00:00
2009-10-08 03:45:47 +00:00
# ifdef _MSC_VER
/* MSVC is stuck in the last century and doesn't have C99's stdint.h. */
typedef __int32 int32_t ;
typedef unsigned __int32 uint32_t ;
typedef unsigned __int64 uint64_t ;
# else
# include <stdint.h>
# endif
2008-11-02 19:10:53 +00:00
2009-10-08 03:45:47 +00:00
typedef int32_t SBits ;
typedef uint32_t UBits ;
2008-11-02 19:10:53 +00:00
2009-10-08 03:45:47 +00:00
typedef union {
lua_Number n ;
# ifdef LUA_NUMBER_DOUBLE
uint64_t b ;
# else
UBits b ;
# endif
} BitNum ;
/* Convert argument to bit type. */
static UBits barg ( lua_State * L , int idx )
{
BitNum bn ;
UBits b ;
bn . n = lua_tonumber ( L , idx ) ;
# if defined(LUA_NUMBER_DOUBLE)
bn . n + = 6755399441055744.0 ; /* 2^52+2^51 */
# ifdef SWAPPED_DOUBLE
b = ( UBits ) ( bn . b > > 32 ) ;
# else
b = ( UBits ) bn . b ;
# endif
# elif defined(LUA_NUMBER_INT) || defined(LUA_NUMBER_LONG) || \
2018-11-16 02:49:19 +00:00
defined ( LUA_NUMBER_LONGLONG ) | | defined ( LUA_NUMBER_LONG_LONG ) | | \
defined ( LUA_NUMBER_LLONG )
2009-10-08 03:45:47 +00:00
if ( sizeof ( UBits ) = = sizeof ( lua_Number ) )
2018-11-16 02:49:19 +00:00
b = bn . b ;
2009-10-08 03:45:47 +00:00
else
2018-11-16 02:49:19 +00:00
b = ( UBits ) ( SBits ) bn . n ;
2009-10-08 03:45:47 +00:00
# elif defined(LUA_NUMBER_FLOAT)
# error "A 'float' lua_Number type is incompatible with this library"
# else
# error "Unknown number type, check LUA_NUMBER_* in luaconf.h"
# endif
if ( b = = 0 & & ! lua_isnumber ( L , idx ) )
2018-11-16 02:49:19 +00:00
luaL_typerror ( L , idx , " number " ) ;
2009-10-08 03:45:47 +00:00
return b ;
}
/* Return bit type. */
# define BRET(b) lua_pushnumber(L, (lua_Number)(SBits)(b)); return 1;
static int bit_tobit ( lua_State * L ) { BRET ( barg ( L , 1 ) ) }
static int bit_bnot ( lua_State * L ) { BRET ( ~ barg ( L , 1 ) ) }
# define BIT_OP(func, opr) \
static int func ( lua_State * L ) { int i ; UBits b = barg ( L , 1 ) ; \
2018-11-16 02:49:19 +00:00
for ( i = lua_gettop ( L ) ; i > 1 ; i - - ) b opr barg ( L , i ) ; BRET ( b ) }
2009-10-08 03:45:47 +00:00
BIT_OP ( bit_band , & = )
BIT_OP ( bit_bor , | = )
BIT_OP ( bit_bxor , ^ = )
# define bshl(b, n) (b << n)
# define bshr(b, n) (b >> n)
# define bsar(b, n) ((SBits)b >> n)
# define brol(b, n) ((b << n) | (b >> (32-n)))
# define bror(b, n) ((b << (32-n)) | (b >> n))
# define BIT_SH(func, fn) \
static int func ( lua_State * L ) { \
2018-11-16 02:49:19 +00:00
UBits b = barg ( L , 1 ) ; UBits n = barg ( L , 2 ) & 31 ; BRET ( fn ( b , n ) ) }
2009-10-08 03:45:47 +00:00
BIT_SH ( bit_lshift , bshl )
BIT_SH ( bit_rshift , bshr )
BIT_SH ( bit_arshift , bsar )
BIT_SH ( bit_rol , brol )
BIT_SH ( bit_ror , bror )
static int bit_bswap ( lua_State * L )
{
UBits b = barg ( L , 1 ) ;
b = ( b > > 24 ) | ( ( b > > 8 ) & 0xff00 ) | ( ( b & 0xff00 ) < < 8 ) | ( b < < 24 ) ;
BRET ( b )
2008-11-02 19:10:53 +00:00
}
2009-10-08 03:45:47 +00:00
static int bit_tohex ( lua_State * L )
{
UBits b = barg ( L , 1 ) ;
SBits n = lua_isnone ( L , 2 ) ? 8 : ( SBits ) barg ( L , 2 ) ;
const char * hexdigits = " 0123456789abcdef " ;
char buf [ 8 ] ;
int i ;
if ( n < 0 ) { n = - n ; hexdigits = " 0123456789ABCDEF " ; }
if ( n > 8 ) n = 8 ;
for ( i = ( int ) n ; - - i > = 0 ; ) { buf [ i ] = hexdigits [ b & 15 ] ; b > > = 4 ; }
lua_pushlstring ( L , buf , ( size_t ) n ) ;
return 1 ;
}
static const struct luaL_Reg bit_funcs [ ] = {
{ " tobit " , bit_tobit } ,
{ " bnot " , bit_bnot } ,
{ " band " , bit_band } ,
{ " bor " , bit_bor } ,
{ " bxor " , bit_bxor } ,
{ " lshift " , bit_lshift } ,
{ " rshift " , bit_rshift } ,
{ " arshift " , bit_arshift } ,
{ " rol " , bit_rol } ,
{ " ror " , bit_ror } ,
{ " bswap " , bit_bswap } ,
{ " tohex " , bit_tohex } ,
{ NULL , NULL }
} ;
/* Signed right-shifts are implementation-defined per C89/C99.
* * But the de facto standard are arithmetic right - shifts on two ' s
* * complement CPUs . This behaviour is required here , so test for it .
*/
# define BAD_SAR (bsar(-8, 2) != (SBits)-2)
2008-11-02 19:10:53 +00:00
2009-10-08 03:45:47 +00:00
bool luabitop_validate ( lua_State * L ) // originally named as luaopen_bit
{
UBits b ;
lua_pushnumber ( L , ( lua_Number ) 1437217655L ) ;
b = barg ( L , - 1 ) ;
if ( b ! = ( UBits ) 1437217655L | | BAD_SAR ) { /* Perform a simple self-test. */
2018-11-16 02:49:19 +00:00
const char * msg = " compiled with incompatible luaconf.h " ;
2009-10-08 03:45:47 +00:00
# ifdef LUA_NUMBER_DOUBLE
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2018-11-16 02:49:19 +00:00
if ( b = = ( UBits ) 1610612736L )
msg = " use D3DCREATE_FPU_PRESERVE with DirectX " ;
2009-10-08 03:45:47 +00:00
# endif
2018-11-16 02:49:19 +00:00
if ( b = = ( UBits ) 1127743488L )
msg = " not compiled with SWAPPED_DOUBLE " ;
2009-10-08 03:45:47 +00:00
# endif
2018-11-16 02:49:19 +00:00
if ( BAD_SAR )
msg = " arithmetic right-shift broken " ;
luaL_error ( L , " bit library self-test failed (%s) " , msg ) ;
return false ;
2009-10-08 03:45:47 +00:00
}
return true ;
2008-11-02 19:10:53 +00:00
}
2009-10-08 03:45:47 +00:00
// LuaBitOp ends here
2008-11-02 19:10:53 +00:00
2009-10-08 03:45:47 +00:00
static int bit_bshift_emulua ( lua_State * L )
{
int shift = luaL_checkinteger ( L , 2 ) ;
if ( shift < 0 ) {
lua_pushinteger ( L , - shift ) ;
lua_replace ( L , 2 ) ;
return bit_lshift ( L ) ;
}
else
return bit_rshift ( L ) ;
2008-11-02 19:10:53 +00:00
}
2009-10-08 03:45:47 +00:00
static int bitbit ( lua_State * L )
{
int rv = 0 ;
int numArgs = lua_gettop ( L ) ;
for ( int i = 1 ; i < = numArgs ; i + + ) {
int where = luaL_checkinteger ( L , i ) ;
if ( where > = 0 & & where < 32 )
rv | = ( 1 < < where ) ;
}
lua_settop ( L , 0 ) ;
BRET ( rv ) ;
}
2008-11-02 19:10:53 +00:00
// The function called periodically to ensure Lua doesn't run amok.
static void FCEU_LuaHookFunction ( lua_State * L , lua_Debug * dbg ) {
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
if ( numTries - - = = 0 ) {
int kill = 0 ;
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2008-11-02 19:10:53 +00:00
// Uh oh
2018-11-16 02:49:19 +00:00
//StopSound(); //mbg merge 7/23/08
2008-11-02 19:10:53 +00:00
int ret = MessageBox ( hAppWnd , " The Lua script running has been running a long time. It may have gone crazy. Kill it? \n \n (No = don't check anymore either) " , " Lua Script Gone Nuts? " , MB_YESNO ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
if ( ret = = IDYES ) {
kill = 1 ;
}
# else
2020-10-31 16:24:52 +00:00
if ( LuaKillMessageBox ( ) )
{
kill = 1 ;
2008-11-02 19:10:53 +00:00
}
2020-10-31 16:24:52 +00:00
//fprintf(stderr, "The Lua script running has been running a long time.\nIt may have gone crazy. Kill it? (I won't ask again if you say No)\n");
//char buffer[64];
//while (TRUE) {
// fprintf(stderr, "(y/n): ");
// fgets(buffer, sizeof(buffer), stdin);
// if (buffer[0] == 'y' || buffer[0] == 'Y') {
// kill = 1;
// break;
// }
// if (buffer[0] == 'n' || buffer[0] == 'N')
// break;
//}
2008-11-02 19:10:53 +00:00
# endif
if ( kill ) {
luaL_error ( L , " Killed by user request. " ) ;
FCEU_LuaOnStop ( ) ;
}
// else, kill the debug hook.
lua_sethook ( L , NULL , 0 , 0 ) ;
}
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
}
2009-10-11 04:39:05 +00:00
static void emu_exec_count_hook ( lua_State * L , lua_Debug * dbg ) {
2008-11-02 19:10:53 +00:00
luaL_error ( L , " exec_count timeout " ) ;
}
2009-10-11 04:39:05 +00:00
static int emu_exec_count ( lua_State * L ) {
2008-11-02 19:10:53 +00:00
int count = ( int ) luaL_checkinteger ( L , 1 ) ;
lua_pushvalue ( L , 2 ) ;
2009-10-11 04:39:05 +00:00
lua_sethook ( L , emu_exec_count_hook , LUA_MASKCOUNT , count ) ;
2008-11-02 19:10:53 +00:00
int ret = lua_pcall ( L , 0 , 0 , 0 ) ;
lua_sethook ( L , NULL , 0 , 0 ) ;
lua_settop ( L , 0 ) ;
lua_pushinteger ( L , ret ) ;
return 1 ;
}
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2008-11-02 19:10:53 +00:00
static HANDLE readyEvent , goEvent ;
2009-10-11 04:39:05 +00:00
DWORD WINAPI emu_exec_time_proc ( LPVOID lpParameter )
2008-10-10 07:29:34 +00:00
{
SetEvent ( readyEvent ) ;
WaitForSingleObject ( goEvent , INFINITE ) ;
lua_State * L = ( lua_State * ) lpParameter ;
lua_pushvalue ( L , 2 ) ;
int ret = lua_pcall ( L , 0 , 0 , 0 ) ;
2008-11-02 19:10:53 +00:00
lua_settop ( L , 0 ) ;
2008-10-10 07:29:34 +00:00
lua_pushinteger ( L , ret ) ;
SetEvent ( readyEvent ) ;
return 0 ;
}
2008-11-02 19:10:53 +00:00
2009-10-11 04:39:05 +00:00
static void emu_exec_time_hook ( lua_State * L , lua_Debug * dbg ) {
2008-11-02 19:10:53 +00:00
luaL_error ( L , " exec_time timeout " ) ;
}
2009-10-11 04:39:05 +00:00
static int emu_exec_time ( lua_State * L )
2008-11-02 19:10:53 +00:00
{
int count = ( int ) luaL_checkinteger ( L , 1 ) ;
readyEvent = CreateEvent ( 0 , true , false , 0 ) ;
goEvent = CreateEvent ( 0 , true , false , 0 ) ;
DWORD threadid ;
2009-10-11 04:39:05 +00:00
HANDLE thread = CreateThread ( 0 , 0 , emu_exec_time_proc , ( LPVOID ) L , 0 , & threadid ) ;
2009-02-07 03:54:42 +00:00
SetThreadAffinityMask ( thread , 1 ) ;
2008-11-02 19:10:53 +00:00
//wait for the lua thread to start
WaitForSingleObject ( readyEvent , INFINITE ) ;
ResetEvent ( readyEvent ) ;
//tell the lua thread to proceed
SetEvent ( goEvent ) ;
//wait for the lua thread to finish, but no more than the specified amount of time
WaitForSingleObject ( readyEvent , count ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
//kill lua (if it hasnt already been killed)
2009-10-11 04:39:05 +00:00
lua_sethook ( L , emu_exec_time_hook , LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT , 1 ) ;
2008-11-02 19:10:53 +00:00
//keep on waiting for the lua thread to come back
WaitForSingleObject ( readyEvent , count ) ;
//clear the lua thread-killer
lua_sethook ( L , NULL , 0 , 0 ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
CloseHandle ( readyEvent ) ;
CloseHandle ( goEvent ) ;
CloseHandle ( thread ) ;
return 1 ;
}
# else
2009-10-11 04:39:05 +00:00
static int emu_exec_time ( lua_State * L ) { return 0 ; }
2008-11-02 19:10:53 +00:00
# endif
2012-10-21 16:40:04 +00:00
2009-10-11 05:09:17 +00:00
static const struct luaL_reg emulib [ ] = {
2009-10-11 04:39:05 +00:00
{ " poweron " , emu_poweron } ,
2017-05-15 05:09:13 +00:00
{ " debuggerloop " , emu_debuggerloop } ,
2017-05-15 17:21:58 +00:00
{ " debuggerloopstep " , emu_debuggerloopstep } ,
2009-10-11 04:39:05 +00:00
{ " softreset " , emu_softreset } ,
{ " speedmode " , emu_speedmode } ,
{ " frameadvance " , emu_frameadvance } ,
2012-01-08 18:19:49 +00:00
{ " paused " , emu_paused } ,
2009-10-11 04:39:05 +00:00
{ " pause " , emu_pause } ,
{ " unpause " , emu_unpause } ,
{ " exec_count " , emu_exec_count } ,
{ " exec_time " , emu_exec_time } ,
{ " setrenderplanes " , emu_setrenderplanes } ,
{ " message " , emu_message } ,
2009-10-11 05:09:17 +00:00
{ " framecount " , emu_framecount } ,
2009-10-11 04:39:05 +00:00
{ " lagcount " , emu_lagcount } ,
{ " lagged " , emu_lagged } ,
2012-09-13 19:48:14 +00:00
{ " setlagflag " , emu_setlagflag } ,
2009-11-08 12:15:25 +00:00
{ " emulating " , emu_emulating } ,
2009-10-11 04:39:05 +00:00
{ " registerbefore " , emu_registerbefore } ,
{ " registerafter " , emu_registerafter } ,
{ " registerexit " , emu_registerexit } ,
2010-05-08 20:34:27 +00:00
{ " addgamegenie " , emu_addgamegenie } ,
{ " delgamegenie " , emu_delgamegenie } ,
2010-05-10 07:20:55 +00:00
{ " getscreenpixel " , emu_getscreenpixel } ,
2009-10-11 21:11:30 +00:00
{ " readonly " , movie_getreadonly } ,
{ " setreadonly " , movie_setreadonly } ,
2018-11-16 02:49:19 +00:00
{ " getdir " , emu_getdir } ,
{ " loadrom " , emu_loadrom } ,
2009-10-19 03:01:26 +00:00
{ " print " , print } , // sure, why not
2020-12-12 10:38:11 +00:00
{ " exit " , emu_exit } , // useful for run-and-close scripts
2008-11-02 19:10:53 +00:00
{ NULL , NULL }
} ;
2008-11-02 20:11:14 +00:00
static const struct luaL_reg romlib [ ] = {
2018-11-25 17:25:55 +00:00
{ " getfilename " , rom_getfilename } ,
{ " gethash " , rom_gethash } ,
2008-11-02 20:11:14 +00:00
{ " readbyte " , rom_readbyte } ,
{ " readbytesigned " , rom_readbytesigned } ,
2009-10-11 05:09:17 +00:00
// alternate naming scheme for unsigned
{ " readbyteunsigned " , rom_readbyte } ,
2017-02-23 21:29:43 +00:00
{ " readbyterange " , rom_readbyterange } ,
2015-06-23 21:04:31 +00:00
{ " writebyte " , rom_writebyte } ,
2008-11-02 20:11:14 +00:00
{ NULL , NULL }
} ;
2008-11-02 19:10:53 +00:00
static const struct luaL_reg memorylib [ ] = {
{ " readbyte " , memory_readbyte } ,
{ " readbyterange " , memory_readbyterange } ,
2018-11-15 00:44:18 +00:00
{ " readbytesigned " , memory_readbytesigned } ,
2013-09-16 16:04:11 +00:00
{ " readbyteunsigned " , memory_readbyte } , // alternate naming scheme for unsigned
2013-09-15 18:37:11 +00:00
{ " readword " , memory_readword } ,
{ " readwordsigned " , memory_readwordsigned } ,
2013-09-16 16:04:11 +00:00
{ " readwordunsigned " , memory_readword } , // alternate naming scheme for unsigned
2008-11-02 19:10:53 +00:00
{ " writebyte " , memory_writebyte } ,
2019-11-23 04:29:25 +00:00
{ " legacywritebyte " , legacymemory_writebyte } ,
2009-10-11 08:50:09 +00:00
{ " getregister " , memory_getregister } ,
{ " setregister " , memory_setregister } ,
2012-10-21 16:40:04 +00:00
2009-10-11 05:09:17 +00:00
// memory hooks
{ " registerwrite " , memory_registerwrite } ,
2009-10-11 08:34:10 +00:00
//{"registerread", memory_registerread}, TODO
{ " registerexec " , memory_registerexec } ,
2009-10-11 05:09:17 +00:00
// alternate names
{ " register " , memory_registerwrite } ,
2009-10-11 08:34:10 +00:00
{ " registerrun " , memory_registerexec } ,
{ " registerexecute " , memory_registerexec } ,
2008-11-02 19:10:53 +00:00
{ NULL , NULL }
} ;
2016-12-22 15:15:38 +00:00
static const struct luaL_reg ppulib [ ] = {
{ " readbyte " , ppu_readbyte } ,
{ " readbyterange " , ppu_readbyterange } ,
{ NULL , NULL }
} ;
2008-11-02 19:10:53 +00:00
static const struct luaL_reg joypadlib [ ] = {
2009-11-08 12:15:25 +00:00
{ " get " , joypad_get } ,
{ " getdown " , joypad_getdown } ,
{ " getup " , joypad_getup } ,
2012-02-01 22:02:26 +00:00
{ " getimmediate " , joypad_getimmediate } ,
2008-11-02 19:10:53 +00:00
{ " set " , joypad_set } ,
2009-10-11 05:09:17 +00:00
// alternative names
2009-11-08 12:15:25 +00:00
{ " read " , joypad_get } ,
2009-10-11 05:09:17 +00:00
{ " write " , joypad_set } ,
2009-11-08 12:15:25 +00:00
{ " readdown " , joypad_getdown } ,
{ " readup " , joypad_getup } ,
2012-02-01 22:02:26 +00:00
{ " readimmediate " , joypad_getimmediate } ,
2008-11-02 19:10:53 +00:00
{ NULL , NULL }
} ;
2009-03-08 18:10:07 +00:00
static const struct luaL_reg zapperlib [ ] = {
{ " read " , zapper_read } ,
2019-12-15 20:31:09 +00:00
{ " set " , zapper_set } ,
2009-03-08 18:10:07 +00:00
{ NULL , NULL }
} ;
2009-03-08 22:36:38 +00:00
static const struct luaL_reg inputlib [ ] = {
{ " get " , input_get } ,
2009-10-12 22:30:06 +00:00
{ " popup " , input_popup } ,
2011-02-27 02:20:01 +00:00
{ " openfilepopup " , input_openfilepopup } ,
{ " savefilepopup " , input_savefilepopup } ,
2009-10-11 05:09:17 +00:00
// alternative names
{ " read " , input_get } ,
2009-03-08 22:36:38 +00:00
{ NULL , NULL }
} ;
2008-11-02 19:10:53 +00:00
static const struct luaL_reg savestatelib [ ] = {
{ " create " , savestate_create } ,
2010-05-09 02:02:20 +00:00
{ " object " , savestate_object } ,
2008-11-02 19:10:53 +00:00
{ " save " , savestate_save } ,
{ " persist " , savestate_persist } ,
{ " load " , savestate_load } ,
2009-10-25 03:02:00 +00:00
{ " registersave " , savestate_registersave } ,
{ " registerload " , savestate_registerload } ,
{ " loadscriptdata " , savestate_loadscriptdata } ,
2008-11-02 19:10:53 +00:00
{ NULL , NULL }
} ;
static const struct luaL_reg movielib [ ] = {
2009-10-11 05:09:17 +00:00
{ " framecount " , emu_framecount } , // for those familiar with other emulators that have movie.framecount() instead of emulatorname.framecount()
2008-11-02 19:10:53 +00:00
{ " mode " , movie_mode } ,
{ " rerecordcounting " , movie_rerecordcounting } ,
{ " stop " , movie_stop } ,
2009-10-11 05:29:41 +00:00
{ " active " , movie_isactive } ,
{ " recording " , movie_isrecording } ,
{ " playing " , movie_isplaying } ,
{ " length " , movie_getlength } ,
2009-03-15 21:12:15 +00:00
{ " rerecordcount " , movie_rerecordcount } ,
2009-10-11 05:09:17 +00:00
{ " name " , movie_getname } ,
2010-05-29 04:06:49 +00:00
{ " filename " , movie_getfilename } ,
2009-10-11 05:09:17 +00:00
{ " readonly " , movie_getreadonly } ,
{ " setreadonly " , movie_setreadonly } ,
2009-10-11 05:29:41 +00:00
{ " replay " , movie_replay } ,
2018-11-29 03:44:45 +00:00
{ " record " , movie_record } ,
{ " play " , movie_playback } ,
2008-11-02 19:10:53 +00:00
2009-10-11 05:09:17 +00:00
// alternative names
{ " close " , movie_stop } ,
{ " getname " , movie_getname } ,
2018-11-29 03:44:45 +00:00
{ " load " , movie_playback } ,
{ " save " , movie_record } ,
{ " playback " , movie_playback } ,
2009-10-11 23:48:45 +00:00
{ " playbeginning " , movie_replay } ,
2009-10-11 05:09:17 +00:00
{ " getreadonly " , movie_getreadonly } ,
2010-04-21 03:24:14 +00:00
{ " ispoweron " , movie_ispoweron } , //If movie recorded from power-on
{ " isfromsavestate " , movie_isfromsavestate } , //If movie is recorded from savestate
2008-11-02 19:10:53 +00:00
{ NULL , NULL }
} ;
static const struct luaL_reg guilib [ ] = {
2012-10-21 16:40:04 +00:00
2009-10-11 05:09:17 +00:00
{ " pixel " , gui_pixel } ,
2010-05-10 07:20:55 +00:00
{ " getpixel " , gui_getpixel } ,
2009-10-11 05:09:17 +00:00
{ " line " , gui_line } ,
{ " box " , gui_box } ,
2008-11-02 19:10:53 +00:00
{ " text " , gui_text } ,
2013-01-15 12:53:04 +00:00
{ " parsecolor " , gui_parsecolor } ,
2010-06-07 16:38:47 +00:00
{ " savescreenshot " , gui_savescreenshot } ,
{ " savescreenshotas " , gui_savescreenshotas } ,
2008-11-02 19:10:53 +00:00
{ " gdscreenshot " , gui_gdscreenshot } ,
{ " gdoverlay " , gui_gdoverlay } ,
2009-10-12 07:32:31 +00:00
{ " opacity " , gui_setopacity } ,
2008-11-02 19:10:53 +00:00
{ " transparency " , gui_transparency } ,
{ " register " , gui_register } ,
2009-10-11 05:09:17 +00:00
2008-11-02 19:10:53 +00:00
{ " popup " , gui_popup } ,
2009-10-11 05:09:17 +00:00
// alternative names
{ " drawtext " , gui_text } ,
{ " drawbox " , gui_box } ,
{ " drawline " , gui_line } ,
{ " drawpixel " , gui_pixel } ,
{ " setpixel " , gui_pixel } ,
{ " writepixel " , gui_pixel } ,
{ " rect " , gui_box } ,
{ " drawrect " , gui_box } ,
{ " drawimage " , gui_gdoverlay } ,
{ " image " , gui_gdoverlay } ,
2008-11-02 19:10:53 +00:00
{ NULL , NULL }
} ;
2011-02-26 15:20:30 +00:00
static const struct luaL_reg soundlib [ ] = {
2012-10-21 16:40:04 +00:00
2011-02-26 15:20:30 +00:00
{ " get " , sound_get } ,
{ NULL , NULL }
} ;
2013-05-26 15:18:30 +00:00
static const struct luaL_reg debuggerlib [ ] = {
{ " hitbreakpoint " , debugger_hitbreakpoint } ,
{ " getcyclescount " , debugger_getcyclescount } ,
{ " getinstructionscount " , debugger_getinstructionscount } ,
{ " resetcyclescount " , debugger_resetcyclescount } ,
{ " resetinstructionscount " , debugger_resetinstructionscount } ,
{ NULL , NULL }
} ;
2011-12-25 21:54:31 +00:00
static const struct luaL_reg taseditorlib [ ] = {
2012-10-21 16:40:04 +00:00
2012-01-08 18:19:49 +00:00
{ " registerauto " , taseditor_registerauto } ,
{ " registermanual " , taseditor_registermanual } ,
2011-12-25 21:54:31 +00:00
{ " engaged " , taseditor_engaged } ,
{ " markedframe " , taseditor_markedframe } ,
{ " getmarker " , taseditor_getmarker } ,
{ " setmarker " , taseditor_setmarker } ,
2012-03-17 15:59:35 +00:00
{ " removemarker " , taseditor_removemarker } ,
2011-12-25 21:54:31 +00:00
{ " getnote " , taseditor_getnote } ,
{ " setnote " , taseditor_setnote } ,
{ " getcurrentbranch " , taseditor_getcurrentbranch } ,
{ " getrecordermode " , taseditor_getrecordermode } ,
2012-02-01 22:02:26 +00:00
{ " getsuperimpose " , taseditor_getsuperimpose } ,
2012-01-29 22:33:23 +00:00
{ " getlostplayback " , taseditor_getlostplayback } ,
2011-12-25 21:54:31 +00:00
{ " getplaybacktarget " , taseditor_getplaybacktarget } ,
2012-01-08 18:19:49 +00:00
{ " setplayback " , taseditor_setplayback } ,
{ " stopseeking " , taseditor_stopseeking } ,
2012-01-29 22:33:23 +00:00
{ " getselection " , taseditor_getselection } ,
{ " setselection " , taseditor_setselection } ,
{ " getinput " , taseditor_getinput } ,
{ " submitinputchange " , taseditor_submitinputchange } ,
{ " submitinsertframes " , taseditor_submitinsertframes } ,
{ " submitdeleteframes " , taseditor_submitdeleteframes } ,
{ " applyinputchanges " , taseditor_applyinputchanges } ,
{ " clearinputchanges " , taseditor_clearinputchanges } ,
2011-12-25 21:54:31 +00:00
{ NULL , NULL }
} ;
2017-02-23 21:29:43 +00:00
static const struct luaL_reg cdloglib [ ] = {
{ " docdlogger " , cdl_docdlogger } ,
{ " savecdlogfile " , cdl_savecdlogfile } ,
{ " savecdlogfileas " , cdl_savecdlogfileas } ,
{ " loadfile " , cdl_loadfile } ,
{ " loadcdlog " , cdl_loadcdlog } ,
{ " docdlogger " , cdl_docdlogger } ,
{ " docdlogger " , cdl_docdlogger } ,
{ " resetcdlog " , cdl_resetcdlog } ,
{ " startcdlogging " , cdl_startcdlogging } ,
{ " pausecdlogging " , cdl_pausecdlogging } ,
{ NULL , NULL }
} ;
2020-10-22 01:40:27 +00:00
void CallExitFunction ( )
{
2009-10-08 05:02:22 +00:00
if ( ! L )
return ;
lua_settop ( L , 0 ) ;
lua_getfield ( L , LUA_REGISTRYINDEX , luaCallIDStrings [ LUACALL_BEFOREEXIT ] ) ;
int errorcode = 0 ;
if ( lua_isfunction ( L , - 1 ) )
{
//chdir(luaCWD);
errorcode = lua_pcall ( L , 0 , 0 , 0 ) ;
//_getcwd(luaCWD, _MAX_PATH);
}
if ( errorcode )
HandleCallbackError ( L ) ;
}
2012-09-23 12:45:28 +00:00
void FCEU_LuaFrameBoundary ( )
{
//printf("Lua Frame\n");
2008-11-02 19:10:53 +00:00
// HA!
if ( ! L | | ! luaRunning )
return ;
// Our function needs calling
lua_settop ( L , 0 ) ;
lua_getfield ( L , LUA_REGISTRYINDEX , frameAdvanceThread ) ;
2012-10-21 16:40:04 +00:00
lua_State * thread = lua_tothread ( L , 1 ) ;
2008-11-02 19:10:53 +00:00
// Lua calling C must know that we're busy inside a frame boundary
frameBoundary = TRUE ;
frameAdvanceWaiting = FALSE ;
numTries = 1000 ;
int result = lua_resume ( thread , 0 ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
if ( result = = LUA_YIELD ) {
// Okay, we're fine with that.
} else if ( result ! = 0 ) {
// Done execution by bad causes
FCEU_LuaOnStop ( ) ;
2018-11-15 00:44:18 +00:00
const char * trace = CallLuaTraceback ( L ) ;
2008-11-02 19:10:53 +00:00
lua_pushnil ( L ) ;
2018-11-15 00:44:18 +00:00
lua_setfield ( L , LUA_REGISTRYINDEX , guiCallbackTable ) ;
char errmsg [ 1024 ] ;
sprintf ( errmsg , " %s \n %s " , lua_tostring ( thread , - 1 ) , trace ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// Error?
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2018-11-15 00:44:18 +00:00
//StopSound();//StopSound(); //mbg merge 7/23/08
MessageBox ( hAppWnd , errmsg , " Lua run error " , MB_OK | MB_ICONSTOP ) ;
2008-11-02 19:10:53 +00:00
# else
2020-10-31 16:24:52 +00:00
LuaPrintfToWindowConsole ( " Lua thread bombed out: %s \n " , errmsg ) ;
2018-11-15 00:44:18 +00:00
fprintf ( stderr , " Lua thread bombed out: %s \n " , errmsg ) ;
2008-11-02 19:10:53 +00:00
# endif
} else {
2009-10-19 03:01:26 +00:00
FCEU_LuaOnStop ( ) ;
2014-03-15 22:04:04 +00:00
//FCEU_DispMessage("Script died of natural causes.\n",0);
// weird sequence of functions calls the above message each time the script starts or stops,
// then this message is overrided by "emu speed" within the same frame, which hides this bug
// uncomment onse solution is found
2008-11-02 19:10:53 +00:00
}
// Past here, the nes actually runs, so any Lua code is called mid-frame. We must
// not do anything too stupid, so let ourselves know.
frameBoundary = FALSE ;
if ( ! frameAdvanceWaiting ) {
FCEU_LuaOnStop ( ) ;
}
2021-02-07 17:23:31 +00:00
# ifndef __WIN_DRIVER__
2020-12-12 11:59:07 +00:00
if ( exitScheduled )
{ // This function does not exit immediately,
// it requests for the application to exit when next convenient.
fceuWrapperRequestAppExit ( ) ;
exitScheduled = FALSE ;
}
# else
2020-12-12 10:38:11 +00:00
if ( exitScheduled )
DoFCEUExit ( ) ;
2020-12-12 11:59:07 +00:00
# endif
2008-11-02 19:10:53 +00:00
}
/**
* Loads and runs the given Lua script .
* The emulator MUST be paused for this function to be
* called . Otherwise , all frame boundary assumptions go out the window .
*
* Returns true on success , false on failure .
*/
2020-10-31 20:08:22 +00:00
int FCEU_LoadLuaCode ( const char * filename , const char * arg )
{
2013-03-20 14:46:55 +00:00
if ( ! DemandLua ( ) )
{
return 0 ;
}
2008-11-02 19:10:53 +00:00
if ( filename ! = luaScriptName )
{
if ( luaScriptName ) free ( luaScriptName ) ;
luaScriptName = strdup ( filename ) ;
}
2010-05-01 18:48:42 +00:00
std : : string getfilepath = filename ;
getfilepath = getfilepath . substr ( 0 , getfilepath . find_last_of ( " / \\ " ) + 1 ) ;
SetCurrentDir ( getfilepath . c_str ( ) ) ;
2008-11-02 19:10:53 +00:00
//stop any lua we might already have had running
FCEU_LuaStop ( ) ;
2010-05-01 16:42:06 +00:00
//Reinit the error count
luaexiterrorcount = 8 ;
2008-11-02 19:10:53 +00:00
if ( ! L ) {
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
L = lua_open ( ) ;
luaL_openlibs ( L ) ;
2021-02-07 17:23:31 +00:00
# if defined( __WIN_DRIVER__) && !defined(NEED_MINGW_HACKS)
2009-11-03 06:11:55 +00:00
iuplua_open ( L ) ;
iupcontrolslua_open ( L ) ;
2011-10-31 19:11:31 +00:00
luaopen_winapi ( L ) ;
2018-12-16 18:20:38 +00:00
imlua_open ( L ) ;
cdlua_open ( L ) ;
cdluaim_open ( L ) ;
2012-10-21 16:40:04 +00:00
2010-06-10 06:13:51 +00:00
//luasocket - yeah, have to open this in a weird way
lua_pushcfunction ( L , luaopen_socket_core ) ;
lua_setglobal ( L , " tmp " ) ;
luaL_dostring ( L , " package.preload[ \" socket.core \" ] = _G.tmp " ) ;
lua_pushcfunction ( L , luaopen_mime_core ) ;
lua_setglobal ( L , " tmp " ) ;
luaL_dostring ( L , " package.preload[ \" mime.core \" ] = _G.tmp " ) ;
2009-11-03 06:11:55 +00:00
# endif
2008-11-02 19:10:53 +00:00
2009-10-11 05:09:17 +00:00
luaL_register ( L , " emu " , emulib ) ; // added for better cross-emulator compatibility
luaL_register ( L , " FCEU " , emulib ) ; // kept for backward compatibility
2008-11-02 19:10:53 +00:00
luaL_register ( L , " memory " , memorylib ) ;
2016-12-22 15:15:38 +00:00
luaL_register ( L , " ppu " , ppulib ) ;
2008-11-02 20:11:14 +00:00
luaL_register ( L , " rom " , romlib ) ;
2008-11-02 19:10:53 +00:00
luaL_register ( L , " joypad " , joypadlib ) ;
2009-03-08 18:10:07 +00:00
luaL_register ( L , " zapper " , zapperlib ) ;
2009-03-08 22:36:38 +00:00
luaL_register ( L , " input " , inputlib ) ;
2019-06-27 07:14:12 +00:00
lua_settop ( L , 0 ) ; // clean the stack, because each call to luaL_register leaves a table on top (eventually overflows)
2008-11-02 19:10:53 +00:00
luaL_register ( L , " savestate " , savestatelib ) ;
luaL_register ( L , " movie " , movielib ) ;
luaL_register ( L , " gui " , guilib ) ;
2011-02-26 15:20:30 +00:00
luaL_register ( L , " sound " , soundlib ) ;
2013-05-26 15:18:30 +00:00
luaL_register ( L , " debugger " , debuggerlib ) ;
2017-02-23 21:29:43 +00:00
luaL_register ( L , " cdlog " , cdloglib ) ;
2011-12-25 21:54:31 +00:00
luaL_register ( L , " taseditor " , taseditorlib ) ;
2009-10-08 03:45:47 +00:00
luaL_register ( L , " bit " , bit_funcs ) ; // LuaBitOp library
2019-06-27 07:14:12 +00:00
lua_settop ( L , 0 ) ;
2009-10-08 03:45:47 +00:00
2009-10-19 03:01:26 +00:00
// register a few utility functions outside of libraries (in the global namespace)
lua_register ( L , " print " , print ) ;
2015-06-23 21:04:31 +00:00
lua_register ( L , " gethash " , gethash ) ,
2009-10-19 03:01:26 +00:00
lua_register ( L , " tostring " , tostring ) ;
2013-09-15 18:37:11 +00:00
lua_register ( L , " tobitstring " , tobitstring ) ;
2009-10-19 03:01:26 +00:00
lua_register ( L , " addressof " , addressof ) ;
lua_register ( L , " copytable " , copytable ) ;
2009-10-08 03:45:47 +00:00
// old bit operation functions
lua_register ( L , " AND " , bit_band ) ;
lua_register ( L , " OR " , bit_bor ) ;
lua_register ( L , " XOR " , bit_bxor ) ;
lua_register ( L , " SHIFT " , bit_bshift_emulua ) ;
2018-11-15 00:44:18 +00:00
lua_register ( L , " BIT " , bitbit ) ;
2008-11-02 19:10:53 +00:00
2010-02-02 22:12:07 +00:00
if ( arg )
{
luaL_Buffer b ;
luaL_buffinit ( L , & b ) ;
luaL_addstring ( & b , arg ) ;
luaL_pushresult ( & b ) ;
lua_setglobal ( L , " arg " ) ;
}
2009-10-08 03:45:47 +00:00
luabitop_validate ( L ) ;
2008-11-02 19:10:53 +00:00
2009-10-11 08:34:10 +00:00
// push arrays for storing hook functions in
for ( int i = 0 ; i < LUAMEMHOOK_COUNT ; i + + )
{
lua_newtable ( L ) ;
lua_setfield ( L , LUA_REGISTRYINDEX , luaMemHookTypeStrings [ i ] ) ;
}
2008-11-02 19:10:53 +00:00
}
// We make our thread NOW because we want it at the bottom of the stack.
// If all goes wrong, we let the garbage collector remove it.
lua_State * thread = lua_newthread ( L ) ;
2012-10-21 16:40:04 +00:00
// Load the data
2008-11-02 19:10:53 +00:00
int result = luaL_loadfile ( L , filename ) ;
if ( result ) {
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2008-11-02 19:10:53 +00:00
// Doing this here caused nasty problems; reverting to MessageBox-from-dialog behavior.
2018-11-16 02:49:19 +00:00
//StopSound();//StopSound(); //mbg merge 7/23/08
2008-11-02 19:10:53 +00:00
MessageBox ( NULL , lua_tostring ( L , - 1 ) , " Lua load error " , MB_OK | MB_ICONSTOP ) ;
# else
2020-10-31 16:24:52 +00:00
LuaPrintfToWindowConsole ( " Failed to compile file: %s \n " , lua_tostring ( L , - 1 ) ) ;
2008-11-02 19:10:53 +00:00
fprintf ( stderr , " Failed to compile file: %s \n " , lua_tostring ( L , - 1 ) ) ;
# endif
// Wipe the stack. Our thread
2018-11-12 22:33:42 +00:00
if ( L )
lua_settop ( L , 0 ) ;
2008-11-02 19:10:53 +00:00
return 0 ; // Oh shit.
}
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2008-12-28 01:36:02 +00:00
AddRecentLuaFile ( filename ) ; //Add the filename to our recent lua menu
# endif
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// Get our function into it
lua_xmove ( L , thread , 1 ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// Save the thread to the registry. This is why I make the thread FIRST.
lua_setfield ( L , LUA_REGISTRYINDEX , frameAdvanceThread ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
// Initialize settings
luaRunning = TRUE ;
skipRerecords = FALSE ;
2009-10-11 08:34:10 +00:00
numMemHooks = 0 ;
2009-10-14 22:43:12 +00:00
transparencyModifier = 255 ; // opaque
2008-11-02 19:10:53 +00:00
2010-04-07 21:45:06 +00:00
//wasPaused = FCEUI_EmulationPaused();
//if (wasPaused) FCEUI_ToggleEmulationPause();
2008-11-02 19:10:53 +00:00
// Set up our protection hook to be executed once every 10,000 bytecode instructions.
//lua_sethook(thread, FCEU_LuaHookFunction, LUA_MASKCOUNT, 10000);
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2009-10-19 03:01:26 +00:00
info_print = PrintToWindowConsole ;
info_onstart = WinLuaOnStart ;
info_onstop = WinLuaOnStop ;
if ( ! LuaConsoleHWnd )
2019-06-21 18:03:05 +00:00
LuaConsoleHWnd = CreateDialog ( fceu_hInstance , MAKEINTRESOURCE ( IDD_LUA ) , hAppWnd , DlgLuaScriptDialog ) ;
2019-07-23 19:12:03 +00:00
info_uid = ( intptr_t ) LuaConsoleHWnd ;
2009-10-19 03:01:26 +00:00
# else
2020-07-20 01:15:56 +00:00
info_print = PrintToWindowConsole ;
info_onstart = WinLuaOnStart ;
info_onstop = WinLuaOnStop ;
info_uid = ( intptr_t ) 0 ;
2009-10-19 03:01:26 +00:00
# endif
if ( info_onstart )
info_onstart ( info_uid ) ;
2019-01-28 01:45:23 +00:00
// And run it right now. :)
FCEU_LuaFrameBoundary ( ) ;
2008-11-02 19:10:53 +00:00
// We're done.
return 1 ;
}
/**
* Equivalent to repeating the last FCEU_LoadLuaCode ( ) call .
*/
void FCEU_ReloadLuaCode ( )
{
if ( ! luaScriptName )
2013-05-26 15:18:30 +00:00
{
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2018-11-15 00:44:18 +00:00
// no script currently running, then try loading the most recent
2013-05-26 15:18:30 +00:00
extern char * recent_lua [ ] ;
char * & fname = recent_lua [ 0 ] ;
extern void UpdateLuaConsole ( const char * fname ) ;
if ( fname )
{
UpdateLuaConsole ( fname ) ;
FCEU_LoadLuaCode ( fname ) ;
} else
{
FCEU_DispMessage ( " There's no script to reload. " , 0 ) ;
}
# else
FCEU_DispMessage ( " There's no script to reload. " , 0 ) ;
# endif
} else
{
2008-11-02 19:10:53 +00:00
FCEU_LoadLuaCode ( luaScriptName ) ;
2013-05-26 15:18:30 +00:00
}
2008-11-02 19:10:53 +00:00
}
/**
* Terminates a running Lua script by killing the whole Lua engine .
*
* Always safe to call , except from within a lua call itself ( duh ) .
*
*/
void FCEU_LuaStop ( ) {
2013-03-20 22:17:14 +00:00
if ( ! CheckLua ( ) )
2013-03-20 14:46:55 +00:00
return ;
2008-11-02 19:10:53 +00:00
//already killed
if ( ! L ) return ;
2010-05-01 16:42:06 +00:00
// Since the script is exiting, we want to prevent an infinite loop.
// CallExitFunction() > HandleCallbackError() > FCEU_LuaStop() > CallExitFunction() ...
if ( luaexiterrorcount > 0 ) {
luaexiterrorcount = luaexiterrorcount - 1 ;
//execute the user's shutdown callbacks
CallExitFunction ( ) ;
}
luaexiterrorcount = luaexiterrorcount + 1 ;
//already killed (after multiple errors)
if ( ! L ) return ;
2008-11-02 19:10:53 +00:00
2009-10-11 08:34:10 +00:00
/*info.*/ numMemHooks = 0 ;
for ( int i = 0 ; i < LUAMEMHOOK_COUNT ; i + + )
CalculateMemHookRegions ( ( LuaMemHookType ) i ) ;
2008-11-02 19:10:53 +00:00
//sometimes iup uninitializes com
//MBG TODO - test whether this is really necessary. i dont think it is
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2008-11-02 19:10:53 +00:00
CoInitialize ( 0 ) ;
# endif
2009-10-19 03:01:26 +00:00
if ( info_onstop )
info_onstop ( info_uid ) ;
2008-11-02 19:10:53 +00:00
//lua_gc(L,LUA_GCCOLLECT,0);
lua_close ( L ) ; // this invokes our garbage collectors for us
L = NULL ;
FCEU_LuaOnStop ( ) ;
}
/**
* Returns true if there is a Lua script running .
*
*/
int FCEU_LuaRunning ( ) {
2009-10-19 03:01:26 +00:00
// FIXME: return false when no callback functions are registered.
return ( int ) ( L ! = NULL ) ; // should return true if callback functions are active.
2008-11-02 19:10:53 +00:00
}
2019-12-15 20:31:09 +00:00
/**
* Applies zapper . set overrides to zapper input .
*/
void FCEU_LuaReadZapper ( const uint32 * mouse_in , uint32 * mouse_out )
{
mouse_out [ 0 ] = luazapperx > = 0 ? luazapperx : mouse_in [ 0 ] ;
mouse_out [ 1 ] = luazappery > = 0 ? luazappery : mouse_in [ 1 ] ;
mouse_out [ 2 ] = luazapperfire > = 0 ? ( luazapperfire | ( mouse_in [ 2 ] & ~ 1 ) ) : mouse_in [ 2 ] ;
}
2008-11-02 19:10:53 +00:00
/**
* Returns true if Lua would like to steal the given joypad control .
*/
2009-09-15 16:39:49 +00:00
//int FCEU_LuaUsingJoypad(int which) {
// return lua_joypads_used & (1 << which);
//}
2008-11-02 19:10:53 +00:00
2009-03-14 21:00:28 +00:00
//adelikat: TODO: should I have a FCEU_LuaUsingJoypadFalse?
2008-11-02 19:10:53 +00:00
/**
* Reads the buttons Lua is feeding for the given joypad , in the same
* format as the OS - specific code .
*
2009-09-15 16:39:49 +00:00
* It may force set or force clear the buttons . It may also simply
* pass the input along or invert it . The act of calling this
* function will reset everything back to pass - through , though .
* Generally means don ' t call it more than once per frame !
2008-11-02 19:10:53 +00:00
*/
2009-09-15 16:39:49 +00:00
uint8 FCEU_LuaReadJoypad ( int which , uint8 joyl ) {
joyl = ( joyl & luajoypads1 [ which ] ) | ( ~ joyl & luajoypads2 [ which ] ) ;
luajoypads1 [ which ] = 0xFF ;
luajoypads2 [ which ] = 0x00 ;
return joyl ;
2008-11-02 19:10:53 +00:00
}
2009-03-14 21:00:28 +00:00
//adelikat: Returns the buttons that will be specifically set to false (as opposed to on or nil)
//This will be used in input.cpp to &(and) against the input to override a button with a false value. This is a work around to allow 3 conditions to be sent be lua, true, false, nil
2009-09-15 16:39:49 +00:00
//uint8 FCEU_LuaReadJoypadFalse(int which) {
// lua_joypads_used_false &= ~(1 << which);
// return lua_joypads_false[which];
//}
2009-03-14 21:00:28 +00:00
2008-11-02 19:10:53 +00:00
/**
* If this function returns true , the movie code should NOT increment
* the rerecord count for a load - state .
*
* This function will not return true if a script is not running .
*/
int FCEU_LuaRerecordCountSkip ( ) {
2009-10-19 03:01:26 +00:00
// FIXME: return true if (there are any active callback functions && skipRerecords)
2008-11-02 19:10:53 +00:00
return L & & luaRunning & & skipRerecords ;
}
/**
* Given an 8 - bit screen with the indicated resolution ,
* draw the current GUI onto it .
*
* Currently we only support 256 x * resolutions .
*/
2012-09-23 12:45:28 +00:00
void FCEU_LuaGui ( uint8 * XBuf )
{
2009-10-19 03:01:26 +00:00
if ( ! L /* || !luaRunning*/ )
2008-11-02 19:10:53 +00:00
return ;
// First, check if we're being called by anybody
lua_getfield ( L , LUA_REGISTRYINDEX , guiCallbackTable ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
if ( lua_isfunction ( L , - 1 ) ) {
// We call it now
numTries = 1000 ;
int ret = lua_pcall ( L , 0 , 0 , 0 ) ;
if ( ret ! = 0 ) {
2021-02-07 17:23:31 +00:00
# ifdef __WIN_DRIVER__
2008-11-02 19:10:53 +00:00
//StopSound();//StopSound(); //mbg merge 7/23/08
MessageBox ( hAppWnd , lua_tostring ( L , - 1 ) , " Lua Error in GUI function " , MB_OK ) ;
# else
2020-10-31 16:24:52 +00:00
LuaPrintfToWindowConsole ( " Lua error in gui.register function: %s \n " , lua_tostring ( L , - 1 ) ) ;
2008-11-02 19:10:53 +00:00
fprintf ( stderr , " Lua error in gui.register function: %s \n " , lua_tostring ( L , - 1 ) ) ;
# endif
// This is grounds for trashing the function
lua_pushnil ( L ) ;
lua_setfield ( L , LUA_REGISTRYINDEX , guiCallbackTable ) ;
2012-10-21 16:40:04 +00:00
2008-11-02 19:10:53 +00:00
}
}
// And wreak the stack
lua_settop ( L , 0 ) ;
if ( gui_used = = GUI_CLEAR )
return ;
2012-09-23 12:45:28 +00:00
if ( gui_used = = GUI_USED_SINCE_LAST_FRAME & & ! FCEUI_EmulationPaused ( ) )
{
memset ( gui_data , 0 , LUA_SCREEN_WIDTH * LUA_SCREEN_HEIGHT * 4 ) ;
gui_used = GUI_CLEAR ;
return ;
}
2008-11-02 19:10:53 +00:00
gui_used = GUI_USED_SINCE_LAST_FRAME ;
2009-10-12 07:32:31 +00:00
int x , y ;
2008-11-02 19:10:53 +00:00
2012-09-23 12:45:28 +00:00
for ( y = 0 ; y < LUA_SCREEN_HEIGHT ; y + + )
{
for ( x = 0 ; x < LUA_SCREEN_WIDTH ; x + + )
{
2009-10-12 07:32:31 +00:00
const uint8 gui_alpha = gui_data [ ( y * LUA_SCREEN_WIDTH + x ) * 4 + 3 ] ;
2012-09-23 12:45:28 +00:00
if ( gui_alpha = = 0 )
{
2009-10-12 22:43:26 +00:00
// do nothing
continue ;
}
2009-10-12 07:32:31 +00:00
const uint8 gui_red = gui_data [ ( y * LUA_SCREEN_WIDTH + x ) * 4 + 2 ] ;
const uint8 gui_green = gui_data [ ( y * LUA_SCREEN_WIDTH + x ) * 4 + 1 ] ;
const uint8 gui_blue = gui_data [ ( y * LUA_SCREEN_WIDTH + x ) * 4 ] ;
int r , g , b ;
2009-10-12 22:43:26 +00:00
if ( gui_alpha = = 255 ) {
2009-10-12 07:32:31 +00:00
// direct copy
r = gui_red ;
g = gui_green ;
b = gui_blue ;
2008-11-02 19:10:53 +00:00
}
2009-10-12 07:32:31 +00:00
else {
// alpha-blending
2009-10-12 22:43:26 +00:00
uint8 scr_red , scr_green , scr_blue ;
2011-07-01 04:01:20 +00:00
FCEUD_GetPalette ( XBuf [ ( y ) * 256 + x ] , & scr_red , & scr_green , & scr_blue ) ;
2009-10-12 07:32:31 +00:00
r = ( ( ( int ) gui_red - scr_red ) * gui_alpha / 255 + scr_red ) & 255 ;
g = ( ( ( int ) gui_green - scr_green ) * gui_alpha / 255 + scr_green ) & 255 ;
b = ( ( ( int ) gui_blue - scr_blue ) * gui_alpha / 255 + scr_blue ) & 255 ;
}
2010-12-18 01:07:10 +00:00
XBuf [ ( y ) * 256 + x ] = gui_colour_rgb ( r , g , b ) ;
2008-11-02 19:10:53 +00:00
}
}
return ;
}
2009-10-19 03:01:26 +00:00
lua_State * FCEU_GetLuaState ( ) {
return L ;
}
char * FCEU_GetLuaScriptName ( ) {
return luaScriptName ;
}