1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <malloc.h>
4 #include <string.h>
5 #include <ctype.h>
6 #include <algorithm>
7 #include <vector>
8 #include <assert.h>
9 #include <map>
10 #include <string>
11
12 using std::min;
13 using std::max;
14
15 #ifdef __linux
16 #include <unistd.h>
17 #include <sys/types.h>
18 #include <sys/wait.h>
19 #endif
20
21 extern "C" {
22 #include "lua.h"
23 #include "lauxlib.h"
24 #include "lualib.h"
25 #include "lstate.h"
26 }
27
28 #include "burner.h"
29 #ifdef WIN32
30 #include <direct.h>
31 #include "win32/resource.h"
32 #endif
33 #include "luaengine.h"
34 #include "luasav.h"
35 #include "../cpu/m68000_intf.h"
36 #include "../cpu/z80/z80.h"
37 extern Z80_Regs Z80;
38
39 #ifndef TRUE
40 #define TRUE 1
41 #define FALSE 0
42 #endif
43
44 #ifndef inline
45 #define inline __inline
46 #endif
47
48 static void(*info_print)(INT64 uid, const char* str);
49 static void(*info_onstart)(INT64 uid);
50 static void(*info_onstop)(INT64 uid);
51 static INT64 info_uid;
52 #ifdef WIN32
53 extern INT_PTR CALLBACK DlgLuaScriptDialog(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
54 extern void PrintToWindowConsole(INT64 hDlgAsInt, const char* str);
55 extern void WinLuaOnStart(INT64 hDlgAsInt);
56 extern void WinLuaOnStop(INT64 hDlgAsInt);
57 #endif
58
59 void EmulatorAppDoFast(bool dofast);
60
61 static lua_State *LUA;
62
63 // Screen
64 static UINT8 *XBuf;
65 static int iScreenWidth = 320;
66 static int iScreenHeight = 240;
67 static int iScreenBpp = 4;
68 static int iScreenPitch = 1024;
69 static int LUA_SCREEN_WIDTH = 320;
70 static int LUA_SCREEN_HEIGHT = 240;
71
72 // Current working directory of the script
73 static char luaCWD [_MAX_PATH] = {0};
74 static char fbnCWD [_MAX_PATH] = {0};
75
76 // Are we running any code right now?
77 static char *luaScriptName = NULL;
78
79 // Are we running any code right now?
80 static int luaRunning = FALSE;
81
82 // True at the frame boundary, false otherwise.
83 static int frameBoundary = FALSE;
84
85 // The execution speed we're running at.
86 static enum {SPEED_NORMAL, SPEED_NOTHROTTLE, SPEED_TURBO, SPEED_MAXIMUM} speedmode = SPEED_NORMAL;
87
88 // Rerecord count skip mode
89 static int skipRerecords = FALSE;
90
91 // Used by the registry to find our functions
92 static const char *frameAdvanceThread = "FBA.FrameAdvance";
93 static const char *guiCallbackTable = "FBA.GUI";
94
95 // True if there's a thread waiting to run after a run of frame-advance.
96 static int frameAdvanceWaiting = FALSE;
97
98 // Transparency strength. 255=opaque, 0=so transparent it's invisible
99 static int transparencyModifier = 255;
100
101 // Our joypads.
102 static short lua_joypads[0x0100];
103 static UINT8 lua_joypads_used;
104
105 static UINT8 gui_enabled = TRUE;
106 static enum { GUI_USED_SINCE_LAST_DISPLAY, GUI_USED_SINCE_LAST_FRAME, GUI_CLEAR } gui_used = GUI_CLEAR;
107 static UINT8 *gui_data = NULL;
108
109 static int MAX_TRIES = 1500;
110
111 // Protects Lua calls from going nuts.
112 // We set this to a big number like MAX_TRIES and decrement it
113 // over time. The script gets knifed once this reaches zero.
114 static int numTries;
115
116 // number of registered memory functions (1 per hooked byte)
117 static unsigned int numMemHooks;
118
119 #ifdef _MSC_VER
120 #define snprintf _snprintf
121 #define vscprintf _vscprintf
122 #else
123 #define stricmp strcasecmp
124 #define strnicmp strncasecmp
125 #define __forceinline __attribute__((always_inline))
126 #endif
127
128 static const char* luaCallIDStrings [] =
129 {
130 "CALL_BEFOREEMULATION",
131 "CALL_AFTEREMULATION",
132 "CALL_BEFOREEXIT",
133 "CALL_ONSTART",
134
135 "CALL_HOTKEY_1",
136 "CALL_HOTKEY_2",
137 "CALL_HOTKEY_3",
138 "CALL_HOTKEY_4",
139 "CALL_HOTKEY_5",
140 "CALL_HOTKEY_6",
141 "CALL_HOTKEY_7",
142 "CALL_HOTKEY_8",
143 "CALL_HOTKEY_9",
144 };
145
146 static char* rawToCString(lua_State* L, int idx=0);
147 static const char* toCString(lua_State* L, int idx=0);
148
149 static const char* luaMemHookTypeStrings [] =
150 {
151 "MEMHOOK_WRITE",
152 "MEMHOOK_READ",
153 "MEMHOOK_EXEC",
154
155 "MEMHOOK_WRITE_SUB",
156 "MEMHOOK_READ_SUB",
157 "MEMHOOK_EXEC_SUB",
158 };
159 //static const int _makeSureWeHaveTheRightNumberOfStrings2 [sizeof(luaMemHookTypeStrings)/sizeof(*luaMemHookTypeStrings) == LUAMEMHOOK_COUNT ? 1 : 0];
160
161 /**
162 * Resets emulator speed / pause states after script exit.
163 * (Actually, FBA doesn't do any of these. They were very annoying.)
164 */
FBA_LuaOnStop()165 static void FBA_LuaOnStop() {
166 luaRunning = FALSE;
167 lua_joypads_used = 0;
168 gui_used = GUI_CLEAR;
169 }
170
171
172 /**
173 * Asks Lua if it wants control of the emulator's speed.
174 * Returns 0 if no, 1 if yes. If yes, caller should also
175 * consult FBA_LuaFrameSkip().
176 */
FBA_LuaSpeed()177 int FBA_LuaSpeed() {
178 if (!LUA || !luaRunning)
179 return 0;
180
181 switch (speedmode) {
182 case SPEED_NOTHROTTLE:
183 case SPEED_TURBO:
184 case SPEED_MAXIMUM:
185 return 1;
186 case SPEED_NORMAL:
187 default:
188 return 0;
189 }
190 }
191
192
193 /**
194 * Asks Lua if it wants control whether this frame is skipped.
195 * Returns 0 if no, 1 if frame should be skipped, -1 if it should not be.
196 */
FBA_LuaFrameSkip()197 int FBA_LuaFrameSkip() {
198 if (!LUA || !luaRunning)
199 return 0;
200
201 switch (speedmode) {
202 case SPEED_NORMAL:
203 return 0;
204 case SPEED_NOTHROTTLE:
205 return -1;
206 case SPEED_TURBO:
207 return 0;
208 case SPEED_MAXIMUM:
209 return 1;
210 }
211
212 return 0;
213 }
214
215
216 ///////////////////////////
217
218 // fba.hardreset()
fba_hardreset(lua_State * L)219 static int fba_hardreset(lua_State *L) {
220 StartFromReset(NULL);
221 return 1;
222 }
223
224 // string fba.romname()
225 //
226 // Returns the name of the running game.
fba_romname(lua_State * L)227 static int fba_romname(lua_State *L) {
228 lua_pushstring(L, BurnDrvGetTextA(DRV_NAME));
229 return 1;
230 }
231
232 // string fba.gamename()
233 //
234 // Returns the name of the source file for the running game.
fba_gamename(lua_State * L)235 static int fba_gamename(lua_State *L) {
236 lua_pushstring(L, BurnDrvGetTextA(DRV_FULLNAME));
237 return 1;
238 }
239
240 // string fba.parentname()
241 //
242 // Returns the name of the source file for the running game.
fba_parentname(lua_State * L)243 static int fba_parentname(lua_State *L) {
244 if (BurnDrvGetTextA(DRV_PARENT))
245 lua_pushstring(L, BurnDrvGetTextA(DRV_PARENT));
246 else
247 lua_pushstring(L, "0");
248 return 1;
249 }
250
251 // string fba.sourcename()
252 //
253 // Returns the name of the source file for the running game.
fba_sourcename(lua_State * L)254 static int fba_sourcename(lua_State *L) {
255 if (BurnDrvGetTextA(DRV_BOARDROM))
256 lua_pushstring(L, BurnDrvGetTextA(DRV_BOARDROM));
257 else
258 lua_pushstring(L, BurnDrvGetTextA(DRV_SYSTEM));
259 return 1;
260 }
261
262 // fba.speedmode(string mode)
263 //
264 // Takes control of the emulation speed
265 // of the system. Normal is normal speed (60fps, 50 for PAL),
266 // nothrottle disables speed control but renders every frame,
267 // turbo renders only a few frames in order to speed up emulation,
268 // maximum renders no frames
fba_speedmode(lua_State * L)269 static int fba_speedmode(lua_State *L) {
270 const char *mode = luaL_checkstring(L,1);
271
272 if (strcmp(mode, "normal")==0) {
273 speedmode = SPEED_NORMAL;
274 EmulatorAppDoFast(0);
275 } else if (strcmp(mode, "turbo")==0) {
276 speedmode = SPEED_TURBO;
277 EmulatorAppDoFast(1);
278 } else
279 luaL_error(L, "Invalid mode %s to fba.speedmode",mode);
280
281 return 0;
282 }
283
284
285 // fba.frameadvance()
286 //
287 // Executes a frame advance. Occurs by yielding the coroutine, then re-running
288 // when we break out.
fba_frameadvance(lua_State * L)289 static int fba_frameadvance(lua_State *L) {
290 // We're going to sleep for a frame-advance. Take notes.
291
292 if (frameAdvanceWaiting)
293 return luaL_error(L, "can't call fba.frameadvance() from here");
294
295 frameAdvanceWaiting = TRUE;
296
297 // Now we can yield to the main
298 return lua_yield(L, 0);
299 }
300
301
302 // fba.pause()
303 //
304 // Pauses the emulator, function "waits" until the user unpauses.
305 // This function MAY be called from a non-frame boundary, but the frame
306 // finishes executing anyways. In this case, the function returns immediately.
fba_pause(lua_State * L)307 static int fba_pause(lua_State *L) {
308 SetPauseMode(1);
309 speedmode = SPEED_NORMAL;
310
311 // If it's on a frame boundary, we also yield.
312 frameAdvanceWaiting = TRUE;
313 return lua_yield(L, 0);
314 }
315
316
317 // fba.unpause()
fba_unpause(lua_State * L)318 static int fba_unpause(lua_State *L) {
319 SetPauseMode(0);
320
321 return lua_yield(L, 0);
322 }
323
324 // int fba.screenwidth()
325 //
326 // Gets the screen width
fba_screenwidth(lua_State * L)327 int fba_screenwidth(lua_State *L) {
328 lua_pushinteger(L, iScreenWidth);
329 return 1;
330 }
331 // int fba.screenheight()
332 //
333 // Gets the screen height
fba_screenheight(lua_State * L)334 int fba_screenheight(lua_State *L) {
335 lua_pushinteger(L, iScreenHeight);
336 return 1;
337 }
338
isalphaorunderscore(char c)339 static inline bool isalphaorunderscore(char c)
340 {
341 return isalpha(c) || c == '_';
342 }
343
344 static std::vector<const void*> s_tableAddressStack; // prevents infinite recursion of a table within a table (when cycle is found, print something like table:parent)
345 static std::vector<const void*> s_metacallStack; // prevents infinite recursion if something's __tostring returns another table that contains that something (when cycle is found, print the inner result without using __tostring)
346
347 #define APPENDPRINT { int _n = snprintf(ptr, remaining,
348 #define END ); if(_n >= 0) { ptr += _n; remaining -= _n; } else { remaining = 0; } }
toCStringConverter(lua_State * L,int i,char * & ptr,int & remaining)349 static void toCStringConverter(lua_State* L, int i, char*& ptr, int& remaining)
350 {
351 if(remaining <= 0)
352 return;
353
354 // const char* str = ptr; // for debugging
355
356 // if there is a __tostring metamethod then call it
357 int usedMeta = luaL_callmeta(L, i, "__tostring");
358 if(usedMeta)
359 {
360 std::vector<const void*>::const_iterator foundCycleIter = std::find(s_metacallStack.begin(), s_metacallStack.end(), lua_topointer(L,i));
361 if(foundCycleIter != s_metacallStack.end())
362 {
363 lua_pop(L, 1);
364 usedMeta = false;
365 }
366 else
367 {
368 s_metacallStack.push_back(lua_topointer(L,i));
369 i = lua_gettop(L);
370 }
371 }
372
373 switch(lua_type(L, i))
374 {
375 case LUA_TNONE: break;
376 case LUA_TNIL: APPENDPRINT "nil" END break;
377 case LUA_TBOOLEAN: APPENDPRINT lua_toboolean(L,i) ? "true" : "false" END break;
378 case LUA_TSTRING: APPENDPRINT "%s",lua_tostring(L,i) END break;
379 case LUA_TNUMBER: APPENDPRINT "%.12g",lua_tonumber(L,i) END break;
380 case LUA_TFUNCTION:
381 if((L->base + i-1)->value.gc->cl.c.isC)
382 {
383 //lua_CFunction func = lua_tocfunction(L, i);
384 //std::map<lua_CFunction, const char*>::iterator iter = s_cFuncInfoMap.find(func);
385 //if(iter == s_cFuncInfoMap.end())
386 goto defcase;
387 //APPENDPRINT "function(%s)", iter->second END
388 }
389 else
390 {
391 APPENDPRINT "function(" END
392 Proto* p = (L->base + i-1)->value.gc->cl.l.p;
393 int numParams = p->numparams + (p->is_vararg?1:0);
394 for (int n=0; n<p->numparams; n++)
395 {
396 APPENDPRINT "%s", getstr(p->locvars[n].varname) END
397 if(n != numParams-1)
398 APPENDPRINT "," END
399 }
400 if(p->is_vararg)
401 APPENDPRINT "..." END
402 APPENDPRINT ")" END
403 }
404 break;
405 defcase:default: APPENDPRINT "%s:%p",luaL_typename(L,i),lua_topointer(L,i) END break;
406 case LUA_TTABLE:
407 {
408 // first make sure there's enough stack space
409 if(!lua_checkstack(L, 4))
410 {
411 // note that even if lua_checkstack never returns false,
412 // that doesn't mean we didn't need to call it,
413 // because calling it retrieves stack space past LUA_MINSTACK
414 goto defcase;
415 }
416
417 std::vector<const void*>::const_iterator foundCycleIter = std::find(s_tableAddressStack.begin(), s_tableAddressStack.end(), lua_topointer(L,i));
418 if(foundCycleIter != s_tableAddressStack.end())
419 {
420 int parentNum = s_tableAddressStack.end() - foundCycleIter;
421 if(parentNum > 1)
422 APPENDPRINT "%s:parent^%d",luaL_typename(L,i),parentNum END
423 else
424 APPENDPRINT "%s:parent",luaL_typename(L,i) END
425 }
426 else
427 {
428 s_tableAddressStack.push_back(lua_topointer(L,i));
429 // struct Scope { ~Scope(){ s_tableAddressStack.pop_back(); } } scope;
430
431 APPENDPRINT "{" END
432
433 lua_pushnil(L); // first key
434 int keyIndex = lua_gettop(L);
435 int valueIndex = keyIndex + 1;
436 bool first = true;
437 bool skipKey = true; // true if we're still in the "array part" of the table
438 lua_Number arrayIndex = (lua_Number)0;
439 while(lua_next(L, i))
440 {
441 if(first)
442 first = false;
443 else
444 APPENDPRINT ", " END
445 if(skipKey)
446 {
447 arrayIndex += (lua_Number)1;
448 bool keyIsNumber = (lua_type(L, keyIndex) == LUA_TNUMBER);
449 skipKey = keyIsNumber && (lua_tonumber(L, keyIndex) == arrayIndex);
450 }
451 if(!skipKey)
452 {
453 bool keyIsString = (lua_type(L, keyIndex) == LUA_TSTRING);
454 bool invalidLuaIdentifier = (!keyIsString || !isalphaorunderscore(*lua_tostring(L, keyIndex)));
455 if(invalidLuaIdentifier) {
456 if(keyIsString)
457 APPENDPRINT "['" END
458 else
459 APPENDPRINT "[" END
460 }
461
462 toCStringConverter(L, keyIndex, ptr, remaining); // key
463
464 if(invalidLuaIdentifier)
465 if(keyIsString)
466 APPENDPRINT "']=" END
467 else
468 APPENDPRINT "]=" END
469 else
470 APPENDPRINT "=" END
471 }
472
473 bool valueIsString = (lua_type(L, valueIndex) == LUA_TSTRING);
474 if(valueIsString)
475 APPENDPRINT "'" END
476
477 toCStringConverter(L, valueIndex, ptr, remaining); // value
478
479 if(valueIsString)
480 APPENDPRINT "'" END
481
482 lua_pop(L, 1);
483
484 if(remaining <= 0)
485 {
486 lua_settop(L, keyIndex-1); // stack might not be clean yet if we're breaking early
487 break;
488 }
489 }
490 APPENDPRINT "}" END
491 }
492 } break;
493 }
494
495 if(usedMeta)
496 {
497 s_metacallStack.pop_back();
498 lua_pop(L, 1);
499 }
500 }
501
502 static const int s_tempStrMaxLen = 64 * 1024;
503 static char s_tempStr [s_tempStrMaxLen];
504
rawToCString(lua_State * L,int idx)505 static char* rawToCString(lua_State* L, int idx)
506 {
507 int a = idx>0 ? idx : 1;
508 int n = idx>0 ? idx : lua_gettop(L);
509
510 char* ptr = s_tempStr;
511 *ptr = 0;
512
513 int remaining = s_tempStrMaxLen;
514 for(int i = a; i <= n; i++)
515 {
516 toCStringConverter(L, i, ptr, remaining);
517 if(i != n)
518 APPENDPRINT " " END
519 }
520
521 if(remaining < 3)
522 {
523 while(remaining < 6)
524 remaining++, ptr--;
525 APPENDPRINT "..." END
526 }
527 APPENDPRINT "\r\n" END
528 // the trailing newline is so print() can avoid having to do wasteful things to print its newline
529 // (string copying would be wasteful and calling info.print() twice can be extremely slow)
530 // at the cost of functions that don't want the newline needing to trim off the last two characters
531 // (which is a very fast operation and thus acceptable in this case)
532
533 return s_tempStr;
534 }
535 #undef APPENDPRINT
536 #undef END
537
538
539 // replacement for luaB_tostring() that is able to show the contents of tables (and formats numbers better, and show function prototypes)
540 // can be called directly from lua via tostring(), assuming tostring hasn't been reassigned
tostring(lua_State * L)541 static int tostring(lua_State *L)
542 {
543 char* str = rawToCString(L);
544 str[strlen(str)-2] = 0; // hack: trim off the \r\n (which is there to simplify the print function's task)
545 lua_pushstring(L, str);
546 return 1;
547 }
548
549 // like rawToCString, but will check if the global Lua function tostring()
550 // has been replaced with a custom function, and call that instead if so
toCString(lua_State * L,int idx)551 static const char* toCString(lua_State* L, int idx)
552 {
553 int a = idx>0 ? idx : 1;
554 int n = idx>0 ? idx : lua_gettop(L);
555 lua_getglobal(L, "tostring");
556 lua_CFunction cf = lua_tocfunction(L,-1);
557 if(cf == tostring) // optimization: if using our own C tostring function, we can bypass the call through Lua and all the string object allocation that would entail
558 {
559 lua_pop(L,1);
560 return rawToCString(L, idx);
561 }
562 else // if the user overrided the tostring function, we have to actually call it and store the temporarily allocated string it returns
563 {
564 lua_pushstring(L, "");
565 for (int i=a; i<=n; i++) {
566 lua_pushvalue(L, -2); // function to be called
567 lua_pushvalue(L, i); // value to print
568 lua_call(L, 1, 1);
569 if(lua_tostring(L, -1) == NULL)
570 luaL_error(L, LUA_QL("tostring") " must return a string to " LUA_QL("print"));
571 lua_pushstring(L, (i<n) ? " " : "\r\n");
572 lua_concat(L, 3);
573 }
574 const char* str = lua_tostring(L, -1);
575 strncpy(s_tempStr, str, s_tempStrMaxLen);
576 s_tempStr[s_tempStrMaxLen-1] = 0;
577 lua_pop(L, 2);
578 return s_tempStr;
579 }
580 }
581
582 // replacement for luaB_print() that goes to the appropriate textbox instead of stdout
print(lua_State * L)583 static int print(lua_State *L)
584 {
585 const char* str = toCString(L);
586
587 int uid = info_uid;//luaStateToUIDMap[L->l_G->mainthread];
588 //LuaContextInfo& info = GetCurrentInfo();
589
590 if(info_print)
591 info_print(uid, str);
592 else
593 puts(str);
594
595 //worry(L, 100);
596 return 0;
597 }
598
599 char fba_message_buffer[1024];
600 // fba.message(string msg)
601 //
602 // Displays the given message on the screen.
fba_message(lua_State * L)603 static int fba_message(lua_State *L) {
604 const char *msg = luaL_checkstring(L,1);
605 sprintf(fba_message_buffer, "%s", msg);
606 VidSNewTinyMsg(_AtoT(fba_message_buffer));
607
608 return 0;
609 }
610
611 // provides an easy way to copy a table from Lua
612 // (simple assignment only makes an alias, but sometimes an independent table is desired)
613 // currently this function only performs a shallow copy,
614 // but I think it should be changed to do a deep copy (possibly of configurable depth?)
615 // that maintains the internal table reference structure
copytable(lua_State * L)616 static int copytable(lua_State *L)
617 {
618 int origIndex = 1; // we only care about the first argument
619 int origType = lua_type(L, origIndex);
620 if(origType == LUA_TNIL)
621 {
622 lua_pushnil(L);
623 return 1;
624 }
625 if(origType != LUA_TTABLE)
626 {
627 luaL_typerror(L, 1, lua_typename(L, LUA_TTABLE));
628 lua_pushnil(L);
629 return 1;
630 }
631
632 lua_createtable(L, lua_objlen(L,1), 0);
633 int copyIndex = lua_gettop(L);
634
635 lua_pushnil(L); // first key
636 int keyIndex = lua_gettop(L);
637 int valueIndex = keyIndex + 1;
638
639 while(lua_next(L, origIndex))
640 {
641 lua_pushvalue(L, keyIndex);
642 lua_pushvalue(L, valueIndex);
643 lua_rawset(L, copyIndex); // copytable[key] = value
644 lua_pop(L, 1);
645 }
646
647 // copy the reference to the metatable as well, if any
648 if(lua_getmetatable(L, origIndex))
649 lua_setmetatable(L, copyIndex);
650
651 return 1; // return the new table
652 }
653
654 // because print traditionally shows the address of tables,
655 // and the print function I provide instead shows the contents of tables,
656 // I also provide this function
657 // (otherwise there would be no way to see a table's address, AFAICT)
addressof(lua_State * L)658 static int addressof(lua_State *L)
659 {
660 const void* ptr = lua_topointer(L,-1);
661 lua_pushinteger(L, (lua_Integer)ptr);
662 return 1;
663 }
664
fba_registerbefore(lua_State * L)665 static int fba_registerbefore(lua_State *L) {
666 if (!lua_isnil(L,1))
667 luaL_checktype(L, 1, LUA_TFUNCTION);
668 lua_settop(L,1);
669 lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEMULATION]);
670 lua_insert(L,1);
671 lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEMULATION]);
672 return 1;
673 }
674
675
fba_registerafter(lua_State * L)676 static int fba_registerafter(lua_State *L) {
677 if (!lua_isnil(L,1))
678 luaL_checktype(L, 1, LUA_TFUNCTION);
679 lua_settop(L,1);
680 lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTEREMULATION]);
681 lua_insert(L,1);
682 lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTEREMULATION]);
683 return 1;
684 }
685
686
fba_registerexit(lua_State * L)687 static int fba_registerexit(lua_State *L) {
688 if (!lua_isnil(L,1))
689 luaL_checktype(L, 1, LUA_TFUNCTION);
690 lua_settop(L,1);
691 lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]);
692 lua_insert(L,1);
693 lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]);
694 return 1;
695 }
696
fba_registerstart(lua_State * L)697 static int fba_registerstart(lua_State *L) {
698 if (!lua_isnil(L,1))
699 luaL_checktype(L, 1, LUA_TFUNCTION);
700 lua_settop(L,1);
701 lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_ONSTART]);
702 lua_insert(L,1);
703 lua_pushvalue(L,-1); // copy the function so we can also call it
704 lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_ONSTART]);
705 if (!lua_isnil(L,-1))
706 lua_call(L,0,0); // call the function now since the game has already started and this start function hasn't been called yet
707 return 1;
708 }
709
710
711
HandleCallbackError(lua_State * L)712 void HandleCallbackError(lua_State* L)
713 {
714 //if(L->errfunc || L->errorJmp)
715 // luaL_error(L, "%s", lua_tostring(L,-1));
716 //else
717 {
718 lua_pushnil(L);
719 lua_setfield(L, LUA_REGISTRYINDEX, guiCallbackTable);
720
721 // Error?
722 #ifdef WIN32
723 MessageBoxA(hScrnWnd, lua_tostring(L,-1), "Lua run error", MB_OK | MB_ICONSTOP);
724 #else
725 fprintf(stderr, "Lua thread bombed out: %s\n", lua_tostring(L,-1));
726 #endif
727
728 FBA_LuaStop();
729 }
730 }
731
732
733 // the purpose of this structure is to provide a way of
734 // QUICKLY determining whether a memory address range has a hook associated with it,
735 // with a bias toward fast rejection because the majority of addresses will not be hooked.
736 // (it must not use any part of Lua or perform any per-script operations,
737 // otherwise it would definitely be too slow.)
738 // calculating the regions when a hook is added/removed may be slow,
739 // but this is an intentional tradeoff to obtain a high speed of checking during later execution
740 struct TieredRegion
741 {
742 template<unsigned int maxGap>
743 struct Region
744 {
745 struct Island
746 {
747 unsigned int start;
748 unsigned int end;
ContainsTieredRegion::Region::Island749 __forceinline bool Contains(unsigned int address, int size) const { return address < end && address+size > start; }
750 };
751 std::vector<Island> islands;
752
CalculateTieredRegion::Region753 void Calculate(const std::vector<unsigned int>& bytes)
754 {
755 islands.clear();
756
757 unsigned int lastEnd = ~0;
758
759 std::vector<unsigned int>::const_iterator iter = bytes.begin();
760 std::vector<unsigned int>::const_iterator end = bytes.end();
761 for(; iter != end; ++iter)
762 {
763 unsigned int addr = *iter;
764 if(addr < lastEnd || addr > lastEnd + (long long)maxGap)
765 {
766 islands.push_back(Island());
767 islands.back().start = addr;
768 }
769 islands.back().end = addr+1;
770 lastEnd = addr+1;
771 }
772 }
773
ContainsTieredRegion::Region774 bool Contains(unsigned int address, int size) const
775 {
776 typename std::vector<Island>::const_iterator iter = islands.begin();
777 typename std::vector<Island>::const_iterator end = islands.end();
778 for(; iter != end; ++iter)
779 if(iter->Contains(address, size))
780 return true;
781 return false;
782 }
783 };
784
785 Region<0xFFFFFFFF> broad;
786 Region<0x1000> mid;
787 Region<0> narrow;
788
CalculateTieredRegion789 void Calculate(std::vector<unsigned int>& bytes)
790 {
791 std::sort(bytes.begin(), bytes.end());
792
793 broad.Calculate(bytes);
794 mid.Calculate(bytes);
795 narrow.Calculate(bytes);
796 }
797
TieredRegionTieredRegion798 TieredRegion()
799 {
800 // Calculate(std::vector<unsigned int>()); // TODO: this doesn't compile... So I wrote the next two lines (I should learn C++)
801 std::vector<unsigned int> bytes;
802 Calculate(bytes);
803 }
804
NotEmptyTieredRegion805 __forceinline int NotEmpty()
806 {
807 return broad.islands.size();
808 }
809
810 // note: it is illegal to call this if NotEmpty() returns 0
ContainsTieredRegion811 __forceinline bool Contains(unsigned int address, int size)
812 {
813 return broad.islands[0].Contains(address,size) &&
814 mid.Contains(address,size) &&
815 narrow.Contains(address,size);
816 }
817 };
818 TieredRegion hookedRegions [LUAMEMHOOK_COUNT];
819
820
CalculateMemHookRegions(LuaMemHookType hookType)821 static void CalculateMemHookRegions(LuaMemHookType hookType)
822 {
823 std::vector<unsigned int> hookedBytes;
824 // std::map<int, LuaContextInfo*>::iterator iter = luaContextInfo.begin();
825 // std::map<int, LuaContextInfo*>::iterator end = luaContextInfo.end();
826 // while(iter != end)
827 // {
828 // LuaContextInfo& info = *iter->second;
829 if(/*info.*/ numMemHooks)
830 {
831 // lua_State* L = info.L;
832 if(LUA)
833 {
834 lua_settop(LUA, 0);
835 lua_getfield(LUA, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]);
836 lua_pushnil(LUA);
837 while(lua_next(LUA, -2))
838 {
839 if(lua_isfunction(LUA, -1))
840 {
841 unsigned int addr = lua_tointeger(LUA, -2);
842 hookedBytes.push_back(addr);
843 }
844 lua_pop(LUA, 1);
845 }
846 lua_settop(LUA, 0);
847 }
848 }
849 // ++iter;
850 // }
851 hookedRegions[hookType].Calculate(hookedBytes);
852 }
853
CallRegisteredLuaMemHook_LuaMatch(unsigned int address,int size,unsigned int value,LuaMemHookType hookType)854 static void CallRegisteredLuaMemHook_LuaMatch(unsigned int address, int size, unsigned int value, LuaMemHookType hookType)
855 {
856 // std::map<int, LuaContextInfo*>::iterator iter = luaContextInfo.begin();
857 // std::map<int, LuaContextInfo*>::iterator end = luaContextInfo.end();
858 // while(iter != end)
859 // {
860 // LuaContextInfo& info = *iter->second;
861 if(/*info.*/ numMemHooks)
862 {
863 // lua_State* L = info.L;
864 if(LUA/* && !info.panic*/)
865 {
866 #ifdef USE_INFO_STACK
867 infoStack.insert(infoStack.begin(), &info);
868 struct Scope { ~Scope(){ infoStack.erase(infoStack.begin()); } } scope;
869 #endif
870 lua_settop(LUA, 0);
871 lua_getfield(LUA, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]);
872 for(int i = address; i != address+size; i++)
873 {
874 lua_rawgeti(LUA, -1, i);
875 if (lua_isfunction(LUA, -1))
876 {
877 bool wasRunning = (luaRunning!=0) /*info.running*/;
878 luaRunning /*info.running*/ = true;
879 //RefreshScriptSpeedStatus();
880 lua_pushinteger(LUA, address);
881 lua_pushinteger(LUA, size);
882 int errorcode = lua_pcall(LUA, 2, 0, 0);
883 luaRunning /*info.running*/ = wasRunning;
884 //RefreshScriptSpeedStatus();
885 if (errorcode)
886 {
887 HandleCallbackError(LUA);
888 //int uid = iter->first;
889 //HandleCallbackError(LUA,info,uid,true);
890 }
891 break;
892 }
893 else
894 {
895 lua_pop(LUA,1);
896 }
897 }
898 lua_settop(LUA, 0);
899 }
900 }
901 // ++iter;
902 // }
903 }
CallRegisteredLuaMemHook(unsigned int address,int size,unsigned int value,LuaMemHookType hookType)904 void CallRegisteredLuaMemHook(unsigned int address, int size, unsigned int value, LuaMemHookType hookType)
905 {
906 // performance critical! (called VERY frequently)
907 // I suggest timing a large number of calls to this function in Release if you change anything in here,
908 // before and after, because even the most innocent change can make it become 30% to 400% slower.
909 // a good amount to test is: 100000000 calls with no hook set, and another 100000000 with a hook set.
910 // (on my system that consistently took 200 ms total in the former case and 350 ms total in the latter case)
911 if(hookedRegions[hookType].NotEmpty())
912 {
913 //if((hookType <= LUAMEMHOOK_EXEC) && (address >= 0xE00000))
914 // address |= 0xFF0000; // account for mirroring of RAM
915 if(hookedRegions[hookType].Contains(address, size))
916 CallRegisteredLuaMemHook_LuaMatch(address, size, value, hookType); // something has hooked this specific address
917 }
918 }
919
CallRegisteredLuaFunctions(LuaCallID calltype)920 void CallRegisteredLuaFunctions(LuaCallID calltype)
921 {
922 assert((unsigned int)calltype < (unsigned int)LUACALL_COUNT);
923 const char* idstring = luaCallIDStrings[calltype];
924
925 if (!LUA)
926 return;
927
928 lua_settop(LUA, 0);
929 lua_getfield(LUA, LUA_REGISTRYINDEX, idstring);
930
931 int errorcode = 0;
932 if (lua_isfunction(LUA, -1))
933 {
934 chdir(luaCWD);
935 errorcode = lua_pcall(LUA, 0, 0, 0);
936 chdir(fbnCWD);
937 if (errorcode)
938 HandleCallbackError(LUA);
939 }
940 else
941 {
942 lua_pop(LUA, 1);
943 }
944 }
945
946
947
948
is_little_endian(lua_State * L,int argn)949 static int is_little_endian(lua_State *L,int argn)
950 {
951 int littleEndian;
952 if(lua_gettop(L) >= argn)
953 littleEndian = lua_toboolean(L,argn);
954 else
955 littleEndian = 0;
956 return littleEndian;
957 }
958
memory_readbyte(lua_State * L)959 static int memory_readbyte(lua_State *L)
960 {
961 lua_pushinteger(L, ReadValueAtHardwareAddress(luaL_checkinteger(L,1),1,0));
962 return 1;
963 }
964
memory_readbytesigned(lua_State * L)965 static int memory_readbytesigned(lua_State *L) {
966 signed char c = (signed char)ReadValueAtHardwareAddress(luaL_checkinteger(L,1),1,0);
967 lua_pushinteger(L, c);
968 return 1;
969 }
970
memory_readword(lua_State * L)971 static int memory_readword(lua_State *L)
972 {
973 lua_pushinteger(L, ReadValueAtHardwareAddress(luaL_checkinteger(L,1),2,is_little_endian(L,2)));
974 return 1;
975 }
976
memory_readwordsigned(lua_State * L)977 static int memory_readwordsigned(lua_State *L) {
978 signed short c = (signed short)ReadValueAtHardwareAddress(luaL_checkinteger(L,1),2,is_little_endian(L,2));
979 lua_pushinteger(L, c);
980 return 1;
981 }
982
memory_readdword(lua_State * L)983 static int memory_readdword(lua_State *L)
984 {
985 UINT32 addr = luaL_checkinteger(L,1);
986 UINT32 val = ReadValueAtHardwareAddress(addr,4,is_little_endian(L,2));
987
988 // lua_pushinteger doesn't work properly for 32bit system, does it?
989 if (val >= 0x80000000 && sizeof(int) <= 4)
990 lua_pushnumber(L, val);
991 else
992 lua_pushinteger(L, val);
993 return 1;
994 }
995
memory_readdwordsigned(lua_State * L)996 static int memory_readdwordsigned(lua_State *L) {
997 UINT32 addr = luaL_checkinteger(L,1);
998 INT32 val = (INT32)ReadValueAtHardwareAddress(addr,4,is_little_endian(L,2));
999
1000 lua_pushinteger(L, val);
1001 return 1;
1002 }
1003
memory_readbyterange(lua_State * L)1004 static int memory_readbyterange(lua_State *L) {
1005 int a,n;
1006 UINT32 address = luaL_checkinteger(L,1);
1007 int length = luaL_checkinteger(L,2);
1008
1009 if(length < 0)
1010 {
1011 address += length;
1012 length = -length;
1013 }
1014
1015 // push the array
1016 lua_createtable(L, abs(length), 0);
1017
1018 // put all the values into the (1-based) array
1019 for(a = address, n = 1; n <= length; a++, n++)
1020 {
1021 unsigned char value = ReadValueAtHardwareAddress(a,1,0);
1022 lua_pushinteger(L, value);
1023 lua_rawseti(L, -2, n);
1024 }
1025
1026 return 1;
1027 }
1028
memory_writebyte(lua_State * L)1029 static int memory_writebyte(lua_State *L)
1030 {
1031 WriteValueAtHardwareAddress(luaL_checkinteger(L,1), luaL_checkinteger(L,2),1,0);
1032 return 0;
1033 }
1034
memory_writeword(lua_State * L)1035 static int memory_writeword(lua_State *L)
1036 {
1037 WriteValueAtHardwareAddress(luaL_checkinteger(L,1), luaL_checkinteger(L,2),2,is_little_endian(L,3));
1038 return 0;
1039 }
1040
memory_writedword(lua_State * L)1041 static int memory_writedword(lua_State *L)
1042 {
1043 WriteValueAtHardwareAddress(luaL_checkinteger(L,1), luaL_checkinteger(L,2),4,is_little_endian(L,3));
1044 return 0;
1045 }
1046
1047
memory_registerHook(lua_State * L,LuaMemHookType hookType,int defaultSize)1048 static int memory_registerHook(lua_State* L, LuaMemHookType hookType, int defaultSize)
1049 {
1050 // get first argument: address
1051 unsigned int addr = luaL_checkinteger(L,1);
1052 if((addr & ~0xFFFFFFFF) == ~0xFFFFFFFF)
1053 addr &= 0xFFFFFFFF;
1054
1055 // get optional second argument: size
1056 int size = defaultSize;
1057 int funcIdx = 2;
1058 if(lua_isnumber(L,2))
1059 {
1060 size = luaL_checkinteger(L,2);
1061 if(size < 0)
1062 {
1063 size = -size;
1064 addr -= size;
1065 }
1066 funcIdx++;
1067 }
1068
1069 // check last argument: callback function
1070 bool clearing = lua_isnil(L,funcIdx);
1071 if(!clearing)
1072 luaL_checktype(L, funcIdx, LUA_TFUNCTION);
1073 lua_settop(L,funcIdx);
1074
1075 // get the address-to-callback table for this hook type of the current script
1076 lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]);
1077
1078 // count how many callback functions we'll be displacing
1079 int numFuncsAfter = clearing ? 0 : size;
1080 int numFuncsBefore = 0;
1081 for(unsigned int i = addr; i != addr+size; i++)
1082 {
1083 lua_rawgeti(L, -1, i);
1084 if(lua_isfunction(L, -1))
1085 numFuncsBefore++;
1086 lua_pop(L,1);
1087 }
1088
1089 // put the callback function in the address slots
1090 for(unsigned int i = addr; i != addr+size; i++)
1091 {
1092 lua_pushvalue(L, -2);
1093 lua_rawseti(L, -2, i);
1094 }
1095
1096 // adjust the count of active hooks
1097 //LuaContextInfo& info = GetCurrentInfo();
1098 /*info.*/ numMemHooks += numFuncsAfter - numFuncsBefore;
1099
1100 // re-cache regions of hooked memory across all scripts
1101 CalculateMemHookRegions(hookType);
1102
1103 //StopScriptIfFinished(luaStateToUIDMap[L]);
1104 return 0;
1105 }
1106
MatchHookTypeToCPU(lua_State * L,LuaMemHookType hookType)1107 LuaMemHookType MatchHookTypeToCPU(lua_State* L, LuaMemHookType hookType)
1108 {
1109 int cpuID = 0;
1110
1111 int cpunameIndex = 0;
1112 if(lua_type(L,2) == LUA_TSTRING)
1113 cpunameIndex = 2;
1114 else if(lua_type(L,3) == LUA_TSTRING)
1115 cpunameIndex = 3;
1116
1117 if(cpunameIndex)
1118 {
1119 const char* cpuName = lua_tostring(L, cpunameIndex);
1120 if(!_stricmp(cpuName, "sub"))
1121 cpuID = 1;
1122 lua_remove(L, cpunameIndex);
1123 }
1124
1125 switch(cpuID)
1126 {
1127 case 0:
1128 return hookType;
1129
1130 case 1:
1131 switch(hookType)
1132 {
1133 case LUAMEMHOOK_WRITE: return LUAMEMHOOK_WRITE_SUB;
1134 case LUAMEMHOOK_READ: return LUAMEMHOOK_READ_SUB;
1135 case LUAMEMHOOK_EXEC: return LUAMEMHOOK_EXEC_SUB;
1136 }
1137 }
1138 return hookType;
1139 }
1140
memory_registerwrite(lua_State * L)1141 static int memory_registerwrite(lua_State *L)
1142 {
1143 return memory_registerHook(L, MatchHookTypeToCPU(L,LUAMEMHOOK_WRITE), 1);
1144 }
memory_registerread(lua_State * L)1145 static int memory_registerread(lua_State *L)
1146 {
1147 return memory_registerHook(L, MatchHookTypeToCPU(L,LUAMEMHOOK_READ), 1);
1148 }
memory_registerexec(lua_State * L)1149 static int memory_registerexec(lua_State *L)
1150 {
1151 return memory_registerHook(L, MatchHookTypeToCPU(L,LUAMEMHOOK_EXEC), 1);
1152 }
1153
1154
1155 struct registerPointerMap
1156 {
1157 const char* registerName;
1158 unsigned int* pointer;
1159 int dataSize;
1160 };
1161
1162 #define RPM_ENTRY(name,var) {name, (unsigned int*)&var, sizeof(var)},
1163
1164 registerPointerMap m68000RegPointerMap [] = {
1165 /*
1166 RPM_ENTRY("pc", M68000_regs.pc)
1167 RPM_ENTRY("d0", M68000_regs.d[0])
1168 RPM_ENTRY("d1", M68000_regs.d[1])
1169 RPM_ENTRY("d2", M68000_regs.d[2])
1170 RPM_ENTRY("d3", M68000_regs.d[3])
1171 RPM_ENTRY("d4", M68000_regs.d[4])
1172 RPM_ENTRY("d5", M68000_regs.d[5])
1173 RPM_ENTRY("d6", M68000_regs.d[6])
1174 RPM_ENTRY("d7", M68000_regs.d[7])
1175 RPM_ENTRY("a0", M68000_regs.a[0])
1176 RPM_ENTRY("a1", M68000_regs.a[1])
1177 RPM_ENTRY("a2", M68000_regs.a[2])
1178 RPM_ENTRY("a3", M68000_regs.a[3])
1179 RPM_ENTRY("a4", M68000_regs.a[4])
1180 RPM_ENTRY("a5", M68000_regs.a[5])
1181 RPM_ENTRY("a6", M68000_regs.a[6])
1182 RPM_ENTRY("a7", M68000_regs.a[7])
1183 */
1184 {}
1185 };
1186
1187 registerPointerMap z80RegPointerMap [] = {
1188 /* RPM_ENTRY("prvpc", Z80.prvpc.w.l)
1189 RPM_ENTRY("pc", Z80.pc.w.l)
1190 RPM_ENTRY("sp", Z80.sp.w.l)
1191 RPM_ENTRY("af", Z80.af.w.l)
1192 RPM_ENTRY("bc", Z80.bc.w.l)
1193 RPM_ENTRY("de", Z80.de.w.l)
1194 RPM_ENTRY("hl", Z80.hl.w.l)
1195 RPM_ENTRY("ix", Z80.ix.w.l)
1196 RPM_ENTRY("iy", Z80.iy.w.l)
1197 */
1198 {}
1199 };
1200
1201 struct cpuToRegisterMap
1202 {
1203 const char* cpuName;
1204 registerPointerMap* rpmap;
1205 }
1206 cpuToRegisterMaps [] =
1207 {
1208 {"m68000.", m68000RegPointerMap},
1209 {"z80.", z80RegPointerMap},
1210 };
1211
1212
1213 //DEFINE_LUA_FUNCTION(memory_getregister, "cpu_dot_registername_string")
memory_getregister(lua_State * L)1214 static int memory_getregister(lua_State *L)
1215 {
1216 const char* qualifiedRegisterName = luaL_checkstring(L,1);
1217 lua_settop(L,0);
1218 for(int cpu = 0; cpu < sizeof(cpuToRegisterMaps)/sizeof(*cpuToRegisterMaps); cpu++)
1219 {
1220 cpuToRegisterMap ctrm = cpuToRegisterMaps[cpu];
1221 int cpuNameLen = strlen(ctrm.cpuName);
1222 if(!strnicmp(qualifiedRegisterName, ctrm.cpuName, cpuNameLen))
1223 {
1224 qualifiedRegisterName += cpuNameLen;
1225 for(int reg = 0; ctrm.rpmap[reg].dataSize; reg++)
1226 {
1227 registerPointerMap rpm = ctrm.rpmap[reg];
1228 if(!stricmp(qualifiedRegisterName, rpm.registerName))
1229 {
1230 switch(rpm.dataSize)
1231 { default:
1232 case 1: lua_pushinteger(L, *(unsigned char*)rpm.pointer); break;
1233 case 2: lua_pushinteger(L, *(unsigned short*)rpm.pointer); break;
1234 case 4: lua_pushinteger(L, *(unsigned long*)rpm.pointer); break;
1235 }
1236 return 1;
1237 }
1238 }
1239 lua_pushnil(L);
1240 return 1;
1241 }
1242 }
1243 lua_pushnil(L);
1244 return 1;
1245 }
1246 //DEFINE_LUA_FUNCTION(memory_setregister, "cpu_dot_registername_string,value")
memory_setregister(lua_State * L)1247 static int memory_setregister(lua_State *L)
1248 {
1249 const char* qualifiedRegisterName = luaL_checkstring(L,1);
1250 unsigned long value = (unsigned long)(luaL_checkinteger(L,2));
1251 lua_settop(L,0);
1252 for(int cpu = 0; cpu < sizeof(cpuToRegisterMaps)/sizeof(*cpuToRegisterMaps); cpu++)
1253 {
1254 cpuToRegisterMap ctrm = cpuToRegisterMaps[cpu];
1255 int cpuNameLen = strlen(ctrm.cpuName);
1256 if(!_strnicmp(qualifiedRegisterName, ctrm.cpuName, cpuNameLen))
1257 {
1258 qualifiedRegisterName += cpuNameLen;
1259 for(int reg = 0; ctrm.rpmap[reg].dataSize; reg++)
1260 {
1261 registerPointerMap rpm = ctrm.rpmap[reg];
1262 if(!stricmp(qualifiedRegisterName, rpm.registerName))
1263 {
1264 switch(rpm.dataSize)
1265 { default:
1266 case 1: *(unsigned char*)rpm.pointer = (unsigned char)(value & 0xFF); break;
1267 case 2: *(unsigned short*)rpm.pointer = (unsigned short)(value & 0xFFFF); break;
1268 case 4: *(unsigned long*)rpm.pointer = value; break;
1269 }
1270 return 0;
1271 }
1272 }
1273 return 0;
1274 }
1275 }
1276 return 0;
1277 }
1278
1279 // table joypad.read()
1280 //
1281 // Reads the joypads as inputted by the user.
joy_get_internal(lua_State * L,bool reportUp,bool reportDown)1282 static int joy_get_internal(lua_State *L, bool reportUp, bool reportDown) {
1283 unsigned int i = 0;
1284 struct GameInp* pgi = NULL;
1285 unsigned short nThisVal;
1286
1287 lua_newtable(L);
1288
1289 // Update the values of all the inputs
1290 for (i = 0, pgi = GameInp; i < nGameInpCount; i++, pgi++) {
1291 if (pgi->nType == 0) {
1292 continue;
1293 }
1294 struct BurnInputInfo bii;
1295
1296 // Get the name of the input
1297 bii.szName = NULL;
1298 BurnDrvGetInputInfo(&bii, i);
1299
1300 // skip unused inputs
1301 if (bii.pVal == NULL) {
1302 continue;
1303 }
1304 if (bii.szName == NULL) {
1305 bii.szName = "";
1306 }
1307
1308 nThisVal = *pgi->Input.pVal;
1309 bool pressed = nThisVal != 0;
1310 if ((pressed && reportDown) || (!pressed && reportUp)) {
1311 if (bii.nType & BIT_DIGITAL && !(bii.nType & BIT_GROUP_CONSTANT))
1312 lua_pushboolean(L,pressed);
1313 else
1314 lua_pushinteger(L,nThisVal);
1315 lua_setfield(L, -2, bii.szName);
1316 }
1317 }
1318 return 1;
1319 }
1320 // joypad.get(which)
1321 // returns a table of every game button,
1322 // true meaning currently-held and false meaning not-currently-held
1323 // (as of last frame boundary)
1324 // this WILL read input from a currently-playing movie
joypad_get(lua_State * L)1325 static int joypad_get(lua_State *L)
1326 {
1327 return joy_get_internal(L, true, true);
1328 }
1329 // joypad.getdown(which)
1330 // returns a table of every game button that is currently held
joypad_getdown(lua_State * L)1331 static int joypad_getdown(lua_State *L)
1332 {
1333 return joy_get_internal(L, false, true);
1334 }
1335 // joypad.getup(which)
1336 // returns a table of every game button that is not currently held
joypad_getup(lua_State * L)1337 static int joypad_getup(lua_State *L)
1338 {
1339 return joy_get_internal(L, true, false);
1340 }
1341
1342 // joypad.set(table buttons)
1343 //
1344 // Sets the given buttons to be pressed during the next
1345 // frame advance. The table should have the right
1346 // keys (no pun intended) set.
joypad_set(lua_State * L)1347 static int joypad_set(lua_State *L) {
1348 struct GameInp* pgi = NULL;
1349 unsigned int i;
1350
1351 // table of buttons.
1352 luaL_checktype(L,1,LUA_TTABLE);
1353
1354 // Set up for taking control of the indicated controller
1355 lua_joypads_used = 1;
1356 memset(lua_joypads,0,sizeof(lua_joypads));
1357
1358 // Update the values of all the inputs
1359 for (i = 0, pgi = GameInp; i < nGameInpCount; i++, pgi++) {
1360 if (pgi->nType == 0) {
1361 continue;
1362 }
1363 struct BurnInputInfo bii;
1364
1365 // Get the name of the input
1366 bii.szName = NULL;
1367 BurnDrvGetInputInfo(&bii, i);
1368
1369 // skip unused inputs
1370 if (bii.pVal == NULL) {
1371 continue;
1372 }
1373 if (bii.szName == NULL) {
1374 bii.szName = "";
1375 }
1376
1377 lua_getfield(L, 1, bii.szName);
1378 if (!lua_isnil(L,-1)) {
1379 if (bii.nType & BIT_DIGITAL && !(bii.nType & BIT_GROUP_CONSTANT)) {
1380 if (lua_toboolean(L,-1))
1381 lua_joypads[i] = 1; // pressed
1382 else
1383 lua_joypads[i] = 2; // unpressed
1384 }
1385 else {
1386 lua_joypads[i] = lua_tonumber(L, -1);
1387 }
1388 // dprintf(_T("*JOYPAD*: '%s' : %d\n"),_AtoT(bii.szName),lua_joypads[i]);
1389 }
1390 lua_pop(L,1);
1391 }
1392
1393 return 0;
1394 }
1395
1396
1397 #ifdef WIN32
1398 char szSavestateFilename[MAX_PATH];
1399
GetSavestateFilename(int nSlot)1400 char *GetSavestateFilename(int nSlot) {
1401 sprintf(szSavestateFilename, "./savestates/%s slot %02x.fs", BurnDrvGetTextA(DRV_NAME), nSlot);
1402 return szSavestateFilename;
1403 }
1404 #endif
1405
1406
luasav_save(const char * filename)1407 void luasav_save(const char *filename) {
1408 LuaSaveData saveData;
1409 char luaSaveFilename[512];
1410 char slotnum[512];
1411 char* filenameEnd;
1412
1413 const char *posa = strrchr(filename, '/') ;
1414 const char *posb = strrchr(filename, '\\');
1415 const char *pos = posa > posb ? posa : posb;
1416 if (pos) {
1417 sprintf(luaSaveFilename, "%s.luasav", filename);
1418 strncpy(slotnum, pos+1, strlen(pos));
1419 filenameEnd = strrchr(slotnum, '.');
1420 filenameEnd[0] = '\0';
1421 strcpy(slotnum, filenameEnd-1);
1422
1423 // call savestate.save callback if any and store the results in a luasav file if any
1424 CallRegisteredLuaSaveFunctions(slotnum, saveData);
1425 if (saveData.recordList) {
1426 FILE* luaSaveFile = fopen(luaSaveFilename, "wb");
1427 if(luaSaveFile) {
1428 saveData.ExportRecords(luaSaveFile);
1429 fclose(luaSaveFile);
1430 }
1431 }
1432 else {
1433 unlink(luaSaveFilename);
1434 }
1435 }
1436 }
1437
luasav_load(const char * filename)1438 void luasav_load(const char *filename) {
1439 LuaSaveData saveData;
1440 char luaSaveFilename[512];
1441 char slotnum[512];
1442 char* filenameEnd;
1443
1444 const char *posa = strrchr(filename, '/') ;
1445 const char *posb = strrchr(filename, '\\');
1446 const char *pos = posa > posb ? posa : posb;
1447 if (pos) {
1448 sprintf(luaSaveFilename, "%s.luasav", filename);
1449 strncpy(slotnum, pos+1, strlen(pos));
1450 filenameEnd = strrchr(slotnum, '.');
1451 filenameEnd[0] = '\0';
1452 strcpy(slotnum, filenameEnd-1);
1453
1454 // call savestate.registerload callback if any
1455 // and pass it the result from the previous savestate.registerload callback to the same state if any
1456 FILE* luaSaveFile = fopen(luaSaveFilename, "rb");
1457 if (luaSaveFile) {
1458 saveData.ImportRecords(luaSaveFile);
1459 fclose(luaSaveFile);
1460 }
1461 CallRegisteredLuaLoadFunctions(slotnum, saveData);
1462 }
1463 }
1464
1465
1466 // Helper function to convert a savestate object to the filename it represents.
savestateobj2filename(lua_State * L,int offset)1467 static char *savestateobj2filename(lua_State *L, int offset) {
1468 // First we get the metatable of the indicated object
1469 int result = lua_getmetatable(L, offset);
1470
1471 if (!result)
1472 luaL_error(L, "object not a savestate object");
1473
1474 // Also check that the type entry is set
1475 lua_getfield(L, -1, "__metatable");
1476 if (strcmp(lua_tostring(L,-1), "FBA Savestate") != 0)
1477 luaL_error(L, "object not a savestate object");
1478 lua_pop(L,1);
1479
1480 // Now, get the field we want
1481 lua_getfield(L, -1, "filename");
1482
1483 // Return it
1484 return (char *) lua_tostring(L, -1);
1485 }
1486
1487
1488 // Helper function for garbage collection.
savestate_gc(lua_State * L)1489 static int savestate_gc(lua_State *L) {
1490 const char *filename;
1491
1492 // The object we're collecting is on top of the stack
1493 lua_getmetatable(L,1);
1494
1495 // Get the filename
1496 lua_getfield(L, -1, "filename");
1497 filename = lua_tostring(L,-1);
1498
1499 // Delete the file
1500 remove(filename);
1501
1502 // We exit, and the garbage collector takes care of the rest.
1503 return 0;
1504 }
1505
1506 // object savestate.create(int which = nil)
1507 //
1508 // Creates an object used for savestates.
1509 // The object can be associated with a player-accessible savestate
1510 // ("which" between 1 and 10) or not (which == nil).
savestate_create(lua_State * L)1511 static int savestate_create(lua_State *L) {
1512 const char *filename;
1513 bool anonymous = false;
1514
1515 if (lua_gettop(L) >= 1)
1516 if (lua_isstring(L,1))
1517 filename = luaL_checkstring(L,1);
1518 else
1519 filename = GetSavestateFilename(luaL_checkinteger(L,1));
1520 else {
1521 filename = tempnam(NULL, "snlua");
1522 anonymous = true;
1523 }
1524
1525 // Our "object". We don't care about the type, we just need the memory and GC services.
1526 lua_newuserdata(L,1);
1527
1528 // The metatable we use, protected from Lua and contains garbage collection info and stuff.
1529 lua_newtable(L);
1530
1531 // First, we must protect it
1532 lua_pushstring(L, "FBA Savestate");
1533 lua_setfield(L, -2, "__metatable");
1534
1535 // Now we need to save the file itself.
1536 lua_pushstring(L, filename);
1537 lua_setfield(L, -2, "filename");
1538
1539 // If it's an anonymous savestate, we must delete the file from disk should it be gargage collected
1540 if (anonymous) {
1541 lua_pushcfunction(L, savestate_gc);
1542 lua_setfield(L, -2, "__gc");
1543 }
1544
1545 // Set the metatable
1546 lua_setmetatable(L, -2);
1547
1548 // Awesome. Return the object
1549 return 1;
1550 }
1551
1552
1553 // savestate.save(object state)
1554 //
1555 // Saves a state to the given object.
savestate_save(lua_State * L)1556 static int savestate_save(lua_State *L) {
1557 const char *filename;
1558
1559 if (lua_type(L,1) == LUA_TUSERDATA)
1560 filename = savestateobj2filename(L,1);
1561 else if (lua_isstring(L,1))
1562 filename = luaL_checkstring(L,1);
1563 else
1564 filename = GetSavestateFilename(luaL_checkinteger(L,1));
1565
1566 // Save states are very expensive. They take time.
1567 numTries--;
1568
1569 BurnStateSave(_AtoT(filename), 1);
1570 return 0;
1571 }
1572
1573 // savestate.load(object state)
1574 //
1575 // Loads the given state
savestate_load(lua_State * L)1576 static int savestate_load(lua_State *L) {
1577 const char *filename;
1578
1579 if (lua_type(L,1) == LUA_TUSERDATA)
1580 filename = savestateobj2filename(L,1);
1581 else if (lua_isstring(L,1))
1582 filename = luaL_checkstring(L,1);
1583 else
1584 filename = GetSavestateFilename(luaL_checkinteger(L,1));
1585
1586 numTries--;
1587
1588 BurnStateLoad(_AtoT(filename), 1, &DrvInitCallback);
1589 return 0;
1590 }
1591
savestate_registersave(lua_State * L)1592 static int savestate_registersave(lua_State *L) {
1593 lua_settop(L,1);
1594 if (!lua_isnil(L,1))
1595 luaL_checktype(L, 1, LUA_TFUNCTION);
1596 lua_getfield(L, LUA_REGISTRYINDEX, LUA_SAVE_CALLBACK_STRING);
1597 lua_pushvalue(L,1);
1598 lua_setfield(L, LUA_REGISTRYINDEX, LUA_SAVE_CALLBACK_STRING);
1599 return 1;
1600 }
1601
savestate_registerload(lua_State * L)1602 static int savestate_registerload(lua_State *L) {
1603 lua_settop(L,1);
1604 if (!lua_isnil(L,1))
1605 luaL_checktype(L, 1, LUA_TFUNCTION);
1606 lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOAD_CALLBACK_STRING);
1607 lua_pushvalue(L,1);
1608 lua_setfield(L, LUA_REGISTRYINDEX, LUA_LOAD_CALLBACK_STRING);
1609 return 1;
1610 }
1611
savestate_loadscriptdata(lua_State * L)1612 static int savestate_loadscriptdata(lua_State *L) {
1613 const char *filename;
1614 LuaSaveData saveData;
1615 char luaSaveFilename[512];
1616 FILE* luaSaveFile;
1617
1618 if (lua_type(L,1) == LUA_TUSERDATA)
1619 filename = savestateobj2filename(L,1);
1620 else
1621 filename = GetSavestateFilename(luaL_checkinteger(L,1));
1622
1623 sprintf(luaSaveFilename, "%s.luasav", filename);
1624 luaSaveFile = fopen(luaSaveFilename, "rb");
1625 if(luaSaveFile)
1626 {
1627 saveData.ImportRecords(luaSaveFile);
1628 fclose(luaSaveFile);
1629
1630 lua_settop(L, 0);
1631 saveData.LoadRecord(L, LUA_DATARECORDKEY, (unsigned int)-1);
1632 return lua_gettop(L);
1633 }
1634 return 0;
1635 }
1636
savestate_savescriptdata(lua_State * L)1637 static int savestate_savescriptdata(lua_State *L) {
1638 const char *filename;
1639
1640 if (lua_type(L,1) == LUA_TUSERDATA)
1641 filename = savestateobj2filename(L,1);
1642 else
1643 filename = GetSavestateFilename(luaL_checkinteger(L,1));
1644
1645 luasav_save(filename);
1646 return 0;
1647 }
1648
1649 extern unsigned int nStartFrame;
1650 // int movie.framecount()
1651 //
1652 // Gets the frame counter for the movie
movie_framecount(lua_State * L)1653 int movie_framecount(lua_State *L) {
1654 lua_pushinteger(L, GetCurrentFrame() - nStartFrame);
1655 return 1;
1656 }
1657
1658
1659 // string movie.mode()
1660 //
1661 // "record", "playback" or nil
movie_mode(lua_State * L)1662 int movie_mode(lua_State *L) {
1663 if (nReplayStatus == 1)
1664 lua_pushstring(L, "record");
1665 else if (nReplayStatus == 2)
1666 lua_pushstring(L, "playback");
1667 else
1668 lua_pushnil(L);
1669 return 1;
1670 }
1671
1672
1673 // movie.setreadonly()
1674 //
1675 // sets the read-only flag
movie_setreadonly(lua_State * L)1676 static int movie_setreadonly(lua_State *L) {
1677 bReplayReadOnly = (lua_toboolean( L, 1 ) == 1);
1678
1679 if (bReplayReadOnly)
1680 VidSNewShortMsg(_T("read-only"));
1681 else
1682 VidSNewShortMsg(_T("read+write"));
1683
1684 return 0;
1685 }
1686
1687
1688 // movie.getreadonly()
1689 //
1690 // returns the state of the read-only flag
movie_getreadonly(lua_State * L)1691 static int movie_getreadonly(lua_State *L) {
1692 lua_pushboolean(L, bReplayReadOnly);
1693
1694 return 1;
1695 }
1696
1697
movie_rerecordcounting(lua_State * L)1698 static int movie_rerecordcounting(lua_State *L) {
1699 if (lua_gettop(L) == 0)
1700 luaL_error(L, "no parameters specified");
1701
1702 skipRerecords = lua_toboolean(L,1);
1703 return 0;
1704 }
1705
1706
1707 // movie.length()
1708 //
1709 // Returns the total number of frames of a movie
movie_length(lua_State * L)1710 static int movie_length(lua_State *L) {
1711 #ifdef WIN32 //adelikat: GetTotalMovieFrames is a win32 only file, so this can only be implemented in win32
1712 //lua_pushinteger(L, GetTotalMovieFrames());
1713 #endif
1714 return 1;
1715 }
1716
1717
1718 // movie.stop()
1719 //
1720 // Stops movie playback/recording. Bombs out if movie is not running.
movie_stop(lua_State * L)1721 static int movie_stop(lua_State *L) {
1722 if (nReplayStatus == 0)
1723 luaL_error(L, "no movie");
1724
1725 StopReplay();
1726 return 0;
1727 }
1728
1729
1730 // movie.playbeginning()
1731 //
1732 // Restarts the movie from beginning
movie_playbeginning(lua_State * L)1733 static int movie_playbeginning(lua_State *L) {
1734 //HK_playFromBeginning(0);
1735 return 0;
1736 }
1737
1738
1739 // Common code by the gui library: make sure the screen array is ready
gui_prepare()1740 static void gui_prepare() {
1741 int x,y;
1742 if (!gui_data)
1743 gui_data = (UINT8 *) malloc(LUA_SCREEN_WIDTH * LUA_SCREEN_HEIGHT * 4);
1744 if (gui_used != GUI_USED_SINCE_LAST_DISPLAY) {
1745 for (y = 0; y < LUA_SCREEN_HEIGHT; y++) {
1746 for (x=0; x < LUA_SCREEN_WIDTH; x++) {
1747 if (gui_data[(y*LUA_SCREEN_WIDTH+x)*4+3] != 0)
1748 gui_data[(y*LUA_SCREEN_WIDTH+x)*4+3] = 0;
1749 }
1750 }
1751 }
1752 // if (gui_used != GUI_USED_SINCE_LAST_DISPLAY) /* mz: 10% slower on my system */
1753 // memset(gui_data,0,LUA_SCREEN_WIDTH * LUA_SCREEN_HEIGHT * 4);
1754 gui_used = GUI_USED_SINCE_LAST_DISPLAY;
1755 }
1756
1757
1758 // pixform for lua graphics
1759 #define BUILD_PIXEL_ARGB8888(A,R,G,B) (((int) (A) << 24) | ((int) (R) << 16) | ((int) (G) << 8) | (int) (B))
1760 #define DECOMPOSE_PIXEL_ARGB8888(PIX,A,R,G,B) { (A) = ((PIX) >> 24) & 0xff; (R) = ((PIX) >> 16) & 0xff; (G) = ((PIX) >> 8) & 0xff; (B) = (PIX) & 0xff; }
1761 #define LUA_BUILD_PIXEL BUILD_PIXEL_ARGB8888
1762 #define LUA_DECOMPOSE_PIXEL DECOMPOSE_PIXEL_ARGB8888
1763 #define LUA_PIXEL_A(PIX) (((PIX) >> 24) & 0xff)
1764 #define LUA_PIXEL_R(PIX) (((PIX) >> 16) & 0xff)
1765 #define LUA_PIXEL_G(PIX) (((PIX) >> 8) & 0xff)
1766 #define LUA_PIXEL_B(PIX) ((PIX) & 0xff)
1767
1768 // I'm going to use this a lot in here
1769 #define swap(T, one, two) { \
1770 T temp = one; \
1771 one = two; \
1772 two = temp; \
1773 }
1774
1775 // write a pixel to buffer
blend32(UINT32 * dstPixel,UINT32 colour)1776 static inline void blend32(UINT32 *dstPixel, UINT32 colour)
1777 {
1778 UINT8 *dst = (UINT8*) dstPixel;
1779 int a, r, g, b;
1780 LUA_DECOMPOSE_PIXEL(colour, a, r, g, b);
1781
1782 if (a == 255 || dst[3] == 0) {
1783 // direct copy
1784 *(UINT32*)(dst) = colour;
1785 }
1786 else if (a == 0) {
1787 // do not copy
1788 }
1789 else {
1790 // alpha-blending
1791 int a_dst = ((255 - a) * dst[3] + 128) / 255;
1792 int a_new = a + a_dst;
1793
1794 dst[0] = (UINT8) ((( dst[0] * a_dst + b * a) + (a_new / 2)) / a_new);
1795 dst[1] = (UINT8) ((( dst[1] * a_dst + g * a) + (a_new / 2)) / a_new);
1796 dst[2] = (UINT8) ((( dst[2] * a_dst + r * a) + (a_new / 2)) / a_new);
1797 dst[3] = (UINT8) a_new;
1798 }
1799 }
1800
1801 // check if a pixel is in the lua canvas
gui_check_boundary(int x,int y)1802 static inline UINT8 gui_check_boundary(int x, int y) {
1803 return !(x < 0 || x >= LUA_SCREEN_WIDTH || y < 0 || y >= LUA_SCREEN_HEIGHT);
1804 }
1805
1806 // write a pixel to gui_data (do not check boundaries for speedup)
gui_drawpixel_fast(int x,int y,UINT32 colour)1807 static inline void gui_drawpixel_fast(int x, int y, UINT32 colour) {
1808 //gui_prepare();
1809 blend32((UINT32*) &gui_data[(y*LUA_SCREEN_WIDTH+x)*4], colour);
1810 }
1811
1812 // write a pixel to gui_data (check boundaries)
gui_drawpixel_internal(int x,int y,UINT32 colour)1813 static inline void gui_drawpixel_internal(int x, int y, UINT32 colour) {
1814 //gui_prepare();
1815 if (gui_check_boundary(x, y))
1816 gui_drawpixel_fast(x, y, colour);
1817 }
1818
1819 // draw a line on gui_data (checks boundaries)
gui_drawline_internal(int x1,int y1,int x2,int y2,UINT8 lastPixel,UINT32 colour)1820 static void gui_drawline_internal(int x1, int y1, int x2, int y2, UINT8 lastPixel, UINT32 colour) {
1821
1822 //gui_prepare();
1823
1824 // Note: New version of Bresenham's Line Algorithm
1825 // http://groups.google.co.jp/group/rec.games.roguelike.development/browse_thread/thread/345f4c42c3b25858/29e07a3af3a450e6?show_docid=29e07a3af3a450e6
1826
1827 int swappedx = 0;
1828 int swappedy = 0;
1829
1830 int xtemp = x1-x2;
1831 int ytemp = y1-y2;
1832
1833 int delta_x;
1834 int delta_y;
1835
1836 signed char ix;
1837 signed char iy;
1838
1839 if (xtemp == 0 && ytemp == 0) {
1840 gui_drawpixel_internal(x1, y1, colour);
1841 return;
1842 }
1843 if (xtemp < 0) {
1844 xtemp = -xtemp;
1845 swappedx = 1;
1846 }
1847 if (ytemp < 0) {
1848 ytemp = -ytemp;
1849 swappedy = 1;
1850 }
1851
1852 delta_x = xtemp << 1;
1853 delta_y = ytemp << 1;
1854
1855 ix = x1 > x2?1:-1;
1856 iy = y1 > y2?1:-1;
1857
1858 if (lastPixel)
1859 gui_drawpixel_internal(x2, y2, colour);
1860
1861 if (delta_x >= delta_y) {
1862 int error = delta_y - (delta_x >> 1);
1863
1864 while (x2 != x1) {
1865 if (error == 0 && !swappedx)
1866 gui_drawpixel_internal(x2+ix, y2, colour);
1867 if (error >= 0) {
1868 if (error || (ix > 0)) {
1869 y2 += iy;
1870 error -= delta_x;
1871 }
1872 }
1873 x2 += ix;
1874 gui_drawpixel_internal(x2, y2, colour);
1875 if (error == 0 && swappedx)
1876 gui_drawpixel_internal(x2, y2+iy, colour);
1877 error += delta_y;
1878 }
1879 }
1880 else {
1881 int error = delta_x - (delta_y >> 1);
1882
1883 while (y2 != y1) {
1884 if (error == 0 && !swappedy)
1885 gui_drawpixel_internal(x2, y2+iy, colour);
1886 if (error >= 0) {
1887 if (error || (iy > 0)) {
1888 x2 += ix;
1889 error -= delta_y;
1890 }
1891 }
1892 y2 += iy;
1893 gui_drawpixel_internal(x2, y2, colour);
1894 if (error == 0 && swappedy)
1895 gui_drawpixel_internal(x2+ix, y2, colour);
1896 error += delta_x;
1897 }
1898 }
1899 }
1900
1901 // draw a rect on gui_data
gui_drawbox_internal(int x1,int y1,int x2,int y2,UINT32 colour)1902 static void gui_drawbox_internal(int x1, int y1, int x2, int y2, UINT32 colour) {
1903
1904 if (x1 > x2)
1905 swap(int, x1, x2);
1906 if (y1 > y2)
1907 swap(int, y1, y2);
1908 if (x1 < 0)
1909 x1 = -1;
1910 if (y1 < 0)
1911 y1 = -1;
1912 if (x2 >= LUA_SCREEN_WIDTH)
1913 x2 = LUA_SCREEN_WIDTH;
1914 if (y2 >= LUA_SCREEN_HEIGHT)
1915 y2 = LUA_SCREEN_HEIGHT;
1916
1917 //gui_prepare();
1918
1919 gui_drawline_internal(x1, y1, x2, y1, TRUE, colour);
1920 gui_drawline_internal(x1, y2, x2, y2, TRUE, colour);
1921 gui_drawline_internal(x1, y1, x1, y2, TRUE, colour);
1922 gui_drawline_internal(x2, y1, x2, y2, TRUE, colour);
1923 }
1924
1925 /*
1926 // draw a circle on gui_data
1927 static void gui_drawcircle_internal(int x0, int y0, int radius, UINT32 colour) {
1928
1929 int f;
1930 int ddF_x;
1931 int ddF_y;
1932 int x;
1933 int y;
1934
1935 //gui_prepare();
1936
1937 if (radius < 0)
1938 radius = -radius;
1939 if (radius == 0)
1940 return;
1941 if (radius == 1) {
1942 gui_drawpixel_internal(x0, y0, colour);
1943 return;
1944 }
1945
1946 // http://en.wikipedia.org/wiki/Midpoint_circle_algorithm
1947
1948 f = 1 - radius;
1949 ddF_x = 1;
1950 ddF_y = -2 * radius;
1951 x = 0;
1952 y = radius;
1953
1954 gui_drawpixel_internal(x0, y0 + radius, colour);
1955 gui_drawpixel_internal(x0, y0 - radius, colour);
1956 gui_drawpixel_internal(x0 + radius, y0, colour);
1957 gui_drawpixel_internal(x0 - radius, y0, colour);
1958
1959 // same pixel shouldn't be drawed twice,
1960 // because each pixel has opacity.
1961 // so now the routine gets ugly.
1962 while(TRUE)
1963 {
1964 assert(ddF_x == 2 * x + 1);
1965 assert(ddF_y == -2 * y);
1966 assert(f == x*x + y*y - radius*radius + 2*x - y + 1);
1967 if(f >= 0)
1968 {
1969 y--;
1970 ddF_y += 2;
1971 f += ddF_y;
1972 }
1973 x++;
1974 ddF_x += 2;
1975 f += ddF_x;
1976 if (x < y) {
1977 gui_drawpixel_internal(x0 + x, y0 + y, colour);
1978 gui_drawpixel_internal(x0 - x, y0 + y, colour);
1979 gui_drawpixel_internal(x0 + x, y0 - y, colour);
1980 gui_drawpixel_internal(x0 - x, y0 - y, colour);
1981 gui_drawpixel_internal(x0 + y, y0 + x, colour);
1982 gui_drawpixel_internal(x0 - y, y0 + x, colour);
1983 gui_drawpixel_internal(x0 + y, y0 - x, colour);
1984 gui_drawpixel_internal(x0 - y, y0 - x, colour);
1985 }
1986 else if (x == y) {
1987 gui_drawpixel_internal(x0 + x, y0 + y, colour);
1988 gui_drawpixel_internal(x0 - x, y0 + y, colour);
1989 gui_drawpixel_internal(x0 + x, y0 - y, colour);
1990 gui_drawpixel_internal(x0 - x, y0 - y, colour);
1991 break;
1992 }
1993 else
1994 break;
1995 }
1996 }
1997 */
1998
1999 // draw fill rect on gui_data
gui_fillbox_internal(int x1,int y1,int x2,int y2,UINT32 colour)2000 static void gui_fillbox_internal(int x1, int y1, int x2, int y2, UINT32 colour) {
2001
2002 int ix, iy;
2003
2004 if (x1 > x2)
2005 swap(int, x1, x2);
2006 if (y1 > y2)
2007 swap(int, y1, y2);
2008 if (x1 < 0)
2009 x1 = 0;
2010 if (y1 < 0)
2011 y1 = 0;
2012 if (x2 >= LUA_SCREEN_WIDTH)
2013 x2 = LUA_SCREEN_WIDTH - 1;
2014 if (y2 >= LUA_SCREEN_HEIGHT)
2015 y2 = LUA_SCREEN_HEIGHT - 1;
2016
2017 //gui_prepare();
2018
2019 for (iy = y1; iy <= y2; iy++) {
2020 for (ix = x1; ix <= x2; ix++) {
2021 gui_drawpixel_fast(ix, iy, colour);
2022 }
2023 }
2024 }
2025
2026 /*
2027 // fill a circle on gui_data
2028 static void gui_fillcircle_internal(int x0, int y0, int radius, UINT32 colour) {
2029
2030 int f;
2031 int ddF_x;
2032 int ddF_y;
2033 int x;
2034 int y;
2035
2036 //gui_prepare();
2037
2038 if (radius < 0)
2039 radius = -radius;
2040 if (radius == 0)
2041 return;
2042 if (radius == 1) {
2043 gui_drawpixel_internal(x0, y0, colour);
2044 return;
2045 }
2046
2047 // http://en.wikipedia.org/wiki/Midpoint_circle_algorithm
2048
2049 f = 1 - radius;
2050 ddF_x = 1;
2051 ddF_y = -2 * radius;
2052 x = 0;
2053 y = radius;
2054
2055 gui_drawline_internal(x0, y0 - radius, x0, y0 + radius, TRUE, colour);
2056
2057 while(TRUE)
2058 {
2059 assert(ddF_x == 2 * x + 1);
2060 assert(ddF_y == -2 * y);
2061 assert(f == x*x + y*y - radius*radius + 2*x - y + 1);
2062 if(f >= 0)
2063 {
2064 y--;
2065 ddF_y += 2;
2066 f += ddF_y;
2067 }
2068 x++;
2069 ddF_x += 2;
2070 f += ddF_x;
2071
2072 if (x < y) {
2073 gui_drawline_internal(x0 + x, y0 - y, x0 + x, y0 + y, TRUE, colour);
2074 gui_drawline_internal(x0 - x, y0 - y, x0 - x, y0 + y, TRUE, colour);
2075 if (f >= 0) {
2076 gui_drawline_internal(x0 + y, y0 - x, x0 + y, y0 + x, TRUE, colour);
2077 gui_drawline_internal(x0 - y, y0 - x, x0 - y, y0 + x, TRUE, colour);
2078 }
2079 }
2080 else if (x == y) {
2081 gui_drawline_internal(x0 + x, y0 - y, x0 + x, y0 + y, TRUE, colour);
2082 gui_drawline_internal(x0 - x, y0 - y, x0 - x, y0 + y, TRUE, colour);
2083 break;
2084 }
2085 else
2086 break;
2087 }
2088 }
2089 */
2090
2091 static const struct ColorMapping
2092 {
2093 const char* name;
2094 int value;
2095 }
2096 s_colorMapping [] =
2097 {
2098 {"white", 0xFFFFFFFF},
2099 {"black", 0x000000FF},
2100 {"clear", 0x00000000},
2101 {"gray", 0x7F7F7FFF},
2102 {"grey", 0x7F7F7FFF},
2103 {"red", 0xFF0000FF},
2104 {"orange", 0xFF7F00FF},
2105 {"yellow", 0xFFFF00FF},
2106 {"chartreuse",0x7FFF00FF},
2107 {"green", 0x00FF00FF},
2108 {"teal", 0x00FF7FFF},
2109 {"cyan" , 0x00FFFFFF},
2110 {"blue", 0x0000FFFF},
2111 {"purple", 0x7F00FFFF},
2112 {"magenta", 0xFF00FFFF},
2113 };
2114
2115 /**
2116 * Converts an integer or a string on the stack at the given
2117 * offset to a RGB32 colour. Several encodings are supported.
2118 * The user may construct their own RGB value, given a simple colour name,
2119 * or an HTML-style "#09abcd" colour. 16 bit reduction doesn't occur at this time.
2120 */
str2colour(UINT32 * colour,const char * str)2121 static inline UINT8 str2colour(UINT32 *colour, const char *str) {
2122 if (str[0] == '#') {
2123 unsigned int color;
2124 int len;
2125 int missing;
2126 sscanf(str+1, "%X", &color);
2127 len = strlen(str+1);
2128 missing = max(0, 8-len);
2129 color <<= missing << 2;
2130 if(missing >= 2) color |= 0xFF;
2131 *colour = color;
2132 return TRUE;
2133 }
2134 else {
2135 unsigned int i;
2136 if(!strnicmp(str, "rand", 4)) {
2137 *colour = ((rand()*255/RAND_MAX) << 8) | ((rand()*255/RAND_MAX) << 16) | ((rand()*255/RAND_MAX) << 24) | 0xFF;
2138 return TRUE;
2139 }
2140 for(i = 0; i < sizeof(s_colorMapping)/sizeof(*s_colorMapping); i++) {
2141 if(!stricmp(str,s_colorMapping[i].name)) {
2142 *colour = s_colorMapping[i].value;
2143 return TRUE;
2144 }
2145 }
2146 }
2147 return FALSE;
2148 }
gui_getcolour_wrapped(lua_State * L,int offset,UINT8 hasDefaultValue,UINT32 defaultColour)2149 static inline UINT32 gui_getcolour_wrapped(lua_State *L, int offset, UINT8 hasDefaultValue, UINT32 defaultColour) {
2150 switch (lua_type(L,offset)) {
2151 case LUA_TSTRING:
2152 {
2153 const char *str = lua_tostring(L,offset);
2154 UINT32 colour;
2155
2156 if (str2colour(&colour, str))
2157 return colour;
2158 else {
2159 if (hasDefaultValue)
2160 return defaultColour;
2161 else
2162 return luaL_error(L, "unknown colour %s", str);
2163 }
2164 }
2165 case LUA_TNUMBER:
2166 {
2167 UINT32 colour = (UINT32) lua_tointeger(L,offset);
2168 return colour;
2169 }
2170 case LUA_TTABLE:
2171 {
2172 int color = 0xFF;
2173 lua_pushnil(L); // first key
2174 int keyIndex = lua_gettop(L);
2175 int valueIndex = keyIndex + 1;
2176 while(lua_next(L, offset))
2177 {
2178 bool keyIsString = (lua_type(L, keyIndex) == LUA_TSTRING);
2179 bool keyIsNumber = (lua_type(L, keyIndex) == LUA_TNUMBER);
2180 int key = keyIsString ? tolower(*lua_tostring(L, keyIndex)) : (keyIsNumber ? lua_tointeger(L, keyIndex) : 0);
2181 int value = lua_tointeger(L, valueIndex);
2182 if(value < 0) value = 0;
2183 if(value > 255) value = 255;
2184 switch(key)
2185 {
2186 case 1: case 'r': color |= value << 24; break;
2187 case 2: case 'g': color |= value << 16; break;
2188 case 3: case 'b': color |= value << 8; break;
2189 case 4: case 'a': color = (color & ~0xFF) | value; break;
2190 }
2191 lua_pop(L, 1);
2192 }
2193 return color;
2194 } break;
2195 case LUA_TFUNCTION:
2196 luaL_error(L, "invalid colour"); // NYI
2197 return 0;
2198 default:
2199 if (hasDefaultValue)
2200 return defaultColour;
2201 else
2202 return luaL_error(L, "invalid colour");
2203 }
2204 }
gui_getcolour(lua_State * L,int offset)2205 static UINT32 gui_getcolour(lua_State *L, int offset) {
2206 UINT32 colour;
2207 UINT32 a, r, g, b;
2208
2209 colour = gui_getcolour_wrapped(L, offset, FALSE, 0);
2210 a = ((colour & 0xff) * transparencyModifier) / 255;
2211 if (a > 255) a = 255;
2212 b = (colour >> 8) & 0xff;
2213 g = (colour >> 16) & 0xff;
2214 r = (colour >> 24) & 0xff;
2215 return LUA_BUILD_PIXEL(a, r, g, b);
2216 }
gui_optcolour(lua_State * L,int offset,UINT32 defaultColour)2217 static UINT32 gui_optcolour(lua_State *L, int offset, UINT32 defaultColour) {
2218 UINT32 colour;
2219 UINT32 a, r, g, b;
2220 UINT8 defA, defB, defG, defR;
2221
2222 LUA_DECOMPOSE_PIXEL(defaultColour, defA, defR, defG, defB);
2223 defaultColour = (defR << 24) | (defG << 16) | (defB << 8) | defA;
2224
2225 colour = gui_getcolour_wrapped(L, offset, TRUE, defaultColour);
2226 a = ((colour & 0xff) * transparencyModifier) / 255;
2227 if (a > 255) a = 255;
2228 b = (colour >> 8) & 0xff;
2229 g = (colour >> 16) & 0xff;
2230 r = (colour >> 24) & 0xff;
2231 return LUA_BUILD_PIXEL(a, r, g, b);
2232 }
2233
2234 // gui.drawpixel(x,y,colour)
gui_drawpixel(lua_State * L)2235 static int gui_drawpixel(lua_State *L) {
2236
2237 int x = luaL_checkinteger(L, 1);
2238 int y = luaL_checkinteger(L,2);
2239
2240 UINT32 colour = gui_getcolour(L,3);
2241
2242 // if (!gui_check_boundary(x, y))
2243 // luaL_error(L,"bad coordinates");
2244
2245 gui_prepare();
2246
2247 gui_drawpixel_internal(x, y, colour);
2248
2249 return 0;
2250 }
2251
2252 // gui.drawline(x1,y1,x2,y2,color,skipFirst)
gui_drawline(lua_State * L)2253 static int gui_drawline(lua_State *L) {
2254
2255 int x1,y1,x2,y2;
2256 UINT32 color;
2257 x1 = luaL_checkinteger(L,1);
2258 y1 = luaL_checkinteger(L,2);
2259 x2 = luaL_checkinteger(L,3);
2260 y2 = luaL_checkinteger(L,4);
2261 color = gui_optcolour(L,5,LUA_BUILD_PIXEL(255, 255, 255, 255));
2262 int skipFirst = lua_toboolean(L,6);
2263
2264 gui_prepare();
2265
2266 gui_drawline_internal(x2, y2, x1, y1, !skipFirst, color);
2267
2268 return 0;
2269 }
2270
2271 // gui.drawbox(x1, y1, x2, y2, fillcolor, outlinecolor)
gui_drawbox(lua_State * L)2272 static int gui_drawbox(lua_State *L) {
2273
2274 int x1,y1,x2,y2;
2275 UINT32 fillcolor;
2276 UINT32 outlinecolor;
2277
2278 x1 = luaL_checkinteger(L,1);
2279 y1 = luaL_checkinteger(L,2);
2280 x2 = luaL_checkinteger(L,3);
2281 y2 = luaL_checkinteger(L,4);
2282 fillcolor = gui_optcolour(L,5,LUA_BUILD_PIXEL(63, 255, 255, 255));
2283 outlinecolor = gui_optcolour(L,6,LUA_BUILD_PIXEL(255, LUA_PIXEL_R(fillcolor), LUA_PIXEL_G(fillcolor), LUA_PIXEL_B(fillcolor)));
2284
2285 if (x1 > x2)
2286 swap(int,x1, x2);
2287 if (y1 > y2)
2288 swap(int, y1, y2);
2289
2290 gui_prepare();
2291
2292 gui_drawbox_internal(x1, y1, x2, y2, outlinecolor);
2293 if ((x2 - x1) >= 2 && (y2 - y1) >= 2)
2294 gui_fillbox_internal(x1+1, y1+1, x2-1, y2-1, fillcolor);
2295
2296 return 0;
2297 }
2298
2299 /*
2300 // gui.drawcircle(x0, y0, radius, colour)
2301 static int gui_drawcircle(lua_State *L) {
2302
2303 int x, y, r;
2304 UINT32 colour;
2305
2306 x = luaL_checkinteger(L,1);
2307 y = luaL_checkinteger(L,2);
2308 r = luaL_checkinteger(L,3);
2309 colour = gui_getcolour(L,4);
2310
2311 gui_prepare();
2312
2313 gui_drawcircle_internal(x, y, r, colour);
2314
2315 return 0;
2316 }
2317
2318 // gui.fillbox(x1, y1, x2, y2, colour)
2319 static int gui_fillbox(lua_State *L) {
2320
2321 int x1,y1,x2,y2;
2322 UINT32 colour;
2323
2324 x1 = luaL_checkinteger(L,1);
2325 y1 = luaL_checkinteger(L,2);
2326 x2 = luaL_checkinteger(L,3);
2327 y2 = luaL_checkinteger(L,4);
2328 colour = gui_getcolour(L,5);
2329
2330 // if (!gui_check_boundary(x1, y1))
2331 // luaL_error(L,"bad coordinates");
2332 //
2333 // if (!gui_check_boundary(x2, y2))
2334 // luaL_error(L,"bad coordinates");
2335
2336 gui_prepare();
2337
2338 gui_fillbox_internal(x1, y1, x2, y2, colour);
2339
2340 return 0;
2341 }
2342
2343 // gui.fillcircle(x0, y0, radius, colour)
2344 static int gui_fillcircle(lua_State *L) {
2345
2346 int x, y, r;
2347 UINT32 colour;
2348
2349 x = luaL_checkinteger(L,1);
2350 y = luaL_checkinteger(L,2);
2351 r = luaL_checkinteger(L,3);
2352 colour = gui_getcolour(L,4);
2353
2354 gui_prepare();
2355
2356 gui_fillcircle_internal(x, y, r, colour);
2357
2358 return 0;
2359 }
2360 */
2361
gui_getpixel(lua_State * L)2362 static int gui_getpixel(lua_State *L) {
2363 int x = luaL_checkinteger(L, 1);
2364 int y = luaL_checkinteger(L, 2);
2365
2366 if(!gui_check_boundary(x,y))
2367 {
2368 lua_pushinteger(L, 0);
2369 lua_pushinteger(L, 0);
2370 lua_pushinteger(L, 0);
2371 }
2372 else
2373 {
2374 switch(iScreenBpp)
2375 {
2376 case 2:
2377 {
2378 UINT16 *screen = (UINT16*) XBuf;
2379 UINT16 pix = screen[y*(iScreenPitch/2) + x];
2380 lua_pushinteger(L, (pix >> 8) & 0xF8); // red
2381 lua_pushinteger(L, (pix >> 3) & 0xFC); // green
2382 lua_pushinteger(L, (pix << 3) & 0xF8); // blue
2383 }
2384 break;
2385 case 3:
2386 {
2387 UINT8 *screen = XBuf;
2388 lua_pushinteger(L, screen[y*iScreenPitch + x*3 + 2]); // red
2389 lua_pushinteger(L, screen[y*iScreenPitch + x*3 + 1]); // green
2390 lua_pushinteger(L, screen[y*iScreenPitch + x*3 + 0]); // blue
2391 }
2392 break;
2393 case 4:
2394 {
2395 UINT8 *screen = XBuf;
2396 lua_pushinteger(L, screen[y*iScreenPitch + x*4 + 2]); // red
2397 lua_pushinteger(L, screen[y*iScreenPitch + x*4 + 1]); // green
2398 lua_pushinteger(L, screen[y*iScreenPitch + x*4 + 0]); // blue
2399 }
2400 break;
2401 default:
2402 lua_pushinteger(L, 0);
2403 lua_pushinteger(L, 0);
2404 lua_pushinteger(L, 0);
2405 break;
2406 }
2407 }
2408
2409 return 3;
2410 }
2411
gui_parsecolor(lua_State * L)2412 static int gui_parsecolor(lua_State *L)
2413 {
2414 int r, g, b, a;
2415 UINT32 color = gui_getcolour(L,1);
2416 LUA_DECOMPOSE_PIXEL(color, a, r, g, b);
2417 lua_pushinteger(L, r);
2418 lua_pushinteger(L, g);
2419 lua_pushinteger(L, b);
2420 lua_pushinteger(L, a);
2421 return 4;
2422 }
2423
2424
2425 // gui.savescreenshot()
2426 //
2427 // Causes FBA to write a screenshot to a file as if the user pressed the associated hotkey.
2428 //
2429 // Unconditionally retrns 1; any failure in taking a screenshot would be reported on-screen
2430 // from the function HK_screenShot().
gui_savescreenshot(lua_State * L)2431 static int gui_savescreenshot(lua_State *L) {
2432 //HK_screenShot(0);
2433 return 1;
2434 }
2435
2436 // gui.gdscreenshot()
2437 //
2438 // Returns a screen shot as a string in gd's v1 file format.
2439 // This allows us to make screen shots available without gd installed locally.
2440 // Users can also just grab pixels via substring selection.
2441 //
2442 // I think... Does lua support grabbing byte values from a string? // yes, string.byte(str,offset)
2443 // Well, either way, just install gd and do what you like with it.
2444 // It really is easier that way.
2445 // example: gd.createFromGdStr(gui.gdscreenshot()):png("outputimage.png")
gui_gdscreenshot(lua_State * L)2446 static int gui_gdscreenshot(lua_State *L) {
2447 int x,y;
2448
2449 int width = iScreenWidth;
2450 int height = iScreenHeight;
2451
2452 int size = 11 + width * height * 4;
2453 char* str = (char*)malloc(size+1);
2454 unsigned char* ptr;
2455
2456 str[size] = 0;
2457 ptr = (unsigned char*)str;
2458
2459 // GD format header for truecolor image (11 bytes)
2460 *ptr++ = (65534 >> 8) & 0xFF;
2461 *ptr++ = (65534 ) & 0xFF;
2462 *ptr++ = (width >> 8) & 0xFF;
2463 *ptr++ = (width ) & 0xFF;
2464 *ptr++ = (height >> 8) & 0xFF;
2465 *ptr++ = (height ) & 0xFF;
2466 *ptr++ = 1;
2467 *ptr++ = 255;
2468 *ptr++ = 255;
2469 *ptr++ = 255;
2470 *ptr++ = 255;
2471
2472 for(y=0; y<height; y++){
2473 for(x=0; x<width; x++){
2474 UINT32 r, g, b;
2475 switch(iScreenBpp)
2476 {
2477 case 2:
2478 {
2479 UINT16 *screen = (UINT16*) XBuf;
2480 r = ((screen[y*(iScreenPitch/2) + x] >> 11) & 31) << 3;
2481 g = ((screen[y*(iScreenPitch/2) + x] >> 5) & 63) << 2;
2482 b = ( screen[y*(iScreenPitch/2) + x] & 31) << 3;
2483 }
2484 break;
2485 case 3:
2486 {
2487 UINT8 *screen = XBuf;
2488 r = screen[y*iScreenPitch + x*3+2];
2489 g = screen[y*iScreenPitch + x*3+1];
2490 b = screen[y*iScreenPitch + x*3];
2491 }
2492 break;
2493 case 4:
2494 default:
2495 {
2496 UINT8 *screen = XBuf;
2497 r = screen[y*iScreenPitch + x*4+2];
2498 g = screen[y*iScreenPitch + x*4+1];
2499 b = screen[y*iScreenPitch + x*4];
2500 }
2501 break;
2502 }
2503
2504 // overlay uncommited Lua drawings if needed
2505 if (gui_used != GUI_CLEAR && gui_enabled) {
2506 const UINT8 gui_alpha = gui_data[(y*LUA_SCREEN_WIDTH+x)*4+3];
2507 const UINT8 gui_red = gui_data[(y*LUA_SCREEN_WIDTH+x)*4+2];
2508 const UINT8 gui_green = gui_data[(y*LUA_SCREEN_WIDTH+x)*4+1];
2509 const UINT8 gui_blue = gui_data[(y*LUA_SCREEN_WIDTH+x)*4];
2510
2511 if (gui_alpha == 255) {
2512 // direct copy
2513 r = gui_red;
2514 g = gui_green;
2515 b = gui_blue;
2516 }
2517 else if (gui_alpha != 0) {
2518 // alpha-blending
2519 r = (((int) gui_red - r) * gui_alpha / 255 + r) & 255;
2520 g = (((int) gui_green - g) * gui_alpha / 255 + g) & 255;
2521 b = (((int) gui_blue - b) * gui_alpha / 255 + b) & 255;
2522 }
2523 }
2524
2525 *ptr++ = 0;
2526 *ptr++ = r;
2527 *ptr++ = g;
2528 *ptr++ = b;
2529 }
2530 }
2531
2532 lua_pushlstring(L, str, size);
2533 free(str);
2534 return 1;
2535 }
2536
2537
2538 // gui.opacity(number alphaValue)
2539 // sets the transparency of subsequent draw calls
2540 // 0.0 is completely transparent, 1.0 is completely opaque
2541 // non-integer values are supported and meaningful, as are values greater than 1.0
2542 // it is not necessary to use this function to get transparency (or the less-recommended gui.transparency() either),
2543 // because you can provide an alpha value in the color argument of each draw call.
2544 // however, it can be convenient to be able to globally modify the drawing transparency
gui_setopacity(lua_State * L)2545 static int gui_setopacity(lua_State *L) {
2546 double opacF = luaL_checknumber(L,1);
2547 transparencyModifier = (int) (opacF * 255);
2548 if (transparencyModifier < 0)
2549 transparencyModifier = 0;
2550 return 0;
2551 }
2552
2553 // gui.transparency(int strength)
2554 //
2555 // 0 = solid,
gui_transparency(lua_State * L)2556 static int gui_transparency(lua_State *L) {
2557 double trans = luaL_checknumber(L,1);
2558 transparencyModifier = (int) ((4.0 - trans) / 4.0 * 255);
2559 if (transparencyModifier < 0)
2560 transparencyModifier = 0;
2561 return 0;
2562 }
2563
2564 // gui.clearuncommitted()
2565 //
2566 // undoes uncommitted drawing commands
gui_clearuncommitted(lua_State * L)2567 static int gui_clearuncommitted(lua_State *L) {
2568 FBA_LuaClearGui();
2569 return 0;
2570 }
2571
2572
2573 static const UINT32 Small_Font_Data[] =
2574 {
2575 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 32
2576 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000000, 0x00000700, 0x00000000, // 33 !
2577 0x00000000, 0x00040002, 0x00050003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 34 "
2578 0x00000000, 0x00040002, 0x00050403, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 35 #
2579 0x00000000, 0x00040300, 0x00000403, 0x00000500, 0x00070600, 0x00000706, 0x00000000, // 36 $
2580 0x00000000, 0x00000002, 0x00050000, 0x00000500, 0x00000005, 0x00080000, 0x00000000, // 37 %
2581 0x00000000, 0x00000300, 0x00050003, 0x00000500, 0x00070005, 0x00080700, 0x00000000, // 38 &
2582 0x00000000, 0x00000300, 0x00000400, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 39 '
2583 0x00000000, 0x00000300, 0x00000003, 0x00000004, 0x00000005, 0x00000700, 0x00000000, // 40 (
2584 0x00000000, 0x00000300, 0x00050000, 0x00060000, 0x00070000, 0x00000700, 0x00000000, // 41 )
2585 0x00000000, 0x00000000, 0x00000400, 0x00060504, 0x00000600, 0x00080006, 0x00000000, // 42 *
2586 0x00000000, 0x00000000, 0x00000400, 0x00060504, 0x00000600, 0x00000000, 0x00000000, // 43 +
2587 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000600, 0x00000700, 0x00000007, // 44 ,
2588 0x00000000, 0x00000000, 0x00000000, 0x00060504, 0x00000000, 0x00000000, 0x00000000, // 45 -
2589 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000700, 0x00000000, // 46 .
2590 0x00030000, 0x00040000, 0x00000400, 0x00000500, 0x00000005, 0x00000006, 0x00000000, // 47 /
2591 0x00000000, 0x00000300, 0x00050003, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 48 0
2592 0x00000000, 0x00000300, 0x00000403, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 49 1
2593 0x00000000, 0x00000302, 0x00050000, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 50 2
2594 0x00000000, 0x00000302, 0x00050000, 0x00000504, 0x00070000, 0x00000706, 0x00000000, // 51 3
2595 0x00000000, 0x00000300, 0x00000003, 0x00060004, 0x00070605, 0x00080000, 0x00000000, // 52 4
2596 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00070000, 0x00000706, 0x00000000, // 53 5
2597 0x00000000, 0x00000300, 0x00000003, 0x00000504, 0x00070005, 0x00000700, 0x00000000, // 54 6
2598 0x00000000, 0x00040302, 0x00050000, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 55 7
2599 0x00000000, 0x00000300, 0x00050003, 0x00000500, 0x00070005, 0x00000700, 0x00000000, // 56 8
2600 0x00000000, 0x00000300, 0x00050003, 0x00060500, 0x00070000, 0x00000700, 0x00000000, // 57 9
2601 0x00000000, 0x00000000, 0x00000400, 0x00000000, 0x00000000, 0x00000700, 0x00000000, // 58 :
2602 0x00000000, 0x00000000, 0x00000000, 0x00000500, 0x00000000, 0x00000700, 0x00000007, // 59 ;
2603 0x00000000, 0x00040000, 0x00000400, 0x00000004, 0x00000600, 0x00080000, 0x00000000, // 60 <
2604 0x00000000, 0x00000000, 0x00050403, 0x00000000, 0x00070605, 0x00000000, 0x00000000, // 61 =
2605 0x00000000, 0x00000002, 0x00000400, 0x00060000, 0x00000600, 0x00000006, 0x00000000, // 62 >
2606 0x00000000, 0x00000302, 0x00050000, 0x00000500, 0x00000000, 0x00000700, 0x00000000, // 63 ?
2607 0x00000000, 0x00000300, 0x00050400, 0x00060004, 0x00070600, 0x00000000, 0x00000000, // 64 @
2608 0x00000000, 0x00000300, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 65 A
2609 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00070005, 0x00000706, 0x00000000, // 66 B
2610 0x00000000, 0x00040300, 0x00000003, 0x00000004, 0x00000005, 0x00080700, 0x00000000, // 67 C
2611 0x00000000, 0x00000302, 0x00050003, 0x00060004, 0x00070005, 0x00000706, 0x00000000, // 68 D
2612 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00000005, 0x00080706, 0x00000000, // 69 E
2613 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 70 F
2614 0x00000000, 0x00040300, 0x00000003, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 71 G
2615 0x00000000, 0x00040002, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 72 H
2616 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 73 I
2617 0x00000000, 0x00040000, 0x00050000, 0x00060000, 0x00070005, 0x00000700, 0x00000000, // 74 J
2618 0x00000000, 0x00040002, 0x00050003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 75 K
2619 0x00000000, 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00080706, 0x00000000, // 76 l
2620 0x00000000, 0x00040002, 0x00050403, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 77 M
2621 0x00000000, 0x00000302, 0x00050003, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 78 N
2622 0x00000000, 0x00040302, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00000000, // 79 O
2623 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 80 P
2624 0x00000000, 0x00040302, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00090000, // 81 Q
2625 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 82 R
2626 0x00000000, 0x00040300, 0x00000003, 0x00000500, 0x00070000, 0x00000706, 0x00000000, // 83 S
2627 0x00000000, 0x00040302, 0x00000400, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 84 T
2628 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00000000, // 85 U
2629 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00000600, 0x00000700, 0x00000000, // 86 V
2630 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 87 W
2631 0x00000000, 0x00040002, 0x00050003, 0x00000500, 0x00070005, 0x00080006, 0x00000000, // 88 X
2632 0x00000000, 0x00040002, 0x00050003, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 89 Y
2633 0x00000000, 0x00040302, 0x00050000, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 90 Z
2634 0x00000000, 0x00040300, 0x00000400, 0x00000500, 0x00000600, 0x00080700, 0x00000000, // 91 [
2635 0x00000000, 0x00000002, 0x00000400, 0x00000500, 0x00070000, 0x00080000, 0x00000000, // 92 '\'
2636 0x00000000, 0x00000302, 0x00000400, 0x00000500, 0x00000600, 0x00000706, 0x00000000, // 93 ]
2637 0x00000000, 0x00000300, 0x00050003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 94 ^
2638 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00080706, 0x00000000, // 95 _
2639 0x00000000, 0x00000002, 0x00000400, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 96 `
2640 0x00000000, 0x00000000, 0x00050400, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 97 a
2641 0x00000000, 0x00000002, 0x00000003, 0x00000504, 0x00070005, 0x00000706, 0x00000000, // 98 b
2642 0x00000000, 0x00000000, 0x00050400, 0x00000004, 0x00000005, 0x00080700, 0x00000000, // 99 c
2643 0x00000000, 0x00040000, 0x00050000, 0x00060500, 0x00070005, 0x00080700, 0x00000000, // 100 d
2644 0x00000000, 0x00000000, 0x00050400, 0x00060504, 0x00000005, 0x00080700, 0x00000000, // 101 e
2645 0x00000000, 0x00040300, 0x00000003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 102 f
2646 0x00000000, 0x00000000, 0x00050400, 0x00060004, 0x00070600, 0x00080000, 0x00000807, // 103 g
2647 0x00000000, 0x00000002, 0x00000003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 104 h
2648 0x00000000, 0x00000300, 0x00000000, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 105 i
2649 0x00000000, 0x00000300, 0x00000000, 0x00000500, 0x00000600, 0x00000700, 0x00000007, // 106 j
2650 0x00000000, 0x00000002, 0x00000003, 0x00060004, 0x00000605, 0x00080006, 0x00000000, // 107 k
2651 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000600, 0x00080000, 0x00000000, // 108 l
2652 0x00000000, 0x00000000, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 109 m
2653 0x00000000, 0x00000000, 0x00000403, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 110 n
2654 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 111 o
2655 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00000605, 0x00000006, 0x00000007, // 112 p
2656 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070600, 0x00080000, 0x00090000, // 113 q
2657 0x00000000, 0x00000000, 0x00050003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 114 r
2658 0x00000000, 0x00000000, 0x00050400, 0x00000004, 0x00070600, 0x00000706, 0x00000000, // 115 s
2659 0x00000000, 0x00000300, 0x00050403, 0x00000500, 0x00000600, 0x00080000, 0x00000000, // 116 t
2660 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 117 u
2661 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 118 v
2662 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 119 w
2663 0x00000000, 0x00000000, 0x00050003, 0x00000500, 0x00070005, 0x00080006, 0x00000000, // 120 x
2664 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00000600, 0x00000700, 0x00000007, // 121 y
2665 0x00000000, 0x00000000, 0x00050403, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 122 z
2666 0x00000000, 0x00040300, 0x00000400, 0x00000504, 0x00000600, 0x00080700, 0x00000000, // 123 {
2667 0x00000000, 0x00000300, 0x00000400, 0x00000000, 0x00000600, 0x00000700, 0x00000000, // 124 |
2668 0x00000000, 0x00000302, 0x00000400, 0x00060500, 0x00000600, 0x00000706, 0x00000000, // 125 }
2669 0x00000000, 0x00000302, 0x00050000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 126 ~
2670 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070605, 0x00000000, 0x00000000, // 127
2671 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
2672 };
2673
2674
PutTextInternal(const char * str,int len,short x,short y,int color,int backcolor)2675 static void PutTextInternal (const char *str, int len, short x, short y, int color, int backcolor)
2676 {
2677 int Opac = (color >> 24) & 0xFF;
2678 int backOpac = (backcolor >> 24) & 0xFF;
2679 int origX = x;
2680
2681 if(!Opac && !backOpac)
2682 return;
2683
2684 while(*str && len && y < LUA_SCREEN_HEIGHT)
2685 {
2686 int c = *str++;
2687 const unsigned char* Cur_Glyph;
2688 int y2,x2,y3,x3;
2689
2690 while (x > LUA_SCREEN_WIDTH && c != '\n') {
2691 c = *str;
2692 if (c == '\0')
2693 break;
2694 str++;
2695 }
2696 if(c == '\n')
2697 {
2698 x = origX;
2699 y += 8;
2700 continue;
2701 }
2702 else if(c == '\t') // just in case
2703 {
2704 const int tabSpace = 8;
2705 x += (tabSpace-(((x-origX)/4)%tabSpace))*4;
2706 continue;
2707 }
2708 if((unsigned int)(c-32) >= 96)
2709 continue;
2710 Cur_Glyph = (const unsigned char*)&Small_Font_Data + (c-32)*7*4;
2711
2712 for(y2 = 0; y2 < 8; y2++)
2713 {
2714 unsigned int glyphLine = *((unsigned int*)Cur_Glyph + y2);
2715 for(x2 = -1; x2 < 4; x2++)
2716 {
2717 int shift = x2 << 3;
2718 int mask = 0xFF << shift;
2719 int intensity = (glyphLine & mask) >> shift;
2720
2721 if(intensity && x2 >= 0 && y2 < 7)
2722 {
2723 //int xdraw = max(0,min(LUA_SCREEN_WIDTH - 1,x+x2));
2724 //int ydraw = max(0,min(LUA_SCREEN_HEIGHT - 1,y+y2));
2725 //gui_drawpixel_fast(xdraw, ydraw, color);
2726 gui_drawpixel_internal(x+x2, y+y2, color);
2727 }
2728 else if(backOpac)
2729 {
2730 for(y3 = max(0,y2-1); y3 <= min(6,y2+1); y3++)
2731 {
2732 unsigned int glyphLineTmp = *((unsigned int*)Cur_Glyph + y3);
2733 for(x3 = max(0,x2-1); x3 <= min(3,x2+1); x3++)
2734 {
2735 int shiftTmp = x3 << 3;
2736 int maskTmp = 0xFF << shiftTmp;
2737 intensity |= (glyphLineTmp & maskTmp) >> shiftTmp;
2738 if (intensity)
2739 goto draw_outline; // speedup?
2740 }
2741 }
2742 draw_outline:
2743 if(intensity)
2744 {
2745 //int xdraw = max(0,min(LUA_SCREEN_WIDTH - 1,x+x2));
2746 //int ydraw = max(0,min(LUA_SCREEN_HEIGHT - 1,y+y2));
2747 //gui_drawpixel_fast(xdraw, ydraw, backcolor);
2748 gui_drawpixel_internal(x+x2, y+y2, backcolor);
2749 }
2750 }
2751 }
2752 }
2753
2754 x += 4;
2755 len--;
2756 }
2757 }
2758
2759
LuaDisplayString(const char * string,int y,int x,UINT32 color,UINT32 outlineColor)2760 static void LuaDisplayString (const char *string, int y, int x, UINT32 color, UINT32 outlineColor)
2761 {
2762 if(!string)
2763 return;
2764
2765 gui_prepare();
2766
2767 PutTextInternal(string, strlen(string), x, y, color, outlineColor);
2768 }
2769
2770
2771 // gui.text(int x, int y, string msg[, color="white"[, outline="black"]])
2772 //
2773 // Displays the given text on the screen, using the same font and techniques as the
2774 // main HUD.
gui_text(lua_State * L)2775 static int gui_text(lua_State *L) {
2776 const char *msg;
2777 int x, y;
2778 UINT32 colour, borderColour;
2779 int argCount = lua_gettop(L);
2780
2781 x = luaL_checkinteger(L,1);
2782 y = luaL_checkinteger(L,2);
2783 msg = luaL_checkstring(L,3);
2784
2785 if(argCount>=4)
2786 colour = gui_getcolour(L,4);
2787 else
2788 colour = gui_optcolour(L,4,LUA_BUILD_PIXEL(255, 255, 255, 255));
2789
2790 if(argCount>=5)
2791 borderColour = gui_getcolour(L,5);
2792 else
2793 borderColour = gui_optcolour(L,5,LUA_BUILD_PIXEL(255, 0, 0, 0));
2794
2795 gui_prepare();
2796
2797 LuaDisplayString(msg, y, x, colour, borderColour);
2798
2799 return 0;
2800 }
2801
2802
2803 // gui.gdoverlay([int dx=0, int dy=0,] string str [, sx=0, sy=0, sw, sh] [, float alphamul=1.0])
2804 //
2805 // Overlays the given image on the screen.
2806 // example: gui.gdoverlay(gd.createFromPng("myimage.png"):gdStr())
gui_gdoverlay(lua_State * L)2807 static int gui_gdoverlay(lua_State *L) {
2808
2809 int i,y,x;
2810 int argCount = lua_gettop(L);
2811
2812 int xStartDst = 0;
2813 int yStartDst = 0;
2814 int xStartSrc = 0;
2815 int yStartSrc = 0;
2816
2817 const unsigned char* ptr;
2818
2819 int trueColor;
2820 int imgwidth;
2821 int width;
2822 int imgheight;
2823 int height;
2824 int pitch;
2825 int alphaMul;
2826 int opacMap[256];
2827 int colorsTotal = 0;
2828 int transparent;
2829 struct { UINT8 r, g, b, a; } pal[256];
2830 const UINT8* pix;
2831 int bytesToNextLine;
2832
2833 int index = 1;
2834 if(lua_type(L,index) == LUA_TNUMBER)
2835 {
2836 xStartDst = lua_tointeger(L,index++);
2837 if(lua_type(L,index) == LUA_TNUMBER)
2838 yStartDst = lua_tointeger(L,index++);
2839 }
2840
2841 luaL_checktype(L,index,LUA_TSTRING);
2842 ptr = (const unsigned char*)lua_tostring(L,index++);
2843
2844 if (ptr[0] != 255 || (ptr[1] != 254 && ptr[1] != 255))
2845 luaL_error(L, "bad image data");
2846 trueColor = (ptr[1] == 254);
2847 ptr += 2;
2848 imgwidth = *ptr++ << 8;
2849 imgwidth |= *ptr++;
2850 width = imgwidth;
2851 imgheight = *ptr++ << 8;
2852 imgheight |= *ptr++;
2853 height = imgheight;
2854 if ((!trueColor && *ptr) || (trueColor && !*ptr))
2855 luaL_error(L, "bad image data");
2856 ptr++;
2857 pitch = imgwidth * (trueColor?4:1);
2858
2859 if ((argCount - index + 1) >= 4) {
2860 xStartSrc = luaL_checkinteger(L,index++);
2861 yStartSrc = luaL_checkinteger(L,index++);
2862 width = luaL_checkinteger(L,index++);
2863 height = luaL_checkinteger(L,index++);
2864 }
2865
2866 alphaMul = transparencyModifier;
2867 if(lua_isnumber(L, index))
2868 alphaMul = (int)(alphaMul * lua_tonumber(L, index++));
2869 if(alphaMul <= 0)
2870 return 0;
2871
2872 // since there aren't that many possible opacity levels,
2873 // do the opacity modification calculations beforehand instead of per pixel
2874 for(i = 0; i < 128; i++)
2875 {
2876 int opac = 255 - ((i << 1) | (i & 1)); // gdAlphaMax = 127, not 255
2877 opac = (opac * alphaMul) / 255;
2878 if(opac < 0) opac = 0;
2879 if(opac > 255) opac = 255;
2880 opacMap[i] = opac;
2881 }
2882 for(i = 128; i < 256; i++)
2883 opacMap[i] = 0; // what should we do for them, actually?
2884
2885 if (!trueColor) {
2886 colorsTotal = *ptr++ << 8;
2887 colorsTotal |= *ptr++;
2888 }
2889 transparent = *ptr++ << 24;
2890 transparent |= *ptr++ << 16;
2891 transparent |= *ptr++ << 8;
2892 transparent |= *ptr++;
2893 if (!trueColor) for (i = 0; i < 256; i++) {
2894 pal[i].r = *ptr++;
2895 pal[i].g = *ptr++;
2896 pal[i].b = *ptr++;
2897 pal[i].a = opacMap[*ptr++];
2898 }
2899
2900 // some of clippings
2901 if (xStartSrc < 0) {
2902 width += xStartSrc;
2903 xStartDst -= xStartSrc;
2904 xStartSrc = 0;
2905 }
2906 if (yStartSrc < 0) {
2907 height += yStartSrc;
2908 yStartDst -= yStartSrc;
2909 yStartSrc = 0;
2910 }
2911 if (xStartSrc+width >= imgwidth)
2912 width = imgwidth - xStartSrc;
2913 if (yStartSrc+height >= imgheight)
2914 height = imgheight - yStartSrc;
2915 if (xStartDst < 0) {
2916 width += xStartDst;
2917 if (width <= 0)
2918 return 0;
2919 xStartSrc = -xStartDst;
2920 xStartDst = 0;
2921 }
2922 if (yStartDst < 0) {
2923 height += yStartDst;
2924 if (height <= 0)
2925 return 0;
2926 yStartSrc = -yStartDst;
2927 yStartDst = 0;
2928 }
2929 if (xStartDst+width >= LUA_SCREEN_WIDTH)
2930 width = LUA_SCREEN_WIDTH - xStartDst;
2931 if (yStartDst+height >= LUA_SCREEN_HEIGHT)
2932 height = LUA_SCREEN_HEIGHT - yStartDst;
2933 if (width <= 0 || height <= 0)
2934 return 0; // out of screen or invalid size
2935
2936 gui_prepare();
2937
2938 pix = (const UINT8*)(&ptr[yStartSrc*pitch + (xStartSrc*(trueColor?4:1))]);
2939 bytesToNextLine = pitch - (width * (trueColor?4:1));
2940 if (trueColor)
2941 for (y = yStartDst; y < height+yStartDst && y < LUA_SCREEN_HEIGHT; y++, pix += bytesToNextLine) {
2942 for (x = xStartDst; x < width+xStartDst && x < LUA_SCREEN_WIDTH; x++, pix += 4) {
2943 gui_drawpixel_fast(x, y, LUA_BUILD_PIXEL(opacMap[pix[0]], pix[1], pix[2], pix[3]));
2944 }
2945 }
2946 else
2947 for (y = yStartDst; y < height+yStartDst && y < LUA_SCREEN_HEIGHT; y++, pix += bytesToNextLine) {
2948 for (x = xStartDst; x < width+xStartDst && x < LUA_SCREEN_WIDTH; x++, pix++) {
2949 gui_drawpixel_fast(x, y, LUA_BUILD_PIXEL(pal[*pix].a, pal[*pix].r, pal[*pix].g, pal[*pix].b));
2950 }
2951 }
2952
2953 return 0;
2954 }
2955
2956
2957 // function gui.register(function f)
2958 //
2959 // This function will be called just before a graphical update.
2960 // More complicated, but doesn't suffer any frame delays.
2961 // Nil will be accepted in place of a function to erase
2962 // a previously registered function, and the previous function
2963 // (if any) is returned, or nil if none.
gui_register(lua_State * L)2964 static int gui_register(lua_State *L) {
2965 // We'll do this straight up.
2966
2967 // First set up the stack.
2968 lua_settop(L,1);
2969
2970 // Verify the validity of the entry
2971 if (!lua_isnil(L,1))
2972 luaL_checktype(L, 1, LUA_TFUNCTION);
2973
2974 // Get the old value
2975 lua_getfield(L, LUA_REGISTRYINDEX, guiCallbackTable);
2976
2977 // Save the new value
2978 lua_pushvalue(L,1);
2979 lua_setfield(L, LUA_REGISTRYINDEX, guiCallbackTable);
2980
2981 // The old value is on top of the stack. Return it.
2982 return 1;
2983 }
2984
2985
doPopup(lua_State * L,const char * deftype,const char * deficon)2986 static int doPopup(lua_State *L, const char* deftype, const char* deficon) {
2987 const char *str = luaL_checkstring(L, 1);
2988 const char* type = lua_type(L,2) == LUA_TSTRING ? lua_tostring(L,2) : deftype;
2989 const char* icon = lua_type(L,3) == LUA_TSTRING ? lua_tostring(L,3) : deficon;
2990
2991 int itype = -1, iters = 0;
2992 int iicon = -1;
2993 static const char * const titles [] = {"Notice", "Question", "Warning", "Error"};
2994 const char* answer = "ok";
2995
2996 while(itype == -1 && iters++ < 2)
2997 {
2998 if(!stricmp(type, "ok")) itype = 0;
2999 else if(!stricmp(type, "yesno")) itype = 1;
3000 else if(!stricmp(type, "yesnocancel")) itype = 2;
3001 else if(!stricmp(type, "okcancel")) itype = 3;
3002 else if(!stricmp(type, "abortretryignore")) itype = 4;
3003 else type = deftype;
3004 }
3005 assert(itype >= 0 && itype <= 4);
3006 if(!(itype >= 0 && itype <= 4)) itype = 0;
3007
3008 iters = 0;
3009 while(iicon == -1 && iters++ < 2)
3010 {
3011 if(!stricmp(icon, "message") || !stricmp(icon, "notice")) iicon = 0;
3012 else if(!stricmp(icon, "question")) iicon = 1;
3013 else if(!stricmp(icon, "warning")) iicon = 2;
3014 else if(!stricmp(icon, "error")) iicon = 3;
3015 else icon = deficon;
3016 }
3017 assert(iicon >= 0 && iicon <= 3);
3018 if(!(iicon >= 0 && iicon <= 3)) iicon = 0;
3019
3020 #ifdef WIN32
3021 {
3022 static const int etypes [] = {MB_OK, MB_YESNO, MB_YESNOCANCEL, MB_OKCANCEL, MB_ABORTRETRYIGNORE};
3023 static const int eicons [] = {MB_ICONINFORMATION, MB_ICONQUESTION, MB_ICONWARNING, MB_ICONERROR};
3024 int ianswer = MessageBoxA(hScrnWnd, str, titles[iicon], etypes[itype] | eicons[iicon]);
3025 switch(ianswer)
3026 {
3027 case IDOK: answer = "ok"; break;
3028 case IDCANCEL: answer = "cancel"; break;
3029 case IDABORT: answer = "abort"; break;
3030 case IDRETRY: answer = "retry"; break;
3031 case IDIGNORE: answer = "ignore"; break;
3032 case IDYES: answer = "yes"; break;
3033 case IDNO: answer = "no"; break;
3034 }
3035
3036 lua_pushstring(L, answer);
3037 return 1;
3038 }
3039 #else
3040
3041 char *t;
3042 #ifdef __linux
3043 int pid; // appease compiler
3044
3045 // Before doing any work, verify the correctness of the parameters.
3046 if (strcmp(type, "ok") == 0)
3047 t = "OK:100";
3048 else if (strcmp(type, "yesno") == 0)
3049 t = "Yes:100,No:101";
3050 else if (strcmp(type, "yesnocancel") == 0)
3051 t = "Yes:100,No:101,Cancel:102";
3052 else
3053 return luaL_error(L, "invalid popup type \"%s\"", type);
3054
3055 // Can we find a copy of xmessage? Search the path.
3056
3057 char *path = strdup(getenv("PATH"));
3058
3059 char *current = path;
3060
3061 char *colon;
3062
3063 int found = 0;
3064
3065 while (current) {
3066 colon = strchr(current, ':');
3067
3068 // Clip off the colon.
3069 *colon++ = 0;
3070
3071 int len = strlen(current);
3072 char *filename = (char*)malloc(len + 12); // always give excess
3073 snprintf(filename, len+12, "%s/xmessage", current);
3074
3075 if (access(filename, X_OK) == 0) {
3076 free(filename);
3077 found = 1;
3078 break;
3079 }
3080
3081 // Failed, move on.
3082 current = colon;
3083 free(filename);
3084
3085 }
3086
3087 free(path);
3088
3089 // We've found it?
3090 if (!found)
3091 goto use_console;
3092
3093 pid = fork();
3094 if (pid == 0) {// I'm the virgin sacrifice
3095
3096 // I'm gonna be dead in a matter of microseconds anyways, so wasted memory doesn't matter to me.
3097 // Go ahead and abuse strdup.
3098 char * parameters[] = {"xmessage", "-buttons", t, strdup(str), NULL};
3099
3100 execvp("xmessage", parameters);
3101
3102 // Aw shitty
3103 perror("exec xmessage");
3104 exit(1);
3105 }
3106 else if (pid < 0) // something went wrong!!! Oh hell... use the console
3107 goto use_console;
3108 else {
3109 // We're the parent. Watch for the child.
3110 int r;
3111 int res = waitpid(pid, &r, 0);
3112 if (res < 0) // wtf?
3113 goto use_console;
3114
3115 // The return value gets copmlicated...
3116 if (!WIFEXITED(r)) {
3117 luaL_error(L, "don't screw with my xmessage process!");
3118 }
3119 r = WEXITSTATUS(r);
3120
3121 // We assume it's worked.
3122 if (r == 0)
3123 {
3124 return 0; // no parameters for an OK
3125 }
3126 if (r == 100) {
3127 lua_pushstring(L, "yes");
3128 return 1;
3129 }
3130 if (r == 101) {
3131 lua_pushstring(L, "no");
3132 return 1;
3133 }
3134 if (r == 102) {
3135 lua_pushstring(L, "cancel");
3136 return 1;
3137 }
3138
3139 // Wtf?
3140 return luaL_error(L, "popup failed due to unknown results involving xmessage (%d)", r);
3141 }
3142
3143 use_console:
3144 #endif
3145
3146 // All else has failed
3147
3148 if (strcmp(type, "ok") == 0)
3149 t = "";
3150 else if (strcmp(type, "yesno") == 0)
3151 t = "yn";
3152 else if (strcmp(type, "yesnocancel") == 0)
3153 t = "ync";
3154 else
3155 return luaL_error(L, "invalid popup type \"%s\"", type);
3156
3157 fprintf(stderr, "Lua Message: %s\n", str);
3158
3159 while (true) {
3160 char buffer[64];
3161
3162 // We don't want parameters
3163 if (!t[0]) {
3164 fprintf(stderr, "[Press Enter]");
3165 fgets(buffer, sizeof(buffer), stdin);
3166 // We're done
3167 return 0;
3168
3169 }
3170 fprintf(stderr, "(%s): ", t);
3171 fgets(buffer, sizeof(buffer), stdin);
3172
3173 // Check if the option is in the list
3174 if (strchr(t, tolower(buffer[0]))) {
3175 switch (tolower(buffer[0])) {
3176 case 'y':
3177 lua_pushstring(L, "yes");
3178 return 1;
3179 case 'n':
3180 lua_pushstring(L, "no");
3181 return 1;
3182 case 'c':
3183 lua_pushstring(L, "cancel");
3184 return 1;
3185 default:
3186 luaL_error(L, "internal logic error in console based prompts for gui.popup");
3187
3188 }
3189 }
3190
3191 // We fell through, so we assume the user answered wrong and prompt again.
3192
3193 }
3194
3195 // Nothing here, since the only way out is in the loop.
3196 #endif
3197
3198 }
3199
input_registerhotkey(lua_State * L)3200 static int input_registerhotkey(lua_State *L)
3201 {
3202 int hotkeyNumber = luaL_checkinteger(L,1);
3203 if (hotkeyNumber < 1 || hotkeyNumber > 9)
3204 {
3205 luaL_error(L, "input.registerhotkey(n,func) requires 1 <= n <= 9, but got n = %d.", hotkeyNumber);
3206 return 0;
3207 }
3208 else
3209 {
3210 const char* key = luaCallIDStrings[LUACALL_HOTKEY_1 + hotkeyNumber-1];
3211 lua_getfield(L, LUA_REGISTRYINDEX, key);
3212 lua_replace(L,1);
3213 if (!lua_isnil(L,2))
3214 luaL_checktype(L, 2, LUA_TFUNCTION);
3215 lua_settop(L,2);
3216 lua_setfield(L, LUA_REGISTRYINDEX, key);
3217 return 1;
3218 }
3219 }
3220
3221 // string gui.popup(string message, string type = "ok", string icon = "message")
3222 // string input.popup(string message, string type = "yesno", string icon = "question")
gui_popup(lua_State * L)3223 static int gui_popup(lua_State *L)
3224 {
3225 return doPopup(L, "ok", "message");
3226 }
input_popup(lua_State * L)3227 static int input_popup(lua_State *L)
3228 {
3229 return doPopup(L, "yesno", "question");
3230 }
3231
3232 #ifdef WIN32
3233
3234 const char* s_keyToName[256] =
3235 {
3236 NULL,
3237 "leftclick",
3238 "rightclick",
3239 NULL,
3240 "middleclick",
3241 NULL,
3242 NULL,
3243 NULL,
3244 "backspace",
3245 "tab",
3246 NULL,
3247 NULL,
3248 NULL,
3249 "enter",
3250 NULL,
3251 NULL,
3252 "shift", // 0x10
3253 "control",
3254 "alt",
3255 "pause",
3256 "capslock",
3257 NULL,
3258 NULL,
3259 NULL,
3260 NULL,
3261 NULL,
3262 NULL,
3263 "escape",
3264 NULL,
3265 NULL,
3266 NULL,
3267 NULL,
3268 "space", // 0x20
3269 "pageup",
3270 "pagedown",
3271 "end",
3272 "home",
3273 "left",
3274 "up",
3275 "right",
3276 "down",
3277 NULL,
3278 NULL,
3279 NULL,
3280 NULL,
3281 "insert",
3282 "delete",
3283 NULL,
3284 "0","1","2","3","4","5","6","7","8","9",
3285 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3286 "A","B","C","D","E","F","G","H","I","J",
3287 "K","L","M","N","O","P","Q","R","S","T",
3288 "U","V","W","X","Y","Z",
3289 NULL,
3290 NULL,
3291 NULL,
3292 NULL,
3293 NULL,
3294 "numpad0","numpad1","numpad2","numpad3","numpad4","numpad5","numpad6","numpad7","numpad8","numpad9",
3295 "numpad*","numpad+",
3296 NULL,
3297 "numpad-","numpad.","numpad/",
3298 "F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12",
3299 "F13","F14","F15","F16","F17","F18","F19","F20","F21","F22","F23","F24",
3300 NULL,
3301 NULL,
3302 NULL,
3303 NULL,
3304 NULL,
3305 NULL,
3306 NULL,
3307 NULL,
3308 "numlock",
3309 "scrolllock",
3310 NULL, // 0x92
3311 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3312 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3313 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3314 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3315 NULL, // 0xB9
3316 "semicolon",
3317 "plus",
3318 "comma",
3319 "minus",
3320 "period",
3321 "slash",
3322 "tilde",
3323 NULL, // 0xC1
3324 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3325 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3326 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3327 NULL, // 0xDA
3328 "leftbracket",
3329 "backslash",
3330 "rightbracket",
3331 "quote",
3332 };
3333
GetMouseData(UINT32 * md)3334 void GetMouseData(UINT32 *md)
3335 {
3336 extern UINT32 mousex,mousey;
3337 RECT t;
3338 GetClientRect(hScrnWnd, &t);
3339 //md[0] = (UINT32)(mousex / ((float)t.right / iScreenWidth));
3340 //md[1] = (UINT32)(mousey / ((float)t.bottom / iScreenHeight));
3341 }
3342
3343 #endif
3344
3345 // input.get()
3346 // takes no input, returns a lua table of entries representing the current input state,
3347 // independent of the joypad buttons the emulated game thinks are pressed
3348 // for example:
3349 // if the user is holding the W key and the left mouse button
3350 // and has the mouse at the bottom-right corner of the game screen,
3351 // then this would return {W=true, leftclick=true, xmouse=255, ymouse=223}
input_getcurrentinputstatus(lua_State * L)3352 static int input_getcurrentinputstatus(lua_State *L) {
3353 lua_newtable(L);
3354
3355 #ifdef WIN32
3356 // keyboard and mouse button status
3357 {
3358 int i;
3359 for(i = 1; i < 255; i++) {
3360 const char* name = s_keyToName[i];
3361 if(name) {
3362 int active;
3363 if(i == VK_CAPITAL || i == VK_NUMLOCK || i == VK_SCROLL)
3364 active = GetKeyState(i) & 0x01;
3365 else
3366 active = GetAsyncKeyState(i) & 0x8000;
3367 if(active) {
3368 lua_pushboolean(L, TRUE);
3369 lua_setfield(L, -2, name);
3370 }
3371 }
3372 }
3373 }
3374 // mouse position in game screen pixel coordinates
3375 {
3376 UINT32 MouseData[2];
3377 int x, y;
3378 GetMouseData(MouseData);
3379 x = MouseData[0];
3380 y = MouseData[1];
3381
3382 lua_pushinteger(L, x);
3383 lua_setfield(L, -2, "xmouse");
3384 lua_pushinteger(L, y);
3385 lua_setfield(L, -2, "ymouse");
3386 }
3387 #else
3388 // NYI (well, return an empty table)
3389 #endif
3390
3391 return 1;
3392 }
3393
3394
3395 // the following bit operations are ported from LuaBitOp 1.0.1,
3396 // because it can handle the sign bit (bit 31) correctly.
3397
3398 /*
3399 ** Lua BitOp -- a bit operations library for Lua 5.1.
3400 ** http://bitop.luajit.org/
3401 **
3402 ** Copyright (C) 2008-2009 Mike Pall. All rights reserved.
3403 **
3404 ** Permission is hereby granted, free of charge, to any person obtaining
3405 ** a copy of this software and associated documentation files (the
3406 ** "Software"), to deal in the Software without restriction, including
3407 ** without limitation the rights to use, copy, modify, merge, publish,
3408 ** distribute, sublicense, and/or sell copies of the Software, and to
3409 ** permit persons to whom the Software is furnished to do so, subject to
3410 ** the following conditions:
3411 **
3412 ** The above copyright notice and this permission notice shall be
3413 ** included in all copies or substantial portions of the Software.
3414 **
3415 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
3416 ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
3417 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
3418 ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
3419 ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
3420 ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
3421 ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3422 **
3423 ** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
3424 */
3425
3426 #ifdef _MSC_VER
3427 /* MSVC is stuck in the last century and doesn't have C99's stdint.h. */
3428 typedef __int32 int32_t;
3429 typedef unsigned __int32 uint32_t;
3430 typedef unsigned __int64 uint64_t;
3431 #else
3432 #include <stdint.h>
3433 #endif
3434
3435 typedef int32_t SBits;
3436 typedef uint32_t UBits;
3437
3438 typedef union {
3439 lua_Number n;
3440 #ifdef LUA_NUMBER_DOUBLE
3441 uint64_t b;
3442 #else
3443 UBits b;
3444 #endif
3445 } BitNum;
3446
3447 /* Convert argument to bit type. */
barg(lua_State * L,int idx)3448 static UBits barg(lua_State *L, int idx)
3449 {
3450 BitNum bn;
3451 UBits b;
3452 bn.n = lua_tonumber(L, idx);
3453 #if defined(LUA_NUMBER_DOUBLE)
3454 bn.n += 6755399441055744.0; /* 2^52+2^51 */
3455 #ifdef SWAPPED_DOUBLE
3456 b = (UBits)(bn.b >> 32);
3457 #else
3458 b = (UBits)bn.b;
3459 #endif
3460 #elif defined(LUA_NUMBER_INT) || defined(LUA_NUMBER_LONG) || \
3461 defined(LUA_NUMBER_LONGLONG) || defined(LUA_NUMBER_LONG_LONG) || \
3462 defined(LUA_NUMBER_LLONG)
3463 if (sizeof(UBits) == sizeof(lua_Number))
3464 b = bn.b;
3465 else
3466 b = (UBits)(SBits)bn.n;
3467 #elif defined(LUA_NUMBER_FLOAT)
3468 #error "A 'float' lua_Number type is incompatible with this library"
3469 #else
3470 #error "Unknown number type, check LUA_NUMBER_* in luaconf.h"
3471 #endif
3472 if (b == 0 && !lua_isnumber(L, idx))
3473 luaL_typerror(L, idx, "number");
3474 return b;
3475 }
3476
3477 /* Return bit type. */
3478 #define BRET(b) lua_pushnumber(L, (lua_Number)(SBits)(b)); return 1;
3479
bit_tobit(lua_State * L)3480 static int bit_tobit(lua_State *L) { BRET(barg(L, 1)) }
bit_bnot(lua_State * L)3481 static int bit_bnot(lua_State *L) { BRET(~barg(L, 1)) }
3482
3483 #define BIT_OP(func, opr) \
3484 static int func(lua_State *L) { int i; UBits b = barg(L, 1); \
3485 for (i = lua_gettop(L); i > 1; i--) b opr barg(L, i); BRET(b) }
3486 BIT_OP(bit_band, &=)
3487 BIT_OP(bit_bor, |=)
3488 BIT_OP(bit_bxor, ^=)
3489
3490 #define bshl(b, n) (b << n)
3491 #define bshr(b, n) (b >> n)
3492 #define bsar(b, n) ((SBits)b >> n)
3493 #define brol(b, n) ((b << n) | (b >> (32-n)))
3494 #define bror(b, n) ((b << (32-n)) | (b >> n))
3495 #define BIT_SH(func, fn) \
3496 static int func(lua_State *L) { \
3497 UBits b = barg(L, 1); UBits n = barg(L, 2) & 31; BRET(fn(b, n)) }
BIT_SH(bit_lshift,bshl)3498 BIT_SH(bit_lshift, bshl)
3499 BIT_SH(bit_rshift, bshr)
3500 BIT_SH(bit_arshift, bsar)
3501 BIT_SH(bit_rol, brol)
3502 BIT_SH(bit_ror, bror)
3503
3504 static int bit_bswap(lua_State *L)
3505 {
3506 UBits b = barg(L, 1);
3507 b = (b >> 24) | ((b >> 8) & 0xff00) | ((b & 0xff00) << 8) | (b << 24);
3508 BRET(b)
3509 }
3510
bit_tohex(lua_State * L)3511 static int bit_tohex(lua_State *L)
3512 {
3513 UBits b = barg(L, 1);
3514 SBits n = lua_isnone(L, 2) ? 8 : (SBits)barg(L, 2);
3515 const char *hexdigits = "0123456789abcdef";
3516 char buf[8];
3517 int i;
3518 if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; }
3519 if (n > 8) n = 8;
3520 for (i = (int)n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; }
3521 lua_pushlstring(L, buf, (size_t)n);
3522 return 1;
3523 }
3524
3525 static const struct luaL_Reg bit_funcs[] = {
3526 { "tobit", bit_tobit },
3527 { "bnot", bit_bnot },
3528 { "band", bit_band },
3529 { "bor", bit_bor },
3530 { "bxor", bit_bxor },
3531 { "lshift", bit_lshift },
3532 { "rshift", bit_rshift },
3533 { "arshift", bit_arshift },
3534 { "rol", bit_rol },
3535 { "ror", bit_ror },
3536 { "bswap", bit_bswap },
3537 { "tohex", bit_tohex },
3538 { NULL, NULL }
3539 };
3540
3541 /* Signed right-shifts are implementation-defined per C89/C99.
3542 ** But the de facto standard are arithmetic right-shifts on two's
3543 ** complement CPUs. This behaviour is required here, so test for it.
3544 */
3545 #define BAD_SAR (bsar(-8, 2) != (SBits)-2)
3546
luabitop_validate(lua_State * L)3547 BOOL luabitop_validate(lua_State *L) // originally named as luaopen_bit
3548 {
3549 UBits b;
3550 lua_pushnumber(L, (lua_Number)1437217655L);
3551 b = barg(L, -1);
3552 if (b != (UBits)1437217655L || BAD_SAR) { /* Perform a simple self-test. */
3553 const char *msg = "compiled with incompatible luaconf.h";
3554 #ifdef LUA_NUMBER_DOUBLE
3555 #ifdef WIN32
3556 if (b == (UBits)1610612736L)
3557 msg = "use D3DCREATE_FPU_PRESERVE with DirectX";
3558 #endif
3559 if (b == (UBits)1127743488L)
3560 msg = "not compiled with SWAPPED_DOUBLE";
3561 #endif
3562 if (BAD_SAR)
3563 msg = "arithmetic right-shift broken";
3564 luaL_error(L, "bit library self-test failed (%s)", msg);
3565 return FALSE;
3566 }
3567 return TRUE;
3568 }
3569
3570 // LuaBitOp ends here
3571
bit_bshift_emulua(lua_State * L)3572 static int bit_bshift_emulua(lua_State *L)
3573 {
3574 int shift = luaL_checkinteger(L,2);
3575 if (shift < 0) {
3576 lua_pushinteger(L, -shift);
3577 lua_replace(L, 2);
3578 return bit_lshift(L);
3579 }
3580 else
3581 return bit_rshift(L);
3582 }
3583
bitbit(lua_State * L)3584 static int bitbit(lua_State *L)
3585 {
3586 int rv = 0;
3587 int numArgs = lua_gettop(L);
3588 int i;
3589 for(i = 1; i <= numArgs; i++) {
3590 int where = luaL_checkinteger(L,i);
3591 if (where >= 0 && where < 32)
3592 rv |= (1 << where);
3593 }
3594 lua_settop(L,0);
3595 BRET(rv);
3596 }
3597
3598
3599 // The function called periodically to ensure Lua doesn't run amok.
FBA_LuaHookFunction(lua_State * L,lua_Debug * dbg)3600 static void FBA_LuaHookFunction(lua_State *L, lua_Debug *dbg) {
3601 if (numTries-- == 0) {
3602
3603 int kill = 0;
3604
3605 #ifdef WIN32
3606 // Uh oh
3607 int ret = MessageBoxA(hScrnWnd, "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);
3608
3609 if (ret == IDYES) {
3610 kill = 1;
3611 }
3612
3613 #else
3614 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");
3615 char buffer[64];
3616 while (TRUE) {
3617 fprintf(stderr, "(y/n): ");
3618 fgets(buffer, sizeof(buffer), stdin);
3619 if (buffer[0] == 'y' || buffer[0] == 'Y') {
3620 kill = 1;
3621 break;
3622 }
3623
3624 if (buffer[0] == 'n' || buffer[0] == 'N')
3625 break;
3626 }
3627 #endif
3628
3629 if (kill) {
3630 luaL_error(L, "Killed by user request.");
3631 FBA_LuaOnStop();
3632 }
3633
3634 // else, kill the debug hook.
3635 lua_sethook(L, NULL, 0, 0);
3636 }
3637 }
3638
3639
CallExitFunction()3640 void CallExitFunction() {
3641 int errorcode = 0;
3642
3643 if (!LUA)
3644 return;
3645
3646 lua_settop(LUA, 0);
3647 lua_getfield(LUA, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]);
3648
3649 if (lua_isfunction(LUA, -1))
3650 {
3651 chdir(luaCWD);
3652 errorcode = lua_pcall(LUA, 0, 0, 0);
3653 chdir(fbnCWD);
3654 }
3655
3656 if (errorcode)
3657 HandleCallbackError(LUA);
3658 }
3659
3660
3661 static const struct luaL_reg fbalib [] = {
3662 {"hardreset", fba_hardreset},
3663 {"romname", fba_romname},
3664 {"gamename", fba_gamename},
3665 {"parentname", fba_parentname},
3666 {"sourcename", fba_sourcename},
3667 {"speedmode", fba_speedmode},
3668 {"frameadvance", fba_frameadvance},
3669 {"pause", fba_pause},
3670 {"unpause", fba_unpause},
3671 {"framecount", movie_framecount},
3672 {"registerbefore", fba_registerbefore},
3673 {"registerafter", fba_registerafter},
3674 {"registerexit", fba_registerexit},
3675 {"registerstart", fba_registerstart},
3676 {"message", fba_message},
3677 {"print", print}, // sure, why not
3678 {"screenwidth", fba_screenwidth},
3679 {"screenheight", fba_screenheight},
3680 {"getreadonly", movie_getreadonly},
3681 {"setreadonly", movie_setreadonly},
3682 {NULL,NULL}
3683 };
3684
3685 static const struct luaL_reg memorylib [] = {
3686 {"readbyte", memory_readbyte},
3687 {"readbytesigned", memory_readbytesigned},
3688 {"readword", memory_readword},
3689 {"readwordsigned", memory_readwordsigned},
3690 {"readdword", memory_readdword},
3691 {"readdwordsigned", memory_readdwordsigned},
3692 {"readbyterange", memory_readbyterange},
3693 {"writebyte", memory_writebyte},
3694 {"writeword", memory_writeword},
3695 {"writedword", memory_writedword},
3696 // alternate naming scheme for word and double-word and unsigned
3697 {"readbyteunsigned", memory_readbyte},
3698 {"readwordunsigned", memory_readword},
3699 {"readdwordunsigned", memory_readdword},
3700 {"readshort", memory_readword},
3701 {"readshortunsigned", memory_readword},
3702 {"readshortsigned", memory_readwordsigned},
3703 {"readlong", memory_readdword},
3704 {"readlongunsigned", memory_readdword},
3705 {"readlongsigned", memory_readdwordsigned},
3706 {"writeshort", memory_writeword},
3707 {"writelong", memory_writedword},
3708
3709 // memory hooks
3710 {"registerwrite", memory_registerwrite},
3711 {"registerread", memory_registerread},
3712 {"registerexec", memory_registerexec},
3713
3714 // registers
3715 {"getregister", memory_getregister},
3716 {"setregister", memory_setregister},
3717
3718 {NULL,NULL}
3719 };
3720
3721 static const struct luaL_reg joypadlib[] = {
3722 {"get", joypad_get},
3723 {"getdown", joypad_getdown},
3724 {"getup", joypad_getup},
3725 {"set", joypad_set},
3726 // alternative names
3727 {"read", joypad_get},
3728 {"readdown", joypad_getdown},
3729 {"readup", joypad_getup},
3730 {"write", joypad_set},
3731 {NULL,NULL}
3732 };
3733
3734 static const struct luaL_reg savestatelib[] = {
3735 {"create", savestate_create},
3736 {"save", savestate_save},
3737 {"load", savestate_load},
3738
3739 {"registersave", savestate_registersave},
3740 {"registerload", savestate_registerload},
3741 {"savescriptdata", savestate_savescriptdata},
3742 {"loadscriptdata", savestate_loadscriptdata},
3743
3744 {NULL,NULL}
3745 };
3746
3747 static const struct luaL_reg movielib[] = {
3748 {"framecount", movie_framecount},
3749 {"mode", movie_mode},
3750 {"rerecordcounting", movie_rerecordcounting},
3751 {"setreadonly", movie_setreadonly},
3752 {"getreadonly", movie_getreadonly},
3753 {"stop", movie_stop},
3754 {"close", movie_stop}, // (alternative name)
3755 {"playbeginning", movie_playbeginning},
3756 {"length", movie_length},
3757 {NULL,NULL}
3758 };
3759
3760 static const struct luaL_reg guilib[] = {
3761 {"register", gui_register},
3762 {"text", gui_text},
3763 {"box", gui_drawbox},
3764 {"line", gui_drawline},
3765 {"pixel", gui_drawpixel},
3766 {"opacity", gui_setopacity},
3767 {"transparency", gui_transparency},
3768 {"popup", gui_popup},
3769 {"parsecolor", gui_parsecolor},
3770 {"savescreenshot", gui_savescreenshot},
3771 {"gdscreenshot", gui_gdscreenshot},
3772 {"gdoverlay", gui_gdoverlay},
3773 {"getpixel", gui_getpixel},
3774 {"clearuncommitted", gui_clearuncommitted},
3775 // alternative names
3776 {"drawtext", gui_text},
3777 {"drawbox", gui_drawbox},
3778 {"drawline", gui_drawline},
3779 {"drawpixel", gui_drawpixel},
3780 {"setpixel", gui_drawpixel},
3781 {"writepixel", gui_drawpixel},
3782 {"rect", gui_drawbox},
3783 {"drawrect", gui_drawbox},
3784 {"drawimage", gui_gdoverlay},
3785 {"image", gui_gdoverlay},
3786 {"readpixel", gui_getpixel},
3787 {NULL,NULL}
3788 };
3789
3790 static const struct luaL_reg inputlib[] = {
3791 {"get", input_getcurrentinputstatus},
3792 {"registerhotkey", input_registerhotkey},
3793 {"popup", input_popup},
3794 // alternative names
3795 {"read", input_getcurrentinputstatus},
3796 {NULL, NULL}
3797 };
3798
3799
FBA_LuaFrameBoundary()3800 void FBA_LuaFrameBoundary() {
3801 lua_State *thread;
3802 int result;
3803
3804 // HA!
3805 if (!LUA || !luaRunning || !bDrvOkay)
3806 return;
3807
3808 // Our function needs calling
3809 lua_settop(LUA,0);
3810 lua_getfield(LUA, LUA_REGISTRYINDEX, frameAdvanceThread);
3811 thread = lua_tothread(LUA,1);
3812
3813 // Lua calling C must know that we're busy inside a frame boundary
3814 frameBoundary = TRUE;
3815 frameAdvanceWaiting = FALSE;
3816
3817 numTries = MAX_TRIES;
3818 chdir(luaCWD);
3819 result = lua_resume(thread, 0);
3820 chdir(fbnCWD);
3821
3822 if (result == LUA_YIELD) {
3823 // Okay, we're fine with that.
3824 } else if (result != 0) {
3825 // Done execution by bad causes
3826 FBA_LuaOnStop();
3827 lua_pushnil(LUA);
3828 lua_setfield(LUA, LUA_REGISTRYINDEX, frameAdvanceThread);
3829
3830 // Error?
3831 #ifdef WIN32
3832 MessageBoxA( hScrnWnd, lua_tostring(thread,-1), "Lua run error", MB_OK | MB_ICONSTOP);
3833 #else
3834 fprintf(stderr, "Lua thread bombed out: %s\n", lua_tostring(thread,-1));
3835 #endif
3836
3837 } else {
3838 FBA_LuaOnStop();
3839 //VidSNewTinyMsg(_T("Script died of natural causes.\n"));
3840 }
3841
3842 // Past here, the nes actually runs, so any Lua code is called mid-frame. We must
3843 // not do anything too stupid, so let ourselves know.
3844 frameBoundary = FALSE;
3845
3846 if (!frameAdvanceWaiting) {
3847 FBA_LuaOnStop();
3848 }
3849 }
3850
3851
3852 /**
3853 * Loads and runs the given Lua script.
3854 * The emulator MUST be paused for this function to be
3855 * called. Otherwise, all frame boundary assumptions go out the window.
3856 *
3857 * Returns true on success, false on failure.
3858 */
FBA_LoadLuaCode(const char * filename)3859 int FBA_LoadLuaCode(const char *filename) {
3860 lua_State *thread;
3861 int result;
3862 char dir[_MAX_PATH];
3863 char *slash, *backslash;
3864 const char *luafile = filename;
3865
3866 if (luafile != luaScriptName)
3867 {
3868 if (luaScriptName) free(luaScriptName);
3869 luaScriptName = strdup(luafile);
3870 }
3871
3872 // Set current directory from luafile (for dofile)
3873 _getcwd(fbnCWD, _MAX_PATH);
3874 strcpy(dir, luafile);
3875 slash = strrchr(dir, '/');
3876 backslash = strrchr(dir, '\\');
3877 if (!slash || (backslash && backslash < slash))
3878 slash = backslash;
3879 if (slash) {
3880 slash[1] = '\0'; // keep slash itself for some reasons
3881 if (!LuaConsoleHWnd) {
3882 luafile+= strlen (dir);
3883 }
3884 _chdir(dir);
3885 }
3886 _getcwd(luaCWD, _MAX_PATH);
3887
3888 FBA_LuaStop();
3889 if (!LUA) {
3890 LUA = lua_open();
3891 luaL_openlibs(LUA);
3892
3893 luaL_register(LUA, "emu", fbalib);
3894 luaL_register(LUA, "fba", fbalib);
3895 luaL_register(LUA, "memory", memorylib);
3896 luaL_register(LUA, "joypad", joypadlib);
3897 luaL_register(LUA, "savestate", savestatelib);
3898 luaL_register(LUA, "movie", movielib);
3899 luaL_register(LUA, "gui", guilib);
3900 luaL_register(LUA, "input", inputlib);
3901 luaL_register(LUA, "bit", bit_funcs); // LuaBitOp library
3902 lua_settop(LUA, 0); // clean the stack, because each call to luaL_register leaves a table on top
3903
3904 // register a few utility functions outside of libraries (in the global namespace)
3905 lua_register(LUA, "print", print);
3906 lua_register(LUA, "tostring", tostring);
3907 lua_register(LUA, "addressof", addressof);
3908 lua_register(LUA, "copytable", copytable);
3909
3910 // old bit operation functions
3911 lua_register(LUA, "AND", bit_band);
3912 lua_register(LUA, "OR", bit_bor);
3913 lua_register(LUA, "XOR", bit_bxor);
3914 lua_register(LUA, "SHIFT", bit_bshift_emulua);
3915 lua_register(LUA, "BIT", bitbit);
3916
3917 luabitop_validate(LUA);
3918
3919 // push arrays for storing hook functions in
3920 for(int i = 0; i < LUAMEMHOOK_COUNT; i++)
3921 {
3922 lua_newtable(LUA);
3923 lua_setfield(LUA, LUA_REGISTRYINDEX, luaMemHookTypeStrings[i]);
3924 }
3925 }
3926
3927 // We make our thread NOW because we want it at the bottom of the stack.
3928 // If all goes wrong, we let the garbage collector remove it.
3929 thread = lua_newthread(LUA);
3930
3931 // Load the data
3932 result = luaL_loadfile(LUA, luafile);
3933
3934 if (result) {
3935 #ifdef WIN32
3936 // Doing this here caused nasty problems; reverting to MessageBox-from-dialog behavior.
3937 MessageBoxA(NULL, lua_tostring(LUA,-1), "Lua load error", MB_OK | MB_ICONSTOP);
3938 #else
3939 fprintf(stderr, "Failed to compile file: %s\n", lua_tostring(LUA,-1));
3940 #endif
3941
3942 // Wipe the stack. Our thread
3943 lua_settop(LUA,0);
3944 _chdir(fbnCWD);
3945 return 0; // Oh shit.
3946 }
3947
3948
3949 // Get our function into it
3950 lua_xmove(LUA, thread, 1);
3951
3952 // Save the thread to the registry. This is why I make the thread FIRST.
3953 lua_setfield(LUA, LUA_REGISTRYINDEX, frameAdvanceThread);
3954
3955
3956 // Initialize settings
3957 luaRunning = TRUE;
3958 skipRerecords = FALSE;
3959 numMemHooks = 0;
3960 transparencyModifier = 255; // opaque
3961 lua_joypads_used = 0; // not used
3962
3963 #ifdef WIN32
3964 info_print = PrintToWindowConsole;
3965 info_onstart = WinLuaOnStart;
3966 info_onstop = WinLuaOnStop;
3967 //if(!LuaConsoleHWnd)
3968 // LuaConsoleHWnd = CreateDialog(hAppInst, MAKEINTRESOURCE(IDD_LUA), NULL, (DLGPROC) DlgLuaScriptDialog);
3969 info_uid = (INT64)LuaConsoleHWnd;
3970 #else
3971 info_print = NULL;
3972 info_onstart = NULL;
3973 info_onstop = NULL;
3974 #endif
3975 if (info_onstart)
3976 info_onstart(info_uid);
3977
3978 // And run it right now. :)
3979 FBA_LuaFrameBoundary();
3980
3981 // Set up our protection hook to be executed once every 10,000 bytecode instructions.
3982 lua_sethook(thread, FBA_LuaHookFunction, LUA_MASKCOUNT, 10000);
3983
3984 _chdir(fbnCWD);
3985
3986 // We're done.
3987 return 1;
3988 }
3989
3990
3991 /**
3992 * Equivalent to repeating the last FBA_LoadLuaCode() call.
3993 */
FBA_ReloadLuaCode()3994 void FBA_ReloadLuaCode()
3995 {
3996 if (!luaScriptName)
3997 VidSNewTinyMsg(_T("There's no script to reload."));
3998 else
3999 FBA_LoadLuaCode(luaScriptName);
4000 }
4001
4002
4003 /**
4004 * Terminates a running Lua script by killing the whole Lua engine.
4005 *
4006 * Always safe to call, except from within a lua call itself (duh).
4007 *
4008 */
FBA_LuaStop()4009 void FBA_LuaStop() {
4010 //already killed
4011 if (!LUA) return;
4012
4013 //execute the user's shutdown callbacks
4014 CallExitFunction();
4015
4016 /*info.*/numMemHooks = 0;
4017 for(int i = 0; i < LUAMEMHOOK_COUNT; i++)
4018 CalculateMemHookRegions((LuaMemHookType)i);
4019
4020 if (info_onstop)
4021 info_onstop(info_uid);
4022
4023 lua_close(LUA); // this invokes our garbage collectors for us
4024 LUA = NULL;
4025 FBA_LuaOnStop();
4026 }
4027
4028
4029 /**
4030 * Returns true if there is a Lua script running.
4031 *
4032 */
FBA_LuaRunning()4033 int FBA_LuaRunning() {
4034 // FIXME: return false when no callback functions are registered.
4035 return (int) (LUA != NULL); // should return true if callback functions are active.
4036 }
4037
4038
4039 /**
4040 * Returns true if Lua would like to steal the given joypad control.
4041 */
FBA_LuaUsingJoypad()4042 int FBA_LuaUsingJoypad() {
4043 if (!FBA_LuaRunning())
4044 return 0;
4045 return lua_joypads_used;
4046 }
4047
4048
4049 /**
4050 * Reads the buttons Lua is feeding for the given joypad, in the same
4051 * format as the OS-specific code.
4052 *
4053 * This function must not be called more than once per frame. Ideally exactly once
4054 * per frame (if FBA_LuaUsingJoypad says it's safe to do so)
4055 */
FBA_LuaReadJoypad()4056 UINT32 FBA_LuaReadJoypad() {
4057 if (!FBA_LuaRunning())
4058 return 1;
4059
4060 if (lua_joypads_used) {
4061 // Update the values of all the inputs
4062 struct GameInp* pgi = NULL;
4063 unsigned int i;
4064 for (i = 0, pgi = GameInp; i < nGameInpCount; i++, pgi++) {
4065 if (pgi->nType == 0) {
4066 continue;
4067 }
4068 struct BurnInputInfo bii;
4069
4070 // Get the name of the input
4071 BurnDrvGetInputInfo(&bii, i);
4072
4073 // skip unused inputs
4074 if (bii.pVal == NULL) {
4075 continue;
4076 }
4077 if (bii.nType & BIT_GROUP_ANALOG) {
4078 *bii.pShortVal = lua_joypads[i];
4079 }
4080 else {
4081 if (bii.nType & BIT_DIGITAL && !(bii.nType & BIT_GROUP_CONSTANT)) {
4082 if(lua_joypads[i] == 1)
4083 *bii.pVal = 1;
4084 if(lua_joypads[i] == 2)
4085 *bii.pVal = 0;
4086 }
4087 else {
4088 *bii.pVal = lua_joypads[i];
4089 }
4090 }
4091 // dprintf(_T("*READ_JOY*: '%s' %d: "),_AtoT(bii.szName),lua_joypads[i]);
4092 }
4093
4094 lua_joypads_used = 0;
4095 memset(lua_joypads,0,sizeof(lua_joypads));
4096 return 0;
4097 }
4098 else
4099 return 1; // disconnected
4100 }
4101
4102
4103 /**
4104 * If this function returns true, the movie code should NOT increment
4105 * the rerecord count for a load-state.
4106 *
4107 * This function will not return true if a script is not running.
4108 */
FBA_LuaRerecordCountSkip()4109 int FBA_LuaRerecordCountSkip() {
4110 // FIXME: return true if (there are any active callback functions && skipRerecords)
4111 return LUA && luaRunning && skipRerecords;
4112 }
4113
4114
4115 /**
4116 * Given an 8-bit screen with the indicated resolution,
4117 * draw the current GUI onto it.
4118 *
4119 * Currently we only support 256x* resolutions.
4120 */
FBA_LuaGui(unsigned char * s,int width,int height,int bpp,int pitch)4121 void FBA_LuaGui(unsigned char *s, int width, int height, int bpp, int pitch) {
4122 XBuf = (UINT8 *)s;
4123
4124 iScreenWidth = width;
4125 iScreenHeight = height;
4126 iScreenBpp = bpp;
4127 iScreenPitch = pitch;
4128
4129 LUA_SCREEN_WIDTH = width;
4130 LUA_SCREEN_HEIGHT = height;
4131
4132 if (!LUA || !bDrvOkay/* || !luaRunning*/)
4133 return;
4134
4135 // dprintf(_T("*LUA GUI START*: x:%d y:%d d:%d p:%d\n"),width,height,bpp,pitch);
4136
4137 // First, check if we're being called by anybody
4138 lua_getfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable);
4139
4140 if (lua_isfunction(LUA, -1)) {
4141 int ret;
4142
4143 // We call it now
4144 numTries = MAX_TRIES;
4145 chdir(luaCWD);
4146 ret = lua_pcall(LUA, 0, 0, 0);
4147 chdir(fbnCWD);
4148 if (ret != 0) {
4149 #ifdef WIN32
4150 MessageBoxA(hScrnWnd, lua_tostring(LUA, -1), "Lua Error in GUI function", MB_OK);
4151 #else
4152 fprintf(stderr, "Lua error in gui.register function: %s\n", lua_tostring(LUA, -1));
4153 #endif
4154 // This is grounds for trashing the function
4155 lua_pushnil(LUA);
4156 lua_setfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable);
4157 }
4158 }
4159
4160 // And wreak the stack
4161 lua_settop(LUA, 0);
4162
4163 if (gui_used == GUI_CLEAR || !gui_enabled)
4164 return;
4165
4166 gui_used = GUI_USED_SINCE_LAST_FRAME;
4167
4168 int x, y;
4169
4170 switch(bpp)
4171 {
4172 case 2:
4173 {
4174 UINT16 *screen = (UINT16*) s;
4175 int ppl = pitch/2;
4176 for (y=0; y < height && y < LUA_SCREEN_HEIGHT; y++) {
4177 for (x=0; x < LUA_SCREEN_WIDTH; x++) {
4178 const UINT8 gui_alpha = gui_data[(y*LUA_SCREEN_WIDTH+x)*4+3];
4179 if (gui_alpha == 0) {
4180 // do nothing
4181 continue;
4182 }
4183
4184 const UINT8 gui_red = gui_data[(y*LUA_SCREEN_WIDTH+x)*4+2];
4185 const UINT8 gui_green = gui_data[(y*LUA_SCREEN_WIDTH+x)*4+1];
4186 const UINT8 gui_blue = gui_data[(y*LUA_SCREEN_WIDTH+x)*4];
4187 int red, green, blue;
4188
4189 if (gui_alpha == 255) {
4190 // direct copy
4191 red = gui_red;
4192 green = gui_green;
4193 blue = gui_blue;
4194 }
4195 else {
4196 // alpha-blending
4197 const UINT8 scr_red = ((screen[y*ppl + x] >> 11) & 31) << 3;
4198 const UINT8 scr_green = ((screen[y*ppl + x] >> 5) & 63) << 2;
4199 const UINT8 scr_blue = ( screen[y*ppl + x] & 31) << 3;
4200 red = (((int) gui_red - scr_red) * gui_alpha / 255 + scr_red) & 255;
4201 green = (((int) gui_green - scr_green) * gui_alpha / 255 + scr_green) & 255;
4202 blue = (((int) gui_blue - scr_blue) * gui_alpha / 255 + scr_blue) & 255;
4203 }
4204 screen[y*ppl + x] = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
4205 }
4206 }
4207 break;
4208 }
4209 case 3:
4210 {
4211 #define bytesPerPixel 3
4212 UINT8 *screen = (UINT8*) s;
4213 for (y=0; y < height && y < LUA_SCREEN_HEIGHT; y++) {
4214 for (x=0; x < LUA_SCREEN_WIDTH; x++) {
4215 const UINT8 gui_alpha = gui_data[(y*LUA_SCREEN_WIDTH+x)*4+3];
4216 if (gui_alpha == 0) {
4217 // do nothing
4218 continue;
4219 }
4220
4221 const UINT8 gui_red = gui_data[(y*LUA_SCREEN_WIDTH+x)*4+2];
4222 const UINT8 gui_green = gui_data[(y*LUA_SCREEN_WIDTH+x)*4+1];
4223 const UINT8 gui_blue = gui_data[(y*LUA_SCREEN_WIDTH+x)*4];
4224 int red, green, blue;
4225
4226 if (gui_alpha == 255) {
4227 // direct copy
4228 red = gui_red;
4229 green = gui_green;
4230 blue = gui_blue;
4231 }
4232 else {
4233 // alpha-blending
4234 const UINT8 scr_red = screen[y*pitch + x*bytesPerPixel + 2];
4235 const UINT8 scr_green = screen[y*pitch + x*bytesPerPixel + 1];
4236 const UINT8 scr_blue = screen[y*pitch + x*bytesPerPixel];
4237 red = (((int) gui_red - scr_red) * gui_alpha / 255 + scr_red) & 255;
4238 green = (((int) gui_green - scr_green) * gui_alpha / 255 + scr_green) & 255;
4239 blue = (((int) gui_blue - scr_blue) * gui_alpha / 255 + scr_blue) & 255;
4240 }
4241 screen[y*pitch + x*bytesPerPixel] = blue;
4242 screen[y*pitch + x*bytesPerPixel + 1] = green;
4243 screen[y*pitch + x*bytesPerPixel + 2] = red;
4244 }
4245 }
4246 #undef bytesPerPixel
4247 break;
4248 }
4249 case 4:
4250 {
4251 #define bytesPerPixel 4
4252 UINT8 *screen = (UINT8*) s;
4253 for (y=0; y < height && y < LUA_SCREEN_HEIGHT; y++) {
4254 for (x=0; x < LUA_SCREEN_WIDTH; x++) {
4255 const UINT8 gui_alpha = gui_data[(y*LUA_SCREEN_WIDTH+x)*4+3];
4256 if (gui_alpha == 0) {
4257 // do nothing
4258 continue;
4259 }
4260
4261 const UINT8 gui_red = gui_data[(y*LUA_SCREEN_WIDTH+x)*4+2];
4262 const UINT8 gui_green = gui_data[(y*LUA_SCREEN_WIDTH+x)*4+1];
4263 const UINT8 gui_blue = gui_data[(y*LUA_SCREEN_WIDTH+x)*4];
4264 int red, green, blue;
4265
4266 if (gui_alpha == 255) {
4267 // direct copy
4268 red = gui_red;
4269 green = gui_green;
4270 blue = gui_blue;
4271 }
4272 else {
4273 // alpha-blending
4274 const UINT8 scr_red = screen[y*pitch + x*bytesPerPixel + 2];
4275 const UINT8 scr_green = screen[y*pitch + x*bytesPerPixel + 1];
4276 const UINT8 scr_blue = screen[y*pitch + x*bytesPerPixel];
4277 red = (((int) gui_red - scr_red) * gui_alpha / 255 + scr_red) & 255;
4278 green = (((int) gui_green - scr_green) * gui_alpha / 255 + scr_green) & 255;
4279 blue = (((int) gui_blue - scr_blue) * gui_alpha / 255 + scr_blue) & 255;
4280 }
4281 screen[y*pitch + x*bytesPerPixel] = blue;
4282 screen[y*pitch + x*bytesPerPixel + 1] = green;
4283 screen[y*pitch + x*bytesPerPixel + 2] = red;
4284 }
4285 }
4286 #undef bytesPerPixel
4287 break;
4288 }
4289 default:
4290 assert(false /* unsupported color-depth */);
4291 }
4292 return;
4293 }
4294
4295
FBA_LuaClearGui()4296 void FBA_LuaClearGui() {
4297 gui_used = GUI_CLEAR;
4298 if (gui_data) {
4299 free(gui_data);
4300 gui_data = NULL;
4301 }
4302 }
4303
FBA_LuaEnableGui(UINT8 enabled)4304 void FBA_LuaEnableGui(UINT8 enabled) {
4305 gui_enabled = enabled;
4306 }
4307
4308
FBA_GetLuaState()4309 lua_State* FBA_GetLuaState() {
4310 return LUA;
4311 }
FBA_GetLuaScriptName()4312 char* FBA_GetLuaScriptName() {
4313 return luaScriptName;
4314 }
4315