#include #include #include #include #include #include #include #include #include #include #include #ifdef __linux #include #include #include #endif extern "C" { #include #include #include } #include "types.h" #include "fceu.h" #include "video.h" #include "drawing.h" #include "state.h" #include "movie.h" #include "driver.h" #include "cheat.h" #include "x6502.h" #include "x6502abbrev.h" #include "utils/xstring.h" #include "utils/memory.h" #include "fceulua.h" #ifdef WIN32 #include "drivers/win/common.h" #endif #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif #ifdef __unix__ #define stricmp strcasecmp #define strnicmp strncasecmp #ifdef __GNUC__ #define __forceinline __attribute__ ((always_inline)) #else #define __forceinline #endif #endif #ifdef WIN32 extern void AddRecentLuaFile(const char *filename); #endif struct LuaSaveState { std::string filename; memorystream *data; bool anonymous, persisted; LuaSaveState() : data(0) , anonymous(false) , persisted(false) {} ~LuaSaveState() { if(data) delete data; } void persist() { persisted = true; FILE* outf = fopen(filename.c_str(),"wb"); fwrite(data->buf(),1,data->size(),outf); fclose(outf); } void ensureLoad() { if(data) return; persisted = true; FILE* inf = fopen(filename.c_str(),"rb"); fseek(inf,0,SEEK_END); int len = ftell(inf); fseek(inf,0,SEEK_SET); data = new memorystream(len); fread(data->buf(),1,len,inf); fclose(inf); } }; static lua_State *L; // Are we running any code right now? static char *luaScriptName = NULL; // Are we running any code right now? int luaRunning = FALSE; // True at the frame boundary, false otherwise. static int frameBoundary = FALSE; // The execution speed we're running at. static enum {SPEED_NORMAL, SPEED_NOTHROTTLE, SPEED_TURBO, SPEED_MAXIMUM} speedmode = SPEED_NORMAL; // Rerecord count skip mode static int skipRerecords = FALSE; // Used by the registry to find our functions static const char *frameAdvanceThread = "FCEU.FrameAdvance"; static const char *guiCallbackTable = "FCEU.GUI"; // True if there's a thread waiting to run after a run of frame-advance. static int frameAdvanceWaiting = FALSE; // We save our pause status in the case of a natural death. static int wasPaused = FALSE; // Transparency strength. 255=opaque, 0=so transparent it's invisible static int transparencyModifier = 255; // Our joypads. static uint8 luajoypads1[4]= { 0xFF, 0xFF, 0xFF, 0xFF }; //x1 static uint8 luajoypads2[4]= { 0x00, 0x00, 0x00, 0x00 }; //0x /* Crazy logic stuff. 11 - true 01 - pass-through (default) 00 - false 10 - invert */ static enum { GUI_USED_SINCE_LAST_DISPLAY, GUI_USED_SINCE_LAST_FRAME, GUI_CLEAR } gui_used = GUI_CLEAR; static uint8 *gui_data = NULL; static int gui_saw_current_palette = FALSE; // Protects Lua calls from going nuts. // We set this to a big number like 1000 and decrement it // over time. The script gets knifed once this reaches zero. static int numTries; // number of registered memory functions (1 per hooked byte) static unsigned int numMemHooks; // Look in fceu.h for macros named like JOY_UP to determine the order. static const char *button_mappings[] = { "A", "B", "select", "start", "up", "down", "left", "right" }; static const char* luaCallIDStrings [] = { "CALL_BEFOREEMULATION", "CALL_AFTEREMULATION", "CALL_BEFOREEXIT", }; //make sure we have the right number of strings CTASSERT(sizeof(luaCallIDStrings)/sizeof(*luaCallIDStrings) == LUACALL_COUNT) static const char* luaMemHookTypeStrings [] = { "MEMHOOK_WRITE", "MEMHOOK_READ", "MEMHOOK_EXEC", "MEMHOOK_WRITE_SUB", "MEMHOOK_READ_SUB", "MEMHOOK_EXEC_SUB", }; //make sure we have the right number of strings CTASSERT(sizeof(luaMemHookTypeStrings)/sizeof(*luaMemHookTypeStrings) == LUAMEMHOOK_COUNT) /** * Resets emulator speed / pause states after script exit. */ static void FCEU_LuaOnStop() { luaRunning = FALSE; for (int i = 0 ; i < 4 ; i++ ){ luajoypads1[i]= 0xFF; // Set these back to pass-through luajoypads2[i]= 0x00; } gui_used = GUI_CLEAR; if (wasPaused && !FCEUI_EmulationPaused()) FCEUI_ToggleEmulationPause(); FCEUD_SetEmulationSpeed(EMUSPEED_NORMAL); //TODO: Ideally lua returns the speed to the speed the user set before running the script //rather than returning it to normal, and turbo off. Perhaps some flags and a FCEUD_GetEmulationSpeed function FCEUD_TurboOff(); //Turn off turbo } /** * Asks Lua if it wants control of the emulator's speed. * Returns 0 if no, 1 if yes. If yes, caller should also * consult FCEU_LuaFrameSkip(). */ int FCEU_LuaSpeed() { if (!L || !luaRunning) return 0; //printf("%d\n", speedmode); switch (speedmode) { case SPEED_NOTHROTTLE: case SPEED_TURBO: case SPEED_MAXIMUM: return 1; case SPEED_NORMAL: default: return 0; } } /** * Asks Lua if it wants control whether this frame is skipped. * Returns 0 if no, 1 if frame should be skipped, -1 if it should not be. */ int FCEU_LuaFrameSkip() { if (!L || !luaRunning) return 0; switch (speedmode) { case SPEED_NORMAL: return 0; case SPEED_NOTHROTTLE: return -1; case SPEED_TURBO: return 0; case SPEED_MAXIMUM: return 1; } return 0; } /** * Toggle certain rendering planes * emu.setrenderingplanes(sprites, background) * Accepts two (lua) boolean values and acts accordingly */ static int emu_setrenderplanes(lua_State *L) { bool sprites = (lua_toboolean( L, 1 ) == 1); bool background = (lua_toboolean( L, 2 ) == 1); FCEUI_SetRenderPlanes(sprites, background); return 0; } /////////////////////////// // emu.speedmode(string mode) // // Takes control of the emulation speed // of the system. Normal is normal speed (60fps, 50 for PAL), // nothrottle disables speed control but renders every frame, // turbo renders only a few frames in order to speed up emulation, // maximum renders no frames // TODO: better enforcement, done in the same way as basicbot... static int emu_speedmode(lua_State *L) { const char *mode = luaL_checkstring(L,1); if (strcasecmp(mode, "normal")==0) { speedmode = SPEED_NORMAL; } else if (strcasecmp(mode, "nothrottle")==0) { speedmode = SPEED_NOTHROTTLE; } else if (strcasecmp(mode, "turbo")==0) { speedmode = SPEED_TURBO; } else if (strcasecmp(mode, "maximum")==0) { speedmode = SPEED_MAXIMUM; } else luaL_error(L, "Invalid mode %s to emu.speedmode",mode); //printf("new speed mode: %d\n", speedmode); if (speedmode == SPEED_NORMAL) FCEUD_SetEmulationSpeed(EMUSPEED_NORMAL); else if (speedmode == SPEED_TURBO) //adelikat: Making turbo actually use turbo. FCEUD_TurboOn(); //Turbo and max speed are two different results. Turbo employs frame skipping and sound bypassing if mute turbo option is enabled. //This makes it faster but with frame skipping. Therefore, maximum is still a useful feature, in case the user is recording an avi or making screenshots (or something else that needs all frames) else FCEUD_SetEmulationSpeed(EMUSPEED_FASTEST); //TODO: Make nothrottle turn off throttle, or remove the option return 0; } // emu.poweron() // // Executes a power cycle static int emu_poweron(lua_State *L) { if (GameInfo) FCEUI_PowerNES(); return 0; } // emu.softreset() // // Executes a power cycle static int emu_softreset(lua_State *L) { if (GameInfo) FCEUI_ResetNES(); return 0; } // emu.frameadvance() // // Executes a frame advance. Occurs by yielding the coroutine, then re-running // when we break out. static int emu_frameadvance(lua_State *L) { // We're going to sleep for a frame-advance. Take notes. if (frameAdvanceWaiting) return luaL_error(L, "can't call emu.frameadvance() from here"); frameAdvanceWaiting = TRUE; // Now we can yield to the main return lua_yield(L, 0); // It's actually rather disappointing... } // emu.pause() // // Pauses the emulator, function "waits" until the user unpauses. // This function MAY be called from a non-frame boundary, but the frame // finishes executing anwyays. In this case, the function returns immediately. static int emu_pause(lua_State *L) { if (!FCEUI_EmulationPaused()) FCEUI_ToggleEmulationPause(); speedmode = SPEED_NORMAL; // Return control if we're midway through a frame. We can't pause here. if (frameAdvanceWaiting) { return 0; } // If it's on a frame boundary, we also yield. frameAdvanceWaiting = TRUE; return lua_yield(L, 0); } //emu.unpause() // //adelikat: Why wasn't this added sooner? //Gives the user a way to unpause the emulator via lua static int emu_unpause(lua_State *L) { if (FCEUI_EmulationPaused()) FCEUI_ToggleEmulationPause(); speedmode = SPEED_NORMAL; // Return control if we're midway through a frame. We can't pause here. if (frameAdvanceWaiting) { return 0; } // If it's on a frame boundary, we also yield. frameAdvanceWaiting = TRUE; return lua_yield(L, 0); } // emu.message(string msg) // // Displays the given message on the screen. static int emu_message(lua_State *L) { const char *msg = luaL_checkstring(L,1); FCEU_DispMessage("%s", msg); return 0; } static int emu_registerbefore(lua_State *L) { if (!lua_isnil(L,1)) luaL_checktype(L, 1, LUA_TFUNCTION); lua_settop(L,1); lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEMULATION]); lua_insert(L,1); lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEMULATION]); //StopScriptIfFinished(luaStateToUIDMap[L]); return 1; } static int emu_registerafter(lua_State *L) { if (!lua_isnil(L,1)) luaL_checktype(L, 1, LUA_TFUNCTION); lua_settop(L,1); lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTEREMULATION]); lua_insert(L,1); lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTEREMULATION]); //StopScriptIfFinished(luaStateToUIDMap[L]); return 1; } static int emu_registerexit(lua_State *L) { if (!lua_isnil(L,1)) luaL_checktype(L, 1, LUA_TFUNCTION); lua_settop(L,1); lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]); lua_insert(L,1); lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]); //StopScriptIfFinished(luaStateToUIDMap[L]); return 1; } static int rom_readbyte(lua_State *L) { lua_pushinteger(L, FCEU_ReadRomByte(luaL_checkinteger(L,1))); return 1; } static int rom_readbytesigned(lua_State *L) { lua_pushinteger(L, (signed char)FCEU_ReadRomByte(luaL_checkinteger(L,1))); return 1; } static int memory_readbyte(lua_State *L) { lua_pushinteger(L, FCEU_CheatGetByte(luaL_checkinteger(L,1))); return 1; } static int memory_writebyte(lua_State *L) { FCEU_CheatSetByte(luaL_checkinteger(L,1), luaL_checkinteger(L,2)); return 0; } static int memory_readbyterange(lua_State *L) { int range_start = luaL_checkinteger(L,1); int range_size = luaL_checkinteger(L,2); if(range_size < 0) return 0; char* buf = (char*)alloca(range_size); for(int i=0;ierrfunc || L->errorJmp) // luaL_error(L, "%s", lua_tostring(L,-1)); //else { lua_pushnil(L); lua_setfield(L, LUA_REGISTRYINDEX, guiCallbackTable); // Error? #ifdef WIN32 MessageBox( hAppWnd, lua_tostring(L,-1), "Lua run error", MB_OK | MB_ICONSTOP); #else fprintf(stderr, "Lua thread bombed out: %s\n", lua_tostring(L,-1)); #endif FCEU_LuaStop(); } } // the purpose of this structure is to provide a way of // QUICKLY determining whether a memory address range has a hook associated with it, // with a bias toward fast rejection because the majority of addresses will not be hooked. // (it must not use any part of Lua or perform any per-script operations, // otherwise it would definitely be too slow.) // calculating the regions when a hook is added/removed may be slow, // but this is an intentional tradeoff to obtain a high speed of checking during later execution struct TieredRegion { template struct Region { struct Island { unsigned int start; unsigned int end; __forceinline bool Contains(unsigned int address, int size) const { return address < end && address+size > start; } }; std::vector islands; void Calculate(const std::vector& bytes) { islands.clear(); unsigned int lastEnd = ~0; std::vector::const_iterator iter = bytes.begin(); std::vector::const_iterator end = bytes.end(); for(; iter != end; ++iter) { unsigned int addr = *iter; if(addr < lastEnd || addr > lastEnd + (long long)maxGap) { islands.push_back(Island()); islands.back().start = addr; } islands.back().end = addr+1; lastEnd = addr+1; } } bool Contains(unsigned int address, int size) const { for (size_t i = 0; i != islands.size(); ++i) { if (islands[i].Contains(address, size)) return true; } return false; } }; Region<0xFFFFFFFF> broad; Region<0x1000> mid; Region<0> narrow; void Calculate(std::vector& bytes) { std::sort(bytes.begin(), bytes.end()); broad.Calculate(bytes); mid.Calculate(bytes); narrow.Calculate(bytes); } TieredRegion() { std::vector temp; Calculate(temp); } __forceinline int NotEmpty() { return broad.islands.size(); } // note: it is illegal to call this if NotEmpty() returns 0 __forceinline bool Contains(unsigned int address, int size) { return broad.islands[0].Contains(address,size) && mid.Contains(address,size) && narrow.Contains(address,size); } }; TieredRegion hookedRegions [LUAMEMHOOK_COUNT]; static void CalculateMemHookRegions(LuaMemHookType hookType) { std::vector hookedBytes; // std::map::iterator iter = luaContextInfo.begin(); // std::map::iterator end = luaContextInfo.end(); // while(iter != end) // { // LuaContextInfo& info = *iter->second; if(/*info.*/ numMemHooks) { // lua_State* L = info.L; if(L) { lua_settop(L, 0); lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]); lua_pushnil(L); while(lua_next(L, -2)) { if(lua_isfunction(L, -1)) { unsigned int addr = lua_tointeger(L, -2); hookedBytes.push_back(addr); } lua_pop(L, 1); } lua_settop(L, 0); } } // ++iter; // } hookedRegions[hookType].Calculate(hookedBytes); } static void CallRegisteredLuaMemHook_LuaMatch(unsigned int address, int size, unsigned int value, LuaMemHookType hookType) { // std::map::iterator iter = luaContextInfo.begin(); // std::map::iterator end = luaContextInfo.end(); // while(iter != end) // { // LuaContextInfo& info = *iter->second; if(/*info.*/ numMemHooks) { // lua_State* L = info.L; if(L/* && !info.panic*/) { #ifdef USE_INFO_STACK infoStack.insert(infoStack.begin(), &info); struct Scope { ~Scope(){ infoStack.erase(infoStack.begin()); } } scope; #endif lua_settop(L, 0); lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]); for(int i = address; i != address+size; i++) { lua_rawgeti(L, -1, i); if (lua_isfunction(L, -1)) { bool wasRunning = (luaRunning!=0) /*info.running*/; luaRunning /*info.running*/ = true; //RefreshScriptSpeedStatus(); lua_pushinteger(L, address); lua_pushinteger(L, size); int errorcode = lua_pcall(L, 2, 0, 0); luaRunning /*info.running*/ = wasRunning; //RefreshScriptSpeedStatus(); if (errorcode) { HandleCallbackError(L); //int uid = iter->first; //HandleCallbackError(L,info,uid,true); } break; } else { lua_pop(L,1); } } lua_settop(L, 0); } } // ++iter; // } } void CallRegisteredLuaMemHook(unsigned int address, int size, unsigned int value, LuaMemHookType hookType) { // performance critical! (called VERY frequently) // I suggest timing a large number of calls to this function in Release if you change anything in here, // before and after, because even the most innocent change can make it become 30% to 400% slower. // a good amount to test is: 100000000 calls with no hook set, and another 100000000 with a hook set. // (on my system that consistently took 200 ms total in the former case and 350 ms total in the latter case) if(hookedRegions[hookType].NotEmpty()) { //if((hookType <= LUAMEMHOOK_EXEC) && (address >= 0xE00000)) // address |= 0xFF0000; // account for mirroring of RAM if(hookedRegions[hookType].Contains(address, size)) CallRegisteredLuaMemHook_LuaMatch(address, size, value, hookType); // something has hooked this specific address } } void CallRegisteredLuaFunctions(LuaCallID calltype) { assert((unsigned int)calltype < (unsigned int)LUACALL_COUNT); const char* idstring = luaCallIDStrings[calltype]; if (!L) return; lua_settop(L, 0); lua_getfield(L, LUA_REGISTRYINDEX, idstring); int errorcode = 0; if (lua_isfunction(L, -1)) { errorcode = lua_pcall(L, 0, 0, 0); if (errorcode) HandleCallbackError(L); } else { lua_pop(L, 1); } } // Not for the signed versions though static int memory_readbytesigned(lua_State *L) { signed char c = (signed char) FCEU_CheatGetByte(luaL_checkinteger(L,1)); lua_pushinteger(L, c); return 1; } static int memory_registerHook(lua_State* L, LuaMemHookType hookType, int defaultSize) { // get first argument: address unsigned int addr = luaL_checkinteger(L,1); if((addr & ~0xFFFFFF) == ~0xFFFFFF) addr &= 0xFFFFFF; // get optional second argument: size int size = defaultSize; int funcIdx = 2; if(lua_isnumber(L,2)) { size = luaL_checkinteger(L,2); if(size < 0) { size = -size; addr -= size; } funcIdx++; } // check last argument: callback function bool clearing = lua_isnil(L,funcIdx); if(!clearing) luaL_checktype(L, funcIdx, LUA_TFUNCTION); lua_settop(L,funcIdx); // get the address-to-callback table for this hook type of the current script lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]); // count how many callback functions we'll be displacing int numFuncsAfter = clearing ? 0 : size; int numFuncsBefore = 0; for(unsigned int i = addr; i != addr+size; i++) { lua_rawgeti(L, -1, i); if(lua_isfunction(L, -1)) numFuncsBefore++; lua_pop(L,1); } // put the callback function in the address slots for(unsigned int i = addr; i != addr+size; i++) { lua_pushvalue(L, -2); lua_rawseti(L, -2, i); } // adjust the count of active hooks //LuaContextInfo& info = GetCurrentInfo(); /*info.*/ numMemHooks += numFuncsAfter - numFuncsBefore; // re-cache regions of hooked memory across all scripts CalculateMemHookRegions(hookType); //StopScriptIfFinished(luaStateToUIDMap[L]); return 0; } LuaMemHookType MatchHookTypeToCPU(lua_State* L, LuaMemHookType hookType) { int cpuID = 0; int cpunameIndex = 0; if(lua_type(L,2) == LUA_TSTRING) cpunameIndex = 2; else if(lua_type(L,3) == LUA_TSTRING) cpunameIndex = 3; if(cpunameIndex) { const char* cpuName = lua_tostring(L, cpunameIndex); if(!stricmp(cpuName, "sub")) cpuID = 1; lua_remove(L, cpunameIndex); } switch(cpuID) { case 0: return hookType; case 1: switch(hookType) { case LUAMEMHOOK_WRITE: return LUAMEMHOOK_WRITE_SUB; case LUAMEMHOOK_READ: return LUAMEMHOOK_READ_SUB; case LUAMEMHOOK_EXEC: return LUAMEMHOOK_EXEC_SUB; } } return hookType; } static int memory_registerwrite(lua_State *L) { return memory_registerHook(L, MatchHookTypeToCPU(L,LUAMEMHOOK_WRITE), 1); } static int memory_registerread(lua_State *L) { return memory_registerHook(L, MatchHookTypeToCPU(L,LUAMEMHOOK_READ), 1); } static int memory_registerexec(lua_State *L) { return memory_registerHook(L, MatchHookTypeToCPU(L,LUAMEMHOOK_EXEC), 2); } //adelikat: table pulled from GENS. credz nitsuja! #ifdef _WIN32 const char* s_keyToName[256] = { NULL, "leftclick", "rightclick", NULL, "middleclick", NULL, NULL, NULL, "backspace", "tab", NULL, NULL, NULL, "enter", NULL, NULL, "shift", // 0x10 "control", "alt", "pause", "capslock", NULL, NULL, NULL, NULL, NULL, NULL, "escape", NULL, NULL, NULL, NULL, "space", // 0x20 "pageup", "pagedown", "end", "home", "left", "up", "right", "down", NULL, NULL, NULL, NULL, "insert", "delete", NULL, "0","1","2","3","4","5","6","7","8","9", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "A","B","C","D","E","F","G","H","I","J", "K","L","M","N","O","P","Q","R","S","T", "U","V","W","X","Y","Z", NULL, NULL, NULL, NULL, NULL, "numpad0","numpad1","numpad2","numpad3","numpad4","numpad5","numpad6","numpad7","numpad8","numpad9", "numpad*","numpad+", NULL, "numpad-","numpad.","numpad/", "F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12", "F13","F14","F15","F16","F17","F18","F19","F20","F21","F22","F23","F24", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "numlock", "scrolllock", NULL, // 0x92 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 0xB9 "semicolon", "plus", "comma", "minus", "period", "slash", "tilde", NULL, // 0xC1 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 0xDA "leftbracket", "backslash", "rightbracket", "quote", }; #endif //adelikat - the code for the keys is copied directly from GENS. Props to nitsuja // the code for the mouse is simply the same code from zapper.get // input.get() // takes no input, returns a lua table of entries representing the current input state, // independent of the joypad buttons the emulated game thinks are pressed // for example: // if the user is holding the W key and the left mouse button // and has the mouse at the bottom-right corner of the game screen, // then this would return {W=true, leftclick=true, xmouse=319, ymouse=223} static int input_get(lua_State *L) { lua_newtable(L); #ifdef _WIN32 // keyboard and mouse button status { extern int EnableBackgroundInput; unsigned char keys [256]; if(!EnableBackgroundInput) { if(GetKeyboardState(keys)) { for(int i = 1; i < 255; i++) { int mask = (i == VK_CAPITAL || i == VK_NUMLOCK || i == VK_SCROLL) ? 0x01 : 0x80; if(keys[i] & mask) { const char* name = s_keyToName[i]; if(name) { lua_pushboolean(L, true); lua_setfield(L, -2, name); } } } } } else // use a slightly different method that will detect background input: { for(int i = 1; i < 255; i++) { const char* name = s_keyToName[i]; if(name) { int active; if(i == VK_CAPITAL || i == VK_NUMLOCK || i == VK_SCROLL) active = GetKeyState(i) & 0x01; else active = GetAsyncKeyState(i) & 0x8000; if(active) { lua_pushboolean(L, true); lua_setfield(L, -2, name); } } } } } // mouse position in game screen pixel coordinates extern void GetMouseData(uint32 (&md)[3]); uint32 MouseData[3]; GetMouseData (MouseData); int x = MouseData[0]; int y = MouseData[1]; int click = MouseData[2]; ///adelikat TODO: remove the ability to store the value 2? Since 2 is right-clicking and not part of zapper input and is used for context menus lua_pushinteger(L, x); lua_setfield(L, -2, "xmouse"); lua_pushinteger(L, y); lua_setfield(L, -2, "ymouse"); lua_pushinteger(L, click); lua_setfield(L, -2, "click"); #else // NYI (well, return an empty table) #endif return 1; } // table zapper.read //int which unecessary because zapper is always controller 2 //Reads the zapper coordinates and a click value (1 if clicked, 0 if not, 2 if right click (but this is not used for zapper input) static int zapper_read(lua_State *L){ lua_newtable(L); extern void GetMouseData(uint32 (&md)[3]); uint32 MouseData[3]; GetMouseData (MouseData); int x = MouseData[0]; int y = MouseData[1]; int click = MouseData[2]; ///adelikat TODO: remove the ability to store the value 2? Since 2 is right-clicking and not part of zapper input and is used for context menus lua_pushinteger(L, x); lua_setfield(L, -2, "xmouse"); lua_pushinteger(L, y); lua_setfield(L, -2, "ymouse"); lua_pushinteger(L, click); lua_setfield(L, -2, "click"); return 1; } // table joypad.read(int which = 1) // // Reads the joypads as inputted by the user. // This is really the only way to get input to the system. // TODO: Don't read in *everything*... static int joypad_read(lua_State *L) { // Reads the joypads as inputted by the user int which = luaL_checkinteger(L,1); if (which < 1 || which > 4) { luaL_error(L,"Invalid input port (valid range 1-4, specified %d)", which); } // Use the OS-specific code to do the reading. extern void FCEUD_UpdateInput(void); FCEUD_UpdateInput(); extern SFORMAT FCEUCTRL_STATEINFO[]; uint8 buttons = ((uint8 *) FCEUCTRL_STATEINFO[1].v)[which - 1]; lua_newtable(L); int i; for (i = 0; i < 8; i++) { if (buttons & (1< 4) { luaL_error(L,"Invalid output port (valid range 1-4, specified %d)", which); } // And the table of buttons. luaL_checktype(L,2,LUA_TTABLE); // Set up for taking control of the indicated controller luajoypads1[which-1] = 0xFF; // .1 Reset right bit luajoypads2[which-1] = 0x00; // 0. Reset left bit int i; for (i=0; i < 8; i++) { lua_getfield(L, 2, button_mappings[i]); //Button is not nil, so find out if it is true/false if (!lua_isnil(L,-1)) { if (lua_toboolean(L,-1)) //True or string luajoypads2[which-1] |= 1 << i; if (lua_toboolean(L,-1) == 0 || lua_isstring(L,-1)) //False or string luajoypads1[which-1] &= ~(1 << i); } else { } lua_pop(L,1); } return 0; } // //// Helper function to convert a savestate object to the filename it represents. //static char *savestateobj2filename(lua_State *L, int offset) { // // // First we get the metatable of the indicated object // int result = lua_getmetatable(L, offset); // // if (!result) // luaL_error(L, "object not a savestate object"); // // // Also check that the type entry is set // lua_getfield(L, -1, "__metatable"); // if (strcmp(lua_tostring(L,-1), "FCEU Savestate") != 0) // luaL_error(L, "object not a savestate object"); // lua_pop(L,1); // // // Now, get the field we want // lua_getfield(L, -1, "filename"); // // // Return it // return (char *) lua_tostring(L, -1); //} // // Helper function for garbage collection. static int savestate_gc(lua_State *L) { LuaSaveState *ss = (LuaSaveState *)lua_touserdata(L, 1); if(ss->persisted && ss->anonymous) remove(ss->filename.c_str()); ss->~LuaSaveState(); //// The object we're collecting is on top of the stack //lua_getmetatable(L,1); // //// Get the filename //const char *filename; //lua_getfield(L, -1, "filename"); //filename = lua_tostring(L,-1); //// Delete the file //remove(filename); // // We exit, and the garbage collector takes care of the rest. return 0; } // object savestate.create(int which = nil) // // Creates an object used for savestates. // The object can be associated with a player-accessible savestate // ("which" between 1 and 10) or not (which == nil). static int savestate_create(lua_State *L) { int which = -1; if (lua_gettop(L) >= 1) { which = luaL_checkinteger(L, 1); if (which < 1 || which > 10) { luaL_error(L, "invalid player's savestate %d", which); } } //lets use lua to allocate the memory, since it is effectively a memory pool. LuaSaveState *ss = new(lua_newuserdata(L,sizeof(LuaSaveState))) LuaSaveState(); if (which > 0) { // Find an appropriate filename. This is OS specific, unfortunately. // So I turned the filename selection code into my bitch. :) // Numbers are 0 through 9 though. ss->filename = FCEU_MakeFName(FCEUMKF_STATE, which - 1, 0); } else { //char tempbuf[100] = "snluaXXXXXX"; //filename = mktemp(tempbuf); //doesnt work -^ //mbg 8/13/08 - this needs to be this way. we'll make a better system later: ss->filename = tempnam(NULL, "snlua"); ss->anonymous = true; } // The metatable we use, protected from Lua and contains garbage collection info and stuff. lua_newtable(L); //// First, we must protect it lua_pushstring(L, "FCEU Savestate"); lua_setfield(L, -2, "__metatable"); // // //// Now we need to save the file itself. //lua_pushstring(L, filename.c_str()); //lua_setfield(L, -2, "filename"); // If it's an anonymous savestate, we must delete the file from disk should it be gargage collected //if (which < 0) { lua_pushcfunction(L, savestate_gc); lua_setfield(L, -2, "__gc"); //} // Set the metatable lua_setmetatable(L, -2); // Awesome. Return the object return 1; } // savestate.save(object state) // // Saves a state to the given object. static int savestate_save(lua_State *L) { //char *filename = savestateobj2filename(L,1); LuaSaveState *ss = (LuaSaveState *)lua_touserdata(L, 1); if(ss->data) delete ss->data; ss->data = new memorystream(); // printf("saving %s\n", filename); // Save states are very expensive. They take time. numTries--; FCEUSS_SaveMS(ss->data,Z_NO_COMPRESSION); ss->data->sync(); return 0; } static int savestate_persist(lua_State *L) { LuaSaveState *ss = (LuaSaveState *)lua_touserdata(L, 1); ss->persist(); return 0; } // savestate.load(object state) // // Loads the given state static int savestate_load(lua_State *L) { //char *filename = savestateobj2filename(L,1); LuaSaveState *ss = (LuaSaveState *)lua_touserdata(L, 1); numTries--; FCEUSS_LoadFP(ss->data,SSLOADPARAM_NOBACKUP); ss->data->seekg(0); return 0; } // int emu.framecount() // // Gets the frame counter int emu_framecount(lua_State *L) { lua_pushinteger(L, FCEUMOV_GetFrame()); return 1; } //int emu.lagcount() // // Gets the current lag count int emu_lagcount(lua_State *L) { lua_pushinteger(L, FCEUI_GetLagCount()); return 1; } //emu_lagged() // //Returns true if the game is currently on a lag frame int emu_lagged (lua_State *L) { bool Lag_Frame = FCEUI_GetLagged(); lua_pushboolean(L, Lag_Frame); return 1; } // string movie.mode() // // "record", "playback" or nil int movie_mode(lua_State *L) { if (FCEUMOV_IsRecording()) lua_pushstring(L, "record"); else if (FCEUMOV_IsPlaying()) lua_pushstring(L, "playback"); else lua_pushnil(L); return 1; } static int movie_rerecordcounting(lua_State *L) { if (lua_gettop(L) == 0) luaL_error(L, "no parameters specified"); skipRerecords = lua_toboolean(L,1); return 0; } // movie.stop() // // Stops movie playback/recording. Bombs out if movie is not running. static int movie_stop(lua_State *L) { if (!FCEUMOV_IsRecording() && !FCEUMOV_IsPlaying()) luaL_error(L, "no movie"); FCEUI_StopMovie(); return 0; } // movie.active() // //returns a bool value is there is a movie currently open int movie_isactive (lua_State *L) { bool movieactive = (FCEUMOV_IsRecording() || FCEUMOV_IsPlaying()); lua_pushboolean(L, movieactive); return 1; } // movie.recording() int movie_isrecording (lua_State *L) { lua_pushboolean(L, FCEUMOV_IsRecording()); return 1; } // movie.playing() int movie_isplaying (lua_State *L) { lua_pushboolean(L, FCEUMOV_IsPlaying()); return 1; } //movie.rerecordcount() // //returns the rerecord count of the current movie static int movie_rerecordcount (lua_State *L) { if (!FCEUMOV_IsRecording() && !FCEUMOV_IsPlaying()) luaL_error(L, "No movie loaded."); lua_pushinteger(L, FCEUI_GetMovieRerecordCount()); return 1; } //movie.length() // //returns an int value representing the total length of the current movie loaded static int movie_getlength (lua_State *L) { if (!FCEUMOV_IsRecording() && !FCEUMOV_IsPlaying()) luaL_error(L, "No movie loaded."); lua_pushinteger(L, FCEUI_GetMovieLength()); return 1; } //movie.readonly // //returns true is emulator is in read-only mode, false if it is in read+write static int movie_getreadonly (lua_State *L) { lua_pushboolean(L, FCEUI_GetMovieToggleReadOnly()); return 1; } //movie.setreadonly // //Sets readonly / read+write status static int movie_setreadonly (lua_State *L) { bool which = (lua_toboolean( L, 1 ) == 1); FCEUI_SetMovieToggleReadOnly(which); return 0; } //movie.getname // //returns the filename of the movie loaded static int movie_getname (lua_State *L) { if (!FCEUMOV_IsRecording() && !FCEUMOV_IsPlaying()) luaL_error(L, "No movie loaded."); std::string name = FCEUI_GetMovieName(); lua_pushstring(L, name.c_str()); return 1; } //movie.replay // //calls the play movie from beginning function static int movie_replay (lua_State *L) { FCEUI_MoviePlayFromBeginning(); return 0; } #define LUA_SCREEN_WIDTH 256 #define LUA_SCREEN_HEIGHT 224 // Common code by the gui library: make sure the screen array is ready static void gui_prepare() { if (!gui_data) gui_data = (uint8*) malloc(LUA_SCREEN_WIDTH*LUA_SCREEN_HEIGHT*4); if (gui_used != GUI_USED_SINCE_LAST_DISPLAY) memset(gui_data, 0, LUA_SCREEN_WIDTH*LUA_SCREEN_HEIGHT*4); gui_used = GUI_USED_SINCE_LAST_DISPLAY; } // pixform for lua graphics #define BUILD_PIXEL_ARGB8888(A,R,G,B) (((int) (A) << 24) | ((int) (R) << 16) | ((int) (G) << 8) | (int) (B)) #define DECOMPOSE_PIXEL_ARGB8888(PIX,A,R,G,B) { (A) = ((PIX) >> 24) & 0xff; (R) = ((PIX) >> 16) & 0xff; (G) = ((PIX) >> 8) & 0xff; (B) = (PIX) & 0xff; } #define LUA_BUILD_PIXEL BUILD_PIXEL_ARGB8888 #define LUA_DECOMPOSE_PIXEL DECOMPOSE_PIXEL_ARGB8888 template static void swap(T &one, T &two) { T temp = one; one = two; two = temp; } // write a pixel to buffer static inline void blend32(uint32 *dstPixel, uint32 colour) { uint8 *dst = (uint8*) dstPixel; int a, r, g, b; LUA_DECOMPOSE_PIXEL(colour, a, r, g, b); if (a == 255 || dst[3] == 0) { // direct copy *(uint32*)(dst) = colour; } else if (a == 0) { // do not copy } else { // alpha-blending int a_dst = ((255 - a) * dst[3] + 128) / 255; int a_new = a + a_dst; dst[0] = (uint8) ((( dst[0] * a_dst + b * a) + (a_new / 2)) / a_new); dst[1] = (uint8) ((( dst[1] * a_dst + g * a) + (a_new / 2)) / a_new); dst[2] = (uint8) ((( dst[2] * a_dst + r * a) + (a_new / 2)) / a_new); dst[3] = (uint8) a_new; } } // check if a pixel is in the lua canvas static inline bool gui_check_boundary(int x, int y) { return !(x < 0 || x >= LUA_SCREEN_WIDTH || y < 0 || y >= LUA_SCREEN_HEIGHT); } // write a pixel to gui_data (do not check boundaries for speedup) static inline void gui_drawpixel_fast(int x, int y, uint32 colour) { //gui_prepare(); blend32((uint32*) &gui_data[(y*LUA_SCREEN_WIDTH+x)*4], colour); } // write a pixel to gui_data (check boundaries) static inline void gui_drawpixel_internal(int x, int y, uint32 colour) { //gui_prepare(); if (gui_check_boundary(x, y)) gui_drawpixel_fast(x, y, colour); } // draw a line on gui_data (checks boundaries) static void gui_drawline_internal(int x1, int y1, int x2, int y2, bool lastPixel, uint32 colour) { //gui_prepare(); // Note: New version of Bresenham's Line Algorithm // http://groups.google.co.jp/group/rec.games.roguelike.development/browse_thread/thread/345f4c42c3b25858/29e07a3af3a450e6?show_docid=29e07a3af3a450e6 int swappedx = 0; int swappedy = 0; int xtemp = x1-x2; int ytemp = y1-y2; if (xtemp == 0 && ytemp == 0) { gui_drawpixel_internal(x1, y1, colour); return; } if (xtemp < 0) { xtemp = -xtemp; swappedx = 1; } if (ytemp < 0) { ytemp = -ytemp; swappedy = 1; } int delta_x = xtemp << 1; int delta_y = ytemp << 1; signed char ix = x1 > x2?1:-1; signed char iy = y1 > y2?1:-1; if (lastPixel) gui_drawpixel_internal(x2, y2, colour); if (delta_x >= delta_y) { int error = delta_y - (delta_x >> 1); while (x2 != x1) { if (error == 0 && !swappedx) gui_drawpixel_internal(x2+ix, y2, colour); if (error >= 0) { if (error || (ix > 0)) { y2 += iy; error -= delta_x; } } x2 += ix; gui_drawpixel_internal(x2, y2, colour); if (error == 0 && swappedx) gui_drawpixel_internal(x2, y2+iy, colour); error += delta_y; } } else { int error = delta_x - (delta_y >> 1); while (y2 != y1) { if (error == 0 && !swappedy) gui_drawpixel_internal(x2, y2+iy, colour); if (error >= 0) { if (error || (iy > 0)) { x2 += ix; error -= delta_y; } } y2 += iy; gui_drawpixel_internal(x2, y2, colour); if (error == 0 && swappedy) gui_drawpixel_internal(x2+ix, y2, colour); error += delta_x; } } } // draw a rect on gui_data static void gui_drawbox_internal(int x1, int y1, int x2, int y2, uint32 colour) { if (x1 > x2) swap(x1, x2); if (y1 > y2) swap(y1, y2); if (x1 < 0) x1 = -1; if (y1 < 0) y1 = -1; if (x2 >= LUA_SCREEN_WIDTH) x2 = LUA_SCREEN_WIDTH; if (y2 >= LUA_SCREEN_HEIGHT) y2 = LUA_SCREEN_HEIGHT; //gui_prepare(); gui_drawline_internal(x1, y1, x2, y1, true, colour); gui_drawline_internal(x1, y2, x2, y2, true, colour); gui_drawline_internal(x1, y1, x1, y2, true, colour); gui_drawline_internal(x2, y1, x2, y2, true, colour); } enum { GUI_COLOUR_CLEAR /* , GUI_COLOUR_WHITE, GUI_COLOUR_BLACK, GUI_COLOUR_GREY , GUI_COLOUR_RED, GUI_COLOUR_GREEN, GUI_COLOUR_BLUE */ }; /** * Returns an index approximating an RGB colour. * TODO: This is easily improvable in terms of speed and probably * quality of matches. (gd overlay & transparency code call it a lot.) * With effort we could also cheat and map indices 0x08 .. 0x3F * ourselves. */ static uint8 gui_colour_rgb(uint8 r, uint8 g, uint8 b) { static uint8 index_lookup[1 << (3+3+3)]; int k; if (!gui_saw_current_palette) { memset(index_lookup, GUI_COLOUR_CLEAR, sizeof(index_lookup)); gui_saw_current_palette = TRUE; } k = ((r & 0xE0) << 1) | ((g & 0xE0) >> 2) | ((b & 0xE0) >> 5); uint16 test, best = GUI_COLOUR_CLEAR; uint32 best_score = 0xffffffffu, test_score; if (index_lookup[k] != GUI_COLOUR_CLEAR) return index_lookup[k]; for (test = 0; test < 0xff; test++) { uint8 tr, tg, tb; if (test == GUI_COLOUR_CLEAR) continue; FCEUD_GetPalette(test, &tr, &tg, &tb); test_score = abs(r - tr) * 66 + abs(g - tg) * 129 + abs(b - tb) * 25; if (test_score < best_score) best_score = test_score, best = test; } index_lookup[k] = best; return best; } void FCEU_LuaUpdatePalette() { gui_saw_current_palette = FALSE; } // Helper for a simple hex parser static int hex2int(lua_State *L, char c) { if (c >= '0' && c <= '9') return c-'0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return luaL_error(L, "invalid hex in colour"); } static const struct ColorMapping { const char* name; int value; } s_colorMapping [] = { {"white", 0xFFFFFFFF}, {"black", 0x000000FF}, {"clear", 0x00000000}, {"gray", 0x7F7F7FFF}, {"grey", 0x7F7F7FFF}, {"red", 0xFF0000FF}, {"orange", 0xFF7F00FF}, {"yellow", 0xFFFF00FF}, {"chartreuse",0x7FFF00FF}, {"green", 0x00FF00FF}, {"teal", 0x00FF7FFF}, {"cyan" , 0x00FFFFFF}, {"blue", 0x0000FFFF}, {"purple", 0x7F00FFFF}, {"magenta", 0xFF00FFFF}, }; /** * Converts an integer or a string on the stack at the given * offset to a RGB32 colour. Several encodings are supported. * The user may construct their own RGB value, given a simple colour name, * or an HTML-style "#09abcd" colour. 16 bit reduction doesn't occur at this time. */ static inline bool str2colour(uint32 *colour, lua_State *L, const char *str) { if (str[0] == '#') { int color; sscanf(str+1, "%X", &color); int len = strlen(str+1); int missing = std::max(0, 8-len); color <<= missing << 2; if(missing >= 2) color |= 0xFF; *colour = color; return true; } else { if(!strnicmp(str, "rand", 4)) { *colour = ((rand()*255/RAND_MAX) << 8) | ((rand()*255/RAND_MAX) << 16) | ((rand()*255/RAND_MAX) << 24) | 0xFF; return true; } for(int i = 0; i < sizeof(s_colorMapping)/sizeof(*s_colorMapping); i++) { if(!stricmp(str,s_colorMapping[i].name)) { *colour = s_colorMapping[i].value; return true; } } } return false; } static inline uint32 gui_getcolour_wrapped(lua_State *L, int offset, bool hasDefaultValue, uint32 defaultColour) { switch (lua_type(L,offset)) { case LUA_TSTRING: { const char *str = lua_tostring(L,offset); uint32 colour; if (str2colour(&colour, L, str)) return colour; else { if (hasDefaultValue) return defaultColour; else return luaL_error(L, "unknown colour %s", str); } } case LUA_TNUMBER: { uint32 colour = (uint32) lua_tointeger(L,offset); return colour; } default: if (hasDefaultValue) return defaultColour; else return luaL_error(L, "invalid colour"); } } static uint32 gui_getcolour(lua_State *L, int offset) { uint32 colour; int a, r, g, b; colour = gui_getcolour_wrapped(L, offset, false, 0); a = ((colour & 0xff) * transparencyModifier) / 255; if (a > 255) a = 255; b = (colour >> 8) & 0xff; g = (colour >> 16) & 0xff; r = (colour >> 24) & 0xff; return LUA_BUILD_PIXEL(a, r, g, b); } static uint32 gui_optcolour(lua_State *L, int offset, uint32 defaultColour) { uint32 colour; int a, r, g, b; uint8 defA, defB, defG, defR; LUA_DECOMPOSE_PIXEL(defaultColour, defA, defR, defG, defB); defaultColour = (defR << 24) | (defG << 16) | (defB << 8) | defA; colour = gui_getcolour_wrapped(L, offset, true, defaultColour); a = ((colour & 0xff) * transparencyModifier) / 255; if (a > 255) a = 255; b = (colour >> 8) & 0xff; g = (colour >> 16) & 0xff; r = (colour >> 24) & 0xff; return LUA_BUILD_PIXEL(a, r, g, b); } // gui.pixel(x,y,colour) static int gui_pixel(lua_State *L) { int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L,2); uint32 colour = gui_getcolour(L,3); // if (!gui_check_boundary(x, y)) // luaL_error(L,"bad coordinates"); gui_prepare(); gui_drawpixel_internal(x, y, colour); return 0; } // gui.line(x1,y1,x2,y2,type colour) static int gui_line(lua_State *L) { int x1,y1,x2,y2; uint32 colour; x1 = luaL_checkinteger(L,1); y1 = luaL_checkinteger(L,2); x2 = luaL_checkinteger(L,3); y2 = luaL_checkinteger(L,4); colour = gui_getcolour(L,5); // if (!gui_check_boundary(x1, y1)) // luaL_error(L,"bad coordinates"); // // if (!gui_check_boundary(x2, y2)) // luaL_error(L,"bad coordinates"); gui_prepare(); gui_drawline_internal(x1, y1, x2, y2, true, colour); return 0; } // gui.box(x1, y1, x2, y2, colour) static int gui_box(lua_State *L) { int x1,y1,x2,y2; uint32 colour; x1 = luaL_checkinteger(L,1); y1 = luaL_checkinteger(L,2); x2 = luaL_checkinteger(L,3); y2 = luaL_checkinteger(L,4); colour = gui_getcolour(L,5); // if (!gui_check_boundary(x1, y1)) // luaL_error(L,"bad coordinates"); // // if (!gui_check_boundary(x2, y2)) // luaL_error(L,"bad coordinates"); gui_prepare(); gui_drawbox_internal(x1, y1, x2, y2, colour); return 0; } // gui.gdscreenshot() // // Returns a screen shot as a string in gd's v1 file format. // This allows us to make screen shots available without gd installed locally. // Users can also just grab pixels via substring selection. // // I think... Does lua support grabbing byte values from a string? // Well, either way, just install gd and do what you like with it. // It really is easier that way. // gui.gdscreenshot() // // Returns a screen shot as a string in gd's v1 file format. // This allows us to make screen shots available without gd installed locally. // Users can also just grab pixels via substring selection. // // I think... Does lua support grabbing byte values from a string? // yes, string.byte(str,offset) // Well, either way, just install gd and do what you like with it. // It really is easier that way. // example: gd.createFromGdStr(gui.gdscreenshot()):png("outputimage.png") static int gui_gdscreenshot(lua_State *L) { int width = LUA_SCREEN_WIDTH; int height = LUA_SCREEN_HEIGHT; int size = 11 + width * height * 4; char* str = new char[size+1]; str[size] = 0; unsigned char* ptr = (unsigned char*)str; // GD format header for truecolor image (11 bytes) *ptr++ = (65534 >> 8) & 0xFF; *ptr++ = (65534 ) & 0xFF; *ptr++ = (width >> 8) & 0xFF; *ptr++ = (width ) & 0xFF; *ptr++ = (height >> 8) & 0xFF; *ptr++ = (height ) & 0xFF; *ptr++ = 1; *ptr++ = 255; *ptr++ = 255; *ptr++ = 255; *ptr++ = 255; for (int y=0; y < height; y++) { for (int x=0; x < width; x++) { uint8 index = XBuf[(y+8)*256 + x]; // Write A,R,G,B (alpha=0 for us): *ptr = 0; FCEUD_GetPalette(index, ptr + 1, ptr + 2, ptr + 3); ptr += 4; } } lua_pushlstring(L, str, size); delete[] str; return 1; } // gui.opacity(number alphaValue) // sets the transparency of subsequent draw calls // 0.0 is completely transparent, 1.0 is completely opaque // non-integer values are supported and meaningful, as are values greater than 1.0 // it is not necessary to use this function to get transparency (or the less-recommended gui.transparency() either), // because you can provide an alpha value in the color argument of each draw call. // however, it can be convenient to be able to globally modify the drawing transparency static int gui_setopacity(lua_State *L) { double opacF = luaL_checknumber(L,1); transparencyModifier = (int) (opacF * 255); if (transparencyModifier < 0) transparencyModifier = 0; return 0; } // gui.transparency(int strength) // // 0 = solid, static int gui_transparency(lua_State *L) { double trans = luaL_checknumber(L,1); transparencyModifier = (int) ((4.0 - trans) / 4.0 * 255); if (transparencyModifier < 0) transparencyModifier = 0; return 0; } static const uint32 Small_Font_Data[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 32 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000000, 0x00000700, 0x00000000, // 33 ! 0x00000000, 0x00040002, 0x00050003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 34 " 0x00000000, 0x00040002, 0x00050403, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 35 # 0x00000000, 0x00040300, 0x00000403, 0x00000500, 0x00070600, 0x00000706, 0x00000000, // 36 $ 0x00000000, 0x00000002, 0x00050000, 0x00000500, 0x00000005, 0x00080000, 0x00000000, // 37 % 0x00000000, 0x00000300, 0x00050003, 0x00000500, 0x00070005, 0x00080700, 0x00000000, // 38 & 0x00000000, 0x00000300, 0x00000400, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 39 ' 0x00000000, 0x00000300, 0x00000003, 0x00000004, 0x00000005, 0x00000700, 0x00000000, // 40 ( 0x00000000, 0x00000300, 0x00050000, 0x00060000, 0x00070000, 0x00000700, 0x00000000, // 41 ) 0x00000000, 0x00000000, 0x00000400, 0x00060504, 0x00000600, 0x00080006, 0x00000000, // 42 * 0x00000000, 0x00000000, 0x00000400, 0x00060504, 0x00000600, 0x00000000, 0x00000000, // 43 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000600, 0x00000700, 0x00000007, // 44 , 0x00000000, 0x00000000, 0x00000000, 0x00060504, 0x00000000, 0x00000000, 0x00000000, // 45 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000700, 0x00000000, // 46 . 0x00030000, 0x00040000, 0x00000400, 0x00000500, 0x00000005, 0x00000006, 0x00000000, // 47 / 0x00000000, 0x00000300, 0x00050003, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 48 0 0x00000000, 0x00000300, 0x00000403, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 49 1 0x00000000, 0x00000302, 0x00050000, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 50 2 0x00000000, 0x00000302, 0x00050000, 0x00000504, 0x00070000, 0x00000706, 0x00000000, // 51 3 0x00000000, 0x00000300, 0x00000003, 0x00060004, 0x00070605, 0x00080000, 0x00000000, // 52 4 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00070000, 0x00000706, 0x00000000, // 53 5 0x00000000, 0x00000300, 0x00000003, 0x00000504, 0x00070005, 0x00000700, 0x00000000, // 54 6 0x00000000, 0x00040302, 0x00050000, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 55 7 0x00000000, 0x00000300, 0x00050003, 0x00000500, 0x00070005, 0x00000700, 0x00000000, // 56 8 0x00000000, 0x00000300, 0x00050003, 0x00060500, 0x00070000, 0x00000700, 0x00000000, // 57 9 0x00000000, 0x00000000, 0x00000400, 0x00000000, 0x00000000, 0x00000700, 0x00000000, // 58 : 0x00000000, 0x00000000, 0x00000000, 0x00000500, 0x00000000, 0x00000700, 0x00000007, // 59 ; 0x00000000, 0x00040000, 0x00000400, 0x00000004, 0x00000600, 0x00080000, 0x00000000, // 60 < 0x00000000, 0x00000000, 0x00050403, 0x00000000, 0x00070605, 0x00000000, 0x00000000, // 61 = 0x00000000, 0x00000002, 0x00000400, 0x00060000, 0x00000600, 0x00000006, 0x00000000, // 62 > 0x00000000, 0x00000302, 0x00050000, 0x00000500, 0x00000000, 0x00000700, 0x00000000, // 63 ? 0x00000000, 0x00000300, 0x00050400, 0x00060004, 0x00070600, 0x00000000, 0x00000000, // 64 @ 0x00000000, 0x00000300, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 65 A 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00070005, 0x00000706, 0x00000000, // 66 B 0x00000000, 0x00040300, 0x00000003, 0x00000004, 0x00000005, 0x00080700, 0x00000000, // 67 C 0x00000000, 0x00000302, 0x00050003, 0x00060004, 0x00070005, 0x00000706, 0x00000000, // 68 D 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00000005, 0x00080706, 0x00000000, // 69 E 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 70 F 0x00000000, 0x00040300, 0x00000003, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 71 G 0x00000000, 0x00040002, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 72 H 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 73 I 0x00000000, 0x00040000, 0x00050000, 0x00060000, 0x00070005, 0x00000700, 0x00000000, // 74 J 0x00000000, 0x00040002, 0x00050003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 75 K 0x00000000, 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00080706, 0x00000000, // 76 l 0x00000000, 0x00040002, 0x00050403, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 77 M 0x00000000, 0x00000302, 0x00050003, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 78 N 0x00000000, 0x00040302, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00000000, // 79 O 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 80 P 0x00000000, 0x00040302, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00090000, // 81 Q 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 82 R 0x00000000, 0x00040300, 0x00000003, 0x00000500, 0x00070000, 0x00000706, 0x00000000, // 83 S 0x00000000, 0x00040302, 0x00000400, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 84 T 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00000000, // 85 U 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00000600, 0x00000700, 0x00000000, // 86 V 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 87 W 0x00000000, 0x00040002, 0x00050003, 0x00000500, 0x00070005, 0x00080006, 0x00000000, // 88 X 0x00000000, 0x00040002, 0x00050003, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 89 Y 0x00000000, 0x00040302, 0x00050000, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 90 Z 0x00000000, 0x00040300, 0x00000400, 0x00000500, 0x00000600, 0x00080700, 0x00000000, // 91 [ 0x00000000, 0x00000002, 0x00000400, 0x00000500, 0x00070000, 0x00080000, 0x00000000, // 92 '\' 0x00000000, 0x00000302, 0x00000400, 0x00000500, 0x00000600, 0x00000706, 0x00000000, // 93 ] 0x00000000, 0x00000300, 0x00050003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 94 ^ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00080706, 0x00000000, // 95 _ 0x00000000, 0x00000002, 0x00000400, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 96 ` 0x00000000, 0x00000000, 0x00050400, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 97 a 0x00000000, 0x00000002, 0x00000003, 0x00000504, 0x00070005, 0x00000706, 0x00000000, // 98 b 0x00000000, 0x00000000, 0x00050400, 0x00000004, 0x00000005, 0x00080700, 0x00000000, // 99 c 0x00000000, 0x00040000, 0x00050000, 0x00060500, 0x00070005, 0x00080700, 0x00000000, // 100 d 0x00000000, 0x00000000, 0x00050400, 0x00060504, 0x00000005, 0x00080700, 0x00000000, // 101 e 0x00000000, 0x00040300, 0x00000003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 102 f 0x00000000, 0x00000000, 0x00050400, 0x00060004, 0x00070600, 0x00080000, 0x00000807, // 103 g 0x00000000, 0x00000002, 0x00000003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 104 h 0x00000000, 0x00000300, 0x00000000, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 105 i 0x00000000, 0x00000300, 0x00000000, 0x00000500, 0x00000600, 0x00000700, 0x00000007, // 106 j 0x00000000, 0x00000002, 0x00000003, 0x00060004, 0x00000605, 0x00080006, 0x00000000, // 107 k 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000600, 0x00080000, 0x00000000, // 108 l 0x00000000, 0x00000000, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 109 m 0x00000000, 0x00000000, 0x00000403, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 110 n 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 111 o 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00000605, 0x00000006, 0x00000007, // 112 p 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070600, 0x00080000, 0x00090000, // 113 q 0x00000000, 0x00000000, 0x00050003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 114 r 0x00000000, 0x00000000, 0x00050400, 0x00000004, 0x00070600, 0x00000706, 0x00000000, // 115 s 0x00000000, 0x00000300, 0x00050403, 0x00000500, 0x00000600, 0x00080000, 0x00000000, // 116 t 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 117 u 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 118 v 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 119 w 0x00000000, 0x00000000, 0x00050003, 0x00000500, 0x00070005, 0x00080006, 0x00000000, // 120 x 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00000600, 0x00000700, 0x00000007, // 121 y 0x00000000, 0x00000000, 0x00050403, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 122 z 0x00000000, 0x00040300, 0x00000400, 0x00000504, 0x00000600, 0x00080700, 0x00000000, // 123 { 0x00000000, 0x00000300, 0x00000400, 0x00000000, 0x00000600, 0x00000700, 0x00000000, // 124 | 0x00000000, 0x00000302, 0x00000400, 0x00060500, 0x00000600, 0x00000706, 0x00000000, // 125 } 0x00000000, 0x00000302, 0x00050000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 126 ~ 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070605, 0x00000000, 0x00000000, // 127  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; static void PutTextInternal (const char *str, int len, short x, short y, int color, int backcolor) { int Opac = (color >> 24) & 0xFF; int backOpac = (backcolor >> 24) & 0xFF; int origX = x; if(!Opac && !backOpac) return; while(*str && len && y < LUA_SCREEN_HEIGHT) { int c = *str++; while (x > LUA_SCREEN_WIDTH && c != '\n') { c = *str; if (c == '\0') break; str++; } if(c == '\n') { x = origX; y += 8; continue; } else if(c == '\t') // just in case { const int tabSpace = 8; x += (tabSpace-(((x-origX)/4)%tabSpace))*4; continue; } if((unsigned int)(c-32) >= 96) continue; const unsigned char* Cur_Glyph = (const unsigned char*)&Small_Font_Data + (c-32)*7*4; for(int y2 = 0; y2 < 8; y2++) { unsigned int glyphLine = *((unsigned int*)Cur_Glyph + y2); for(int x2 = -1; x2 < 4; x2++) { int shift = x2 << 3; int mask = 0xFF << shift; int intensity = (glyphLine & mask) >> shift; if(intensity && x2 >= 0 && y2 < 7) { //int xdraw = std::max(0,std::min(LUA_SCREEN_WIDTH - 1,x+x2)); //int ydraw = std::max(0,std::min(LUA_SCREEN_HEIGHT - 1,y+y2)); //gui_drawpixel_fast(xdraw, ydraw, color); gui_drawpixel_internal(x+x2, y+y2, color); } else if(backOpac) { for(int y3 = std::max(0,y2-1); y3 <= std::min(6,y2+1); y3++) { unsigned int glyphLine = *((unsigned int*)Cur_Glyph + y3); for(int x3 = std::max(0,x2-1); x3 <= std::min(3,x2+1); x3++) { int shift = x3 << 3; int mask = 0xFF << shift; intensity |= (glyphLine & mask) >> shift; if (intensity) goto draw_outline; // speedup? } } draw_outline: if(intensity) { //int xdraw = std::max(0,std::min(LUA_SCREEN_WIDTH - 1,x+x2)); //int ydraw = std::max(0,std::min(LUA_SCREEN_HEIGHT - 1,y+y2)); //gui_drawpixel_fast(xdraw, ydraw, backcolor); gui_drawpixel_internal(x+x2, y+y2, backcolor); } } } } x += 4; len--; } } static int strlinelen(const char* string) { const char* s = string; while(*s && *s != '\n') s++; if(*s) s++; return s - string; } static void LuaDisplayString (const char *string, int y, int x, uint32 color, uint32 outlineColor) { if(!string) return; gui_prepare(); PutTextInternal(string, strlen(string), x, y, color, outlineColor); /* const char* ptr = string; while(*ptr && y < LUA_SCREEN_HEIGHT) { int len = strlinelen(ptr); int skip = 0; if(len < 1) len = 1; // break up the line if it's too long to display otherwise if(len > 63) { len = 63; const char* ptr2 = ptr + len-1; for(int j = len-1; j; j--, ptr2--) { if(*ptr2 == ' ' || *ptr2 == '\t') { len = j; skip = 1; break; } } } int xl = 0; int yl = 0; int xh = (LUA_SCREEN_WIDTH - 1 - 1) - 4*len; int yh = LUA_SCREEN_HEIGHT - 1; int x2 = std::min(std::max(x,xl),xh); int y2 = std::min(std::max(y,yl),yh); PutTextInternal(ptr,len,x2,y2,color,outlineColor); ptr += len + skip; y += 8; } */ } static uint8 FCEUFont[792] = { 6, 0, 0, 0, 0, 0, 0, 0, 3, 64, 64, 64, 64, 64, 0, 64, 5, 80, 80, 80, 0, 0, 0, 0, 6, 80, 80,248, 80,248, 80, 80, 6, 32,120,160,112, 40,240, 32, 6, 64,168, 80, 32, 80,168, 16, 6, 96,144,160, 64,168,144,104, 3, 64, 64, 0, 0, 0, 0, 0, 4, 32, 64, 64, 64, 64, 64, 32, 4, 64, 32, 32, 32, 32, 32, 64, 6, 0, 80, 32,248, 32, 80, 0, 6, 0, 32, 32,248, 32, 32, 0, 3, 0, 0, 0, 0, 0, 64,128, 5, 0, 0, 0,240, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 64, 5, 16, 16, 32, 32, 32, 64, 64, 6,112,136,136,136,136,136,112, //0 6, 32, 96, 32, 32, 32, 32, 32, 6,112,136, 8, 48, 64,128,248, 6,112,136, 8, 48, 8,136,112, 6, 16, 48, 80,144,248, 16, 16, 6,248,128,128,240, 8, 8,240, 6, 48, 64,128,240,136,136,112, 6,248, 8, 16, 16, 32, 32, 32, 6,112,136,136,112,136,136,112, 6,112,136,136,120, 8, 16, 96, 3, 0, 0, 64, 0, 0, 64, 0, 3, 0, 0, 64, 0, 0, 64,128, 4, 0, 32, 64,128, 64, 32, 0, 5, 0, 0,240, 0,240, 0, 0, 4, 0,128, 64, 32, 64,128, 0, 5,112,136, 8, 16, 32, 0, 32, 6,112,136,136,184,176,128,112, 6,112,136,136,248,136,136,136, //A 6,240,136,136,240,136,136,240, 6,112,136,128,128,128,136,112, 6,224,144,136,136,136,144,224, 6,248,128,128,240,128,128,248, 6,248,128,128,240,128,128,128, 6,112,136,128,184,136,136,120, 6,136,136,136,248,136,136,136, 4,224, 64, 64, 64, 64, 64,224, 6, 8, 8, 8, 8, 8,136,112, 6,136,144,160,192,160,144,136, 6,128,128,128,128,128,128,248, 6,136,216,168,168,136,136,136, 6,136,136,200,168,152,136,136, 7, 48, 72,132,132,132, 72, 48, 6,240,136,136,240,128,128,128, 6,112,136,136,136,168,144,104, 6,240,136,136,240,144,136,136, 6,112,136,128,112, 8,136,112, 6,248, 32, 32, 32, 32, 32, 32, 6,136,136,136,136,136,136,112, 6,136,136,136, 80, 80, 32, 32, 6,136,136,136,136,168,168, 80, 6,136,136, 80, 32, 80,136,136, 6,136,136, 80, 32, 32, 32, 32, 6,248, 8, 16, 32, 64,128,248, 3,192,128,128,128,128,128,192, 5, 64, 64, 32, 32, 32, 16, 16, 3,192, 64, 64, 64, 64, 64,192, 4, 64,160, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0,248, 3,128, 64, 0, 0, 0, 0, 0, 5, 0, 0, 96, 16,112,144,112, //a 5,128,128,224,144,144,144,224, 5, 0, 0,112,128,128,128,112, 5, 16, 16,112,144,144,144,112, 5, 0, 0, 96,144,240,128,112, 5, 48, 64,224, 64, 64, 64, 64, 5, 0,112,144,144,112, 16,224, 5,128,128,224,144,144,144,144, 2,128, 0,128,128,128,128,128, 4, 32, 0, 32, 32, 32, 32,192, 5,128,128,144,160,192,160,144, 2,128,128,128,128,128,128,128, 6, 0, 0,208,168,168,168,168, 5, 0, 0,224,144,144,144,144, 5, 0, 0, 96,144,144,144, 96, 5, 0, 0,224,144,144,224,128, 5, 0, 0,112,144,144,112, 16, 5, 0, 0,176,192,128,128,128, 5, 0, 0,112,128, 96, 16,224, 4, 64, 64,224, 64, 64, 64, 32, 5, 0, 0,144,144,144,144,112, 5, 0, 0,144,144,144,160,192, 6, 0, 0,136,136,168,168, 80, 5, 0, 0,144,144, 96,144,144, 5, 0,144,144,144,112, 16, 96, 5, 0, 0,240, 32, 64,128,240, 4, 32, 64, 64,128, 64, 64, 32, 3, 64, 64, 64, 64, 64, 64, 64, 4,128, 64, 64, 32, 64, 64,128, 6, 0,104,176, 0, 0, 0, 0 }; static int FixJoedChar(uint8 ch) { int c = ch; c -= 32; return (c < 0 || c > 98) ? 0 : c; } static int JoedCharWidth(uint8 ch) { return FCEUFont[FixJoedChar(ch)*8]; } void LuaDrawTextTransWH(const char *str, int x, int y, uint32 color, uint32 backcolor) { int Opac = (color >> 24) & 0xFF; int backOpac = (backcolor >> 24) & 0xFF; int origX = x; if(!Opac && !backOpac) return; int len = strlen(str); int defaultAlpha = std::max(0, std::min(transparencyModifier, 255)); while(*str && len && y < LUA_SCREEN_HEIGHT) { int c = *str++; while (x > LUA_SCREEN_WIDTH && c != '\n') { c = *str; if (c == '\0') break; str++; } if(c == '\n') { x = origX; y += 8; continue; } else if(c == '\t') // just in case { const int tabSpace = 8; x += (tabSpace-(((x-origX)/8)%tabSpace))*8; continue; } int ch = FixJoedChar(c); int wid = JoedCharWidth(c); for(int y2 = 0; y2 < 7; y2++) { uint8 d = FCEUFont[ch*8 + 1+y2]; for(int x2 = 0; x2 < wid; x2++) { int c = (d >> (7-x2)) & 1; if(c) gui_drawpixel_internal(x+x2, y+y2, color); else gui_drawpixel_internal(x+x2, y+y2, backcolor); } } // shadows :P for(int x2 = 0; x2 < wid; x2++) gui_drawpixel_internal(x+x2, y+7, LUA_BUILD_PIXEL(defaultAlpha, 0, 0, 0)); if (*str == '\0' || *str == '\n') for(int y2 = 0; y2 < 7; y2++) gui_drawpixel_internal(x+wid, y+y2, LUA_BUILD_PIXEL(defaultAlpha, 0, 0, 0)); x += wid; len--; } } // gui.text(int x, int y, string msg) // // Displays the given text on the screen, using the same font and techniques as the // main HUD. static int gui_text(lua_State *L) { extern int font_height; const char *msg; int x, y; x = luaL_checkinteger(L,1); y = luaL_checkinteger(L,2); msg = luaL_checkstring(L,3); // if (x < 0 || x >= LUA_SCREEN_WIDTH || y < 0 || y >= (LUA_SCREEN_HEIGHT - font_height)) // luaL_error(L,"bad coordinates"); #if 0 uint32 colour = gui_optcolour(L,4,LUA_BUILD_PIXEL(255, 255, 255, 255)); uint32 borderColour = gui_optcolour(L,5,LUA_BUILD_PIXEL(255, 0, 0, 0)); gui_prepare(); LuaDisplayString(msg, y, x, colour, borderColour); #else uint32 color = gui_optcolour(L,4,LUA_BUILD_PIXEL(255, 255, 255, 255)); uint32 bgcolor = gui_optcolour(L,5,LUA_BUILD_PIXEL(255, 27, 18, 105)); gui_prepare(); LuaDrawTextTransWH(msg, x, y, color, bgcolor); #endif return 0; } // gui.gdoverlay([int dx=0, int dy=0,] string str [, sx=0, sy=0, sw, sh] [, float alphamul=1.0]) // // Overlays the given image on the screen. // example: gui.gdoverlay(gd.createFromPng("myimage.png"):gdStr()) static int gui_gdoverlay(lua_State *L) { int argCount = lua_gettop(L); int xStartDst = 0; int yStartDst = 0; int xStartSrc = 0; int yStartSrc = 0; int index = 1; if(lua_type(L,index) == LUA_TNUMBER) { xStartDst = lua_tointeger(L,index++); if(lua_type(L,index) == LUA_TNUMBER) yStartDst = lua_tointeger(L,index++); } luaL_checktype(L,index,LUA_TSTRING); const unsigned char* ptr = (const unsigned char*)lua_tostring(L,index++); if (ptr[0] != 255 || (ptr[1] != 254 && ptr[1] != 255)) luaL_error(L, "bad image data"); bool trueColor = (ptr[1] == 254); ptr += 2; int imgwidth = *ptr++ << 8; imgwidth |= *ptr++; int width = imgwidth; int imgheight = *ptr++ << 8; imgheight |= *ptr++; int height = imgheight; if ((!trueColor && *ptr) || (trueColor && !*ptr)) luaL_error(L, "bad image data"); ptr++; int pitch = imgwidth * (trueColor?4:1); if ((argCount - index + 1) >= 4) { xStartSrc = luaL_checkinteger(L,index++); yStartSrc = luaL_checkinteger(L,index++); width = luaL_checkinteger(L,index++); height = luaL_checkinteger(L,index++); } int alphaMul = transparencyModifier; if(lua_isnumber(L, index)) alphaMul = (int)(alphaMul * lua_tonumber(L, index++)); if(alphaMul <= 0) return 0; // since there aren't that many possible opacity levels, // do the opacity modification calculations beforehand instead of per pixel int opacMap[256]; for(int i = 0; i < 128; i++) { int opac = 255 - ((i << 1) | (i & 1)); // gdAlphaMax = 127, not 255 opac = (opac * alphaMul) / 255; if(opac < 0) opac = 0; if(opac > 255) opac = 255; opacMap[i] = opac; } for(int i = 128; i < 256; i++) opacMap[i] = 0; // what should we do for them, actually? int colorsTotal = 0; if (!trueColor) { colorsTotal = *ptr++ << 8; colorsTotal |= *ptr++; } int transparent = *ptr++ << 24; transparent |= *ptr++ << 16; transparent |= *ptr++ << 8; transparent |= *ptr++; struct { uint8 r, g, b, a; } pal[256]; if (!trueColor) for (int i = 0; i < 256; i++) { pal[i].r = *ptr++; pal[i].g = *ptr++; pal[i].b = *ptr++; pal[i].a = opacMap[*ptr++]; } // some of clippings if (xStartSrc < 0) { width += xStartSrc; xStartDst -= xStartSrc; xStartSrc = 0; } if (yStartSrc < 0) { height += yStartSrc; yStartDst -= yStartSrc; yStartSrc = 0; } if (xStartSrc+width >= imgwidth) width = imgwidth - xStartSrc; if (yStartSrc+height >= imgheight) height = imgheight - yStartSrc; if (xStartDst < 0) { width += xStartDst; if (width <= 0) return 0; xStartSrc = -xStartDst; xStartDst = 0; } if (yStartDst < 0) { height += yStartDst; if (height <= 0) return 0; yStartSrc = -yStartDst; yStartDst = 0; } if (xStartDst+width >= LUA_SCREEN_WIDTH) width = LUA_SCREEN_WIDTH - xStartDst; if (yStartDst+height >= LUA_SCREEN_HEIGHT) height = LUA_SCREEN_HEIGHT - yStartDst; if (width <= 0 || height <= 0) return 0; // out of screen or invalid size gui_prepare(); const uint8* pix = (const uint8*)(&ptr[yStartSrc*pitch + (xStartSrc*(trueColor?4:1))]); int bytesToNextLine = pitch - (width * (trueColor?4:1)); if (trueColor) for (int y = yStartDst; y < height+yStartDst && y < LUA_SCREEN_HEIGHT; y++, pix += bytesToNextLine) { for (int x = xStartDst; x < width+xStartDst && x < LUA_SCREEN_WIDTH; x++, pix += 4) { gui_drawpixel_fast(x, y, LUA_BUILD_PIXEL(opacMap[pix[0]], pix[1], pix[2], pix[3])); } } else for (int y = yStartDst; y < height+yStartDst && y < LUA_SCREEN_HEIGHT; y++, pix += bytesToNextLine) { for (int x = xStartDst; x < width+xStartDst && x < LUA_SCREEN_WIDTH; x++, pix++) { gui_drawpixel_fast(x, y, LUA_BUILD_PIXEL(pal[*pix].a, pal[*pix].r, pal[*pix].g, pal[*pix].b)); } } return 0; } // function gui.register(function f) // // This function will be called just before a graphical update. // More complicated, but doesn't suffer any frame delays. // Nil will be accepted in place of a function to erase // a previously registered function, and the previous function // (if any) is returned, or nil if none. static int gui_register(lua_State *L) { // We'll do this straight up. // First set up the stack. lua_settop(L,1); // Verify the validity of the entry if (!lua_isnil(L,1)) luaL_checktype(L, 1, LUA_TFUNCTION); // Get the old value lua_getfield(L, LUA_REGISTRYINDEX, guiCallbackTable); // Save the new value lua_pushvalue(L,1); lua_setfield(L, LUA_REGISTRYINDEX, guiCallbackTable); // The old value is on top of the stack. Return it. return 1; } static int doPopup(lua_State *L, const char* deftype, const char* deficon) { const char *str = luaL_checkstring(L, 1); const char* type = lua_type(L,2) == LUA_TSTRING ? lua_tostring(L,2) : deftype; const char* icon = lua_type(L,3) == LUA_TSTRING ? lua_tostring(L,3) : deficon; int itype = -1, iters = 0; while(itype == -1 && iters++ < 2) { if(!stricmp(type, "ok")) itype = 0; else if(!stricmp(type, "yesno")) itype = 1; else if(!stricmp(type, "yesnocancel")) itype = 2; else if(!stricmp(type, "okcancel")) itype = 3; else if(!stricmp(type, "abortretryignore")) itype = 4; else type = deftype; } assert(itype >= 0 && itype <= 4); if(!(itype >= 0 && itype <= 4)) itype = 0; int iicon = -1; iters = 0; while(iicon == -1 && iters++ < 2) { if(!stricmp(icon, "message") || !stricmp(icon, "notice")) iicon = 0; else if(!stricmp(icon, "question")) iicon = 1; else if(!stricmp(icon, "warning")) iicon = 2; else if(!stricmp(icon, "error")) iicon = 3; else icon = deficon; } assert(iicon >= 0 && iicon <= 3); if(!(iicon >= 0 && iicon <= 3)) iicon = 0; static const char * const titles [] = {"Notice", "Question", "Warning", "Error"}; const char* answer = "ok"; #ifdef WIN32 static const int etypes [] = {MB_OK, MB_YESNO, MB_YESNOCANCEL, MB_OKCANCEL, MB_ABORTRETRYIGNORE}; static const int eicons [] = {MB_ICONINFORMATION, MB_ICONQUESTION, MB_ICONWARNING, MB_ICONERROR}; //StopSound(); //mbg merge 7/27/08 int ianswer = MessageBox(hAppWnd, str, titles[iicon], etypes[itype] | eicons[iicon]); switch(ianswer) { case IDOK: answer = "ok"; break; case IDCANCEL: answer = "cancel"; break; case IDABORT: answer = "abort"; break; case IDRETRY: answer = "retry"; break; case IDIGNORE: answer = "ignore"; break; case IDYES: answer = "yes"; break; case IDNO: answer = "no"; break; } lua_pushstring(L, answer); return 1; #else char *t; #ifdef __linux int pid; // appease compiler // Before doing any work, verify the correctness of the parameters. if (strcmp(type, "ok") == 0) t = "OK:100"; else if (strcmp(type, "yesno") == 0) t = "Yes:100,No:101"; else if (strcmp(type, "yesnocancel") == 0) t = "Yes:100,No:101,Cancel:102"; else return luaL_error(L, "invalid popup type \"%s\"", type); // Can we find a copy of xmessage? Search the path. char *path = strdup(getenv("PATH")); char *current = path; char *colon; int found = 0; while (current) { colon = strchr(current, ':'); // Clip off the colon. *colon++ = 0; int len = strlen(current); char *filename = (char*)malloc(len + 12); // always give excess snprintf(filename, len+12, "%s/xmessage", current); if (access(filename, X_OK) == 0) { free(filename); found = 1; break; } // Failed, move on. current = colon; free(filename); } free(path); // We've found it? if (!found) goto use_console; pid = fork(); if (pid == 0) {// I'm the virgin sacrifice // I'm gonna be dead in a matter of microseconds anyways, so wasted memory doesn't matter to me. // Go ahead and abuse strdup. char * parameters[] = {"xmessage", "-buttons", t, strdup(str), NULL}; execvp("xmessage", parameters); // Aw shitty perror("exec xmessage"); exit(1); } else if (pid < 0) // something went wrong!!! Oh hell... use the console goto use_console; else { // We're the parent. Watch for the child. int r; int res = waitpid(pid, &r, 0); if (res < 0) // wtf? goto use_console; // The return value gets copmlicated... if (!WIFEXITED(r)) { luaL_error(L, "don't screw with my xmessage process!"); } r = WEXITSTATUS(r); // We assume it's worked. if (r == 0) { return 0; // no parameters for an OK } if (r == 100) { lua_pushstring(L, "yes"); return 1; } if (r == 101) { lua_pushstring(L, "no"); return 1; } if (r == 102) { lua_pushstring(L, "cancel"); return 1; } // Wtf? return luaL_error(L, "popup failed due to unknown results involving xmessage (%d)", r); } use_console: #endif // All else has failed if (strcmp(type, "ok") == 0) t = ""; else if (strcmp(type, "yesno") == 0) t = "yn"; else if (strcmp(type, "yesnocancel") == 0) t = "ync"; else return luaL_error(L, "invalid popup type \"%s\"", type); fprintf(stderr, "Lua Message: %s\n", str); while (true) { char buffer[64]; // We don't want parameters if (!t[0]) { fprintf(stderr, "[Press Enter]"); fgets(buffer, sizeof(buffer), stdin); // We're done return 0; } fprintf(stderr, "(%s): ", t); fgets(buffer, sizeof(buffer), stdin); // Check if the option is in the list if (strchr(t, tolower(buffer[0]))) { switch (tolower(buffer[0])) { case 'y': lua_pushstring(L, "yes"); return 1; case 'n': lua_pushstring(L, "no"); return 1; case 'c': lua_pushstring(L, "cancel"); return 1; default: luaL_error(L, "internal logic error in console based prompts for gui.popup"); } } // We fell through, so we assume the user answered wrong and prompt again. } // Nothing here, since the only way out is in the loop. #endif } // string gui.popup(string message, string type = "ok", string icon = "message") // string input.popup(string message, string type = "yesno", string icon = "question") static int gui_popup(lua_State *L) { return doPopup(L, "ok", "message"); } static int input_popup(lua_State *L) { return doPopup(L, "yesno", "question"); } // the following bit operations are ported from LuaBitOp 1.0.1, // because it can handle the sign bit (bit 31) correctly. /* ** Lua BitOp -- a bit operations library for Lua 5.1. ** http://bitop.luajit.org/ ** ** Copyright (C) 2008-2009 Mike Pall. All rights reserved. ** ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be ** included in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ** ** [ MIT license: http://www.opensource.org/licenses/mit-license.php ] */ #ifdef _MSC_VER /* MSVC is stuck in the last century and doesn't have C99's stdint.h. */ typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef unsigned __int64 uint64_t; #else #include #endif typedef int32_t SBits; typedef uint32_t UBits; typedef union { lua_Number n; #ifdef LUA_NUMBER_DOUBLE uint64_t b; #else UBits b; #endif } BitNum; /* Convert argument to bit type. */ static UBits barg(lua_State *L, int idx) { BitNum bn; UBits b; bn.n = lua_tonumber(L, idx); #if defined(LUA_NUMBER_DOUBLE) bn.n += 6755399441055744.0; /* 2^52+2^51 */ #ifdef SWAPPED_DOUBLE b = (UBits)(bn.b >> 32); #else b = (UBits)bn.b; #endif #elif defined(LUA_NUMBER_INT) || defined(LUA_NUMBER_LONG) || \ defined(LUA_NUMBER_LONGLONG) || defined(LUA_NUMBER_LONG_LONG) || \ defined(LUA_NUMBER_LLONG) if (sizeof(UBits) == sizeof(lua_Number)) b = bn.b; else b = (UBits)(SBits)bn.n; #elif defined(LUA_NUMBER_FLOAT) #error "A 'float' lua_Number type is incompatible with this library" #else #error "Unknown number type, check LUA_NUMBER_* in luaconf.h" #endif if (b == 0 && !lua_isnumber(L, idx)) luaL_typerror(L, idx, "number"); return b; } /* Return bit type. */ #define BRET(b) lua_pushnumber(L, (lua_Number)(SBits)(b)); return 1; static int bit_tobit(lua_State *L) { BRET(barg(L, 1)) } static int bit_bnot(lua_State *L) { BRET(~barg(L, 1)) } #define BIT_OP(func, opr) \ static int func(lua_State *L) { int i; UBits b = barg(L, 1); \ for (i = lua_gettop(L); i > 1; i--) b opr barg(L, i); BRET(b) } BIT_OP(bit_band, &=) BIT_OP(bit_bor, |=) BIT_OP(bit_bxor, ^=) #define bshl(b, n) (b << n) #define bshr(b, n) (b >> n) #define bsar(b, n) ((SBits)b >> n) #define brol(b, n) ((b << n) | (b >> (32-n))) #define bror(b, n) ((b << (32-n)) | (b >> n)) #define BIT_SH(func, fn) \ static int func(lua_State *L) { \ UBits b = barg(L, 1); UBits n = barg(L, 2) & 31; BRET(fn(b, n)) } BIT_SH(bit_lshift, bshl) BIT_SH(bit_rshift, bshr) BIT_SH(bit_arshift, bsar) BIT_SH(bit_rol, brol) BIT_SH(bit_ror, bror) static int bit_bswap(lua_State *L) { UBits b = barg(L, 1); b = (b >> 24) | ((b >> 8) & 0xff00) | ((b & 0xff00) << 8) | (b << 24); BRET(b) } static int bit_tohex(lua_State *L) { UBits b = barg(L, 1); SBits n = lua_isnone(L, 2) ? 8 : (SBits)barg(L, 2); const char *hexdigits = "0123456789abcdef"; char buf[8]; int i; if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; } if (n > 8) n = 8; for (i = (int)n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; } lua_pushlstring(L, buf, (size_t)n); return 1; } static const struct luaL_Reg bit_funcs[] = { { "tobit", bit_tobit }, { "bnot", bit_bnot }, { "band", bit_band }, { "bor", bit_bor }, { "bxor", bit_bxor }, { "lshift", bit_lshift }, { "rshift", bit_rshift }, { "arshift", bit_arshift }, { "rol", bit_rol }, { "ror", bit_ror }, { "bswap", bit_bswap }, { "tohex", bit_tohex }, { NULL, NULL } }; /* Signed right-shifts are implementation-defined per C89/C99. ** But the de facto standard are arithmetic right-shifts on two's ** complement CPUs. This behaviour is required here, so test for it. */ #define BAD_SAR (bsar(-8, 2) != (SBits)-2) bool luabitop_validate(lua_State *L) // originally named as luaopen_bit { UBits b; lua_pushnumber(L, (lua_Number)1437217655L); b = barg(L, -1); if (b != (UBits)1437217655L || BAD_SAR) { /* Perform a simple self-test. */ const char *msg = "compiled with incompatible luaconf.h"; #ifdef LUA_NUMBER_DOUBLE #ifdef _WIN32 if (b == (UBits)1610612736L) msg = "use D3DCREATE_FPU_PRESERVE with DirectX"; #endif if (b == (UBits)1127743488L) msg = "not compiled with SWAPPED_DOUBLE"; #endif if (BAD_SAR) msg = "arithmetic right-shift broken"; luaL_error(L, "bit library self-test failed (%s)", msg); return false; } return true; } // LuaBitOp ends here static int bit_bshift_emulua(lua_State *L) { int shift = luaL_checkinteger(L,2); if (shift < 0) { lua_pushinteger(L, -shift); lua_replace(L, 2); return bit_lshift(L); } else return bit_rshift(L); } static int bitbit(lua_State *L) { int rv = 0; int numArgs = lua_gettop(L); for(int i = 1; i <= numArgs; i++) { int where = luaL_checkinteger(L,i); if (where >= 0 && where < 32) rv |= (1 << where); } lua_settop(L,0); BRET(rv); } // The function called periodically to ensure Lua doesn't run amok. static void FCEU_LuaHookFunction(lua_State *L, lua_Debug *dbg) { if (numTries-- == 0) { int kill = 0; #ifdef WIN32 // Uh oh //StopSound(); //mbg merge 7/23/08 int ret = MessageBox(hAppWnd, "The Lua script running has been running a long time. It may have gone crazy. Kill it?\n\n(No = don't check anymore either)", "Lua Script Gone Nuts?", MB_YESNO); if (ret == IDYES) { kill = 1; } #else fprintf(stderr, "The Lua script running has been running a long time.\nIt may have gone crazy. Kill it? (I won't ask again if you say No)\n"); char buffer[64]; while (TRUE) { fprintf(stderr, "(y/n): "); fgets(buffer, sizeof(buffer), stdin); if (buffer[0] == 'y' || buffer[0] == 'Y') { kill = 1; break; } if (buffer[0] == 'n' || buffer[0] == 'N') break; } #endif if (kill) { luaL_error(L, "Killed by user request."); FCEU_LuaOnStop(); } // else, kill the debug hook. lua_sethook(L, NULL, 0, 0); } } static void emu_exec_count_hook(lua_State *L, lua_Debug *dbg) { luaL_error(L, "exec_count timeout"); } static int emu_exec_count(lua_State *L) { int count = (int)luaL_checkinteger(L,1); lua_pushvalue(L, 2); lua_sethook(L, emu_exec_count_hook, LUA_MASKCOUNT, count); int ret = lua_pcall(L, 0, 0, 0); lua_sethook(L, NULL, 0, 0); lua_settop(L,0); lua_pushinteger(L, ret); return 1; } #ifdef WIN32 static HANDLE readyEvent, goEvent; DWORD WINAPI emu_exec_time_proc(LPVOID lpParameter) { SetEvent(readyEvent); WaitForSingleObject(goEvent,INFINITE); lua_State *L = (lua_State *)lpParameter; lua_pushvalue(L, 2); int ret = lua_pcall(L, 0, 0, 0); lua_settop(L,0); lua_pushinteger(L, ret); SetEvent(readyEvent); return 0; } static void emu_exec_time_hook(lua_State *L, lua_Debug *dbg) { luaL_error(L, "exec_time timeout"); } static int emu_exec_time(lua_State *L) { int count = (int)luaL_checkinteger(L,1); readyEvent = CreateEvent(0,true,false,0); goEvent = CreateEvent(0,true,false,0); DWORD threadid; HANDLE thread = CreateThread(0,0,emu_exec_time_proc,(LPVOID)L,0,&threadid); SetThreadAffinityMask(thread,1); //wait for the lua thread to start WaitForSingleObject(readyEvent,INFINITE); ResetEvent(readyEvent); //tell the lua thread to proceed SetEvent(goEvent); //wait for the lua thread to finish, but no more than the specified amount of time WaitForSingleObject(readyEvent,count); //kill lua (if it hasnt already been killed) lua_sethook(L, emu_exec_time_hook, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); //keep on waiting for the lua thread to come back WaitForSingleObject(readyEvent,count); //clear the lua thread-killer lua_sethook(L, NULL, 0, 0); CloseHandle(readyEvent); CloseHandle(goEvent); CloseHandle(thread); return 1; } #else static int emu_exec_time(lua_State *L) { return 0; } #endif static const struct luaL_reg emulib [] = { {"poweron", emu_poweron}, {"softreset", emu_softreset}, {"speedmode", emu_speedmode}, {"frameadvance", emu_frameadvance}, {"pause", emu_pause}, {"unpause", emu_unpause}, {"exec_count", emu_exec_count}, {"exec_time", emu_exec_time}, {"setrenderplanes", emu_setrenderplanes}, {"message", emu_message}, {"framecount", emu_framecount}, {"lagcount", emu_lagcount}, {"lagged", emu_lagged}, {"registerbefore", emu_registerbefore}, {"registerafter", emu_registerafter}, {"registerexit", emu_registerexit}, {"readonly", movie_getreadonly}, {"setreadonly", movie_setreadonly}, {NULL,NULL} }; static const struct luaL_reg romlib [] = { {"readbyte", rom_readbyte}, {"readbytesigned", rom_readbytesigned}, // alternate naming scheme for unsigned {"readbyteunsigned", rom_readbyte}, {NULL,NULL} }; static const struct luaL_reg memorylib [] = { {"readbyte", memory_readbyte}, {"readbyterange", memory_readbyterange}, {"readbytesigned", memory_readbytesigned}, // alternate naming scheme for unsigned {"readbyteunsigned", memory_readbyte}, {"writebyte", memory_writebyte}, {"getregister", memory_getregister}, {"setregister", memory_setregister}, // memory hooks {"registerwrite", memory_registerwrite}, //{"registerread", memory_registerread}, TODO {"registerexec", memory_registerexec}, // alternate names {"register", memory_registerwrite}, {"registerrun", memory_registerexec}, {"registerexecute", memory_registerexec}, {NULL,NULL} }; static const struct luaL_reg joypadlib[] = { {"get", joypad_read}, {"set", joypad_set}, // alternative names {"read", joypad_read}, {"write", joypad_set}, {NULL,NULL} }; static const struct luaL_reg zapperlib[] = { {"read", zapper_read}, {NULL,NULL} }; static const struct luaL_reg inputlib[] = { {"get", input_get}, {"popup", input_popup}, // alternative names {"read", input_get}, {NULL,NULL} }; static const struct luaL_reg savestatelib[] = { {"create", savestate_create}, {"save", savestate_save}, {"persist", savestate_persist}, {"load", savestate_load}, {NULL,NULL} }; static const struct luaL_reg movielib[] = { {"framecount", emu_framecount}, // for those familiar with other emulators that have movie.framecount() instead of emulatorname.framecount() {"mode", movie_mode}, {"rerecordcounting", movie_rerecordcounting}, {"stop", movie_stop}, {"active", movie_isactive}, {"recording", movie_isrecording}, {"playing", movie_isplaying}, {"length", movie_getlength}, {"rerecordcount", movie_rerecordcount}, {"name", movie_getname}, {"readonly", movie_getreadonly}, {"setreadonly", movie_setreadonly}, {"replay", movie_replay}, // {"record", movie_record}, // {"play", movie_playback}, // alternative names {"close", movie_stop}, {"getname", movie_getname}, // {"playback", movie_playback}, {"playbeginning", movie_replay}, {"getreadonly", movie_getreadonly}, {NULL,NULL} }; static const struct luaL_reg guilib[] = { {"pixel", gui_pixel}, {"line", gui_line}, {"box", gui_box}, {"text", gui_text}, {"gdscreenshot", gui_gdscreenshot}, {"gdoverlay", gui_gdoverlay}, {"opacity", gui_setopacity}, {"transparency", gui_transparency}, {"register", gui_register}, {"popup", gui_popup}, // alternative names {"drawtext", gui_text}, {"drawbox", gui_box}, {"drawline", gui_line}, {"drawpixel", gui_pixel}, {"setpixel", gui_pixel}, {"writepixel", gui_pixel}, {"rect", gui_box}, {"drawrect", gui_box}, {"drawimage", gui_gdoverlay}, {"image", gui_gdoverlay}, {NULL,NULL} }; void CallExitFunction() { if (!L) return; lua_settop(L, 0); lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]); int errorcode = 0; if (lua_isfunction(L, -1)) { //chdir(luaCWD); errorcode = lua_pcall(L, 0, 0, 0); //_getcwd(luaCWD, _MAX_PATH); } if (errorcode) HandleCallbackError(L); } void FCEU_LuaFrameBoundary() { // printf("Lua Frame\n"); // HA! if (!L || !luaRunning) return; // Our function needs calling lua_settop(L,0); lua_getfield(L, LUA_REGISTRYINDEX, frameAdvanceThread); lua_State *thread = lua_tothread(L,1); // Lua calling C must know that we're busy inside a frame boundary frameBoundary = TRUE; frameAdvanceWaiting = FALSE; numTries = 1000; int result = lua_resume(thread, 0); if (result == LUA_YIELD) { // Okay, we're fine with that. } else if (result != 0) { // Done execution by bad causes FCEU_LuaOnStop(); lua_pushnil(L); lua_setfield(L, LUA_REGISTRYINDEX, frameAdvanceThread); // Error? #ifdef WIN32 //StopSound();//StopSound(); //mbg merge 7/23/08 MessageBox( hAppWnd, lua_tostring(thread,-1), "Lua run error", MB_OK | MB_ICONSTOP); #else fprintf(stderr, "Lua thread bombed out: %s\n", lua_tostring(thread,-1)); #endif } else { FCEU_LuaStop(); FCEU_DispMessage("Script died of natural causes.\n"); } // Past here, the nes actually runs, so any Lua code is called mid-frame. We must // not do anything too stupid, so let ourselves know. frameBoundary = FALSE; if (!frameAdvanceWaiting) { FCEU_LuaOnStop(); } } /** * Loads and runs the given Lua script. * The emulator MUST be paused for this function to be * called. Otherwise, all frame boundary assumptions go out the window. * * Returns true on success, false on failure. */ int FCEU_LoadLuaCode(const char *filename) { if (filename != luaScriptName) { if (luaScriptName) free(luaScriptName); luaScriptName = strdup(filename); } //stop any lua we might already have had running FCEU_LuaStop(); if (!L) { #ifdef WIN32 HMODULE test = LoadLibrary("lua5.1.dll"); if(!test) { FCEUD_PrintError("Couldn't initialize lua system due to failure loading lua5.1.dll"); return 0; } FreeLibrary(test); #endif L = lua_open(); luaL_openlibs(L); luaL_register(L, "emu", emulib); // added for better cross-emulator compatibility luaL_register(L, "FCEU", emulib); // kept for backward compatibility luaL_register(L, "memory", memorylib); luaL_register(L, "rom", romlib); luaL_register(L, "joypad", joypadlib); luaL_register(L, "zapper", zapperlib); luaL_register(L, "input", inputlib); luaL_register(L, "savestate", savestatelib); luaL_register(L, "movie", movielib); luaL_register(L, "gui", guilib); luaL_register(L, "bit", bit_funcs); // LuaBitOp library lua_settop(L, 0); // clean the stack, because each call to luaL_register leaves a table on top // old bit operation functions lua_register(L, "AND", bit_band); lua_register(L, "OR", bit_bor); lua_register(L, "XOR", bit_bxor); lua_register(L, "SHIFT", bit_bshift_emulua); lua_register(L, "BIT", bitbit); luabitop_validate(L); // push arrays for storing hook functions in for(int i = 0; i < LUAMEMHOOK_COUNT; i++) { lua_newtable(L); lua_setfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[i]); } } // We make our thread NOW because we want it at the bottom of the stack. // If all goes wrong, we let the garbage collector remove it. lua_State *thread = lua_newthread(L); // Load the data int result = luaL_loadfile(L,filename); if (result) { #ifdef WIN32 // Doing this here caused nasty problems; reverting to MessageBox-from-dialog behavior. //StopSound();//StopSound(); //mbg merge 7/23/08 MessageBox(NULL, lua_tostring(L,-1), "Lua load error", MB_OK | MB_ICONSTOP); #else fprintf(stderr, "Failed to compile file: %s\n", lua_tostring(L,-1)); #endif // Wipe the stack. Our thread lua_settop(L,0); return 0; // Oh shit. } #ifdef WIN32 AddRecentLuaFile(filename); //Add the filename to our recent lua menu #endif // Get our function into it lua_xmove(L, thread, 1); // Save the thread to the registry. This is why I make the thread FIRST. lua_setfield(L, LUA_REGISTRYINDEX, frameAdvanceThread); // Initialize settings luaRunning = TRUE; skipRerecords = FALSE; numMemHooks = 0; transparencyModifier = 255; // opaque wasPaused = FCEUI_EmulationPaused(); if (wasPaused) FCEUI_ToggleEmulationPause(); // And run it right now. :) //FCEU_LuaFrameBoundary(); // Set up our protection hook to be executed once every 10,000 bytecode instructions. //lua_sethook(thread, FCEU_LuaHookFunction, LUA_MASKCOUNT, 10000); // We're done. return 1; } /** * Equivalent to repeating the last FCEU_LoadLuaCode() call. */ void FCEU_ReloadLuaCode() { if (!luaScriptName) FCEU_DispMessage("There's no script to reload."); else FCEU_LoadLuaCode(luaScriptName); } /** * Terminates a running Lua script by killing the whole Lua engine. * * Always safe to call, except from within a lua call itself (duh). * */ void FCEU_LuaStop() { //already killed if (!L) return; //execute the user's shutdown callbacks CallExitFunction(); /*info.*/numMemHooks = 0; for(int i = 0; i < LUAMEMHOOK_COUNT; i++) CalculateMemHookRegions((LuaMemHookType)i); //sometimes iup uninitializes com //MBG TODO - test whether this is really necessary. i dont think it is #ifdef WIN32 CoInitialize(0); #endif //lua_gc(L,LUA_GCCOLLECT,0); lua_close(L); // this invokes our garbage collectors for us L = NULL; FCEU_LuaOnStop(); } /** * Returns true if there is a Lua script running. * */ int FCEU_LuaRunning() { return L && luaRunning; } /** * Returns true if Lua would like to steal the given joypad control. */ //int FCEU_LuaUsingJoypad(int which) { // return lua_joypads_used & (1 << which); //} //adelikat: TODO: should I have a FCEU_LuaUsingJoypadFalse? /** * Reads the buttons Lua is feeding for the given joypad, in the same * format as the OS-specific code. * * It may force set or force clear the buttons. It may also simply * pass the input along or invert it. The act of calling this * function will reset everything back to pass-through, though. * Generally means don't call it more than once per frame! */ uint8 FCEU_LuaReadJoypad(int which, uint8 joyl) { joyl = (joyl & luajoypads1[which]) | (~joyl & luajoypads2[which]); luajoypads1[which] = 0xFF; luajoypads2[which] = 0x00; return joyl; } //adelikat: Returns the buttons that will be specifically set to false (as opposed to on or nil) //This will be used in input.cpp to &(and) against the input to override a button with a false value. This is a work around to allow 3 conditions to be sent be lua, true, false, nil //uint8 FCEU_LuaReadJoypadFalse(int which) { // lua_joypads_used_false &= ~(1 << which); // return lua_joypads_false[which]; //} /** * If this function returns true, the movie code should NOT increment * the rerecord count for a load-state. * * This function will not return true if a script is not running. */ int FCEU_LuaRerecordCountSkip() { return L && luaRunning && skipRerecords; } /** * Given an 8-bit screen with the indicated resolution, * draw the current GUI onto it. * * Currently we only support 256x* resolutions. */ void FCEU_LuaGui(uint8 *XBuf) { if (!L || !luaRunning) return; // First, check if we're being called by anybody lua_getfield(L, LUA_REGISTRYINDEX, guiCallbackTable); if (lua_isfunction(L, -1)) { // We call it now numTries = 1000; int ret = lua_pcall(L, 0, 0, 0); if (ret != 0) { #ifdef WIN32 //StopSound();//StopSound(); //mbg merge 7/23/08 MessageBox(hAppWnd, lua_tostring(L, -1), "Lua Error in GUI function", MB_OK); #else fprintf(stderr, "Lua error in gui.register function: %s\n", lua_tostring(L, -1)); #endif // This is grounds for trashing the function lua_pushnil(L); lua_setfield(L, LUA_REGISTRYINDEX, guiCallbackTable); } } // And wreak the stack lua_settop(L, 0); if (gui_used == GUI_CLEAR) return; gui_used = GUI_USED_SINCE_LAST_FRAME; int x, y; for (y = 0; y < LUA_SCREEN_HEIGHT; y++) { for (x=0; x < LUA_SCREEN_WIDTH; x++) { const uint8 gui_alpha = gui_data[(y*LUA_SCREEN_WIDTH+x)*4+3]; if (gui_alpha == 0) { // do nothing continue; } const uint8 gui_red = gui_data[(y*LUA_SCREEN_WIDTH+x)*4+2]; const uint8 gui_green = gui_data[(y*LUA_SCREEN_WIDTH+x)*4+1]; const uint8 gui_blue = gui_data[(y*LUA_SCREEN_WIDTH+x)*4]; int r, g, b; if (gui_alpha == 255) { // direct copy r = gui_red; g = gui_green; b = gui_blue; } else { // alpha-blending uint8 scr_red, scr_green, scr_blue; FCEUD_GetPalette(XBuf[(y+8)*256+x], &scr_red, &scr_green, &scr_blue); r = (((int) gui_red - scr_red) * gui_alpha / 255 + scr_red) & 255; g = (((int) gui_green - scr_green) * gui_alpha / 255 + scr_green) & 255; b = (((int) gui_blue - scr_blue) * gui_alpha / 255 + scr_blue) & 255; } XBuf[(y+8)*256+x] = gui_colour_rgb(r, g, b); } } return; }