rlm@1: #include rlm@1: #include rlm@1: #include rlm@1: #include rlm@1: #include rlm@1: #include rlm@1: #include rlm@1: #include rlm@1: rlm@1: #include rlm@1: #include rlm@1: #include rlm@1: #include rlm@1: rlm@1: using namespace std; rlm@1: rlm@1: #ifdef __linux rlm@1: #include // for unlink rlm@1: #include rlm@1: #include rlm@1: #endif rlm@1: #if (defined(WIN32) && !defined(SDL)) rlm@1: #include rlm@1: #include "../win32/stdafx.h" rlm@1: #include "../win32/Input.h" rlm@1: #include "../win32/MainWnd.h" rlm@1: #include "../win32/VBA.h" rlm@1: #include "../win32/LuaOpenDialog.h" rlm@1: #else rlm@1: #define stricmp strcasecmp rlm@1: #define strnicmp strncasecmp rlm@1: #endif rlm@1: rlm@1: #include "../Port.h" rlm@1: #include "System.h" rlm@1: #include "movie.h" rlm@1: #include "../gba/GBA.h" rlm@1: #include "../gba/GBAGlobals.h" rlm@1: #include "../gb/GB.h" rlm@1: #include "../gb/gbGlobals.h" rlm@1: #include "../gba/GBASound.h" rlm@1: rlm@1: #ifdef _WIN32 rlm@1: #include "../win32/Sound.h" rlm@1: //#include "../win32/WinMiscUtil.h" rlm@1: extern CString winGetSavestateFilename(const CString &LogicalRomName, int nID); rlm@1: #else rlm@1: #endif rlm@1: rlm@1: extern "C" rlm@1: { rlm@1: #include "../lua/src/lua.h" rlm@1: #include "../lua/src/lauxlib.h" rlm@1: #include "../lua/src/lualib.h" rlm@1: #include "../lua/src/lstate.h" rlm@1: } rlm@1: #include "vbalua.h" rlm@1: rlm@1: #include "../SFMT/SFMT.c" rlm@1: rlm@1: static void (*info_print)(int uid, const char *str); rlm@1: static void (*info_onstart)(int uid); rlm@1: static void (*info_onstop)(int uid); rlm@1: static int info_uid; rlm@1: rlm@1: #ifndef countof rlm@1: #define countof(a) (sizeof(a) / sizeof(a[0])) rlm@1: #endif rlm@1: rlm@1: static lua_State *LUA; rlm@1: rlm@1: // Are we running any code right now? rlm@1: static char *luaScriptName = NULL; rlm@1: rlm@1: // Are we running any code right now? rlm@1: static bool8 luaRunning = false; rlm@1: rlm@1: // True at the frame boundary, false otherwise. rlm@1: static bool8 frameBoundary = false; rlm@1: rlm@1: // The execution speed we're running at. rlm@1: static enum { SPEED_NORMAL, SPEED_NOTHROTTLE, SPEED_TURBO, SPEED_MAXIMUM } speedmode = SPEED_NORMAL; rlm@1: rlm@1: // Rerecord count skip mode rlm@1: static bool8 skipRerecords = false; rlm@1: rlm@1: // Used by the registry to find our functions rlm@1: static const char *frameAdvanceThread = "VBA.FrameAdvance"; rlm@1: static const char *guiCallbackTable = "VBA.GUI"; rlm@1: rlm@1: // True if there's a thread waiting to run after a run of frame-advance. rlm@1: static bool8 frameAdvanceWaiting = false; rlm@1: rlm@1: // We save our pause status in the case of a natural death. rlm@1: //static bool8 wasPaused = false; rlm@1: rlm@1: // Transparency strength. 255=opaque, 0=so transparent it's invisible rlm@1: static int transparencyModifier = 255; rlm@1: rlm@1: // Our joypads. rlm@1: static uint32 lua_joypads[4]; rlm@1: static uint8 lua_joypads_used = 0; rlm@1: rlm@1: static bool8 gui_used = false; rlm@1: static uint8 *gui_data = NULL; // BGRA rlm@1: rlm@1: // Protects Lua calls from going nuts. rlm@1: // We set this to a big number like 1000 and decrement it rlm@1: // over time. The script gets knifed once this reaches zero. rlm@1: static int numTries; rlm@1: rlm@1: // number of registered memory functions (1 per hooked byte) rlm@1: static unsigned int numMemHooks; rlm@1: rlm@1: // Look in inputglobal.h for macros named like BUTTON_MASK_UP to determine the order. rlm@1: static const char *button_mappings[] = { rlm@1: "A", "B", "select", "start", "right", "left", "up", "down", "R", "L" rlm@1: }; rlm@1: rlm@1: #ifdef _MSC_VER rlm@1: #define snprintf _snprintf rlm@1: #define vscprintf _vscprintf rlm@1: #else rlm@1: #define stricmp strcasecmp rlm@1: #define strnicmp strncasecmp rlm@1: #define __forceinline __attribute__((always_inline)) rlm@1: #endif rlm@1: rlm@1: static const char *luaCallIDStrings[] = rlm@1: { rlm@1: "CALL_BEFOREEMULATION", rlm@1: "CALL_AFTEREMULATION", rlm@1: "CALL_BEFOREEXIT" rlm@1: }; rlm@1: rlm@1: //make sure we have the right number of strings rlm@1: CTASSERT(sizeof(luaCallIDStrings) / sizeof(*luaCallIDStrings) == LUACALL_COUNT) rlm@1: rlm@1: static const char *luaMemHookTypeStrings [] = rlm@1: { rlm@1: "MEMHOOK_WRITE", rlm@1: "MEMHOOK_READ", rlm@1: "MEMHOOK_EXEC", rlm@1: rlm@1: "MEMHOOK_WRITE_SUB", rlm@1: "MEMHOOK_READ_SUB", rlm@1: "MEMHOOK_EXEC_SUB", rlm@1: }; rlm@1: rlm@1: //make sure we have the right number of strings rlm@1: CTASSERT(sizeof(luaMemHookTypeStrings) / sizeof(*luaMemHookTypeStrings) == LUAMEMHOOK_COUNT) rlm@1: rlm@1: static char *rawToCString(lua_State * L, int idx = 0); rlm@1: static const char *toCString(lua_State *L, int idx = 0); rlm@1: rlm@1: // GBA memory I/O functions copied from win32/MemoryViewerDlg.cpp rlm@1: static inline u8 CPUReadByteQuick(u32 addr) rlm@1: { rlm@1: return ::map[addr >> 24].address[addr & ::map[addr >> 24].mask]; rlm@1: } rlm@1: rlm@1: static inline void CPUWriteByteQuick(u32 addr, u8 b) rlm@1: { rlm@1: ::map[addr >> 24].address[addr & ::map[addr >> 24].mask] = b; rlm@1: } rlm@1: rlm@1: static inline u16 CPUReadHalfWordQuick(u32 addr) rlm@1: { rlm@1: return *((u16 *) &::map[addr >> 24].address[addr & ::map[addr >> 24].mask]); rlm@1: } rlm@1: rlm@1: static inline void CPUWriteHalfWordQuick(u32 addr, u16 b) rlm@1: { rlm@1: *((u16 *) &::map[addr >> 24].address[addr & ::map[addr >> 24].mask]) = b; rlm@1: } rlm@1: rlm@1: static inline u32 CPUReadMemoryQuick(u32 addr) rlm@1: { rlm@1: return *((u32 *) &::map[addr >> 24].address[addr & ::map[addr >> 24].mask]); rlm@1: } rlm@1: rlm@1: static inline void CPUWriteMemoryQuick(u32 addr, u32 b) rlm@1: { rlm@1: *((u32 *) &::map[addr >> 24].address[addr & ::map[addr >> 24].mask]) = b; rlm@1: } rlm@1: rlm@1: // GB rlm@1: static inline u8 gbReadMemoryQuick8(u16 addr) rlm@1: { rlm@1: return gbReadMemoryQuick(addr); rlm@1: } rlm@1: rlm@1: static inline void gbWriteMemoryQuick8(u16 addr, u8 b) rlm@1: { rlm@1: gbWriteMemoryQuick(addr, b); rlm@1: } rlm@1: rlm@1: static inline u16 gbReadMemoryQuick16(u16 addr) rlm@1: { rlm@1: return (gbReadMemoryQuick(addr + 1) << 8) | gbReadMemoryQuick(addr); rlm@1: } rlm@1: rlm@1: static inline void gbWriteMemoryQuick16(u16 addr, u16 b) rlm@1: { rlm@1: gbWriteMemoryQuick(addr, b & 0xff); rlm@1: gbWriteMemoryQuick(addr + 1, (b >> 8) & 0xff); rlm@1: } rlm@1: rlm@1: static inline u32 gbReadMemoryQuick32(u16 addr) rlm@1: { rlm@1: return (gbReadMemoryQuick(addr + 3) << 24) | rlm@1: (gbReadMemoryQuick(addr + 2) << 16) | rlm@1: (gbReadMemoryQuick(addr + 1) << 8) | rlm@1: gbReadMemoryQuick(addr); rlm@1: } rlm@1: rlm@1: static inline void gbWriteMemoryQuick32(u16 addr, u32 b) rlm@1: { rlm@1: gbWriteMemoryQuick(addr, b & 0xff); rlm@1: gbWriteMemoryQuick(addr + 1, (b >> 8) & 0xff); rlm@1: gbWriteMemoryQuick(addr + 2, (b >> 16) & 0xff); rlm@1: gbWriteMemoryQuick(addr + 1, (b >> 24) & 0xff); rlm@1: } rlm@1: rlm@1: static inline u8 gbReadROMQuick8(u32 addr) rlm@1: { rlm@1: return gbReadROMQuick(addr & gbRomSizeMask); rlm@1: } rlm@1: rlm@1: static inline u8 gbReadROMQuick16(u32 addr) rlm@1: { rlm@1: return (gbReadROMQuick(addr+1 & gbRomSizeMask) << 8) | gbReadROMQuick(addr & gbRomSizeMask); rlm@1: } rlm@1: rlm@1: static inline u8 gbReadROMQuick32(u32 addr) rlm@1: { rlm@1: return (gbReadROMQuick(addr+3 & gbRomSizeMask) << 24) | rlm@1: (gbReadROMQuick(addr+2 & gbRomSizeMask) << 16) | rlm@1: (gbReadROMQuick(addr+1 & gbRomSizeMask) << 8) | rlm@1: gbReadROMQuick(addr & gbRomSizeMask); rlm@1: } rlm@1: rlm@1: typedef void (*GetColorFunc)(const uint8 *, uint8 *, uint8 *, uint8 *); rlm@1: typedef void (*SetColorFunc)(uint8 *, uint8, uint8, uint8); rlm@1: rlm@1: static void getColor16(const uint8 *s, uint8 *r, uint8 *g, uint8 *b) rlm@1: { rlm@1: u16 v = *(const uint16 *)s; rlm@1: *r = ((v >> systemBlueShift) & 0x001f) << 3; rlm@1: *g = ((v >> systemGreenShift) & 0x001f) << 3; rlm@1: *b = ((v >> systemRedShift) & 0x001f) << 3; rlm@1: } rlm@1: rlm@1: static void getColor24(const uint8 *s, uint8 *r, uint8 *g, uint8 *b) rlm@1: { rlm@1: if (systemRedShift > systemBlueShift) rlm@1: *b = s[0], *g = s[1], *r = s[2]; rlm@1: else rlm@1: *r = s[0], *g = s[1], *b = s[2]; rlm@1: } rlm@1: rlm@1: static void getColor32(const uint8 *s, uint8 *r, uint8 *g, uint8 *b) rlm@1: { rlm@1: u32 v = *(const uint32 *)s; rlm@1: *b = ((v >> systemBlueShift) & 0x001f) << 3; rlm@1: *g = ((v >> systemGreenShift) & 0x001f) << 3; rlm@1: *r = ((v >> systemRedShift) & 0x001f) << 3; rlm@1: } rlm@1: rlm@1: static void setColor16(uint8 *s, uint8 r, uint8 g, uint8 b) rlm@1: { rlm@1: *(uint16 *)s = ((b >> 3) & 0x01f) << rlm@1: systemBlueShift | rlm@1: ((g >> 3) & 0x01f) << rlm@1: systemGreenShift | rlm@1: ((r >> 3) & 0x01f) << rlm@1: systemRedShift; rlm@1: } rlm@1: rlm@1: static void setColor24(uint8 *s, uint8 r, uint8 g, uint8 b) rlm@1: { rlm@1: if (systemRedShift > systemBlueShift) rlm@1: s[0] = b, s[1] = g, s[2] = r; rlm@1: else rlm@1: s[0] = r, s[1] = g, s[2] = b; rlm@1: } rlm@1: rlm@1: static void setColor32(uint8 *s, uint8 r, uint8 g, uint8 b) rlm@1: { rlm@1: *(uint32 *)s = ((b >> 3) & 0x01f) << rlm@1: systemBlueShift | rlm@1: ((g >> 3) & 0x01f) << rlm@1: systemGreenShift | rlm@1: ((r >> 3) & 0x01f) << rlm@1: systemRedShift; rlm@1: } rlm@1: rlm@1: static bool getColorIOFunc(int depth, GetColorFunc *getColor, SetColorFunc *setColor) rlm@1: { rlm@1: switch (depth) rlm@1: { rlm@1: case 16: rlm@1: if (getColor) rlm@1: *getColor = getColor16; rlm@1: if (setColor) rlm@1: *setColor = setColor16; rlm@1: return true; rlm@1: case 24: rlm@1: if (getColor) rlm@1: *getColor = getColor24; rlm@1: if (setColor) rlm@1: *setColor = setColor24; rlm@1: return true; rlm@1: case 32: rlm@1: if (getColor) rlm@1: *getColor = getColor32; rlm@1: if (setColor) rlm@1: *setColor = setColor32; rlm@1: return true; rlm@1: default: rlm@1: return false; rlm@1: } rlm@1: } rlm@1: rlm@1: /** rlm@1: * Resets emulator speed / pause states after script exit. rlm@1: */ rlm@1: static void VBALuaOnStop(void) rlm@1: { rlm@1: luaRunning = false; rlm@1: lua_joypads_used = 0; rlm@1: gui_used = false; rlm@1: //if (wasPaused) rlm@1: // systemSetPause(true); rlm@1: } rlm@1: rlm@1: /** rlm@1: * Asks Lua if it wants control of the emulator's speed. rlm@1: * Returns 0 if no, 1 if yes. If yes, we also tamper with the rlm@1: * IPPU's settings for speed ourselves, so the calling code rlm@1: * need not do anything. rlm@1: */ rlm@1: int VBALuaSpeed(void) rlm@1: { rlm@1: if (!LUA || !luaRunning) rlm@1: return 0; rlm@1: rlm@1: //printf("%d\n", speedmode); rlm@1: switch (speedmode) rlm@1: { rlm@1: /* rlm@1: case SPEED_NORMAL: rlm@1: return 0; rlm@1: case SPEED_NOTHROTTLE: rlm@1: IPPU.RenderThisFrame = true; rlm@1: return 1; rlm@1: rlm@1: case SPEED_TURBO: rlm@1: IPPU.SkippedFrames++; rlm@1: if (IPPU.SkippedFrames >= 40) { rlm@1: IPPU.SkippedFrames = 0; rlm@1: IPPU.RenderThisFrame = true; rlm@1: } rlm@1: else rlm@1: IPPU.RenderThisFrame = false; rlm@1: return 1; rlm@1: rlm@1: // In mode 3, SkippedFrames is set to zero so that the frame rlm@1: // skipping code doesn't try anything funny. rlm@1: case SPEED_MAXIMUM: rlm@1: IPPU.SkippedFrames=0; rlm@1: IPPU.RenderThisFrame = false; rlm@1: return 1; rlm@1: */ rlm@1: case 0: // FIXME: to get rid of the warning rlm@1: default: rlm@1: assert(false); rlm@1: return 0; rlm@1: } rlm@1: } rlm@1: rlm@1: /////////////////////////// rlm@1: // vba.speedmode(string mode) rlm@1: // rlm@1: // Takes control of the emulation speed rlm@1: // of the system. Normal is normal speed (60fps, 50 for PAL), rlm@1: // nothrottle disables speed control but renders every frame, rlm@1: // turbo renders only a few frames in order to speed up emulation, rlm@1: rlm@1: // maximum renders no frames rlm@1: static int vba_speedmode(lua_State *L) rlm@1: { rlm@1: const char *mode = luaL_checkstring(L, 1); rlm@1: rlm@1: if (strcasecmp(mode, "normal") == 0) rlm@1: { rlm@1: speedmode = SPEED_NORMAL; rlm@1: } rlm@1: else if (strcasecmp(mode, "nothrottle") == 0) rlm@1: { rlm@1: speedmode = SPEED_NOTHROTTLE; rlm@1: } rlm@1: else if (strcasecmp(mode, "turbo") == 0) rlm@1: { rlm@1: speedmode = SPEED_TURBO; rlm@1: } rlm@1: else if (strcasecmp(mode, "maximum") == 0) rlm@1: { rlm@1: speedmode = SPEED_MAXIMUM; rlm@1: } rlm@1: else rlm@1: luaL_error(L, "Invalid mode %s to vba.speedmode", mode); rlm@1: rlm@1: //printf("new speed mode: %d\n", speedmode); rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // vba.frameadvnace() rlm@1: // rlm@1: // Executes a frame advance. Occurs by yielding the coroutine, then re-running rlm@1: rlm@1: // when we break out. rlm@1: static int vba_frameadvance(lua_State *L) rlm@1: { rlm@1: // We're going to sleep for a frame-advance. Take notes. rlm@1: if (frameAdvanceWaiting) rlm@1: return luaL_error(L, "can't call vba.frameadvance() from here"); rlm@1: rlm@1: frameAdvanceWaiting = true; rlm@1: rlm@1: // Don't do this! The user won't like us sending their emulator out of control! rlm@1: // Settings.FrameAdvance = true; rlm@1: // Now we can yield to the main rlm@1: return lua_yield(L, 0); rlm@1: rlm@1: // It's actually rather disappointing... rlm@1: } rlm@1: rlm@1: // vba.pause() rlm@1: // rlm@1: // Pauses the emulator, function "waits" until the user unpauses. rlm@1: // This function MAY be called from a non-frame boundary, but the frame rlm@1: rlm@1: // finishes executing anwyays. In this case, the function returns immediately. rlm@1: static int vba_pause(lua_State *L) rlm@1: { rlm@1: systemSetPause(true); rlm@1: speedmode = SPEED_NORMAL; rlm@1: rlm@1: // Return control if we're midway through a frame. We can't pause here. rlm@1: if (frameAdvanceWaiting) rlm@1: { rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // If it's on a frame boundary, we also yield. rlm@1: frameAdvanceWaiting = true; rlm@1: return lua_yield(L, 0); rlm@1: } rlm@1: rlm@1: static int vba_registerbefore(lua_State *L) rlm@1: { rlm@1: if (!lua_isnil(L, 1)) rlm@1: luaL_checktype(L, 1, LUA_TFUNCTION); rlm@1: lua_settop(L, 1); rlm@1: lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEMULATION]); rlm@1: lua_insert(L, 1); rlm@1: lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEMULATION]); rlm@1: rlm@1: //StopScriptIfFinished(luaStateToUIDMap[L]); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int vba_registerafter(lua_State *L) rlm@1: { rlm@1: if (!lua_isnil(L, 1)) rlm@1: luaL_checktype(L, 1, LUA_TFUNCTION); rlm@1: lua_settop(L, 1); rlm@1: lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTEREMULATION]); rlm@1: lua_insert(L, 1); rlm@1: lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTEREMULATION]); rlm@1: rlm@1: //StopScriptIfFinished(luaStateToUIDMap[L]); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int vba_registerexit(lua_State *L) rlm@1: { rlm@1: if (!lua_isnil(L, 1)) rlm@1: luaL_checktype(L, 1, LUA_TFUNCTION); rlm@1: lua_settop(L, 1); rlm@1: lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]); rlm@1: lua_insert(L, 1); rlm@1: lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]); rlm@1: rlm@1: //StopScriptIfFinished(luaStateToUIDMap[L]); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static inline bool isalphaorunderscore(char c) rlm@1: { rlm@1: return isalpha(c) || c == '_'; rlm@1: } rlm@1: rlm@1: static std::vector s_tableAddressStack; // prevents infinite recursion of a table within a table (when cycle is rlm@1: // found, print something like table:parent) rlm@1: static std::vector s_metacallStack; // prevents infinite recursion if something's __tostring returns another table rlm@1: // that contains that something (when cycle is found, print the inner result rlm@1: // without using __tostring) rlm@1: rlm@1: #define APPENDPRINT { int _n = snprintf(ptr, remaining, rlm@1: #define END ); if (_n >= 0) { ptr += _n; remaining -= _n; } else { remaining = 0; } } rlm@1: static void toCStringConverter(lua_State *L, int i, char * &ptr, int &remaining) rlm@1: { rlm@1: if (remaining <= 0) rlm@1: return; rlm@1: rlm@1: const char *str = ptr; // for debugging rlm@1: rlm@1: // if there is a __tostring metamethod then call it rlm@1: int usedMeta = luaL_callmeta(L, i, "__tostring"); rlm@1: if (usedMeta) rlm@1: { rlm@1: std::vector::const_iterator foundCycleIter = std::find(s_metacallStack.begin(), s_metacallStack.end(), lua_topointer(L, i)); rlm@1: if (foundCycleIter != s_metacallStack.end()) rlm@1: { rlm@1: lua_pop(L, 1); rlm@1: usedMeta = false; rlm@1: } rlm@1: else rlm@1: { rlm@1: s_metacallStack.push_back(lua_topointer(L, i)); rlm@1: i = lua_gettop(L); rlm@1: } rlm@1: } rlm@1: rlm@1: switch (lua_type(L, i)) rlm@1: { rlm@1: case LUA_TNONE: rlm@1: break; rlm@1: case LUA_TNIL: rlm@1: APPENDPRINT "nil" END break; rlm@1: case LUA_TBOOLEAN: rlm@1: APPENDPRINT lua_toboolean(L, i) ? "true" : "false" END break; rlm@1: case LUA_TSTRING : APPENDPRINT "%s", lua_tostring(L, i) END break; rlm@1: case LUA_TNUMBER: rlm@1: APPENDPRINT "%.12Lg", lua_tonumber(L, i) END break; rlm@1: case LUA_TFUNCTION: rlm@1: if ((L->base + i - 1)->value.gc->cl.c.isC) rlm@1: { rlm@1: //lua_CFunction func = lua_tocfunction(L, i); rlm@1: //std::map::iterator iter = s_cFuncInfoMap.find(func); rlm@1: //if(iter == s_cFuncInfoMap.end()) rlm@1: goto defcase; rlm@1: //APPENDPRINT "function(%s)", iter->second END rlm@1: } rlm@1: else rlm@1: { rlm@1: APPENDPRINT "function(" END rlm@1: Proto * p = (L->base + i - 1)->value.gc->cl.l.p; rlm@1: int numParams = p->numparams + (p->is_vararg ? 1 : 0); rlm@1: for (int n = 0; n < p->numparams; n++) rlm@1: { rlm@1: APPENDPRINT "%s", getstr(p->locvars[n].varname) END rlm@1: if (n != numParams - 1) rlm@1: APPENDPRINT "," END rlm@1: } rlm@1: if (p->is_vararg) rlm@1: APPENDPRINT "..." END rlm@1: APPENDPRINT ")" END rlm@1: } rlm@1: break; rlm@1: defcase: default: rlm@1: APPENDPRINT "%s:%p", luaL_typename(L, i), lua_topointer(L, i) END break; rlm@1: case LUA_TTABLE: rlm@1: { rlm@1: // first make sure there's enough stack space rlm@1: if (!lua_checkstack(L, 4)) rlm@1: { rlm@1: // note that even if lua_checkstack never returns false, rlm@1: // that doesn't mean we didn't need to call it, rlm@1: // because calling it retrieves stack space past LUA_MINSTACK rlm@1: goto defcase; rlm@1: } rlm@1: rlm@1: std::vector::const_iterator foundCycleIter = rlm@1: std::find(s_tableAddressStack.begin(), s_tableAddressStack.end(), lua_topointer(L, i)); rlm@1: if (foundCycleIter != s_tableAddressStack.end()) rlm@1: { rlm@1: int parentNum = s_tableAddressStack.end() - foundCycleIter; rlm@1: if (parentNum > 1) rlm@1: APPENDPRINT "%s:parent^%d", luaL_typename(L, i), parentNum END rlm@1: else rlm@1: APPENDPRINT "%s:parent", luaL_typename(L, i) END rlm@1: } rlm@1: else rlm@1: { rlm@1: s_tableAddressStack.push_back(lua_topointer(L, i)); rlm@1: struct Scope { ~Scope(){ s_tableAddressStack. pop_back(); } } scope; rlm@1: rlm@1: APPENDPRINT "{" END rlm@1: rlm@1: lua_pushnil(L); // first key rlm@1: int keyIndex = lua_gettop(L); rlm@1: int valueIndex = keyIndex + 1; rlm@1: bool first = true; rlm@1: bool skipKey = true; // true if we're still in the "array part" of the table rlm@1: lua_Number arrayIndex = (lua_Number)0; rlm@1: while (lua_next(L, i)) rlm@1: { rlm@1: if (first) rlm@1: first = false; rlm@1: else rlm@1: APPENDPRINT ", " END rlm@1: if (skipKey) rlm@1: { rlm@1: arrayIndex += (lua_Number)1; rlm@1: bool keyIsNumber = (lua_type(L, keyIndex) == LUA_TNUMBER); rlm@1: skipKey = keyIsNumber && (lua_tonumber(L, keyIndex) == arrayIndex); rlm@1: } rlm@1: if (!skipKey) rlm@1: { rlm@1: bool keyIsString = (lua_type(L, keyIndex) == LUA_TSTRING); rlm@1: bool invalidLuaIdentifier = (!keyIsString || !isalphaorunderscore(*lua_tostring(L, keyIndex))); rlm@1: if (invalidLuaIdentifier) rlm@1: if (keyIsString) rlm@1: APPENDPRINT "['" END rlm@1: else rlm@1: APPENDPRINT "[" END rlm@1: rlm@1: toCStringConverter(L, keyIndex, ptr, remaining); rlm@1: // key rlm@1: rlm@1: if (invalidLuaIdentifier) rlm@1: if (keyIsString) rlm@1: APPENDPRINT "']=" END rlm@1: else rlm@1: APPENDPRINT "]=" END rlm@1: else rlm@1: APPENDPRINT "=" END rlm@1: } rlm@1: rlm@1: bool valueIsString = (lua_type(L, valueIndex) == LUA_TSTRING); rlm@1: if (valueIsString) rlm@1: APPENDPRINT "'" END rlm@1: rlm@1: toCStringConverter(L, valueIndex, ptr, remaining); // value rlm@1: rlm@1: if (valueIsString) rlm@1: APPENDPRINT "'" END rlm@1: rlm@1: lua_pop(L, 1); rlm@1: rlm@1: if (remaining <= 0) rlm@1: { rlm@1: lua_settop(L, keyIndex - 1); // stack might not be clean yet if we're breaking rlm@1: // early rlm@1: break; rlm@1: } rlm@1: } rlm@1: APPENDPRINT "}" END rlm@1: } rlm@1: } rlm@1: break; rlm@1: } rlm@1: rlm@1: if (usedMeta) rlm@1: { rlm@1: s_metacallStack.pop_back(); rlm@1: lua_pop(L, 1); rlm@1: } rlm@1: } rlm@1: rlm@1: static const int s_tempStrMaxLen = 64 * 1024; rlm@1: static char s_tempStr [s_tempStrMaxLen]; rlm@1: rlm@1: static char *rawToCString(lua_State *L, int idx) rlm@1: { rlm@1: int a = idx > 0 ? idx : 1; rlm@1: int n = idx > 0 ? idx : lua_gettop(L); rlm@1: rlm@1: char *ptr = s_tempStr; rlm@1: *ptr = 0; rlm@1: rlm@1: int remaining = s_tempStrMaxLen; rlm@1: for (int i = a; i <= n; i++) rlm@1: { rlm@1: toCStringConverter(L, i, ptr, remaining); rlm@1: if (i != n) rlm@1: APPENDPRINT " " END rlm@1: } rlm@1: rlm@1: if (remaining < 3) rlm@1: { rlm@1: while (remaining < 6) rlm@1: remaining++, ptr--; rlm@1: APPENDPRINT "..." END rlm@1: } rlm@1: APPENDPRINT "\r\n" END rlm@1: // the trailing newline is so print() can avoid having to do wasteful things to print its newline rlm@1: // (string copying would be wasteful and calling info.print() twice can be extremely slow) rlm@1: // at the cost of functions that don't want the newline needing to trim off the last two characters rlm@1: // (which is a very fast operation and thus acceptable in this case) rlm@1: rlm@1: return s_tempStr; rlm@1: } rlm@1: #undef APPENDPRINT rlm@1: #undef END rlm@1: rlm@1: // replacement for luaB_tostring() that is able to show the contents of tables (and formats numbers better, and show function rlm@1: // prototypes) rlm@1: // can be called directly from lua via tostring(), assuming tostring hasn't been reassigned rlm@1: static int tostring(lua_State *L) rlm@1: { rlm@1: char *str = rawToCString(L); rlm@1: str[strlen(str) - 2] = 0; // hack: trim off the \r\n (which is there to simplify the print function's rlm@1: // task) rlm@1: lua_pushstring(L, str); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: // like rawToCString, but will check if the global Lua function tostring() rlm@1: // has been replaced with a custom function, and call that instead if so rlm@1: static const char *toCString(lua_State *L, int idx) rlm@1: { rlm@1: int a = idx > 0 ? idx : 1; rlm@1: int n = idx > 0 ? idx : lua_gettop(L); rlm@1: lua_getglobal(L, "tostring"); rlm@1: lua_CFunction cf = lua_tocfunction(L, -1); rlm@1: if (cf == tostring || lua_isnil(L, -1)) // optimization: if using our own C tostring function, we can rlm@1: // bypass the call through Lua and all the string object rlm@1: // allocation that would entail rlm@1: { rlm@1: lua_pop(L, 1); rlm@1: return rawToCString(L, idx); rlm@1: } rlm@1: else // if the user overrided the tostring function, we have to actually call it and store the rlm@1: // temporarily allocated string it returns rlm@1: { rlm@1: lua_pushstring(L, ""); rlm@1: for (int i = a; i <= n; i++) rlm@1: { rlm@1: lua_pushvalue(L, -2); // function to be called rlm@1: lua_pushvalue(L, i); // value to print rlm@1: lua_call(L, 1, 1); rlm@1: if (lua_tostring(L, -1) == NULL) rlm@1: luaL_error(L, LUA_QL("tostring") " must return a string to " LUA_QL("print")); rlm@1: lua_pushstring(L, (i < n) ? " " : "\r\n"); rlm@1: lua_concat(L, 3); rlm@1: } rlm@1: const char *str = lua_tostring(L, -1); rlm@1: strncpy(s_tempStr, str, s_tempStrMaxLen); rlm@1: s_tempStr[s_tempStrMaxLen - 1] = 0; rlm@1: lua_pop(L, 2); rlm@1: return s_tempStr; rlm@1: } rlm@1: } rlm@1: rlm@1: // replacement for luaB_print() that goes to the appropriate textbox instead of stdout rlm@1: static int print(lua_State *L) rlm@1: { rlm@1: const char *str = toCString(L); rlm@1: rlm@1: int uid = info_uid; //luaStateToUIDMap[L->l_G->mainthread]; rlm@1: //LuaContextInfo& info = GetCurrentInfo(); rlm@1: rlm@1: if (info_print) rlm@1: info_print(uid, str); rlm@1: else rlm@1: puts(str); rlm@1: rlm@1: //worry(L, 100); rlm@1: return 0; rlm@1: } rlm@1: rlm@1: static int printerror(lua_State *L, int idx) rlm@1: { rlm@1: lua_checkstack(L, lua_gettop(L) + 4); rlm@1: rlm@1: if (idx < 0) rlm@1: idx = lua_gettop(L) + 1 + idx; rlm@1: rlm@1: const char *str = rawToCString(L, idx); rlm@1: rlm@1: int uid = info_uid; //luaStateToUIDMap[L->l_G->mainthread]; rlm@1: //LuaContextInfo& info = GetCurrentInfo(); rlm@1: rlm@1: if (info_print) rlm@1: info_print(uid, str); rlm@1: else rlm@1: fputs(str, stderr); rlm@1: rlm@1: //worry(L, 100); rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // vba.message(string msg) rlm@1: // rlm@1: // Displays the given message on the screen. rlm@1: static int vba_message(lua_State *L) rlm@1: { rlm@1: const char *msg = luaL_checkstring(L, 1); rlm@1: systemScreenMessage(msg); rlm@1: rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // provides an easy way to copy a table from Lua rlm@1: // (simple assignment only makes an alias, but sometimes an independent table is desired) rlm@1: // currently this function only performs a shallow copy, rlm@1: // but I think it should be changed to do a deep copy (possibly of configurable depth?) rlm@1: // that maintains the internal table reference structure rlm@1: static int copytable(lua_State *L) rlm@1: { rlm@1: int origIndex = 1; // we only care about the first argument rlm@1: int origType = lua_type(L, origIndex); rlm@1: if (origType == LUA_TNIL) rlm@1: { rlm@1: lua_pushnil(L); rlm@1: return 1; rlm@1: } rlm@1: if (origType != LUA_TTABLE) rlm@1: { rlm@1: luaL_typerror(L, 1, lua_typename(L, LUA_TTABLE)); rlm@1: lua_pushnil(L); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: lua_createtable(L, lua_objlen(L, 1), 0); rlm@1: int copyIndex = lua_gettop(L); rlm@1: rlm@1: lua_pushnil(L); // first key rlm@1: int keyIndex = lua_gettop(L); rlm@1: int valueIndex = keyIndex + 1; rlm@1: rlm@1: while (lua_next(L, origIndex)) rlm@1: { rlm@1: lua_pushvalue(L, keyIndex); rlm@1: lua_pushvalue(L, valueIndex); rlm@1: lua_rawset(L, copyIndex); // copytable[key] = value rlm@1: lua_pop(L, 1); rlm@1: } rlm@1: rlm@1: // copy the reference to the metatable as well, if any rlm@1: if (lua_getmetatable(L, origIndex)) rlm@1: lua_setmetatable(L, copyIndex); rlm@1: rlm@1: return 1; // return the new table rlm@1: } rlm@1: rlm@1: // because print traditionally shows the address of tables, rlm@1: // and the print function I provide instead shows the contents of tables, rlm@1: // I also provide this function rlm@1: // (otherwise there would be no way to see a table's address, AFAICT) rlm@1: static int addressof(lua_State *L) rlm@1: { rlm@1: const void *ptr = lua_topointer(L, -1); rlm@1: lua_pushinteger(L, (lua_Integer)ptr); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: struct registerPointerMap rlm@1: { rlm@1: const char * registerName; rlm@1: unsigned int *pointer; rlm@1: int dataSize; rlm@1: }; rlm@1: rlm@1: #define RPM_ENTRY(name, var) \ rlm@1: { name, (unsigned int *)&var, sizeof(var) \ rlm@1: } \ rlm@1: , rlm@1: rlm@1: extern gbRegister AF; rlm@1: extern gbRegister BC; rlm@1: extern gbRegister DE; rlm@1: extern gbRegister HL; rlm@1: extern gbRegister SP; rlm@1: extern gbRegister PC; rlm@1: extern u16 IFF; rlm@1: rlm@1: registerPointerMap regPointerMap [] = { rlm@1: // gba registers rlm@1: RPM_ENTRY("r0", reg[0].I) rlm@1: RPM_ENTRY("r1", reg[1].I) rlm@1: RPM_ENTRY("r2", reg[2].I) rlm@1: RPM_ENTRY("r3", reg[3].I) rlm@1: RPM_ENTRY("r4", reg[4].I) rlm@1: RPM_ENTRY("r5", reg[5].I) rlm@1: RPM_ENTRY("r6", reg[6].I) rlm@1: RPM_ENTRY("r7", reg[7].I) rlm@1: RPM_ENTRY("r8", reg[8].I) rlm@1: RPM_ENTRY("r9", reg[9].I) rlm@1: RPM_ENTRY("r10", reg[10].I) rlm@1: RPM_ENTRY("r11", reg[11].I) rlm@1: RPM_ENTRY("r12", reg[12].I) rlm@1: RPM_ENTRY("r13", reg[13].I) rlm@1: RPM_ENTRY("r14", reg[14].I) rlm@1: RPM_ENTRY("r15", reg[15].I) rlm@1: RPM_ENTRY("cpsr", reg[16].I) rlm@1: RPM_ENTRY("spsr", reg[17].I) rlm@1: // gb registers rlm@1: RPM_ENTRY("a", AF.B.B1) rlm@1: RPM_ENTRY("f", AF.B.B0) rlm@1: RPM_ENTRY("b", BC.B.B1) rlm@1: RPM_ENTRY("c", BC.B.B0) rlm@1: RPM_ENTRY("d", DE.B.B1) rlm@1: RPM_ENTRY("e", DE.B.B0) rlm@1: RPM_ENTRY("h", HL.B.B1) rlm@1: RPM_ENTRY("l", HL.B.B0) rlm@1: RPM_ENTRY("af", AF.W) rlm@1: RPM_ENTRY("bc", BC.W) rlm@1: RPM_ENTRY("de", DE.W) rlm@1: RPM_ENTRY("hl", HL.W) rlm@1: RPM_ENTRY("sp", SP.W) rlm@1: RPM_ENTRY("pc", PC.W) rlm@1: {} rlm@1: }; rlm@1: rlm@1: struct cpuToRegisterMap rlm@1: { rlm@1: const char *cpuName; rlm@1: registerPointerMap *rpmap; rlm@1: } rlm@1: cpuToRegisterMaps [] = rlm@1: { rlm@1: { "", regPointerMap }, rlm@1: }; rlm@1: rlm@1: //DEFINE_LUA_FUNCTION(memory_getregister, "cpu_dot_registername_string") rlm@1: static int memory_getregister(lua_State *L) rlm@1: { rlm@1: const char *qualifiedRegisterName = luaL_checkstring(L, 1); rlm@1: lua_settop(L, 0); rlm@1: for (int cpu = 0; cpu < sizeof(cpuToRegisterMaps) / sizeof(*cpuToRegisterMaps); cpu++) rlm@1: { rlm@1: cpuToRegisterMap ctrm = cpuToRegisterMaps[cpu]; rlm@1: int cpuNameLen = strlen(ctrm.cpuName); rlm@1: if (!strnicmp(qualifiedRegisterName, ctrm.cpuName, cpuNameLen)) rlm@1: { rlm@1: qualifiedRegisterName += cpuNameLen; rlm@1: for (int reg = 0; ctrm.rpmap[reg].dataSize; reg++) rlm@1: { rlm@1: registerPointerMap rpm = ctrm.rpmap[reg]; rlm@1: if (!stricmp(qualifiedRegisterName, rpm.registerName)) rlm@1: { rlm@1: switch (rpm.dataSize) rlm@1: { rlm@1: default: rlm@1: case 1: rlm@1: lua_pushinteger(L, *(unsigned char *)rpm.pointer); break; rlm@1: case 2: rlm@1: lua_pushinteger(L, *(unsigned short *)rpm.pointer); break; rlm@1: case 4: rlm@1: lua_pushinteger(L, *(unsigned long *)rpm.pointer); break; rlm@1: } rlm@1: return 1; rlm@1: } rlm@1: } rlm@1: lua_pushnil(L); rlm@1: return 1; rlm@1: } rlm@1: } rlm@1: lua_pushnil(L); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: //DEFINE_LUA_FUNCTION(memory_setregister, "cpu_dot_registername_string,value") rlm@1: static int memory_setregister(lua_State *L) rlm@1: { rlm@1: const char * qualifiedRegisterName = luaL_checkstring(L, 1); rlm@1: unsigned long value = (unsigned long)(luaL_checkinteger(L, 2)); rlm@1: lua_settop(L, 0); rlm@1: for (int cpu = 0; cpu < sizeof(cpuToRegisterMaps) / sizeof(*cpuToRegisterMaps); cpu++) rlm@1: { rlm@1: cpuToRegisterMap ctrm = cpuToRegisterMaps[cpu]; rlm@1: int cpuNameLen = strlen(ctrm.cpuName); rlm@1: if (!strnicmp(qualifiedRegisterName, ctrm.cpuName, cpuNameLen)) rlm@1: { rlm@1: qualifiedRegisterName += cpuNameLen; rlm@1: for (int reg = 0; ctrm.rpmap[reg].dataSize; reg++) rlm@1: { rlm@1: registerPointerMap rpm = ctrm.rpmap[reg]; rlm@1: if (!stricmp(qualifiedRegisterName, rpm.registerName)) rlm@1: { rlm@1: switch (rpm.dataSize) rlm@1: { rlm@1: default: rlm@1: case 1: rlm@1: *(unsigned char *)rpm.pointer = (unsigned char)(value & 0xFF); break; rlm@1: case 2: rlm@1: *(unsigned short *)rpm.pointer = (unsigned short)(value & 0xFFFF); break; rlm@1: case 4: rlm@1: *(unsigned long *)rpm.pointer = value; break; rlm@1: } rlm@1: return 0; rlm@1: } rlm@1: } rlm@1: return 0; rlm@1: } rlm@1: } rlm@1: return 0; rlm@1: } rlm@1: rlm@1: void HandleCallbackError(lua_State *L) rlm@1: { rlm@1: if (L->errfunc || L->errorJmp) rlm@1: luaL_error(L, "%s", lua_tostring(L, -1)); rlm@1: else rlm@1: { rlm@1: lua_pushnil(LUA); rlm@1: lua_setfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable); rlm@1: rlm@1: // Error? rlm@1: //#if (defined(WIN32) && !defined(SDL)) rlm@1: // info_print(info_uid, lua_tostring(LUA, -1)); //Clear_Sound_Buffer(); rlm@1: // AfxGetApp()->m_pMainWnd->MessageBox(lua_tostring(LUA, -1), "Lua run error", MB_OK | MB_ICONSTOP); rlm@1: //#else rlm@1: // fprintf(stderr, "Lua thread bombed out: %s\n", lua_tostring(LUA, -1)); rlm@1: //#endif rlm@1: printerror(LUA, -1); rlm@1: VBALuaStop(); rlm@1: } rlm@1: } rlm@1: rlm@1: void CallRegisteredLuaFunctions(LuaCallID calltype) rlm@1: { rlm@1: assert((unsigned int)calltype < (unsigned int)LUACALL_COUNT); rlm@1: rlm@1: const char *idstring = luaCallIDStrings[calltype]; rlm@1: rlm@1: if (!LUA) rlm@1: return; rlm@1: rlm@1: lua_settop(LUA, 0); rlm@1: lua_getfield(LUA, LUA_REGISTRYINDEX, idstring); rlm@1: rlm@1: int errorcode = 0; rlm@1: if (lua_isfunction(LUA, -1)) rlm@1: { rlm@1: errorcode = lua_pcall(LUA, 0, 0, 0); rlm@1: if (errorcode) rlm@1: HandleCallbackError(LUA); rlm@1: } rlm@1: else rlm@1: { rlm@1: lua_pop(LUA, 1); rlm@1: } rlm@1: } rlm@1: rlm@1: // the purpose of this structure is to provide a way of rlm@1: // QUICKLY determining whether a memory address range has a hook associated with it, rlm@1: // with a bias toward fast rejection because the majority of addresses will not be hooked. rlm@1: // (it must not use any part of Lua or perform any per-script operations, rlm@1: // otherwise it would definitely be too slow.) rlm@1: // calculating the regions when a hook is added/removed may be slow, rlm@1: // but this is an intentional tradeoff to obtain a high speed of checking during later execution rlm@1: struct TieredRegion rlm@1: { rlm@1: template rlm@1: struct Region rlm@1: { rlm@1: struct Island rlm@1: { rlm@1: unsigned int start; rlm@1: unsigned int end; rlm@1: __forceinline bool Contains(unsigned int address, int size) const { return address < end && address + size > start; } rlm@1: }; rlm@1: std::vector islands; rlm@1: rlm@1: void Calculate(const std::vector &bytes) rlm@1: { rlm@1: islands. clear(); rlm@1: rlm@1: unsigned int lastEnd = ~0; rlm@1: rlm@1: std::vector::const_iterator iter = bytes.begin(); rlm@1: std::vector::const_iterator end = bytes.end(); rlm@1: for (; iter != end; ++iter) rlm@1: { rlm@1: unsigned int addr = *iter; rlm@1: if (addr < lastEnd || addr > lastEnd + (long long)maxGap) rlm@1: { rlm@1: islands. push_back(Island()); rlm@1: islands. back().start = addr; rlm@1: } rlm@1: islands.back(). end = addr + 1; rlm@1: lastEnd = addr + 1; rlm@1: } rlm@1: } rlm@1: rlm@1: bool Contains(unsigned int address, int size) const rlm@1: { rlm@1: for (size_t i = 0; i != islands.size(); ++i) rlm@1: { rlm@1: if (islands[i].Contains(address, size)) rlm@1: return true; rlm@1: } rlm@1: return false; rlm@1: } rlm@1: }; rlm@1: rlm@1: Region<0xFFFFFFFF> broad; rlm@1: Region<0x1000> mid; rlm@1: Region<0> narrow; rlm@1: rlm@1: void Calculate(std::vector &bytes) rlm@1: { rlm@1: std:: sort(bytes.begin(), bytes.end()); rlm@1: rlm@1: broad. Calculate(bytes); rlm@1: mid. Calculate(bytes); rlm@1: narrow. Calculate(bytes); rlm@1: } rlm@1: rlm@1: TieredRegion() rlm@1: { rlm@1: std::vector temp; rlm@1: Calculate(temp); rlm@1: } rlm@1: rlm@1: __forceinline int NotEmpty() rlm@1: { rlm@1: return broad.islands.size(); rlm@1: } rlm@1: rlm@1: // note: it is illegal to call this if NotEmpty() returns 0 rlm@1: __forceinline bool Contains(unsigned int address, int size) rlm@1: { rlm@1: return broad.islands[0].Contains(address, size) && rlm@1: mid.Contains(address, size) && rlm@1: narrow.Contains(address, size); rlm@1: } rlm@1: }; rlm@1: TieredRegion hookedRegions [LUAMEMHOOK_COUNT]; rlm@1: rlm@1: static void CalculateMemHookRegions(LuaMemHookType hookType) rlm@1: { rlm@1: std::vector hookedBytes; rlm@1: // std::map::iterator iter = luaContextInfo.begin(); rlm@1: // std::map::iterator end = luaContextInfo.end(); rlm@1: // while(iter != end) rlm@1: // { rlm@1: // LuaContextInfo& info = *iter->second; rlm@1: if (/*info.*/ numMemHooks) rlm@1: { rlm@1: lua_State *L = LUA /*info.L*/; rlm@1: if (L) rlm@1: { rlm@1: lua_settop(L, 0); rlm@1: lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]); rlm@1: lua_pushnil(L); rlm@1: while (lua_next(L, -2)) rlm@1: { rlm@1: if (lua_isfunction(L, -1)) rlm@1: { rlm@1: unsigned int addr = lua_tointeger(L, -2); rlm@1: hookedBytes.push_back(addr); rlm@1: } rlm@1: lua_pop(L, 1); rlm@1: } rlm@1: lua_settop(L, 0); rlm@1: } rlm@1: } rlm@1: // ++iter; rlm@1: // } rlm@1: hookedRegions[hookType].Calculate(hookedBytes); rlm@1: } rlm@1: rlm@1: static void CallRegisteredLuaMemHook_LuaMatch(unsigned int address, int size, unsigned int value, LuaMemHookType hookType) rlm@1: { rlm@1: // std::map::iterator iter = luaContextInfo.begin(); rlm@1: // std::map::iterator end = luaContextInfo.end(); rlm@1: // while(iter != end) rlm@1: // { rlm@1: // LuaContextInfo& info = *iter->second; rlm@1: if (/*info.*/ numMemHooks) rlm@1: { rlm@1: lua_State *L = LUA /*info.L*/; rlm@1: if (L /* && !info.panic*/) rlm@1: { rlm@1: #ifdef USE_INFO_STACK rlm@1: infoStack.insert(infoStack.begin(), &info); rlm@1: struct Scope { ~Scope(){ infoStack. erase(infoStack.begin()); } } scope; rlm@1: #endif rlm@1: lua_settop(L, 0); rlm@1: lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]); rlm@1: for (int i = address; i != address + size; i++) rlm@1: { rlm@1: lua_rawgeti(L, -1, i); rlm@1: if (lua_isfunction(L, -1)) rlm@1: { rlm@1: bool wasRunning = (luaRunning != 0) /*info.running*/; rlm@1: luaRunning /*info.running*/ = true; rlm@1: //RefreshScriptSpeedStatus(); rlm@1: lua_pushinteger(L, address); rlm@1: lua_pushinteger(L, size); rlm@1: int errorcode = lua_pcall(L, 2, 0, 0); rlm@1: luaRunning /*info.running*/ = wasRunning; rlm@1: //RefreshScriptSpeedStatus(); rlm@1: if (errorcode) rlm@1: { rlm@1: HandleCallbackError(L); rlm@1: //int uid = iter->first; rlm@1: //HandleCallbackError(L,info,uid,true); rlm@1: } rlm@1: break; rlm@1: } rlm@1: else rlm@1: { rlm@1: lua_pop(L, 1); rlm@1: } rlm@1: } rlm@1: lua_settop(L, 0); rlm@1: } rlm@1: } rlm@1: // ++iter; rlm@1: // } rlm@1: } rlm@1: rlm@1: void CallRegisteredLuaMemHook(unsigned int address, int size, unsigned int value, LuaMemHookType hookType) rlm@1: { rlm@1: // performance critical! (called VERY frequently) rlm@1: // I suggest timing a large number of calls to this function in Release if you change anything in here, rlm@1: // before and after, because even the most innocent change can make it become 30% to 400% slower. rlm@1: // a good amount to test is: 100000000 calls with no hook set, and another 100000000 with a hook set. rlm@1: // (on my system that consistently took 200 ms total in the former case and 350 ms total in the latter rlm@1: // case) rlm@1: if (hookedRegions[hookType].NotEmpty()) rlm@1: { rlm@1: //if((hookType <= LUAMEMHOOK_EXEC) && (address >= 0xE00000)) rlm@1: // address |= 0xFF0000; // account for mirroring of RAM rlm@1: if (hookedRegions[hookType].Contains(address, size)) rlm@1: CallRegisteredLuaMemHook_LuaMatch(address, size, value, hookType); // something has hooked this rlm@1: // specific address rlm@1: } rlm@1: } rlm@1: rlm@1: static int memory_registerHook(lua_State *L, LuaMemHookType hookType, int defaultSize) rlm@1: { rlm@1: // get first argument: address rlm@1: unsigned int addr = luaL_checkinteger(L, 1); rlm@1: //if((addr & ~0xFFFFFF) == ~0xFFFFFF) rlm@1: // addr &= 0xFFFFFF; rlm@1: rlm@1: // get optional second argument: size rlm@1: int size = defaultSize; rlm@1: int funcIdx = 2; rlm@1: if (lua_isnumber(L, 2)) rlm@1: { rlm@1: size = luaL_checkinteger(L, 2); rlm@1: if (size < 0) rlm@1: { rlm@1: size = -size; rlm@1: addr -= size; rlm@1: } rlm@1: funcIdx++; rlm@1: } rlm@1: rlm@1: // check last argument: callback function rlm@1: bool clearing = lua_isnil(L, funcIdx); rlm@1: if (!clearing) rlm@1: luaL_checktype(L, funcIdx, LUA_TFUNCTION); rlm@1: lua_settop(L, funcIdx); rlm@1: rlm@1: // get the address-to-callback table for this hook type of the current script rlm@1: lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]); rlm@1: rlm@1: // count how many callback functions we'll be displacing rlm@1: int numFuncsAfter = clearing ? 0 : size; rlm@1: int numFuncsBefore = 0; rlm@1: for (unsigned int i = addr; i != addr + size; i++) rlm@1: { rlm@1: lua_rawgeti(L, -1, i); rlm@1: if (lua_isfunction(L, -1)) rlm@1: numFuncsBefore++; rlm@1: lua_pop(L, 1); rlm@1: } rlm@1: rlm@1: // put the callback function in the address slots rlm@1: for (unsigned int i = addr; i != addr + size; i++) rlm@1: { rlm@1: lua_pushvalue(L, -2); rlm@1: lua_rawseti(L, -2, i); rlm@1: } rlm@1: rlm@1: // adjust the count of active hooks rlm@1: //LuaContextInfo& info = GetCurrentInfo(); rlm@1: /*info.*/ numMemHooks += numFuncsAfter - numFuncsBefore; rlm@1: rlm@1: // re-cache regions of hooked memory across all scripts rlm@1: CalculateMemHookRegions(hookType); rlm@1: rlm@1: //StopScriptIfFinished(luaStateToUIDMap[L]); rlm@1: return 0; rlm@1: } rlm@1: rlm@1: LuaMemHookType MatchHookTypeToCPU(lua_State *L, LuaMemHookType hookType) rlm@1: { rlm@1: int cpuID = 0; rlm@1: rlm@1: int cpunameIndex = 0; rlm@1: if (lua_type(L, 2) == LUA_TSTRING) rlm@1: cpunameIndex = 2; rlm@1: else if (lua_type(L, 3) == LUA_TSTRING) rlm@1: cpunameIndex = 3; rlm@1: rlm@1: if (cpunameIndex) rlm@1: { rlm@1: const char *cpuName = lua_tostring(L, cpunameIndex); rlm@1: if (!stricmp(cpuName, "sub")) rlm@1: cpuID = 1; rlm@1: lua_remove(L, cpunameIndex); rlm@1: } rlm@1: rlm@1: switch (cpuID) rlm@1: { rlm@1: case 0: rlm@1: return hookType; rlm@1: rlm@1: case 1: rlm@1: switch (hookType) rlm@1: { rlm@1: case LUAMEMHOOK_WRITE: rlm@1: return LUAMEMHOOK_WRITE_SUB; rlm@1: case LUAMEMHOOK_READ: rlm@1: return LUAMEMHOOK_READ_SUB; rlm@1: case LUAMEMHOOK_EXEC: rlm@1: return LUAMEMHOOK_EXEC_SUB; rlm@1: } rlm@1: } rlm@1: return hookType; rlm@1: } rlm@1: rlm@1: static int memory_registerwrite(lua_State *L) rlm@1: { rlm@1: return memory_registerHook(L, MatchHookTypeToCPU(L, LUAMEMHOOK_WRITE), 1); rlm@1: } rlm@1: rlm@1: static int memory_registerread(lua_State *L) rlm@1: { rlm@1: return memory_registerHook(L, MatchHookTypeToCPU(L, LUAMEMHOOK_READ), 1); rlm@1: } rlm@1: rlm@1: static int memory_registerexec(lua_State *L) rlm@1: { rlm@1: return memory_registerHook(L, MatchHookTypeToCPU(L, LUAMEMHOOK_EXEC), 1); rlm@1: } rlm@1: rlm@1: //int vba.lagcount rlm@1: // rlm@1: rlm@1: //Returns the lagcounter variable rlm@1: static int vba_getlagcount(lua_State *L) rlm@1: { rlm@1: lua_pushinteger(L, systemCounters.lagCount); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: //int vba.lagged rlm@1: // rlm@1: //Returns true if the current frame is a lag frame rlm@1: static int vba_lagged(lua_State *L) rlm@1: { rlm@1: lua_pushboolean(L, systemCounters.laggedLast); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: // boolean vba.emulating() rlm@1: int vba_emulating(lua_State *L) rlm@1: { rlm@1: lua_pushboolean(L, systemIsEmulating()); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: int movie_isactive(lua_State *L) rlm@1: { rlm@1: lua_pushboolean(L, VBAMovieActive()); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: int movie_isrecording(lua_State *L) rlm@1: { rlm@1: lua_pushboolean(L, VBAMovieRecording()); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: int movie_isplaying(lua_State *L) rlm@1: { rlm@1: lua_pushboolean(L, VBAMoviePlaying()); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: int movie_getlength(lua_State *L) rlm@1: { rlm@1: if (VBAMovieActive()) rlm@1: lua_pushinteger(L, VBAMovieGetLength()); rlm@1: else rlm@1: lua_pushinteger(L, 0); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int memory_readbyte(lua_State *L) rlm@1: { rlm@1: u32 addr; rlm@1: u8 val; rlm@1: rlm@1: addr = luaL_checkinteger(L, 1); rlm@1: if (systemIsRunningGBA()) rlm@1: { rlm@1: val = CPUReadByteQuick(addr); rlm@1: } rlm@1: else rlm@1: { rlm@1: val = gbReadMemoryQuick8(addr); rlm@1: } rlm@1: rlm@1: lua_pushinteger(L, val); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int memory_readbytesigned(lua_State *L) rlm@1: { rlm@1: u32 addr; rlm@1: s8 val; rlm@1: rlm@1: addr = luaL_checkinteger(L, 1); rlm@1: if (systemIsRunningGBA()) rlm@1: { rlm@1: val = (s8) CPUReadByteQuick(addr); rlm@1: } rlm@1: else rlm@1: { rlm@1: val = (s8) gbReadMemoryQuick8(addr); rlm@1: } rlm@1: rlm@1: lua_pushinteger(L, val); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int memory_readword(lua_State *L) rlm@1: { rlm@1: u32 addr; rlm@1: u16 val; rlm@1: rlm@1: addr = luaL_checkinteger(L, 1); rlm@1: if (systemIsRunningGBA()) rlm@1: { rlm@1: val = CPUReadHalfWordQuick(addr); rlm@1: } rlm@1: else rlm@1: { rlm@1: val = gbReadMemoryQuick16(addr & 0x0000FFFF); rlm@1: } rlm@1: rlm@1: lua_pushinteger(L, val); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int memory_readwordsigned(lua_State *L) rlm@1: { rlm@1: u32 addr; rlm@1: s16 val; rlm@1: rlm@1: addr = luaL_checkinteger(L, 1); rlm@1: if (systemIsRunningGBA()) rlm@1: { rlm@1: val = (s16) CPUReadHalfWordQuick(addr); rlm@1: } rlm@1: else rlm@1: { rlm@1: val = (s16) gbReadMemoryQuick16(addr); rlm@1: } rlm@1: rlm@1: lua_pushinteger(L, val); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int memory_readdword(lua_State *L) rlm@1: { rlm@1: u32 addr; rlm@1: u32 val; rlm@1: rlm@1: addr = luaL_checkinteger(L, 1); rlm@1: if (systemIsRunningGBA()) rlm@1: { rlm@1: val = CPUReadMemoryQuick(addr); rlm@1: } rlm@1: else rlm@1: { rlm@1: val = gbReadMemoryQuick32(addr & 0x0000FFFF); rlm@1: } rlm@1: rlm@1: // lua_pushinteger doesn't work properly for 32bit system, does it? rlm@1: if (val >= 0x80000000 && sizeof(int) <= 4) rlm@1: lua_pushnumber(L, val); rlm@1: else rlm@1: lua_pushinteger(L, val); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int memory_readdwordsigned(lua_State *L) rlm@1: { rlm@1: u32 addr; rlm@1: s32 val; rlm@1: rlm@1: addr = luaL_checkinteger(L, 1); rlm@1: if (systemIsRunningGBA()) rlm@1: { rlm@1: val = (s32) CPUReadMemoryQuick(addr); rlm@1: } rlm@1: else rlm@1: { rlm@1: val = (s32) gbReadMemoryQuick32(addr); rlm@1: } rlm@1: rlm@1: lua_pushinteger(L, val); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int memory_readbyterange(lua_State *L) rlm@1: { rlm@1: uint32 address = luaL_checkinteger(L, 1); rlm@1: int length = luaL_checkinteger(L, 2); rlm@1: rlm@1: if (length < 0) rlm@1: { rlm@1: address += length; rlm@1: length = -length; rlm@1: } rlm@1: rlm@1: // push the array rlm@1: lua_createtable(L, abs(length), 0); rlm@1: rlm@1: // put all the values into the (1-based) array rlm@1: for (int a = address, n = 1; n <= length; a++, n++) rlm@1: { rlm@1: unsigned char value; rlm@1: rlm@1: if (systemIsRunningGBA()) rlm@1: { rlm@1: value = CPUReadByteQuick(a); rlm@1: } rlm@1: else rlm@1: { rlm@1: value = gbReadMemoryQuick8(a); rlm@1: } rlm@1: rlm@1: lua_pushinteger(L, value); rlm@1: lua_rawseti(L, -2, n); rlm@1: } rlm@1: rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int memory_writebyte(lua_State *L) rlm@1: { rlm@1: u32 addr; rlm@1: int val; rlm@1: rlm@1: addr = luaL_checkinteger(L, 1); rlm@1: val = luaL_checkinteger(L, 2); rlm@1: if (systemIsRunningGBA()) rlm@1: { rlm@1: CPUWriteByteQuick(addr, val); rlm@1: } rlm@1: else rlm@1: { rlm@1: gbWriteMemoryQuick8(addr, val); rlm@1: } rlm@1: rlm@1: CallRegisteredLuaMemHook(addr, 1, val, LUAMEMHOOK_WRITE); rlm@1: return 0; rlm@1: } rlm@1: rlm@1: static int memory_writeword(lua_State *L) rlm@1: { rlm@1: u32 addr; rlm@1: int val; rlm@1: rlm@1: addr = luaL_checkinteger(L, 1); rlm@1: val = luaL_checkinteger(L, 2); rlm@1: if (systemIsRunningGBA()) rlm@1: { rlm@1: CPUWriteHalfWordQuick(addr, val); rlm@1: } rlm@1: else rlm@1: { rlm@1: gbWriteMemoryQuick16(addr, val); rlm@1: } rlm@1: rlm@1: CallRegisteredLuaMemHook(addr, 2, val, LUAMEMHOOK_WRITE); rlm@1: return 0; rlm@1: } rlm@1: rlm@1: static int memory_writedword(lua_State *L) rlm@1: { rlm@1: u32 addr; rlm@1: int val; rlm@1: rlm@1: addr = luaL_checkinteger(L, 1); rlm@1: val = luaL_checkinteger(L, 2); rlm@1: if (systemIsRunningGBA()) rlm@1: { rlm@1: CPUWriteMemoryQuick(addr, val); rlm@1: } rlm@1: else rlm@1: { rlm@1: gbWriteMemoryQuick32(addr, val); rlm@1: } rlm@1: rlm@1: CallRegisteredLuaMemHook(addr, 4, val, LUAMEMHOOK_WRITE); rlm@1: return 0; rlm@1: } rlm@1: rlm@1: static int memory_gbromreadbyte(lua_State *L) rlm@1: { rlm@1: u32 addr; rlm@1: u8 val; rlm@1: rlm@1: addr = luaL_checkinteger(L, 1); rlm@1: if (systemIsRunningGBA()) rlm@1: { rlm@1: lua_pushnil(L); rlm@1: return 1; rlm@1: } rlm@1: else rlm@1: { rlm@1: val = gbReadROMQuick8(addr); rlm@1: } rlm@1: rlm@1: lua_pushinteger(L, val); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int memory_gbromreadbytesigned(lua_State *L) rlm@1: { rlm@1: u32 addr; rlm@1: s8 val; rlm@1: rlm@1: addr = luaL_checkinteger(L, 1); rlm@1: if (systemIsRunningGBA()) rlm@1: { rlm@1: lua_pushnil(L); rlm@1: return 1; rlm@1: } rlm@1: else rlm@1: { rlm@1: val = (s8) gbReadROMQuick8(addr); rlm@1: } rlm@1: rlm@1: lua_pushinteger(L, val); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int memory_gbromreadword(lua_State *L) rlm@1: { rlm@1: u32 addr; rlm@1: u16 val; rlm@1: rlm@1: addr = luaL_checkinteger(L, 1); rlm@1: if (systemIsRunningGBA()) rlm@1: { rlm@1: lua_pushnil(L); rlm@1: return 1; rlm@1: } rlm@1: else rlm@1: { rlm@1: val = gbReadROMQuick16(addr); rlm@1: } rlm@1: rlm@1: lua_pushinteger(L, val); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int memory_gbromreadwordsigned(lua_State *L) rlm@1: { rlm@1: u32 addr; rlm@1: s16 val; rlm@1: rlm@1: addr = luaL_checkinteger(L, 1); rlm@1: if (systemIsRunningGBA()) rlm@1: { rlm@1: lua_pushnil(L); rlm@1: return 1; rlm@1: } rlm@1: else rlm@1: { rlm@1: val = (s16) gbReadROMQuick16(addr); rlm@1: } rlm@1: rlm@1: lua_pushinteger(L, val); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int memory_gbromreaddword(lua_State *L) rlm@1: { rlm@1: u32 addr; rlm@1: u32 val; rlm@1: rlm@1: addr = luaL_checkinteger(L, 1); rlm@1: if (systemIsRunningGBA()) rlm@1: { rlm@1: lua_pushnil(L); rlm@1: return 1; rlm@1: } rlm@1: else rlm@1: { rlm@1: val = gbReadROMQuick32(addr); rlm@1: } rlm@1: rlm@1: // lua_pushinteger doesn't work properly for 32bit system, does it? rlm@1: if (val >= 0x80000000 && sizeof(int) <= 4) rlm@1: lua_pushnumber(L, val); rlm@1: else rlm@1: lua_pushinteger(L, val); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int memory_gbromreaddwordsigned(lua_State *L) rlm@1: { rlm@1: u32 addr; rlm@1: s32 val; rlm@1: rlm@1: addr = luaL_checkinteger(L, 1); rlm@1: if (systemIsRunningGBA()) rlm@1: { rlm@1: lua_pushnil(L); rlm@1: return 1; rlm@1: } rlm@1: else rlm@1: { rlm@1: val = (s32) gbReadROMQuick32(addr); rlm@1: } rlm@1: rlm@1: lua_pushinteger(L, val); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int memory_gbromreadbyterange(lua_State *L) rlm@1: { rlm@1: uint32 address = luaL_checkinteger(L, 1); rlm@1: int length = luaL_checkinteger(L, 2); rlm@1: rlm@1: if (length < 0) rlm@1: { rlm@1: address += length; rlm@1: length = -length; rlm@1: } rlm@1: rlm@1: // push the array rlm@1: lua_createtable(L, abs(length), 0); rlm@1: rlm@1: // put all the values into the (1-based) array rlm@1: for (int a = address, n = 1; n <= length; a++, n++) rlm@1: { rlm@1: unsigned char value; rlm@1: rlm@1: if (systemIsRunningGBA()) rlm@1: { rlm@1: lua_pushnil(L); rlm@1: return 1; rlm@1: } rlm@1: else rlm@1: { rlm@1: value = gbReadROMQuick8(a); rlm@1: } rlm@1: rlm@1: lua_pushinteger(L, value); rlm@1: lua_rawseti(L, -2, n); rlm@1: } rlm@1: rlm@1: return 1; rlm@1: } rlm@1: rlm@1: // table joypad.get(int which = 1) rlm@1: // rlm@1: // Reads the joypads as inputted by the user. rlm@1: static int joy_get_internal(lua_State *L, bool reportUp, bool reportDown) rlm@1: { rlm@1: // Reads the joypads as inputted by the user rlm@1: int which = luaL_checkinteger(L, 1); rlm@1: rlm@1: if (which < 0 || which > 4) rlm@1: { rlm@1: luaL_error(L, "Invalid input port (valid range 0-4, specified %d)", which); rlm@1: } rlm@1: rlm@1: uint32 buttons = systemGetOriginalJoypad(which - 1, false); rlm@1: rlm@1: lua_newtable(L); rlm@1: rlm@1: int i; rlm@1: for (i = 0; i < 10; i++) rlm@1: { rlm@1: bool pressed = (buttons & (1 << i)) != 0; rlm@1: if ((pressed && reportDown) || (!pressed && reportUp)) rlm@1: { rlm@1: lua_pushboolean(L, pressed); rlm@1: lua_setfield(L, -2, button_mappings[i]); rlm@1: } rlm@1: } rlm@1: rlm@1: return 1; rlm@1: } rlm@1: rlm@1: // joypad.get(which) rlm@1: // returns a table of every game button, rlm@1: // true meaning currently-held and false meaning not-currently-held rlm@1: // (as of last frame boundary) rlm@1: // this WILL read input from a currently-playing movie rlm@1: static int joypad_get(lua_State *L) rlm@1: { rlm@1: return joy_get_internal(L, true, true); rlm@1: } rlm@1: rlm@1: // joypad.getdown(which) rlm@1: // returns a table of every game button that is currently held rlm@1: static int joypad_getdown(lua_State *L) rlm@1: { rlm@1: return joy_get_internal(L, false, true); rlm@1: } rlm@1: rlm@1: // joypad.getup(which) rlm@1: // returns a table of every game button that is not currently held rlm@1: static int joypad_getup(lua_State *L) rlm@1: { rlm@1: return joy_get_internal(L, true, false); rlm@1: } rlm@1: rlm@1: // joypad.set(int which, table buttons) rlm@1: // rlm@1: // Sets the given buttons to be pressed during the next rlm@1: // frame advance. The table should have the right rlm@1: rlm@1: // keys (no pun intended) set. rlm@1: static int joypad_set(lua_State *L) rlm@1: { rlm@1: // Which joypad we're tampering with rlm@1: int which = luaL_checkinteger(L, 1); rlm@1: if (which < 0 || which > 4) rlm@1: { rlm@1: luaL_error(L, "Invalid output port (valid range 0-4, specified %d)", which); rlm@1: } rlm@1: rlm@1: if (which == 0) rlm@1: which = systemGetDefaultJoypad(); rlm@1: rlm@1: // And the table of buttons. rlm@1: luaL_checktype(L, 2, LUA_TTABLE); rlm@1: rlm@1: // Set up for taking control of the indicated controller rlm@1: lua_joypads_used |= 1 << (which - 1); rlm@1: lua_joypads[which - 1] = 0; rlm@1: rlm@1: for (int i = 0; i < 10; i++) rlm@1: { rlm@1: const char *name = button_mappings[i]; rlm@1: lua_getfield(L, 2, name); rlm@1: if (!lua_isnil(L, -1)) rlm@1: { rlm@1: bool pressed = lua_toboolean(L, -1) != 0; rlm@1: if (pressed) rlm@1: lua_joypads[which - 1] |= 1 << i; rlm@1: else rlm@1: lua_joypads[which - 1] &= ~(1 << i); rlm@1: } rlm@1: lua_pop(L, 1); rlm@1: } rlm@1: rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // Helper function to convert a savestate object to the filename it represents. rlm@1: static const char *savestateobj2filename(lua_State *L, int offset) rlm@1: { rlm@1: // First we get the metatable of the indicated object rlm@1: int result = lua_getmetatable(L, offset); rlm@1: rlm@1: if (!result) rlm@1: luaL_error(L, "object not a savestate object"); rlm@1: rlm@1: // Also check that the type entry is set rlm@1: lua_getfield(L, -1, "__metatable"); rlm@1: if (strcmp(lua_tostring(L, -1), "vba Savestate") != 0) rlm@1: luaL_error(L, "object not a savestate object"); rlm@1: lua_pop(L, 1); rlm@1: rlm@1: // Now, get the field we want rlm@1: lua_getfield(L, -1, "filename"); rlm@1: rlm@1: // Return it rlm@1: return lua_tostring(L, -1); rlm@1: } rlm@1: rlm@1: // Helper function for garbage collection. rlm@1: static int savestate_gc(lua_State *L) rlm@1: { rlm@1: // The object we're collecting is on top of the stack rlm@1: lua_getmetatable(L, 1); rlm@1: rlm@1: // Get the filename rlm@1: const char *filename; rlm@1: lua_getfield(L, -1, "filename"); rlm@1: filename = lua_tostring(L, -1); rlm@1: rlm@1: // Delete the file rlm@1: remove(filename); rlm@1: rlm@1: // We exit, and the garbage collector takes care of the rest. rlm@1: // Edit: Visual Studio needs a return value anyway, so returns 0. rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // object savestate.create(int which = nil) rlm@1: // rlm@1: // Creates an object used for savestates. rlm@1: // The object can be associated with a player-accessible savestate rlm@1: rlm@1: // ("which" between 1 and 12) or not (which == nil). rlm@1: static int savestate_create(lua_State *L) rlm@1: { rlm@1: int which = -1; rlm@1: if (lua_gettop(L) >= 1) rlm@1: { rlm@1: which = luaL_checkinteger(L, 1); rlm@1: if (which < 1 || which > 12) rlm@1: { rlm@1: luaL_error(L, "invalid player's savestate %d", which); rlm@1: } rlm@1: } rlm@1: rlm@1: char stateName[2048]; rlm@1: rlm@1: if (which > 0) rlm@1: { rlm@1: // Find an appropriate filename. This is OS specific, unfortunately. rlm@1: #if (defined(WIN32) && !defined(SDL)) rlm@1: CString stateName = winGetSavestateFilename(theApp.gameFilename, which); rlm@1: #else rlm@1: extern char saveDir[2048]; rlm@1: extern char filename[2048]; rlm@1: extern char *sdlGetFilename(char *name); rlm@1: rlm@1: if (saveDir[0]) rlm@1: sprintf(stateName, "%s/%s%d.sgm", saveDir, sdlGetFilename(filename), which); rlm@1: else rlm@1: sprintf(stateName, "%s%d.sgm", filename, which); rlm@1: #endif rlm@1: } rlm@1: else rlm@1: { rlm@1: char *stateNameTemp = tempnam(NULL, "snlua"); rlm@1: strcpy(stateName, stateNameTemp); rlm@1: if (stateNameTemp) rlm@1: free(stateNameTemp); rlm@1: } rlm@1: rlm@1: // Our "object". We don't care about the type, we just need the memory and GC services. rlm@1: lua_newuserdata(L, 1); rlm@1: rlm@1: // The metatable we use, protected from Lua and contains garbage collection info and stuff. rlm@1: lua_newtable(L); rlm@1: rlm@1: // First, we must protect it rlm@1: lua_pushstring(L, "vba Savestate"); rlm@1: lua_setfield(L, -2, "__metatable"); rlm@1: rlm@1: // Now we need to save the file itself. rlm@1: lua_pushstring(L, stateName); rlm@1: lua_setfield(L, -2, "filename"); rlm@1: rlm@1: // If it's an anonymous savestate, we must delete the file from disk should it be gargage collected rlm@1: if (which < 0) rlm@1: { rlm@1: lua_pushcfunction(L, savestate_gc); rlm@1: lua_setfield(L, -2, "__gc"); rlm@1: } rlm@1: rlm@1: // Set the metatable rlm@1: lua_setmetatable(L, -2); rlm@1: rlm@1: // Awesome. Return the object rlm@1: return 1; rlm@1: } rlm@1: rlm@1: // savestate.save(object state) rlm@1: // rlm@1: rlm@1: // Saves a state to the given object. rlm@1: static int savestate_save(lua_State *L) rlm@1: { rlm@1: const char *filename = savestateobj2filename(L, 1); rlm@1: rlm@1: // printf("saving %s\n", filename); rlm@1: // Save states are very expensive. They take time. rlm@1: numTries--; rlm@1: rlm@1: bool8 retvalue = theEmulator.emuWriteState ? theEmulator.emuWriteState(filename) : false; rlm@1: if (!retvalue) rlm@1: { rlm@1: // Uh oh rlm@1: luaL_error(L, "savestate failed"); rlm@1: } rlm@1: rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // savestate.load(object state) rlm@1: // rlm@1: rlm@1: // Loads the given state rlm@1: static int savestate_load(lua_State *L) rlm@1: { rlm@1: const char *filename = savestateobj2filename(L, 1); rlm@1: rlm@1: numTries--; rlm@1: rlm@1: // printf("loading %s\n", filename); rlm@1: bool8 retvalue = theEmulator.emuReadState ? theEmulator.emuReadState(filename) : false; rlm@1: if (!retvalue) rlm@1: { rlm@1: // Uh oh rlm@1: luaL_error(L, "loadstate failed"); rlm@1: } rlm@1: rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // int vba.framecount() rlm@1: // rlm@1: rlm@1: // Gets the frame counter for the movie, or the number of frames since last reset. rlm@1: int vba_framecount(lua_State *L) rlm@1: { rlm@1: if (!VBAMovieActive()) rlm@1: { rlm@1: lua_pushinteger(L, systemCounters.frameCount); rlm@1: } rlm@1: else rlm@1: { rlm@1: lua_pushinteger(L, VBAMovieGetFrameCounter()); rlm@1: } rlm@1: rlm@1: return 1; rlm@1: } rlm@1: rlm@1: //string movie.getauthor rlm@1: // rlm@1: rlm@1: // returns author info field of .vbm file rlm@1: int movie_getauthor(lua_State *L) rlm@1: { rlm@1: if (!VBAMovieActive()) rlm@1: { rlm@1: //lua_pushnil(L); rlm@1: lua_pushstring(L, ""); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: lua_pushstring(L, VBAMovieGetAuthorInfo().c_str()); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: //string movie.filename rlm@1: int movie_getfilename(lua_State *L) rlm@1: { rlm@1: if (!VBAMovieActive()) rlm@1: { rlm@1: //lua_pushnil(L); rlm@1: lua_pushstring(L, ""); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: lua_pushstring(L, VBAMovieGetFilename().c_str()); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: // string movie.mode() rlm@1: // rlm@1: rlm@1: // "record", "playback" or nil rlm@1: int movie_getmode(lua_State *L) rlm@1: { rlm@1: assert(!VBAMovieLoading()); rlm@1: if (!VBAMovieActive()) rlm@1: { rlm@1: lua_pushnil(L); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: if (VBAMovieRecording()) rlm@1: lua_pushstring(L, "record"); rlm@1: else rlm@1: lua_pushstring(L, "playback"); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int movie_rerecordcount(lua_State *L) rlm@1: { rlm@1: if (VBAMovieActive()) rlm@1: lua_pushinteger(L, VBAMovieGetRerecordCount()); rlm@1: else rlm@1: lua_pushinteger(L, 0); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int movie_setrerecordcount(lua_State *L) rlm@1: { rlm@1: if (VBAMovieActive()) rlm@1: VBAMovieSetRerecordCount(luaL_checkinteger(L, 1)); rlm@1: return 0; rlm@1: } rlm@1: rlm@1: static int movie_rerecordcounting(lua_State *L) rlm@1: { rlm@1: if (lua_gettop(L) == 0) rlm@1: luaL_error(L, "no parameters specified"); rlm@1: rlm@1: skipRerecords = lua_toboolean(L, 1); rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // movie.stop() rlm@1: // rlm@1: rlm@1: // Stops movie playback/recording. Bombs out if movie is not running. rlm@1: static int movie_stop(lua_State *L) rlm@1: { rlm@1: if (!VBAMovieActive()) rlm@1: luaL_error(L, "no movie"); rlm@1: rlm@1: VBAMovieStop(false); rlm@1: return 0; rlm@1: } rlm@1: rlm@1: #define LUA_SCREEN_WIDTH 256 rlm@1: #define LUA_SCREEN_HEIGHT 239 rlm@1: rlm@1: // Common code by the gui library: make sure the screen array is ready rlm@1: static void gui_prepare(void) rlm@1: { rlm@1: if (!gui_data) rlm@1: gui_data = (uint8 *)malloc(LUA_SCREEN_WIDTH * LUA_SCREEN_HEIGHT * 4); rlm@1: if (!gui_used) rlm@1: memset(gui_data, 0, LUA_SCREEN_WIDTH * LUA_SCREEN_HEIGHT * 4); rlm@1: gui_used = true; rlm@1: } rlm@1: rlm@1: // pixform for lua graphics rlm@1: #define BUILD_PIXEL_ARGB8888(A, R, G, B) (((int)(A) << 24) | ((int)(R) << 16) | ((int)(G) << 8) | (int)(B)) rlm@1: #define DECOMPOSE_PIXEL_ARGB8888(PIX, A, R, G, B) \ rlm@1: { \ rlm@1: (A) = ((PIX) >> 24) & 0xff; \ rlm@1: (R) = ((PIX) >> 16) & 0xff; \ rlm@1: (G) = ((PIX) >> 8) & 0xff; \ rlm@1: (B) = (PIX) & 0xff; \ rlm@1: } rlm@1: #define LUA_BUILD_PIXEL BUILD_PIXEL_ARGB8888 rlm@1: #define LUA_DECOMPOSE_PIXEL DECOMPOSE_PIXEL_ARGB8888 rlm@1: #define LUA_PIXEL_A(PIX) (((PIX) >> 24) & 0xff) rlm@1: #define LUA_PIXEL_R(PIX) (((PIX) >> 16) & 0xff) rlm@1: #define LUA_PIXEL_G(PIX) (((PIX) >> 8) & 0xff) rlm@1: #define LUA_PIXEL_B(PIX) ((PIX) & 0xff) rlm@1: rlm@1: template rlm@1: static void swap(T &one, T &two) rlm@1: { rlm@1: T temp = one; rlm@1: one = two; rlm@1: two = temp; rlm@1: } rlm@1: rlm@1: // write a pixel to buffer rlm@1: static inline void blend32(uint32 *dstPixel, uint32 colour) rlm@1: { rlm@1: uint8 *dst = (uint8 *)dstPixel; rlm@1: int a, r, g, b; rlm@1: LUA_DECOMPOSE_PIXEL(colour, a, r, g, b); rlm@1: rlm@1: if (a == 255 || dst[3] == 0) rlm@1: { rlm@1: // direct copy rlm@1: *(uint32 *) (dst) = colour; rlm@1: } rlm@1: else if (a == 0) rlm@1: { rlm@1: // do not copy rlm@1: } rlm@1: else rlm@1: { rlm@1: // alpha-blending rlm@1: int a_dst = ((255 - a) * dst[3] + 128) / 255; rlm@1: int a_new = a + a_dst; rlm@1: rlm@1: dst[0] = (uint8) (((dst[0] * a_dst + b * a) + (a_new / 2)) / a_new); rlm@1: dst[1] = (uint8) (((dst[1] * a_dst + g * a) + (a_new / 2)) / a_new); rlm@1: dst[2] = (uint8) (((dst[2] * a_dst + r * a) + (a_new / 2)) / a_new); rlm@1: dst[3] = (uint8) a_new; rlm@1: } rlm@1: } rlm@1: rlm@1: // check if a pixel is in the lua canvas rlm@1: static inline bool gui_check_boundary(int x, int y) rlm@1: { rlm@1: return !(x < 0 || x >= LUA_SCREEN_WIDTH || y < 0 || y >= LUA_SCREEN_HEIGHT); rlm@1: } rlm@1: rlm@1: // check if any part of a box is in the lua canvas rlm@1: static inline bool gui_checkbox(int x1, int y1, int x2, int y2) rlm@1: { rlm@1: if ((x1 < 0 && x2 < 0) rlm@1: || (x1 >= LUA_SCREEN_WIDTH && x2 >= LUA_SCREEN_WIDTH) rlm@1: || (y1 < 0 && y2 < 0) rlm@1: || (y1 >= LUA_SCREEN_HEIGHT && y2 >= LUA_SCREEN_HEIGHT)) rlm@1: return false; rlm@1: return true; rlm@1: } rlm@1: rlm@1: // write a pixel to gui_data (do not check boundaries for speedup) rlm@1: static inline void gui_drawpixel_fast(int x, int y, uint32 colour) rlm@1: { rlm@1: //gui_prepare(); rlm@1: blend32((uint32 *) &gui_data[(y * LUA_SCREEN_WIDTH + x) * 4], colour); rlm@1: } rlm@1: rlm@1: // write a pixel to gui_data (check boundaries) rlm@1: static inline void gui_drawpixel_internal(int x, int y, uint32 colour) rlm@1: { rlm@1: //gui_prepare(); rlm@1: if (gui_check_boundary(x, y)) rlm@1: gui_drawpixel_fast(x, y, colour); rlm@1: } rlm@1: rlm@1: // draw a line on gui_data (checks boundaries) rlm@1: static void gui_drawline_internal(int x1, int y1, int x2, int y2, bool lastPixel, uint32 colour) rlm@1: { rlm@1: //gui_prepare(); rlm@1: // Note: New version of Bresenham's Line Algorithm rlm@1: // rlm@1: // rlm@1: // http://groups.google.co.jp/group/rec.games.roguelike.development/browse_thread/thread/345f4c42c3b25858/29e07a3af3a450e6?show_docid=29e07a3af3a450e6 rlm@1: int swappedx = 0; rlm@1: int swappedy = 0; rlm@1: rlm@1: int xtemp = x1 - x2; rlm@1: int ytemp = y1 - y2; rlm@1: if (xtemp == 0 && ytemp == 0) rlm@1: { rlm@1: gui_drawpixel_internal(x1, y1, colour); rlm@1: return; rlm@1: } rlm@1: rlm@1: if (xtemp < 0) rlm@1: { rlm@1: xtemp = -xtemp; rlm@1: swappedx = 1; rlm@1: } rlm@1: rlm@1: if (ytemp < 0) rlm@1: { rlm@1: ytemp = -ytemp; rlm@1: swappedy = 1; rlm@1: } rlm@1: rlm@1: int delta_x = xtemp << 1; rlm@1: int delta_y = ytemp << 1; rlm@1: rlm@1: signed char ix = x1 > x2 ? 1 : -1; rlm@1: signed char iy = y1 > y2 ? 1 : -1; rlm@1: rlm@1: if (lastPixel) rlm@1: gui_drawpixel_internal(x2, y2, colour); rlm@1: rlm@1: if (delta_x >= delta_y) rlm@1: { rlm@1: int error = delta_y - (delta_x >> 1); rlm@1: rlm@1: while (x2 != x1) rlm@1: { rlm@1: if (error == 0 && !swappedx) rlm@1: gui_drawpixel_internal(x2 + ix, y2, colour); rlm@1: if (error >= 0) rlm@1: { rlm@1: if (error || (ix > 0)) rlm@1: { rlm@1: y2 += iy; rlm@1: error -= delta_x; rlm@1: } rlm@1: } rlm@1: rlm@1: x2 += ix; rlm@1: gui_drawpixel_internal(x2, y2, colour); rlm@1: if (error == 0 && swappedx) rlm@1: gui_drawpixel_internal(x2, y2 + iy, colour); rlm@1: error += delta_y; rlm@1: } rlm@1: } rlm@1: else rlm@1: { rlm@1: int error = delta_x - (delta_y >> 1); rlm@1: rlm@1: while (y2 != y1) rlm@1: { rlm@1: if (error == 0 && !swappedy) rlm@1: gui_drawpixel_internal(x2, y2 + iy, colour); rlm@1: if (error >= 0) rlm@1: { rlm@1: if (error || (iy > 0)) rlm@1: { rlm@1: x2 += ix; rlm@1: error -= delta_y; rlm@1: } rlm@1: } rlm@1: rlm@1: y2 += iy; rlm@1: gui_drawpixel_internal(x2, y2, colour); rlm@1: if (error == 0 && swappedy) rlm@1: gui_drawpixel_internal(x2 + ix, y2, colour); rlm@1: error += delta_x; rlm@1: } rlm@1: } rlm@1: } rlm@1: rlm@1: // draw a rect on gui_data rlm@1: static void gui_drawbox_internal(int x1, int y1, int x2, int y2, uint32 colour) rlm@1: { rlm@1: if (x1 > x2) rlm@1: std::swap(x1, x2); rlm@1: if (y1 > y2) rlm@1: std::swap(y1, y2); rlm@1: if (x1 < 0) rlm@1: x1 = -1; rlm@1: if (y1 < 0) rlm@1: y1 = -1; rlm@1: if (x2 >= LUA_SCREEN_WIDTH) rlm@1: x2 = LUA_SCREEN_WIDTH; rlm@1: if (y2 >= LUA_SCREEN_HEIGHT) rlm@1: y2 = LUA_SCREEN_HEIGHT; rlm@1: rlm@1: if (!gui_checkbox(x1, y1, x2, y2)) rlm@1: return; rlm@1: rlm@1: //gui_prepare(); rlm@1: gui_drawline_internal(x1, y1, x2, y1, true, colour); rlm@1: gui_drawline_internal(x1, y2, x2, y2, true, colour); rlm@1: gui_drawline_internal(x1, y1, x1, y2, true, colour); rlm@1: gui_drawline_internal(x2, y1, x2, y2, true, colour); rlm@1: } rlm@1: rlm@1: // draw a circle on gui_data rlm@1: static void gui_drawcircle_internal(int x0, int y0, int radius, uint32 colour) rlm@1: { rlm@1: //gui_prepare(); rlm@1: if (radius < 0) rlm@1: radius = -radius; rlm@1: if (radius == 0) rlm@1: return; rlm@1: if (radius == 1) rlm@1: { rlm@1: gui_drawpixel_internal(x0, y0, colour); rlm@1: return; rlm@1: } rlm@1: rlm@1: // http://en.wikipedia.org/wiki/Midpoint_circle_algorithm rlm@1: int f = 1 - radius; rlm@1: int ddF_x = 1; rlm@1: int ddF_y = -2 * radius; rlm@1: int x = 0; rlm@1: int y = radius; rlm@1: rlm@1: if (!gui_checkbox(x0 - radius, y0 - radius, x0 + radius, y0 + radius)) rlm@1: return; rlm@1: rlm@1: gui_drawpixel_internal(x0, y0 + radius, colour); rlm@1: gui_drawpixel_internal(x0, y0 - radius, colour); rlm@1: gui_drawpixel_internal(x0 + radius, y0, colour); rlm@1: gui_drawpixel_internal(x0 - radius, y0, colour); rlm@1: rlm@1: // same pixel shouldn't be drawed twice, rlm@1: // because each pixel has opacity. rlm@1: // so now the routine gets ugly. rlm@1: while (true) rlm@1: { rlm@1: assert(ddF_x == 2 * x + 1); rlm@1: assert(ddF_y == -2 * y); rlm@1: assert(f == x * x + y * y - radius * radius + 2 * x - y + 1); rlm@1: if (f >= 0) rlm@1: { rlm@1: y--; rlm@1: ddF_y += 2; rlm@1: f += ddF_y; rlm@1: } rlm@1: rlm@1: x++; rlm@1: ddF_x += 2; rlm@1: f += ddF_x; rlm@1: if (x < y) rlm@1: { rlm@1: gui_drawpixel_internal(x0 + x, y0 + y, colour); rlm@1: gui_drawpixel_internal(x0 - x, y0 + y, colour); rlm@1: gui_drawpixel_internal(x0 + x, y0 - y, colour); rlm@1: gui_drawpixel_internal(x0 - x, y0 - y, colour); rlm@1: gui_drawpixel_internal(x0 + y, y0 + x, colour); rlm@1: gui_drawpixel_internal(x0 - y, y0 + x, colour); rlm@1: gui_drawpixel_internal(x0 + y, y0 - x, colour); rlm@1: gui_drawpixel_internal(x0 - y, y0 - x, colour); rlm@1: } rlm@1: else if (x == y) rlm@1: { rlm@1: gui_drawpixel_internal(x0 + x, y0 + y, colour); rlm@1: gui_drawpixel_internal(x0 - x, y0 + y, colour); rlm@1: gui_drawpixel_internal(x0 + x, y0 - y, colour); rlm@1: gui_drawpixel_internal(x0 - x, y0 - y, colour); rlm@1: break; rlm@1: } rlm@1: else rlm@1: break; rlm@1: } rlm@1: } rlm@1: rlm@1: // draw fill rect on gui_data rlm@1: static void gui_fillbox_internal(int x1, int y1, int x2, int y2, uint32 colour) rlm@1: { rlm@1: if (x1 > x2) rlm@1: std::swap(x1, x2); rlm@1: if (y1 > y2) rlm@1: std::swap(y1, y2); rlm@1: if (x1 < 0) rlm@1: x1 = 0; rlm@1: if (y1 < 0) rlm@1: y1 = 0; rlm@1: if (x2 >= LUA_SCREEN_WIDTH) rlm@1: x2 = LUA_SCREEN_WIDTH - 1; rlm@1: if (y2 >= LUA_SCREEN_HEIGHT) rlm@1: y2 = LUA_SCREEN_HEIGHT - 1; rlm@1: rlm@1: //gui_prepare(); rlm@1: int ix, iy; rlm@1: for (iy = y1; iy <= y2; iy++) rlm@1: { rlm@1: for (ix = x1; ix <= x2; ix++) rlm@1: { rlm@1: gui_drawpixel_fast(ix, iy, colour); rlm@1: } rlm@1: } rlm@1: } rlm@1: rlm@1: // fill a circle on gui_data rlm@1: static void gui_fillcircle_internal(int x0, int y0, int radius, uint32 colour) rlm@1: { rlm@1: //gui_prepare(); rlm@1: if (radius < 0) rlm@1: radius = -radius; rlm@1: if (radius == 0) rlm@1: return; rlm@1: if (radius == 1) rlm@1: { rlm@1: gui_drawpixel_internal(x0, y0, colour); rlm@1: return; rlm@1: } rlm@1: rlm@1: // http://en.wikipedia.org/wiki/Midpoint_circle_algorithm rlm@1: int f = 1 - radius; rlm@1: int ddF_x = 1; rlm@1: int ddF_y = -2 * radius; rlm@1: int x = 0; rlm@1: int y = radius; rlm@1: rlm@1: if (!gui_checkbox(x0 - radius, y0 - radius, x0 + radius, y0 + radius)) rlm@1: return; rlm@1: rlm@1: gui_drawline_internal(x0, y0 - radius, x0, y0 + radius, true, colour); rlm@1: rlm@1: while (true) rlm@1: { rlm@1: assert(ddF_x == 2 * x + 1); rlm@1: assert(ddF_y == -2 * y); rlm@1: assert(f == x * x + y * y - radius * radius + 2 * x - y + 1); rlm@1: if (f >= 0) rlm@1: { rlm@1: y--; rlm@1: ddF_y += 2; rlm@1: f += ddF_y; rlm@1: } rlm@1: rlm@1: x++; rlm@1: ddF_x += 2; rlm@1: f += ddF_x; rlm@1: rlm@1: if (x < y) rlm@1: { rlm@1: gui_drawline_internal(x0 + x, y0 - y, x0 + x, y0 + y, true, colour); rlm@1: gui_drawline_internal(x0 - x, y0 - y, x0 - x, y0 + y, true, colour); rlm@1: if (f >= 0) rlm@1: { rlm@1: gui_drawline_internal(x0 + y, y0 - x, x0 + y, y0 + x, true, colour); rlm@1: gui_drawline_internal(x0 - y, y0 - x, x0 - y, y0 + x, true, colour); rlm@1: } rlm@1: } rlm@1: else if (x == y) rlm@1: { rlm@1: gui_drawline_internal(x0 + x, y0 - y, x0 + x, y0 + y, true, colour); rlm@1: gui_drawline_internal(x0 - x, y0 - y, x0 - x, y0 + y, true, colour); rlm@1: break; rlm@1: } rlm@1: else rlm@1: break; rlm@1: } rlm@1: } rlm@1: rlm@1: // Helper for a simple hex parser rlm@1: static int hex2int(lua_State *L, char c) rlm@1: { rlm@1: if (c >= '0' && c <= '9') rlm@1: return c - '0'; rlm@1: if (c >= 'a' && c <= 'f') rlm@1: return c - 'a' + 10; rlm@1: if (c >= 'A' && c <= 'F') rlm@1: return c - 'A' + 10; rlm@1: return luaL_error(L, "invalid hex in colour"); rlm@1: } rlm@1: rlm@1: static const struct ColorMapping rlm@1: { rlm@1: const char *name; rlm@1: int value; rlm@1: } rlm@1: s_colorMapping[] = rlm@1: { rlm@1: { "white", 0xFFFFFFFF }, rlm@1: { "black", 0x000000FF }, rlm@1: { "clear", 0x00000000 }, rlm@1: { "gray", 0x7F7F7FFF }, rlm@1: { "grey", 0x7F7F7FFF }, rlm@1: { "red", 0xFF0000FF }, rlm@1: { "orange", 0xFF7F00FF }, rlm@1: { "yellow", 0xFFFF00FF }, rlm@1: { "chartreuse", 0x7FFF00FF }, rlm@1: { "green", 0x00FF00FF }, rlm@1: { "teal", 0x00FF7FFF }, rlm@1: { "cyan", 0x00FFFFFF }, rlm@1: { "blue", 0x0000FFFF }, rlm@1: { "purple", 0x7F00FFFF }, rlm@1: { "magenta", 0xFF00FFFF }, rlm@1: }; rlm@1: rlm@1: /** rlm@1: * Converts an integer or a string on the stack at the given rlm@1: * offset to a RGB32 colour. Several encodings are supported. rlm@1: * The user may construct their own RGB value, given a simple colour name, rlm@1: * or an HTML-style "#09abcd" colour. 16 bit reduction doesn't occur at this time. rlm@1: */ rlm@1: static inline bool str2colour(uint32 *colour, lua_State *L, const char *str) rlm@1: { rlm@1: if (str[0] == '#') rlm@1: { rlm@1: int color; rlm@1: sscanf(str + 1, "%X", &color); rlm@1: rlm@1: int len = strlen(str + 1); rlm@1: int missing = max(0, 8 - len); rlm@1: color <<= missing << 2; rlm@1: if (missing >= 2) rlm@1: color |= 0xFF; rlm@1: *colour = color; rlm@1: return true; rlm@1: } rlm@1: else rlm@1: { rlm@1: if (!strnicmp(str, "rand", 4)) rlm@1: { rlm@1: *colour = gen_rand32() | 0xFF; //((rand()*255/RAND_MAX) << 8) | ((rand()*255/RAND_MAX) << 16) | rlm@1: // ((rand()*255/RAND_MAX) << 24) | 0xFF; rlm@1: return true; rlm@1: } rlm@1: rlm@1: for (int i = 0; i < sizeof(s_colorMapping) / sizeof(*s_colorMapping); i++) rlm@1: { rlm@1: if (!stricmp(str, s_colorMapping[i].name)) rlm@1: { rlm@1: *colour = s_colorMapping[i].value; rlm@1: return true; rlm@1: } rlm@1: } rlm@1: } rlm@1: rlm@1: return false; rlm@1: } rlm@1: rlm@1: static inline uint32 gui_getcolour_wrapped(lua_State *L, int offset, bool hasDefaultValue, uint32 defaultColour) rlm@1: { rlm@1: switch (lua_type(L, offset)) rlm@1: { rlm@1: case LUA_TSTRING: rlm@1: { rlm@1: const char *str = lua_tostring(L, offset); rlm@1: uint32 colour; rlm@1: rlm@1: if (str2colour(&colour, L, str)) rlm@1: return colour; rlm@1: else rlm@1: { rlm@1: if (hasDefaultValue) rlm@1: return defaultColour; rlm@1: else rlm@1: return luaL_error(L, "unknown colour %s", str); rlm@1: } rlm@1: } rlm@1: rlm@1: case LUA_TNUMBER: rlm@1: { rlm@1: uint32 colour = (uint32) lua_tointeger(L, offset); rlm@1: return colour; rlm@1: } rlm@1: rlm@1: case LUA_TTABLE: rlm@1: { rlm@1: int color = 0xFF; rlm@1: lua_pushnil(L); // first key rlm@1: int keyIndex = lua_gettop(L); rlm@1: int valueIndex = keyIndex + 1; rlm@1: bool first = true; rlm@1: while (lua_next(L, offset)) rlm@1: { rlm@1: bool keyIsString = (lua_type(L, keyIndex) == LUA_TSTRING); rlm@1: bool keyIsNumber = (lua_type(L, keyIndex) == LUA_TNUMBER); rlm@1: int key = keyIsString ? tolower(*lua_tostring(L, keyIndex)) : (keyIsNumber ? lua_tointeger(L, keyIndex) : 0); rlm@1: int value = lua_tointeger(L, valueIndex); rlm@1: if (value < 0) value = 0; rlm@1: if (value > 255) value = 255; rlm@1: switch (key) rlm@1: { rlm@1: case 1: rlm@1: case 'r': rlm@1: color |= value << 24; break; rlm@1: case 2: rlm@1: case 'g': rlm@1: color |= value << 16; break; rlm@1: case 3: rlm@1: case 'b': rlm@1: color |= value << 8; break; rlm@1: case 4: rlm@1: case 'a': rlm@1: color = (color & ~0xFF) | value; break; rlm@1: } rlm@1: lua_pop(L, 1); rlm@1: } rlm@1: return color; rlm@1: } break; rlm@1: rlm@1: case LUA_TFUNCTION: rlm@1: luaL_error(L, "invalid colour"); // NYI rlm@1: return 0; rlm@1: rlm@1: default: rlm@1: if (hasDefaultValue) rlm@1: return defaultColour; rlm@1: else rlm@1: return luaL_error(L, "invalid colour"); rlm@1: } rlm@1: } rlm@1: rlm@1: static uint32 gui_getcolour(lua_State *L, int offset) rlm@1: { rlm@1: uint32 colour; rlm@1: int a, r, g, b; rlm@1: rlm@1: colour = gui_getcolour_wrapped(L, offset, false, 0); rlm@1: a = ((colour & 0xff) * transparencyModifier) / 255; rlm@1: if (a > 255) rlm@1: a = 255; rlm@1: b = (colour >> 8) & 0xff; rlm@1: g = (colour >> 16) & 0xff; rlm@1: r = (colour >> 24) & 0xff; rlm@1: return LUA_BUILD_PIXEL(a, r, g, b); rlm@1: } rlm@1: rlm@1: static uint32 gui_optcolour(lua_State *L, int offset, uint32 defaultColour) rlm@1: { rlm@1: uint32 colour; rlm@1: int a, r, g, b; rlm@1: uint8 defA, defB, defG, defR; rlm@1: rlm@1: LUA_DECOMPOSE_PIXEL(defaultColour, defA, defR, defG, defB); rlm@1: defaultColour = (defR << 24) | (defG << 16) | (defB << 8) | defA; rlm@1: rlm@1: colour = gui_getcolour_wrapped(L, offset, true, defaultColour); rlm@1: a = ((colour & 0xff) * transparencyModifier) / 255; rlm@1: if (a > 255) rlm@1: a = 255; rlm@1: b = (colour >> 8) & 0xff; rlm@1: g = (colour >> 16) & 0xff; rlm@1: r = (colour >> 24) & 0xff; rlm@1: return LUA_BUILD_PIXEL(a, r, g, b); rlm@1: } rlm@1: rlm@1: // gui.drawpixel(x,y,colour) rlm@1: static int gui_drawpixel(lua_State *L) rlm@1: { rlm@1: int x = luaL_checkinteger(L, 1); rlm@1: int y = luaL_checkinteger(L, 2); rlm@1: rlm@1: uint32 colour = gui_getcolour(L, 3); rlm@1: rlm@1: // if (!gui_check_boundary(x, y)) rlm@1: // luaL_error(L,"bad coordinates"); rlm@1: gui_prepare(); rlm@1: rlm@1: gui_drawpixel_internal(x, y, colour); rlm@1: rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // gui.drawline(x1,y1,x2,y2,color,skipFirst) rlm@1: static int gui_drawline(lua_State *L) rlm@1: { rlm@1: int x1, y1, x2, y2; rlm@1: uint32 color; rlm@1: x1 = luaL_checkinteger(L, 1); rlm@1: y1 = luaL_checkinteger(L, 2); rlm@1: x2 = luaL_checkinteger(L, 3); rlm@1: y2 = luaL_checkinteger(L, 4); rlm@1: color = gui_optcolour(L, 5, LUA_BUILD_PIXEL(255, 255, 255, 255)); rlm@1: int skipFirst = lua_toboolean(L, 6); rlm@1: rlm@1: gui_prepare(); rlm@1: rlm@1: gui_drawline_internal(x2, y2, x1, y1, !skipFirst, color); rlm@1: rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // gui.drawbox(x1, y1, x2, y2, fillcolor, outlinecolor) rlm@1: static int gui_drawbox(lua_State *L) rlm@1: { rlm@1: int x1, y1, x2, y2; rlm@1: uint32 fillcolor; rlm@1: uint32 outlinecolor; rlm@1: rlm@1: x1 = luaL_checkinteger(L, 1); rlm@1: y1 = luaL_checkinteger(L, 2); rlm@1: x2 = luaL_checkinteger(L, 3); rlm@1: y2 = luaL_checkinteger(L, 4); rlm@1: fillcolor = gui_optcolour(L, 5, LUA_BUILD_PIXEL(63, 255, 255, 255)); rlm@1: outlinecolor = gui_optcolour(L, 6, LUA_BUILD_PIXEL(255, LUA_PIXEL_R(fillcolor), LUA_PIXEL_G(fillcolor), LUA_PIXEL_B(fillcolor))); rlm@1: rlm@1: if (x1 > x2) rlm@1: std::swap(x1, x2); rlm@1: if (y1 > y2) rlm@1: std::swap(y1, y2); rlm@1: rlm@1: gui_prepare(); rlm@1: rlm@1: gui_drawbox_internal(x1, y1, x2, y2, outlinecolor); rlm@1: if ((x2 - x1) >= 2 && (y2 - y1) >= 2) rlm@1: gui_fillbox_internal(x1 + 1, y1 + 1, x2 - 1, y2 - 1, fillcolor); rlm@1: rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // gui.drawcircle(x0, y0, radius, colour) rlm@1: static int gui_drawcircle(lua_State *L) rlm@1: { rlm@1: int x, y, r; rlm@1: uint32 colour; rlm@1: rlm@1: x = luaL_checkinteger(L, 1); rlm@1: y = luaL_checkinteger(L, 2); rlm@1: r = luaL_checkinteger(L, 3); rlm@1: colour = gui_getcolour(L, 4); rlm@1: rlm@1: gui_prepare(); rlm@1: rlm@1: gui_drawcircle_internal(x, y, r, colour); rlm@1: rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // gui.fillbox(x1, y1, x2, y2, colour) rlm@1: static int gui_fillbox(lua_State *L) rlm@1: { rlm@1: int x1, y1, x2, y2; rlm@1: uint32 colour; rlm@1: rlm@1: x1 = luaL_checkinteger(L, 1); rlm@1: y1 = luaL_checkinteger(L, 2); rlm@1: x2 = luaL_checkinteger(L, 3); rlm@1: y2 = luaL_checkinteger(L, 4); rlm@1: colour = gui_getcolour(L, 5); rlm@1: rlm@1: // if (!gui_check_boundary(x1, y1)) rlm@1: // luaL_error(L,"bad coordinates"); rlm@1: // rlm@1: // if (!gui_check_boundary(x2, y2)) rlm@1: // luaL_error(L,"bad coordinates"); rlm@1: gui_prepare(); rlm@1: rlm@1: if (!gui_checkbox(x1, y1, x2, y2)) rlm@1: return 0; rlm@1: rlm@1: gui_fillbox_internal(x1, y1, x2, y2, colour); rlm@1: rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // gui.fillcircle(x0, y0, radius, colour) rlm@1: static int gui_fillcircle(lua_State *L) rlm@1: { rlm@1: int x, y, r; rlm@1: uint32 colour; rlm@1: rlm@1: x = luaL_checkinteger(L, 1); rlm@1: y = luaL_checkinteger(L, 2); rlm@1: r = luaL_checkinteger(L, 3); rlm@1: colour = gui_getcolour(L, 4); rlm@1: rlm@1: gui_prepare(); rlm@1: rlm@1: gui_fillcircle_internal(x, y, r, colour); rlm@1: rlm@1: return 0; rlm@1: } rlm@1: rlm@1: static int gui_getpixel(lua_State *L) rlm@1: { rlm@1: int x = luaL_checkinteger(L, 1); rlm@1: int y = luaL_checkinteger(L, 2); rlm@1: rlm@1: int pixWidth = 240, pixHeight = 160; rlm@1: int scrWidth = 240, scrHeight = 160; rlm@1: int scrOffsetX = 0, scrOffsetY = 0; rlm@1: int pitch; rlm@1: if (!systemIsRunningGBA()) rlm@1: { rlm@1: if (gbBorderOn) rlm@1: { rlm@1: pixWidth = 256, pixHeight = 224; rlm@1: scrOffsetX = 48, scrOffsetY = 40; rlm@1: } rlm@1: else rlm@1: { rlm@1: pixWidth = 160, pixHeight = 144; rlm@1: } rlm@1: scrWidth = 160, scrHeight = 144; rlm@1: } rlm@1: pitch = pixWidth * (systemColorDepth / 8) + (systemColorDepth == 24 ? 0 : 4); rlm@1: scrOffsetY++; // don't know why it's needed rlm@1: rlm@1: if (!(x >= 0 && y >= 0 && x < scrWidth && y < scrHeight) /*!gui_check_boundary(x,y)*/) rlm@1: { rlm@1: lua_pushinteger(L, 0); rlm@1: lua_pushinteger(L, 0); rlm@1: lua_pushinteger(L, 0); rlm@1: } rlm@1: else rlm@1: { rlm@1: switch (systemColorDepth) rlm@1: { rlm@1: case 16: rlm@1: { rlm@1: uint16 *screen = (uint16 *) (&pix[scrOffsetY * pitch + scrOffsetX * 2]); rlm@1: uint16 pixColor = screen[y * pitch / 2 + x]; rlm@1: lua_pushinteger(L, (pixColor >> 8) & 0xF8); // red rlm@1: lua_pushinteger(L, (pixColor >> 3) & 0xFC); // green rlm@1: lua_pushinteger(L, (pixColor << 3) & 0xF8); // blue rlm@1: } rlm@1: break; rlm@1: case 24: rlm@1: { rlm@1: uint8 *screen = &pix[scrOffsetY * pitch + scrOffsetX * 3]; rlm@1: lua_pushinteger(L, screen[y * pitch + x * 3 + 2]); // red rlm@1: lua_pushinteger(L, screen[y * pitch + x * 3 + 1]); // green rlm@1: lua_pushinteger(L, screen[y * pitch + x * 3 + 0]); // blue rlm@1: } rlm@1: break; rlm@1: case 32: rlm@1: { rlm@1: uint8 *screen = &pix[scrOffsetY * pitch + scrOffsetX * 4]; rlm@1: lua_pushinteger(L, screen[y * pitch + x * 4 + 2]); // red rlm@1: lua_pushinteger(L, screen[y * pitch + x * 4 + 1]); // green rlm@1: lua_pushinteger(L, screen[y * pitch + x * 4 + 0]); // blue rlm@1: } rlm@1: break; rlm@1: default: rlm@1: lua_pushinteger(L, 0); rlm@1: lua_pushinteger(L, 0); rlm@1: lua_pushinteger(L, 0); rlm@1: break; rlm@1: } rlm@1: } rlm@1: return 3; rlm@1: } rlm@1: rlm@1: static int gui_parsecolor(lua_State *L) rlm@1: { rlm@1: int r, g, b, a; rlm@1: uint32 color = gui_getcolour(L, 1); rlm@1: LUA_DECOMPOSE_PIXEL(color, a, r, g, b); rlm@1: lua_pushinteger(L, r); rlm@1: lua_pushinteger(L, g); rlm@1: lua_pushinteger(L, b); rlm@1: lua_pushinteger(L, a); rlm@1: return 4; rlm@1: } rlm@1: rlm@1: // gui.gdscreenshot() rlm@1: // rlm@1: // Returns a screen shot as a string in gd's v1 file format. rlm@1: // This allows us to make screen shots available without gd installed locally. rlm@1: // Users can also just grab pixels via substring selection. rlm@1: // rlm@1: // I think... Does lua support grabbing byte values from a string? // yes, string.byte(str,offset) rlm@1: // Well, either way, just install gd and do what you like with it. rlm@1: // It really is easier that way. rlm@1: rlm@1: // example: gd.createFromGdStr(gui.gdscreenshot()):png("outputimage.png") rlm@1: static int gui_gdscreenshot(lua_State *L) rlm@1: { rlm@1: int xofs = 0, yofs = 0, ppl = 240, width = 240, height = 160; rlm@1: if (!systemIsRunningGBA()) rlm@1: { rlm@1: if (gbBorderOn) rlm@1: xofs = 48, yofs = 40, ppl = 256; rlm@1: else rlm@1: ppl = 160; rlm@1: width = 160, height = 144; rlm@1: } rlm@1: rlm@1: yofs++; rlm@1: rlm@1: //int pitch = (((ppl * systemColorDepth + 7)>>3)+3)&~3; rlm@1: int pitch = ppl * (systemColorDepth / 8) + (systemColorDepth == 24 ? 0 : 4); rlm@1: uint8 *screen = &pix[yofs * pitch + xofs * (systemColorDepth / 8)]; rlm@1: rlm@1: int size = 11 + width * height * 4; rlm@1: char *str = new char[size + 1]; rlm@1: str[size] = 0; rlm@1: rlm@1: unsigned char *ptr = (unsigned char *)str; rlm@1: rlm@1: // GD format header for truecolor image (11 bytes) rlm@1: *ptr++ = (65534 >> 8) & 0xFF; rlm@1: *ptr++ = (65534) & 0xFF; rlm@1: *ptr++ = (width >> 8) & 0xFF; rlm@1: *ptr++ = (width) & 0xFF; rlm@1: *ptr++ = (height >> 8) & 0xFF; rlm@1: *ptr++ = (height) & 0xFF; rlm@1: *ptr++ = 1; rlm@1: *ptr++ = 255; rlm@1: *ptr++ = 255; rlm@1: *ptr++ = 255; rlm@1: *ptr++ = 255; rlm@1: rlm@1: GetColorFunc getColor; rlm@1: getColorIOFunc(systemColorDepth, &getColor, NULL); rlm@1: rlm@1: int x, y; rlm@1: for (y = 0; y < height; y++) rlm@1: { rlm@1: uint8 *s = &screen[y * pitch]; rlm@1: for (x = 0; x < width; x++, s += systemColorDepth / 8) rlm@1: { rlm@1: uint8 r, g, b; rlm@1: getColor(s, &r, &g, &b); rlm@1: rlm@1: *ptr++ = 0; rlm@1: *ptr++ = r; rlm@1: *ptr++ = g; rlm@1: *ptr++ = b; rlm@1: } rlm@1: } rlm@1: rlm@1: lua_pushlstring(L, str, size); rlm@1: delete[] str; rlm@1: return 1; rlm@1: } rlm@1: rlm@1: // gui.opacity(number alphaValue) rlm@1: // sets the transparency of subsequent draw calls rlm@1: // 0.0 is completely transparent, 1.0 is completely opaque rlm@1: // non-integer values are supported and meaningful, as are values greater than 1.0 rlm@1: // it is not necessary to use this function to get transparency (or the less-recommended gui.transparency() either), rlm@1: // because you can provide an alpha value in the color argument of each draw call. rlm@1: rlm@1: // however, it can be convenient to be able to globally modify the drawing transparency rlm@1: static int gui_setopacity(lua_State *L) rlm@1: { rlm@1: double opacF = luaL_checknumber(L, 1); rlm@1: transparencyModifier = (int)(opacF * 255); rlm@1: if (transparencyModifier < 0) rlm@1: transparencyModifier = 0; rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // gui.transparency(int strength) rlm@1: // rlm@1: rlm@1: // 0 = solid, rlm@1: static int gui_transparency(lua_State *L) rlm@1: { rlm@1: double trans = luaL_checknumber(L, 1); rlm@1: transparencyModifier = (int)((4.0 - trans) / 4.0 * 255); rlm@1: if (transparencyModifier < 0) rlm@1: transparencyModifier = 0; rlm@1: return 0; rlm@1: } rlm@1: rlm@1: static const uint32 Small_Font_Data[] = rlm@1: { rlm@1: 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 32 rlm@1: 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000000, 0x00000700, 0x00000000, // 33 ! rlm@1: 0x00000000, 0x00040002, 0x00050003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 34 " rlm@1: 0x00000000, 0x00040002, 0x00050403, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 35 # rlm@1: 0x00000000, 0x00040300, 0x00000403, 0x00000500, 0x00070600, 0x00000706, 0x00000000, // 36 $ rlm@1: 0x00000000, 0x00000002, 0x00050000, 0x00000500, 0x00000005, 0x00080000, 0x00000000, // 37 % rlm@1: 0x00000000, 0x00000300, 0x00050003, 0x00000500, 0x00070005, 0x00080700, 0x00000000, // 38 & rlm@1: 0x00000000, 0x00000300, 0x00000400, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 39 ' rlm@1: 0x00000000, 0x00000300, 0x00000003, 0x00000004, 0x00000005, 0x00000700, 0x00000000, // 40 ( rlm@1: 0x00000000, 0x00000300, 0x00050000, 0x00060000, 0x00070000, 0x00000700, 0x00000000, // 41 ) rlm@1: 0x00000000, 0x00000000, 0x00000400, 0x00060504, 0x00000600, 0x00080006, 0x00000000, // 42 * rlm@1: 0x00000000, 0x00000000, 0x00000400, 0x00060504, 0x00000600, 0x00000000, 0x00000000, // 43 + rlm@1: 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000600, 0x00000700, 0x00000007, // 44 , rlm@1: 0x00000000, 0x00000000, 0x00000000, 0x00060504, 0x00000000, 0x00000000, 0x00000000, // 45 - rlm@1: 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000700, 0x00000000, // 46 . rlm@1: 0x00030000, 0x00040000, 0x00000400, 0x00000500, 0x00000005, 0x00000006, 0x00000000, // 47 / rlm@1: 0x00000000, 0x00000300, 0x00050003, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 48 0 rlm@1: 0x00000000, 0x00000300, 0x00000403, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 49 1 rlm@1: 0x00000000, 0x00000302, 0x00050000, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 50 2 rlm@1: 0x00000000, 0x00000302, 0x00050000, 0x00000504, 0x00070000, 0x00000706, 0x00000000, // 51 3 rlm@1: 0x00000000, 0x00000300, 0x00000003, 0x00060004, 0x00070605, 0x00080000, 0x00000000, // 52 4 rlm@1: 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00070000, 0x00000706, 0x00000000, // 53 5 rlm@1: 0x00000000, 0x00000300, 0x00000003, 0x00000504, 0x00070005, 0x00000700, 0x00000000, // 54 6 rlm@1: 0x00000000, 0x00040302, 0x00050000, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 55 7 rlm@1: 0x00000000, 0x00000300, 0x00050003, 0x00000500, 0x00070005, 0x00000700, 0x00000000, // 56 8 rlm@1: 0x00000000, 0x00000300, 0x00050003, 0x00060500, 0x00070000, 0x00000700, 0x00000000, // 57 9 rlm@1: 0x00000000, 0x00000000, 0x00000400, 0x00000000, 0x00000000, 0x00000700, 0x00000000, // 58 : rlm@1: 0x00000000, 0x00000000, 0x00000000, 0x00000500, 0x00000000, 0x00000700, 0x00000007, // 59 ; rlm@1: 0x00000000, 0x00040000, 0x00000400, 0x00000004, 0x00000600, 0x00080000, 0x00000000, // 60 < rlm@1: 0x00000000, 0x00000000, 0x00050403, 0x00000000, 0x00070605, 0x00000000, 0x00000000, // 61 = rlm@1: 0x00000000, 0x00000002, 0x00000400, 0x00060000, 0x00000600, 0x00000006, 0x00000000, // 62 > rlm@1: 0x00000000, 0x00000302, 0x00050000, 0x00000500, 0x00000000, 0x00000700, 0x00000000, // 63 ? rlm@1: 0x00000000, 0x00000300, 0x00050400, 0x00060004, 0x00070600, 0x00000000, 0x00000000, // 64 @ rlm@1: 0x00000000, 0x00000300, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 65 A rlm@1: 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00070005, 0x00000706, 0x00000000, // 66 B rlm@1: 0x00000000, 0x00040300, 0x00000003, 0x00000004, 0x00000005, 0x00080700, 0x00000000, // 67 C rlm@1: 0x00000000, 0x00000302, 0x00050003, 0x00060004, 0x00070005, 0x00000706, 0x00000000, // 68 D rlm@1: 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00000005, 0x00080706, 0x00000000, // 69 E rlm@1: 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 70 F rlm@1: 0x00000000, 0x00040300, 0x00000003, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 71 G rlm@1: 0x00000000, 0x00040002, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 72 H rlm@1: 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 73 I rlm@1: 0x00000000, 0x00040000, 0x00050000, 0x00060000, 0x00070005, 0x00000700, 0x00000000, // 74 J rlm@1: 0x00000000, 0x00040002, 0x00050003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 75 K rlm@1: 0x00000000, 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00080706, 0x00000000, // 76 l rlm@1: 0x00000000, 0x00040002, 0x00050403, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 77 M rlm@1: 0x00000000, 0x00000302, 0x00050003, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 78 N rlm@1: 0x00000000, 0x00040302, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00000000, // 79 O rlm@1: 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 80 P rlm@1: 0x00000000, 0x00040302, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00090000, // 81 Q rlm@1: 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 82 R rlm@1: 0x00000000, 0x00040300, 0x00000003, 0x00000500, 0x00070000, 0x00000706, 0x00000000, // 83 S rlm@1: 0x00000000, 0x00040302, 0x00000400, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 84 T rlm@1: 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00000000, // 85 U rlm@1: 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00000600, 0x00000700, 0x00000000, // 86 V rlm@1: 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 87 W rlm@1: 0x00000000, 0x00040002, 0x00050003, 0x00000500, 0x00070005, 0x00080006, 0x00000000, // 88 X rlm@1: 0x00000000, 0x00040002, 0x00050003, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 89 Y rlm@1: 0x00000000, 0x00040302, 0x00050000, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 90 Z rlm@1: 0x00000000, 0x00040300, 0x00000400, 0x00000500, 0x00000600, 0x00080700, 0x00000000, // 91 [ rlm@1: 0x00000000, 0x00000002, 0x00000400, 0x00000500, 0x00070000, 0x00080000, 0x00000000, // 92 '\' rlm@1: 0x00000000, 0x00000302, 0x00000400, 0x00000500, 0x00000600, 0x00000706, 0x00000000, // 93 ] rlm@1: 0x00000000, 0x00000300, 0x00050003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 94 ^ rlm@1: 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00080706, 0x00000000, // 95 _ rlm@1: 0x00000000, 0x00000002, 0x00000400, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 96 ` rlm@1: 0x00000000, 0x00000000, 0x00050400, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 97 a rlm@1: 0x00000000, 0x00000002, 0x00000003, 0x00000504, 0x00070005, 0x00000706, 0x00000000, // 98 b rlm@1: 0x00000000, 0x00000000, 0x00050400, 0x00000004, 0x00000005, 0x00080700, 0x00000000, // 99 c rlm@1: 0x00000000, 0x00040000, 0x00050000, 0x00060500, 0x00070005, 0x00080700, 0x00000000, // 100 d rlm@1: 0x00000000, 0x00000000, 0x00050400, 0x00060504, 0x00000005, 0x00080700, 0x00000000, // 101 e rlm@1: 0x00000000, 0x00040300, 0x00000003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 102 f rlm@1: 0x00000000, 0x00000000, 0x00050400, 0x00060004, 0x00070600, 0x00080000, 0x00000807, // 103 g rlm@1: 0x00000000, 0x00000002, 0x00000003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 104 h rlm@1: 0x00000000, 0x00000300, 0x00000000, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 105 i rlm@1: 0x00000000, 0x00000300, 0x00000000, 0x00000500, 0x00000600, 0x00000700, 0x00000007, // 106 j rlm@1: 0x00000000, 0x00000002, 0x00000003, 0x00060004, 0x00000605, 0x00080006, 0x00000000, // 107 k rlm@1: 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000600, 0x00080000, 0x00000000, // 108 l rlm@1: 0x00000000, 0x00000000, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 109 m rlm@1: 0x00000000, 0x00000000, 0x00000403, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 110 n rlm@1: 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 111 o rlm@1: 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00000605, 0x00000006, 0x00000007, // 112 p rlm@1: 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070600, 0x00080000, 0x00090000, // 113 q rlm@1: 0x00000000, 0x00000000, 0x00050003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 114 r rlm@1: 0x00000000, 0x00000000, 0x00050400, 0x00000004, 0x00070600, 0x00000706, 0x00000000, // 115 s rlm@1: 0x00000000, 0x00000300, 0x00050403, 0x00000500, 0x00000600, 0x00080000, 0x00000000, // 116 t rlm@1: 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 117 u rlm@1: 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 118 v rlm@1: 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 119 w rlm@1: 0x00000000, 0x00000000, 0x00050003, 0x00000500, 0x00070005, 0x00080006, 0x00000000, // 120 x rlm@1: 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00000600, 0x00000700, 0x00000007, // 121 y rlm@1: 0x00000000, 0x00000000, 0x00050403, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 122 z rlm@1: 0x00000000, 0x00040300, 0x00000400, 0x00000504, 0x00000600, 0x00080700, 0x00000000, // 123 { rlm@1: 0x00000000, 0x00000300, 0x00000400, 0x00000000, 0x00000600, 0x00000700, 0x00000000, // 124 | rlm@1: 0x00000000, 0x00000302, 0x00000400, 0x00060500, 0x00000600, 0x00000706, 0x00000000, // 125 } rlm@1: 0x00000000, 0x00000302, 0x00050000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 126 ~ rlm@1: 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070605, 0x00000000, 0x00000000, // 127  rlm@1: 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, rlm@1: }; rlm@1: rlm@1: static void PutTextInternal(const char *str, int len, short x, short y, int color, int backcolor) rlm@1: { rlm@1: int Opac = (color >> 24) & 0xFF; rlm@1: int backOpac = (backcolor >> 24) & 0xFF; rlm@1: int origX = x; rlm@1: rlm@1: if (!Opac && !backOpac) rlm@1: return; rlm@1: rlm@1: while (*str && len && y < LUA_SCREEN_HEIGHT) rlm@1: { rlm@1: int c = *str++; rlm@1: while (x > LUA_SCREEN_WIDTH && c != '\n') rlm@1: { rlm@1: c = *str; rlm@1: if (c == '\0') rlm@1: break; rlm@1: str++; rlm@1: } rlm@1: rlm@1: if (c == '\n') rlm@1: { rlm@1: x = origX; rlm@1: y += 8; rlm@1: continue; rlm@1: } rlm@1: else if (c == '\t') // just in case rlm@1: { rlm@1: const int tabSpace = 8; rlm@1: x += (tabSpace - (((x - origX) / 4) % tabSpace)) * 4; rlm@1: continue; rlm@1: } rlm@1: rlm@1: if ((unsigned int)(c - 32) >= 96) rlm@1: continue; rlm@1: rlm@1: const unsigned char *Cur_Glyph = (const unsigned char *) &Small_Font_Data + (c - 32) * 7 * 4; rlm@1: rlm@1: for (int y2 = 0; y2 < 8; y2++) rlm@1: { rlm@1: unsigned int glyphLine = *((unsigned int *)Cur_Glyph + y2); rlm@1: for (int x2 = -1; x2 < 4; x2++) rlm@1: { rlm@1: int shift = x2 << 3; rlm@1: int mask = 0xFF << shift; rlm@1: int intensity = (glyphLine & mask) >> shift; rlm@1: rlm@1: if (intensity && x2 >= 0 && y2 < 7) rlm@1: { rlm@1: //int xdraw = max(0,min(LUA_SCREEN_WIDTH - 1,x+x2)); rlm@1: //int ydraw = max(0,min(LUA_SCREEN_HEIGHT - 1,y+y2)); rlm@1: //gui_drawpixel_fast(xdraw, ydraw, color); rlm@1: gui_drawpixel_internal(x + x2, y + y2, color); rlm@1: } rlm@1: else if (backOpac) rlm@1: { rlm@1: for (int y3 = max(0, y2 - 1); y3 <= min(6, y2 + 1); y3++) rlm@1: { rlm@1: unsigned int glyphLine = *((unsigned int *)Cur_Glyph + y3); rlm@1: for (int x3 = max(0, x2 - 1); x3 <= min(3, x2 + 1); x3++) rlm@1: { rlm@1: int shift = x3 << 3; rlm@1: int mask = 0xFF << shift; rlm@1: intensity |= (glyphLine & mask) >> shift; rlm@1: if (intensity) rlm@1: goto draw_outline; // speedup? rlm@1: } rlm@1: } rlm@1: rlm@1: draw_outline: rlm@1: if (intensity) rlm@1: { rlm@1: //int xdraw = max(0,min(LUA_SCREEN_WIDTH - 1,x+x2)); rlm@1: //int ydraw = max(0,min(LUA_SCREEN_HEIGHT - 1,y+y2)); rlm@1: //gui_drawpixel_fast(xdraw, ydraw, backcolor); rlm@1: gui_drawpixel_internal(x + x2, y + y2, backcolor); rlm@1: } rlm@1: } rlm@1: } rlm@1: } rlm@1: rlm@1: x += 4; rlm@1: len--; rlm@1: } rlm@1: } rlm@1: rlm@1: static int strlinelen(const char *string) rlm@1: { rlm@1: const char *s = string; rlm@1: while (*s && *s != '\n') rlm@1: s++; rlm@1: if (*s) rlm@1: s++; rlm@1: return s - string; rlm@1: } rlm@1: rlm@1: static void LuaDisplayString(const char *string, int y, int x, uint32 color, uint32 outlineColor) rlm@1: { rlm@1: if (!string) rlm@1: return; rlm@1: rlm@1: gui_prepare(); rlm@1: rlm@1: PutTextInternal(string, strlen(string), x, y, color, outlineColor); rlm@1: rlm@1: /* rlm@1: const char* ptr = string; rlm@1: while(*ptr && y < LUA_SCREEN_HEIGHT) rlm@1: { rlm@1: int len = strlinelen(ptr); rlm@1: int skip = 0; rlm@1: if(len < 1) len = 1; rlm@1: rlm@1: // break up the line if it's too long to display otherwise rlm@1: if(len > 63) rlm@1: { rlm@1: len = 63; rlm@1: const char* ptr2 = ptr + len-1; rlm@1: for(int j = len-1; j; j--, ptr2--) rlm@1: { rlm@1: if(*ptr2 == ' ' || *ptr2 == '\t') rlm@1: { rlm@1: len = j; rlm@1: skip = 1; rlm@1: break; rlm@1: } rlm@1: } rlm@1: } rlm@1: rlm@1: int xl = 0; rlm@1: int yl = 0; rlm@1: int xh = (LUA_SCREEN_WIDTH - 1 - 1) - 4*len; rlm@1: int yh = LUA_SCREEN_HEIGHT - 1; rlm@1: int x2 = min(max(x,xl),xh); rlm@1: int y2 = min(max(y,yl),yh); rlm@1: rlm@1: PutTextInternal(ptr,len,x2,y2,color,outlineColor); rlm@1: rlm@1: ptr += len + skip; rlm@1: y += 8; rlm@1: } rlm@1: */ rlm@1: } rlm@1: rlm@1: // gui.text(int x, int y, string msg) rlm@1: // rlm@1: // Displays the given text on the screen, using the same font and techniques as the rlm@1: rlm@1: // main HUD. rlm@1: static int gui_text(lua_State *L) rlm@1: { rlm@1: //extern int font_height; rlm@1: const char *msg; rlm@1: int x, y; rlm@1: uint32 colour, borderColour; rlm@1: rlm@1: x = luaL_checkinteger(L, 1); rlm@1: y = luaL_checkinteger(L, 2); rlm@1: //msg = luaL_checkstring(L, 3); rlm@1: msg = toCString(L, 3); rlm@1: rlm@1: // if (x < 0 || x >= LUA_SCREEN_WIDTH || y < 0 || y >= (LUA_SCREEN_HEIGHT - font_height)) rlm@1: // luaL_error(L,"bad coordinates"); rlm@1: colour = gui_optcolour(L, 4, LUA_BUILD_PIXEL(255, 255, 255, 255)); rlm@1: borderColour = gui_optcolour(L, 5, LUA_BUILD_PIXEL(255, 0, 0, 0)); rlm@1: rlm@1: gui_prepare(); rlm@1: rlm@1: LuaDisplayString(msg, y, x, colour, borderColour); rlm@1: rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // gui.gdoverlay([int dx=0, int dy=0,] string str [, sx=0, sy=0, sw, sh] [, float alphamul=1.0]) rlm@1: // rlm@1: // Overlays the given image on the screen. rlm@1: rlm@1: // example: gui.gdoverlay(gd.createFromPng("myimage.png"):gdStr()) rlm@1: static int gui_gdoverlay(lua_State *L) rlm@1: { rlm@1: int argCount = lua_gettop(L); rlm@1: rlm@1: int xStartDst = 0; rlm@1: int yStartDst = 0; rlm@1: int xStartSrc = 0; rlm@1: int yStartSrc = 0; rlm@1: rlm@1: int index = 1; rlm@1: if (lua_type(L, index) == LUA_TNUMBER) rlm@1: { rlm@1: xStartDst = lua_tointeger(L, index++); rlm@1: if (lua_type(L, index) == LUA_TNUMBER) rlm@1: yStartDst = lua_tointeger(L, index++); rlm@1: } rlm@1: rlm@1: luaL_checktype(L, index, LUA_TSTRING); rlm@1: rlm@1: const unsigned char *ptr = (const unsigned char *)lua_tostring(L, index++); rlm@1: rlm@1: if (ptr[0] != 255 || (ptr[1] != 254 && ptr[1] != 255)) rlm@1: luaL_error(L, "bad image data"); rlm@1: rlm@1: bool trueColor = (ptr[1] == 254); rlm@1: ptr += 2; rlm@1: rlm@1: int imgwidth = *ptr++ << 8; rlm@1: imgwidth |= *ptr++; rlm@1: rlm@1: int width = imgwidth; rlm@1: int imgheight = *ptr++ << 8; rlm@1: imgheight |= *ptr++; rlm@1: rlm@1: int height = imgheight; rlm@1: if ((!trueColor && *ptr) || (trueColor && !*ptr)) rlm@1: luaL_error(L, "bad image data"); rlm@1: ptr++; rlm@1: rlm@1: int pitch = imgwidth * (trueColor ? 4 : 1); rlm@1: rlm@1: if ((argCount - index + 1) >= 4) rlm@1: { rlm@1: xStartSrc = luaL_checkinteger(L, index++); rlm@1: yStartSrc = luaL_checkinteger(L, index++); rlm@1: width = luaL_checkinteger(L, index++); rlm@1: height = luaL_checkinteger(L, index++); rlm@1: } rlm@1: rlm@1: int alphaMul = transparencyModifier; rlm@1: if (lua_isnumber(L, index)) rlm@1: alphaMul = (int)(alphaMul * lua_tonumber(L, index++)); rlm@1: if (alphaMul <= 0) rlm@1: return 0; rlm@1: rlm@1: // since there aren't that many possible opacity levels, rlm@1: // do the opacity modification calculations beforehand instead of per pixel rlm@1: int opacMap[256]; rlm@1: for (int i = 0; i < 128; i++) rlm@1: { rlm@1: int opac = 255 - ((i << 1) | (i & 1)); // gdAlphaMax = 127, not 255 rlm@1: opac = (opac * alphaMul) / 255; rlm@1: if (opac < 0) rlm@1: opac = 0; rlm@1: if (opac > 255) rlm@1: opac = 255; rlm@1: opacMap[i] = opac; rlm@1: } rlm@1: rlm@1: for (int i = 128; i < 256; i++) rlm@1: opacMap[i] = 0; // what should we do for them, actually? rlm@1: int colorsTotal = 0; rlm@1: if (!trueColor) rlm@1: { rlm@1: colorsTotal = *ptr++ << 8; rlm@1: colorsTotal |= *ptr++; rlm@1: } rlm@1: rlm@1: int transparent = *ptr++ << 24; rlm@1: transparent |= *ptr++ << 16; rlm@1: transparent |= *ptr++ << 8; rlm@1: transparent |= *ptr++; rlm@1: struct rlm@1: { rlm@1: uint8 r, g, b, a; rlm@1: } pal[256]; rlm@1: if (!trueColor) rlm@1: for (int i = 0; i < 256; i++) rlm@1: { rlm@1: pal[i].r = *ptr++; rlm@1: pal[i].g = *ptr++; rlm@1: pal[i].b = *ptr++; rlm@1: pal[i].a = opacMap[*ptr++]; rlm@1: } rlm@1: rlm@1: // some of clippings rlm@1: if (xStartSrc < 0) rlm@1: { rlm@1: width += xStartSrc; rlm@1: xStartDst -= xStartSrc; rlm@1: xStartSrc = 0; rlm@1: } rlm@1: rlm@1: if (yStartSrc < 0) rlm@1: { rlm@1: height += yStartSrc; rlm@1: yStartDst -= yStartSrc; rlm@1: yStartSrc = 0; rlm@1: } rlm@1: rlm@1: if (xStartSrc + width >= imgwidth) rlm@1: width = imgwidth - xStartSrc; rlm@1: if (yStartSrc + height >= imgheight) rlm@1: height = imgheight - yStartSrc; rlm@1: if (xStartDst < 0) rlm@1: { rlm@1: width += xStartDst; rlm@1: if (width <= 0) rlm@1: return 0; rlm@1: xStartSrc = -xStartDst; rlm@1: xStartDst = 0; rlm@1: } rlm@1: rlm@1: if (yStartDst < 0) rlm@1: { rlm@1: height += yStartDst; rlm@1: if (height <= 0) rlm@1: return 0; rlm@1: yStartSrc = -yStartDst; rlm@1: yStartDst = 0; rlm@1: } rlm@1: rlm@1: if (xStartDst + width >= LUA_SCREEN_WIDTH) rlm@1: width = LUA_SCREEN_WIDTH - xStartDst; rlm@1: if (yStartDst + height >= LUA_SCREEN_HEIGHT) rlm@1: height = LUA_SCREEN_HEIGHT - yStartDst; rlm@1: if (width <= 0 || height <= 0) rlm@1: return 0; // out of screen or invalid size rlm@1: gui_prepare(); rlm@1: rlm@1: const uint8 *pix = (const uint8 *)(&ptr[yStartSrc * pitch + (xStartSrc * (trueColor ? 4 : 1))]); rlm@1: int bytesToNextLine = pitch - (width * (trueColor ? 4 : 1)); rlm@1: if (trueColor) rlm@1: { rlm@1: for (int y = yStartDst; y < height + yStartDst && y < LUA_SCREEN_HEIGHT; y++, pix += bytesToNextLine) rlm@1: { rlm@1: for (int x = xStartDst; x < width + xStartDst && x < LUA_SCREEN_WIDTH; x++, pix += 4) rlm@1: { rlm@1: gui_drawpixel_fast(x, y, LUA_BUILD_PIXEL(opacMap[pix[0]], pix[1], pix[2], pix[3])); rlm@1: } rlm@1: } rlm@1: } rlm@1: else rlm@1: { rlm@1: for (int y = yStartDst; y < height + yStartDst && y < LUA_SCREEN_HEIGHT; y++, pix += bytesToNextLine) rlm@1: { rlm@1: for (int x = xStartDst; x < width + xStartDst && x < LUA_SCREEN_WIDTH; x++, pix++) rlm@1: { rlm@1: gui_drawpixel_fast(x, y, LUA_BUILD_PIXEL(pal[*pix].a, pal[*pix].r, pal[*pix].g, pal[*pix].b)); rlm@1: } rlm@1: } rlm@1: } rlm@1: rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // function gui.register(function f) rlm@1: // rlm@1: // This function will be called just before a graphical update. rlm@1: // More complicated, but doesn't suffer any frame delays. rlm@1: // Nil will be accepted in place of a function to erase rlm@1: // a previously registered function, and the previous function rlm@1: rlm@1: // (if any) is returned, or nil if none. rlm@1: static int gui_register(lua_State *L) rlm@1: { rlm@1: // We'll do this straight up. rlm@1: // First set up the stack. rlm@1: lua_settop(L, 1); rlm@1: rlm@1: // Verify the validity of the entry rlm@1: if (!lua_isnil(L, 1)) rlm@1: luaL_checktype(L, 1, LUA_TFUNCTION); rlm@1: rlm@1: // Get the old value rlm@1: lua_getfield(L, LUA_REGISTRYINDEX, guiCallbackTable); rlm@1: rlm@1: // Save the new value rlm@1: lua_pushvalue(L, 1); rlm@1: lua_setfield(L, LUA_REGISTRYINDEX, guiCallbackTable); rlm@1: rlm@1: // The old value is on top of the stack. Return it. rlm@1: return 1; rlm@1: } rlm@1: rlm@1: // string gui.popup(string message, [string type = "ok"]) rlm@1: // rlm@1: rlm@1: // Popup dialog! rlm@1: int gui_popup(lua_State *L) rlm@1: { rlm@1: const char *message = luaL_checkstring(L, 1); rlm@1: const char *type = luaL_optstring(L, 2, "ok"); rlm@1: rlm@1: #if (defined(WIN32) && !defined(SDL)) rlm@1: int t; rlm@1: if (strcmp(type, "ok") == 0) rlm@1: t = MB_OK; rlm@1: else if (strcmp(type, "yesno") == 0) rlm@1: t = MB_YESNO; rlm@1: else if (strcmp(type, "yesnocancel") == 0) rlm@1: t = MB_YESNOCANCEL; rlm@1: else rlm@1: return luaL_error(L, "invalid popup type \"%s\"", type); rlm@1: rlm@1: theApp.winCheckFullscreen(); rlm@1: systemSoundClearBuffer(); rlm@1: int result = AfxGetApp()->m_pMainWnd->MessageBox(message, "Lua Script Pop-up", t); rlm@1: rlm@1: lua_settop(L, 1); rlm@1: rlm@1: if (t != MB_OK) rlm@1: { rlm@1: if (result == IDYES) rlm@1: lua_pushstring(L, "yes"); rlm@1: else if (result == IDNO) rlm@1: lua_pushstring(L, "no"); rlm@1: else if (result == IDCANCEL) rlm@1: lua_pushstring(L, "cancel"); rlm@1: else rlm@1: luaL_error(L, "win32 unrecognized return value %d", result); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: // else, we don't care. rlm@1: return 0; rlm@1: #else rlm@1: char *t; rlm@1: #ifdef __linux rlm@1: // The Linux backend has a "FromPause" variable. rlm@1: // If set to 1, assume some known external event has screwed with the flow of time. rlm@1: // Since this pauses the emulator waiting for a response, we set it to 1. rlm@1: // FIXME: Well, actually it doesn't rlm@1: // extern int FromPause; rlm@1: // FromPause = 1; rlm@1: rlm@1: int pid; // appease compiler rlm@1: rlm@1: // Before doing any work, verify the correctness of the parameters. rlm@1: if (strcmp(type, "ok") == 0) rlm@1: t = "OK:100"; rlm@1: else if (strcmp(type, "yesno") == 0) rlm@1: t = "Yes:100,No:101"; rlm@1: else if (strcmp(type, "yesnocancel") == 0) rlm@1: t = "Yes:100,No:101,Cancel:102"; rlm@1: else rlm@1: return luaL_error(L, "invalid popup type \"%s\"", type); rlm@1: rlm@1: // Can we find a copy of xmessage? Search the path. rlm@1: char *path = strdup(getenv("PATH")); rlm@1: rlm@1: char *current = path; rlm@1: rlm@1: char *colon; rlm@1: rlm@1: int found = 0; rlm@1: rlm@1: while (current) rlm@1: { rlm@1: colon = strchr(current, ':'); rlm@1: rlm@1: // Clip off the colon. rlm@1: *colon++ = 0; rlm@1: rlm@1: int len = strlen(current); rlm@1: char *filename = (char *)malloc(len + 12); // always give excess rlm@1: snprintf(filename, len + 12, "%s/xmessage", current); rlm@1: rlm@1: if (access(filename, X_OK) == 0) rlm@1: { rlm@1: free(filename); rlm@1: found = 1; rlm@1: break; rlm@1: } rlm@1: rlm@1: // Failed, move on. rlm@1: current = colon; rlm@1: free(filename); rlm@1: } rlm@1: rlm@1: free(path); rlm@1: rlm@1: // We've found it? rlm@1: if (!found) rlm@1: goto use_console; rlm@1: rlm@1: pid = fork(); rlm@1: if (pid == 0) rlm@1: { // I'm the virgin sacrifice rlm@1: // I'm gonna be dead in a matter of microseconds anyways, so wasted memory doesn't matter to me. rlm@1: // Go ahead and abuse strdup. rlm@1: char *parameters[] = { "xmessage", "-buttons", t, strdup(message), NULL }; rlm@1: rlm@1: execvp("xmessage", parameters); rlm@1: rlm@1: // Aw shitty rlm@1: perror("exec xmessage"); rlm@1: exit(1); rlm@1: } rlm@1: else if (pid < 0) // something went wrong!!! Oh hell... use the console rlm@1: goto use_console; rlm@1: else rlm@1: { rlm@1: // We're the parent. Watch for the child. rlm@1: int r; rlm@1: int res = waitpid(pid, &r, 0); rlm@1: if (res < 0) // wtf? rlm@1: goto use_console; rlm@1: rlm@1: // The return value gets copmlicated... rlm@1: if (!WIFEXITED(r)) rlm@1: { rlm@1: luaL_error(L, "don't screw with my xmessage process!"); rlm@1: } rlm@1: rlm@1: r = WEXITSTATUS(r); rlm@1: rlm@1: // We assume it's worked. rlm@1: if (r == 0) rlm@1: { rlm@1: return 0; // no parameters for an OK rlm@1: } rlm@1: rlm@1: if (r == 100) rlm@1: { rlm@1: lua_pushstring(L, "yes"); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: if (r == 101) rlm@1: { rlm@1: lua_pushstring(L, "no"); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: if (r == 102) rlm@1: { rlm@1: lua_pushstring(L, "cancel"); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: // Wtf? rlm@1: return luaL_error(L, "popup failed due to unknown results involving xmessage (%d)", r); rlm@1: } rlm@1: rlm@1: use_console: rlm@1: #endif rlm@1: rlm@1: // All else has failed rlm@1: if (strcmp(type, "ok") == 0) rlm@1: t = ""; rlm@1: else if (strcmp(type, "yesno") == 0) rlm@1: t = "yn"; rlm@1: else if (strcmp(type, "yesnocancel") == 0) rlm@1: t = "ync"; rlm@1: else rlm@1: return luaL_error(L, "invalid popup type \"%s\"", type); rlm@1: rlm@1: fprintf(stderr, "Lua Message: %s\n", message); rlm@1: rlm@1: while (true) rlm@1: { rlm@1: char buffer[64]; rlm@1: rlm@1: // We don't want parameters rlm@1: if (!t[0]) rlm@1: { rlm@1: fprintf(stderr, "[Press Enter]"); rlm@1: fgets(buffer, sizeof(buffer), stdin); rlm@1: rlm@1: // We're done rlm@1: return 0; rlm@1: } rlm@1: rlm@1: fprintf(stderr, "(%s): ", t); rlm@1: fgets(buffer, sizeof(buffer), stdin); rlm@1: rlm@1: // Check if the option is in the list rlm@1: if (strchr(t, tolower(buffer[0]))) rlm@1: { rlm@1: switch (tolower(buffer[0])) rlm@1: { rlm@1: case 'y': rlm@1: lua_pushstring(L, "yes"); rlm@1: return 1; rlm@1: case 'n': rlm@1: lua_pushstring(L, "no"); rlm@1: return 1; rlm@1: case 'c': rlm@1: lua_pushstring(L, "cancel"); rlm@1: return 1; rlm@1: default: rlm@1: luaL_error(L, "internal logic error in console based prompts for gui.popup"); rlm@1: } rlm@1: } rlm@1: rlm@1: // We fell through, so we assume the user answered wrong and prompt again. rlm@1: } rlm@1: rlm@1: // Nothing here, since the only way out is in the loop. rlm@1: #endif rlm@1: } rlm@1: rlm@1: #if (defined(WIN32) && !defined(SDL)) rlm@1: const char *s_keyToName[256] = rlm@1: { rlm@1: NULL, rlm@1: "leftclick", rlm@1: "rightclick", rlm@1: NULL, rlm@1: "middleclick", rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: "backspace", rlm@1: "tab", rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: "enter", rlm@1: NULL, rlm@1: NULL, rlm@1: "shift", // 0x10 rlm@1: "control", rlm@1: "alt", rlm@1: "pause", rlm@1: "capslock", rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: "escape", rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: "space", // 0x20 rlm@1: "pageup", rlm@1: "pagedown", rlm@1: "end", rlm@1: "home", rlm@1: "left", rlm@1: "up", rlm@1: "right", rlm@1: "down", rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: "insert", rlm@1: "delete", rlm@1: NULL, rlm@1: "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", rlm@1: NULL, NULL, NULL, NULL, NULL, NULL, NULL, rlm@1: "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", rlm@1: "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", rlm@1: "U", "V", "W", "X", "Y", "Z", rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: "numpad0", "numpad1", "numpad2", "numpad3", "numpad4", "numpad5", "numpad6", "numpad7", "numpad8", "numpad9", rlm@1: "numpad*", "numpad+", rlm@1: NULL, rlm@1: "numpad-", "numpad.", "numpad/", rlm@1: "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", rlm@1: "F12", rlm@1: "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", rlm@1: "F24", rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: NULL, rlm@1: "numlock", rlm@1: "scrolllock", rlm@1: NULL, // 0x92 rlm@1: NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, rlm@1: NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, rlm@1: NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, rlm@1: NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, rlm@1: NULL, // 0xB9 rlm@1: "semicolon", rlm@1: "plus", rlm@1: "comma", rlm@1: "minus", rlm@1: "period", rlm@1: "slash", rlm@1: "tilde", rlm@1: NULL, // 0xC1 rlm@1: NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, rlm@1: NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, rlm@1: NULL, NULL, NULL, NULL, NULL, NULL, NULL, rlm@1: NULL, // 0xDA rlm@1: "leftbracket", rlm@1: "backslash", rlm@1: "rightbracket", rlm@1: "quote", rlm@1: }; rlm@1: #endif rlm@1: rlm@1: // input.get() rlm@1: // takes no input, returns a lua table of entries representing the current input state, rlm@1: // independent of the joypad buttons the emulated game thinks are pressed rlm@1: // for example: rlm@1: // if the user is holding the W key and the left mouse button rlm@1: // and has the mouse at the bottom-right corner of the game screen, rlm@1: rlm@1: // then this would return {W=true, leftclick=true, xmouse=255, ymouse=223} rlm@1: static int input_getcurrentinputstatus(lua_State *L) rlm@1: { rlm@1: lua_newtable(L); rlm@1: rlm@1: #if (defined(WIN32) && !defined(SDL)) rlm@1: // keyboard and mouse button status rlm@1: { rlm@1: unsigned char keys[256]; rlm@1: if (true /*!GUI.BackgroundInput*/) // TODO: background input rlm@1: { rlm@1: if (GetKeyboardState(keys)) rlm@1: { rlm@1: for (int i = 1; i < 255; i++) rlm@1: { rlm@1: int mask = (i == VK_CAPITAL || i == VK_NUMLOCK || i == VK_SCROLL) ? 0x01 : 0x80; rlm@1: if (keys[i] & mask) rlm@1: { rlm@1: const char *name = s_keyToName[i]; rlm@1: if (name) rlm@1: { rlm@1: lua_pushboolean(L, true); rlm@1: lua_setfield(L, -2, name); rlm@1: } rlm@1: } rlm@1: } rlm@1: } rlm@1: } rlm@1: else // use a slightly different method that will detect background input: rlm@1: { rlm@1: for (int i = 1; i < 255; i++) rlm@1: { rlm@1: const char *name = s_keyToName[i]; rlm@1: if (name) rlm@1: { rlm@1: int active; rlm@1: if (i == VK_CAPITAL || i == VK_NUMLOCK || i == VK_SCROLL) rlm@1: active = GetKeyState(i) & 0x01; rlm@1: else rlm@1: active = GetAsyncKeyState(i) & 0x8000; rlm@1: if (active) rlm@1: { rlm@1: lua_pushboolean(L, true); rlm@1: lua_setfield(L, -2, name); rlm@1: } rlm@1: } rlm@1: } rlm@1: } rlm@1: } rlm@1: rlm@1: // mouse position in game screen pixel coordinates rlm@1: { rlm@1: POINT mouse; rlm@1: rlm@1: int xofs = 0, yofs = 0, width = 240, height = 160; rlm@1: if (!systemIsRunningGBA()) rlm@1: { rlm@1: if (gbBorderOn) rlm@1: width = 256, height = 224, xofs = 48, yofs = 40; rlm@1: else rlm@1: width = 160, height = 144; rlm@1: } rlm@1: rlm@1: GetCursorPos(&mouse); rlm@1: AfxGetApp()->m_pMainWnd->ScreenToClient(&mouse); rlm@1: rlm@1: // game screen is always fully stretched to window size, rlm@1: // with no aspect rate correction, or something like that. rlm@1: RECT clientRect; rlm@1: AfxGetApp()->m_pMainWnd->GetClientRect(&clientRect); rlm@1: rlm@1: int wndWidth = clientRect.right - clientRect.left; rlm@1: int wndHeight = clientRect.bottom - clientRect.top; rlm@1: mouse.x = (LONG) (mouse.x * ((float)width / wndWidth)) - xofs; rlm@1: mouse.y = (LONG) (mouse.y * ((float)height / wndHeight)) - yofs; rlm@1: rlm@1: lua_pushinteger(L, mouse.x); rlm@1: lua_setfield(L, -2, "xmouse"); rlm@1: lua_pushinteger(L, mouse.y); rlm@1: lua_setfield(L, -2, "ymouse"); rlm@1: } rlm@1: rlm@1: #else rlm@1: // NYI (well, return an empty table) rlm@1: #endif rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int avi_framecount(lua_State *L) rlm@1: { rlm@1: #ifdef WIN32 rlm@1: if (theApp.aviRecorder != NULL) rlm@1: { rlm@1: lua_pushinteger(L, theApp.aviRecorder->videoFrames()); rlm@1: } rlm@1: else rlm@1: #endif rlm@1: { rlm@1: lua_pushinteger(L, 0); rlm@1: } rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int avi_pause(lua_State *L) rlm@1: { rlm@1: #ifdef WIN32 rlm@1: if (theApp.aviRecorder != NULL) rlm@1: theApp.aviRecorder->Pause(true); rlm@1: #endif rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int avi_resume(lua_State *L) rlm@1: { rlm@1: #ifdef WIN32 rlm@1: if (theApp.aviRecorder != NULL) rlm@1: theApp.aviRecorder->Pause(false); rlm@1: #endif rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static int sound_get(lua_State *L) rlm@1: { rlm@1: extern int32 soundLevel1; rlm@1: extern int32 soundLevel2; rlm@1: extern int32 soundBalance; rlm@1: extern int32 soundMasterOn; rlm@1: extern int32 soundVIN; rlm@1: extern int32 sound1On; rlm@1: extern int32 sound1EnvelopeVolume; rlm@1: extern int32 sound2On; rlm@1: extern int32 sound2EnvelopeVolume; rlm@1: extern int32 sound3On; rlm@1: extern int32 sound3OutputLevel; rlm@1: extern int32 sound3Bank; rlm@1: extern int32 sound3DataSize; rlm@1: extern int32 sound3ForcedOutput; rlm@1: extern int32 sound4On; rlm@1: extern int32 sound4EnvelopeVolume; rlm@1: extern u8 sound3WaveRam[0x20]; rlm@1: rlm@1: int freqReg; rlm@1: double freq; rlm@1: double leftvolscale; rlm@1: double rightvolscale; rlm@1: double panpot; rlm@1: bool gba = systemIsRunningGBA(); rlm@1: u8* gbMem = gba ? ioMem : gbMemory; rlm@1: const int rNR10 = gba ? 0x60 : 0xff10; rlm@1: const int rNR11 = gba ? 0x62 : 0xff11; rlm@1: const int rNR12 = gba ? 0x63 : 0xff12; rlm@1: const int rNR13 = gba ? 0x64 : 0xff13; rlm@1: const int rNR14 = gba ? 0x65 : 0xff14; rlm@1: const int rNR21 = gba ? 0x68 : 0xff16; rlm@1: const int rNR22 = gba ? 0x69 : 0xff17; rlm@1: const int rNR23 = gba ? 0x6c : 0xff18; rlm@1: const int rNR24 = gba ? 0x6d : 0xff19; rlm@1: const int rNR30 = gba ? 0x70 : 0xff1a; rlm@1: const int rNR31 = gba ? 0x72 : 0xff1b; rlm@1: const int rNR32 = gba ? 0x73 : 0xff1c; rlm@1: const int rNR33 = gba ? 0x74 : 0xff1d; rlm@1: const int rNR34 = gba ? 0x75 : 0xff1e; rlm@1: const int rNR41 = gba ? 0x78 : 0xff20; rlm@1: const int rNR42 = gba ? 0x79 : 0xff21; rlm@1: const int rNR43 = gba ? 0x7c : 0xff22; rlm@1: const int rNR44 = gba ? 0x7d : 0xff23; rlm@1: const int rNR50 = gba ? 0x80 : 0xff24; rlm@1: const int rNR51 = gba ? 0x81 : 0xff25; rlm@1: const int rNR52 = gba ? 0x84 : 0xff26; rlm@1: const int rWAVE_RAM = gba ? 0x90 : 0xff30; rlm@1: rlm@1: const int32 _soundVIN = 0x88; // gba ? 0x88 : soundVIN; rlm@1: const bool soundVINLeft = ((_soundVIN & 0x80) != 0); rlm@1: const bool soundVINRight = ((_soundVIN & 0x08) != 0); rlm@1: rlm@1: lua_newtable(L); rlm@1: rlm@1: // square1 rlm@1: lua_newtable(L); rlm@1: if(sound1On == 0 || soundMasterOn == 0) rlm@1: { rlm@1: lua_pushnumber(L, 0.0); rlm@1: panpot = 0.5; rlm@1: } rlm@1: else rlm@1: { rlm@1: double envVolume = sound1EnvelopeVolume / 15.0; rlm@1: if (soundVINLeft && (soundBalance & 0x10) != 0) rlm@1: leftvolscale = ((soundLevel2 / 7.0) * envVolume); rlm@1: else rlm@1: leftvolscale = 0.0; rlm@1: if (soundVINRight && (soundBalance & 0x01) != 0) rlm@1: rightvolscale = ((soundLevel1 / 7.0) * envVolume); rlm@1: else rlm@1: rightvolscale = 0.0; rlm@1: if ((leftvolscale + rightvolscale) != 0) rlm@1: panpot = rightvolscale / (leftvolscale + rightvolscale); rlm@1: else rlm@1: panpot = 0.5; rlm@1: lua_pushnumber(L, (leftvolscale + rightvolscale) / 2.0); rlm@1: } rlm@1: lua_setfield(L, -2, "volume"); rlm@1: lua_pushnumber(L, panpot); rlm@1: lua_setfield(L, -2, "panpot"); rlm@1: freqReg = (((int)(gbMem[rNR14] & 7) << 8) | gbMem[rNR13]); rlm@1: freq = 131072.0 / (2048 - freqReg); rlm@1: lua_pushnumber(L, freq); rlm@1: lua_setfield(L, -2, "frequency"); rlm@1: lua_pushnumber(L, (log(freq / 440.0) * 12 / log(2.0)) + 69); rlm@1: lua_setfield(L, -2, "midikey"); rlm@1: lua_pushinteger(L, (gbMem[rNR11] & 0xC0) >> 6); rlm@1: lua_setfield(L, -2, "duty"); rlm@1: lua_newtable(L); rlm@1: lua_pushinteger(L, freqReg); rlm@1: lua_setfield(L, -2, "frequency"); rlm@1: lua_setfield(L, -2, "regs"); rlm@1: lua_setfield(L, -2, "square1"); rlm@1: // square2 rlm@1: lua_newtable(L); rlm@1: if(sound2On == 0 || soundMasterOn == 0) rlm@1: { rlm@1: lua_pushnumber(L, 0.0); rlm@1: panpot = 0.5; rlm@1: } rlm@1: else rlm@1: { rlm@1: double envVolume = sound2EnvelopeVolume / 15.0; rlm@1: if (soundVINLeft && (soundBalance & 0x20) != 0) rlm@1: leftvolscale = ((soundLevel2 / 7.0) * envVolume); rlm@1: else rlm@1: leftvolscale = 0.0; rlm@1: if (soundVINRight && (soundBalance & 0x02) != 0) rlm@1: rightvolscale = ((soundLevel1 / 7.0) * envVolume); rlm@1: else rlm@1: rightvolscale = 0.0; rlm@1: if ((leftvolscale + rightvolscale) != 0) rlm@1: panpot = rightvolscale / (leftvolscale + rightvolscale); rlm@1: else rlm@1: panpot = 0.5; rlm@1: lua_pushnumber(L, (leftvolscale + rightvolscale) / 2.0); rlm@1: } rlm@1: lua_setfield(L, -2, "volume"); rlm@1: lua_pushnumber(L, panpot); rlm@1: lua_setfield(L, -2, "panpot"); rlm@1: freqReg = (((int)(gbMem[rNR24] & 7) << 8) | gbMem[rNR23]); rlm@1: freq = 131072.0 / (2048 - freqReg); rlm@1: lua_pushnumber(L, freq); rlm@1: lua_setfield(L, -2, "frequency"); rlm@1: lua_pushnumber(L, (log(freq / 440.0) * 12 / log(2.0)) + 69); rlm@1: lua_setfield(L, -2, "midikey"); rlm@1: lua_pushinteger(L, (gbMem[rNR21] & 0xC0) >> 6); rlm@1: lua_setfield(L, -2, "duty"); rlm@1: lua_newtable(L); rlm@1: lua_pushinteger(L, freqReg); rlm@1: lua_setfield(L, -2, "frequency"); rlm@1: lua_setfield(L, -2, "regs"); rlm@1: lua_setfield(L, -2, "square2"); rlm@1: // wavememory rlm@1: lua_newtable(L); rlm@1: if(sound3On == 0 || soundMasterOn == 0) rlm@1: { rlm@1: lua_pushnumber(L, 0.0); rlm@1: panpot = 0.5; rlm@1: } rlm@1: else rlm@1: { rlm@1: double envVolume; rlm@1: if (gba && sound3ForcedOutput != 0) rlm@1: envVolume = 0.75; rlm@1: else rlm@1: { rlm@1: double volTable[4] = { 0.0, 1.0, 0.5, 0.25 }; rlm@1: envVolume = volTable[sound3OutputLevel & 3]; rlm@1: } rlm@1: rlm@1: if (soundVINLeft && (soundBalance & 0x40) != 0) rlm@1: leftvolscale = ((soundLevel2 / 7.0) * envVolume); rlm@1: else rlm@1: leftvolscale = 0.0; rlm@1: if (soundVINRight && (soundBalance & 0x04) != 0) rlm@1: rightvolscale = ((soundLevel1 / 7.0) * envVolume); rlm@1: else rlm@1: rightvolscale = 0.0; rlm@1: if ((leftvolscale + rightvolscale) != 0) rlm@1: panpot = rightvolscale / (leftvolscale + rightvolscale); rlm@1: else rlm@1: panpot = 0.5; rlm@1: lua_pushnumber(L, (leftvolscale + rightvolscale) / 2.0); rlm@1: } rlm@1: lua_setfield(L, -2, "volume"); rlm@1: lua_pushnumber(L, panpot); rlm@1: lua_setfield(L, -2, "panpot"); rlm@1: int waveMemSamples = 32; rlm@1: if (gba) rlm@1: { rlm@1: lua_pushlstring(L, (const char *) &sound3WaveRam[sound3Bank * 0x10], sound3DataSize ? 0x20 : 0x10); rlm@1: waveMemSamples = sound3DataSize ? 64 : 32; rlm@1: } rlm@1: else rlm@1: { rlm@1: lua_pushlstring(L, (const char *) &gbMem[rWAVE_RAM], 0x10); rlm@1: } rlm@1: lua_setfield(L, -2, "waveform"); rlm@1: freqReg = (((int)(gbMem[rNR34] & 7) << 8) | gbMem[rNR33]); rlm@1: freq = 2097152.0 / (waveMemSamples * (2048 - freqReg)); rlm@1: lua_pushnumber(L, freq); rlm@1: lua_setfield(L, -2, "frequency"); rlm@1: lua_pushnumber(L, (log(freq / 440.0) * 12 / log(2.0)) + 69); rlm@1: lua_setfield(L, -2, "midikey"); rlm@1: lua_newtable(L); rlm@1: lua_pushinteger(L, freqReg); rlm@1: lua_setfield(L, -2, "frequency"); rlm@1: lua_setfield(L, -2, "regs"); rlm@1: lua_setfield(L, -2, "wavememory"); rlm@1: // noise rlm@1: lua_newtable(L); rlm@1: if(sound4On == 0 || soundMasterOn == 0) rlm@1: { rlm@1: lua_pushnumber(L, 0.0); rlm@1: panpot = 0.5; rlm@1: } rlm@1: else rlm@1: { rlm@1: double envVolume = sound4EnvelopeVolume / 15.0; rlm@1: if (soundVINLeft && (soundBalance & 0x80) != 0) rlm@1: leftvolscale = ((soundLevel2 / 7.0) * envVolume); rlm@1: else rlm@1: leftvolscale = 0.0; rlm@1: if (soundVINRight && (soundBalance & 0x08) != 0) rlm@1: rightvolscale = ((soundLevel1 / 7.0) * envVolume); rlm@1: else rlm@1: rightvolscale = 0.0; rlm@1: if ((leftvolscale + rightvolscale) != 0) rlm@1: panpot = rightvolscale / (leftvolscale + rightvolscale); rlm@1: else rlm@1: panpot = 0.5; rlm@1: lua_pushnumber(L, (leftvolscale + rightvolscale) / 2.0); rlm@1: } rlm@1: lua_setfield(L, -2, "volume"); rlm@1: lua_pushnumber(L, panpot); rlm@1: lua_setfield(L, -2, "panpot"); rlm@1: const int gbNoiseFreqTable[8] = { 1, 2, 4, 6, 8, 10, 12, 14 }; rlm@1: freqReg = gbNoiseFreqTable[gbMem[rNR43] & 7] << (1 + (gbMem[rNR43] >> 4)); rlm@1: lua_pushboolean(L, (gbMem[rNR43] & 8) != 0); rlm@1: lua_setfield(L, -2, "short"); rlm@1: freq = 1048576.0 / freqReg; rlm@1: lua_pushnumber(L, freq); rlm@1: lua_setfield(L, -2, "frequency"); rlm@1: lua_pushnumber(L, (log(freq / 440.0) * 12 / log(2.0)) + 69); rlm@1: lua_setfield(L, -2, "midikey"); rlm@1: lua_newtable(L); rlm@1: lua_pushinteger(L, freqReg); rlm@1: lua_setfield(L, -2, "frequency"); rlm@1: lua_setfield(L, -2, "regs"); rlm@1: lua_setfield(L, -2, "noise"); rlm@1: rlm@1: return 1; rlm@1: } rlm@1: rlm@1: // same as math.random, but uses SFMT instead of C rand() rlm@1: // FIXME: this function doesn't care multi-instance, rlm@1: rlm@1: // original math.random either though (Lua 5.1) rlm@1: static int sfmt_random(lua_State *L) rlm@1: { rlm@1: lua_Number r = (lua_Number) genrand_real2(); rlm@1: switch (lua_gettop(L)) rlm@1: { // check number of arguments rlm@1: case 0: rlm@1: { // no arguments rlm@1: lua_pushnumber(L, r); // Number between 0 and 1 rlm@1: break; rlm@1: } rlm@1: rlm@1: case 1: rlm@1: { // only upper limit rlm@1: int u = luaL_checkint(L, 1); rlm@1: luaL_argcheck(L, 1 <= u, 1, "interval is empty"); rlm@1: lua_pushnumber(L, floor(r * u) + 1); // int between 1 and `u' rlm@1: break; rlm@1: } rlm@1: rlm@1: case 2: rlm@1: { // lower and upper limits rlm@1: int l = luaL_checkint(L, 1); rlm@1: int u = luaL_checkint(L, 2); rlm@1: luaL_argcheck(L, l <= u, 2, "interval is empty"); rlm@1: lua_pushnumber(L, floor(r * (u - l + 1)) + l); // int between `l' and `u' rlm@1: break; rlm@1: } rlm@1: rlm@1: default: rlm@1: return luaL_error(L, "wrong number of arguments"); rlm@1: } rlm@1: rlm@1: return 1; rlm@1: } rlm@1: rlm@1: // same as math.randomseed, but uses SFMT instead of C srand() rlm@1: // FIXME: this function doesn't care multi-instance, rlm@1: rlm@1: // original math.randomseed either though (Lua 5.1) rlm@1: static int sfmt_randomseed(lua_State *L) rlm@1: { rlm@1: init_gen_rand(luaL_checkint(L, 1)); rlm@1: return 0; rlm@1: } rlm@1: rlm@1: // the following bit operations are ported from LuaBitOp 1.0.1, rlm@1: // because it can handle the sign bit (bit 31) correctly. rlm@1: rlm@1: /* rlm@1: ** Lua BitOp -- a bit operations library for Lua 5.1. rlm@1: ** http://bitop.luajit.org/ rlm@1: ** rlm@1: ** Copyright (C) 2008-2009 Mike Pall. All rights reserved. rlm@1: ** rlm@1: ** Permission is hereby granted, free of charge, to any person obtaining rlm@1: ** a copy of this software and associated documentation files (the rlm@1: ** "Software"), to deal in the Software without restriction, including rlm@1: ** without limitation the rights to use, copy, modify, merge, publish, rlm@1: ** distribute, sublicense, and/or sell copies of the Software, and to rlm@1: ** permit persons to whom the Software is furnished to do so, subject to rlm@1: ** the following conditions: rlm@1: ** rlm@1: ** The above copyright notice and this permission notice shall be rlm@1: ** included in all copies or substantial portions of the Software. rlm@1: ** rlm@1: ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, rlm@1: ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF rlm@1: ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. rlm@1: ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY rlm@1: ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, rlm@1: ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE rlm@1: ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rlm@1: ** rlm@1: ** [ MIT license: http://www.opensource.org/licenses/mit-license.php ] rlm@1: */ rlm@1: rlm@1: #ifdef _MSC_VER rlm@1: /* MSVC is stuck in the last century and doesn't have C99's stdint.h. */ rlm@1: typedef __int32 int32_t; rlm@1: typedef unsigned __int32 uint32_t; rlm@1: typedef unsigned __int64 uint64_t; rlm@1: #else rlm@1: #include rlm@1: #endif rlm@1: rlm@1: typedef int32_t SBits; rlm@1: typedef uint32_t UBits; rlm@1: rlm@1: typedef union rlm@1: { rlm@1: lua_Number n; rlm@1: #ifdef LUA_NUMBER_DOUBLE rlm@1: uint64_t b; rlm@1: #else rlm@1: UBits b; rlm@1: #endif rlm@1: } BitNum; rlm@1: rlm@1: /* Convert argument to bit type. */ rlm@1: static UBits barg(lua_State *L, int idx) rlm@1: { rlm@1: BitNum bn; rlm@1: UBits b; rlm@1: bn.n = lua_tonumber(L, idx); rlm@1: #if defined(LUA_NUMBER_DOUBLE) rlm@1: bn.n += 6755399441055744.0; /* 2^52+2^51 */ rlm@1: #ifdef SWAPPED_DOUBLE rlm@1: b = (UBits)(bn.b >> 32); rlm@1: #else rlm@1: b = (UBits)(bn.b & 0xffffffff); rlm@1: #endif rlm@1: #elif defined(LUA_NUMBER_INT) || defined(LUA_NUMBER_LONG) || \ rlm@1: defined(LUA_NUMBER_LONGLONG) || defined(LUA_NUMBER_LONG_LONG) || \ rlm@1: defined(LUA_NUMBER_LLONG) rlm@1: if (sizeof(UBits) == sizeof(lua_Number)) rlm@1: b = bn.b; rlm@1: else rlm@1: b = (UBits)(SBits)bn.n; rlm@1: #elif defined(LUA_NUMBER_FLOAT) rlm@1: #error "A 'float' lua_Number type is incompatible with this library" rlm@1: #else rlm@1: #error "Unknown number type, check LUA_NUMBER_* in luaconf.h" rlm@1: #endif rlm@1: if (b == 0 && !lua_isnumber(L, idx)) rlm@1: luaL_typerror(L, idx, "number"); rlm@1: return b; rlm@1: } rlm@1: rlm@1: /* Return bit type. */ rlm@1: #define BRET(b) lua_pushnumber(L, (lua_Number)(SBits)(b)); return 1; rlm@1: rlm@1: static int bit_tobit(lua_State *L) { BRET(barg(L, 1)) } rlm@1: static int bit_bnot(lua_State *L) { BRET(~barg(L, 1)) } rlm@1: rlm@1: #define BIT_OP(func, opr) \ rlm@1: static int func(lua_State * L) { int i; UBits b = barg(L, 1); \ rlm@1: for (i = lua_gettop(L); i > 1; i--) \ rlm@1: b opr barg(L, i); BRET(b) } rlm@1: BIT_OP(bit_band, &= ) rlm@1: BIT_OP(bit_bor, |= ) rlm@1: BIT_OP(bit_bxor, ^= ) rlm@1: rlm@1: #define bshl(b, n) (b << n) rlm@1: #define bshr(b, n) (b >> n) rlm@1: #define bsar(b, n) ((SBits)b >> n) rlm@1: #define brol(b, n) ((b << n) | (b >> (32 - n))) rlm@1: #define bror(b, n) ((b << (32 - n)) | (b >> n)) rlm@1: #define BIT_SH(func, fn) \ rlm@1: static int func(lua_State * L) { \ rlm@1: UBits b = barg(L, 1); UBits n = barg(L, 2) & 31; BRET(fn(b, n)) } rlm@1: BIT_SH(bit_lshift, bshl) rlm@1: BIT_SH(bit_rshift, bshr) rlm@1: BIT_SH(bit_arshift, bsar) rlm@1: BIT_SH(bit_rol, brol) rlm@1: BIT_SH(bit_ror, bror) rlm@1: rlm@1: static int bit_bswap(lua_State *L) rlm@1: { rlm@1: UBits b = barg(L, 1); rlm@1: b = (b >> 24) | ((b >> 8) & 0xff00) | ((b & 0xff00) << 8) | (b << 24); rlm@1: BRET(b) rlm@1: } rlm@1: rlm@1: static int bit_tohex(lua_State *L) rlm@1: { rlm@1: UBits b = barg(L, 1); rlm@1: SBits n = lua_isnone(L, 2) ? 8 : (SBits)barg(L, 2); rlm@1: const char *hexdigits = "0123456789abcdef"; rlm@1: char buf[8]; rlm@1: int i; rlm@1: if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; } rlm@1: if (n > 8) n = 8; rlm@1: for (i = (int)n; --i >= 0; ) rlm@1: { rlm@1: buf[i] = hexdigits[b & 15]; b >>= 4; rlm@1: } rlm@1: lua_pushlstring(L, buf, (size_t)n); rlm@1: return 1; rlm@1: } rlm@1: rlm@1: static const struct luaL_Reg bit_funcs[] = { rlm@1: { "tobit", bit_tobit }, rlm@1: { "bnot", bit_bnot }, rlm@1: { "band", bit_band }, rlm@1: { "bor", bit_bor }, rlm@1: { "bxor", bit_bxor }, rlm@1: { "lshift", bit_lshift }, rlm@1: { "rshift", bit_rshift }, rlm@1: { "arshift", bit_arshift }, rlm@1: { "rol", bit_rol }, rlm@1: { "ror", bit_ror }, rlm@1: { "bswap", bit_bswap }, rlm@1: { "tohex", bit_tohex }, rlm@1: { NULL, NULL } rlm@1: }; rlm@1: rlm@1: /* Signed right-shifts are implementation-defined per C89/C99. rlm@1: ** But the de facto standard are arithmetic right-shifts on two's rlm@1: ** complement CPUs. This behaviour is required here, so test for it. rlm@1: */ rlm@1: #define BAD_SAR (bsar(-8, 2) != (SBits) - 2) rlm@1: rlm@1: bool luabitop_validate(lua_State *L) // originally named as luaopen_bit rlm@1: { rlm@1: UBits b; rlm@1: lua_pushnumber(L, (lua_Number)1437217655L); rlm@1: b = barg(L, -1); rlm@1: if (b != (UBits)1437217655L || BAD_SAR) /* Perform a simple self-test. */ rlm@1: { rlm@1: const char *msg = "compiled with incompatible luaconf.h"; rlm@1: #ifdef LUA_NUMBER_DOUBLE rlm@1: #ifdef WIN32 rlm@1: if (b == (UBits)1610612736L) rlm@1: msg = "use D3DCREATE_FPU_PRESERVE with DirectX"; rlm@1: #endif rlm@1: if (b == (UBits)1127743488L) rlm@1: msg = "not compiled with SWAPPED_DOUBLE"; rlm@1: #endif rlm@1: if (BAD_SAR) rlm@1: msg = "arithmetic right-shift broken"; rlm@1: luaL_error(L, "bit library self-test failed (%s)", msg); rlm@1: return false; rlm@1: } rlm@1: return true; rlm@1: } rlm@1: rlm@1: // LuaBitOp ends here rlm@1: rlm@1: static int bit_bshift_emulua(lua_State *L) rlm@1: { rlm@1: int shift = luaL_checkinteger(L, 2); rlm@1: if (shift < 0) rlm@1: { rlm@1: lua_pushinteger(L, -shift); rlm@1: lua_replace(L, 2); rlm@1: return bit_lshift(L); rlm@1: } rlm@1: else rlm@1: return bit_rshift(L); rlm@1: } rlm@1: rlm@1: static int bitbit(lua_State *L) rlm@1: { rlm@1: int rv = 0; rlm@1: int numArgs = lua_gettop(L); rlm@1: for (int i = 1; i <= numArgs; i++) rlm@1: { rlm@1: int where = luaL_checkinteger(L, i); rlm@1: if (where >= 0 && where < 32) rlm@1: rv |= (1 << where); rlm@1: } rlm@1: lua_settop(L, 0); rlm@1: BRET(rv); rlm@1: } rlm@1: rlm@1: // The function called periodically to ensure Lua doesn't run amok. rlm@1: static void VBALuaHookFunction(lua_State *L, lua_Debug *dbg) rlm@1: { rlm@1: if (numTries-- == 0) rlm@1: { rlm@1: int kill = 0; rlm@1: rlm@1: #if (defined(WIN32) && !defined(SDL)) rlm@1: // Uh oh rlm@1: theApp.winCheckFullscreen(); rlm@1: systemSoundClearBuffer(); rlm@1: int ret = AfxGetApp()->m_pMainWnd->MessageBox( rlm@1: "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)", rlm@1: "Lua Script Gone Nuts?", rlm@1: MB_YESNO); rlm@1: rlm@1: if (ret == IDYES) rlm@1: { rlm@1: kill = 1; rlm@1: } rlm@1: rlm@1: #else rlm@1: fprintf( rlm@1: stderr, rlm@1: "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"); rlm@1: rlm@1: char buffer[64]; rlm@1: while (true) rlm@1: { rlm@1: fprintf(stderr, "(y/n): "); rlm@1: fgets(buffer, sizeof(buffer), stdin); rlm@1: if (buffer[0] == 'y' || buffer[0] == 'Y') rlm@1: { rlm@1: kill = 1; rlm@1: break; rlm@1: } rlm@1: rlm@1: if (buffer[0] == 'n' || buffer[0] == 'N') rlm@1: break; rlm@1: } rlm@1: #endif rlm@1: if (kill) rlm@1: { rlm@1: luaL_error(L, "Killed by user request."); rlm@1: VBALuaOnStop(); rlm@1: } rlm@1: rlm@1: // else, kill the debug hook. rlm@1: lua_sethook(L, NULL, 0, 0); rlm@1: } rlm@1: } rlm@1: rlm@1: static const struct luaL_reg vbalib[] = { rlm@1: // {"speedmode", vba_speedmode}, // TODO: NYI rlm@1: { "frameadvance", vba_frameadvance }, rlm@1: { "pause", vba_pause }, rlm@1: { "framecount", vba_framecount }, rlm@1: { "lagcount", vba_getlagcount }, rlm@1: { "lagged", vba_lagged }, rlm@1: { "emulating", vba_emulating }, rlm@1: { "registerbefore", vba_registerbefore }, rlm@1: { "registerafter", vba_registerafter }, rlm@1: { "registerexit", vba_registerexit }, rlm@1: { "message", vba_message }, rlm@1: { "print", print }, // sure, why not rlm@1: { NULL, NULL } rlm@1: }; rlm@1: rlm@1: static const struct luaL_reg memorylib[] = { rlm@1: { "readbyte", memory_readbyte }, rlm@1: { "readbytesigned", memory_readbytesigned }, rlm@1: { "readword", memory_readword }, rlm@1: { "readwordsigned", memory_readwordsigned }, rlm@1: { "readdword", memory_readdword }, rlm@1: { "readdwordsigned", memory_readdwordsigned }, rlm@1: { "readbyterange", memory_readbyterange }, rlm@1: { "writebyte", memory_writebyte }, rlm@1: { "writeword", memory_writeword }, rlm@1: { "writedword", memory_writedword }, rlm@1: { "getregister", memory_getregister }, rlm@1: { "setregister", memory_setregister }, rlm@1: { "gbromreadbyte", memory_gbromreadbyte }, rlm@1: { "gbromreadbytesigned", memory_gbromreadbytesigned }, rlm@1: { "gbromreadword", memory_gbromreadword }, rlm@1: { "gbromreadwordsigned", memory_gbromreadwordsigned }, rlm@1: { "gbromreaddword", memory_gbromreaddword }, rlm@1: { "gbromreaddwordsigned", memory_gbromreaddwordsigned }, rlm@1: { "gbromreadbyterange", memory_gbromreadbyterange }, rlm@1: rlm@1: // alternate naming scheme for word and double-word and unsigned rlm@1: { "readbyteunsigned", memory_readbyte }, rlm@1: { "readwordunsigned", memory_readword }, rlm@1: { "readdwordunsigned", memory_readdword }, rlm@1: { "readshort", memory_readword }, rlm@1: { "readshortunsigned", memory_readword }, rlm@1: { "readshortsigned", memory_readwordsigned }, rlm@1: { "readlong", memory_readdword }, rlm@1: { "readlongunsigned", memory_readdword }, rlm@1: { "readlongsigned", memory_readdwordsigned }, rlm@1: { "writeshort", memory_writeword }, rlm@1: { "writelong", memory_writedword }, rlm@1: { "gbromreadbyteunsigned", memory_gbromreadbyte }, rlm@1: { "gbromreadwordunsigned", memory_gbromreadword }, rlm@1: { "gbromreaddwordunsigned", memory_gbromreaddword }, rlm@1: { "gbromreadshort", memory_gbromreadword }, rlm@1: { "gbromreadshortunsigned", memory_gbromreadword }, rlm@1: { "gbromreadshortsigned", memory_gbromreadwordsigned }, rlm@1: { "gbromreadlong", memory_gbromreaddword }, rlm@1: { "gbromreadlongunsigned", memory_gbromreaddword }, rlm@1: { "gbromreadlongsigned", memory_gbromreaddwordsigned }, rlm@1: rlm@1: // memory hooks rlm@1: { "registerwrite", memory_registerwrite }, rlm@1: //{"registerread", memory_registerread}, rlm@1: { "registerexec", memory_registerexec }, rlm@1: // alternate names rlm@1: { "register", memory_registerwrite }, rlm@1: { "registerrun", memory_registerexec }, rlm@1: { "registerexecute", memory_registerexec }, rlm@1: rlm@1: { NULL, NULL } rlm@1: }; rlm@1: rlm@1: static const struct luaL_reg joypadlib[] = { rlm@1: { "get", joypad_get }, rlm@1: { "getdown", joypad_getdown }, rlm@1: { "getup", joypad_getup }, rlm@1: { "set", joypad_set }, rlm@1: rlm@1: // alternative names rlm@1: { "read", joypad_get }, rlm@1: { "write", joypad_set }, rlm@1: { "readdown", joypad_getdown }, rlm@1: { "readup", joypad_getup }, rlm@1: { NULL, NULL } rlm@1: }; rlm@1: rlm@1: static const struct luaL_reg savestatelib[] = { rlm@1: { "create", savestate_create }, rlm@1: { "save", savestate_save }, rlm@1: { "load", savestate_load }, rlm@1: rlm@1: { NULL, NULL } rlm@1: }; rlm@1: rlm@1: static const struct luaL_reg movielib[] = { rlm@1: { "active", movie_isactive }, rlm@1: { "recording", movie_isrecording }, rlm@1: { "playing", movie_isplaying }, rlm@1: { "mode", movie_getmode }, rlm@1: rlm@1: { "length", movie_getlength }, rlm@1: { "author", movie_getauthor }, rlm@1: { "name", movie_getfilename }, rlm@1: { "rerecordcount", movie_rerecordcount }, rlm@1: { "setrerecordcount", movie_setrerecordcount }, rlm@1: rlm@1: { "rerecordcounting", movie_rerecordcounting }, rlm@1: { "framecount", vba_framecount }, // for those familiar with rlm@1: // other emulators that have rlm@1: // movie.framecount() rlm@1: // instead of rlm@1: // emulatorname.framecount() rlm@1: rlm@1: { "stop", movie_stop }, rlm@1: rlm@1: // alternative names rlm@1: { "close", movie_stop }, rlm@1: { "getauthor", movie_getauthor }, rlm@1: { "getname", movie_getfilename }, rlm@1: { NULL, NULL } rlm@1: }; rlm@1: rlm@1: static const struct luaL_reg guilib[] = { rlm@1: { "register", gui_register }, rlm@1: { "text", gui_text }, rlm@1: { "box", gui_drawbox }, rlm@1: { "line", gui_drawline }, rlm@1: { "pixel", gui_drawpixel }, rlm@1: { "opacity", gui_setopacity }, rlm@1: { "transparency", gui_transparency }, rlm@1: { "popup", gui_popup }, rlm@1: { "parsecolor", gui_parsecolor }, rlm@1: { "gdscreenshot", gui_gdscreenshot }, rlm@1: { "gdoverlay", gui_gdoverlay }, rlm@1: { "getpixel", gui_getpixel }, rlm@1: rlm@1: // alternative names rlm@1: { "drawtext", gui_text }, rlm@1: { "drawbox", gui_drawbox }, rlm@1: { "drawline", gui_drawline }, rlm@1: { "drawpixel", gui_drawpixel }, rlm@1: { "setpixel", gui_drawpixel }, rlm@1: { "writepixel", gui_drawpixel }, rlm@1: { "rect", gui_drawbox }, rlm@1: { "drawrect", gui_drawbox }, rlm@1: { "drawimage", gui_gdoverlay }, rlm@1: { "image", gui_gdoverlay }, rlm@1: { "readpixel", gui_getpixel }, rlm@1: { NULL, NULL } rlm@1: }; rlm@1: rlm@1: static const struct luaL_reg inputlib[] = { rlm@1: { "get", input_getcurrentinputstatus }, rlm@1: rlm@1: // alternative names rlm@1: { "read", input_getcurrentinputstatus }, rlm@1: { NULL, NULL } rlm@1: }; rlm@1: rlm@1: static const struct luaL_reg soundlib[] = { rlm@1: { "get", sound_get }, rlm@1: rlm@1: // alternative names rlm@1: { NULL, NULL } rlm@1: }; rlm@1: rlm@1: // gocha: since vba dumps avi so badly, rlm@1: // I add avilib as a workaround for enhanced video encoding. rlm@1: static const struct luaL_reg avilib[] = { rlm@1: { "framecount", avi_framecount }, rlm@1: { "pause", avi_pause }, rlm@1: { "resume", avi_resume }, rlm@1: { NULL, NULL } rlm@1: }; rlm@1: rlm@1: void CallExitFunction(void) rlm@1: { rlm@1: if (!LUA) rlm@1: return; rlm@1: rlm@1: lua_settop(LUA, 0); rlm@1: lua_getfield(LUA, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]); rlm@1: rlm@1: int errorcode = 0; rlm@1: if (lua_isfunction(LUA, -1)) rlm@1: { rlm@1: errorcode = lua_pcall(LUA, 0, 0, 0); rlm@1: } rlm@1: rlm@1: if (errorcode) rlm@1: HandleCallbackError(LUA); rlm@1: } rlm@1: rlm@1: void VBALuaFrameBoundary(void) rlm@1: { rlm@1: // printf("Lua Frame\n"); rlm@1: rlm@1: lua_joypads_used = 0; rlm@1: rlm@1: // HA! rlm@1: if (!LUA || !luaRunning) rlm@1: return; rlm@1: rlm@1: // Our function needs calling rlm@1: lua_settop(LUA, 0); rlm@1: lua_getfield(LUA, LUA_REGISTRYINDEX, frameAdvanceThread); rlm@1: rlm@1: lua_State *thread = lua_tothread(LUA, 1); rlm@1: rlm@1: // Lua calling C must know that we're busy inside a frame boundary rlm@1: frameBoundary = true; rlm@1: frameAdvanceWaiting = false; rlm@1: rlm@1: numTries = 1000; rlm@1: rlm@1: int result = lua_resume(thread, 0); rlm@1: rlm@1: if (result == LUA_YIELD) rlm@1: { rlm@1: // Okay, we're fine with that. rlm@1: } rlm@1: else if (result != 0) rlm@1: { rlm@1: // Done execution by bad causes rlm@1: VBALuaOnStop(); rlm@1: lua_pushnil(LUA); rlm@1: lua_setfield(LUA, LUA_REGISTRYINDEX, frameAdvanceThread); rlm@1: lua_pushnil(LUA); rlm@1: lua_setfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable); rlm@1: rlm@1: // Error? rlm@1: //#if (defined(WIN32) && !defined(SDL)) rlm@1: // info_print(info_uid, lua_tostring(thread, -1)); //Clear_Sound_Buffer(); rlm@1: // AfxGetApp()->m_pMainWnd->MessageBox(lua_tostring(thread, -1), "Lua run error", MB_OK | MB_ICONSTOP); rlm@1: //#else rlm@1: // fprintf(stderr, "Lua thread bombed out: %s\n", lua_tostring(thread, -1)); rlm@1: //#endif rlm@1: printerror(thread, -1); rlm@1: } rlm@1: else rlm@1: { rlm@1: VBALuaOnStop(); rlm@1: printf("Script died of natural causes.\n"); rlm@1: } rlm@1: rlm@1: // Past here, VBA actually runs, so any Lua code is called mid-frame. We must rlm@1: // not do anything too stupid, so let ourselves know. rlm@1: frameBoundary = false; rlm@1: rlm@1: if (!frameAdvanceWaiting) rlm@1: { rlm@1: VBALuaOnStop(); rlm@1: } rlm@1: } rlm@1: rlm@1: /** rlm@1: * Loads and runs the given Lua script. rlm@1: * The emulator MUST be paused for this function to be rlm@1: * called. Otherwise, all frame boundary assumptions go out the window. rlm@1: * rlm@1: * Returns true on success, false on failure. rlm@1: */ rlm@1: int VBALoadLuaCode(const char *filename) rlm@1: { rlm@1: static bool sfmtInitialized = false; rlm@1: if (!sfmtInitialized) rlm@1: { rlm@1: init_gen_rand((unsigned)time(NULL)); rlm@1: sfmtInitialized = true; rlm@1: } rlm@1: rlm@1: if (filename != luaScriptName) rlm@1: { rlm@1: if (luaScriptName) rlm@1: free(luaScriptName); rlm@1: luaScriptName = strdup(filename); rlm@1: } rlm@1: rlm@1: //stop any lua we might already have had running rlm@1: VBALuaStop(); rlm@1: rlm@1: // Set current directory from filename (for dofile) rlm@1: char dir[_MAX_PATH]; rlm@1: char *slash, *backslash; rlm@1: strcpy(dir, filename); rlm@1: slash = strrchr(dir, '/'); rlm@1: backslash = strrchr(dir, '\\'); rlm@1: if (!slash || (backslash && backslash < slash)) rlm@1: slash = backslash; rlm@1: if (slash) rlm@1: { rlm@1: slash[1] = '\0'; // keep slash itself for some reasons rlm@1: chdir(dir); rlm@1: } rlm@1: rlm@1: if (!LUA) rlm@1: { rlm@1: LUA = lua_open(); rlm@1: luaL_openlibs(LUA); rlm@1: rlm@1: luaL_register(LUA, "emu", vbalib); // added for better cross-emulator compatibility rlm@1: luaL_register(LUA, "vba", vbalib); // kept for backward compatibility rlm@1: luaL_register(LUA, "memory", memorylib); rlm@1: luaL_register(LUA, "joypad", joypadlib); rlm@1: luaL_register(LUA, "savestate", savestatelib); rlm@1: luaL_register(LUA, "movie", movielib); rlm@1: luaL_register(LUA, "gui", guilib); rlm@1: luaL_register(LUA, "input", inputlib); rlm@1: luaL_register(LUA, "sound", soundlib); rlm@1: luaL_register(LUA, "bit", bit_funcs); // LuaBitOp library rlm@1: luaL_register(LUA, "avi", avilib); // workaround for enhanced video encoding rlm@1: lua_settop(LUA, 0); // clean the stack, because each call to luaL_register leaves a table on top rlm@1: rlm@1: // register a few utility functions outside of libraries (in the global namespace) rlm@1: lua_register(LUA, "print", print); rlm@1: lua_register(LUA, "tostring", tostring); rlm@1: lua_register(LUA, "addressof", addressof); rlm@1: lua_register(LUA, "copytable", copytable); rlm@1: rlm@1: // old bit operation functions rlm@1: lua_register(LUA, "AND", bit_band); rlm@1: lua_register(LUA, "OR", bit_bor); rlm@1: lua_register(LUA, "XOR", bit_bxor); rlm@1: lua_register(LUA, "SHIFT", bit_bshift_emulua); rlm@1: lua_register(LUA, "BIT", bitbit); rlm@1: rlm@1: luabitop_validate(LUA); rlm@1: rlm@1: lua_pushstring(LUA, "math"); rlm@1: lua_gettable(LUA, LUA_GLOBALSINDEX); rlm@1: lua_pushcfunction(LUA, sfmt_random); rlm@1: lua_setfield(LUA, -2, "random"); rlm@1: lua_pushcfunction(LUA, sfmt_randomseed); rlm@1: lua_setfield(LUA, -2, "randomseed"); rlm@1: lua_settop(LUA, 0); rlm@1: rlm@1: // push arrays for storing hook functions in rlm@1: for (int i = 0; i < LUAMEMHOOK_COUNT; i++) rlm@1: { rlm@1: lua_newtable(LUA); rlm@1: lua_setfield(LUA, LUA_REGISTRYINDEX, luaMemHookTypeStrings[i]); rlm@1: } rlm@1: } rlm@1: rlm@1: // We make our thread NOW because we want it at the bottom of the stack. rlm@1: // If all goes wrong, we let the garbage collector remove it. rlm@1: lua_State *thread = lua_newthread(LUA); rlm@1: rlm@1: // Load the data rlm@1: int result = luaL_loadfile(LUA, filename); rlm@1: rlm@1: if (result) rlm@1: { rlm@1: //#if (defined(WIN32) && !defined(SDL)) rlm@1: // info_print(info_uid, lua_tostring(LUA, -1)); //Clear_Sound_Buffer(); rlm@1: // AfxGetApp()->m_pMainWnd->MessageBox(lua_tostring(LUA, -1), "Lua load error", MB_OK | MB_ICONSTOP); rlm@1: //#else rlm@1: // fprintf(stderr, "Failed to compile file: %s\n", lua_tostring(LUA, -1)); rlm@1: //#endif rlm@1: printerror(LUA, -1); rlm@1: rlm@1: // Wipe the stack. Our thread rlm@1: lua_settop(LUA, 0); rlm@1: return 0; // Oh shit. rlm@1: } rlm@1: rlm@1: // Get our function into it rlm@1: lua_xmove(LUA, thread, 1); rlm@1: rlm@1: // Save the thread to the registry. This is why I make the thread FIRST. rlm@1: lua_setfield(LUA, LUA_REGISTRYINDEX, frameAdvanceThread); rlm@1: rlm@1: // Initialize settings rlm@1: luaRunning = true; rlm@1: skipRerecords = false; rlm@1: numMemHooks = 0; rlm@1: transparencyModifier = 255; // opaque rlm@1: lua_joypads_used = 0; // not used rlm@1: //wasPaused = systemIsPaused(); rlm@1: //systemSetPause(false); rlm@1: rlm@1: // Set up our protection hook to be executed once every 10,000 bytecode instructions. rlm@1: lua_sethook(thread, VBALuaHookFunction, LUA_MASKCOUNT, 10000); rlm@1: rlm@1: #ifdef WIN32 rlm@1: info_print = PrintToWindowConsole; rlm@1: info_onstart = WinLuaOnStart; rlm@1: info_onstop = WinLuaOnStop; rlm@1: if (!LuaConsoleHWnd) rlm@1: LuaConsoleHWnd = CreateDialog(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDD_LUA), rlm@1: AfxGetMainWnd()->GetSafeHwnd(), (DLGPROC) DlgLuaScriptDialog); rlm@1: info_uid = (int)LuaConsoleHWnd; rlm@1: #else rlm@1: info_print = NULL; rlm@1: info_onstart = NULL; rlm@1: info_onstop = NULL; rlm@1: #endif rlm@1: if (info_onstart) rlm@1: info_onstart(info_uid); rlm@1: rlm@1: // And run it right now. :) rlm@1: VBALuaFrameBoundary(); rlm@1: systemRenderFrame(); rlm@1: rlm@1: // We're done. rlm@1: return 1; rlm@1: } rlm@1: rlm@1: /** rlm@1: * Equivalent to repeating the last VBALoadLuaCode() call. rlm@1: */ rlm@1: int VBAReloadLuaCode(void) rlm@1: { rlm@1: if (!luaScriptName) rlm@1: { rlm@1: systemScreenMessage("There's no script to reload."); rlm@1: return 0; rlm@1: } rlm@1: else rlm@1: return VBALoadLuaCode(luaScriptName); rlm@1: } rlm@1: rlm@1: /** rlm@1: * Terminates a running Lua script by killing the whole Lua engine. rlm@1: * rlm@1: * Always safe to call, except from within a lua call itself (duh). rlm@1: * rlm@1: */ rlm@1: void VBALuaStop(void) rlm@1: { rlm@1: //already killed rlm@1: if (!LUA) rlm@1: return; rlm@1: rlm@1: //execute the user's shutdown callbacks rlm@1: CallExitFunction(); rlm@1: rlm@1: /*info.*/ numMemHooks = 0; rlm@1: for (int i = 0; i < LUAMEMHOOK_COUNT; i++) rlm@1: CalculateMemHookRegions((LuaMemHookType)i); rlm@1: rlm@1: //sometimes iup uninitializes com rlm@1: //MBG TODO - test whether this is really necessary. i dont think it is rlm@1: #if (defined(WIN32) && !defined(SDL)) rlm@1: CoInitialize(0); rlm@1: #endif rlm@1: rlm@1: if (info_onstop) rlm@1: info_onstop(info_uid); rlm@1: rlm@1: //lua_gc(LUA,LUA_GCCOLLECT,0); rlm@1: lua_close(LUA); // this invokes our garbage collectors for us rlm@1: LUA = NULL; rlm@1: VBALuaOnStop(); rlm@1: } rlm@1: rlm@1: /** rlm@1: * Returns true if there is a Lua script running. rlm@1: * rlm@1: */ rlm@1: int VBALuaRunning(void) rlm@1: { rlm@1: // FIXME: return false when no callback functions are registered. rlm@1: return (int) (LUA != NULL); // should return true if callback functions are active. rlm@1: } rlm@1: rlm@1: /** rlm@1: * Returns true if Lua would like to steal the given joypad control. rlm@1: * rlm@1: * Range is 0 through 3 rlm@1: */ rlm@1: int VBALuaUsingJoypad(int which) rlm@1: { rlm@1: if (which < 0 || which > 3) rlm@1: which = systemGetDefaultJoypad(); rlm@1: return lua_joypads_used & (1 << which); rlm@1: } rlm@1: rlm@1: /** rlm@1: * Reads the buttons Lua is feeding for the given joypad, in the same rlm@1: * format as the OS-specific code. rlm@1: * rlm@1: * This function must not be called more than once per frame. Ideally exactly once rlm@1: * per frame (if VBALuaUsingJoypad says it's safe to do so) rlm@1: */ rlm@1: int VBALuaReadJoypad(int which) rlm@1: { rlm@1: if (which < 0 || which > 3) rlm@1: which = systemGetDefaultJoypad(); rlm@1: rlm@1: //lua_joypads_used &= ~(1 << which); rlm@1: return lua_joypads[which]; rlm@1: } rlm@1: rlm@1: /** rlm@1: * If this function returns true, the movie code should NOT increment rlm@1: * the rerecord count for a load-state. rlm@1: * rlm@1: * This function will not return true if a script is not running. rlm@1: */ rlm@1: bool8 VBALuaRerecordCountSkip(void) rlm@1: { rlm@1: // FIXME: return true if (there are any active callback functions && skipRerecords) rlm@1: return LUA && luaRunning && skipRerecords; rlm@1: } rlm@1: rlm@1: /** rlm@1: * Given a screen with the indicated resolution, rlm@1: * draw the current GUI onto it. rlm@1: */ rlm@1: void VBALuaGui(uint8 *screen, int ppl, int width, int height) rlm@1: { rlm@1: if (!LUA /* || !luaRunning*/) rlm@1: return; rlm@1: rlm@1: // First, check if we're being called by anybody rlm@1: lua_getfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable); rlm@1: rlm@1: if (lua_isfunction(LUA, -1)) rlm@1: { rlm@1: // We call it now rlm@1: numTries = 1000; rlm@1: rlm@1: int ret = lua_pcall(LUA, 0, 0, 0); rlm@1: if (ret != 0) rlm@1: { rlm@1: // This is grounds for trashing the function rlm@1: // Note: This must be done before the messagebox pops up, rlm@1: // otherwise the messagebox will cause a paint event which causes a weird rlm@1: // infinite call sequence that makes Snes9x silently exit with error code 3, rlm@1: // if a Lua GUI function crashes. (nitsuja) rlm@1: lua_pushnil(LUA); rlm@1: lua_setfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable); rlm@1: rlm@1: //#if (defined(WIN32) && !defined(SDL)) rlm@1: // info_print(info_uid, lua_tostring(LUA, -1)); //AfxGetApp()->m_pMainWnd->MessageBox(lua_tostring(LUA, -1), "Lua Error rlm@1: // in GUI function", MB_OK); rlm@1: //#else rlm@1: // fprintf(stderr, "Lua error in gui.register function: %s\n", lua_tostring(LUA, -1)); rlm@1: //#endif rlm@1: printerror(LUA, -1); rlm@1: } rlm@1: } rlm@1: rlm@1: // And wreak the stack rlm@1: lua_settop(LUA, 0); rlm@1: rlm@1: if (!gui_used) rlm@1: return; rlm@1: rlm@1: gui_used = false; rlm@1: rlm@1: int x, y; rlm@1: rlm@1: //int pitch = (((ppl * systemColorDepth + 7)>>3)+3)&~3; rlm@1: int pitch = ppl * (systemColorDepth / 8) + (systemColorDepth == 24 ? 0 : 4); rlm@1: rlm@1: if (width > LUA_SCREEN_WIDTH) rlm@1: width = LUA_SCREEN_WIDTH; rlm@1: if (height > LUA_SCREEN_HEIGHT) rlm@1: height = LUA_SCREEN_HEIGHT; rlm@1: rlm@1: GetColorFunc getColor; rlm@1: SetColorFunc setColor; rlm@1: getColorIOFunc(systemColorDepth, &getColor, &setColor); rlm@1: rlm@1: for (y = 0; y < height; y++) rlm@1: { rlm@1: uint8 *scr = &screen[y * pitch]; rlm@1: for (x = 0; x < width; x++, scr += systemColorDepth / 8) rlm@1: { rlm@1: const uint8 gui_alpha = gui_data[(y * LUA_SCREEN_WIDTH + x) * 4 + 3]; rlm@1: if (gui_alpha == 0) rlm@1: { rlm@1: // do nothing rlm@1: continue; rlm@1: } rlm@1: rlm@1: const uint8 gui_red = gui_data[(y * LUA_SCREEN_WIDTH + x) * 4 + 2]; rlm@1: const uint8 gui_green = gui_data[(y * LUA_SCREEN_WIDTH + x) * 4 + 1]; rlm@1: const uint8 gui_blue = gui_data[(y * LUA_SCREEN_WIDTH + x) * 4]; rlm@1: int red, green, blue; rlm@1: rlm@1: if (gui_alpha == 255) rlm@1: { rlm@1: // direct copy rlm@1: red = gui_red; rlm@1: green = gui_green; rlm@1: blue = gui_blue; rlm@1: } rlm@1: else rlm@1: { rlm@1: // alpha-blending rlm@1: uint8 scr_red, scr_green, scr_blue; rlm@1: getColor(scr, &scr_red, &scr_green, &scr_blue); rlm@1: red = (((int)gui_red - scr_red) * gui_alpha / 255 + scr_red) & 255; rlm@1: green = (((int)gui_green - scr_green) * gui_alpha / 255 + scr_green) & 255; rlm@1: blue = (((int)gui_blue - scr_blue) * gui_alpha / 255 + scr_blue) & 255; rlm@1: } rlm@1: rlm@1: setColor(scr, (uint8) red, (uint8) green, (uint8) blue); rlm@1: } rlm@1: } rlm@1: rlm@1: return; rlm@1: } rlm@1: rlm@1: void VBALuaClearGui(void) rlm@1: { rlm@1: gui_used = false; rlm@1: } rlm@1: rlm@1: lua_State *VBAGetLuaState() rlm@1: { rlm@1: return LUA; rlm@1: } rlm@1: rlm@1: char *VBAGetLuaScriptName() rlm@1: { rlm@1: return luaScriptName; rlm@1: } rlm@1: