Mercurial > vba-clojure
view src/common/lua-engine.cpp @ 24:59790d015f25 works-incomplete
checkpoint
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sun, 04 Mar 2012 17:50:56 -0600 |
parents | bf9169ad4222 |
children |
line wrap: on
line source
1 #include <cstdio>2 #include <cstdlib>3 #include <malloc.h>4 #include <string>5 #include <cassert>6 #include <cctype>7 #include <cmath>8 #include <ctime>10 #include <vector>11 #include <map>12 #include <string>13 #include <algorithm>15 using namespace std;17 #ifdef __linux18 #include <unistd.h> // for unlink19 #include <sys/types.h>20 #include <sys/wait.h>21 #endif22 #if (defined(WIN32) && !defined(SDL))23 #include <direct.h>24 #include "../win32/stdafx.h"25 #include "../win32/Input.h"26 #include "../win32/MainWnd.h"27 #include "../win32/VBA.h"28 #include "../win32/LuaOpenDialog.h"29 #else30 #define stricmp strcasecmp31 #define strnicmp strncasecmp32 #endif34 #include "../Port.h"35 #include "System.h"36 #include "movie.h"37 #include "../gba/GBA.h"38 #include "../gba/GBAGlobals.h"39 #include "../gb/GB.h"40 #include "../gb/gbGlobals.h"41 #include "../gba/GBASound.h"43 #ifdef _WIN3244 #include "../win32/Sound.h"45 //#include "../win32/WinMiscUtil.h"46 extern CString winGetSavestateFilename(const CString &LogicalRomName, int nID);47 #else48 #endif50 extern "C"51 {52 #include "../lua/lua.h"53 #include "../lua/lauxlib.h"54 #include "../lua/lualib.h"55 #include "../lua/lstate.h"56 }57 #include "vbalua.h"59 #include "../SFMT/SFMT.c"61 static void (*info_print)(int uid, const char *str);62 static void (*info_onstart)(int uid);63 static void (*info_onstop)(int uid);64 static int info_uid;66 #ifndef countof67 #define countof(a) (sizeof(a) / sizeof(a[0]))68 #endif70 static lua_State *LUA;72 // Are we running any code right now?73 static char *luaScriptName = NULL;75 // Are we running any code right now?76 static bool8 luaRunning = false;78 // True at the frame boundary, false otherwise.79 static bool8 frameBoundary = false;81 // The execution speed we're running at.82 static enum { SPEED_NORMAL, SPEED_NOTHROTTLE, SPEED_TURBO, SPEED_MAXIMUM } speedmode = SPEED_NORMAL;84 // Rerecord count skip mode85 static bool8 skipRerecords = false;87 // Used by the registry to find our functions88 static const char *frameAdvanceThread = "VBA.FrameAdvance";89 static const char *guiCallbackTable = "VBA.GUI";91 // True if there's a thread waiting to run after a run of frame-advance.92 static bool8 frameAdvanceWaiting = false;94 // We save our pause status in the case of a natural death.95 //static bool8 wasPaused = false;97 // Transparency strength. 255=opaque, 0=so transparent it's invisible98 static int transparencyModifier = 255;100 // Our joypads.101 static uint32 lua_joypads[4];102 static uint8 lua_joypads_used = 0;104 static bool8 gui_used = false;105 static uint8 *gui_data = NULL; // BGRA107 // Protects Lua calls from going nuts.108 // We set this to a big number like 1000 and decrement it109 // over time. The script gets knifed once this reaches zero.110 static int numTries;112 // number of registered memory functions (1 per hooked byte)113 static unsigned int numMemHooks;115 // Look in inputglobal.h for macros named like BUTTON_MASK_UP to determine the order.116 static const char *button_mappings[] = {117 "A", "B", "select", "start", "right", "left", "up", "down", "R", "L"118 };120 #ifdef _MSC_VER121 #define snprintf _snprintf122 #define vscprintf _vscprintf123 #else124 #define stricmp strcasecmp125 #define strnicmp strncasecmp126 #define __forceinline __attribute__((always_inline))127 #endif129 static const char *luaCallIDStrings[] =130 {131 "CALL_BEFOREEMULATION",132 "CALL_AFTEREMULATION",133 "CALL_BEFOREEXIT"134 };136 //make sure we have the right number of strings137 CTASSERT(sizeof(luaCallIDStrings) / sizeof(*luaCallIDStrings) == LUACALL_COUNT)139 static const char *luaMemHookTypeStrings [] =140 {141 "MEMHOOK_WRITE",142 "MEMHOOK_READ",143 "MEMHOOK_EXEC",145 "MEMHOOK_WRITE_SUB",146 "MEMHOOK_READ_SUB",147 "MEMHOOK_EXEC_SUB",148 };150 //make sure we have the right number of strings151 CTASSERT(sizeof(luaMemHookTypeStrings) / sizeof(*luaMemHookTypeStrings) == LUAMEMHOOK_COUNT)153 static char *rawToCString(lua_State * L, int idx = 0);154 static const char *toCString(lua_State *L, int idx = 0);156 // GBA memory I/O functions copied from win32/MemoryViewerDlg.cpp157 static inline u8 CPUReadByteQuick(u32 addr)158 {159 return ::map[addr >> 24].address[addr & ::map[addr >> 24].mask];160 }162 static inline void CPUWriteByteQuick(u32 addr, u8 b)163 {164 ::map[addr >> 24].address[addr & ::map[addr >> 24].mask] = b;165 }167 static inline u16 CPUReadHalfWordQuick(u32 addr)168 {169 return *((u16 *) &::map[addr >> 24].address[addr & ::map[addr >> 24].mask]);170 }172 static inline void CPUWriteHalfWordQuick(u32 addr, u16 b)173 {174 *((u16 *) &::map[addr >> 24].address[addr & ::map[addr >> 24].mask]) = b;175 }177 static inline u32 CPUReadMemoryQuick(u32 addr)178 {179 return *((u32 *) &::map[addr >> 24].address[addr & ::map[addr >> 24].mask]);180 }182 static inline void CPUWriteMemoryQuick(u32 addr, u32 b)183 {184 *((u32 *) &::map[addr >> 24].address[addr & ::map[addr >> 24].mask]) = b;185 }187 // GB188 static inline u8 gbReadMemoryQuick8(u16 addr)189 {190 return gbReadMemoryQuick(addr);191 }193 static inline void gbWriteMemoryQuick8(u16 addr, u8 b)194 {195 gbWriteMemoryQuick(addr, b);196 }198 static inline u16 gbReadMemoryQuick16(u16 addr)199 {200 return (gbReadMemoryQuick(addr + 1) << 8) | gbReadMemoryQuick(addr);201 }203 static inline void gbWriteMemoryQuick16(u16 addr, u16 b)204 {205 gbWriteMemoryQuick(addr, b & 0xff);206 gbWriteMemoryQuick(addr + 1, (b >> 8) & 0xff);207 }209 static inline u32 gbReadMemoryQuick32(u16 addr)210 {211 return (gbReadMemoryQuick(addr + 3) << 24) |212 (gbReadMemoryQuick(addr + 2) << 16) |213 (gbReadMemoryQuick(addr + 1) << 8) |214 gbReadMemoryQuick(addr);215 }217 static inline void gbWriteMemoryQuick32(u16 addr, u32 b)218 {219 gbWriteMemoryQuick(addr, b & 0xff);220 gbWriteMemoryQuick(addr + 1, (b >> 8) & 0xff);221 gbWriteMemoryQuick(addr + 2, (b >> 16) & 0xff);222 gbWriteMemoryQuick(addr + 1, (b >> 24) & 0xff);223 }225 static inline u8 gbReadROMQuick8(u32 addr)226 {227 return gbReadROMQuick(addr & gbRomSizeMask);228 }230 static inline u8 gbReadROMQuick16(u32 addr)231 {232 return (gbReadROMQuick(addr+1 & gbRomSizeMask) << 8) | gbReadROMQuick(addr & gbRomSizeMask);233 }235 static inline u8 gbReadROMQuick32(u32 addr)236 {237 return (gbReadROMQuick(addr+3 & gbRomSizeMask) << 24) |238 (gbReadROMQuick(addr+2 & gbRomSizeMask) << 16) |239 (gbReadROMQuick(addr+1 & gbRomSizeMask) << 8) |240 gbReadROMQuick(addr & gbRomSizeMask);241 }243 typedef void (*GetColorFunc)(const uint8 *, uint8 *, uint8 *, uint8 *);244 typedef void (*SetColorFunc)(uint8 *, uint8, uint8, uint8);246 static void getColor16(const uint8 *s, uint8 *r, uint8 *g, uint8 *b)247 {248 u16 v = *(const uint16 *)s;249 *r = ((v >> systemBlueShift) & 0x001f) << 3;250 *g = ((v >> systemGreenShift) & 0x001f) << 3;251 *b = ((v >> systemRedShift) & 0x001f) << 3;252 }254 static void getColor24(const uint8 *s, uint8 *r, uint8 *g, uint8 *b)255 {256 if (systemRedShift > systemBlueShift)257 *b = s[0], *g = s[1], *r = s[2];258 else259 *r = s[0], *g = s[1], *b = s[2];260 }262 static void getColor32(const uint8 *s, uint8 *r, uint8 *g, uint8 *b)263 {264 u32 v = *(const uint32 *)s;265 *b = ((v >> systemBlueShift) & 0x001f) << 3;266 *g = ((v >> systemGreenShift) & 0x001f) << 3;267 *r = ((v >> systemRedShift) & 0x001f) << 3;268 }270 static void setColor16(uint8 *s, uint8 r, uint8 g, uint8 b)271 {272 *(uint16 *)s = ((b >> 3) & 0x01f) <<273 systemBlueShift |274 ((g >> 3) & 0x01f) <<275 systemGreenShift |276 ((r >> 3) & 0x01f) <<277 systemRedShift;278 }280 static void setColor24(uint8 *s, uint8 r, uint8 g, uint8 b)281 {282 if (systemRedShift > systemBlueShift)283 s[0] = b, s[1] = g, s[2] = r;284 else285 s[0] = r, s[1] = g, s[2] = b;286 }288 static void setColor32(uint8 *s, uint8 r, uint8 g, uint8 b)289 {290 *(uint32 *)s = ((b >> 3) & 0x01f) <<291 systemBlueShift |292 ((g >> 3) & 0x01f) <<293 systemGreenShift |294 ((r >> 3) & 0x01f) <<295 systemRedShift;296 }298 static bool getColorIOFunc(int depth, GetColorFunc *getColor, SetColorFunc *setColor)299 {300 switch (depth)301 {302 case 16:303 if (getColor)304 *getColor = getColor16;305 if (setColor)306 *setColor = setColor16;307 return true;308 case 24:309 if (getColor)310 *getColor = getColor24;311 if (setColor)312 *setColor = setColor24;313 return true;314 case 32:315 if (getColor)316 *getColor = getColor32;317 if (setColor)318 *setColor = setColor32;319 return true;320 default:321 return false;322 }323 }325 /**326 * Resets emulator speed / pause states after script exit.327 */328 static void VBALuaOnStop(void)329 {330 luaRunning = false;331 lua_joypads_used = 0;332 gui_used = false;333 //if (wasPaused)334 // systemSetPause(true);335 }337 /**338 * Asks Lua if it wants control of the emulator's speed.339 * Returns 0 if no, 1 if yes. If yes, we also tamper with the340 * IPPU's settings for speed ourselves, so the calling code341 * need not do anything.342 */343 int VBALuaSpeed(void)344 {345 if (!LUA || !luaRunning)346 return 0;348 //printf("%d\n", speedmode);349 switch (speedmode)350 {351 /*352 case SPEED_NORMAL:353 return 0;354 case SPEED_NOTHROTTLE:355 IPPU.RenderThisFrame = true;356 return 1;358 case SPEED_TURBO:359 IPPU.SkippedFrames++;360 if (IPPU.SkippedFrames >= 40) {361 IPPU.SkippedFrames = 0;362 IPPU.RenderThisFrame = true;363 }364 else365 IPPU.RenderThisFrame = false;366 return 1;368 // In mode 3, SkippedFrames is set to zero so that the frame369 // skipping code doesn't try anything funny.370 case SPEED_MAXIMUM:371 IPPU.SkippedFrames=0;372 IPPU.RenderThisFrame = false;373 return 1;374 */375 case 0: // FIXME: to get rid of the warning376 default:377 assert(false);378 return 0;379 }380 }382 ///////////////////////////383 // vba.speedmode(string mode)384 //385 // Takes control of the emulation speed386 // of the system. Normal is normal speed (60fps, 50 for PAL),387 // nothrottle disables speed control but renders every frame,388 // turbo renders only a few frames in order to speed up emulation,390 // maximum renders no frames391 static int vba_speedmode(lua_State *L)392 {393 const char *mode = luaL_checkstring(L, 1);395 if (strcasecmp(mode, "normal") == 0)396 {397 speedmode = SPEED_NORMAL;398 }399 else if (strcasecmp(mode, "nothrottle") == 0)400 {401 speedmode = SPEED_NOTHROTTLE;402 }403 else if (strcasecmp(mode, "turbo") == 0)404 {405 speedmode = SPEED_TURBO;406 }407 else if (strcasecmp(mode, "maximum") == 0)408 {409 speedmode = SPEED_MAXIMUM;410 }411 else412 luaL_error(L, "Invalid mode %s to vba.speedmode", mode);414 //printf("new speed mode: %d\n", speedmode);415 return 0;416 }418 // vba.frameadvnace()419 //420 // Executes a frame advance. Occurs by yielding the coroutine, then re-running422 // when we break out.423 static int vba_frameadvance(lua_State *L)424 {425 // We're going to sleep for a frame-advance. Take notes.426 if (frameAdvanceWaiting)427 return luaL_error(L, "can't call vba.frameadvance() from here");429 frameAdvanceWaiting = true;431 // Don't do this! The user won't like us sending their emulator out of control!432 // Settings.FrameAdvance = true;433 // Now we can yield to the main434 return lua_yield(L, 0);436 // It's actually rather disappointing...437 }439 // vba.pause()440 //441 // Pauses the emulator, function "waits" until the user unpauses.442 // This function MAY be called from a non-frame boundary, but the frame444 // finishes executing anwyays. In this case, the function returns immediately.445 static int vba_pause(lua_State *L)446 {447 systemSetPause(true);448 speedmode = SPEED_NORMAL;450 // Return control if we're midway through a frame. We can't pause here.451 if (frameAdvanceWaiting)452 {453 return 0;454 }456 // If it's on a frame boundary, we also yield.457 frameAdvanceWaiting = true;458 return lua_yield(L, 0);459 }461 static int vba_registerbefore(lua_State *L)462 {463 if (!lua_isnil(L, 1))464 luaL_checktype(L, 1, LUA_TFUNCTION);465 lua_settop(L, 1);466 lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEMULATION]);467 lua_insert(L, 1);468 lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEMULATION]);470 //StopScriptIfFinished(luaStateToUIDMap[L]);471 return 1;472 }474 static int vba_registerafter(lua_State *L)475 {476 if (!lua_isnil(L, 1))477 luaL_checktype(L, 1, LUA_TFUNCTION);478 lua_settop(L, 1);479 lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTEREMULATION]);480 lua_insert(L, 1);481 lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTEREMULATION]);483 //StopScriptIfFinished(luaStateToUIDMap[L]);484 return 1;485 }487 static int vba_registerexit(lua_State *L)488 {489 if (!lua_isnil(L, 1))490 luaL_checktype(L, 1, LUA_TFUNCTION);491 lua_settop(L, 1);492 lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]);493 lua_insert(L, 1);494 lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]);496 //StopScriptIfFinished(luaStateToUIDMap[L]);497 return 1;498 }500 static inline bool isalphaorunderscore(char c)501 {502 return isalpha(c) || c == '_';503 }505 static std::vector<const void *> s_tableAddressStack; // prevents infinite recursion of a table within a table (when cycle is506 // found, print something like table:parent)507 static std::vector<const void *> s_metacallStack; // prevents infinite recursion if something's __tostring returns another table508 // that contains that something (when cycle is found, print the inner result509 // without using __tostring)511 #define APPENDPRINT { int _n = snprintf(ptr, remaining,512 #define END ); if (_n >= 0) { ptr += _n; remaining -= _n; } else { remaining = 0; } }513 static void toCStringConverter(lua_State *L, int i, char * &ptr, int &remaining)514 {515 if (remaining <= 0)516 return;518 const char *str = ptr; // for debugging520 // if there is a __tostring metamethod then call it521 int usedMeta = luaL_callmeta(L, i, "__tostring");522 if (usedMeta)523 {524 std::vector<const void *>::const_iterator foundCycleIter = std::find(s_metacallStack.begin(), s_metacallStack.end(), lua_topointer(L, i));525 if (foundCycleIter != s_metacallStack.end())526 {527 lua_pop(L, 1);528 usedMeta = false;529 }530 else531 {532 s_metacallStack.push_back(lua_topointer(L, i));533 i = lua_gettop(L);534 }535 }537 switch (lua_type(L, i))538 {539 case LUA_TNONE:540 break;541 case LUA_TNIL:542 APPENDPRINT "nil" END break;543 case LUA_TBOOLEAN:544 APPENDPRINT lua_toboolean(L, i) ? "true" : "false" END break;545 case LUA_TSTRING : APPENDPRINT "%s", lua_tostring(L, i) END break;546 case LUA_TNUMBER:547 APPENDPRINT "%.12Lg", lua_tonumber(L, i) END break;548 case LUA_TFUNCTION:549 if ((L->base + i - 1)->value.gc->cl.c.isC)550 {551 //lua_CFunction func = lua_tocfunction(L, i);552 //std::map<lua_CFunction, const char*>::iterator iter = s_cFuncInfoMap.find(func);553 //if(iter == s_cFuncInfoMap.end())554 goto defcase;555 //APPENDPRINT "function(%s)", iter->second END556 }557 else558 {559 APPENDPRINT "function(" END560 Proto * p = (L->base + i - 1)->value.gc->cl.l.p;561 int numParams = p->numparams + (p->is_vararg ? 1 : 0);562 for (int n = 0; n < p->numparams; n++)563 {564 APPENDPRINT "%s", getstr(p->locvars[n].varname) END565 if (n != numParams - 1)566 APPENDPRINT "," END567 }568 if (p->is_vararg)569 APPENDPRINT "..." END570 APPENDPRINT ")" END571 }572 break;573 defcase: default:574 APPENDPRINT "%s:%p", luaL_typename(L, i), lua_topointer(L, i) END break;575 case LUA_TTABLE:576 {577 // first make sure there's enough stack space578 if (!lua_checkstack(L, 4))579 {580 // note that even if lua_checkstack never returns false,581 // that doesn't mean we didn't need to call it,582 // because calling it retrieves stack space past LUA_MINSTACK583 goto defcase;584 }586 std::vector<const void *>::const_iterator foundCycleIter =587 std::find(s_tableAddressStack.begin(), s_tableAddressStack.end(), lua_topointer(L, i));588 if (foundCycleIter != s_tableAddressStack.end())589 {590 int parentNum = s_tableAddressStack.end() - foundCycleIter;591 if (parentNum > 1)592 APPENDPRINT "%s:parent^%d", luaL_typename(L, i), parentNum END593 else594 APPENDPRINT "%s:parent", luaL_typename(L, i) END595 }596 else597 {598 s_tableAddressStack.push_back(lua_topointer(L, i));599 struct Scope { ~Scope(){ s_tableAddressStack. pop_back(); } } scope;601 APPENDPRINT "{" END603 lua_pushnil(L); // first key604 int keyIndex = lua_gettop(L);605 int valueIndex = keyIndex + 1;606 bool first = true;607 bool skipKey = true; // true if we're still in the "array part" of the table608 lua_Number arrayIndex = (lua_Number)0;609 while (lua_next(L, i))610 {611 if (first)612 first = false;613 else614 APPENDPRINT ", " END615 if (skipKey)616 {617 arrayIndex += (lua_Number)1;618 bool keyIsNumber = (lua_type(L, keyIndex) == LUA_TNUMBER);619 skipKey = keyIsNumber && (lua_tonumber(L, keyIndex) == arrayIndex);620 }621 if (!skipKey)622 {623 bool keyIsString = (lua_type(L, keyIndex) == LUA_TSTRING);624 bool invalidLuaIdentifier = (!keyIsString || !isalphaorunderscore(*lua_tostring(L, keyIndex)));625 if (invalidLuaIdentifier)626 if (keyIsString)627 APPENDPRINT "['" END628 else629 APPENDPRINT "[" END631 toCStringConverter(L, keyIndex, ptr, remaining);632 // key634 if (invalidLuaIdentifier)635 if (keyIsString)636 APPENDPRINT "']=" END637 else638 APPENDPRINT "]=" END639 else640 APPENDPRINT "=" END641 }643 bool valueIsString = (lua_type(L, valueIndex) == LUA_TSTRING);644 if (valueIsString)645 APPENDPRINT "'" END647 toCStringConverter(L, valueIndex, ptr, remaining); // value649 if (valueIsString)650 APPENDPRINT "'" END652 lua_pop(L, 1);654 if (remaining <= 0)655 {656 lua_settop(L, keyIndex - 1); // stack might not be clean yet if we're breaking657 // early658 break;659 }660 }661 APPENDPRINT "}" END662 }663 }664 break;665 }667 if (usedMeta)668 {669 s_metacallStack.pop_back();670 lua_pop(L, 1);671 }672 }674 static const int s_tempStrMaxLen = 64 * 1024;675 static char s_tempStr [s_tempStrMaxLen];677 static char *rawToCString(lua_State *L, int idx)678 {679 int a = idx > 0 ? idx : 1;680 int n = idx > 0 ? idx : lua_gettop(L);682 char *ptr = s_tempStr;683 *ptr = 0;685 int remaining = s_tempStrMaxLen;686 for (int i = a; i <= n; i++)687 {688 toCStringConverter(L, i, ptr, remaining);689 if (i != n)690 APPENDPRINT " " END691 }693 if (remaining < 3)694 {695 while (remaining < 6)696 remaining++, ptr--;697 APPENDPRINT "..." END698 }699 APPENDPRINT "\r\n" END700 // the trailing newline is so print() can avoid having to do wasteful things to print its newline701 // (string copying would be wasteful and calling info.print() twice can be extremely slow)702 // at the cost of functions that don't want the newline needing to trim off the last two characters703 // (which is a very fast operation and thus acceptable in this case)705 return s_tempStr;706 }707 #undef APPENDPRINT708 #undef END710 // replacement for luaB_tostring() that is able to show the contents of tables (and formats numbers better, and show function711 // prototypes)712 // can be called directly from lua via tostring(), assuming tostring hasn't been reassigned713 static int tostring(lua_State *L)714 {715 char *str = rawToCString(L);716 str[strlen(str) - 2] = 0; // hack: trim off the \r\n (which is there to simplify the print function's717 // task)718 lua_pushstring(L, str);719 return 1;720 }722 // like rawToCString, but will check if the global Lua function tostring()723 // has been replaced with a custom function, and call that instead if so724 static const char *toCString(lua_State *L, int idx)725 {726 int a = idx > 0 ? idx : 1;727 int n = idx > 0 ? idx : lua_gettop(L);728 lua_getglobal(L, "tostring");729 lua_CFunction cf = lua_tocfunction(L, -1);730 if (cf == tostring || lua_isnil(L, -1)) // optimization: if using our own C tostring function, we can731 // bypass the call through Lua and all the string object732 // allocation that would entail733 {734 lua_pop(L, 1);735 return rawToCString(L, idx);736 }737 else // if the user overrided the tostring function, we have to actually call it and store the738 // temporarily allocated string it returns739 {740 lua_pushstring(L, "");741 for (int i = a; i <= n; i++)742 {743 lua_pushvalue(L, -2); // function to be called744 lua_pushvalue(L, i); // value to print745 lua_call(L, 1, 1);746 if (lua_tostring(L, -1) == NULL)747 luaL_error(L, LUA_QL("tostring") " must return a string to " LUA_QL("print"));748 lua_pushstring(L, (i < n) ? " " : "\r\n");749 lua_concat(L, 3);750 }751 const char *str = lua_tostring(L, -1);752 strncpy(s_tempStr, str, s_tempStrMaxLen);753 s_tempStr[s_tempStrMaxLen - 1] = 0;754 lua_pop(L, 2);755 return s_tempStr;756 }757 }759 // replacement for luaB_print() that goes to the appropriate textbox instead of stdout760 static int print(lua_State *L)761 {762 const char *str = toCString(L);764 int uid = info_uid; //luaStateToUIDMap[L->l_G->mainthread];765 //LuaContextInfo& info = GetCurrentInfo();767 if (info_print)768 info_print(uid, str);769 else770 puts(str);772 //worry(L, 100);773 return 0;774 }776 static int printerror(lua_State *L, int idx)777 {778 lua_checkstack(L, lua_gettop(L) + 4);780 if (idx < 0)781 idx = lua_gettop(L) + 1 + idx;783 const char *str = rawToCString(L, idx);785 int uid = info_uid; //luaStateToUIDMap[L->l_G->mainthread];786 //LuaContextInfo& info = GetCurrentInfo();788 if (info_print)789 info_print(uid, str);790 else791 fputs(str, stderr);793 //worry(L, 100);794 return 0;795 }797 // vba.message(string msg)798 //799 // Displays the given message on the screen.800 static int vba_message(lua_State *L)801 {802 const char *msg = luaL_checkstring(L, 1);803 systemScreenMessage(msg);805 return 0;806 }808 // provides an easy way to copy a table from Lua809 // (simple assignment only makes an alias, but sometimes an independent table is desired)810 // currently this function only performs a shallow copy,811 // but I think it should be changed to do a deep copy (possibly of configurable depth?)812 // that maintains the internal table reference structure813 static int copytable(lua_State *L)814 {815 int origIndex = 1; // we only care about the first argument816 int origType = lua_type(L, origIndex);817 if (origType == LUA_TNIL)818 {819 lua_pushnil(L);820 return 1;821 }822 if (origType != LUA_TTABLE)823 {824 luaL_typerror(L, 1, lua_typename(L, LUA_TTABLE));825 lua_pushnil(L);826 return 1;827 }829 lua_createtable(L, lua_objlen(L, 1), 0);830 int copyIndex = lua_gettop(L);832 lua_pushnil(L); // first key833 int keyIndex = lua_gettop(L);834 int valueIndex = keyIndex + 1;836 while (lua_next(L, origIndex))837 {838 lua_pushvalue(L, keyIndex);839 lua_pushvalue(L, valueIndex);840 lua_rawset(L, copyIndex); // copytable[key] = value841 lua_pop(L, 1);842 }844 // copy the reference to the metatable as well, if any845 if (lua_getmetatable(L, origIndex))846 lua_setmetatable(L, copyIndex);848 return 1; // return the new table849 }851 // because print traditionally shows the address of tables,852 // and the print function I provide instead shows the contents of tables,853 // I also provide this function854 // (otherwise there would be no way to see a table's address, AFAICT)855 static int addressof(lua_State *L)856 {857 const void *ptr = lua_topointer(L, -1);858 lua_pushinteger(L, (lua_Integer)ptr);859 return 1;860 }862 struct registerPointerMap863 {864 const char * registerName;865 unsigned int *pointer;866 int dataSize;867 };869 #define RPM_ENTRY(name, var) \870 { name, (unsigned int *)&var, sizeof(var) \871 } \872 ,874 extern gbRegister AF;875 extern gbRegister BC;876 extern gbRegister DE;877 extern gbRegister HL;878 extern gbRegister SP;879 extern gbRegister PC;880 extern u16 IFF;882 registerPointerMap regPointerMap [] = {883 // gba registers884 RPM_ENTRY("r0", reg[0].I)885 RPM_ENTRY("r1", reg[1].I)886 RPM_ENTRY("r2", reg[2].I)887 RPM_ENTRY("r3", reg[3].I)888 RPM_ENTRY("r4", reg[4].I)889 RPM_ENTRY("r5", reg[5].I)890 RPM_ENTRY("r6", reg[6].I)891 RPM_ENTRY("r7", reg[7].I)892 RPM_ENTRY("r8", reg[8].I)893 RPM_ENTRY("r9", reg[9].I)894 RPM_ENTRY("r10", reg[10].I)895 RPM_ENTRY("r11", reg[11].I)896 RPM_ENTRY("r12", reg[12].I)897 RPM_ENTRY("r13", reg[13].I)898 RPM_ENTRY("r14", reg[14].I)899 RPM_ENTRY("r15", reg[15].I)900 RPM_ENTRY("cpsr", reg[16].I)901 RPM_ENTRY("spsr", reg[17].I)902 // gb registers903 RPM_ENTRY("a", AF.B.B1)904 RPM_ENTRY("f", AF.B.B0)905 RPM_ENTRY("b", BC.B.B1)906 RPM_ENTRY("c", BC.B.B0)907 RPM_ENTRY("d", DE.B.B1)908 RPM_ENTRY("e", DE.B.B0)909 RPM_ENTRY("h", HL.B.B1)910 RPM_ENTRY("l", HL.B.B0)911 RPM_ENTRY("af", AF.W)912 RPM_ENTRY("bc", BC.W)913 RPM_ENTRY("de", DE.W)914 RPM_ENTRY("hl", HL.W)915 RPM_ENTRY("sp", SP.W)916 RPM_ENTRY("pc", PC.W)917 {}918 };920 struct cpuToRegisterMap921 {922 const char *cpuName;923 registerPointerMap *rpmap;924 }925 cpuToRegisterMaps [] =926 {927 { "", regPointerMap },928 };930 //DEFINE_LUA_FUNCTION(memory_getregister, "cpu_dot_registername_string")931 static int memory_getregister(lua_State *L)932 {933 const char *qualifiedRegisterName = luaL_checkstring(L, 1);934 lua_settop(L, 0);935 for (int cpu = 0; cpu < sizeof(cpuToRegisterMaps) / sizeof(*cpuToRegisterMaps); cpu++)936 {937 cpuToRegisterMap ctrm = cpuToRegisterMaps[cpu];938 int cpuNameLen = strlen(ctrm.cpuName);939 if (!strnicmp(qualifiedRegisterName, ctrm.cpuName, cpuNameLen))940 {941 qualifiedRegisterName += cpuNameLen;942 for (int reg = 0; ctrm.rpmap[reg].dataSize; reg++)943 {944 registerPointerMap rpm = ctrm.rpmap[reg];945 if (!stricmp(qualifiedRegisterName, rpm.registerName))946 {947 switch (rpm.dataSize)948 {949 default:950 case 1:951 lua_pushinteger(L, *(unsigned char *)rpm.pointer); break;952 case 2:953 lua_pushinteger(L, *(unsigned short *)rpm.pointer); break;954 case 4:955 lua_pushinteger(L, *(unsigned long *)rpm.pointer); break;956 }957 return 1;958 }959 }960 lua_pushnil(L);961 return 1;962 }963 }964 lua_pushnil(L);965 return 1;966 }968 //DEFINE_LUA_FUNCTION(memory_setregister, "cpu_dot_registername_string,value")969 static int memory_setregister(lua_State *L)970 {971 const char * qualifiedRegisterName = luaL_checkstring(L, 1);972 unsigned long value = (unsigned long)(luaL_checkinteger(L, 2));973 lua_settop(L, 0);974 for (int cpu = 0; cpu < sizeof(cpuToRegisterMaps) / sizeof(*cpuToRegisterMaps); cpu++)975 {976 cpuToRegisterMap ctrm = cpuToRegisterMaps[cpu];977 int cpuNameLen = strlen(ctrm.cpuName);978 if (!strnicmp(qualifiedRegisterName, ctrm.cpuName, cpuNameLen))979 {980 qualifiedRegisterName += cpuNameLen;981 for (int reg = 0; ctrm.rpmap[reg].dataSize; reg++)982 {983 registerPointerMap rpm = ctrm.rpmap[reg];984 if (!stricmp(qualifiedRegisterName, rpm.registerName))985 {986 switch (rpm.dataSize)987 {988 default:989 case 1:990 *(unsigned char *)rpm.pointer = (unsigned char)(value & 0xFF); break;991 case 2:992 *(unsigned short *)rpm.pointer = (unsigned short)(value & 0xFFFF); break;993 case 4:994 *(unsigned long *)rpm.pointer = value; break;995 }996 return 0;997 }998 }999 return 0;1000 }1001 }1002 return 0;1003 }1005 void HandleCallbackError(lua_State *L)1006 {1007 if (L->errfunc || L->errorJmp)1008 luaL_error(L, "%s", lua_tostring(L, -1));1009 else1010 {1011 lua_pushnil(LUA);1012 lua_setfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable);1014 // Error?1015 //#if (defined(WIN32) && !defined(SDL))1016 // info_print(info_uid, lua_tostring(LUA, -1)); //Clear_Sound_Buffer();1017 // AfxGetApp()->m_pMainWnd->MessageBox(lua_tostring(LUA, -1), "Lua run error", MB_OK | MB_ICONSTOP);1018 //#else1019 // fprintf(stderr, "Lua thread bombed out: %s\n", lua_tostring(LUA, -1));1020 //#endif1021 printerror(LUA, -1);1022 VBALuaStop();1023 }1024 }1026 void CallRegisteredLuaFunctions(LuaCallID calltype)1027 {1028 assert((unsigned int)calltype < (unsigned int)LUACALL_COUNT);1030 const char *idstring = luaCallIDStrings[calltype];1032 if (!LUA)1033 return;1035 lua_settop(LUA, 0);1036 lua_getfield(LUA, LUA_REGISTRYINDEX, idstring);1038 int errorcode = 0;1039 if (lua_isfunction(LUA, -1))1040 {1041 errorcode = lua_pcall(LUA, 0, 0, 0);1042 if (errorcode)1043 HandleCallbackError(LUA);1044 }1045 else1046 {1047 lua_pop(LUA, 1);1048 }1049 }1051 // the purpose of this structure is to provide a way of1052 // QUICKLY determining whether a memory address range has a hook associated with it,1053 // with a bias toward fast rejection because the majority of addresses will not be hooked.1054 // (it must not use any part of Lua or perform any per-script operations,1055 // otherwise it would definitely be too slow.)1056 // calculating the regions when a hook is added/removed may be slow,1057 // but this is an intentional tradeoff to obtain a high speed of checking during later execution1058 struct TieredRegion1059 {1060 template<unsigned int maxGap>1061 struct Region1062 {1063 struct Island1064 {1065 unsigned int start;1066 unsigned int end;1067 __forceinline bool Contains(unsigned int address, int size) const { return address < end && address + size > start; }1068 };1069 std::vector<Island> islands;1071 void Calculate(const std::vector<unsigned int> &bytes)1072 {1073 islands. clear();1075 unsigned int lastEnd = ~0;1077 std::vector<unsigned int>::const_iterator iter = bytes.begin();1078 std::vector<unsigned int>::const_iterator end = bytes.end();1079 for (; iter != end; ++iter)1080 {1081 unsigned int addr = *iter;1082 if (addr < lastEnd || addr > lastEnd + (long long)maxGap)1083 {1084 islands. push_back(Island());1085 islands. back().start = addr;1086 }1087 islands.back(). end = addr + 1;1088 lastEnd = addr + 1;1089 }1090 }1092 bool Contains(unsigned int address, int size) const1093 {1094 for (size_t i = 0; i != islands.size(); ++i)1095 {1096 if (islands[i].Contains(address, size))1097 return true;1098 }1099 return false;1100 }1101 };1103 Region<0xFFFFFFFF> broad;1104 Region<0x1000> mid;1105 Region<0> narrow;1107 void Calculate(std::vector<unsigned int> &bytes)1108 {1109 std:: sort(bytes.begin(), bytes.end());1111 broad. Calculate(bytes);1112 mid. Calculate(bytes);1113 narrow. Calculate(bytes);1114 }1116 TieredRegion()1117 {1118 std::vector <unsigned int> temp;1119 Calculate(temp);1120 }1122 __forceinline int NotEmpty()1123 {1124 return broad.islands.size();1125 }1127 // note: it is illegal to call this if NotEmpty() returns 01128 __forceinline bool Contains(unsigned int address, int size)1129 {1130 return broad.islands[0].Contains(address, size) &&1131 mid.Contains(address, size) &&1132 narrow.Contains(address, size);1133 }1134 };1135 TieredRegion hookedRegions [LUAMEMHOOK_COUNT];1137 static void CalculateMemHookRegions(LuaMemHookType hookType)1138 {1139 std::vector<unsigned int> hookedBytes;1140 // std::map<int, LuaContextInfo*>::iterator iter = luaContextInfo.begin();1141 // std::map<int, LuaContextInfo*>::iterator end = luaContextInfo.end();1142 // while(iter != end)1143 // {1144 // LuaContextInfo& info = *iter->second;1145 if (/*info.*/ numMemHooks)1146 {1147 lua_State *L = LUA /*info.L*/;1148 if (L)1149 {1150 lua_settop(L, 0);1151 lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]);1152 lua_pushnil(L);1153 while (lua_next(L, -2))1154 {1155 if (lua_isfunction(L, -1))1156 {1157 unsigned int addr = lua_tointeger(L, -2);1158 hookedBytes.push_back(addr);1159 }1160 lua_pop(L, 1);1161 }1162 lua_settop(L, 0);1163 }1164 }1165 // ++iter;1166 // }1167 hookedRegions[hookType].Calculate(hookedBytes);1168 }1170 static void CallRegisteredLuaMemHook_LuaMatch(unsigned int address, int size, unsigned int value, LuaMemHookType hookType)1171 {1172 // std::map<int, LuaContextInfo*>::iterator iter = luaContextInfo.begin();1173 // std::map<int, LuaContextInfo*>::iterator end = luaContextInfo.end();1174 // while(iter != end)1175 // {1176 // LuaContextInfo& info = *iter->second;1177 if (/*info.*/ numMemHooks)1178 {1179 lua_State *L = LUA /*info.L*/;1180 if (L /* && !info.panic*/)1181 {1182 #ifdef USE_INFO_STACK1183 infoStack.insert(infoStack.begin(), &info);1184 struct Scope { ~Scope(){ infoStack. erase(infoStack.begin()); } } scope;1185 #endif1186 lua_settop(L, 0);1187 lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]);1188 for (int i = address; i != address + size; i++)1189 {1190 lua_rawgeti(L, -1, i);1191 if (lua_isfunction(L, -1))1192 {1193 bool wasRunning = (luaRunning != 0) /*info.running*/;1194 luaRunning /*info.running*/ = true;1195 //RefreshScriptSpeedStatus();1196 lua_pushinteger(L, address);1197 lua_pushinteger(L, size);1198 int errorcode = lua_pcall(L, 2, 0, 0);1199 luaRunning /*info.running*/ = wasRunning;1200 //RefreshScriptSpeedStatus();1201 if (errorcode)1202 {1203 HandleCallbackError(L);1204 //int uid = iter->first;1205 //HandleCallbackError(L,info,uid,true);1206 }1207 break;1208 }1209 else1210 {1211 lua_pop(L, 1);1212 }1213 }1214 lua_settop(L, 0);1215 }1216 }1217 // ++iter;1218 // }1219 }1221 void CallRegisteredLuaMemHook(unsigned int address, int size, unsigned int value, LuaMemHookType hookType)1222 {1223 // performance critical! (called VERY frequently)1224 // I suggest timing a large number of calls to this function in Release if you change anything in here,1225 // before and after, because even the most innocent change can make it become 30% to 400% slower.1226 // a good amount to test is: 100000000 calls with no hook set, and another 100000000 with a hook set.1227 // (on my system that consistently took 200 ms total in the former case and 350 ms total in the latter1228 // case)1229 if (hookedRegions[hookType].NotEmpty())1230 {1231 //if((hookType <= LUAMEMHOOK_EXEC) && (address >= 0xE00000))1232 // address |= 0xFF0000; // account for mirroring of RAM1233 if (hookedRegions[hookType].Contains(address, size))1234 CallRegisteredLuaMemHook_LuaMatch(address, size, value, hookType); // something has hooked this1235 // specific address1236 }1237 }1239 static int memory_registerHook(lua_State *L, LuaMemHookType hookType, int defaultSize)1240 {1241 // get first argument: address1242 unsigned int addr = luaL_checkinteger(L, 1);1243 //if((addr & ~0xFFFFFF) == ~0xFFFFFF)1244 // addr &= 0xFFFFFF;1246 // get optional second argument: size1247 int size = defaultSize;1248 int funcIdx = 2;1249 if (lua_isnumber(L, 2))1250 {1251 size = luaL_checkinteger(L, 2);1252 if (size < 0)1253 {1254 size = -size;1255 addr -= size;1256 }1257 funcIdx++;1258 }1260 // check last argument: callback function1261 bool clearing = lua_isnil(L, funcIdx);1262 if (!clearing)1263 luaL_checktype(L, funcIdx, LUA_TFUNCTION);1264 lua_settop(L, funcIdx);1266 // get the address-to-callback table for this hook type of the current script1267 lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]);1269 // count how many callback functions we'll be displacing1270 int numFuncsAfter = clearing ? 0 : size;1271 int numFuncsBefore = 0;1272 for (unsigned int i = addr; i != addr + size; i++)1273 {1274 lua_rawgeti(L, -1, i);1275 if (lua_isfunction(L, -1))1276 numFuncsBefore++;1277 lua_pop(L, 1);1278 }1280 // put the callback function in the address slots1281 for (unsigned int i = addr; i != addr + size; i++)1282 {1283 lua_pushvalue(L, -2);1284 lua_rawseti(L, -2, i);1285 }1287 // adjust the count of active hooks1288 //LuaContextInfo& info = GetCurrentInfo();1289 /*info.*/ numMemHooks += numFuncsAfter - numFuncsBefore;1291 // re-cache regions of hooked memory across all scripts1292 CalculateMemHookRegions(hookType);1294 //StopScriptIfFinished(luaStateToUIDMap[L]);1295 return 0;1296 }1298 LuaMemHookType MatchHookTypeToCPU(lua_State *L, LuaMemHookType hookType)1299 {1300 int cpuID = 0;1302 int cpunameIndex = 0;1303 if (lua_type(L, 2) == LUA_TSTRING)1304 cpunameIndex = 2;1305 else if (lua_type(L, 3) == LUA_TSTRING)1306 cpunameIndex = 3;1308 if (cpunameIndex)1309 {1310 const char *cpuName = lua_tostring(L, cpunameIndex);1311 if (!stricmp(cpuName, "sub"))1312 cpuID = 1;1313 lua_remove(L, cpunameIndex);1314 }1316 switch (cpuID)1317 {1318 case 0:1319 return hookType;1321 case 1:1322 switch (hookType)1323 {1324 case LUAMEMHOOK_WRITE:1325 return LUAMEMHOOK_WRITE_SUB;1326 case LUAMEMHOOK_READ:1327 return LUAMEMHOOK_READ_SUB;1328 case LUAMEMHOOK_EXEC:1329 return LUAMEMHOOK_EXEC_SUB;1330 }1331 }1332 return hookType;1333 }1335 static int memory_registerwrite(lua_State *L)1336 {1337 return memory_registerHook(L, MatchHookTypeToCPU(L, LUAMEMHOOK_WRITE), 1);1338 }1340 static int memory_registerread(lua_State *L)1341 {1342 return memory_registerHook(L, MatchHookTypeToCPU(L, LUAMEMHOOK_READ), 1);1343 }1345 static int memory_registerexec(lua_State *L)1346 {1347 return memory_registerHook(L, MatchHookTypeToCPU(L, LUAMEMHOOK_EXEC), 1);1348 }1350 //int vba.lagcount1351 //1353 //Returns the lagcounter variable1354 static int vba_getlagcount(lua_State *L)1355 {1356 lua_pushinteger(L, systemCounters.lagCount);1357 return 1;1358 }1360 //int vba.lagged1361 //1362 //Returns true if the current frame is a lag frame1363 static int vba_lagged(lua_State *L)1364 {1365 lua_pushboolean(L, systemCounters.laggedLast);1366 return 1;1367 }1369 // boolean vba.emulating()1370 int vba_emulating(lua_State *L)1371 {1372 lua_pushboolean(L, systemIsEmulating());1373 return 1;1374 }1376 int movie_isactive(lua_State *L)1377 {1378 lua_pushboolean(L, VBAMovieActive());1379 return 1;1380 }1382 int movie_isrecording(lua_State *L)1383 {1384 lua_pushboolean(L, VBAMovieRecording());1385 return 1;1386 }1388 int movie_isplaying(lua_State *L)1389 {1390 lua_pushboolean(L, VBAMoviePlaying());1391 return 1;1392 }1394 int movie_getlength(lua_State *L)1395 {1396 if (VBAMovieActive())1397 lua_pushinteger(L, VBAMovieGetLength());1398 else1399 lua_pushinteger(L, 0);1400 return 1;1401 }1403 static int memory_readbyte(lua_State *L)1404 {1405 u32 addr;1406 u8 val;1408 addr = luaL_checkinteger(L, 1);1409 if (systemIsRunningGBA())1410 {1411 val = CPUReadByteQuick(addr);1412 }1413 else1414 {1415 val = gbReadMemoryQuick8(addr);1416 }1418 lua_pushinteger(L, val);1419 return 1;1420 }1422 static int memory_readbytesigned(lua_State *L)1423 {1424 u32 addr;1425 s8 val;1427 addr = luaL_checkinteger(L, 1);1428 if (systemIsRunningGBA())1429 {1430 val = (s8) CPUReadByteQuick(addr);1431 }1432 else1433 {1434 val = (s8) gbReadMemoryQuick8(addr);1435 }1437 lua_pushinteger(L, val);1438 return 1;1439 }1441 static int memory_readword(lua_State *L)1442 {1443 u32 addr;1444 u16 val;1446 addr = luaL_checkinteger(L, 1);1447 if (systemIsRunningGBA())1448 {1449 val = CPUReadHalfWordQuick(addr);1450 }1451 else1452 {1453 val = gbReadMemoryQuick16(addr & 0x0000FFFF);1454 }1456 lua_pushinteger(L, val);1457 return 1;1458 }1460 static int memory_readwordsigned(lua_State *L)1461 {1462 u32 addr;1463 s16 val;1465 addr = luaL_checkinteger(L, 1);1466 if (systemIsRunningGBA())1467 {1468 val = (s16) CPUReadHalfWordQuick(addr);1469 }1470 else1471 {1472 val = (s16) gbReadMemoryQuick16(addr);1473 }1475 lua_pushinteger(L, val);1476 return 1;1477 }1479 static int memory_readdword(lua_State *L)1480 {1481 u32 addr;1482 u32 val;1484 addr = luaL_checkinteger(L, 1);1485 if (systemIsRunningGBA())1486 {1487 val = CPUReadMemoryQuick(addr);1488 }1489 else1490 {1491 val = gbReadMemoryQuick32(addr & 0x0000FFFF);1492 }1494 // lua_pushinteger doesn't work properly for 32bit system, does it?1495 if (val >= 0x80000000 && sizeof(int) <= 4)1496 lua_pushnumber(L, val);1497 else1498 lua_pushinteger(L, val);1499 return 1;1500 }1502 static int memory_readdwordsigned(lua_State *L)1503 {1504 u32 addr;1505 s32 val;1507 addr = luaL_checkinteger(L, 1);1508 if (systemIsRunningGBA())1509 {1510 val = (s32) CPUReadMemoryQuick(addr);1511 }1512 else1513 {1514 val = (s32) gbReadMemoryQuick32(addr);1515 }1517 lua_pushinteger(L, val);1518 return 1;1519 }1521 static int memory_readbyterange(lua_State *L)1522 {1523 uint32 address = luaL_checkinteger(L, 1);1524 int length = luaL_checkinteger(L, 2);1526 if (length < 0)1527 {1528 address += length;1529 length = -length;1530 }1532 // push the array1533 lua_createtable(L, abs(length), 0);1535 // put all the values into the (1-based) array1536 for (int a = address, n = 1; n <= length; a++, n++)1537 {1538 unsigned char value;1540 if (systemIsRunningGBA())1541 {1542 value = CPUReadByteQuick(a);1543 }1544 else1545 {1546 value = gbReadMemoryQuick8(a);1547 }1549 lua_pushinteger(L, value);1550 lua_rawseti(L, -2, n);1551 }1553 return 1;1554 }1556 static int memory_writebyte(lua_State *L)1557 {1558 u32 addr;1559 int val;1561 addr = luaL_checkinteger(L, 1);1562 val = luaL_checkinteger(L, 2);1563 if (systemIsRunningGBA())1564 {1565 CPUWriteByteQuick(addr, val);1566 }1567 else1568 {1569 gbWriteMemoryQuick8(addr, val);1570 }1572 CallRegisteredLuaMemHook(addr, 1, val, LUAMEMHOOK_WRITE);1573 return 0;1574 }1576 static int memory_writeword(lua_State *L)1577 {1578 u32 addr;1579 int val;1581 addr = luaL_checkinteger(L, 1);1582 val = luaL_checkinteger(L, 2);1583 if (systemIsRunningGBA())1584 {1585 CPUWriteHalfWordQuick(addr, val);1586 }1587 else1588 {1589 gbWriteMemoryQuick16(addr, val);1590 }1592 CallRegisteredLuaMemHook(addr, 2, val, LUAMEMHOOK_WRITE);1593 return 0;1594 }1596 static int memory_writedword(lua_State *L)1597 {1598 u32 addr;1599 int val;1601 addr = luaL_checkinteger(L, 1);1602 val = luaL_checkinteger(L, 2);1603 if (systemIsRunningGBA())1604 {1605 CPUWriteMemoryQuick(addr, val);1606 }1607 else1608 {1609 gbWriteMemoryQuick32(addr, val);1610 }1612 CallRegisteredLuaMemHook(addr, 4, val, LUAMEMHOOK_WRITE);1613 return 0;1614 }1616 static int memory_gbromreadbyte(lua_State *L)1617 {1618 u32 addr;1619 u8 val;1621 addr = luaL_checkinteger(L, 1);1622 if (systemIsRunningGBA())1623 {1624 lua_pushnil(L);1625 return 1;1626 }1627 else1628 {1629 val = gbReadROMQuick8(addr);1630 }1632 lua_pushinteger(L, val);1633 return 1;1634 }1636 static int memory_gbromreadbytesigned(lua_State *L)1637 {1638 u32 addr;1639 s8 val;1641 addr = luaL_checkinteger(L, 1);1642 if (systemIsRunningGBA())1643 {1644 lua_pushnil(L);1645 return 1;1646 }1647 else1648 {1649 val = (s8) gbReadROMQuick8(addr);1650 }1652 lua_pushinteger(L, val);1653 return 1;1654 }1656 static int memory_gbromreadword(lua_State *L)1657 {1658 u32 addr;1659 u16 val;1661 addr = luaL_checkinteger(L, 1);1662 if (systemIsRunningGBA())1663 {1664 lua_pushnil(L);1665 return 1;1666 }1667 else1668 {1669 val = gbReadROMQuick16(addr);1670 }1672 lua_pushinteger(L, val);1673 return 1;1674 }1676 static int memory_gbromreadwordsigned(lua_State *L)1677 {1678 u32 addr;1679 s16 val;1681 addr = luaL_checkinteger(L, 1);1682 if (systemIsRunningGBA())1683 {1684 lua_pushnil(L);1685 return 1;1686 }1687 else1688 {1689 val = (s16) gbReadROMQuick16(addr);1690 }1692 lua_pushinteger(L, val);1693 return 1;1694 }1696 static int memory_gbromreaddword(lua_State *L)1697 {1698 u32 addr;1699 u32 val;1701 addr = luaL_checkinteger(L, 1);1702 if (systemIsRunningGBA())1703 {1704 lua_pushnil(L);1705 return 1;1706 }1707 else1708 {1709 val = gbReadROMQuick32(addr);1710 }1712 // lua_pushinteger doesn't work properly for 32bit system, does it?1713 if (val >= 0x80000000 && sizeof(int) <= 4)1714 lua_pushnumber(L, val);1715 else1716 lua_pushinteger(L, val);1717 return 1;1718 }1720 static int memory_gbromreaddwordsigned(lua_State *L)1721 {1722 u32 addr;1723 s32 val;1725 addr = luaL_checkinteger(L, 1);1726 if (systemIsRunningGBA())1727 {1728 lua_pushnil(L);1729 return 1;1730 }1731 else1732 {1733 val = (s32) gbReadROMQuick32(addr);1734 }1736 lua_pushinteger(L, val);1737 return 1;1738 }1740 static int memory_gbromreadbyterange(lua_State *L)1741 {1742 uint32 address = luaL_checkinteger(L, 1);1743 int length = luaL_checkinteger(L, 2);1745 if (length < 0)1746 {1747 address += length;1748 length = -length;1749 }1751 // push the array1752 lua_createtable(L, abs(length), 0);1754 // put all the values into the (1-based) array1755 for (int a = address, n = 1; n <= length; a++, n++)1756 {1757 unsigned char value;1759 if (systemIsRunningGBA())1760 {1761 lua_pushnil(L);1762 return 1;1763 }1764 else1765 {1766 value = gbReadROMQuick8(a);1767 }1769 lua_pushinteger(L, value);1770 lua_rawseti(L, -2, n);1771 }1773 return 1;1774 }1776 // table joypad.get(int which = 1)1777 //1778 // Reads the joypads as inputted by the user.1779 static int joy_get_internal(lua_State *L, bool reportUp, bool reportDown)1780 {1781 // Reads the joypads as inputted by the user1782 int which = luaL_checkinteger(L, 1);1784 if (which < 0 || which > 4)1785 {1786 luaL_error(L, "Invalid input port (valid range 0-4, specified %d)", which);1787 }1789 uint32 buttons = systemGetOriginalJoypad(which - 1, false);1791 lua_newtable(L);1793 int i;1794 for (i = 0; i < 10; i++)1795 {1796 bool pressed = (buttons & (1 << i)) != 0;1797 if ((pressed && reportDown) || (!pressed && reportUp))1798 {1799 lua_pushboolean(L, pressed);1800 lua_setfield(L, -2, button_mappings[i]);1801 }1802 }1804 return 1;1805 }1807 // joypad.get(which)1808 // returns a table of every game button,1809 // true meaning currently-held and false meaning not-currently-held1810 // (as of last frame boundary)1811 // this WILL read input from a currently-playing movie1812 static int joypad_get(lua_State *L)1813 {1814 return joy_get_internal(L, true, true);1815 }1817 // joypad.getdown(which)1818 // returns a table of every game button that is currently held1819 static int joypad_getdown(lua_State *L)1820 {1821 return joy_get_internal(L, false, true);1822 }1824 // joypad.getup(which)1825 // returns a table of every game button that is not currently held1826 static int joypad_getup(lua_State *L)1827 {1828 return joy_get_internal(L, true, false);1829 }1831 // joypad.set(int which, table buttons)1832 //1833 // Sets the given buttons to be pressed during the next1834 // frame advance. The table should have the right1836 // keys (no pun intended) set.1837 static int joypad_set(lua_State *L)1838 {1839 // Which joypad we're tampering with1840 int which = luaL_checkinteger(L, 1);1841 if (which < 0 || which > 4)1842 {1843 luaL_error(L, "Invalid output port (valid range 0-4, specified %d)", which);1844 }1846 if (which == 0)1847 which = systemGetDefaultJoypad();1849 // And the table of buttons.1850 luaL_checktype(L, 2, LUA_TTABLE);1852 // Set up for taking control of the indicated controller1853 lua_joypads_used |= 1 << (which - 1);1854 lua_joypads[which - 1] = 0;1856 for (int i = 0; i < 10; i++)1857 {1858 const char *name = button_mappings[i];1859 lua_getfield(L, 2, name);1860 if (!lua_isnil(L, -1))1861 {1862 bool pressed = lua_toboolean(L, -1) != 0;1863 if (pressed)1864 lua_joypads[which - 1] |= 1 << i;1865 else1866 lua_joypads[which - 1] &= ~(1 << i);1867 }1868 lua_pop(L, 1);1869 }1871 return 0;1872 }1874 // Helper function to convert a savestate object to the filename it represents.1875 static const char *savestateobj2filename(lua_State *L, int offset)1876 {1877 // First we get the metatable of the indicated object1878 int result = lua_getmetatable(L, offset);1880 if (!result)1881 luaL_error(L, "object not a savestate object");1883 // Also check that the type entry is set1884 lua_getfield(L, -1, "__metatable");1885 if (strcmp(lua_tostring(L, -1), "vba Savestate") != 0)1886 luaL_error(L, "object not a savestate object");1887 lua_pop(L, 1);1889 // Now, get the field we want1890 lua_getfield(L, -1, "filename");1892 // Return it1893 return lua_tostring(L, -1);1894 }1896 // Helper function for garbage collection.1897 static int savestate_gc(lua_State *L)1898 {1899 // The object we're collecting is on top of the stack1900 lua_getmetatable(L, 1);1902 // Get the filename1903 const char *filename;1904 lua_getfield(L, -1, "filename");1905 filename = lua_tostring(L, -1);1907 // Delete the file1908 remove(filename);1910 // We exit, and the garbage collector takes care of the rest.1911 // Edit: Visual Studio needs a return value anyway, so returns 0.1912 return 0;1913 }1915 // object savestate.create(int which = nil)1916 //1917 // Creates an object used for savestates.1918 // The object can be associated with a player-accessible savestate1920 // ("which" between 1 and 12) or not (which == nil).1921 static int savestate_create(lua_State *L)1922 {1923 int which = -1;1924 if (lua_gettop(L) >= 1)1925 {1926 which = luaL_checkinteger(L, 1);1927 if (which < 1 || which > 12)1928 {1929 luaL_error(L, "invalid player's savestate %d", which);1930 }1931 }1933 char stateName[2048];1935 if (which > 0)1936 {1937 // Find an appropriate filename. This is OS specific, unfortunately.1938 #if (defined(WIN32) && !defined(SDL))1939 CString stateName = winGetSavestateFilename(theApp.gameFilename, which);1940 #else1941 extern char saveDir[2048];1942 extern char filename[2048];1943 extern char *sdlGetFilename(char *name);1945 if (saveDir[0])1946 sprintf(stateName, "%s/%s%d.sgm", saveDir, sdlGetFilename(filename), which);1947 else1948 sprintf(stateName, "%s%d.sgm", filename, which);1949 #endif1950 }1951 else1952 {1953 char *stateNameTemp = tempnam(NULL, "snlua");1954 strcpy(stateName, stateNameTemp);1955 if (stateNameTemp)1956 free(stateNameTemp);1957 }1959 // Our "object". We don't care about the type, we just need the memory and GC services.1960 lua_newuserdata(L, 1);1962 // The metatable we use, protected from Lua and contains garbage collection info and stuff.1963 lua_newtable(L);1965 // First, we must protect it1966 lua_pushstring(L, "vba Savestate");1967 lua_setfield(L, -2, "__metatable");1969 // Now we need to save the file itself.1970 lua_pushstring(L, stateName);1971 lua_setfield(L, -2, "filename");1973 // If it's an anonymous savestate, we must delete the file from disk should it be gargage collected1974 if (which < 0)1975 {1976 lua_pushcfunction(L, savestate_gc);1977 lua_setfield(L, -2, "__gc");1978 }1980 // Set the metatable1981 lua_setmetatable(L, -2);1983 // Awesome. Return the object1984 return 1;1985 }1987 // savestate.save(object state)1988 //1990 // Saves a state to the given object.1991 static int savestate_save(lua_State *L)1992 {1993 const char *filename = savestateobj2filename(L, 1);1995 // printf("saving %s\n", filename);1996 // Save states are very expensive. They take time.1997 numTries--;1999 bool8 retvalue = theEmulator.emuWriteState ? theEmulator.emuWriteState(filename) : false;2000 if (!retvalue)2001 {2002 // Uh oh2003 luaL_error(L, "savestate failed");2004 }2006 return 0;2007 }2009 // savestate.load(object state)2010 //2012 // Loads the given state2013 static int savestate_load(lua_State *L)2014 {2015 const char *filename = savestateobj2filename(L, 1);2017 numTries--;2019 // printf("loading %s\n", filename);2020 bool8 retvalue = theEmulator.emuReadState ? theEmulator.emuReadState(filename) : false;2021 if (!retvalue)2022 {2023 // Uh oh2024 luaL_error(L, "loadstate failed");2025 }2027 return 0;2028 }2030 // int vba.framecount()2031 //2033 // Gets the frame counter for the movie, or the number of frames since last reset.2034 int vba_framecount(lua_State *L)2035 {2036 if (!VBAMovieActive())2037 {2038 lua_pushinteger(L, systemCounters.frameCount);2039 }2040 else2041 {2042 lua_pushinteger(L, VBAMovieGetFrameCounter());2043 }2045 return 1;2046 }2048 //string movie.getauthor2049 //2051 // returns author info field of .vbm file2052 int movie_getauthor(lua_State *L)2053 {2054 if (!VBAMovieActive())2055 {2056 //lua_pushnil(L);2057 lua_pushstring(L, "");2058 return 1;2059 }2061 lua_pushstring(L, VBAMovieGetAuthorInfo().c_str());2062 return 1;2063 }2065 //string movie.filename2066 int movie_getfilename(lua_State *L)2067 {2068 if (!VBAMovieActive())2069 {2070 //lua_pushnil(L);2071 lua_pushstring(L, "");2072 return 1;2073 }2075 lua_pushstring(L, VBAMovieGetFilename().c_str());2076 return 1;2077 }2079 // string movie.mode()2080 //2082 // "record", "playback" or nil2083 int movie_getmode(lua_State *L)2084 {2085 assert(!VBAMovieLoading());2086 if (!VBAMovieActive())2087 {2088 lua_pushnil(L);2089 return 1;2090 }2092 if (VBAMovieRecording())2093 lua_pushstring(L, "record");2094 else2095 lua_pushstring(L, "playback");2096 return 1;2097 }2099 static int movie_rerecordcount(lua_State *L)2100 {2101 if (VBAMovieActive())2102 lua_pushinteger(L, VBAMovieGetRerecordCount());2103 else2104 lua_pushinteger(L, 0);2105 return 1;2106 }2108 static int movie_setrerecordcount(lua_State *L)2109 {2110 if (VBAMovieActive())2111 VBAMovieSetRerecordCount(luaL_checkinteger(L, 1));2112 return 0;2113 }2115 static int movie_rerecordcounting(lua_State *L)2116 {2117 if (lua_gettop(L) == 0)2118 luaL_error(L, "no parameters specified");2120 skipRerecords = lua_toboolean(L, 1);2121 return 0;2122 }2124 // movie.stop()2125 //2127 // Stops movie playback/recording. Bombs out if movie is not running.2128 static int movie_stop(lua_State *L)2129 {2130 if (!VBAMovieActive())2131 luaL_error(L, "no movie");2133 VBAMovieStop(false);2134 return 0;2135 }2137 #define LUA_SCREEN_WIDTH 2562138 #define LUA_SCREEN_HEIGHT 2392140 // Common code by the gui library: make sure the screen array is ready2141 static void gui_prepare(void)2142 {2143 if (!gui_data)2144 gui_data = (uint8 *)malloc(LUA_SCREEN_WIDTH * LUA_SCREEN_HEIGHT * 4);2145 if (!gui_used)2146 memset(gui_data, 0, LUA_SCREEN_WIDTH * LUA_SCREEN_HEIGHT * 4);2147 gui_used = true;2148 }2150 // pixform for lua graphics2151 #define BUILD_PIXEL_ARGB8888(A, R, G, B) (((int)(A) << 24) | ((int)(R) << 16) | ((int)(G) << 8) | (int)(B))2152 #define DECOMPOSE_PIXEL_ARGB8888(PIX, A, R, G, B) \2153 { \2154 (A) = ((PIX) >> 24) & 0xff; \2155 (R) = ((PIX) >> 16) & 0xff; \2156 (G) = ((PIX) >> 8) & 0xff; \2157 (B) = (PIX) & 0xff; \2158 }2159 #define LUA_BUILD_PIXEL BUILD_PIXEL_ARGB88882160 #define LUA_DECOMPOSE_PIXEL DECOMPOSE_PIXEL_ARGB88882161 #define LUA_PIXEL_A(PIX) (((PIX) >> 24) & 0xff)2162 #define LUA_PIXEL_R(PIX) (((PIX) >> 16) & 0xff)2163 #define LUA_PIXEL_G(PIX) (((PIX) >> 8) & 0xff)2164 #define LUA_PIXEL_B(PIX) ((PIX) & 0xff)2166 template<class T>2167 static void swap(T &one, T &two)2168 {2169 T temp = one;2170 one = two;2171 two = temp;2172 }2174 // write a pixel to buffer2175 static inline void blend32(uint32 *dstPixel, uint32 colour)2176 {2177 uint8 *dst = (uint8 *)dstPixel;2178 int a, r, g, b;2179 LUA_DECOMPOSE_PIXEL(colour, a, r, g, b);2181 if (a == 255 || dst[3] == 0)2182 {2183 // direct copy2184 *(uint32 *) (dst) = colour;2185 }2186 else if (a == 0)2187 {2188 // do not copy2189 }2190 else2191 {2192 // alpha-blending2193 int a_dst = ((255 - a) * dst[3] + 128) / 255;2194 int a_new = a + a_dst;2196 dst[0] = (uint8) (((dst[0] * a_dst + b * a) + (a_new / 2)) / a_new);2197 dst[1] = (uint8) (((dst[1] * a_dst + g * a) + (a_new / 2)) / a_new);2198 dst[2] = (uint8) (((dst[2] * a_dst + r * a) + (a_new / 2)) / a_new);2199 dst[3] = (uint8) a_new;2200 }2201 }2203 // check if a pixel is in the lua canvas2204 static inline bool gui_check_boundary(int x, int y)2205 {2206 return !(x < 0 || x >= LUA_SCREEN_WIDTH || y < 0 || y >= LUA_SCREEN_HEIGHT);2207 }2209 // check if any part of a box is in the lua canvas2210 static inline bool gui_checkbox(int x1, int y1, int x2, int y2)2211 {2212 if ((x1 < 0 && x2 < 0)2213 || (x1 >= LUA_SCREEN_WIDTH && x2 >= LUA_SCREEN_WIDTH)2214 || (y1 < 0 && y2 < 0)2215 || (y1 >= LUA_SCREEN_HEIGHT && y2 >= LUA_SCREEN_HEIGHT))2216 return false;2217 return true;2218 }2220 // write a pixel to gui_data (do not check boundaries for speedup)2221 static inline void gui_drawpixel_fast(int x, int y, uint32 colour)2222 {2223 //gui_prepare();2224 blend32((uint32 *) &gui_data[(y * LUA_SCREEN_WIDTH + x) * 4], colour);2225 }2227 // write a pixel to gui_data (check boundaries)2228 static inline void gui_drawpixel_internal(int x, int y, uint32 colour)2229 {2230 //gui_prepare();2231 if (gui_check_boundary(x, y))2232 gui_drawpixel_fast(x, y, colour);2233 }2235 // draw a line on gui_data (checks boundaries)2236 static void gui_drawline_internal(int x1, int y1, int x2, int y2, bool lastPixel, uint32 colour)2237 {2238 //gui_prepare();2239 // Note: New version of Bresenham's Line Algorithm2240 //2241 //2242 // http://groups.google.co.jp/group/rec.games.roguelike.development/browse_thread/thread/345f4c42c3b25858/29e07a3af3a450e6?show_docid=29e07a3af3a450e62243 int swappedx = 0;2244 int swappedy = 0;2246 int xtemp = x1 - x2;2247 int ytemp = y1 - y2;2248 if (xtemp == 0 && ytemp == 0)2249 {2250 gui_drawpixel_internal(x1, y1, colour);2251 return;2252 }2254 if (xtemp < 0)2255 {2256 xtemp = -xtemp;2257 swappedx = 1;2258 }2260 if (ytemp < 0)2261 {2262 ytemp = -ytemp;2263 swappedy = 1;2264 }2266 int delta_x = xtemp << 1;2267 int delta_y = ytemp << 1;2269 signed char ix = x1 > x2 ? 1 : -1;2270 signed char iy = y1 > y2 ? 1 : -1;2272 if (lastPixel)2273 gui_drawpixel_internal(x2, y2, colour);2275 if (delta_x >= delta_y)2276 {2277 int error = delta_y - (delta_x >> 1);2279 while (x2 != x1)2280 {2281 if (error == 0 && !swappedx)2282 gui_drawpixel_internal(x2 + ix, y2, colour);2283 if (error >= 0)2284 {2285 if (error || (ix > 0))2286 {2287 y2 += iy;2288 error -= delta_x;2289 }2290 }2292 x2 += ix;2293 gui_drawpixel_internal(x2, y2, colour);2294 if (error == 0 && swappedx)2295 gui_drawpixel_internal(x2, y2 + iy, colour);2296 error += delta_y;2297 }2298 }2299 else2300 {2301 int error = delta_x - (delta_y >> 1);2303 while (y2 != y1)2304 {2305 if (error == 0 && !swappedy)2306 gui_drawpixel_internal(x2, y2 + iy, colour);2307 if (error >= 0)2308 {2309 if (error || (iy > 0))2310 {2311 x2 += ix;2312 error -= delta_y;2313 }2314 }2316 y2 += iy;2317 gui_drawpixel_internal(x2, y2, colour);2318 if (error == 0 && swappedy)2319 gui_drawpixel_internal(x2 + ix, y2, colour);2320 error += delta_x;2321 }2322 }2323 }2325 // draw a rect on gui_data2326 static void gui_drawbox_internal(int x1, int y1, int x2, int y2, uint32 colour)2327 {2328 if (x1 > x2)2329 std::swap(x1, x2);2330 if (y1 > y2)2331 std::swap(y1, y2);2332 if (x1 < 0)2333 x1 = -1;2334 if (y1 < 0)2335 y1 = -1;2336 if (x2 >= LUA_SCREEN_WIDTH)2337 x2 = LUA_SCREEN_WIDTH;2338 if (y2 >= LUA_SCREEN_HEIGHT)2339 y2 = LUA_SCREEN_HEIGHT;2341 if (!gui_checkbox(x1, y1, x2, y2))2342 return;2344 //gui_prepare();2345 gui_drawline_internal(x1, y1, x2, y1, true, colour);2346 gui_drawline_internal(x1, y2, x2, y2, true, colour);2347 gui_drawline_internal(x1, y1, x1, y2, true, colour);2348 gui_drawline_internal(x2, y1, x2, y2, true, colour);2349 }2351 // draw a circle on gui_data2352 static void gui_drawcircle_internal(int x0, int y0, int radius, uint32 colour)2353 {2354 //gui_prepare();2355 if (radius < 0)2356 radius = -radius;2357 if (radius == 0)2358 return;2359 if (radius == 1)2360 {2361 gui_drawpixel_internal(x0, y0, colour);2362 return;2363 }2365 // http://en.wikipedia.org/wiki/Midpoint_circle_algorithm2366 int f = 1 - radius;2367 int ddF_x = 1;2368 int ddF_y = -2 * radius;2369 int x = 0;2370 int y = radius;2372 if (!gui_checkbox(x0 - radius, y0 - radius, x0 + radius, y0 + radius))2373 return;2375 gui_drawpixel_internal(x0, y0 + radius, colour);2376 gui_drawpixel_internal(x0, y0 - radius, colour);2377 gui_drawpixel_internal(x0 + radius, y0, colour);2378 gui_drawpixel_internal(x0 - radius, y0, colour);2380 // same pixel shouldn't be drawed twice,2381 // because each pixel has opacity.2382 // so now the routine gets ugly.2383 while (true)2384 {2385 assert(ddF_x == 2 * x + 1);2386 assert(ddF_y == -2 * y);2387 assert(f == x * x + y * y - radius * radius + 2 * x - y + 1);2388 if (f >= 0)2389 {2390 y--;2391 ddF_y += 2;2392 f += ddF_y;2393 }2395 x++;2396 ddF_x += 2;2397 f += ddF_x;2398 if (x < y)2399 {2400 gui_drawpixel_internal(x0 + x, y0 + y, colour);2401 gui_drawpixel_internal(x0 - x, y0 + y, colour);2402 gui_drawpixel_internal(x0 + x, y0 - y, colour);2403 gui_drawpixel_internal(x0 - x, y0 - y, colour);2404 gui_drawpixel_internal(x0 + y, y0 + x, colour);2405 gui_drawpixel_internal(x0 - y, y0 + x, colour);2406 gui_drawpixel_internal(x0 + y, y0 - x, colour);2407 gui_drawpixel_internal(x0 - y, y0 - x, colour);2408 }2409 else if (x == y)2410 {2411 gui_drawpixel_internal(x0 + x, y0 + y, colour);2412 gui_drawpixel_internal(x0 - x, y0 + y, colour);2413 gui_drawpixel_internal(x0 + x, y0 - y, colour);2414 gui_drawpixel_internal(x0 - x, y0 - y, colour);2415 break;2416 }2417 else2418 break;2419 }2420 }2422 // draw fill rect on gui_data2423 static void gui_fillbox_internal(int x1, int y1, int x2, int y2, uint32 colour)2424 {2425 if (x1 > x2)2426 std::swap(x1, x2);2427 if (y1 > y2)2428 std::swap(y1, y2);2429 if (x1 < 0)2430 x1 = 0;2431 if (y1 < 0)2432 y1 = 0;2433 if (x2 >= LUA_SCREEN_WIDTH)2434 x2 = LUA_SCREEN_WIDTH - 1;2435 if (y2 >= LUA_SCREEN_HEIGHT)2436 y2 = LUA_SCREEN_HEIGHT - 1;2438 //gui_prepare();2439 int ix, iy;2440 for (iy = y1; iy <= y2; iy++)2441 {2442 for (ix = x1; ix <= x2; ix++)2443 {2444 gui_drawpixel_fast(ix, iy, colour);2445 }2446 }2447 }2449 // fill a circle on gui_data2450 static void gui_fillcircle_internal(int x0, int y0, int radius, uint32 colour)2451 {2452 //gui_prepare();2453 if (radius < 0)2454 radius = -radius;2455 if (radius == 0)2456 return;2457 if (radius == 1)2458 {2459 gui_drawpixel_internal(x0, y0, colour);2460 return;2461 }2463 // http://en.wikipedia.org/wiki/Midpoint_circle_algorithm2464 int f = 1 - radius;2465 int ddF_x = 1;2466 int ddF_y = -2 * radius;2467 int x = 0;2468 int y = radius;2470 if (!gui_checkbox(x0 - radius, y0 - radius, x0 + radius, y0 + radius))2471 return;2473 gui_drawline_internal(x0, y0 - radius, x0, y0 + radius, true, colour);2475 while (true)2476 {2477 assert(ddF_x == 2 * x + 1);2478 assert(ddF_y == -2 * y);2479 assert(f == x * x + y * y - radius * radius + 2 * x - y + 1);2480 if (f >= 0)2481 {2482 y--;2483 ddF_y += 2;2484 f += ddF_y;2485 }2487 x++;2488 ddF_x += 2;2489 f += ddF_x;2491 if (x < y)2492 {2493 gui_drawline_internal(x0 + x, y0 - y, x0 + x, y0 + y, true, colour);2494 gui_drawline_internal(x0 - x, y0 - y, x0 - x, y0 + y, true, colour);2495 if (f >= 0)2496 {2497 gui_drawline_internal(x0 + y, y0 - x, x0 + y, y0 + x, true, colour);2498 gui_drawline_internal(x0 - y, y0 - x, x0 - y, y0 + x, true, colour);2499 }2500 }2501 else if (x == y)2502 {2503 gui_drawline_internal(x0 + x, y0 - y, x0 + x, y0 + y, true, colour);2504 gui_drawline_internal(x0 - x, y0 - y, x0 - x, y0 + y, true, colour);2505 break;2506 }2507 else2508 break;2509 }2510 }2512 // Helper for a simple hex parser2513 static int hex2int(lua_State *L, char c)2514 {2515 if (c >= '0' && c <= '9')2516 return c - '0';2517 if (c >= 'a' && c <= 'f')2518 return c - 'a' + 10;2519 if (c >= 'A' && c <= 'F')2520 return c - 'A' + 10;2521 return luaL_error(L, "invalid hex in colour");2522 }2524 static const struct ColorMapping2525 {2526 const char *name;2527 int value;2528 }2529 s_colorMapping[] =2530 {2531 { "white", 0xFFFFFFFF },2532 { "black", 0x000000FF },2533 { "clear", 0x00000000 },2534 { "gray", 0x7F7F7FFF },2535 { "grey", 0x7F7F7FFF },2536 { "red", 0xFF0000FF },2537 { "orange", 0xFF7F00FF },2538 { "yellow", 0xFFFF00FF },2539 { "chartreuse", 0x7FFF00FF },2540 { "green", 0x00FF00FF },2541 { "teal", 0x00FF7FFF },2542 { "cyan", 0x00FFFFFF },2543 { "blue", 0x0000FFFF },2544 { "purple", 0x7F00FFFF },2545 { "magenta", 0xFF00FFFF },2546 };2548 /**2549 * Converts an integer or a string on the stack at the given2550 * offset to a RGB32 colour. Several encodings are supported.2551 * The user may construct their own RGB value, given a simple colour name,2552 * or an HTML-style "#09abcd" colour. 16 bit reduction doesn't occur at this time.2553 */2554 static inline bool str2colour(uint32 *colour, lua_State *L, const char *str)2555 {2556 if (str[0] == '#')2557 {2558 int color;2559 sscanf(str + 1, "%X", &color);2561 int len = strlen(str + 1);2562 int missing = max(0, 8 - len);2563 color <<= missing << 2;2564 if (missing >= 2)2565 color |= 0xFF;2566 *colour = color;2567 return true;2568 }2569 else2570 {2571 if (!strnicmp(str, "rand", 4))2572 {2573 *colour = gen_rand32() | 0xFF; //((rand()*255/RAND_MAX) << 8) | ((rand()*255/RAND_MAX) << 16) |2574 // ((rand()*255/RAND_MAX) << 24) | 0xFF;2575 return true;2576 }2578 for (int i = 0; i < sizeof(s_colorMapping) / sizeof(*s_colorMapping); i++)2579 {2580 if (!stricmp(str, s_colorMapping[i].name))2581 {2582 *colour = s_colorMapping[i].value;2583 return true;2584 }2585 }2586 }2588 return false;2589 }2591 static inline uint32 gui_getcolour_wrapped(lua_State *L, int offset, bool hasDefaultValue, uint32 defaultColour)2592 {2593 switch (lua_type(L, offset))2594 {2595 case LUA_TSTRING:2596 {2597 const char *str = lua_tostring(L, offset);2598 uint32 colour;2600 if (str2colour(&colour, L, str))2601 return colour;2602 else2603 {2604 if (hasDefaultValue)2605 return defaultColour;2606 else2607 return luaL_error(L, "unknown colour %s", str);2608 }2609 }2611 case LUA_TNUMBER:2612 {2613 uint32 colour = (uint32) lua_tointeger(L, offset);2614 return colour;2615 }2617 case LUA_TTABLE:2618 {2619 int color = 0xFF;2620 lua_pushnil(L); // first key2621 int keyIndex = lua_gettop(L);2622 int valueIndex = keyIndex + 1;2623 bool first = true;2624 while (lua_next(L, offset))2625 {2626 bool keyIsString = (lua_type(L, keyIndex) == LUA_TSTRING);2627 bool keyIsNumber = (lua_type(L, keyIndex) == LUA_TNUMBER);2628 int key = keyIsString ? tolower(*lua_tostring(L, keyIndex)) : (keyIsNumber ? lua_tointeger(L, keyIndex) : 0);2629 int value = lua_tointeger(L, valueIndex);2630 if (value < 0) value = 0;2631 if (value > 255) value = 255;2632 switch (key)2633 {2634 case 1:2635 case 'r':2636 color |= value << 24; break;2637 case 2:2638 case 'g':2639 color |= value << 16; break;2640 case 3:2641 case 'b':2642 color |= value << 8; break;2643 case 4:2644 case 'a':2645 color = (color & ~0xFF) | value; break;2646 }2647 lua_pop(L, 1);2648 }2649 return color;2650 } break;2652 case LUA_TFUNCTION:2653 luaL_error(L, "invalid colour"); // NYI2654 return 0;2656 default:2657 if (hasDefaultValue)2658 return defaultColour;2659 else2660 return luaL_error(L, "invalid colour");2661 }2662 }2664 static uint32 gui_getcolour(lua_State *L, int offset)2665 {2666 uint32 colour;2667 int a, r, g, b;2669 colour = gui_getcolour_wrapped(L, offset, false, 0);2670 a = ((colour & 0xff) * transparencyModifier) / 255;2671 if (a > 255)2672 a = 255;2673 b = (colour >> 8) & 0xff;2674 g = (colour >> 16) & 0xff;2675 r = (colour >> 24) & 0xff;2676 return LUA_BUILD_PIXEL(a, r, g, b);2677 }2679 static uint32 gui_optcolour(lua_State *L, int offset, uint32 defaultColour)2680 {2681 uint32 colour;2682 int a, r, g, b;2683 uint8 defA, defB, defG, defR;2685 LUA_DECOMPOSE_PIXEL(defaultColour, defA, defR, defG, defB);2686 defaultColour = (defR << 24) | (defG << 16) | (defB << 8) | defA;2688 colour = gui_getcolour_wrapped(L, offset, true, defaultColour);2689 a = ((colour & 0xff) * transparencyModifier) / 255;2690 if (a > 255)2691 a = 255;2692 b = (colour >> 8) & 0xff;2693 g = (colour >> 16) & 0xff;2694 r = (colour >> 24) & 0xff;2695 return LUA_BUILD_PIXEL(a, r, g, b);2696 }2698 // gui.drawpixel(x,y,colour)2699 static int gui_drawpixel(lua_State *L)2700 {2701 int x = luaL_checkinteger(L, 1);2702 int y = luaL_checkinteger(L, 2);2704 uint32 colour = gui_getcolour(L, 3);2706 // if (!gui_check_boundary(x, y))2707 // luaL_error(L,"bad coordinates");2708 gui_prepare();2710 gui_drawpixel_internal(x, y, colour);2712 return 0;2713 }2715 // gui.drawline(x1,y1,x2,y2,color,skipFirst)2716 static int gui_drawline(lua_State *L)2717 {2718 int x1, y1, x2, y2;2719 uint32 color;2720 x1 = luaL_checkinteger(L, 1);2721 y1 = luaL_checkinteger(L, 2);2722 x2 = luaL_checkinteger(L, 3);2723 y2 = luaL_checkinteger(L, 4);2724 color = gui_optcolour(L, 5, LUA_BUILD_PIXEL(255, 255, 255, 255));2725 int skipFirst = lua_toboolean(L, 6);2727 gui_prepare();2729 gui_drawline_internal(x2, y2, x1, y1, !skipFirst, color);2731 return 0;2732 }2734 // gui.drawbox(x1, y1, x2, y2, fillcolor, outlinecolor)2735 static int gui_drawbox(lua_State *L)2736 {2737 int x1, y1, x2, y2;2738 uint32 fillcolor;2739 uint32 outlinecolor;2741 x1 = luaL_checkinteger(L, 1);2742 y1 = luaL_checkinteger(L, 2);2743 x2 = luaL_checkinteger(L, 3);2744 y2 = luaL_checkinteger(L, 4);2745 fillcolor = gui_optcolour(L, 5, LUA_BUILD_PIXEL(63, 255, 255, 255));2746 outlinecolor = gui_optcolour(L, 6, LUA_BUILD_PIXEL(255, LUA_PIXEL_R(fillcolor), LUA_PIXEL_G(fillcolor), LUA_PIXEL_B(fillcolor)));2748 if (x1 > x2)2749 std::swap(x1, x2);2750 if (y1 > y2)2751 std::swap(y1, y2);2753 gui_prepare();2755 gui_drawbox_internal(x1, y1, x2, y2, outlinecolor);2756 if ((x2 - x1) >= 2 && (y2 - y1) >= 2)2757 gui_fillbox_internal(x1 + 1, y1 + 1, x2 - 1, y2 - 1, fillcolor);2759 return 0;2760 }2762 // gui.drawcircle(x0, y0, radius, colour)2763 static int gui_drawcircle(lua_State *L)2764 {2765 int x, y, r;2766 uint32 colour;2768 x = luaL_checkinteger(L, 1);2769 y = luaL_checkinteger(L, 2);2770 r = luaL_checkinteger(L, 3);2771 colour = gui_getcolour(L, 4);2773 gui_prepare();2775 gui_drawcircle_internal(x, y, r, colour);2777 return 0;2778 }2780 // gui.fillbox(x1, y1, x2, y2, colour)2781 static int gui_fillbox(lua_State *L)2782 {2783 int x1, y1, x2, y2;2784 uint32 colour;2786 x1 = luaL_checkinteger(L, 1);2787 y1 = luaL_checkinteger(L, 2);2788 x2 = luaL_checkinteger(L, 3);2789 y2 = luaL_checkinteger(L, 4);2790 colour = gui_getcolour(L, 5);2792 // if (!gui_check_boundary(x1, y1))2793 // luaL_error(L,"bad coordinates");2794 //2795 // if (!gui_check_boundary(x2, y2))2796 // luaL_error(L,"bad coordinates");2797 gui_prepare();2799 if (!gui_checkbox(x1, y1, x2, y2))2800 return 0;2802 gui_fillbox_internal(x1, y1, x2, y2, colour);2804 return 0;2805 }2807 // gui.fillcircle(x0, y0, radius, colour)2808 static int gui_fillcircle(lua_State *L)2809 {2810 int x, y, r;2811 uint32 colour;2813 x = luaL_checkinteger(L, 1);2814 y = luaL_checkinteger(L, 2);2815 r = luaL_checkinteger(L, 3);2816 colour = gui_getcolour(L, 4);2818 gui_prepare();2820 gui_fillcircle_internal(x, y, r, colour);2822 return 0;2823 }2825 static int gui_getpixel(lua_State *L)2826 {2827 int x = luaL_checkinteger(L, 1);2828 int y = luaL_checkinteger(L, 2);2830 int pixWidth = 240, pixHeight = 160;2831 int scrWidth = 240, scrHeight = 160;2832 int scrOffsetX = 0, scrOffsetY = 0;2833 int pitch;2834 if (!systemIsRunningGBA())2835 {2836 if (gbBorderOn)2837 {2838 pixWidth = 256, pixHeight = 224;2839 scrOffsetX = 48, scrOffsetY = 40;2840 }2841 else2842 {2843 pixWidth = 160, pixHeight = 144;2844 }2845 scrWidth = 160, scrHeight = 144;2846 }2847 pitch = pixWidth * (systemColorDepth / 8) + (systemColorDepth == 24 ? 0 : 4);2848 scrOffsetY++; // don't know why it's needed2850 if (!(x >= 0 && y >= 0 && x < scrWidth && y < scrHeight) /*!gui_check_boundary(x,y)*/)2851 {2852 lua_pushinteger(L, 0);2853 lua_pushinteger(L, 0);2854 lua_pushinteger(L, 0);2855 }2856 else2857 {2858 switch (systemColorDepth)2859 {2860 case 16:2861 {2862 uint16 *screen = (uint16 *) (&pix[scrOffsetY * pitch + scrOffsetX * 2]);2863 uint16 pixColor = screen[y * pitch / 2 + x];2864 lua_pushinteger(L, (pixColor >> 8) & 0xF8); // red2865 lua_pushinteger(L, (pixColor >> 3) & 0xFC); // green2866 lua_pushinteger(L, (pixColor << 3) & 0xF8); // blue2867 }2868 break;2869 case 24:2870 {2871 uint8 *screen = &pix[scrOffsetY * pitch + scrOffsetX * 3];2872 lua_pushinteger(L, screen[y * pitch + x * 3 + 2]); // red2873 lua_pushinteger(L, screen[y * pitch + x * 3 + 1]); // green2874 lua_pushinteger(L, screen[y * pitch + x * 3 + 0]); // blue2875 }2876 break;2877 case 32:2878 {2879 uint8 *screen = &pix[scrOffsetY * pitch + scrOffsetX * 4];2880 lua_pushinteger(L, screen[y * pitch + x * 4 + 2]); // red2881 lua_pushinteger(L, screen[y * pitch + x * 4 + 1]); // green2882 lua_pushinteger(L, screen[y * pitch + x * 4 + 0]); // blue2883 }2884 break;2885 default:2886 lua_pushinteger(L, 0);2887 lua_pushinteger(L, 0);2888 lua_pushinteger(L, 0);2889 break;2890 }2891 }2892 return 3;2893 }2895 static int gui_parsecolor(lua_State *L)2896 {2897 int r, g, b, a;2898 uint32 color = gui_getcolour(L, 1);2899 LUA_DECOMPOSE_PIXEL(color, a, r, g, b);2900 lua_pushinteger(L, r);2901 lua_pushinteger(L, g);2902 lua_pushinteger(L, b);2903 lua_pushinteger(L, a);2904 return 4;2905 }2907 // gui.gdscreenshot()2908 //2909 // Returns a screen shot as a string in gd's v1 file format.2910 // This allows us to make screen shots available without gd installed locally.2911 // Users can also just grab pixels via substring selection.2912 //2913 // I think... Does lua support grabbing byte values from a string? // yes, string.byte(str,offset)2914 // Well, either way, just install gd and do what you like with it.2915 // It really is easier that way.2917 // example: gd.createFromGdStr(gui.gdscreenshot()):png("outputimage.png")2918 static int gui_gdscreenshot(lua_State *L)2919 {2920 int xofs = 0, yofs = 0, ppl = 240, width = 240, height = 160;2921 if (!systemIsRunningGBA())2922 {2923 if (gbBorderOn)2924 xofs = 48, yofs = 40, ppl = 256;2925 else2926 ppl = 160;2927 width = 160, height = 144;2928 }2930 yofs++;2932 //int pitch = (((ppl * systemColorDepth + 7)>>3)+3)&~3;2933 int pitch = ppl * (systemColorDepth / 8) + (systemColorDepth == 24 ? 0 : 4);2934 uint8 *screen = &pix[yofs * pitch + xofs * (systemColorDepth / 8)];2936 int size = 11 + width * height * 4;2937 char *str = new char[size + 1];2938 str[size] = 0;2940 unsigned char *ptr = (unsigned char *)str;2942 // GD format header for truecolor image (11 bytes)2943 *ptr++ = (65534 >> 8) & 0xFF;2944 *ptr++ = (65534) & 0xFF;2945 *ptr++ = (width >> 8) & 0xFF;2946 *ptr++ = (width) & 0xFF;2947 *ptr++ = (height >> 8) & 0xFF;2948 *ptr++ = (height) & 0xFF;2949 *ptr++ = 1;2950 *ptr++ = 255;2951 *ptr++ = 255;2952 *ptr++ = 255;2953 *ptr++ = 255;2955 GetColorFunc getColor;2956 getColorIOFunc(systemColorDepth, &getColor, NULL);2958 int x, y;2959 for (y = 0; y < height; y++)2960 {2961 uint8 *s = &screen[y * pitch];2962 for (x = 0; x < width; x++, s += systemColorDepth / 8)2963 {2964 uint8 r, g, b;2965 getColor(s, &r, &g, &b);2967 *ptr++ = 0;2968 *ptr++ = r;2969 *ptr++ = g;2970 *ptr++ = b;2971 }2972 }2974 lua_pushlstring(L, str, size);2975 delete[] str;2976 return 1;2977 }2979 // gui.opacity(number alphaValue)2980 // sets the transparency of subsequent draw calls2981 // 0.0 is completely transparent, 1.0 is completely opaque2982 // non-integer values are supported and meaningful, as are values greater than 1.02983 // it is not necessary to use this function to get transparency (or the less-recommended gui.transparency() either),2984 // because you can provide an alpha value in the color argument of each draw call.2986 // however, it can be convenient to be able to globally modify the drawing transparency2987 static int gui_setopacity(lua_State *L)2988 {2989 double opacF = luaL_checknumber(L, 1);2990 transparencyModifier = (int)(opacF * 255);2991 if (transparencyModifier < 0)2992 transparencyModifier = 0;2993 return 0;2994 }2996 // gui.transparency(int strength)2997 //2999 // 0 = solid,3000 static int gui_transparency(lua_State *L)3001 {3002 double trans = luaL_checknumber(L, 1);3003 transparencyModifier = (int)((4.0 - trans) / 4.0 * 255);3004 if (transparencyModifier < 0)3005 transparencyModifier = 0;3006 return 0;3007 }3009 static const uint32 Small_Font_Data[] =3010 {3011 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 323012 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000000, 0x00000700, 0x00000000, // 33 !3013 0x00000000, 0x00040002, 0x00050003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 34 "3014 0x00000000, 0x00040002, 0x00050403, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 35 #3015 0x00000000, 0x00040300, 0x00000403, 0x00000500, 0x00070600, 0x00000706, 0x00000000, // 36 $3016 0x00000000, 0x00000002, 0x00050000, 0x00000500, 0x00000005, 0x00080000, 0x00000000, // 37 %3017 0x00000000, 0x00000300, 0x00050003, 0x00000500, 0x00070005, 0x00080700, 0x00000000, // 38 &3018 0x00000000, 0x00000300, 0x00000400, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 39 '3019 0x00000000, 0x00000300, 0x00000003, 0x00000004, 0x00000005, 0x00000700, 0x00000000, // 40 (3020 0x00000000, 0x00000300, 0x00050000, 0x00060000, 0x00070000, 0x00000700, 0x00000000, // 41 )3021 0x00000000, 0x00000000, 0x00000400, 0x00060504, 0x00000600, 0x00080006, 0x00000000, // 42 *3022 0x00000000, 0x00000000, 0x00000400, 0x00060504, 0x00000600, 0x00000000, 0x00000000, // 43 +3023 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000600, 0x00000700, 0x00000007, // 44 ,3024 0x00000000, 0x00000000, 0x00000000, 0x00060504, 0x00000000, 0x00000000, 0x00000000, // 45 -3025 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000700, 0x00000000, // 46 .3026 0x00030000, 0x00040000, 0x00000400, 0x00000500, 0x00000005, 0x00000006, 0x00000000, // 47 /3027 0x00000000, 0x00000300, 0x00050003, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 48 03028 0x00000000, 0x00000300, 0x00000403, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 49 13029 0x00000000, 0x00000302, 0x00050000, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 50 23030 0x00000000, 0x00000302, 0x00050000, 0x00000504, 0x00070000, 0x00000706, 0x00000000, // 51 33031 0x00000000, 0x00000300, 0x00000003, 0x00060004, 0x00070605, 0x00080000, 0x00000000, // 52 43032 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00070000, 0x00000706, 0x00000000, // 53 53033 0x00000000, 0x00000300, 0x00000003, 0x00000504, 0x00070005, 0x00000700, 0x00000000, // 54 63034 0x00000000, 0x00040302, 0x00050000, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 55 73035 0x00000000, 0x00000300, 0x00050003, 0x00000500, 0x00070005, 0x00000700, 0x00000000, // 56 83036 0x00000000, 0x00000300, 0x00050003, 0x00060500, 0x00070000, 0x00000700, 0x00000000, // 57 93037 0x00000000, 0x00000000, 0x00000400, 0x00000000, 0x00000000, 0x00000700, 0x00000000, // 58 :3038 0x00000000, 0x00000000, 0x00000000, 0x00000500, 0x00000000, 0x00000700, 0x00000007, // 59 ;3039 0x00000000, 0x00040000, 0x00000400, 0x00000004, 0x00000600, 0x00080000, 0x00000000, // 60 <3040 0x00000000, 0x00000000, 0x00050403, 0x00000000, 0x00070605, 0x00000000, 0x00000000, // 61 =3041 0x00000000, 0x00000002, 0x00000400, 0x00060000, 0x00000600, 0x00000006, 0x00000000, // 62 >3042 0x00000000, 0x00000302, 0x00050000, 0x00000500, 0x00000000, 0x00000700, 0x00000000, // 63 ?3043 0x00000000, 0x00000300, 0x00050400, 0x00060004, 0x00070600, 0x00000000, 0x00000000, // 64 @3044 0x00000000, 0x00000300, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 65 A3045 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00070005, 0x00000706, 0x00000000, // 66 B3046 0x00000000, 0x00040300, 0x00000003, 0x00000004, 0x00000005, 0x00080700, 0x00000000, // 67 C3047 0x00000000, 0x00000302, 0x00050003, 0x00060004, 0x00070005, 0x00000706, 0x00000000, // 68 D3048 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00000005, 0x00080706, 0x00000000, // 69 E3049 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 70 F3050 0x00000000, 0x00040300, 0x00000003, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 71 G3051 0x00000000, 0x00040002, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 72 H3052 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 73 I3053 0x00000000, 0x00040000, 0x00050000, 0x00060000, 0x00070005, 0x00000700, 0x00000000, // 74 J3054 0x00000000, 0x00040002, 0x00050003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 75 K3055 0x00000000, 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00080706, 0x00000000, // 76 l3056 0x00000000, 0x00040002, 0x00050403, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 77 M3057 0x00000000, 0x00000302, 0x00050003, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 78 N3058 0x00000000, 0x00040302, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00000000, // 79 O3059 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 80 P3060 0x00000000, 0x00040302, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00090000, // 81 Q3061 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 82 R3062 0x00000000, 0x00040300, 0x00000003, 0x00000500, 0x00070000, 0x00000706, 0x00000000, // 83 S3063 0x00000000, 0x00040302, 0x00000400, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 84 T3064 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00000000, // 85 U3065 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00000600, 0x00000700, 0x00000000, // 86 V3066 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 87 W3067 0x00000000, 0x00040002, 0x00050003, 0x00000500, 0x00070005, 0x00080006, 0x00000000, // 88 X3068 0x00000000, 0x00040002, 0x00050003, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 89 Y3069 0x00000000, 0x00040302, 0x00050000, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 90 Z3070 0x00000000, 0x00040300, 0x00000400, 0x00000500, 0x00000600, 0x00080700, 0x00000000, // 91 [3071 0x00000000, 0x00000002, 0x00000400, 0x00000500, 0x00070000, 0x00080000, 0x00000000, // 92 '\'3072 0x00000000, 0x00000302, 0x00000400, 0x00000500, 0x00000600, 0x00000706, 0x00000000, // 93 ]3073 0x00000000, 0x00000300, 0x00050003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 94 ^3074 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00080706, 0x00000000, // 95 _3075 0x00000000, 0x00000002, 0x00000400, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 96 `3076 0x00000000, 0x00000000, 0x00050400, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 97 a3077 0x00000000, 0x00000002, 0x00000003, 0x00000504, 0x00070005, 0x00000706, 0x00000000, // 98 b3078 0x00000000, 0x00000000, 0x00050400, 0x00000004, 0x00000005, 0x00080700, 0x00000000, // 99 c3079 0x00000000, 0x00040000, 0x00050000, 0x00060500, 0x00070005, 0x00080700, 0x00000000, // 100 d3080 0x00000000, 0x00000000, 0x00050400, 0x00060504, 0x00000005, 0x00080700, 0x00000000, // 101 e3081 0x00000000, 0x00040300, 0x00000003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 102 f3082 0x00000000, 0x00000000, 0x00050400, 0x00060004, 0x00070600, 0x00080000, 0x00000807, // 103 g3083 0x00000000, 0x00000002, 0x00000003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 104 h3084 0x00000000, 0x00000300, 0x00000000, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 105 i3085 0x00000000, 0x00000300, 0x00000000, 0x00000500, 0x00000600, 0x00000700, 0x00000007, // 106 j3086 0x00000000, 0x00000002, 0x00000003, 0x00060004, 0x00000605, 0x00080006, 0x00000000, // 107 k3087 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000600, 0x00080000, 0x00000000, // 108 l3088 0x00000000, 0x00000000, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 109 m3089 0x00000000, 0x00000000, 0x00000403, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 110 n3090 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 111 o3091 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00000605, 0x00000006, 0x00000007, // 112 p3092 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070600, 0x00080000, 0x00090000, // 113 q3093 0x00000000, 0x00000000, 0x00050003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 114 r3094 0x00000000, 0x00000000, 0x00050400, 0x00000004, 0x00070600, 0x00000706, 0x00000000, // 115 s3095 0x00000000, 0x00000300, 0x00050403, 0x00000500, 0x00000600, 0x00080000, 0x00000000, // 116 t3096 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 117 u3097 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 118 v3098 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 119 w3099 0x00000000, 0x00000000, 0x00050003, 0x00000500, 0x00070005, 0x00080006, 0x00000000, // 120 x3100 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00000600, 0x00000700, 0x00000007, // 121 y3101 0x00000000, 0x00000000, 0x00050403, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 122 z3102 0x00000000, 0x00040300, 0x00000400, 0x00000504, 0x00000600, 0x00080700, 0x00000000, // 123 {3103 0x00000000, 0x00000300, 0x00000400, 0x00000000, 0x00000600, 0x00000700, 0x00000000, // 124 |3104 0x00000000, 0x00000302, 0x00000400, 0x00060500, 0x00000600, 0x00000706, 0x00000000, // 125 }3105 0x00000000, 0x00000302, 0x00050000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 126 ~3106 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070605, 0x00000000, 0x00000000, // 127 3107 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,3108 };3110 static void PutTextInternal(const char *str, int len, short x, short y, int color, int backcolor)3111 {3112 int Opac = (color >> 24) & 0xFF;3113 int backOpac = (backcolor >> 24) & 0xFF;3114 int origX = x;3116 if (!Opac && !backOpac)3117 return;3119 while (*str && len && y < LUA_SCREEN_HEIGHT)3120 {3121 int c = *str++;3122 while (x > LUA_SCREEN_WIDTH && c != '\n')3123 {3124 c = *str;3125 if (c == '\0')3126 break;3127 str++;3128 }3130 if (c == '\n')3131 {3132 x = origX;3133 y += 8;3134 continue;3135 }3136 else if (c == '\t') // just in case3137 {3138 const int tabSpace = 8;3139 x += (tabSpace - (((x - origX) / 4) % tabSpace)) * 4;3140 continue;3141 }3143 if ((unsigned int)(c - 32) >= 96)3144 continue;3146 const unsigned char *Cur_Glyph = (const unsigned char *) &Small_Font_Data + (c - 32) * 7 * 4;3148 for (int y2 = 0; y2 < 8; y2++)3149 {3150 unsigned int glyphLine = *((unsigned int *)Cur_Glyph + y2);3151 for (int x2 = -1; x2 < 4; x2++)3152 {3153 int shift = x2 << 3;3154 int mask = 0xFF << shift;3155 int intensity = (glyphLine & mask) >> shift;3157 if (intensity && x2 >= 0 && y2 < 7)3158 {3159 //int xdraw = max(0,min(LUA_SCREEN_WIDTH - 1,x+x2));3160 //int ydraw = max(0,min(LUA_SCREEN_HEIGHT - 1,y+y2));3161 //gui_drawpixel_fast(xdraw, ydraw, color);3162 gui_drawpixel_internal(x + x2, y + y2, color);3163 }3164 else if (backOpac)3165 {3166 for (int y3 = max(0, y2 - 1); y3 <= min(6, y2 + 1); y3++)3167 {3168 unsigned int glyphLine = *((unsigned int *)Cur_Glyph + y3);3169 for (int x3 = max(0, x2 - 1); x3 <= min(3, x2 + 1); x3++)3170 {3171 int shift = x3 << 3;3172 int mask = 0xFF << shift;3173 intensity |= (glyphLine & mask) >> shift;3174 if (intensity)3175 goto draw_outline; // speedup?3176 }3177 }3179 draw_outline:3180 if (intensity)3181 {3182 //int xdraw = max(0,min(LUA_SCREEN_WIDTH - 1,x+x2));3183 //int ydraw = max(0,min(LUA_SCREEN_HEIGHT - 1,y+y2));3184 //gui_drawpixel_fast(xdraw, ydraw, backcolor);3185 gui_drawpixel_internal(x + x2, y + y2, backcolor);3186 }3187 }3188 }3189 }3191 x += 4;3192 len--;3193 }3194 }3196 static int strlinelen(const char *string)3197 {3198 const char *s = string;3199 while (*s && *s != '\n')3200 s++;3201 if (*s)3202 s++;3203 return s - string;3204 }3206 static void LuaDisplayString(const char *string, int y, int x, uint32 color, uint32 outlineColor)3207 {3208 if (!string)3209 return;3211 gui_prepare();3213 PutTextInternal(string, strlen(string), x, y, color, outlineColor);3215 /*3216 const char* ptr = string;3217 while(*ptr && y < LUA_SCREEN_HEIGHT)3218 {3219 int len = strlinelen(ptr);3220 int skip = 0;3221 if(len < 1) len = 1;3223 // break up the line if it's too long to display otherwise3224 if(len > 63)3225 {3226 len = 63;3227 const char* ptr2 = ptr + len-1;3228 for(int j = len-1; j; j--, ptr2--)3229 {3230 if(*ptr2 == ' ' || *ptr2 == '\t')3231 {3232 len = j;3233 skip = 1;3234 break;3235 }3236 }3237 }3239 int xl = 0;3240 int yl = 0;3241 int xh = (LUA_SCREEN_WIDTH - 1 - 1) - 4*len;3242 int yh = LUA_SCREEN_HEIGHT - 1;3243 int x2 = min(max(x,xl),xh);3244 int y2 = min(max(y,yl),yh);3246 PutTextInternal(ptr,len,x2,y2,color,outlineColor);3248 ptr += len + skip;3249 y += 8;3250 }3251 */3252 }3254 // gui.text(int x, int y, string msg)3255 //3256 // Displays the given text on the screen, using the same font and techniques as the3258 // main HUD.3259 static int gui_text(lua_State *L)3260 {3261 //extern int font_height;3262 const char *msg;3263 int x, y;3264 uint32 colour, borderColour;3266 x = luaL_checkinteger(L, 1);3267 y = luaL_checkinteger(L, 2);3268 //msg = luaL_checkstring(L, 3);3269 msg = toCString(L, 3);3271 // if (x < 0 || x >= LUA_SCREEN_WIDTH || y < 0 || y >= (LUA_SCREEN_HEIGHT - font_height))3272 // luaL_error(L,"bad coordinates");3273 colour = gui_optcolour(L, 4, LUA_BUILD_PIXEL(255, 255, 255, 255));3274 borderColour = gui_optcolour(L, 5, LUA_BUILD_PIXEL(255, 0, 0, 0));3276 gui_prepare();3278 LuaDisplayString(msg, y, x, colour, borderColour);3280 return 0;3281 }3283 // gui.gdoverlay([int dx=0, int dy=0,] string str [, sx=0, sy=0, sw, sh] [, float alphamul=1.0])3284 //3285 // Overlays the given image on the screen.3287 // example: gui.gdoverlay(gd.createFromPng("myimage.png"):gdStr())3288 static int gui_gdoverlay(lua_State *L)3289 {3290 int argCount = lua_gettop(L);3292 int xStartDst = 0;3293 int yStartDst = 0;3294 int xStartSrc = 0;3295 int yStartSrc = 0;3297 int index = 1;3298 if (lua_type(L, index) == LUA_TNUMBER)3299 {3300 xStartDst = lua_tointeger(L, index++);3301 if (lua_type(L, index) == LUA_TNUMBER)3302 yStartDst = lua_tointeger(L, index++);3303 }3305 luaL_checktype(L, index, LUA_TSTRING);3307 const unsigned char *ptr = (const unsigned char *)lua_tostring(L, index++);3309 if (ptr[0] != 255 || (ptr[1] != 254 && ptr[1] != 255))3310 luaL_error(L, "bad image data");3312 bool trueColor = (ptr[1] == 254);3313 ptr += 2;3315 int imgwidth = *ptr++ << 8;3316 imgwidth |= *ptr++;3318 int width = imgwidth;3319 int imgheight = *ptr++ << 8;3320 imgheight |= *ptr++;3322 int height = imgheight;3323 if ((!trueColor && *ptr) || (trueColor && !*ptr))3324 luaL_error(L, "bad image data");3325 ptr++;3327 int pitch = imgwidth * (trueColor ? 4 : 1);3329 if ((argCount - index + 1) >= 4)3330 {3331 xStartSrc = luaL_checkinteger(L, index++);3332 yStartSrc = luaL_checkinteger(L, index++);3333 width = luaL_checkinteger(L, index++);3334 height = luaL_checkinteger(L, index++);3335 }3337 int alphaMul = transparencyModifier;3338 if (lua_isnumber(L, index))3339 alphaMul = (int)(alphaMul * lua_tonumber(L, index++));3340 if (alphaMul <= 0)3341 return 0;3343 // since there aren't that many possible opacity levels,3344 // do the opacity modification calculations beforehand instead of per pixel3345 int opacMap[256];3346 for (int i = 0; i < 128; i++)3347 {3348 int opac = 255 - ((i << 1) | (i & 1)); // gdAlphaMax = 127, not 2553349 opac = (opac * alphaMul) / 255;3350 if (opac < 0)3351 opac = 0;3352 if (opac > 255)3353 opac = 255;3354 opacMap[i] = opac;3355 }3357 for (int i = 128; i < 256; i++)3358 opacMap[i] = 0; // what should we do for them, actually?3359 int colorsTotal = 0;3360 if (!trueColor)3361 {3362 colorsTotal = *ptr++ << 8;3363 colorsTotal |= *ptr++;3364 }3366 int transparent = *ptr++ << 24;3367 transparent |= *ptr++ << 16;3368 transparent |= *ptr++ << 8;3369 transparent |= *ptr++;3370 struct3371 {3372 uint8 r, g, b, a;3373 } pal[256];3374 if (!trueColor)3375 for (int i = 0; i < 256; i++)3376 {3377 pal[i].r = *ptr++;3378 pal[i].g = *ptr++;3379 pal[i].b = *ptr++;3380 pal[i].a = opacMap[*ptr++];3381 }3383 // some of clippings3384 if (xStartSrc < 0)3385 {3386 width += xStartSrc;3387 xStartDst -= xStartSrc;3388 xStartSrc = 0;3389 }3391 if (yStartSrc < 0)3392 {3393 height += yStartSrc;3394 yStartDst -= yStartSrc;3395 yStartSrc = 0;3396 }3398 if (xStartSrc + width >= imgwidth)3399 width = imgwidth - xStartSrc;3400 if (yStartSrc + height >= imgheight)3401 height = imgheight - yStartSrc;3402 if (xStartDst < 0)3403 {3404 width += xStartDst;3405 if (width <= 0)3406 return 0;3407 xStartSrc = -xStartDst;3408 xStartDst = 0;3409 }3411 if (yStartDst < 0)3412 {3413 height += yStartDst;3414 if (height <= 0)3415 return 0;3416 yStartSrc = -yStartDst;3417 yStartDst = 0;3418 }3420 if (xStartDst + width >= LUA_SCREEN_WIDTH)3421 width = LUA_SCREEN_WIDTH - xStartDst;3422 if (yStartDst + height >= LUA_SCREEN_HEIGHT)3423 height = LUA_SCREEN_HEIGHT - yStartDst;3424 if (width <= 0 || height <= 0)3425 return 0; // out of screen or invalid size3426 gui_prepare();3428 const uint8 *pix = (const uint8 *)(&ptr[yStartSrc * pitch + (xStartSrc * (trueColor ? 4 : 1))]);3429 int bytesToNextLine = pitch - (width * (trueColor ? 4 : 1));3430 if (trueColor)3431 {3432 for (int y = yStartDst; y < height + yStartDst && y < LUA_SCREEN_HEIGHT; y++, pix += bytesToNextLine)3433 {3434 for (int x = xStartDst; x < width + xStartDst && x < LUA_SCREEN_WIDTH; x++, pix += 4)3435 {3436 gui_drawpixel_fast(x, y, LUA_BUILD_PIXEL(opacMap[pix[0]], pix[1], pix[2], pix[3]));3437 }3438 }3439 }3440 else3441 {3442 for (int y = yStartDst; y < height + yStartDst && y < LUA_SCREEN_HEIGHT; y++, pix += bytesToNextLine)3443 {3444 for (int x = xStartDst; x < width + xStartDst && x < LUA_SCREEN_WIDTH; x++, pix++)3445 {3446 gui_drawpixel_fast(x, y, LUA_BUILD_PIXEL(pal[*pix].a, pal[*pix].r, pal[*pix].g, pal[*pix].b));3447 }3448 }3449 }3451 return 0;3452 }3454 // function gui.register(function f)3455 //3456 // This function will be called just before a graphical update.3457 // More complicated, but doesn't suffer any frame delays.3458 // Nil will be accepted in place of a function to erase3459 // a previously registered function, and the previous function3461 // (if any) is returned, or nil if none.3462 static int gui_register(lua_State *L)3463 {3464 // We'll do this straight up.3465 // First set up the stack.3466 lua_settop(L, 1);3468 // Verify the validity of the entry3469 if (!lua_isnil(L, 1))3470 luaL_checktype(L, 1, LUA_TFUNCTION);3472 // Get the old value3473 lua_getfield(L, LUA_REGISTRYINDEX, guiCallbackTable);3475 // Save the new value3476 lua_pushvalue(L, 1);3477 lua_setfield(L, LUA_REGISTRYINDEX, guiCallbackTable);3479 // The old value is on top of the stack. Return it.3480 return 1;3481 }3483 // string gui.popup(string message, [string type = "ok"])3484 //3486 // Popup dialog!3487 int gui_popup(lua_State *L)3488 {3489 const char *message = luaL_checkstring(L, 1);3490 const char *type = luaL_optstring(L, 2, "ok");3492 #if (defined(WIN32) && !defined(SDL))3493 int t;3494 if (strcmp(type, "ok") == 0)3495 t = MB_OK;3496 else if (strcmp(type, "yesno") == 0)3497 t = MB_YESNO;3498 else if (strcmp(type, "yesnocancel") == 0)3499 t = MB_YESNOCANCEL;3500 else3501 return luaL_error(L, "invalid popup type \"%s\"", type);3503 theApp.winCheckFullscreen();3504 systemSoundClearBuffer();3505 int result = AfxGetApp()->m_pMainWnd->MessageBox(message, "Lua Script Pop-up", t);3507 lua_settop(L, 1);3509 if (t != MB_OK)3510 {3511 if (result == IDYES)3512 lua_pushstring(L, "yes");3513 else if (result == IDNO)3514 lua_pushstring(L, "no");3515 else if (result == IDCANCEL)3516 lua_pushstring(L, "cancel");3517 else3518 luaL_error(L, "win32 unrecognized return value %d", result);3519 return 1;3520 }3522 // else, we don't care.3523 return 0;3524 #else3525 char *t;3526 #ifdef __linux3527 // The Linux backend has a "FromPause" variable.3528 // If set to 1, assume some known external event has screwed with the flow of time.3529 // Since this pauses the emulator waiting for a response, we set it to 1.3530 // FIXME: Well, actually it doesn't3531 // extern int FromPause;3532 // FromPause = 1;3534 int pid; // appease compiler3536 // Before doing any work, verify the correctness of the parameters.3537 if (strcmp(type, "ok") == 0)3538 t = "OK:100";3539 else if (strcmp(type, "yesno") == 0)3540 t = "Yes:100,No:101";3541 else if (strcmp(type, "yesnocancel") == 0)3542 t = "Yes:100,No:101,Cancel:102";3543 else3544 return luaL_error(L, "invalid popup type \"%s\"", type);3546 // Can we find a copy of xmessage? Search the path.3547 char *path = strdup(getenv("PATH"));3549 char *current = path;3551 char *colon;3553 int found = 0;3555 while (current)3556 {3557 colon = strchr(current, ':');3559 // Clip off the colon.3560 *colon++ = 0;3562 int len = strlen(current);3563 char *filename = (char *)malloc(len + 12); // always give excess3564 snprintf(filename, len + 12, "%s/xmessage", current);3566 if (access(filename, X_OK) == 0)3567 {3568 free(filename);3569 found = 1;3570 break;3571 }3573 // Failed, move on.3574 current = colon;3575 free(filename);3576 }3578 free(path);3580 // We've found it?3581 if (!found)3582 goto use_console;3584 pid = fork();3585 if (pid == 0)3586 { // I'm the virgin sacrifice3587 // I'm gonna be dead in a matter of microseconds anyways, so wasted memory doesn't matter to me.3588 // Go ahead and abuse strdup.3589 char *parameters[] = { "xmessage", "-buttons", t, strdup(message), NULL };3591 execvp("xmessage", parameters);3593 // Aw shitty3594 perror("exec xmessage");3595 exit(1);3596 }3597 else if (pid < 0) // something went wrong!!! Oh hell... use the console3598 goto use_console;3599 else3600 {3601 // We're the parent. Watch for the child.3602 int r;3603 int res = waitpid(pid, &r, 0);3604 if (res < 0) // wtf?3605 goto use_console;3607 // The return value gets copmlicated...3608 if (!WIFEXITED(r))3609 {3610 luaL_error(L, "don't screw with my xmessage process!");3611 }3613 r = WEXITSTATUS(r);3615 // We assume it's worked.3616 if (r == 0)3617 {3618 return 0; // no parameters for an OK3619 }3621 if (r == 100)3622 {3623 lua_pushstring(L, "yes");3624 return 1;3625 }3627 if (r == 101)3628 {3629 lua_pushstring(L, "no");3630 return 1;3631 }3633 if (r == 102)3634 {3635 lua_pushstring(L, "cancel");3636 return 1;3637 }3639 // Wtf?3640 return luaL_error(L, "popup failed due to unknown results involving xmessage (%d)", r);3641 }3643 use_console:3644 #endif3646 // All else has failed3647 if (strcmp(type, "ok") == 0)3648 t = "";3649 else if (strcmp(type, "yesno") == 0)3650 t = "yn";3651 else if (strcmp(type, "yesnocancel") == 0)3652 t = "ync";3653 else3654 return luaL_error(L, "invalid popup type \"%s\"", type);3656 fprintf(stderr, "Lua Message: %s\n", message);3658 while (true)3659 {3660 char buffer[64];3662 // We don't want parameters3663 if (!t[0])3664 {3665 fprintf(stderr, "[Press Enter]");3666 fgets(buffer, sizeof(buffer), stdin);3668 // We're done3669 return 0;3670 }3672 fprintf(stderr, "(%s): ", t);3673 fgets(buffer, sizeof(buffer), stdin);3675 // Check if the option is in the list3676 if (strchr(t, tolower(buffer[0])))3677 {3678 switch (tolower(buffer[0]))3679 {3680 case 'y':3681 lua_pushstring(L, "yes");3682 return 1;3683 case 'n':3684 lua_pushstring(L, "no");3685 return 1;3686 case 'c':3687 lua_pushstring(L, "cancel");3688 return 1;3689 default:3690 luaL_error(L, "internal logic error in console based prompts for gui.popup");3691 }3692 }3694 // We fell through, so we assume the user answered wrong and prompt again.3695 }3697 // Nothing here, since the only way out is in the loop.3698 #endif3699 }3701 #if (defined(WIN32) && !defined(SDL))3702 const char *s_keyToName[256] =3703 {3704 NULL,3705 "leftclick",3706 "rightclick",3707 NULL,3708 "middleclick",3709 NULL,3710 NULL,3711 NULL,3712 "backspace",3713 "tab",3714 NULL,3715 NULL,3716 NULL,3717 "enter",3718 NULL,3719 NULL,3720 "shift", // 0x103721 "control",3722 "alt",3723 "pause",3724 "capslock",3725 NULL,3726 NULL,3727 NULL,3728 NULL,3729 NULL,3730 NULL,3731 "escape",3732 NULL,3733 NULL,3734 NULL,3735 NULL,3736 "space", // 0x203737 "pageup",3738 "pagedown",3739 "end",3740 "home",3741 "left",3742 "up",3743 "right",3744 "down",3745 NULL,3746 NULL,3747 NULL,3748 NULL,3749 "insert",3750 "delete",3751 NULL,3752 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",3753 NULL, NULL, NULL, NULL, NULL, NULL, NULL,3754 "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",3755 "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",3756 "U", "V", "W", "X", "Y", "Z",3757 NULL,3758 NULL,3759 NULL,3760 NULL,3761 NULL,3762 "numpad0", "numpad1", "numpad2", "numpad3", "numpad4", "numpad5", "numpad6", "numpad7", "numpad8", "numpad9",3763 "numpad*", "numpad+",3764 NULL,3765 "numpad-", "numpad.", "numpad/",3766 "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11",3767 "F12",3768 "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23",3769 "F24",3770 NULL,3771 NULL,3772 NULL,3773 NULL,3774 NULL,3775 NULL,3776 NULL,3777 NULL,3778 "numlock",3779 "scrolllock",3780 NULL, // 0x923781 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,3782 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,3783 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,3784 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,3785 NULL, // 0xB93786 "semicolon",3787 "plus",3788 "comma",3789 "minus",3790 "period",3791 "slash",3792 "tilde",3793 NULL, // 0xC13794 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,3795 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,3796 NULL, NULL, NULL, NULL, NULL, NULL, NULL,3797 NULL, // 0xDA3798 "leftbracket",3799 "backslash",3800 "rightbracket",3801 "quote",3802 };3803 #endif3805 // input.get()3806 // takes no input, returns a lua table of entries representing the current input state,3807 // independent of the joypad buttons the emulated game thinks are pressed3808 // for example:3809 // if the user is holding the W key and the left mouse button3810 // and has the mouse at the bottom-right corner of the game screen,3812 // then this would return {W=true, leftclick=true, xmouse=255, ymouse=223}3813 static int input_getcurrentinputstatus(lua_State *L)3814 {3815 lua_newtable(L);3817 #if (defined(WIN32) && !defined(SDL))3818 // keyboard and mouse button status3819 {3820 unsigned char keys[256];3821 if (true /*!GUI.BackgroundInput*/) // TODO: background input3822 {3823 if (GetKeyboardState(keys))3824 {3825 for (int i = 1; i < 255; i++)3826 {3827 int mask = (i == VK_CAPITAL || i == VK_NUMLOCK || i == VK_SCROLL) ? 0x01 : 0x80;3828 if (keys[i] & mask)3829 {3830 const char *name = s_keyToName[i];3831 if (name)3832 {3833 lua_pushboolean(L, true);3834 lua_setfield(L, -2, name);3835 }3836 }3837 }3838 }3839 }3840 else // use a slightly different method that will detect background input:3841 {3842 for (int i = 1; i < 255; i++)3843 {3844 const char *name = s_keyToName[i];3845 if (name)3846 {3847 int active;3848 if (i == VK_CAPITAL || i == VK_NUMLOCK || i == VK_SCROLL)3849 active = GetKeyState(i) & 0x01;3850 else3851 active = GetAsyncKeyState(i) & 0x8000;3852 if (active)3853 {3854 lua_pushboolean(L, true);3855 lua_setfield(L, -2, name);3856 }3857 }3858 }3859 }3860 }3862 // mouse position in game screen pixel coordinates3863 {3864 POINT mouse;3866 int xofs = 0, yofs = 0, width = 240, height = 160;3867 if (!systemIsRunningGBA())3868 {3869 if (gbBorderOn)3870 width = 256, height = 224, xofs = 48, yofs = 40;3871 else3872 width = 160, height = 144;3873 }3875 GetCursorPos(&mouse);3876 AfxGetApp()->m_pMainWnd->ScreenToClient(&mouse);3878 // game screen is always fully stretched to window size,3879 // with no aspect rate correction, or something like that.3880 RECT clientRect;3881 AfxGetApp()->m_pMainWnd->GetClientRect(&clientRect);3883 int wndWidth = clientRect.right - clientRect.left;3884 int wndHeight = clientRect.bottom - clientRect.top;3885 mouse.x = (LONG) (mouse.x * ((float)width / wndWidth)) - xofs;3886 mouse.y = (LONG) (mouse.y * ((float)height / wndHeight)) - yofs;3888 lua_pushinteger(L, mouse.x);3889 lua_setfield(L, -2, "xmouse");3890 lua_pushinteger(L, mouse.y);3891 lua_setfield(L, -2, "ymouse");3892 }3894 #else3895 // NYI (well, return an empty table)3896 #endif3897 return 1;3898 }3900 static int avi_framecount(lua_State *L)3901 {3902 #ifdef WIN323903 if (theApp.aviRecorder != NULL)3904 {3905 lua_pushinteger(L, theApp.aviRecorder->videoFrames());3906 }3907 else3908 #endif3909 {3910 lua_pushinteger(L, 0);3911 }3912 return 1;3913 }3915 static int avi_pause(lua_State *L)3916 {3917 #ifdef WIN323918 if (theApp.aviRecorder != NULL)3919 theApp.aviRecorder->Pause(true);3920 #endif3921 return 1;3922 }3924 static int avi_resume(lua_State *L)3925 {3926 #ifdef WIN323927 if (theApp.aviRecorder != NULL)3928 theApp.aviRecorder->Pause(false);3929 #endif3930 return 1;3931 }3933 static int sound_get(lua_State *L)3934 {3935 extern int32 soundLevel1;3936 extern int32 soundLevel2;3937 extern int32 soundBalance;3938 extern int32 soundMasterOn;3939 extern int32 soundVIN;3940 extern int32 sound1On;3941 extern int32 sound1EnvelopeVolume;3942 extern int32 sound2On;3943 extern int32 sound2EnvelopeVolume;3944 extern int32 sound3On;3945 extern int32 sound3OutputLevel;3946 extern int32 sound3Bank;3947 extern int32 sound3DataSize;3948 extern int32 sound3ForcedOutput;3949 extern int32 sound4On;3950 extern int32 sound4EnvelopeVolume;3951 extern u8 sound3WaveRam[0x20];3953 int freqReg;3954 double freq;3955 double leftvolscale;3956 double rightvolscale;3957 double panpot;3958 bool gba = systemIsRunningGBA();3959 u8* gbMem = gba ? ioMem : gbMemory;3960 const int rNR10 = gba ? 0x60 : 0xff10;3961 const int rNR11 = gba ? 0x62 : 0xff11;3962 const int rNR12 = gba ? 0x63 : 0xff12;3963 const int rNR13 = gba ? 0x64 : 0xff13;3964 const int rNR14 = gba ? 0x65 : 0xff14;3965 const int rNR21 = gba ? 0x68 : 0xff16;3966 const int rNR22 = gba ? 0x69 : 0xff17;3967 const int rNR23 = gba ? 0x6c : 0xff18;3968 const int rNR24 = gba ? 0x6d : 0xff19;3969 const int rNR30 = gba ? 0x70 : 0xff1a;3970 const int rNR31 = gba ? 0x72 : 0xff1b;3971 const int rNR32 = gba ? 0x73 : 0xff1c;3972 const int rNR33 = gba ? 0x74 : 0xff1d;3973 const int rNR34 = gba ? 0x75 : 0xff1e;3974 const int rNR41 = gba ? 0x78 : 0xff20;3975 const int rNR42 = gba ? 0x79 : 0xff21;3976 const int rNR43 = gba ? 0x7c : 0xff22;3977 const int rNR44 = gba ? 0x7d : 0xff23;3978 const int rNR50 = gba ? 0x80 : 0xff24;3979 const int rNR51 = gba ? 0x81 : 0xff25;3980 const int rNR52 = gba ? 0x84 : 0xff26;3981 const int rWAVE_RAM = gba ? 0x90 : 0xff30;3983 const int32 _soundVIN = 0x88; // gba ? 0x88 : soundVIN;3984 const bool soundVINLeft = ((_soundVIN & 0x80) != 0);3985 const bool soundVINRight = ((_soundVIN & 0x08) != 0);3987 lua_newtable(L);3989 // square13990 lua_newtable(L);3991 if(sound1On == 0 || soundMasterOn == 0)3992 {3993 lua_pushnumber(L, 0.0);3994 panpot = 0.5;3995 }3996 else3997 {3998 double envVolume = sound1EnvelopeVolume / 15.0;3999 if (soundVINLeft && (soundBalance & 0x10) != 0)4000 leftvolscale = ((soundLevel2 / 7.0) * envVolume);4001 else4002 leftvolscale = 0.0;4003 if (soundVINRight && (soundBalance & 0x01) != 0)4004 rightvolscale = ((soundLevel1 / 7.0) * envVolume);4005 else4006 rightvolscale = 0.0;4007 if ((leftvolscale + rightvolscale) != 0)4008 panpot = rightvolscale / (leftvolscale + rightvolscale);4009 else4010 panpot = 0.5;4011 lua_pushnumber(L, (leftvolscale + rightvolscale) / 2.0);4012 }4013 lua_setfield(L, -2, "volume");4014 lua_pushnumber(L, panpot);4015 lua_setfield(L, -2, "panpot");4016 freqReg = (((int)(gbMem[rNR14] & 7) << 8) | gbMem[rNR13]);4017 freq = 131072.0 / (2048 - freqReg);4018 lua_pushnumber(L, freq);4019 lua_setfield(L, -2, "frequency");4020 lua_pushnumber(L, (log(freq / 440.0) * 12 / log(2.0)) + 69);4021 lua_setfield(L, -2, "midikey");4022 lua_pushinteger(L, (gbMem[rNR11] & 0xC0) >> 6);4023 lua_setfield(L, -2, "duty");4024 lua_newtable(L);4025 lua_pushinteger(L, freqReg);4026 lua_setfield(L, -2, "frequency");4027 lua_setfield(L, -2, "regs");4028 lua_setfield(L, -2, "square1");4029 // square24030 lua_newtable(L);4031 if(sound2On == 0 || soundMasterOn == 0)4032 {4033 lua_pushnumber(L, 0.0);4034 panpot = 0.5;4035 }4036 else4037 {4038 double envVolume = sound2EnvelopeVolume / 15.0;4039 if (soundVINLeft && (soundBalance & 0x20) != 0)4040 leftvolscale = ((soundLevel2 / 7.0) * envVolume);4041 else4042 leftvolscale = 0.0;4043 if (soundVINRight && (soundBalance & 0x02) != 0)4044 rightvolscale = ((soundLevel1 / 7.0) * envVolume);4045 else4046 rightvolscale = 0.0;4047 if ((leftvolscale + rightvolscale) != 0)4048 panpot = rightvolscale / (leftvolscale + rightvolscale);4049 else4050 panpot = 0.5;4051 lua_pushnumber(L, (leftvolscale + rightvolscale) / 2.0);4052 }4053 lua_setfield(L, -2, "volume");4054 lua_pushnumber(L, panpot);4055 lua_setfield(L, -2, "panpot");4056 freqReg = (((int)(gbMem[rNR24] & 7) << 8) | gbMem[rNR23]);4057 freq = 131072.0 / (2048 - freqReg);4058 lua_pushnumber(L, freq);4059 lua_setfield(L, -2, "frequency");4060 lua_pushnumber(L, (log(freq / 440.0) * 12 / log(2.0)) + 69);4061 lua_setfield(L, -2, "midikey");4062 lua_pushinteger(L, (gbMem[rNR21] & 0xC0) >> 6);4063 lua_setfield(L, -2, "duty");4064 lua_newtable(L);4065 lua_pushinteger(L, freqReg);4066 lua_setfield(L, -2, "frequency");4067 lua_setfield(L, -2, "regs");4068 lua_setfield(L, -2, "square2");4069 // wavememory4070 lua_newtable(L);4071 if(sound3On == 0 || soundMasterOn == 0)4072 {4073 lua_pushnumber(L, 0.0);4074 panpot = 0.5;4075 }4076 else4077 {4078 double envVolume;4079 if (gba && sound3ForcedOutput != 0)4080 envVolume = 0.75;4081 else4082 {4083 double volTable[4] = { 0.0, 1.0, 0.5, 0.25 };4084 envVolume = volTable[sound3OutputLevel & 3];4085 }4087 if (soundVINLeft && (soundBalance & 0x40) != 0)4088 leftvolscale = ((soundLevel2 / 7.0) * envVolume);4089 else4090 leftvolscale = 0.0;4091 if (soundVINRight && (soundBalance & 0x04) != 0)4092 rightvolscale = ((soundLevel1 / 7.0) * envVolume);4093 else4094 rightvolscale = 0.0;4095 if ((leftvolscale + rightvolscale) != 0)4096 panpot = rightvolscale / (leftvolscale + rightvolscale);4097 else4098 panpot = 0.5;4099 lua_pushnumber(L, (leftvolscale + rightvolscale) / 2.0);4100 }4101 lua_setfield(L, -2, "volume");4102 lua_pushnumber(L, panpot);4103 lua_setfield(L, -2, "panpot");4104 int waveMemSamples = 32;4105 if (gba)4106 {4107 lua_pushlstring(L, (const char *) &sound3WaveRam[sound3Bank * 0x10], sound3DataSize ? 0x20 : 0x10);4108 waveMemSamples = sound3DataSize ? 64 : 32;4109 }4110 else4111 {4112 lua_pushlstring(L, (const char *) &gbMem[rWAVE_RAM], 0x10);4113 }4114 lua_setfield(L, -2, "waveform");4115 freqReg = (((int)(gbMem[rNR34] & 7) << 8) | gbMem[rNR33]);4116 freq = 2097152.0 / (waveMemSamples * (2048 - freqReg));4117 lua_pushnumber(L, freq);4118 lua_setfield(L, -2, "frequency");4119 lua_pushnumber(L, (log(freq / 440.0) * 12 / log(2.0)) + 69);4120 lua_setfield(L, -2, "midikey");4121 lua_newtable(L);4122 lua_pushinteger(L, freqReg);4123 lua_setfield(L, -2, "frequency");4124 lua_setfield(L, -2, "regs");4125 lua_setfield(L, -2, "wavememory");4126 // noise4127 lua_newtable(L);4128 if(sound4On == 0 || soundMasterOn == 0)4129 {4130 lua_pushnumber(L, 0.0);4131 panpot = 0.5;4132 }4133 else4134 {4135 double envVolume = sound4EnvelopeVolume / 15.0;4136 if (soundVINLeft && (soundBalance & 0x80) != 0)4137 leftvolscale = ((soundLevel2 / 7.0) * envVolume);4138 else4139 leftvolscale = 0.0;4140 if (soundVINRight && (soundBalance & 0x08) != 0)4141 rightvolscale = ((soundLevel1 / 7.0) * envVolume);4142 else4143 rightvolscale = 0.0;4144 if ((leftvolscale + rightvolscale) != 0)4145 panpot = rightvolscale / (leftvolscale + rightvolscale);4146 else4147 panpot = 0.5;4148 lua_pushnumber(L, (leftvolscale + rightvolscale) / 2.0);4149 }4150 lua_setfield(L, -2, "volume");4151 lua_pushnumber(L, panpot);4152 lua_setfield(L, -2, "panpot");4153 const int gbNoiseFreqTable[8] = { 1, 2, 4, 6, 8, 10, 12, 14 };4154 freqReg = gbNoiseFreqTable[gbMem[rNR43] & 7] << (1 + (gbMem[rNR43] >> 4));4155 lua_pushboolean(L, (gbMem[rNR43] & 8) != 0);4156 lua_setfield(L, -2, "short");4157 freq = 1048576.0 / freqReg;4158 lua_pushnumber(L, freq);4159 lua_setfield(L, -2, "frequency");4160 lua_pushnumber(L, (log(freq / 440.0) * 12 / log(2.0)) + 69);4161 lua_setfield(L, -2, "midikey");4162 lua_newtable(L);4163 lua_pushinteger(L, freqReg);4164 lua_setfield(L, -2, "frequency");4165 lua_setfield(L, -2, "regs");4166 lua_setfield(L, -2, "noise");4168 return 1;4169 }4171 // same as math.random, but uses SFMT instead of C rand()4172 // FIXME: this function doesn't care multi-instance,4174 // original math.random either though (Lua 5.1)4175 static int sfmt_random(lua_State *L)4176 {4177 lua_Number r = (lua_Number) genrand_real2();4178 switch (lua_gettop(L))4179 { // check number of arguments4180 case 0:4181 { // no arguments4182 lua_pushnumber(L, r); // Number between 0 and 14183 break;4184 }4186 case 1:4187 { // only upper limit4188 int u = luaL_checkint(L, 1);4189 luaL_argcheck(L, 1 <= u, 1, "interval is empty");4190 lua_pushnumber(L, floor(r * u) + 1); // int between 1 and `u'4191 break;4192 }4194 case 2:4195 { // lower and upper limits4196 int l = luaL_checkint(L, 1);4197 int u = luaL_checkint(L, 2);4198 luaL_argcheck(L, l <= u, 2, "interval is empty");4199 lua_pushnumber(L, floor(r * (u - l + 1)) + l); // int between `l' and `u'4200 break;4201 }4203 default:4204 return luaL_error(L, "wrong number of arguments");4205 }4207 return 1;4208 }4210 // same as math.randomseed, but uses SFMT instead of C srand()4211 // FIXME: this function doesn't care multi-instance,4213 // original math.randomseed either though (Lua 5.1)4214 static int sfmt_randomseed(lua_State *L)4215 {4216 init_gen_rand(luaL_checkint(L, 1));4217 return 0;4218 }4220 // the following bit operations are ported from LuaBitOp 1.0.1,4221 // because it can handle the sign bit (bit 31) correctly.4223 /*4224 ** Lua BitOp -- a bit operations library for Lua 5.1.4225 ** http://bitop.luajit.org/4226 **4227 ** Copyright (C) 2008-2009 Mike Pall. All rights reserved.4228 **4229 ** Permission is hereby granted, free of charge, to any person obtaining4230 ** a copy of this software and associated documentation files (the4231 ** "Software"), to deal in the Software without restriction, including4232 ** without limitation the rights to use, copy, modify, merge, publish,4233 ** distribute, sublicense, and/or sell copies of the Software, and to4234 ** permit persons to whom the Software is furnished to do so, subject to4235 ** the following conditions:4236 **4237 ** The above copyright notice and this permission notice shall be4238 ** included in all copies or substantial portions of the Software.4239 **4240 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,4241 ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF4242 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.4243 ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY4244 ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,4245 ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE4246 ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.4247 **4248 ** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]4249 */4251 #ifdef _MSC_VER4252 /* MSVC is stuck in the last century and doesn't have C99's stdint.h. */4253 typedef __int32 int32_t;4254 typedef unsigned __int32 uint32_t;4255 typedef unsigned __int64 uint64_t;4256 #else4257 #include <stdint.h>4258 #endif4260 typedef int32_t SBits;4261 typedef uint32_t UBits;4263 typedef union4264 {4265 lua_Number n;4266 #ifdef LUA_NUMBER_DOUBLE4267 uint64_t b;4268 #else4269 UBits b;4270 #endif4271 } BitNum;4273 /* Convert argument to bit type. */4274 static UBits barg(lua_State *L, int idx)4275 {4276 BitNum bn;4277 UBits b;4278 bn.n = lua_tonumber(L, idx);4279 #if defined(LUA_NUMBER_DOUBLE)4280 bn.n += 6755399441055744.0; /* 2^52+2^51 */4281 #ifdef SWAPPED_DOUBLE4282 b = (UBits)(bn.b >> 32);4283 #else4284 b = (UBits)(bn.b & 0xffffffff);4285 #endif4286 #elif defined(LUA_NUMBER_INT) || defined(LUA_NUMBER_LONG) || \4287 defined(LUA_NUMBER_LONGLONG) || defined(LUA_NUMBER_LONG_LONG) || \4288 defined(LUA_NUMBER_LLONG)4289 if (sizeof(UBits) == sizeof(lua_Number))4290 b = bn.b;4291 else4292 b = (UBits)(SBits)bn.n;4293 #elif defined(LUA_NUMBER_FLOAT)4294 #error "A 'float' lua_Number type is incompatible with this library"4295 #else4296 #error "Unknown number type, check LUA_NUMBER_* in luaconf.h"4297 #endif4298 if (b == 0 && !lua_isnumber(L, idx))4299 luaL_typerror(L, idx, "number");4300 return b;4301 }4303 /* Return bit type. */4304 #define BRET(b) lua_pushnumber(L, (lua_Number)(SBits)(b)); return 1;4306 static int bit_tobit(lua_State *L) { BRET(barg(L, 1)) }4307 static int bit_bnot(lua_State *L) { BRET(~barg(L, 1)) }4309 #define BIT_OP(func, opr) \4310 static int func(lua_State * L) { int i; UBits b = barg(L, 1); \4311 for (i = lua_gettop(L); i > 1; i--) \4312 b opr barg(L, i); BRET(b) }4313 BIT_OP(bit_band, &= )4314 BIT_OP(bit_bor, |= )4315 BIT_OP(bit_bxor, ^= )4317 #define bshl(b, n) (b << n)4318 #define bshr(b, n) (b >> n)4319 #define bsar(b, n) ((SBits)b >> n)4320 #define brol(b, n) ((b << n) | (b >> (32 - n)))4321 #define bror(b, n) ((b << (32 - n)) | (b >> n))4322 #define BIT_SH(func, fn) \4323 static int func(lua_State * L) { \4324 UBits b = barg(L, 1); UBits n = barg(L, 2) & 31; BRET(fn(b, n)) }4325 BIT_SH(bit_lshift, bshl)4326 BIT_SH(bit_rshift, bshr)4327 BIT_SH(bit_arshift, bsar)4328 BIT_SH(bit_rol, brol)4329 BIT_SH(bit_ror, bror)4331 static int bit_bswap(lua_State *L)4332 {4333 UBits b = barg(L, 1);4334 b = (b >> 24) | ((b >> 8) & 0xff00) | ((b & 0xff00) << 8) | (b << 24);4335 BRET(b)4336 }4338 static int bit_tohex(lua_State *L)4339 {4340 UBits b = barg(L, 1);4341 SBits n = lua_isnone(L, 2) ? 8 : (SBits)barg(L, 2);4342 const char *hexdigits = "0123456789abcdef";4343 char buf[8];4344 int i;4345 if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; }4346 if (n > 8) n = 8;4347 for (i = (int)n; --i >= 0; )4348 {4349 buf[i] = hexdigits[b & 15]; b >>= 4;4350 }4351 lua_pushlstring(L, buf, (size_t)n);4352 return 1;4353 }4355 static const struct luaL_Reg bit_funcs[] = {4356 { "tobit", bit_tobit },4357 { "bnot", bit_bnot },4358 { "band", bit_band },4359 { "bor", bit_bor },4360 { "bxor", bit_bxor },4361 { "lshift", bit_lshift },4362 { "rshift", bit_rshift },4363 { "arshift", bit_arshift },4364 { "rol", bit_rol },4365 { "ror", bit_ror },4366 { "bswap", bit_bswap },4367 { "tohex", bit_tohex },4368 { NULL, NULL }4369 };4371 /* Signed right-shifts are implementation-defined per C89/C99.4372 ** But the de facto standard are arithmetic right-shifts on two's4373 ** complement CPUs. This behaviour is required here, so test for it.4374 */4375 #define BAD_SAR (bsar(-8, 2) != (SBits) - 2)4377 bool luabitop_validate(lua_State *L) // originally named as luaopen_bit4378 {4379 UBits b;4380 lua_pushnumber(L, (lua_Number)1437217655L);4381 b = barg(L, -1);4382 if (b != (UBits)1437217655L || BAD_SAR) /* Perform a simple self-test. */4383 {4384 const char *msg = "compiled with incompatible luaconf.h";4385 #ifdef LUA_NUMBER_DOUBLE4386 #ifdef WIN324387 if (b == (UBits)1610612736L)4388 msg = "use D3DCREATE_FPU_PRESERVE with DirectX";4389 #endif4390 if (b == (UBits)1127743488L)4391 msg = "not compiled with SWAPPED_DOUBLE";4392 #endif4393 if (BAD_SAR)4394 msg = "arithmetic right-shift broken";4395 luaL_error(L, "bit library self-test failed (%s)", msg);4396 return false;4397 }4398 return true;4399 }4401 // LuaBitOp ends here4403 static int bit_bshift_emulua(lua_State *L)4404 {4405 int shift = luaL_checkinteger(L, 2);4406 if (shift < 0)4407 {4408 lua_pushinteger(L, -shift);4409 lua_replace(L, 2);4410 return bit_lshift(L);4411 }4412 else4413 return bit_rshift(L);4414 }4416 static int bitbit(lua_State *L)4417 {4418 int rv = 0;4419 int numArgs = lua_gettop(L);4420 for (int i = 1; i <= numArgs; i++)4421 {4422 int where = luaL_checkinteger(L, i);4423 if (where >= 0 && where < 32)4424 rv |= (1 << where);4425 }4426 lua_settop(L, 0);4427 BRET(rv);4428 }4430 // The function called periodically to ensure Lua doesn't run amok.4431 static void VBALuaHookFunction(lua_State *L, lua_Debug *dbg)4432 {4433 if (numTries-- == 0)4434 {4435 int kill = 0;4437 #if (defined(WIN32) && !defined(SDL))4438 // Uh oh4439 theApp.winCheckFullscreen();4440 systemSoundClearBuffer();4441 int ret = AfxGetApp()->m_pMainWnd->MessageBox(4442 "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)",4443 "Lua Script Gone Nuts?",4444 MB_YESNO);4446 if (ret == IDYES)4447 {4448 kill = 1;4449 }4451 #else4452 fprintf(4453 stderr,4454 "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");4456 char buffer[64];4457 while (true)4458 {4459 fprintf(stderr, "(y/n): ");4460 fgets(buffer, sizeof(buffer), stdin);4461 if (buffer[0] == 'y' || buffer[0] == 'Y')4462 {4463 kill = 1;4464 break;4465 }4467 if (buffer[0] == 'n' || buffer[0] == 'N')4468 break;4469 }4470 #endif4471 if (kill)4472 {4473 luaL_error(L, "Killed by user request.");4474 VBALuaOnStop();4475 }4477 // else, kill the debug hook.4478 lua_sethook(L, NULL, 0, 0);4479 }4480 }4482 static const struct luaL_reg vbalib[] = {4483 // {"speedmode", vba_speedmode}, // TODO: NYI4484 { "frameadvance", vba_frameadvance },4485 { "pause", vba_pause },4486 { "framecount", vba_framecount },4487 { "lagcount", vba_getlagcount },4488 { "lagged", vba_lagged },4489 { "emulating", vba_emulating },4490 { "registerbefore", vba_registerbefore },4491 { "registerafter", vba_registerafter },4492 { "registerexit", vba_registerexit },4493 { "message", vba_message },4494 { "print", print }, // sure, why not4495 { NULL, NULL }4496 };4498 static const struct luaL_reg memorylib[] = {4499 { "readbyte", memory_readbyte },4500 { "readbytesigned", memory_readbytesigned },4501 { "readword", memory_readword },4502 { "readwordsigned", memory_readwordsigned },4503 { "readdword", memory_readdword },4504 { "readdwordsigned", memory_readdwordsigned },4505 { "readbyterange", memory_readbyterange },4506 { "writebyte", memory_writebyte },4507 { "writeword", memory_writeword },4508 { "writedword", memory_writedword },4509 { "getregister", memory_getregister },4510 { "setregister", memory_setregister },4511 { "gbromreadbyte", memory_gbromreadbyte },4512 { "gbromreadbytesigned", memory_gbromreadbytesigned },4513 { "gbromreadword", memory_gbromreadword },4514 { "gbromreadwordsigned", memory_gbromreadwordsigned },4515 { "gbromreaddword", memory_gbromreaddword },4516 { "gbromreaddwordsigned", memory_gbromreaddwordsigned },4517 { "gbromreadbyterange", memory_gbromreadbyterange },4519 // alternate naming scheme for word and double-word and unsigned4520 { "readbyteunsigned", memory_readbyte },4521 { "readwordunsigned", memory_readword },4522 { "readdwordunsigned", memory_readdword },4523 { "readshort", memory_readword },4524 { "readshortunsigned", memory_readword },4525 { "readshortsigned", memory_readwordsigned },4526 { "readlong", memory_readdword },4527 { "readlongunsigned", memory_readdword },4528 { "readlongsigned", memory_readdwordsigned },4529 { "writeshort", memory_writeword },4530 { "writelong", memory_writedword },4531 { "gbromreadbyteunsigned", memory_gbromreadbyte },4532 { "gbromreadwordunsigned", memory_gbromreadword },4533 { "gbromreaddwordunsigned", memory_gbromreaddword },4534 { "gbromreadshort", memory_gbromreadword },4535 { "gbromreadshortunsigned", memory_gbromreadword },4536 { "gbromreadshortsigned", memory_gbromreadwordsigned },4537 { "gbromreadlong", memory_gbromreaddword },4538 { "gbromreadlongunsigned", memory_gbromreaddword },4539 { "gbromreadlongsigned", memory_gbromreaddwordsigned },4541 // memory hooks4542 { "registerwrite", memory_registerwrite },4543 //{"registerread", memory_registerread},4544 { "registerexec", memory_registerexec },4545 // alternate names4546 { "register", memory_registerwrite },4547 { "registerrun", memory_registerexec },4548 { "registerexecute", memory_registerexec },4550 { NULL, NULL }4551 };4553 static const struct luaL_reg joypadlib[] = {4554 { "get", joypad_get },4555 { "getdown", joypad_getdown },4556 { "getup", joypad_getup },4557 { "set", joypad_set },4559 // alternative names4560 { "read", joypad_get },4561 { "write", joypad_set },4562 { "readdown", joypad_getdown },4563 { "readup", joypad_getup },4564 { NULL, NULL }4565 };4567 static const struct luaL_reg savestatelib[] = {4568 { "create", savestate_create },4569 { "save", savestate_save },4570 { "load", savestate_load },4572 { NULL, NULL }4573 };4575 static const struct luaL_reg movielib[] = {4576 { "active", movie_isactive },4577 { "recording", movie_isrecording },4578 { "playing", movie_isplaying },4579 { "mode", movie_getmode },4581 { "length", movie_getlength },4582 { "author", movie_getauthor },4583 { "name", movie_getfilename },4584 { "rerecordcount", movie_rerecordcount },4585 { "setrerecordcount", movie_setrerecordcount },4587 { "rerecordcounting", movie_rerecordcounting },4588 { "framecount", vba_framecount }, // for those familiar with4589 // other emulators that have4590 // movie.framecount()4591 // instead of4592 // emulatorname.framecount()4594 { "stop", movie_stop },4596 // alternative names4597 { "close", movie_stop },4598 { "getauthor", movie_getauthor },4599 { "getname", movie_getfilename },4600 { NULL, NULL }4601 };4603 static const struct luaL_reg guilib[] = {4604 { "register", gui_register },4605 { "text", gui_text },4606 { "box", gui_drawbox },4607 { "line", gui_drawline },4608 { "pixel", gui_drawpixel },4609 { "opacity", gui_setopacity },4610 { "transparency", gui_transparency },4611 { "popup", gui_popup },4612 { "parsecolor", gui_parsecolor },4613 { "gdscreenshot", gui_gdscreenshot },4614 { "gdoverlay", gui_gdoverlay },4615 { "getpixel", gui_getpixel },4617 // alternative names4618 { "drawtext", gui_text },4619 { "drawbox", gui_drawbox },4620 { "drawline", gui_drawline },4621 { "drawpixel", gui_drawpixel },4622 { "setpixel", gui_drawpixel },4623 { "writepixel", gui_drawpixel },4624 { "rect", gui_drawbox },4625 { "drawrect", gui_drawbox },4626 { "drawimage", gui_gdoverlay },4627 { "image", gui_gdoverlay },4628 { "readpixel", gui_getpixel },4629 { NULL, NULL }4630 };4632 static const struct luaL_reg inputlib[] = {4633 { "get", input_getcurrentinputstatus },4635 // alternative names4636 { "read", input_getcurrentinputstatus },4637 { NULL, NULL }4638 };4640 static const struct luaL_reg soundlib[] = {4641 { "get", sound_get },4643 // alternative names4644 { NULL, NULL }4645 };4647 // gocha: since vba dumps avi so badly,4648 // I add avilib as a workaround for enhanced video encoding.4649 static const struct luaL_reg avilib[] = {4650 { "framecount", avi_framecount },4651 { "pause", avi_pause },4652 { "resume", avi_resume },4653 { NULL, NULL }4654 };4656 void CallExitFunction(void)4657 {4658 if (!LUA)4659 return;4661 lua_settop(LUA, 0);4662 lua_getfield(LUA, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]);4664 int errorcode = 0;4665 if (lua_isfunction(LUA, -1))4666 {4667 errorcode = lua_pcall(LUA, 0, 0, 0);4668 }4670 if (errorcode)4671 HandleCallbackError(LUA);4672 }4674 void VBALuaFrameBoundary(void)4675 {4676 // printf("Lua Frame\n");4678 lua_joypads_used = 0;4680 // HA!4681 if (!LUA || !luaRunning)4682 return;4684 // Our function needs calling4685 lua_settop(LUA, 0);4686 lua_getfield(LUA, LUA_REGISTRYINDEX, frameAdvanceThread);4688 lua_State *thread = lua_tothread(LUA, 1);4690 // Lua calling C must know that we're busy inside a frame boundary4691 frameBoundary = true;4692 frameAdvanceWaiting = false;4694 numTries = 1000;4696 int result = lua_resume(thread, 0);4698 if (result == LUA_YIELD)4699 {4700 // Okay, we're fine with that.4701 }4702 else if (result != 0)4703 {4704 // Done execution by bad causes4705 VBALuaOnStop();4706 lua_pushnil(LUA);4707 lua_setfield(LUA, LUA_REGISTRYINDEX, frameAdvanceThread);4708 lua_pushnil(LUA);4709 lua_setfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable);4711 // Error?4712 //#if (defined(WIN32) && !defined(SDL))4713 // info_print(info_uid, lua_tostring(thread, -1)); //Clear_Sound_Buffer();4714 // AfxGetApp()->m_pMainWnd->MessageBox(lua_tostring(thread, -1), "Lua run error", MB_OK | MB_ICONSTOP);4715 //#else4716 // fprintf(stderr, "Lua thread bombed out: %s\n", lua_tostring(thread, -1));4717 //#endif4718 printerror(thread, -1);4719 }4720 else4721 {4722 VBALuaOnStop();4723 printf("Script died of natural causes.\n");4724 }4726 // Past here, VBA actually runs, so any Lua code is called mid-frame. We must4727 // not do anything too stupid, so let ourselves know.4728 frameBoundary = false;4730 if (!frameAdvanceWaiting)4731 {4732 VBALuaOnStop();4733 }4734 }4736 /**4737 * Loads and runs the given Lua script.4738 * The emulator MUST be paused for this function to be4739 * called. Otherwise, all frame boundary assumptions go out the window.4740 *4741 * Returns true on success, false on failure.4742 */4743 int VBALoadLuaCode(const char *filename)4744 {4745 static bool sfmtInitialized = false;4746 if (!sfmtInitialized)4747 {4748 init_gen_rand((unsigned)time(NULL));4749 sfmtInitialized = true;4750 }4752 if (filename != luaScriptName)4753 {4754 if (luaScriptName)4755 free(luaScriptName);4756 luaScriptName = strdup(filename);4757 }4759 //stop any lua we might already have had running4760 VBALuaStop();4762 // Set current directory from filename (for dofile)4763 char dir[_MAX_PATH];4764 char *slash, *backslash;4765 strcpy(dir, filename);4766 slash = strrchr(dir, '/');4767 backslash = strrchr(dir, '\\');4768 if (!slash || (backslash && backslash < slash))4769 slash = backslash;4770 if (slash)4771 {4772 slash[1] = '\0'; // keep slash itself for some reasons4773 chdir(dir);4774 }4776 if (!LUA)4777 {4778 LUA = lua_open();4779 luaL_openlibs(LUA);4781 luaL_register(LUA, "emu", vbalib); // added for better cross-emulator compatibility4782 luaL_register(LUA, "vba", vbalib); // kept for backward compatibility4783 luaL_register(LUA, "memory", memorylib);4784 luaL_register(LUA, "joypad", joypadlib);4785 luaL_register(LUA, "savestate", savestatelib);4786 luaL_register(LUA, "movie", movielib);4787 luaL_register(LUA, "gui", guilib);4788 luaL_register(LUA, "input", inputlib);4789 luaL_register(LUA, "sound", soundlib);4790 luaL_register(LUA, "bit", bit_funcs); // LuaBitOp library4791 luaL_register(LUA, "avi", avilib); // workaround for enhanced video encoding4792 lua_settop(LUA, 0); // clean the stack, because each call to luaL_register leaves a table on top4794 // register a few utility functions outside of libraries (in the global namespace)4795 lua_register(LUA, "print", print);4796 lua_register(LUA, "tostring", tostring);4797 lua_register(LUA, "addressof", addressof);4798 lua_register(LUA, "copytable", copytable);4800 // old bit operation functions4801 lua_register(LUA, "AND", bit_band);4802 lua_register(LUA, "OR", bit_bor);4803 lua_register(LUA, "XOR", bit_bxor);4804 lua_register(LUA, "SHIFT", bit_bshift_emulua);4805 lua_register(LUA, "BIT", bitbit);4807 luabitop_validate(LUA);4809 lua_pushstring(LUA, "math");4810 lua_gettable(LUA, LUA_GLOBALSINDEX);4811 lua_pushcfunction(LUA, sfmt_random);4812 lua_setfield(LUA, -2, "random");4813 lua_pushcfunction(LUA, sfmt_randomseed);4814 lua_setfield(LUA, -2, "randomseed");4815 lua_settop(LUA, 0);4817 // push arrays for storing hook functions in4818 for (int i = 0; i < LUAMEMHOOK_COUNT; i++)4819 {4820 lua_newtable(LUA);4821 lua_setfield(LUA, LUA_REGISTRYINDEX, luaMemHookTypeStrings[i]);4822 }4823 }4825 // We make our thread NOW because we want it at the bottom of the stack.4826 // If all goes wrong, we let the garbage collector remove it.4827 lua_State *thread = lua_newthread(LUA);4829 // Load the data4830 int result = luaL_loadfile(LUA, filename);4832 if (result)4833 {4834 //#if (defined(WIN32) && !defined(SDL))4835 // info_print(info_uid, lua_tostring(LUA, -1)); //Clear_Sound_Buffer();4836 // AfxGetApp()->m_pMainWnd->MessageBox(lua_tostring(LUA, -1), "Lua load error", MB_OK | MB_ICONSTOP);4837 //#else4838 // fprintf(stderr, "Failed to compile file: %s\n", lua_tostring(LUA, -1));4839 //#endif4840 printerror(LUA, -1);4842 // Wipe the stack. Our thread4843 lua_settop(LUA, 0);4844 return 0; // Oh shit.4845 }4847 // Get our function into it4848 lua_xmove(LUA, thread, 1);4850 // Save the thread to the registry. This is why I make the thread FIRST.4851 lua_setfield(LUA, LUA_REGISTRYINDEX, frameAdvanceThread);4853 // Initialize settings4854 luaRunning = true;4855 skipRerecords = false;4856 numMemHooks = 0;4857 transparencyModifier = 255; // opaque4858 lua_joypads_used = 0; // not used4859 //wasPaused = systemIsPaused();4860 //systemSetPause(false);4862 // Set up our protection hook to be executed once every 10,000 bytecode instructions.4863 lua_sethook(thread, VBALuaHookFunction, LUA_MASKCOUNT, 10000);4865 #ifdef WIN324866 info_print = PrintToWindowConsole;4867 info_onstart = WinLuaOnStart;4868 info_onstop = WinLuaOnStop;4869 if (!LuaConsoleHWnd)4870 LuaConsoleHWnd = CreateDialog(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDD_LUA),4871 AfxGetMainWnd()->GetSafeHwnd(), (DLGPROC) DlgLuaScriptDialog);4872 info_uid = (int)LuaConsoleHWnd;4873 #else4874 info_print = NULL;4875 info_onstart = NULL;4876 info_onstop = NULL;4877 #endif4878 if (info_onstart)4879 info_onstart(info_uid);4881 // And run it right now. :)4882 VBALuaFrameBoundary();4883 systemRenderFrame();4885 // We're done.4886 return 1;4887 }4889 /**4890 * Equivalent to repeating the last VBALoadLuaCode() call.4891 */4892 int VBAReloadLuaCode(void)4893 {4894 if (!luaScriptName)4895 {4896 systemScreenMessage("There's no script to reload.");4897 return 0;4898 }4899 else4900 return VBALoadLuaCode(luaScriptName);4901 }4903 /**4904 * Terminates a running Lua script by killing the whole Lua engine.4905 *4906 * Always safe to call, except from within a lua call itself (duh).4907 *4908 */4909 void VBALuaStop(void)4910 {4911 //already killed4912 if (!LUA)4913 return;4915 //execute the user's shutdown callbacks4916 CallExitFunction();4918 /*info.*/ numMemHooks = 0;4919 for (int i = 0; i < LUAMEMHOOK_COUNT; i++)4920 CalculateMemHookRegions((LuaMemHookType)i);4922 //sometimes iup uninitializes com4923 //MBG TODO - test whether this is really necessary. i dont think it is4924 #if (defined(WIN32) && !defined(SDL))4925 CoInitialize(0);4926 #endif4928 if (info_onstop)4929 info_onstop(info_uid);4931 //lua_gc(LUA,LUA_GCCOLLECT,0);4932 lua_close(LUA); // this invokes our garbage collectors for us4933 LUA = NULL;4934 VBALuaOnStop();4935 }4937 /**4938 * Returns true if there is a Lua script running.4939 *4940 */4941 int VBALuaRunning(void)4942 {4943 // FIXME: return false when no callback functions are registered.4944 return (int) (LUA != NULL); // should return true if callback functions are active.4945 }4947 /**4948 * Returns true if Lua would like to steal the given joypad control.4949 *4950 * Range is 0 through 34951 */4952 int VBALuaUsingJoypad(int which)4953 {4954 if (which < 0 || which > 3)4955 which = systemGetDefaultJoypad();4956 return lua_joypads_used & (1 << which);4957 }4959 /**4960 * Reads the buttons Lua is feeding for the given joypad, in the same4961 * format as the OS-specific code.4962 *4963 * <del>This function must not be called more than once per frame. </del>Ideally exactly once4964 * per frame (if VBALuaUsingJoypad says it's safe to do so)4965 */4966 int VBALuaReadJoypad(int which)4967 {4968 if (which < 0 || which > 3)4969 which = systemGetDefaultJoypad();4971 //lua_joypads_used &= ~(1 << which);4972 return lua_joypads[which];4973 }4975 /**4976 * If this function returns true, the movie code should NOT increment4977 * the rerecord count for a load-state.4978 *4979 * This function will not return true if a script is not running.4980 */4981 bool8 VBALuaRerecordCountSkip(void)4982 {4983 // FIXME: return true if (there are any active callback functions && skipRerecords)4984 return LUA && luaRunning && skipRerecords;4985 }4987 /**4988 * Given a screen with the indicated resolution,4989 * draw the current GUI onto it.4990 */4991 void VBALuaGui(uint8 *screen, int ppl, int width, int height)4992 {4993 if (!LUA /* || !luaRunning*/)4994 return;4996 // First, check if we're being called by anybody4997 lua_getfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable);4999 if (lua_isfunction(LUA, -1))5000 {5001 // We call it now5002 numTries = 1000;5004 int ret = lua_pcall(LUA, 0, 0, 0);5005 if (ret != 0)5006 {5007 // This is grounds for trashing the function5008 // Note: This must be done before the messagebox pops up,5009 // otherwise the messagebox will cause a paint event which causes a weird5010 // infinite call sequence that makes Snes9x silently exit with error code 3,5011 // if a Lua GUI function crashes. (nitsuja)5012 lua_pushnil(LUA);5013 lua_setfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable);5015 //#if (defined(WIN32) && !defined(SDL))5016 // info_print(info_uid, lua_tostring(LUA, -1)); //AfxGetApp()->m_pMainWnd->MessageBox(lua_tostring(LUA, -1), "Lua Error5017 // in GUI function", MB_OK);5018 //#else5019 // fprintf(stderr, "Lua error in gui.register function: %s\n", lua_tostring(LUA, -1));5020 //#endif5021 printerror(LUA, -1);5022 }5023 }5025 // And wreak the stack5026 lua_settop(LUA, 0);5028 if (!gui_used)5029 return;5031 gui_used = false;5033 int x, y;5035 //int pitch = (((ppl * systemColorDepth + 7)>>3)+3)&~3;5036 int pitch = ppl * (systemColorDepth / 8) + (systemColorDepth == 24 ? 0 : 4);5038 if (width > LUA_SCREEN_WIDTH)5039 width = LUA_SCREEN_WIDTH;5040 if (height > LUA_SCREEN_HEIGHT)5041 height = LUA_SCREEN_HEIGHT;5043 GetColorFunc getColor;5044 SetColorFunc setColor;5045 getColorIOFunc(systemColorDepth, &getColor, &setColor);5047 for (y = 0; y < height; y++)5048 {5049 uint8 *scr = &screen[y * pitch];5050 for (x = 0; x < width; x++, scr += systemColorDepth / 8)5051 {5052 const uint8 gui_alpha = gui_data[(y * LUA_SCREEN_WIDTH + x) * 4 + 3];5053 if (gui_alpha == 0)5054 {5055 // do nothing5056 continue;5057 }5059 const uint8 gui_red = gui_data[(y * LUA_SCREEN_WIDTH + x) * 4 + 2];5060 const uint8 gui_green = gui_data[(y * LUA_SCREEN_WIDTH + x) * 4 + 1];5061 const uint8 gui_blue = gui_data[(y * LUA_SCREEN_WIDTH + x) * 4];5062 int red, green, blue;5064 if (gui_alpha == 255)5065 {5066 // direct copy5067 red = gui_red;5068 green = gui_green;5069 blue = gui_blue;5070 }5071 else5072 {5073 // alpha-blending5074 uint8 scr_red, scr_green, scr_blue;5075 getColor(scr, &scr_red, &scr_green, &scr_blue);5076 red = (((int)gui_red - scr_red) * gui_alpha / 255 + scr_red) & 255;5077 green = (((int)gui_green - scr_green) * gui_alpha / 255 + scr_green) & 255;5078 blue = (((int)gui_blue - scr_blue) * gui_alpha / 255 + scr_blue) & 255;5079 }5081 setColor(scr, (uint8) red, (uint8) green, (uint8) blue);5082 }5083 }5085 return;5086 }5088 void VBALuaClearGui(void)5089 {5090 gui_used = false;5091 }5093 lua_State *VBAGetLuaState()5094 {5095 return LUA;5096 }5098 char *VBAGetLuaScriptName()5099 {5100 return luaScriptName;5101 }