Mercurial > vba-clojure
comparison src/common/lua-engine.cpp @ 1:f9f4f1b99eed
importing src directory
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 03 Mar 2012 10:31:27 -0600 |
parents | |
children | bf9169ad4222 |
comparison
equal
deleted
inserted
replaced
0:8ced16adf2e1 | 1:f9f4f1b99eed |
---|---|
1 #include <cstdio> | |
2 #include <cstdlib> | |
3 #include <malloc.h> | |
4 #include <string> | |
5 #include <cassert> | |
6 #include <cctype> | |
7 #include <cmath> | |
8 #include <ctime> | |
9 | |
10 #include <vector> | |
11 #include <map> | |
12 #include <string> | |
13 #include <algorithm> | |
14 | |
15 using namespace std; | |
16 | |
17 #ifdef __linux | |
18 #include <unistd.h> // for unlink | |
19 #include <sys/types.h> | |
20 #include <sys/wait.h> | |
21 #endif | |
22 #if (defined(WIN32) && !defined(SDL)) | |
23 #include <direct.h> | |
24 #include "../win32/stdafx.h" | |
25 #include "../win32/Input.h" | |
26 #include "../win32/MainWnd.h" | |
27 #include "../win32/VBA.h" | |
28 #include "../win32/LuaOpenDialog.h" | |
29 #else | |
30 #define stricmp strcasecmp | |
31 #define strnicmp strncasecmp | |
32 #endif | |
33 | |
34 #include "../Port.h" | |
35 #include "System.h" | |
36 #include "movie.h" | |
37 #include "../gba/GBA.h" | |
38 #include "../gba/GBAGlobals.h" | |
39 #include "../gb/GB.h" | |
40 #include "../gb/gbGlobals.h" | |
41 #include "../gba/GBASound.h" | |
42 | |
43 #ifdef _WIN32 | |
44 #include "../win32/Sound.h" | |
45 //#include "../win32/WinMiscUtil.h" | |
46 extern CString winGetSavestateFilename(const CString &LogicalRomName, int nID); | |
47 #else | |
48 #endif | |
49 | |
50 extern "C" | |
51 { | |
52 #include "../lua/src/lua.h" | |
53 #include "../lua/src/lauxlib.h" | |
54 #include "../lua/src/lualib.h" | |
55 #include "../lua/src/lstate.h" | |
56 } | |
57 #include "vbalua.h" | |
58 | |
59 #include "../SFMT/SFMT.c" | |
60 | |
61 static void (*info_print)(int uid, const char *str); | |
62 static void (*info_onstart)(int uid); | |
63 static void (*info_onstop)(int uid); | |
64 static int info_uid; | |
65 | |
66 #ifndef countof | |
67 #define countof(a) (sizeof(a) / sizeof(a[0])) | |
68 #endif | |
69 | |
70 static lua_State *LUA; | |
71 | |
72 // Are we running any code right now? | |
73 static char *luaScriptName = NULL; | |
74 | |
75 // Are we running any code right now? | |
76 static bool8 luaRunning = false; | |
77 | |
78 // True at the frame boundary, false otherwise. | |
79 static bool8 frameBoundary = false; | |
80 | |
81 // The execution speed we're running at. | |
82 static enum { SPEED_NORMAL, SPEED_NOTHROTTLE, SPEED_TURBO, SPEED_MAXIMUM } speedmode = SPEED_NORMAL; | |
83 | |
84 // Rerecord count skip mode | |
85 static bool8 skipRerecords = false; | |
86 | |
87 // Used by the registry to find our functions | |
88 static const char *frameAdvanceThread = "VBA.FrameAdvance"; | |
89 static const char *guiCallbackTable = "VBA.GUI"; | |
90 | |
91 // True if there's a thread waiting to run after a run of frame-advance. | |
92 static bool8 frameAdvanceWaiting = false; | |
93 | |
94 // We save our pause status in the case of a natural death. | |
95 //static bool8 wasPaused = false; | |
96 | |
97 // Transparency strength. 255=opaque, 0=so transparent it's invisible | |
98 static int transparencyModifier = 255; | |
99 | |
100 // Our joypads. | |
101 static uint32 lua_joypads[4]; | |
102 static uint8 lua_joypads_used = 0; | |
103 | |
104 static bool8 gui_used = false; | |
105 static uint8 *gui_data = NULL; // BGRA | |
106 | |
107 // Protects Lua calls from going nuts. | |
108 // We set this to a big number like 1000 and decrement it | |
109 // over time. The script gets knifed once this reaches zero. | |
110 static int numTries; | |
111 | |
112 // number of registered memory functions (1 per hooked byte) | |
113 static unsigned int numMemHooks; | |
114 | |
115 // Look in inputglobal.h for macros named like BUTTON_MASK_UP to determine the order. | |
116 static const char *button_mappings[] = { | |
117 "A", "B", "select", "start", "right", "left", "up", "down", "R", "L" | |
118 }; | |
119 | |
120 #ifdef _MSC_VER | |
121 #define snprintf _snprintf | |
122 #define vscprintf _vscprintf | |
123 #else | |
124 #define stricmp strcasecmp | |
125 #define strnicmp strncasecmp | |
126 #define __forceinline __attribute__((always_inline)) | |
127 #endif | |
128 | |
129 static const char *luaCallIDStrings[] = | |
130 { | |
131 "CALL_BEFOREEMULATION", | |
132 "CALL_AFTEREMULATION", | |
133 "CALL_BEFOREEXIT" | |
134 }; | |
135 | |
136 //make sure we have the right number of strings | |
137 CTASSERT(sizeof(luaCallIDStrings) / sizeof(*luaCallIDStrings) == LUACALL_COUNT) | |
138 | |
139 static const char *luaMemHookTypeStrings [] = | |
140 { | |
141 "MEMHOOK_WRITE", | |
142 "MEMHOOK_READ", | |
143 "MEMHOOK_EXEC", | |
144 | |
145 "MEMHOOK_WRITE_SUB", | |
146 "MEMHOOK_READ_SUB", | |
147 "MEMHOOK_EXEC_SUB", | |
148 }; | |
149 | |
150 //make sure we have the right number of strings | |
151 CTASSERT(sizeof(luaMemHookTypeStrings) / sizeof(*luaMemHookTypeStrings) == LUAMEMHOOK_COUNT) | |
152 | |
153 static char *rawToCString(lua_State * L, int idx = 0); | |
154 static const char *toCString(lua_State *L, int idx = 0); | |
155 | |
156 // GBA memory I/O functions copied from win32/MemoryViewerDlg.cpp | |
157 static inline u8 CPUReadByteQuick(u32 addr) | |
158 { | |
159 return ::map[addr >> 24].address[addr & ::map[addr >> 24].mask]; | |
160 } | |
161 | |
162 static inline void CPUWriteByteQuick(u32 addr, u8 b) | |
163 { | |
164 ::map[addr >> 24].address[addr & ::map[addr >> 24].mask] = b; | |
165 } | |
166 | |
167 static inline u16 CPUReadHalfWordQuick(u32 addr) | |
168 { | |
169 return *((u16 *) &::map[addr >> 24].address[addr & ::map[addr >> 24].mask]); | |
170 } | |
171 | |
172 static inline void CPUWriteHalfWordQuick(u32 addr, u16 b) | |
173 { | |
174 *((u16 *) &::map[addr >> 24].address[addr & ::map[addr >> 24].mask]) = b; | |
175 } | |
176 | |
177 static inline u32 CPUReadMemoryQuick(u32 addr) | |
178 { | |
179 return *((u32 *) &::map[addr >> 24].address[addr & ::map[addr >> 24].mask]); | |
180 } | |
181 | |
182 static inline void CPUWriteMemoryQuick(u32 addr, u32 b) | |
183 { | |
184 *((u32 *) &::map[addr >> 24].address[addr & ::map[addr >> 24].mask]) = b; | |
185 } | |
186 | |
187 // GB | |
188 static inline u8 gbReadMemoryQuick8(u16 addr) | |
189 { | |
190 return gbReadMemoryQuick(addr); | |
191 } | |
192 | |
193 static inline void gbWriteMemoryQuick8(u16 addr, u8 b) | |
194 { | |
195 gbWriteMemoryQuick(addr, b); | |
196 } | |
197 | |
198 static inline u16 gbReadMemoryQuick16(u16 addr) | |
199 { | |
200 return (gbReadMemoryQuick(addr + 1) << 8) | gbReadMemoryQuick(addr); | |
201 } | |
202 | |
203 static inline void gbWriteMemoryQuick16(u16 addr, u16 b) | |
204 { | |
205 gbWriteMemoryQuick(addr, b & 0xff); | |
206 gbWriteMemoryQuick(addr + 1, (b >> 8) & 0xff); | |
207 } | |
208 | |
209 static inline u32 gbReadMemoryQuick32(u16 addr) | |
210 { | |
211 return (gbReadMemoryQuick(addr + 3) << 24) | | |
212 (gbReadMemoryQuick(addr + 2) << 16) | | |
213 (gbReadMemoryQuick(addr + 1) << 8) | | |
214 gbReadMemoryQuick(addr); | |
215 } | |
216 | |
217 static inline void gbWriteMemoryQuick32(u16 addr, u32 b) | |
218 { | |
219 gbWriteMemoryQuick(addr, b & 0xff); | |
220 gbWriteMemoryQuick(addr + 1, (b >> 8) & 0xff); | |
221 gbWriteMemoryQuick(addr + 2, (b >> 16) & 0xff); | |
222 gbWriteMemoryQuick(addr + 1, (b >> 24) & 0xff); | |
223 } | |
224 | |
225 static inline u8 gbReadROMQuick8(u32 addr) | |
226 { | |
227 return gbReadROMQuick(addr & gbRomSizeMask); | |
228 } | |
229 | |
230 static inline u8 gbReadROMQuick16(u32 addr) | |
231 { | |
232 return (gbReadROMQuick(addr+1 & gbRomSizeMask) << 8) | gbReadROMQuick(addr & gbRomSizeMask); | |
233 } | |
234 | |
235 static inline u8 gbReadROMQuick32(u32 addr) | |
236 { | |
237 return (gbReadROMQuick(addr+3 & gbRomSizeMask) << 24) | | |
238 (gbReadROMQuick(addr+2 & gbRomSizeMask) << 16) | | |
239 (gbReadROMQuick(addr+1 & gbRomSizeMask) << 8) | | |
240 gbReadROMQuick(addr & gbRomSizeMask); | |
241 } | |
242 | |
243 typedef void (*GetColorFunc)(const uint8 *, uint8 *, uint8 *, uint8 *); | |
244 typedef void (*SetColorFunc)(uint8 *, uint8, uint8, uint8); | |
245 | |
246 static void getColor16(const uint8 *s, uint8 *r, uint8 *g, uint8 *b) | |
247 { | |
248 u16 v = *(const uint16 *)s; | |
249 *r = ((v >> systemBlueShift) & 0x001f) << 3; | |
250 *g = ((v >> systemGreenShift) & 0x001f) << 3; | |
251 *b = ((v >> systemRedShift) & 0x001f) << 3; | |
252 } | |
253 | |
254 static void getColor24(const uint8 *s, uint8 *r, uint8 *g, uint8 *b) | |
255 { | |
256 if (systemRedShift > systemBlueShift) | |
257 *b = s[0], *g = s[1], *r = s[2]; | |
258 else | |
259 *r = s[0], *g = s[1], *b = s[2]; | |
260 } | |
261 | |
262 static void getColor32(const uint8 *s, uint8 *r, uint8 *g, uint8 *b) | |
263 { | |
264 u32 v = *(const uint32 *)s; | |
265 *b = ((v >> systemBlueShift) & 0x001f) << 3; | |
266 *g = ((v >> systemGreenShift) & 0x001f) << 3; | |
267 *r = ((v >> systemRedShift) & 0x001f) << 3; | |
268 } | |
269 | |
270 static void setColor16(uint8 *s, uint8 r, uint8 g, uint8 b) | |
271 { | |
272 *(uint16 *)s = ((b >> 3) & 0x01f) << | |
273 systemBlueShift | | |
274 ((g >> 3) & 0x01f) << | |
275 systemGreenShift | | |
276 ((r >> 3) & 0x01f) << | |
277 systemRedShift; | |
278 } | |
279 | |
280 static void setColor24(uint8 *s, uint8 r, uint8 g, uint8 b) | |
281 { | |
282 if (systemRedShift > systemBlueShift) | |
283 s[0] = b, s[1] = g, s[2] = r; | |
284 else | |
285 s[0] = r, s[1] = g, s[2] = b; | |
286 } | |
287 | |
288 static void setColor32(uint8 *s, uint8 r, uint8 g, uint8 b) | |
289 { | |
290 *(uint32 *)s = ((b >> 3) & 0x01f) << | |
291 systemBlueShift | | |
292 ((g >> 3) & 0x01f) << | |
293 systemGreenShift | | |
294 ((r >> 3) & 0x01f) << | |
295 systemRedShift; | |
296 } | |
297 | |
298 static bool getColorIOFunc(int depth, GetColorFunc *getColor, SetColorFunc *setColor) | |
299 { | |
300 switch (depth) | |
301 { | |
302 case 16: | |
303 if (getColor) | |
304 *getColor = getColor16; | |
305 if (setColor) | |
306 *setColor = setColor16; | |
307 return true; | |
308 case 24: | |
309 if (getColor) | |
310 *getColor = getColor24; | |
311 if (setColor) | |
312 *setColor = setColor24; | |
313 return true; | |
314 case 32: | |
315 if (getColor) | |
316 *getColor = getColor32; | |
317 if (setColor) | |
318 *setColor = setColor32; | |
319 return true; | |
320 default: | |
321 return false; | |
322 } | |
323 } | |
324 | |
325 /** | |
326 * Resets emulator speed / pause states after script exit. | |
327 */ | |
328 static void VBALuaOnStop(void) | |
329 { | |
330 luaRunning = false; | |
331 lua_joypads_used = 0; | |
332 gui_used = false; | |
333 //if (wasPaused) | |
334 // systemSetPause(true); | |
335 } | |
336 | |
337 /** | |
338 * Asks Lua if it wants control of the emulator's speed. | |
339 * Returns 0 if no, 1 if yes. If yes, we also tamper with the | |
340 * IPPU's settings for speed ourselves, so the calling code | |
341 * need not do anything. | |
342 */ | |
343 int VBALuaSpeed(void) | |
344 { | |
345 if (!LUA || !luaRunning) | |
346 return 0; | |
347 | |
348 //printf("%d\n", speedmode); | |
349 switch (speedmode) | |
350 { | |
351 /* | |
352 case SPEED_NORMAL: | |
353 return 0; | |
354 case SPEED_NOTHROTTLE: | |
355 IPPU.RenderThisFrame = true; | |
356 return 1; | |
357 | |
358 case SPEED_TURBO: | |
359 IPPU.SkippedFrames++; | |
360 if (IPPU.SkippedFrames >= 40) { | |
361 IPPU.SkippedFrames = 0; | |
362 IPPU.RenderThisFrame = true; | |
363 } | |
364 else | |
365 IPPU.RenderThisFrame = false; | |
366 return 1; | |
367 | |
368 // In mode 3, SkippedFrames is set to zero so that the frame | |
369 // skipping code doesn't try anything funny. | |
370 case SPEED_MAXIMUM: | |
371 IPPU.SkippedFrames=0; | |
372 IPPU.RenderThisFrame = false; | |
373 return 1; | |
374 */ | |
375 case 0: // FIXME: to get rid of the warning | |
376 default: | |
377 assert(false); | |
378 return 0; | |
379 } | |
380 } | |
381 | |
382 /////////////////////////// | |
383 // vba.speedmode(string mode) | |
384 // | |
385 // Takes control of the emulation speed | |
386 // of the system. Normal is normal speed (60fps, 50 for PAL), | |
387 // nothrottle disables speed control but renders every frame, | |
388 // turbo renders only a few frames in order to speed up emulation, | |
389 | |
390 // maximum renders no frames | |
391 static int vba_speedmode(lua_State *L) | |
392 { | |
393 const char *mode = luaL_checkstring(L, 1); | |
394 | |
395 if (strcasecmp(mode, "normal") == 0) | |
396 { | |
397 speedmode = SPEED_NORMAL; | |
398 } | |
399 else if (strcasecmp(mode, "nothrottle") == 0) | |
400 { | |
401 speedmode = SPEED_NOTHROTTLE; | |
402 } | |
403 else if (strcasecmp(mode, "turbo") == 0) | |
404 { | |
405 speedmode = SPEED_TURBO; | |
406 } | |
407 else if (strcasecmp(mode, "maximum") == 0) | |
408 { | |
409 speedmode = SPEED_MAXIMUM; | |
410 } | |
411 else | |
412 luaL_error(L, "Invalid mode %s to vba.speedmode", mode); | |
413 | |
414 //printf("new speed mode: %d\n", speedmode); | |
415 return 0; | |
416 } | |
417 | |
418 // vba.frameadvnace() | |
419 // | |
420 // Executes a frame advance. Occurs by yielding the coroutine, then re-running | |
421 | |
422 // when we break out. | |
423 static int vba_frameadvance(lua_State *L) | |
424 { | |
425 // We're going to sleep for a frame-advance. Take notes. | |
426 if (frameAdvanceWaiting) | |
427 return luaL_error(L, "can't call vba.frameadvance() from here"); | |
428 | |
429 frameAdvanceWaiting = true; | |
430 | |
431 // Don't do this! The user won't like us sending their emulator out of control! | |
432 // Settings.FrameAdvance = true; | |
433 // Now we can yield to the main | |
434 return lua_yield(L, 0); | |
435 | |
436 // It's actually rather disappointing... | |
437 } | |
438 | |
439 // vba.pause() | |
440 // | |
441 // Pauses the emulator, function "waits" until the user unpauses. | |
442 // This function MAY be called from a non-frame boundary, but the frame | |
443 | |
444 // finishes executing anwyays. In this case, the function returns immediately. | |
445 static int vba_pause(lua_State *L) | |
446 { | |
447 systemSetPause(true); | |
448 speedmode = SPEED_NORMAL; | |
449 | |
450 // Return control if we're midway through a frame. We can't pause here. | |
451 if (frameAdvanceWaiting) | |
452 { | |
453 return 0; | |
454 } | |
455 | |
456 // If it's on a frame boundary, we also yield. | |
457 frameAdvanceWaiting = true; | |
458 return lua_yield(L, 0); | |
459 } | |
460 | |
461 static int vba_registerbefore(lua_State *L) | |
462 { | |
463 if (!lua_isnil(L, 1)) | |
464 luaL_checktype(L, 1, LUA_TFUNCTION); | |
465 lua_settop(L, 1); | |
466 lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEMULATION]); | |
467 lua_insert(L, 1); | |
468 lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEMULATION]); | |
469 | |
470 //StopScriptIfFinished(luaStateToUIDMap[L]); | |
471 return 1; | |
472 } | |
473 | |
474 static int vba_registerafter(lua_State *L) | |
475 { | |
476 if (!lua_isnil(L, 1)) | |
477 luaL_checktype(L, 1, LUA_TFUNCTION); | |
478 lua_settop(L, 1); | |
479 lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTEREMULATION]); | |
480 lua_insert(L, 1); | |
481 lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTEREMULATION]); | |
482 | |
483 //StopScriptIfFinished(luaStateToUIDMap[L]); | |
484 return 1; | |
485 } | |
486 | |
487 static int vba_registerexit(lua_State *L) | |
488 { | |
489 if (!lua_isnil(L, 1)) | |
490 luaL_checktype(L, 1, LUA_TFUNCTION); | |
491 lua_settop(L, 1); | |
492 lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]); | |
493 lua_insert(L, 1); | |
494 lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]); | |
495 | |
496 //StopScriptIfFinished(luaStateToUIDMap[L]); | |
497 return 1; | |
498 } | |
499 | |
500 static inline bool isalphaorunderscore(char c) | |
501 { | |
502 return isalpha(c) || c == '_'; | |
503 } | |
504 | |
505 static std::vector<const void *> s_tableAddressStack; // prevents infinite recursion of a table within a table (when cycle is | |
506 // found, print something like table:parent) | |
507 static std::vector<const void *> s_metacallStack; // prevents infinite recursion if something's __tostring returns another table | |
508 // that contains that something (when cycle is found, print the inner result | |
509 // without using __tostring) | |
510 | |
511 #define APPENDPRINT { int _n = snprintf(ptr, remaining, | |
512 #define END ); if (_n >= 0) { ptr += _n; remaining -= _n; } else { remaining = 0; } } | |
513 static void toCStringConverter(lua_State *L, int i, char * &ptr, int &remaining) | |
514 { | |
515 if (remaining <= 0) | |
516 return; | |
517 | |
518 const char *str = ptr; // for debugging | |
519 | |
520 // if there is a __tostring metamethod then call it | |
521 int usedMeta = luaL_callmeta(L, i, "__tostring"); | |
522 if (usedMeta) | |
523 { | |
524 std::vector<const void *>::const_iterator foundCycleIter = std::find(s_metacallStack.begin(), s_metacallStack.end(), lua_topointer(L, i)); | |
525 if (foundCycleIter != s_metacallStack.end()) | |
526 { | |
527 lua_pop(L, 1); | |
528 usedMeta = false; | |
529 } | |
530 else | |
531 { | |
532 s_metacallStack.push_back(lua_topointer(L, i)); | |
533 i = lua_gettop(L); | |
534 } | |
535 } | |
536 | |
537 switch (lua_type(L, i)) | |
538 { | |
539 case LUA_TNONE: | |
540 break; | |
541 case LUA_TNIL: | |
542 APPENDPRINT "nil" END break; | |
543 case LUA_TBOOLEAN: | |
544 APPENDPRINT lua_toboolean(L, i) ? "true" : "false" END break; | |
545 case LUA_TSTRING : APPENDPRINT "%s", lua_tostring(L, i) END break; | |
546 case LUA_TNUMBER: | |
547 APPENDPRINT "%.12Lg", lua_tonumber(L, i) END break; | |
548 case LUA_TFUNCTION: | |
549 if ((L->base + i - 1)->value.gc->cl.c.isC) | |
550 { | |
551 //lua_CFunction func = lua_tocfunction(L, i); | |
552 //std::map<lua_CFunction, const char*>::iterator iter = s_cFuncInfoMap.find(func); | |
553 //if(iter == s_cFuncInfoMap.end()) | |
554 goto defcase; | |
555 //APPENDPRINT "function(%s)", iter->second END | |
556 } | |
557 else | |
558 { | |
559 APPENDPRINT "function(" END | |
560 Proto * p = (L->base + i - 1)->value.gc->cl.l.p; | |
561 int numParams = p->numparams + (p->is_vararg ? 1 : 0); | |
562 for (int n = 0; n < p->numparams; n++) | |
563 { | |
564 APPENDPRINT "%s", getstr(p->locvars[n].varname) END | |
565 if (n != numParams - 1) | |
566 APPENDPRINT "," END | |
567 } | |
568 if (p->is_vararg) | |
569 APPENDPRINT "..." END | |
570 APPENDPRINT ")" END | |
571 } | |
572 break; | |
573 defcase: default: | |
574 APPENDPRINT "%s:%p", luaL_typename(L, i), lua_topointer(L, i) END break; | |
575 case LUA_TTABLE: | |
576 { | |
577 // first make sure there's enough stack space | |
578 if (!lua_checkstack(L, 4)) | |
579 { | |
580 // note that even if lua_checkstack never returns false, | |
581 // that doesn't mean we didn't need to call it, | |
582 // because calling it retrieves stack space past LUA_MINSTACK | |
583 goto defcase; | |
584 } | |
585 | |
586 std::vector<const void *>::const_iterator foundCycleIter = | |
587 std::find(s_tableAddressStack.begin(), s_tableAddressStack.end(), lua_topointer(L, i)); | |
588 if (foundCycleIter != s_tableAddressStack.end()) | |
589 { | |
590 int parentNum = s_tableAddressStack.end() - foundCycleIter; | |
591 if (parentNum > 1) | |
592 APPENDPRINT "%s:parent^%d", luaL_typename(L, i), parentNum END | |
593 else | |
594 APPENDPRINT "%s:parent", luaL_typename(L, i) END | |
595 } | |
596 else | |
597 { | |
598 s_tableAddressStack.push_back(lua_topointer(L, i)); | |
599 struct Scope { ~Scope(){ s_tableAddressStack. pop_back(); } } scope; | |
600 | |
601 APPENDPRINT "{" END | |
602 | |
603 lua_pushnil(L); // first key | |
604 int keyIndex = lua_gettop(L); | |
605 int valueIndex = keyIndex + 1; | |
606 bool first = true; | |
607 bool skipKey = true; // true if we're still in the "array part" of the table | |
608 lua_Number arrayIndex = (lua_Number)0; | |
609 while (lua_next(L, i)) | |
610 { | |
611 if (first) | |
612 first = false; | |
613 else | |
614 APPENDPRINT ", " END | |
615 if (skipKey) | |
616 { | |
617 arrayIndex += (lua_Number)1; | |
618 bool keyIsNumber = (lua_type(L, keyIndex) == LUA_TNUMBER); | |
619 skipKey = keyIsNumber && (lua_tonumber(L, keyIndex) == arrayIndex); | |
620 } | |
621 if (!skipKey) | |
622 { | |
623 bool keyIsString = (lua_type(L, keyIndex) == LUA_TSTRING); | |
624 bool invalidLuaIdentifier = (!keyIsString || !isalphaorunderscore(*lua_tostring(L, keyIndex))); | |
625 if (invalidLuaIdentifier) | |
626 if (keyIsString) | |
627 APPENDPRINT "['" END | |
628 else | |
629 APPENDPRINT "[" END | |
630 | |
631 toCStringConverter(L, keyIndex, ptr, remaining); | |
632 // key | |
633 | |
634 if (invalidLuaIdentifier) | |
635 if (keyIsString) | |
636 APPENDPRINT "']=" END | |
637 else | |
638 APPENDPRINT "]=" END | |
639 else | |
640 APPENDPRINT "=" END | |
641 } | |
642 | |
643 bool valueIsString = (lua_type(L, valueIndex) == LUA_TSTRING); | |
644 if (valueIsString) | |
645 APPENDPRINT "'" END | |
646 | |
647 toCStringConverter(L, valueIndex, ptr, remaining); // value | |
648 | |
649 if (valueIsString) | |
650 APPENDPRINT "'" END | |
651 | |
652 lua_pop(L, 1); | |
653 | |
654 if (remaining <= 0) | |
655 { | |
656 lua_settop(L, keyIndex - 1); // stack might not be clean yet if we're breaking | |
657 // early | |
658 break; | |
659 } | |
660 } | |
661 APPENDPRINT "}" END | |
662 } | |
663 } | |
664 break; | |
665 } | |
666 | |
667 if (usedMeta) | |
668 { | |
669 s_metacallStack.pop_back(); | |
670 lua_pop(L, 1); | |
671 } | |
672 } | |
673 | |
674 static const int s_tempStrMaxLen = 64 * 1024; | |
675 static char s_tempStr [s_tempStrMaxLen]; | |
676 | |
677 static char *rawToCString(lua_State *L, int idx) | |
678 { | |
679 int a = idx > 0 ? idx : 1; | |
680 int n = idx > 0 ? idx : lua_gettop(L); | |
681 | |
682 char *ptr = s_tempStr; | |
683 *ptr = 0; | |
684 | |
685 int remaining = s_tempStrMaxLen; | |
686 for (int i = a; i <= n; i++) | |
687 { | |
688 toCStringConverter(L, i, ptr, remaining); | |
689 if (i != n) | |
690 APPENDPRINT " " END | |
691 } | |
692 | |
693 if (remaining < 3) | |
694 { | |
695 while (remaining < 6) | |
696 remaining++, ptr--; | |
697 APPENDPRINT "..." END | |
698 } | |
699 APPENDPRINT "\r\n" END | |
700 // the trailing newline is so print() can avoid having to do wasteful things to print its newline | |
701 // (string copying would be wasteful and calling info.print() twice can be extremely slow) | |
702 // at the cost of functions that don't want the newline needing to trim off the last two characters | |
703 // (which is a very fast operation and thus acceptable in this case) | |
704 | |
705 return s_tempStr; | |
706 } | |
707 #undef APPENDPRINT | |
708 #undef END | |
709 | |
710 // replacement for luaB_tostring() that is able to show the contents of tables (and formats numbers better, and show function | |
711 // prototypes) | |
712 // can be called directly from lua via tostring(), assuming tostring hasn't been reassigned | |
713 static int tostring(lua_State *L) | |
714 { | |
715 char *str = rawToCString(L); | |
716 str[strlen(str) - 2] = 0; // hack: trim off the \r\n (which is there to simplify the print function's | |
717 // task) | |
718 lua_pushstring(L, str); | |
719 return 1; | |
720 } | |
721 | |
722 // like rawToCString, but will check if the global Lua function tostring() | |
723 // has been replaced with a custom function, and call that instead if so | |
724 static const char *toCString(lua_State *L, int idx) | |
725 { | |
726 int a = idx > 0 ? idx : 1; | |
727 int n = idx > 0 ? idx : lua_gettop(L); | |
728 lua_getglobal(L, "tostring"); | |
729 lua_CFunction cf = lua_tocfunction(L, -1); | |
730 if (cf == tostring || lua_isnil(L, -1)) // optimization: if using our own C tostring function, we can | |
731 // bypass the call through Lua and all the string object | |
732 // allocation that would entail | |
733 { | |
734 lua_pop(L, 1); | |
735 return rawToCString(L, idx); | |
736 } | |
737 else // if the user overrided the tostring function, we have to actually call it and store the | |
738 // temporarily allocated string it returns | |
739 { | |
740 lua_pushstring(L, ""); | |
741 for (int i = a; i <= n; i++) | |
742 { | |
743 lua_pushvalue(L, -2); // function to be called | |
744 lua_pushvalue(L, i); // value to print | |
745 lua_call(L, 1, 1); | |
746 if (lua_tostring(L, -1) == NULL) | |
747 luaL_error(L, LUA_QL("tostring") " must return a string to " LUA_QL("print")); | |
748 lua_pushstring(L, (i < n) ? " " : "\r\n"); | |
749 lua_concat(L, 3); | |
750 } | |
751 const char *str = lua_tostring(L, -1); | |
752 strncpy(s_tempStr, str, s_tempStrMaxLen); | |
753 s_tempStr[s_tempStrMaxLen - 1] = 0; | |
754 lua_pop(L, 2); | |
755 return s_tempStr; | |
756 } | |
757 } | |
758 | |
759 // replacement for luaB_print() that goes to the appropriate textbox instead of stdout | |
760 static int print(lua_State *L) | |
761 { | |
762 const char *str = toCString(L); | |
763 | |
764 int uid = info_uid; //luaStateToUIDMap[L->l_G->mainthread]; | |
765 //LuaContextInfo& info = GetCurrentInfo(); | |
766 | |
767 if (info_print) | |
768 info_print(uid, str); | |
769 else | |
770 puts(str); | |
771 | |
772 //worry(L, 100); | |
773 return 0; | |
774 } | |
775 | |
776 static int printerror(lua_State *L, int idx) | |
777 { | |
778 lua_checkstack(L, lua_gettop(L) + 4); | |
779 | |
780 if (idx < 0) | |
781 idx = lua_gettop(L) + 1 + idx; | |
782 | |
783 const char *str = rawToCString(L, idx); | |
784 | |
785 int uid = info_uid; //luaStateToUIDMap[L->l_G->mainthread]; | |
786 //LuaContextInfo& info = GetCurrentInfo(); | |
787 | |
788 if (info_print) | |
789 info_print(uid, str); | |
790 else | |
791 fputs(str, stderr); | |
792 | |
793 //worry(L, 100); | |
794 return 0; | |
795 } | |
796 | |
797 // vba.message(string msg) | |
798 // | |
799 // Displays the given message on the screen. | |
800 static int vba_message(lua_State *L) | |
801 { | |
802 const char *msg = luaL_checkstring(L, 1); | |
803 systemScreenMessage(msg); | |
804 | |
805 return 0; | |
806 } | |
807 | |
808 // provides an easy way to copy a table from Lua | |
809 // (simple assignment only makes an alias, but sometimes an independent table is desired) | |
810 // currently this function only performs a shallow copy, | |
811 // but I think it should be changed to do a deep copy (possibly of configurable depth?) | |
812 // that maintains the internal table reference structure | |
813 static int copytable(lua_State *L) | |
814 { | |
815 int origIndex = 1; // we only care about the first argument | |
816 int origType = lua_type(L, origIndex); | |
817 if (origType == LUA_TNIL) | |
818 { | |
819 lua_pushnil(L); | |
820 return 1; | |
821 } | |
822 if (origType != LUA_TTABLE) | |
823 { | |
824 luaL_typerror(L, 1, lua_typename(L, LUA_TTABLE)); | |
825 lua_pushnil(L); | |
826 return 1; | |
827 } | |
828 | |
829 lua_createtable(L, lua_objlen(L, 1), 0); | |
830 int copyIndex = lua_gettop(L); | |
831 | |
832 lua_pushnil(L); // first key | |
833 int keyIndex = lua_gettop(L); | |
834 int valueIndex = keyIndex + 1; | |
835 | |
836 while (lua_next(L, origIndex)) | |
837 { | |
838 lua_pushvalue(L, keyIndex); | |
839 lua_pushvalue(L, valueIndex); | |
840 lua_rawset(L, copyIndex); // copytable[key] = value | |
841 lua_pop(L, 1); | |
842 } | |
843 | |
844 // copy the reference to the metatable as well, if any | |
845 if (lua_getmetatable(L, origIndex)) | |
846 lua_setmetatable(L, copyIndex); | |
847 | |
848 return 1; // return the new table | |
849 } | |
850 | |
851 // because print traditionally shows the address of tables, | |
852 // and the print function I provide instead shows the contents of tables, | |
853 // I also provide this function | |
854 // (otherwise there would be no way to see a table's address, AFAICT) | |
855 static int addressof(lua_State *L) | |
856 { | |
857 const void *ptr = lua_topointer(L, -1); | |
858 lua_pushinteger(L, (lua_Integer)ptr); | |
859 return 1; | |
860 } | |
861 | |
862 struct registerPointerMap | |
863 { | |
864 const char * registerName; | |
865 unsigned int *pointer; | |
866 int dataSize; | |
867 }; | |
868 | |
869 #define RPM_ENTRY(name, var) \ | |
870 { name, (unsigned int *)&var, sizeof(var) \ | |
871 } \ | |
872 , | |
873 | |
874 extern gbRegister AF; | |
875 extern gbRegister BC; | |
876 extern gbRegister DE; | |
877 extern gbRegister HL; | |
878 extern gbRegister SP; | |
879 extern gbRegister PC; | |
880 extern u16 IFF; | |
881 | |
882 registerPointerMap regPointerMap [] = { | |
883 // gba registers | |
884 RPM_ENTRY("r0", reg[0].I) | |
885 RPM_ENTRY("r1", reg[1].I) | |
886 RPM_ENTRY("r2", reg[2].I) | |
887 RPM_ENTRY("r3", reg[3].I) | |
888 RPM_ENTRY("r4", reg[4].I) | |
889 RPM_ENTRY("r5", reg[5].I) | |
890 RPM_ENTRY("r6", reg[6].I) | |
891 RPM_ENTRY("r7", reg[7].I) | |
892 RPM_ENTRY("r8", reg[8].I) | |
893 RPM_ENTRY("r9", reg[9].I) | |
894 RPM_ENTRY("r10", reg[10].I) | |
895 RPM_ENTRY("r11", reg[11].I) | |
896 RPM_ENTRY("r12", reg[12].I) | |
897 RPM_ENTRY("r13", reg[13].I) | |
898 RPM_ENTRY("r14", reg[14].I) | |
899 RPM_ENTRY("r15", reg[15].I) | |
900 RPM_ENTRY("cpsr", reg[16].I) | |
901 RPM_ENTRY("spsr", reg[17].I) | |
902 // gb registers | |
903 RPM_ENTRY("a", AF.B.B1) | |
904 RPM_ENTRY("f", AF.B.B0) | |
905 RPM_ENTRY("b", BC.B.B1) | |
906 RPM_ENTRY("c", BC.B.B0) | |
907 RPM_ENTRY("d", DE.B.B1) | |
908 RPM_ENTRY("e", DE.B.B0) | |
909 RPM_ENTRY("h", HL.B.B1) | |
910 RPM_ENTRY("l", HL.B.B0) | |
911 RPM_ENTRY("af", AF.W) | |
912 RPM_ENTRY("bc", BC.W) | |
913 RPM_ENTRY("de", DE.W) | |
914 RPM_ENTRY("hl", HL.W) | |
915 RPM_ENTRY("sp", SP.W) | |
916 RPM_ENTRY("pc", PC.W) | |
917 {} | |
918 }; | |
919 | |
920 struct cpuToRegisterMap | |
921 { | |
922 const char *cpuName; | |
923 registerPointerMap *rpmap; | |
924 } | |
925 cpuToRegisterMaps [] = | |
926 { | |
927 { "", regPointerMap }, | |
928 }; | |
929 | |
930 //DEFINE_LUA_FUNCTION(memory_getregister, "cpu_dot_registername_string") | |
931 static int memory_getregister(lua_State *L) | |
932 { | |
933 const char *qualifiedRegisterName = luaL_checkstring(L, 1); | |
934 lua_settop(L, 0); | |
935 for (int cpu = 0; cpu < sizeof(cpuToRegisterMaps) / sizeof(*cpuToRegisterMaps); cpu++) | |
936 { | |
937 cpuToRegisterMap ctrm = cpuToRegisterMaps[cpu]; | |
938 int cpuNameLen = strlen(ctrm.cpuName); | |
939 if (!strnicmp(qualifiedRegisterName, ctrm.cpuName, cpuNameLen)) | |
940 { | |
941 qualifiedRegisterName += cpuNameLen; | |
942 for (int reg = 0; ctrm.rpmap[reg].dataSize; reg++) | |
943 { | |
944 registerPointerMap rpm = ctrm.rpmap[reg]; | |
945 if (!stricmp(qualifiedRegisterName, rpm.registerName)) | |
946 { | |
947 switch (rpm.dataSize) | |
948 { | |
949 default: | |
950 case 1: | |
951 lua_pushinteger(L, *(unsigned char *)rpm.pointer); break; | |
952 case 2: | |
953 lua_pushinteger(L, *(unsigned short *)rpm.pointer); break; | |
954 case 4: | |
955 lua_pushinteger(L, *(unsigned long *)rpm.pointer); break; | |
956 } | |
957 return 1; | |
958 } | |
959 } | |
960 lua_pushnil(L); | |
961 return 1; | |
962 } | |
963 } | |
964 lua_pushnil(L); | |
965 return 1; | |
966 } | |
967 | |
968 //DEFINE_LUA_FUNCTION(memory_setregister, "cpu_dot_registername_string,value") | |
969 static int memory_setregister(lua_State *L) | |
970 { | |
971 const char * qualifiedRegisterName = luaL_checkstring(L, 1); | |
972 unsigned long value = (unsigned long)(luaL_checkinteger(L, 2)); | |
973 lua_settop(L, 0); | |
974 for (int cpu = 0; cpu < sizeof(cpuToRegisterMaps) / sizeof(*cpuToRegisterMaps); cpu++) | |
975 { | |
976 cpuToRegisterMap ctrm = cpuToRegisterMaps[cpu]; | |
977 int cpuNameLen = strlen(ctrm.cpuName); | |
978 if (!strnicmp(qualifiedRegisterName, ctrm.cpuName, cpuNameLen)) | |
979 { | |
980 qualifiedRegisterName += cpuNameLen; | |
981 for (int reg = 0; ctrm.rpmap[reg].dataSize; reg++) | |
982 { | |
983 registerPointerMap rpm = ctrm.rpmap[reg]; | |
984 if (!stricmp(qualifiedRegisterName, rpm.registerName)) | |
985 { | |
986 switch (rpm.dataSize) | |
987 { | |
988 default: | |
989 case 1: | |
990 *(unsigned char *)rpm.pointer = (unsigned char)(value & 0xFF); break; | |
991 case 2: | |
992 *(unsigned short *)rpm.pointer = (unsigned short)(value & 0xFFFF); break; | |
993 case 4: | |
994 *(unsigned long *)rpm.pointer = value; break; | |
995 } | |
996 return 0; | |
997 } | |
998 } | |
999 return 0; | |
1000 } | |
1001 } | |
1002 return 0; | |
1003 } | |
1004 | |
1005 void HandleCallbackError(lua_State *L) | |
1006 { | |
1007 if (L->errfunc || L->errorJmp) | |
1008 luaL_error(L, "%s", lua_tostring(L, -1)); | |
1009 else | |
1010 { | |
1011 lua_pushnil(LUA); | |
1012 lua_setfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable); | |
1013 | |
1014 // Error? | |
1015 //#if (defined(WIN32) && !defined(SDL)) | |
1016 // info_print(info_uid, lua_tostring(LUA, -1)); //Clear_Sound_Buffer(); | |
1017 // AfxGetApp()->m_pMainWnd->MessageBox(lua_tostring(LUA, -1), "Lua run error", MB_OK | MB_ICONSTOP); | |
1018 //#else | |
1019 // fprintf(stderr, "Lua thread bombed out: %s\n", lua_tostring(LUA, -1)); | |
1020 //#endif | |
1021 printerror(LUA, -1); | |
1022 VBALuaStop(); | |
1023 } | |
1024 } | |
1025 | |
1026 void CallRegisteredLuaFunctions(LuaCallID calltype) | |
1027 { | |
1028 assert((unsigned int)calltype < (unsigned int)LUACALL_COUNT); | |
1029 | |
1030 const char *idstring = luaCallIDStrings[calltype]; | |
1031 | |
1032 if (!LUA) | |
1033 return; | |
1034 | |
1035 lua_settop(LUA, 0); | |
1036 lua_getfield(LUA, LUA_REGISTRYINDEX, idstring); | |
1037 | |
1038 int errorcode = 0; | |
1039 if (lua_isfunction(LUA, -1)) | |
1040 { | |
1041 errorcode = lua_pcall(LUA, 0, 0, 0); | |
1042 if (errorcode) | |
1043 HandleCallbackError(LUA); | |
1044 } | |
1045 else | |
1046 { | |
1047 lua_pop(LUA, 1); | |
1048 } | |
1049 } | |
1050 | |
1051 // the purpose of this structure is to provide a way of | |
1052 // QUICKLY determining whether a memory address range has a hook associated with it, | |
1053 // with a bias toward fast rejection because the majority of addresses will not be hooked. | |
1054 // (it must not use any part of Lua or perform any per-script operations, | |
1055 // otherwise it would definitely be too slow.) | |
1056 // calculating the regions when a hook is added/removed may be slow, | |
1057 // but this is an intentional tradeoff to obtain a high speed of checking during later execution | |
1058 struct TieredRegion | |
1059 { | |
1060 template<unsigned int maxGap> | |
1061 struct Region | |
1062 { | |
1063 struct Island | |
1064 { | |
1065 unsigned int start; | |
1066 unsigned int end; | |
1067 __forceinline bool Contains(unsigned int address, int size) const { return address < end && address + size > start; } | |
1068 }; | |
1069 std::vector<Island> islands; | |
1070 | |
1071 void Calculate(const std::vector<unsigned int> &bytes) | |
1072 { | |
1073 islands. clear(); | |
1074 | |
1075 unsigned int lastEnd = ~0; | |
1076 | |
1077 std::vector<unsigned int>::const_iterator iter = bytes.begin(); | |
1078 std::vector<unsigned int>::const_iterator end = bytes.end(); | |
1079 for (; iter != end; ++iter) | |
1080 { | |
1081 unsigned int addr = *iter; | |
1082 if (addr < lastEnd || addr > lastEnd + (long long)maxGap) | |
1083 { | |
1084 islands. push_back(Island()); | |
1085 islands. back().start = addr; | |
1086 } | |
1087 islands.back(). end = addr + 1; | |
1088 lastEnd = addr + 1; | |
1089 } | |
1090 } | |
1091 | |
1092 bool Contains(unsigned int address, int size) const | |
1093 { | |
1094 for (size_t i = 0; i != islands.size(); ++i) | |
1095 { | |
1096 if (islands[i].Contains(address, size)) | |
1097 return true; | |
1098 } | |
1099 return false; | |
1100 } | |
1101 }; | |
1102 | |
1103 Region<0xFFFFFFFF> broad; | |
1104 Region<0x1000> mid; | |
1105 Region<0> narrow; | |
1106 | |
1107 void Calculate(std::vector<unsigned int> &bytes) | |
1108 { | |
1109 std:: sort(bytes.begin(), bytes.end()); | |
1110 | |
1111 broad. Calculate(bytes); | |
1112 mid. Calculate(bytes); | |
1113 narrow. Calculate(bytes); | |
1114 } | |
1115 | |
1116 TieredRegion() | |
1117 { | |
1118 std::vector <unsigned int> temp; | |
1119 Calculate(temp); | |
1120 } | |
1121 | |
1122 __forceinline int NotEmpty() | |
1123 { | |
1124 return broad.islands.size(); | |
1125 } | |
1126 | |
1127 // note: it is illegal to call this if NotEmpty() returns 0 | |
1128 __forceinline bool Contains(unsigned int address, int size) | |
1129 { | |
1130 return broad.islands[0].Contains(address, size) && | |
1131 mid.Contains(address, size) && | |
1132 narrow.Contains(address, size); | |
1133 } | |
1134 }; | |
1135 TieredRegion hookedRegions [LUAMEMHOOK_COUNT]; | |
1136 | |
1137 static void CalculateMemHookRegions(LuaMemHookType hookType) | |
1138 { | |
1139 std::vector<unsigned int> hookedBytes; | |
1140 // std::map<int, LuaContextInfo*>::iterator iter = luaContextInfo.begin(); | |
1141 // std::map<int, LuaContextInfo*>::iterator end = luaContextInfo.end(); | |
1142 // while(iter != end) | |
1143 // { | |
1144 // LuaContextInfo& info = *iter->second; | |
1145 if (/*info.*/ numMemHooks) | |
1146 { | |
1147 lua_State *L = LUA /*info.L*/; | |
1148 if (L) | |
1149 { | |
1150 lua_settop(L, 0); | |
1151 lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]); | |
1152 lua_pushnil(L); | |
1153 while (lua_next(L, -2)) | |
1154 { | |
1155 if (lua_isfunction(L, -1)) | |
1156 { | |
1157 unsigned int addr = lua_tointeger(L, -2); | |
1158 hookedBytes.push_back(addr); | |
1159 } | |
1160 lua_pop(L, 1); | |
1161 } | |
1162 lua_settop(L, 0); | |
1163 } | |
1164 } | |
1165 // ++iter; | |
1166 // } | |
1167 hookedRegions[hookType].Calculate(hookedBytes); | |
1168 } | |
1169 | |
1170 static void CallRegisteredLuaMemHook_LuaMatch(unsigned int address, int size, unsigned int value, LuaMemHookType hookType) | |
1171 { | |
1172 // std::map<int, LuaContextInfo*>::iterator iter = luaContextInfo.begin(); | |
1173 // std::map<int, LuaContextInfo*>::iterator end = luaContextInfo.end(); | |
1174 // while(iter != end) | |
1175 // { | |
1176 // LuaContextInfo& info = *iter->second; | |
1177 if (/*info.*/ numMemHooks) | |
1178 { | |
1179 lua_State *L = LUA /*info.L*/; | |
1180 if (L /* && !info.panic*/) | |
1181 { | |
1182 #ifdef USE_INFO_STACK | |
1183 infoStack.insert(infoStack.begin(), &info); | |
1184 struct Scope { ~Scope(){ infoStack. erase(infoStack.begin()); } } scope; | |
1185 #endif | |
1186 lua_settop(L, 0); | |
1187 lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]); | |
1188 for (int i = address; i != address + size; i++) | |
1189 { | |
1190 lua_rawgeti(L, -1, i); | |
1191 if (lua_isfunction(L, -1)) | |
1192 { | |
1193 bool wasRunning = (luaRunning != 0) /*info.running*/; | |
1194 luaRunning /*info.running*/ = true; | |
1195 //RefreshScriptSpeedStatus(); | |
1196 lua_pushinteger(L, address); | |
1197 lua_pushinteger(L, size); | |
1198 int errorcode = lua_pcall(L, 2, 0, 0); | |
1199 luaRunning /*info.running*/ = wasRunning; | |
1200 //RefreshScriptSpeedStatus(); | |
1201 if (errorcode) | |
1202 { | |
1203 HandleCallbackError(L); | |
1204 //int uid = iter->first; | |
1205 //HandleCallbackError(L,info,uid,true); | |
1206 } | |
1207 break; | |
1208 } | |
1209 else | |
1210 { | |
1211 lua_pop(L, 1); | |
1212 } | |
1213 } | |
1214 lua_settop(L, 0); | |
1215 } | |
1216 } | |
1217 // ++iter; | |
1218 // } | |
1219 } | |
1220 | |
1221 void CallRegisteredLuaMemHook(unsigned int address, int size, unsigned int value, LuaMemHookType hookType) | |
1222 { | |
1223 // performance critical! (called VERY frequently) | |
1224 // I suggest timing a large number of calls to this function in Release if you change anything in here, | |
1225 // before and after, because even the most innocent change can make it become 30% to 400% slower. | |
1226 // a good amount to test is: 100000000 calls with no hook set, and another 100000000 with a hook set. | |
1227 // (on my system that consistently took 200 ms total in the former case and 350 ms total in the latter | |
1228 // case) | |
1229 if (hookedRegions[hookType].NotEmpty()) | |
1230 { | |
1231 //if((hookType <= LUAMEMHOOK_EXEC) && (address >= 0xE00000)) | |
1232 // address |= 0xFF0000; // account for mirroring of RAM | |
1233 if (hookedRegions[hookType].Contains(address, size)) | |
1234 CallRegisteredLuaMemHook_LuaMatch(address, size, value, hookType); // something has hooked this | |
1235 // specific address | |
1236 } | |
1237 } | |
1238 | |
1239 static int memory_registerHook(lua_State *L, LuaMemHookType hookType, int defaultSize) | |
1240 { | |
1241 // get first argument: address | |
1242 unsigned int addr = luaL_checkinteger(L, 1); | |
1243 //if((addr & ~0xFFFFFF) == ~0xFFFFFF) | |
1244 // addr &= 0xFFFFFF; | |
1245 | |
1246 // get optional second argument: size | |
1247 int size = defaultSize; | |
1248 int funcIdx = 2; | |
1249 if (lua_isnumber(L, 2)) | |
1250 { | |
1251 size = luaL_checkinteger(L, 2); | |
1252 if (size < 0) | |
1253 { | |
1254 size = -size; | |
1255 addr -= size; | |
1256 } | |
1257 funcIdx++; | |
1258 } | |
1259 | |
1260 // check last argument: callback function | |
1261 bool clearing = lua_isnil(L, funcIdx); | |
1262 if (!clearing) | |
1263 luaL_checktype(L, funcIdx, LUA_TFUNCTION); | |
1264 lua_settop(L, funcIdx); | |
1265 | |
1266 // get the address-to-callback table for this hook type of the current script | |
1267 lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]); | |
1268 | |
1269 // count how many callback functions we'll be displacing | |
1270 int numFuncsAfter = clearing ? 0 : size; | |
1271 int numFuncsBefore = 0; | |
1272 for (unsigned int i = addr; i != addr + size; i++) | |
1273 { | |
1274 lua_rawgeti(L, -1, i); | |
1275 if (lua_isfunction(L, -1)) | |
1276 numFuncsBefore++; | |
1277 lua_pop(L, 1); | |
1278 } | |
1279 | |
1280 // put the callback function in the address slots | |
1281 for (unsigned int i = addr; i != addr + size; i++) | |
1282 { | |
1283 lua_pushvalue(L, -2); | |
1284 lua_rawseti(L, -2, i); | |
1285 } | |
1286 | |
1287 // adjust the count of active hooks | |
1288 //LuaContextInfo& info = GetCurrentInfo(); | |
1289 /*info.*/ numMemHooks += numFuncsAfter - numFuncsBefore; | |
1290 | |
1291 // re-cache regions of hooked memory across all scripts | |
1292 CalculateMemHookRegions(hookType); | |
1293 | |
1294 //StopScriptIfFinished(luaStateToUIDMap[L]); | |
1295 return 0; | |
1296 } | |
1297 | |
1298 LuaMemHookType MatchHookTypeToCPU(lua_State *L, LuaMemHookType hookType) | |
1299 { | |
1300 int cpuID = 0; | |
1301 | |
1302 int cpunameIndex = 0; | |
1303 if (lua_type(L, 2) == LUA_TSTRING) | |
1304 cpunameIndex = 2; | |
1305 else if (lua_type(L, 3) == LUA_TSTRING) | |
1306 cpunameIndex = 3; | |
1307 | |
1308 if (cpunameIndex) | |
1309 { | |
1310 const char *cpuName = lua_tostring(L, cpunameIndex); | |
1311 if (!stricmp(cpuName, "sub")) | |
1312 cpuID = 1; | |
1313 lua_remove(L, cpunameIndex); | |
1314 } | |
1315 | |
1316 switch (cpuID) | |
1317 { | |
1318 case 0: | |
1319 return hookType; | |
1320 | |
1321 case 1: | |
1322 switch (hookType) | |
1323 { | |
1324 case LUAMEMHOOK_WRITE: | |
1325 return LUAMEMHOOK_WRITE_SUB; | |
1326 case LUAMEMHOOK_READ: | |
1327 return LUAMEMHOOK_READ_SUB; | |
1328 case LUAMEMHOOK_EXEC: | |
1329 return LUAMEMHOOK_EXEC_SUB; | |
1330 } | |
1331 } | |
1332 return hookType; | |
1333 } | |
1334 | |
1335 static int memory_registerwrite(lua_State *L) | |
1336 { | |
1337 return memory_registerHook(L, MatchHookTypeToCPU(L, LUAMEMHOOK_WRITE), 1); | |
1338 } | |
1339 | |
1340 static int memory_registerread(lua_State *L) | |
1341 { | |
1342 return memory_registerHook(L, MatchHookTypeToCPU(L, LUAMEMHOOK_READ), 1); | |
1343 } | |
1344 | |
1345 static int memory_registerexec(lua_State *L) | |
1346 { | |
1347 return memory_registerHook(L, MatchHookTypeToCPU(L, LUAMEMHOOK_EXEC), 1); | |
1348 } | |
1349 | |
1350 //int vba.lagcount | |
1351 // | |
1352 | |
1353 //Returns the lagcounter variable | |
1354 static int vba_getlagcount(lua_State *L) | |
1355 { | |
1356 lua_pushinteger(L, systemCounters.lagCount); | |
1357 return 1; | |
1358 } | |
1359 | |
1360 //int vba.lagged | |
1361 // | |
1362 //Returns true if the current frame is a lag frame | |
1363 static int vba_lagged(lua_State *L) | |
1364 { | |
1365 lua_pushboolean(L, systemCounters.laggedLast); | |
1366 return 1; | |
1367 } | |
1368 | |
1369 // boolean vba.emulating() | |
1370 int vba_emulating(lua_State *L) | |
1371 { | |
1372 lua_pushboolean(L, systemIsEmulating()); | |
1373 return 1; | |
1374 } | |
1375 | |
1376 int movie_isactive(lua_State *L) | |
1377 { | |
1378 lua_pushboolean(L, VBAMovieActive()); | |
1379 return 1; | |
1380 } | |
1381 | |
1382 int movie_isrecording(lua_State *L) | |
1383 { | |
1384 lua_pushboolean(L, VBAMovieRecording()); | |
1385 return 1; | |
1386 } | |
1387 | |
1388 int movie_isplaying(lua_State *L) | |
1389 { | |
1390 lua_pushboolean(L, VBAMoviePlaying()); | |
1391 return 1; | |
1392 } | |
1393 | |
1394 int movie_getlength(lua_State *L) | |
1395 { | |
1396 if (VBAMovieActive()) | |
1397 lua_pushinteger(L, VBAMovieGetLength()); | |
1398 else | |
1399 lua_pushinteger(L, 0); | |
1400 return 1; | |
1401 } | |
1402 | |
1403 static int memory_readbyte(lua_State *L) | |
1404 { | |
1405 u32 addr; | |
1406 u8 val; | |
1407 | |
1408 addr = luaL_checkinteger(L, 1); | |
1409 if (systemIsRunningGBA()) | |
1410 { | |
1411 val = CPUReadByteQuick(addr); | |
1412 } | |
1413 else | |
1414 { | |
1415 val = gbReadMemoryQuick8(addr); | |
1416 } | |
1417 | |
1418 lua_pushinteger(L, val); | |
1419 return 1; | |
1420 } | |
1421 | |
1422 static int memory_readbytesigned(lua_State *L) | |
1423 { | |
1424 u32 addr; | |
1425 s8 val; | |
1426 | |
1427 addr = luaL_checkinteger(L, 1); | |
1428 if (systemIsRunningGBA()) | |
1429 { | |
1430 val = (s8) CPUReadByteQuick(addr); | |
1431 } | |
1432 else | |
1433 { | |
1434 val = (s8) gbReadMemoryQuick8(addr); | |
1435 } | |
1436 | |
1437 lua_pushinteger(L, val); | |
1438 return 1; | |
1439 } | |
1440 | |
1441 static int memory_readword(lua_State *L) | |
1442 { | |
1443 u32 addr; | |
1444 u16 val; | |
1445 | |
1446 addr = luaL_checkinteger(L, 1); | |
1447 if (systemIsRunningGBA()) | |
1448 { | |
1449 val = CPUReadHalfWordQuick(addr); | |
1450 } | |
1451 else | |
1452 { | |
1453 val = gbReadMemoryQuick16(addr & 0x0000FFFF); | |
1454 } | |
1455 | |
1456 lua_pushinteger(L, val); | |
1457 return 1; | |
1458 } | |
1459 | |
1460 static int memory_readwordsigned(lua_State *L) | |
1461 { | |
1462 u32 addr; | |
1463 s16 val; | |
1464 | |
1465 addr = luaL_checkinteger(L, 1); | |
1466 if (systemIsRunningGBA()) | |
1467 { | |
1468 val = (s16) CPUReadHalfWordQuick(addr); | |
1469 } | |
1470 else | |
1471 { | |
1472 val = (s16) gbReadMemoryQuick16(addr); | |
1473 } | |
1474 | |
1475 lua_pushinteger(L, val); | |
1476 return 1; | |
1477 } | |
1478 | |
1479 static int memory_readdword(lua_State *L) | |
1480 { | |
1481 u32 addr; | |
1482 u32 val; | |
1483 | |
1484 addr = luaL_checkinteger(L, 1); | |
1485 if (systemIsRunningGBA()) | |
1486 { | |
1487 val = CPUReadMemoryQuick(addr); | |
1488 } | |
1489 else | |
1490 { | |
1491 val = gbReadMemoryQuick32(addr & 0x0000FFFF); | |
1492 } | |
1493 | |
1494 // lua_pushinteger doesn't work properly for 32bit system, does it? | |
1495 if (val >= 0x80000000 && sizeof(int) <= 4) | |
1496 lua_pushnumber(L, val); | |
1497 else | |
1498 lua_pushinteger(L, val); | |
1499 return 1; | |
1500 } | |
1501 | |
1502 static int memory_readdwordsigned(lua_State *L) | |
1503 { | |
1504 u32 addr; | |
1505 s32 val; | |
1506 | |
1507 addr = luaL_checkinteger(L, 1); | |
1508 if (systemIsRunningGBA()) | |
1509 { | |
1510 val = (s32) CPUReadMemoryQuick(addr); | |
1511 } | |
1512 else | |
1513 { | |
1514 val = (s32) gbReadMemoryQuick32(addr); | |
1515 } | |
1516 | |
1517 lua_pushinteger(L, val); | |
1518 return 1; | |
1519 } | |
1520 | |
1521 static int memory_readbyterange(lua_State *L) | |
1522 { | |
1523 uint32 address = luaL_checkinteger(L, 1); | |
1524 int length = luaL_checkinteger(L, 2); | |
1525 | |
1526 if (length < 0) | |
1527 { | |
1528 address += length; | |
1529 length = -length; | |
1530 } | |
1531 | |
1532 // push the array | |
1533 lua_createtable(L, abs(length), 0); | |
1534 | |
1535 // put all the values into the (1-based) array | |
1536 for (int a = address, n = 1; n <= length; a++, n++) | |
1537 { | |
1538 unsigned char value; | |
1539 | |
1540 if (systemIsRunningGBA()) | |
1541 { | |
1542 value = CPUReadByteQuick(a); | |
1543 } | |
1544 else | |
1545 { | |
1546 value = gbReadMemoryQuick8(a); | |
1547 } | |
1548 | |
1549 lua_pushinteger(L, value); | |
1550 lua_rawseti(L, -2, n); | |
1551 } | |
1552 | |
1553 return 1; | |
1554 } | |
1555 | |
1556 static int memory_writebyte(lua_State *L) | |
1557 { | |
1558 u32 addr; | |
1559 int val; | |
1560 | |
1561 addr = luaL_checkinteger(L, 1); | |
1562 val = luaL_checkinteger(L, 2); | |
1563 if (systemIsRunningGBA()) | |
1564 { | |
1565 CPUWriteByteQuick(addr, val); | |
1566 } | |
1567 else | |
1568 { | |
1569 gbWriteMemoryQuick8(addr, val); | |
1570 } | |
1571 | |
1572 CallRegisteredLuaMemHook(addr, 1, val, LUAMEMHOOK_WRITE); | |
1573 return 0; | |
1574 } | |
1575 | |
1576 static int memory_writeword(lua_State *L) | |
1577 { | |
1578 u32 addr; | |
1579 int val; | |
1580 | |
1581 addr = luaL_checkinteger(L, 1); | |
1582 val = luaL_checkinteger(L, 2); | |
1583 if (systemIsRunningGBA()) | |
1584 { | |
1585 CPUWriteHalfWordQuick(addr, val); | |
1586 } | |
1587 else | |
1588 { | |
1589 gbWriteMemoryQuick16(addr, val); | |
1590 } | |
1591 | |
1592 CallRegisteredLuaMemHook(addr, 2, val, LUAMEMHOOK_WRITE); | |
1593 return 0; | |
1594 } | |
1595 | |
1596 static int memory_writedword(lua_State *L) | |
1597 { | |
1598 u32 addr; | |
1599 int val; | |
1600 | |
1601 addr = luaL_checkinteger(L, 1); | |
1602 val = luaL_checkinteger(L, 2); | |
1603 if (systemIsRunningGBA()) | |
1604 { | |
1605 CPUWriteMemoryQuick(addr, val); | |
1606 } | |
1607 else | |
1608 { | |
1609 gbWriteMemoryQuick32(addr, val); | |
1610 } | |
1611 | |
1612 CallRegisteredLuaMemHook(addr, 4, val, LUAMEMHOOK_WRITE); | |
1613 return 0; | |
1614 } | |
1615 | |
1616 static int memory_gbromreadbyte(lua_State *L) | |
1617 { | |
1618 u32 addr; | |
1619 u8 val; | |
1620 | |
1621 addr = luaL_checkinteger(L, 1); | |
1622 if (systemIsRunningGBA()) | |
1623 { | |
1624 lua_pushnil(L); | |
1625 return 1; | |
1626 } | |
1627 else | |
1628 { | |
1629 val = gbReadROMQuick8(addr); | |
1630 } | |
1631 | |
1632 lua_pushinteger(L, val); | |
1633 return 1; | |
1634 } | |
1635 | |
1636 static int memory_gbromreadbytesigned(lua_State *L) | |
1637 { | |
1638 u32 addr; | |
1639 s8 val; | |
1640 | |
1641 addr = luaL_checkinteger(L, 1); | |
1642 if (systemIsRunningGBA()) | |
1643 { | |
1644 lua_pushnil(L); | |
1645 return 1; | |
1646 } | |
1647 else | |
1648 { | |
1649 val = (s8) gbReadROMQuick8(addr); | |
1650 } | |
1651 | |
1652 lua_pushinteger(L, val); | |
1653 return 1; | |
1654 } | |
1655 | |
1656 static int memory_gbromreadword(lua_State *L) | |
1657 { | |
1658 u32 addr; | |
1659 u16 val; | |
1660 | |
1661 addr = luaL_checkinteger(L, 1); | |
1662 if (systemIsRunningGBA()) | |
1663 { | |
1664 lua_pushnil(L); | |
1665 return 1; | |
1666 } | |
1667 else | |
1668 { | |
1669 val = gbReadROMQuick16(addr); | |
1670 } | |
1671 | |
1672 lua_pushinteger(L, val); | |
1673 return 1; | |
1674 } | |
1675 | |
1676 static int memory_gbromreadwordsigned(lua_State *L) | |
1677 { | |
1678 u32 addr; | |
1679 s16 val; | |
1680 | |
1681 addr = luaL_checkinteger(L, 1); | |
1682 if (systemIsRunningGBA()) | |
1683 { | |
1684 lua_pushnil(L); | |
1685 return 1; | |
1686 } | |
1687 else | |
1688 { | |
1689 val = (s16) gbReadROMQuick16(addr); | |
1690 } | |
1691 | |
1692 lua_pushinteger(L, val); | |
1693 return 1; | |
1694 } | |
1695 | |
1696 static int memory_gbromreaddword(lua_State *L) | |
1697 { | |
1698 u32 addr; | |
1699 u32 val; | |
1700 | |
1701 addr = luaL_checkinteger(L, 1); | |
1702 if (systemIsRunningGBA()) | |
1703 { | |
1704 lua_pushnil(L); | |
1705 return 1; | |
1706 } | |
1707 else | |
1708 { | |
1709 val = gbReadROMQuick32(addr); | |
1710 } | |
1711 | |
1712 // lua_pushinteger doesn't work properly for 32bit system, does it? | |
1713 if (val >= 0x80000000 && sizeof(int) <= 4) | |
1714 lua_pushnumber(L, val); | |
1715 else | |
1716 lua_pushinteger(L, val); | |
1717 return 1; | |
1718 } | |
1719 | |
1720 static int memory_gbromreaddwordsigned(lua_State *L) | |
1721 { | |
1722 u32 addr; | |
1723 s32 val; | |
1724 | |
1725 addr = luaL_checkinteger(L, 1); | |
1726 if (systemIsRunningGBA()) | |
1727 { | |
1728 lua_pushnil(L); | |
1729 return 1; | |
1730 } | |
1731 else | |
1732 { | |
1733 val = (s32) gbReadROMQuick32(addr); | |
1734 } | |
1735 | |
1736 lua_pushinteger(L, val); | |
1737 return 1; | |
1738 } | |
1739 | |
1740 static int memory_gbromreadbyterange(lua_State *L) | |
1741 { | |
1742 uint32 address = luaL_checkinteger(L, 1); | |
1743 int length = luaL_checkinteger(L, 2); | |
1744 | |
1745 if (length < 0) | |
1746 { | |
1747 address += length; | |
1748 length = -length; | |
1749 } | |
1750 | |
1751 // push the array | |
1752 lua_createtable(L, abs(length), 0); | |
1753 | |
1754 // put all the values into the (1-based) array | |
1755 for (int a = address, n = 1; n <= length; a++, n++) | |
1756 { | |
1757 unsigned char value; | |
1758 | |
1759 if (systemIsRunningGBA()) | |
1760 { | |
1761 lua_pushnil(L); | |
1762 return 1; | |
1763 } | |
1764 else | |
1765 { | |
1766 value = gbReadROMQuick8(a); | |
1767 } | |
1768 | |
1769 lua_pushinteger(L, value); | |
1770 lua_rawseti(L, -2, n); | |
1771 } | |
1772 | |
1773 return 1; | |
1774 } | |
1775 | |
1776 // table joypad.get(int which = 1) | |
1777 // | |
1778 // Reads the joypads as inputted by the user. | |
1779 static int joy_get_internal(lua_State *L, bool reportUp, bool reportDown) | |
1780 { | |
1781 // Reads the joypads as inputted by the user | |
1782 int which = luaL_checkinteger(L, 1); | |
1783 | |
1784 if (which < 0 || which > 4) | |
1785 { | |
1786 luaL_error(L, "Invalid input port (valid range 0-4, specified %d)", which); | |
1787 } | |
1788 | |
1789 uint32 buttons = systemGetOriginalJoypad(which - 1, false); | |
1790 | |
1791 lua_newtable(L); | |
1792 | |
1793 int i; | |
1794 for (i = 0; i < 10; i++) | |
1795 { | |
1796 bool pressed = (buttons & (1 << i)) != 0; | |
1797 if ((pressed && reportDown) || (!pressed && reportUp)) | |
1798 { | |
1799 lua_pushboolean(L, pressed); | |
1800 lua_setfield(L, -2, button_mappings[i]); | |
1801 } | |
1802 } | |
1803 | |
1804 return 1; | |
1805 } | |
1806 | |
1807 // joypad.get(which) | |
1808 // returns a table of every game button, | |
1809 // true meaning currently-held and false meaning not-currently-held | |
1810 // (as of last frame boundary) | |
1811 // this WILL read input from a currently-playing movie | |
1812 static int joypad_get(lua_State *L) | |
1813 { | |
1814 return joy_get_internal(L, true, true); | |
1815 } | |
1816 | |
1817 // joypad.getdown(which) | |
1818 // returns a table of every game button that is currently held | |
1819 static int joypad_getdown(lua_State *L) | |
1820 { | |
1821 return joy_get_internal(L, false, true); | |
1822 } | |
1823 | |
1824 // joypad.getup(which) | |
1825 // returns a table of every game button that is not currently held | |
1826 static int joypad_getup(lua_State *L) | |
1827 { | |
1828 return joy_get_internal(L, true, false); | |
1829 } | |
1830 | |
1831 // joypad.set(int which, table buttons) | |
1832 // | |
1833 // Sets the given buttons to be pressed during the next | |
1834 // frame advance. The table should have the right | |
1835 | |
1836 // keys (no pun intended) set. | |
1837 static int joypad_set(lua_State *L) | |
1838 { | |
1839 // Which joypad we're tampering with | |
1840 int which = luaL_checkinteger(L, 1); | |
1841 if (which < 0 || which > 4) | |
1842 { | |
1843 luaL_error(L, "Invalid output port (valid range 0-4, specified %d)", which); | |
1844 } | |
1845 | |
1846 if (which == 0) | |
1847 which = systemGetDefaultJoypad(); | |
1848 | |
1849 // And the table of buttons. | |
1850 luaL_checktype(L, 2, LUA_TTABLE); | |
1851 | |
1852 // Set up for taking control of the indicated controller | |
1853 lua_joypads_used |= 1 << (which - 1); | |
1854 lua_joypads[which - 1] = 0; | |
1855 | |
1856 for (int i = 0; i < 10; i++) | |
1857 { | |
1858 const char *name = button_mappings[i]; | |
1859 lua_getfield(L, 2, name); | |
1860 if (!lua_isnil(L, -1)) | |
1861 { | |
1862 bool pressed = lua_toboolean(L, -1) != 0; | |
1863 if (pressed) | |
1864 lua_joypads[which - 1] |= 1 << i; | |
1865 else | |
1866 lua_joypads[which - 1] &= ~(1 << i); | |
1867 } | |
1868 lua_pop(L, 1); | |
1869 } | |
1870 | |
1871 return 0; | |
1872 } | |
1873 | |
1874 // Helper function to convert a savestate object to the filename it represents. | |
1875 static const char *savestateobj2filename(lua_State *L, int offset) | |
1876 { | |
1877 // First we get the metatable of the indicated object | |
1878 int result = lua_getmetatable(L, offset); | |
1879 | |
1880 if (!result) | |
1881 luaL_error(L, "object not a savestate object"); | |
1882 | |
1883 // Also check that the type entry is set | |
1884 lua_getfield(L, -1, "__metatable"); | |
1885 if (strcmp(lua_tostring(L, -1), "vba Savestate") != 0) | |
1886 luaL_error(L, "object not a savestate object"); | |
1887 lua_pop(L, 1); | |
1888 | |
1889 // Now, get the field we want | |
1890 lua_getfield(L, -1, "filename"); | |
1891 | |
1892 // Return it | |
1893 return lua_tostring(L, -1); | |
1894 } | |
1895 | |
1896 // Helper function for garbage collection. | |
1897 static int savestate_gc(lua_State *L) | |
1898 { | |
1899 // The object we're collecting is on top of the stack | |
1900 lua_getmetatable(L, 1); | |
1901 | |
1902 // Get the filename | |
1903 const char *filename; | |
1904 lua_getfield(L, -1, "filename"); | |
1905 filename = lua_tostring(L, -1); | |
1906 | |
1907 // Delete the file | |
1908 remove(filename); | |
1909 | |
1910 // We exit, and the garbage collector takes care of the rest. | |
1911 // Edit: Visual Studio needs a return value anyway, so returns 0. | |
1912 return 0; | |
1913 } | |
1914 | |
1915 // object savestate.create(int which = nil) | |
1916 // | |
1917 // Creates an object used for savestates. | |
1918 // The object can be associated with a player-accessible savestate | |
1919 | |
1920 // ("which" between 1 and 12) or not (which == nil). | |
1921 static int savestate_create(lua_State *L) | |
1922 { | |
1923 int which = -1; | |
1924 if (lua_gettop(L) >= 1) | |
1925 { | |
1926 which = luaL_checkinteger(L, 1); | |
1927 if (which < 1 || which > 12) | |
1928 { | |
1929 luaL_error(L, "invalid player's savestate %d", which); | |
1930 } | |
1931 } | |
1932 | |
1933 char stateName[2048]; | |
1934 | |
1935 if (which > 0) | |
1936 { | |
1937 // Find an appropriate filename. This is OS specific, unfortunately. | |
1938 #if (defined(WIN32) && !defined(SDL)) | |
1939 CString stateName = winGetSavestateFilename(theApp.gameFilename, which); | |
1940 #else | |
1941 extern char saveDir[2048]; | |
1942 extern char filename[2048]; | |
1943 extern char *sdlGetFilename(char *name); | |
1944 | |
1945 if (saveDir[0]) | |
1946 sprintf(stateName, "%s/%s%d.sgm", saveDir, sdlGetFilename(filename), which); | |
1947 else | |
1948 sprintf(stateName, "%s%d.sgm", filename, which); | |
1949 #endif | |
1950 } | |
1951 else | |
1952 { | |
1953 char *stateNameTemp = tempnam(NULL, "snlua"); | |
1954 strcpy(stateName, stateNameTemp); | |
1955 if (stateNameTemp) | |
1956 free(stateNameTemp); | |
1957 } | |
1958 | |
1959 // Our "object". We don't care about the type, we just need the memory and GC services. | |
1960 lua_newuserdata(L, 1); | |
1961 | |
1962 // The metatable we use, protected from Lua and contains garbage collection info and stuff. | |
1963 lua_newtable(L); | |
1964 | |
1965 // First, we must protect it | |
1966 lua_pushstring(L, "vba Savestate"); | |
1967 lua_setfield(L, -2, "__metatable"); | |
1968 | |
1969 // Now we need to save the file itself. | |
1970 lua_pushstring(L, stateName); | |
1971 lua_setfield(L, -2, "filename"); | |
1972 | |
1973 // If it's an anonymous savestate, we must delete the file from disk should it be gargage collected | |
1974 if (which < 0) | |
1975 { | |
1976 lua_pushcfunction(L, savestate_gc); | |
1977 lua_setfield(L, -2, "__gc"); | |
1978 } | |
1979 | |
1980 // Set the metatable | |
1981 lua_setmetatable(L, -2); | |
1982 | |
1983 // Awesome. Return the object | |
1984 return 1; | |
1985 } | |
1986 | |
1987 // savestate.save(object state) | |
1988 // | |
1989 | |
1990 // Saves a state to the given object. | |
1991 static int savestate_save(lua_State *L) | |
1992 { | |
1993 const char *filename = savestateobj2filename(L, 1); | |
1994 | |
1995 // printf("saving %s\n", filename); | |
1996 // Save states are very expensive. They take time. | |
1997 numTries--; | |
1998 | |
1999 bool8 retvalue = theEmulator.emuWriteState ? theEmulator.emuWriteState(filename) : false; | |
2000 if (!retvalue) | |
2001 { | |
2002 // Uh oh | |
2003 luaL_error(L, "savestate failed"); | |
2004 } | |
2005 | |
2006 return 0; | |
2007 } | |
2008 | |
2009 // savestate.load(object state) | |
2010 // | |
2011 | |
2012 // Loads the given state | |
2013 static int savestate_load(lua_State *L) | |
2014 { | |
2015 const char *filename = savestateobj2filename(L, 1); | |
2016 | |
2017 numTries--; | |
2018 | |
2019 // printf("loading %s\n", filename); | |
2020 bool8 retvalue = theEmulator.emuReadState ? theEmulator.emuReadState(filename) : false; | |
2021 if (!retvalue) | |
2022 { | |
2023 // Uh oh | |
2024 luaL_error(L, "loadstate failed"); | |
2025 } | |
2026 | |
2027 return 0; | |
2028 } | |
2029 | |
2030 // int vba.framecount() | |
2031 // | |
2032 | |
2033 // Gets the frame counter for the movie, or the number of frames since last reset. | |
2034 int vba_framecount(lua_State *L) | |
2035 { | |
2036 if (!VBAMovieActive()) | |
2037 { | |
2038 lua_pushinteger(L, systemCounters.frameCount); | |
2039 } | |
2040 else | |
2041 { | |
2042 lua_pushinteger(L, VBAMovieGetFrameCounter()); | |
2043 } | |
2044 | |
2045 return 1; | |
2046 } | |
2047 | |
2048 //string movie.getauthor | |
2049 // | |
2050 | |
2051 // returns author info field of .vbm file | |
2052 int movie_getauthor(lua_State *L) | |
2053 { | |
2054 if (!VBAMovieActive()) | |
2055 { | |
2056 //lua_pushnil(L); | |
2057 lua_pushstring(L, ""); | |
2058 return 1; | |
2059 } | |
2060 | |
2061 lua_pushstring(L, VBAMovieGetAuthorInfo().c_str()); | |
2062 return 1; | |
2063 } | |
2064 | |
2065 //string movie.filename | |
2066 int movie_getfilename(lua_State *L) | |
2067 { | |
2068 if (!VBAMovieActive()) | |
2069 { | |
2070 //lua_pushnil(L); | |
2071 lua_pushstring(L, ""); | |
2072 return 1; | |
2073 } | |
2074 | |
2075 lua_pushstring(L, VBAMovieGetFilename().c_str()); | |
2076 return 1; | |
2077 } | |
2078 | |
2079 // string movie.mode() | |
2080 // | |
2081 | |
2082 // "record", "playback" or nil | |
2083 int movie_getmode(lua_State *L) | |
2084 { | |
2085 assert(!VBAMovieLoading()); | |
2086 if (!VBAMovieActive()) | |
2087 { | |
2088 lua_pushnil(L); | |
2089 return 1; | |
2090 } | |
2091 | |
2092 if (VBAMovieRecording()) | |
2093 lua_pushstring(L, "record"); | |
2094 else | |
2095 lua_pushstring(L, "playback"); | |
2096 return 1; | |
2097 } | |
2098 | |
2099 static int movie_rerecordcount(lua_State *L) | |
2100 { | |
2101 if (VBAMovieActive()) | |
2102 lua_pushinteger(L, VBAMovieGetRerecordCount()); | |
2103 else | |
2104 lua_pushinteger(L, 0); | |
2105 return 1; | |
2106 } | |
2107 | |
2108 static int movie_setrerecordcount(lua_State *L) | |
2109 { | |
2110 if (VBAMovieActive()) | |
2111 VBAMovieSetRerecordCount(luaL_checkinteger(L, 1)); | |
2112 return 0; | |
2113 } | |
2114 | |
2115 static int movie_rerecordcounting(lua_State *L) | |
2116 { | |
2117 if (lua_gettop(L) == 0) | |
2118 luaL_error(L, "no parameters specified"); | |
2119 | |
2120 skipRerecords = lua_toboolean(L, 1); | |
2121 return 0; | |
2122 } | |
2123 | |
2124 // movie.stop() | |
2125 // | |
2126 | |
2127 // Stops movie playback/recording. Bombs out if movie is not running. | |
2128 static int movie_stop(lua_State *L) | |
2129 { | |
2130 if (!VBAMovieActive()) | |
2131 luaL_error(L, "no movie"); | |
2132 | |
2133 VBAMovieStop(false); | |
2134 return 0; | |
2135 } | |
2136 | |
2137 #define LUA_SCREEN_WIDTH 256 | |
2138 #define LUA_SCREEN_HEIGHT 239 | |
2139 | |
2140 // Common code by the gui library: make sure the screen array is ready | |
2141 static void gui_prepare(void) | |
2142 { | |
2143 if (!gui_data) | |
2144 gui_data = (uint8 *)malloc(LUA_SCREEN_WIDTH * LUA_SCREEN_HEIGHT * 4); | |
2145 if (!gui_used) | |
2146 memset(gui_data, 0, LUA_SCREEN_WIDTH * LUA_SCREEN_HEIGHT * 4); | |
2147 gui_used = true; | |
2148 } | |
2149 | |
2150 // pixform for lua graphics | |
2151 #define BUILD_PIXEL_ARGB8888(A, R, G, B) (((int)(A) << 24) | ((int)(R) << 16) | ((int)(G) << 8) | (int)(B)) | |
2152 #define DECOMPOSE_PIXEL_ARGB8888(PIX, A, R, G, B) \ | |
2153 { \ | |
2154 (A) = ((PIX) >> 24) & 0xff; \ | |
2155 (R) = ((PIX) >> 16) & 0xff; \ | |
2156 (G) = ((PIX) >> 8) & 0xff; \ | |
2157 (B) = (PIX) & 0xff; \ | |
2158 } | |
2159 #define LUA_BUILD_PIXEL BUILD_PIXEL_ARGB8888 | |
2160 #define LUA_DECOMPOSE_PIXEL DECOMPOSE_PIXEL_ARGB8888 | |
2161 #define LUA_PIXEL_A(PIX) (((PIX) >> 24) & 0xff) | |
2162 #define LUA_PIXEL_R(PIX) (((PIX) >> 16) & 0xff) | |
2163 #define LUA_PIXEL_G(PIX) (((PIX) >> 8) & 0xff) | |
2164 #define LUA_PIXEL_B(PIX) ((PIX) & 0xff) | |
2165 | |
2166 template<class T> | |
2167 static void swap(T &one, T &two) | |
2168 { | |
2169 T temp = one; | |
2170 one = two; | |
2171 two = temp; | |
2172 } | |
2173 | |
2174 // write a pixel to buffer | |
2175 static inline void blend32(uint32 *dstPixel, uint32 colour) | |
2176 { | |
2177 uint8 *dst = (uint8 *)dstPixel; | |
2178 int a, r, g, b; | |
2179 LUA_DECOMPOSE_PIXEL(colour, a, r, g, b); | |
2180 | |
2181 if (a == 255 || dst[3] == 0) | |
2182 { | |
2183 // direct copy | |
2184 *(uint32 *) (dst) = colour; | |
2185 } | |
2186 else if (a == 0) | |
2187 { | |
2188 // do not copy | |
2189 } | |
2190 else | |
2191 { | |
2192 // alpha-blending | |
2193 int a_dst = ((255 - a) * dst[3] + 128) / 255; | |
2194 int a_new = a + a_dst; | |
2195 | |
2196 dst[0] = (uint8) (((dst[0] * a_dst + b * a) + (a_new / 2)) / a_new); | |
2197 dst[1] = (uint8) (((dst[1] * a_dst + g * a) + (a_new / 2)) / a_new); | |
2198 dst[2] = (uint8) (((dst[2] * a_dst + r * a) + (a_new / 2)) / a_new); | |
2199 dst[3] = (uint8) a_new; | |
2200 } | |
2201 } | |
2202 | |
2203 // check if a pixel is in the lua canvas | |
2204 static inline bool gui_check_boundary(int x, int y) | |
2205 { | |
2206 return !(x < 0 || x >= LUA_SCREEN_WIDTH || y < 0 || y >= LUA_SCREEN_HEIGHT); | |
2207 } | |
2208 | |
2209 // check if any part of a box is in the lua canvas | |
2210 static inline bool gui_checkbox(int x1, int y1, int x2, int y2) | |
2211 { | |
2212 if ((x1 < 0 && x2 < 0) | |
2213 || (x1 >= LUA_SCREEN_WIDTH && x2 >= LUA_SCREEN_WIDTH) | |
2214 || (y1 < 0 && y2 < 0) | |
2215 || (y1 >= LUA_SCREEN_HEIGHT && y2 >= LUA_SCREEN_HEIGHT)) | |
2216 return false; | |
2217 return true; | |
2218 } | |
2219 | |
2220 // write a pixel to gui_data (do not check boundaries for speedup) | |
2221 static inline void gui_drawpixel_fast(int x, int y, uint32 colour) | |
2222 { | |
2223 //gui_prepare(); | |
2224 blend32((uint32 *) &gui_data[(y * LUA_SCREEN_WIDTH + x) * 4], colour); | |
2225 } | |
2226 | |
2227 // write a pixel to gui_data (check boundaries) | |
2228 static inline void gui_drawpixel_internal(int x, int y, uint32 colour) | |
2229 { | |
2230 //gui_prepare(); | |
2231 if (gui_check_boundary(x, y)) | |
2232 gui_drawpixel_fast(x, y, colour); | |
2233 } | |
2234 | |
2235 // draw a line on gui_data (checks boundaries) | |
2236 static void gui_drawline_internal(int x1, int y1, int x2, int y2, bool lastPixel, uint32 colour) | |
2237 { | |
2238 //gui_prepare(); | |
2239 // Note: New version of Bresenham's Line Algorithm | |
2240 // | |
2241 // | |
2242 // http://groups.google.co.jp/group/rec.games.roguelike.development/browse_thread/thread/345f4c42c3b25858/29e07a3af3a450e6?show_docid=29e07a3af3a450e6 | |
2243 int swappedx = 0; | |
2244 int swappedy = 0; | |
2245 | |
2246 int xtemp = x1 - x2; | |
2247 int ytemp = y1 - y2; | |
2248 if (xtemp == 0 && ytemp == 0) | |
2249 { | |
2250 gui_drawpixel_internal(x1, y1, colour); | |
2251 return; | |
2252 } | |
2253 | |
2254 if (xtemp < 0) | |
2255 { | |
2256 xtemp = -xtemp; | |
2257 swappedx = 1; | |
2258 } | |
2259 | |
2260 if (ytemp < 0) | |
2261 { | |
2262 ytemp = -ytemp; | |
2263 swappedy = 1; | |
2264 } | |
2265 | |
2266 int delta_x = xtemp << 1; | |
2267 int delta_y = ytemp << 1; | |
2268 | |
2269 signed char ix = x1 > x2 ? 1 : -1; | |
2270 signed char iy = y1 > y2 ? 1 : -1; | |
2271 | |
2272 if (lastPixel) | |
2273 gui_drawpixel_internal(x2, y2, colour); | |
2274 | |
2275 if (delta_x >= delta_y) | |
2276 { | |
2277 int error = delta_y - (delta_x >> 1); | |
2278 | |
2279 while (x2 != x1) | |
2280 { | |
2281 if (error == 0 && !swappedx) | |
2282 gui_drawpixel_internal(x2 + ix, y2, colour); | |
2283 if (error >= 0) | |
2284 { | |
2285 if (error || (ix > 0)) | |
2286 { | |
2287 y2 += iy; | |
2288 error -= delta_x; | |
2289 } | |
2290 } | |
2291 | |
2292 x2 += ix; | |
2293 gui_drawpixel_internal(x2, y2, colour); | |
2294 if (error == 0 && swappedx) | |
2295 gui_drawpixel_internal(x2, y2 + iy, colour); | |
2296 error += delta_y; | |
2297 } | |
2298 } | |
2299 else | |
2300 { | |
2301 int error = delta_x - (delta_y >> 1); | |
2302 | |
2303 while (y2 != y1) | |
2304 { | |
2305 if (error == 0 && !swappedy) | |
2306 gui_drawpixel_internal(x2, y2 + iy, colour); | |
2307 if (error >= 0) | |
2308 { | |
2309 if (error || (iy > 0)) | |
2310 { | |
2311 x2 += ix; | |
2312 error -= delta_y; | |
2313 } | |
2314 } | |
2315 | |
2316 y2 += iy; | |
2317 gui_drawpixel_internal(x2, y2, colour); | |
2318 if (error == 0 && swappedy) | |
2319 gui_drawpixel_internal(x2 + ix, y2, colour); | |
2320 error += delta_x; | |
2321 } | |
2322 } | |
2323 } | |
2324 | |
2325 // draw a rect on gui_data | |
2326 static void gui_drawbox_internal(int x1, int y1, int x2, int y2, uint32 colour) | |
2327 { | |
2328 if (x1 > x2) | |
2329 std::swap(x1, x2); | |
2330 if (y1 > y2) | |
2331 std::swap(y1, y2); | |
2332 if (x1 < 0) | |
2333 x1 = -1; | |
2334 if (y1 < 0) | |
2335 y1 = -1; | |
2336 if (x2 >= LUA_SCREEN_WIDTH) | |
2337 x2 = LUA_SCREEN_WIDTH; | |
2338 if (y2 >= LUA_SCREEN_HEIGHT) | |
2339 y2 = LUA_SCREEN_HEIGHT; | |
2340 | |
2341 if (!gui_checkbox(x1, y1, x2, y2)) | |
2342 return; | |
2343 | |
2344 //gui_prepare(); | |
2345 gui_drawline_internal(x1, y1, x2, y1, true, colour); | |
2346 gui_drawline_internal(x1, y2, x2, y2, true, colour); | |
2347 gui_drawline_internal(x1, y1, x1, y2, true, colour); | |
2348 gui_drawline_internal(x2, y1, x2, y2, true, colour); | |
2349 } | |
2350 | |
2351 // draw a circle on gui_data | |
2352 static void gui_drawcircle_internal(int x0, int y0, int radius, uint32 colour) | |
2353 { | |
2354 //gui_prepare(); | |
2355 if (radius < 0) | |
2356 radius = -radius; | |
2357 if (radius == 0) | |
2358 return; | |
2359 if (radius == 1) | |
2360 { | |
2361 gui_drawpixel_internal(x0, y0, colour); | |
2362 return; | |
2363 } | |
2364 | |
2365 // http://en.wikipedia.org/wiki/Midpoint_circle_algorithm | |
2366 int f = 1 - radius; | |
2367 int ddF_x = 1; | |
2368 int ddF_y = -2 * radius; | |
2369 int x = 0; | |
2370 int y = radius; | |
2371 | |
2372 if (!gui_checkbox(x0 - radius, y0 - radius, x0 + radius, y0 + radius)) | |
2373 return; | |
2374 | |
2375 gui_drawpixel_internal(x0, y0 + radius, colour); | |
2376 gui_drawpixel_internal(x0, y0 - radius, colour); | |
2377 gui_drawpixel_internal(x0 + radius, y0, colour); | |
2378 gui_drawpixel_internal(x0 - radius, y0, colour); | |
2379 | |
2380 // same pixel shouldn't be drawed twice, | |
2381 // because each pixel has opacity. | |
2382 // so now the routine gets ugly. | |
2383 while (true) | |
2384 { | |
2385 assert(ddF_x == 2 * x + 1); | |
2386 assert(ddF_y == -2 * y); | |
2387 assert(f == x * x + y * y - radius * radius + 2 * x - y + 1); | |
2388 if (f >= 0) | |
2389 { | |
2390 y--; | |
2391 ddF_y += 2; | |
2392 f += ddF_y; | |
2393 } | |
2394 | |
2395 x++; | |
2396 ddF_x += 2; | |
2397 f += ddF_x; | |
2398 if (x < y) | |
2399 { | |
2400 gui_drawpixel_internal(x0 + x, y0 + y, colour); | |
2401 gui_drawpixel_internal(x0 - x, y0 + y, colour); | |
2402 gui_drawpixel_internal(x0 + x, y0 - y, colour); | |
2403 gui_drawpixel_internal(x0 - x, y0 - y, colour); | |
2404 gui_drawpixel_internal(x0 + y, y0 + x, colour); | |
2405 gui_drawpixel_internal(x0 - y, y0 + x, colour); | |
2406 gui_drawpixel_internal(x0 + y, y0 - x, colour); | |
2407 gui_drawpixel_internal(x0 - y, y0 - x, colour); | |
2408 } | |
2409 else if (x == y) | |
2410 { | |
2411 gui_drawpixel_internal(x0 + x, y0 + y, colour); | |
2412 gui_drawpixel_internal(x0 - x, y0 + y, colour); | |
2413 gui_drawpixel_internal(x0 + x, y0 - y, colour); | |
2414 gui_drawpixel_internal(x0 - x, y0 - y, colour); | |
2415 break; | |
2416 } | |
2417 else | |
2418 break; | |
2419 } | |
2420 } | |
2421 | |
2422 // draw fill rect on gui_data | |
2423 static void gui_fillbox_internal(int x1, int y1, int x2, int y2, uint32 colour) | |
2424 { | |
2425 if (x1 > x2) | |
2426 std::swap(x1, x2); | |
2427 if (y1 > y2) | |
2428 std::swap(y1, y2); | |
2429 if (x1 < 0) | |
2430 x1 = 0; | |
2431 if (y1 < 0) | |
2432 y1 = 0; | |
2433 if (x2 >= LUA_SCREEN_WIDTH) | |
2434 x2 = LUA_SCREEN_WIDTH - 1; | |
2435 if (y2 >= LUA_SCREEN_HEIGHT) | |
2436 y2 = LUA_SCREEN_HEIGHT - 1; | |
2437 | |
2438 //gui_prepare(); | |
2439 int ix, iy; | |
2440 for (iy = y1; iy <= y2; iy++) | |
2441 { | |
2442 for (ix = x1; ix <= x2; ix++) | |
2443 { | |
2444 gui_drawpixel_fast(ix, iy, colour); | |
2445 } | |
2446 } | |
2447 } | |
2448 | |
2449 // fill a circle on gui_data | |
2450 static void gui_fillcircle_internal(int x0, int y0, int radius, uint32 colour) | |
2451 { | |
2452 //gui_prepare(); | |
2453 if (radius < 0) | |
2454 radius = -radius; | |
2455 if (radius == 0) | |
2456 return; | |
2457 if (radius == 1) | |
2458 { | |
2459 gui_drawpixel_internal(x0, y0, colour); | |
2460 return; | |
2461 } | |
2462 | |
2463 // http://en.wikipedia.org/wiki/Midpoint_circle_algorithm | |
2464 int f = 1 - radius; | |
2465 int ddF_x = 1; | |
2466 int ddF_y = -2 * radius; | |
2467 int x = 0; | |
2468 int y = radius; | |
2469 | |
2470 if (!gui_checkbox(x0 - radius, y0 - radius, x0 + radius, y0 + radius)) | |
2471 return; | |
2472 | |
2473 gui_drawline_internal(x0, y0 - radius, x0, y0 + radius, true, colour); | |
2474 | |
2475 while (true) | |
2476 { | |
2477 assert(ddF_x == 2 * x + 1); | |
2478 assert(ddF_y == -2 * y); | |
2479 assert(f == x * x + y * y - radius * radius + 2 * x - y + 1); | |
2480 if (f >= 0) | |
2481 { | |
2482 y--; | |
2483 ddF_y += 2; | |
2484 f += ddF_y; | |
2485 } | |
2486 | |
2487 x++; | |
2488 ddF_x += 2; | |
2489 f += ddF_x; | |
2490 | |
2491 if (x < y) | |
2492 { | |
2493 gui_drawline_internal(x0 + x, y0 - y, x0 + x, y0 + y, true, colour); | |
2494 gui_drawline_internal(x0 - x, y0 - y, x0 - x, y0 + y, true, colour); | |
2495 if (f >= 0) | |
2496 { | |
2497 gui_drawline_internal(x0 + y, y0 - x, x0 + y, y0 + x, true, colour); | |
2498 gui_drawline_internal(x0 - y, y0 - x, x0 - y, y0 + x, true, colour); | |
2499 } | |
2500 } | |
2501 else if (x == y) | |
2502 { | |
2503 gui_drawline_internal(x0 + x, y0 - y, x0 + x, y0 + y, true, colour); | |
2504 gui_drawline_internal(x0 - x, y0 - y, x0 - x, y0 + y, true, colour); | |
2505 break; | |
2506 } | |
2507 else | |
2508 break; | |
2509 } | |
2510 } | |
2511 | |
2512 // Helper for a simple hex parser | |
2513 static int hex2int(lua_State *L, char c) | |
2514 { | |
2515 if (c >= '0' && c <= '9') | |
2516 return c - '0'; | |
2517 if (c >= 'a' && c <= 'f') | |
2518 return c - 'a' + 10; | |
2519 if (c >= 'A' && c <= 'F') | |
2520 return c - 'A' + 10; | |
2521 return luaL_error(L, "invalid hex in colour"); | |
2522 } | |
2523 | |
2524 static const struct ColorMapping | |
2525 { | |
2526 const char *name; | |
2527 int value; | |
2528 } | |
2529 s_colorMapping[] = | |
2530 { | |
2531 { "white", 0xFFFFFFFF }, | |
2532 { "black", 0x000000FF }, | |
2533 { "clear", 0x00000000 }, | |
2534 { "gray", 0x7F7F7FFF }, | |
2535 { "grey", 0x7F7F7FFF }, | |
2536 { "red", 0xFF0000FF }, | |
2537 { "orange", 0xFF7F00FF }, | |
2538 { "yellow", 0xFFFF00FF }, | |
2539 { "chartreuse", 0x7FFF00FF }, | |
2540 { "green", 0x00FF00FF }, | |
2541 { "teal", 0x00FF7FFF }, | |
2542 { "cyan", 0x00FFFFFF }, | |
2543 { "blue", 0x0000FFFF }, | |
2544 { "purple", 0x7F00FFFF }, | |
2545 { "magenta", 0xFF00FFFF }, | |
2546 }; | |
2547 | |
2548 /** | |
2549 * Converts an integer or a string on the stack at the given | |
2550 * offset to a RGB32 colour. Several encodings are supported. | |
2551 * The user may construct their own RGB value, given a simple colour name, | |
2552 * or an HTML-style "#09abcd" colour. 16 bit reduction doesn't occur at this time. | |
2553 */ | |
2554 static inline bool str2colour(uint32 *colour, lua_State *L, const char *str) | |
2555 { | |
2556 if (str[0] == '#') | |
2557 { | |
2558 int color; | |
2559 sscanf(str + 1, "%X", &color); | |
2560 | |
2561 int len = strlen(str + 1); | |
2562 int missing = max(0, 8 - len); | |
2563 color <<= missing << 2; | |
2564 if (missing >= 2) | |
2565 color |= 0xFF; | |
2566 *colour = color; | |
2567 return true; | |
2568 } | |
2569 else | |
2570 { | |
2571 if (!strnicmp(str, "rand", 4)) | |
2572 { | |
2573 *colour = gen_rand32() | 0xFF; //((rand()*255/RAND_MAX) << 8) | ((rand()*255/RAND_MAX) << 16) | | |
2574 // ((rand()*255/RAND_MAX) << 24) | 0xFF; | |
2575 return true; | |
2576 } | |
2577 | |
2578 for (int i = 0; i < sizeof(s_colorMapping) / sizeof(*s_colorMapping); i++) | |
2579 { | |
2580 if (!stricmp(str, s_colorMapping[i].name)) | |
2581 { | |
2582 *colour = s_colorMapping[i].value; | |
2583 return true; | |
2584 } | |
2585 } | |
2586 } | |
2587 | |
2588 return false; | |
2589 } | |
2590 | |
2591 static inline uint32 gui_getcolour_wrapped(lua_State *L, int offset, bool hasDefaultValue, uint32 defaultColour) | |
2592 { | |
2593 switch (lua_type(L, offset)) | |
2594 { | |
2595 case LUA_TSTRING: | |
2596 { | |
2597 const char *str = lua_tostring(L, offset); | |
2598 uint32 colour; | |
2599 | |
2600 if (str2colour(&colour, L, str)) | |
2601 return colour; | |
2602 else | |
2603 { | |
2604 if (hasDefaultValue) | |
2605 return defaultColour; | |
2606 else | |
2607 return luaL_error(L, "unknown colour %s", str); | |
2608 } | |
2609 } | |
2610 | |
2611 case LUA_TNUMBER: | |
2612 { | |
2613 uint32 colour = (uint32) lua_tointeger(L, offset); | |
2614 return colour; | |
2615 } | |
2616 | |
2617 case LUA_TTABLE: | |
2618 { | |
2619 int color = 0xFF; | |
2620 lua_pushnil(L); // first key | |
2621 int keyIndex = lua_gettop(L); | |
2622 int valueIndex = keyIndex + 1; | |
2623 bool first = true; | |
2624 while (lua_next(L, offset)) | |
2625 { | |
2626 bool keyIsString = (lua_type(L, keyIndex) == LUA_TSTRING); | |
2627 bool keyIsNumber = (lua_type(L, keyIndex) == LUA_TNUMBER); | |
2628 int key = keyIsString ? tolower(*lua_tostring(L, keyIndex)) : (keyIsNumber ? lua_tointeger(L, keyIndex) : 0); | |
2629 int value = lua_tointeger(L, valueIndex); | |
2630 if (value < 0) value = 0; | |
2631 if (value > 255) value = 255; | |
2632 switch (key) | |
2633 { | |
2634 case 1: | |
2635 case 'r': | |
2636 color |= value << 24; break; | |
2637 case 2: | |
2638 case 'g': | |
2639 color |= value << 16; break; | |
2640 case 3: | |
2641 case 'b': | |
2642 color |= value << 8; break; | |
2643 case 4: | |
2644 case 'a': | |
2645 color = (color & ~0xFF) | value; break; | |
2646 } | |
2647 lua_pop(L, 1); | |
2648 } | |
2649 return color; | |
2650 } break; | |
2651 | |
2652 case LUA_TFUNCTION: | |
2653 luaL_error(L, "invalid colour"); // NYI | |
2654 return 0; | |
2655 | |
2656 default: | |
2657 if (hasDefaultValue) | |
2658 return defaultColour; | |
2659 else | |
2660 return luaL_error(L, "invalid colour"); | |
2661 } | |
2662 } | |
2663 | |
2664 static uint32 gui_getcolour(lua_State *L, int offset) | |
2665 { | |
2666 uint32 colour; | |
2667 int a, r, g, b; | |
2668 | |
2669 colour = gui_getcolour_wrapped(L, offset, false, 0); | |
2670 a = ((colour & 0xff) * transparencyModifier) / 255; | |
2671 if (a > 255) | |
2672 a = 255; | |
2673 b = (colour >> 8) & 0xff; | |
2674 g = (colour >> 16) & 0xff; | |
2675 r = (colour >> 24) & 0xff; | |
2676 return LUA_BUILD_PIXEL(a, r, g, b); | |
2677 } | |
2678 | |
2679 static uint32 gui_optcolour(lua_State *L, int offset, uint32 defaultColour) | |
2680 { | |
2681 uint32 colour; | |
2682 int a, r, g, b; | |
2683 uint8 defA, defB, defG, defR; | |
2684 | |
2685 LUA_DECOMPOSE_PIXEL(defaultColour, defA, defR, defG, defB); | |
2686 defaultColour = (defR << 24) | (defG << 16) | (defB << 8) | defA; | |
2687 | |
2688 colour = gui_getcolour_wrapped(L, offset, true, defaultColour); | |
2689 a = ((colour & 0xff) * transparencyModifier) / 255; | |
2690 if (a > 255) | |
2691 a = 255; | |
2692 b = (colour >> 8) & 0xff; | |
2693 g = (colour >> 16) & 0xff; | |
2694 r = (colour >> 24) & 0xff; | |
2695 return LUA_BUILD_PIXEL(a, r, g, b); | |
2696 } | |
2697 | |
2698 // gui.drawpixel(x,y,colour) | |
2699 static int gui_drawpixel(lua_State *L) | |
2700 { | |
2701 int x = luaL_checkinteger(L, 1); | |
2702 int y = luaL_checkinteger(L, 2); | |
2703 | |
2704 uint32 colour = gui_getcolour(L, 3); | |
2705 | |
2706 // if (!gui_check_boundary(x, y)) | |
2707 // luaL_error(L,"bad coordinates"); | |
2708 gui_prepare(); | |
2709 | |
2710 gui_drawpixel_internal(x, y, colour); | |
2711 | |
2712 return 0; | |
2713 } | |
2714 | |
2715 // gui.drawline(x1,y1,x2,y2,color,skipFirst) | |
2716 static int gui_drawline(lua_State *L) | |
2717 { | |
2718 int x1, y1, x2, y2; | |
2719 uint32 color; | |
2720 x1 = luaL_checkinteger(L, 1); | |
2721 y1 = luaL_checkinteger(L, 2); | |
2722 x2 = luaL_checkinteger(L, 3); | |
2723 y2 = luaL_checkinteger(L, 4); | |
2724 color = gui_optcolour(L, 5, LUA_BUILD_PIXEL(255, 255, 255, 255)); | |
2725 int skipFirst = lua_toboolean(L, 6); | |
2726 | |
2727 gui_prepare(); | |
2728 | |
2729 gui_drawline_internal(x2, y2, x1, y1, !skipFirst, color); | |
2730 | |
2731 return 0; | |
2732 } | |
2733 | |
2734 // gui.drawbox(x1, y1, x2, y2, fillcolor, outlinecolor) | |
2735 static int gui_drawbox(lua_State *L) | |
2736 { | |
2737 int x1, y1, x2, y2; | |
2738 uint32 fillcolor; | |
2739 uint32 outlinecolor; | |
2740 | |
2741 x1 = luaL_checkinteger(L, 1); | |
2742 y1 = luaL_checkinteger(L, 2); | |
2743 x2 = luaL_checkinteger(L, 3); | |
2744 y2 = luaL_checkinteger(L, 4); | |
2745 fillcolor = gui_optcolour(L, 5, LUA_BUILD_PIXEL(63, 255, 255, 255)); | |
2746 outlinecolor = gui_optcolour(L, 6, LUA_BUILD_PIXEL(255, LUA_PIXEL_R(fillcolor), LUA_PIXEL_G(fillcolor), LUA_PIXEL_B(fillcolor))); | |
2747 | |
2748 if (x1 > x2) | |
2749 std::swap(x1, x2); | |
2750 if (y1 > y2) | |
2751 std::swap(y1, y2); | |
2752 | |
2753 gui_prepare(); | |
2754 | |
2755 gui_drawbox_internal(x1, y1, x2, y2, outlinecolor); | |
2756 if ((x2 - x1) >= 2 && (y2 - y1) >= 2) | |
2757 gui_fillbox_internal(x1 + 1, y1 + 1, x2 - 1, y2 - 1, fillcolor); | |
2758 | |
2759 return 0; | |
2760 } | |
2761 | |
2762 // gui.drawcircle(x0, y0, radius, colour) | |
2763 static int gui_drawcircle(lua_State *L) | |
2764 { | |
2765 int x, y, r; | |
2766 uint32 colour; | |
2767 | |
2768 x = luaL_checkinteger(L, 1); | |
2769 y = luaL_checkinteger(L, 2); | |
2770 r = luaL_checkinteger(L, 3); | |
2771 colour = gui_getcolour(L, 4); | |
2772 | |
2773 gui_prepare(); | |
2774 | |
2775 gui_drawcircle_internal(x, y, r, colour); | |
2776 | |
2777 return 0; | |
2778 } | |
2779 | |
2780 // gui.fillbox(x1, y1, x2, y2, colour) | |
2781 static int gui_fillbox(lua_State *L) | |
2782 { | |
2783 int x1, y1, x2, y2; | |
2784 uint32 colour; | |
2785 | |
2786 x1 = luaL_checkinteger(L, 1); | |
2787 y1 = luaL_checkinteger(L, 2); | |
2788 x2 = luaL_checkinteger(L, 3); | |
2789 y2 = luaL_checkinteger(L, 4); | |
2790 colour = gui_getcolour(L, 5); | |
2791 | |
2792 // if (!gui_check_boundary(x1, y1)) | |
2793 // luaL_error(L,"bad coordinates"); | |
2794 // | |
2795 // if (!gui_check_boundary(x2, y2)) | |
2796 // luaL_error(L,"bad coordinates"); | |
2797 gui_prepare(); | |
2798 | |
2799 if (!gui_checkbox(x1, y1, x2, y2)) | |
2800 return 0; | |
2801 | |
2802 gui_fillbox_internal(x1, y1, x2, y2, colour); | |
2803 | |
2804 return 0; | |
2805 } | |
2806 | |
2807 // gui.fillcircle(x0, y0, radius, colour) | |
2808 static int gui_fillcircle(lua_State *L) | |
2809 { | |
2810 int x, y, r; | |
2811 uint32 colour; | |
2812 | |
2813 x = luaL_checkinteger(L, 1); | |
2814 y = luaL_checkinteger(L, 2); | |
2815 r = luaL_checkinteger(L, 3); | |
2816 colour = gui_getcolour(L, 4); | |
2817 | |
2818 gui_prepare(); | |
2819 | |
2820 gui_fillcircle_internal(x, y, r, colour); | |
2821 | |
2822 return 0; | |
2823 } | |
2824 | |
2825 static int gui_getpixel(lua_State *L) | |
2826 { | |
2827 int x = luaL_checkinteger(L, 1); | |
2828 int y = luaL_checkinteger(L, 2); | |
2829 | |
2830 int pixWidth = 240, pixHeight = 160; | |
2831 int scrWidth = 240, scrHeight = 160; | |
2832 int scrOffsetX = 0, scrOffsetY = 0; | |
2833 int pitch; | |
2834 if (!systemIsRunningGBA()) | |
2835 { | |
2836 if (gbBorderOn) | |
2837 { | |
2838 pixWidth = 256, pixHeight = 224; | |
2839 scrOffsetX = 48, scrOffsetY = 40; | |
2840 } | |
2841 else | |
2842 { | |
2843 pixWidth = 160, pixHeight = 144; | |
2844 } | |
2845 scrWidth = 160, scrHeight = 144; | |
2846 } | |
2847 pitch = pixWidth * (systemColorDepth / 8) + (systemColorDepth == 24 ? 0 : 4); | |
2848 scrOffsetY++; // don't know why it's needed | |
2849 | |
2850 if (!(x >= 0 && y >= 0 && x < scrWidth && y < scrHeight) /*!gui_check_boundary(x,y)*/) | |
2851 { | |
2852 lua_pushinteger(L, 0); | |
2853 lua_pushinteger(L, 0); | |
2854 lua_pushinteger(L, 0); | |
2855 } | |
2856 else | |
2857 { | |
2858 switch (systemColorDepth) | |
2859 { | |
2860 case 16: | |
2861 { | |
2862 uint16 *screen = (uint16 *) (&pix[scrOffsetY * pitch + scrOffsetX * 2]); | |
2863 uint16 pixColor = screen[y * pitch / 2 + x]; | |
2864 lua_pushinteger(L, (pixColor >> 8) & 0xF8); // red | |
2865 lua_pushinteger(L, (pixColor >> 3) & 0xFC); // green | |
2866 lua_pushinteger(L, (pixColor << 3) & 0xF8); // blue | |
2867 } | |
2868 break; | |
2869 case 24: | |
2870 { | |
2871 uint8 *screen = &pix[scrOffsetY * pitch + scrOffsetX * 3]; | |
2872 lua_pushinteger(L, screen[y * pitch + x * 3 + 2]); // red | |
2873 lua_pushinteger(L, screen[y * pitch + x * 3 + 1]); // green | |
2874 lua_pushinteger(L, screen[y * pitch + x * 3 + 0]); // blue | |
2875 } | |
2876 break; | |
2877 case 32: | |
2878 { | |
2879 uint8 *screen = &pix[scrOffsetY * pitch + scrOffsetX * 4]; | |
2880 lua_pushinteger(L, screen[y * pitch + x * 4 + 2]); // red | |
2881 lua_pushinteger(L, screen[y * pitch + x * 4 + 1]); // green | |
2882 lua_pushinteger(L, screen[y * pitch + x * 4 + 0]); // blue | |
2883 } | |
2884 break; | |
2885 default: | |
2886 lua_pushinteger(L, 0); | |
2887 lua_pushinteger(L, 0); | |
2888 lua_pushinteger(L, 0); | |
2889 break; | |
2890 } | |
2891 } | |
2892 return 3; | |
2893 } | |
2894 | |
2895 static int gui_parsecolor(lua_State *L) | |
2896 { | |
2897 int r, g, b, a; | |
2898 uint32 color = gui_getcolour(L, 1); | |
2899 LUA_DECOMPOSE_PIXEL(color, a, r, g, b); | |
2900 lua_pushinteger(L, r); | |
2901 lua_pushinteger(L, g); | |
2902 lua_pushinteger(L, b); | |
2903 lua_pushinteger(L, a); | |
2904 return 4; | |
2905 } | |
2906 | |
2907 // gui.gdscreenshot() | |
2908 // | |
2909 // Returns a screen shot as a string in gd's v1 file format. | |
2910 // This allows us to make screen shots available without gd installed locally. | |
2911 // Users can also just grab pixels via substring selection. | |
2912 // | |
2913 // I think... Does lua support grabbing byte values from a string? // yes, string.byte(str,offset) | |
2914 // Well, either way, just install gd and do what you like with it. | |
2915 // It really is easier that way. | |
2916 | |
2917 // example: gd.createFromGdStr(gui.gdscreenshot()):png("outputimage.png") | |
2918 static int gui_gdscreenshot(lua_State *L) | |
2919 { | |
2920 int xofs = 0, yofs = 0, ppl = 240, width = 240, height = 160; | |
2921 if (!systemIsRunningGBA()) | |
2922 { | |
2923 if (gbBorderOn) | |
2924 xofs = 48, yofs = 40, ppl = 256; | |
2925 else | |
2926 ppl = 160; | |
2927 width = 160, height = 144; | |
2928 } | |
2929 | |
2930 yofs++; | |
2931 | |
2932 //int pitch = (((ppl * systemColorDepth + 7)>>3)+3)&~3; | |
2933 int pitch = ppl * (systemColorDepth / 8) + (systemColorDepth == 24 ? 0 : 4); | |
2934 uint8 *screen = &pix[yofs * pitch + xofs * (systemColorDepth / 8)]; | |
2935 | |
2936 int size = 11 + width * height * 4; | |
2937 char *str = new char[size + 1]; | |
2938 str[size] = 0; | |
2939 | |
2940 unsigned char *ptr = (unsigned char *)str; | |
2941 | |
2942 // GD format header for truecolor image (11 bytes) | |
2943 *ptr++ = (65534 >> 8) & 0xFF; | |
2944 *ptr++ = (65534) & 0xFF; | |
2945 *ptr++ = (width >> 8) & 0xFF; | |
2946 *ptr++ = (width) & 0xFF; | |
2947 *ptr++ = (height >> 8) & 0xFF; | |
2948 *ptr++ = (height) & 0xFF; | |
2949 *ptr++ = 1; | |
2950 *ptr++ = 255; | |
2951 *ptr++ = 255; | |
2952 *ptr++ = 255; | |
2953 *ptr++ = 255; | |
2954 | |
2955 GetColorFunc getColor; | |
2956 getColorIOFunc(systemColorDepth, &getColor, NULL); | |
2957 | |
2958 int x, y; | |
2959 for (y = 0; y < height; y++) | |
2960 { | |
2961 uint8 *s = &screen[y * pitch]; | |
2962 for (x = 0; x < width; x++, s += systemColorDepth / 8) | |
2963 { | |
2964 uint8 r, g, b; | |
2965 getColor(s, &r, &g, &b); | |
2966 | |
2967 *ptr++ = 0; | |
2968 *ptr++ = r; | |
2969 *ptr++ = g; | |
2970 *ptr++ = b; | |
2971 } | |
2972 } | |
2973 | |
2974 lua_pushlstring(L, str, size); | |
2975 delete[] str; | |
2976 return 1; | |
2977 } | |
2978 | |
2979 // gui.opacity(number alphaValue) | |
2980 // sets the transparency of subsequent draw calls | |
2981 // 0.0 is completely transparent, 1.0 is completely opaque | |
2982 // non-integer values are supported and meaningful, as are values greater than 1.0 | |
2983 // it is not necessary to use this function to get transparency (or the less-recommended gui.transparency() either), | |
2984 // because you can provide an alpha value in the color argument of each draw call. | |
2985 | |
2986 // however, it can be convenient to be able to globally modify the drawing transparency | |
2987 static int gui_setopacity(lua_State *L) | |
2988 { | |
2989 double opacF = luaL_checknumber(L, 1); | |
2990 transparencyModifier = (int)(opacF * 255); | |
2991 if (transparencyModifier < 0) | |
2992 transparencyModifier = 0; | |
2993 return 0; | |
2994 } | |
2995 | |
2996 // gui.transparency(int strength) | |
2997 // | |
2998 | |
2999 // 0 = solid, | |
3000 static int gui_transparency(lua_State *L) | |
3001 { | |
3002 double trans = luaL_checknumber(L, 1); | |
3003 transparencyModifier = (int)((4.0 - trans) / 4.0 * 255); | |
3004 if (transparencyModifier < 0) | |
3005 transparencyModifier = 0; | |
3006 return 0; | |
3007 } | |
3008 | |
3009 static const uint32 Small_Font_Data[] = | |
3010 { | |
3011 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 32 | |
3012 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000000, 0x00000700, 0x00000000, // 33 ! | |
3013 0x00000000, 0x00040002, 0x00050003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 34 " | |
3014 0x00000000, 0x00040002, 0x00050403, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 35 # | |
3015 0x00000000, 0x00040300, 0x00000403, 0x00000500, 0x00070600, 0x00000706, 0x00000000, // 36 $ | |
3016 0x00000000, 0x00000002, 0x00050000, 0x00000500, 0x00000005, 0x00080000, 0x00000000, // 37 % | |
3017 0x00000000, 0x00000300, 0x00050003, 0x00000500, 0x00070005, 0x00080700, 0x00000000, // 38 & | |
3018 0x00000000, 0x00000300, 0x00000400, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 39 ' | |
3019 0x00000000, 0x00000300, 0x00000003, 0x00000004, 0x00000005, 0x00000700, 0x00000000, // 40 ( | |
3020 0x00000000, 0x00000300, 0x00050000, 0x00060000, 0x00070000, 0x00000700, 0x00000000, // 41 ) | |
3021 0x00000000, 0x00000000, 0x00000400, 0x00060504, 0x00000600, 0x00080006, 0x00000000, // 42 * | |
3022 0x00000000, 0x00000000, 0x00000400, 0x00060504, 0x00000600, 0x00000000, 0x00000000, // 43 + | |
3023 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000600, 0x00000700, 0x00000007, // 44 , | |
3024 0x00000000, 0x00000000, 0x00000000, 0x00060504, 0x00000000, 0x00000000, 0x00000000, // 45 - | |
3025 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000700, 0x00000000, // 46 . | |
3026 0x00030000, 0x00040000, 0x00000400, 0x00000500, 0x00000005, 0x00000006, 0x00000000, // 47 / | |
3027 0x00000000, 0x00000300, 0x00050003, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 48 0 | |
3028 0x00000000, 0x00000300, 0x00000403, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 49 1 | |
3029 0x00000000, 0x00000302, 0x00050000, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 50 2 | |
3030 0x00000000, 0x00000302, 0x00050000, 0x00000504, 0x00070000, 0x00000706, 0x00000000, // 51 3 | |
3031 0x00000000, 0x00000300, 0x00000003, 0x00060004, 0x00070605, 0x00080000, 0x00000000, // 52 4 | |
3032 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00070000, 0x00000706, 0x00000000, // 53 5 | |
3033 0x00000000, 0x00000300, 0x00000003, 0x00000504, 0x00070005, 0x00000700, 0x00000000, // 54 6 | |
3034 0x00000000, 0x00040302, 0x00050000, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 55 7 | |
3035 0x00000000, 0x00000300, 0x00050003, 0x00000500, 0x00070005, 0x00000700, 0x00000000, // 56 8 | |
3036 0x00000000, 0x00000300, 0x00050003, 0x00060500, 0x00070000, 0x00000700, 0x00000000, // 57 9 | |
3037 0x00000000, 0x00000000, 0x00000400, 0x00000000, 0x00000000, 0x00000700, 0x00000000, // 58 : | |
3038 0x00000000, 0x00000000, 0x00000000, 0x00000500, 0x00000000, 0x00000700, 0x00000007, // 59 ; | |
3039 0x00000000, 0x00040000, 0x00000400, 0x00000004, 0x00000600, 0x00080000, 0x00000000, // 60 < | |
3040 0x00000000, 0x00000000, 0x00050403, 0x00000000, 0x00070605, 0x00000000, 0x00000000, // 61 = | |
3041 0x00000000, 0x00000002, 0x00000400, 0x00060000, 0x00000600, 0x00000006, 0x00000000, // 62 > | |
3042 0x00000000, 0x00000302, 0x00050000, 0x00000500, 0x00000000, 0x00000700, 0x00000000, // 63 ? | |
3043 0x00000000, 0x00000300, 0x00050400, 0x00060004, 0x00070600, 0x00000000, 0x00000000, // 64 @ | |
3044 0x00000000, 0x00000300, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 65 A | |
3045 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00070005, 0x00000706, 0x00000000, // 66 B | |
3046 0x00000000, 0x00040300, 0x00000003, 0x00000004, 0x00000005, 0x00080700, 0x00000000, // 67 C | |
3047 0x00000000, 0x00000302, 0x00050003, 0x00060004, 0x00070005, 0x00000706, 0x00000000, // 68 D | |
3048 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00000005, 0x00080706, 0x00000000, // 69 E | |
3049 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 70 F | |
3050 0x00000000, 0x00040300, 0x00000003, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 71 G | |
3051 0x00000000, 0x00040002, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 72 H | |
3052 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 73 I | |
3053 0x00000000, 0x00040000, 0x00050000, 0x00060000, 0x00070005, 0x00000700, 0x00000000, // 74 J | |
3054 0x00000000, 0x00040002, 0x00050003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 75 K | |
3055 0x00000000, 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00080706, 0x00000000, // 76 l | |
3056 0x00000000, 0x00040002, 0x00050403, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 77 M | |
3057 0x00000000, 0x00000302, 0x00050003, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 78 N | |
3058 0x00000000, 0x00040302, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00000000, // 79 O | |
3059 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 80 P | |
3060 0x00000000, 0x00040302, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00090000, // 81 Q | |
3061 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 82 R | |
3062 0x00000000, 0x00040300, 0x00000003, 0x00000500, 0x00070000, 0x00000706, 0x00000000, // 83 S | |
3063 0x00000000, 0x00040302, 0x00000400, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 84 T | |
3064 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00000000, // 85 U | |
3065 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00000600, 0x00000700, 0x00000000, // 86 V | |
3066 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 87 W | |
3067 0x00000000, 0x00040002, 0x00050003, 0x00000500, 0x00070005, 0x00080006, 0x00000000, // 88 X | |
3068 0x00000000, 0x00040002, 0x00050003, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 89 Y | |
3069 0x00000000, 0x00040302, 0x00050000, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 90 Z | |
3070 0x00000000, 0x00040300, 0x00000400, 0x00000500, 0x00000600, 0x00080700, 0x00000000, // 91 [ | |
3071 0x00000000, 0x00000002, 0x00000400, 0x00000500, 0x00070000, 0x00080000, 0x00000000, // 92 '\' | |
3072 0x00000000, 0x00000302, 0x00000400, 0x00000500, 0x00000600, 0x00000706, 0x00000000, // 93 ] | |
3073 0x00000000, 0x00000300, 0x00050003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 94 ^ | |
3074 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00080706, 0x00000000, // 95 _ | |
3075 0x00000000, 0x00000002, 0x00000400, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 96 ` | |
3076 0x00000000, 0x00000000, 0x00050400, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 97 a | |
3077 0x00000000, 0x00000002, 0x00000003, 0x00000504, 0x00070005, 0x00000706, 0x00000000, // 98 b | |
3078 0x00000000, 0x00000000, 0x00050400, 0x00000004, 0x00000005, 0x00080700, 0x00000000, // 99 c | |
3079 0x00000000, 0x00040000, 0x00050000, 0x00060500, 0x00070005, 0x00080700, 0x00000000, // 100 d | |
3080 0x00000000, 0x00000000, 0x00050400, 0x00060504, 0x00000005, 0x00080700, 0x00000000, // 101 e | |
3081 0x00000000, 0x00040300, 0x00000003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 102 f | |
3082 0x00000000, 0x00000000, 0x00050400, 0x00060004, 0x00070600, 0x00080000, 0x00000807, // 103 g | |
3083 0x00000000, 0x00000002, 0x00000003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 104 h | |
3084 0x00000000, 0x00000300, 0x00000000, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 105 i | |
3085 0x00000000, 0x00000300, 0x00000000, 0x00000500, 0x00000600, 0x00000700, 0x00000007, // 106 j | |
3086 0x00000000, 0x00000002, 0x00000003, 0x00060004, 0x00000605, 0x00080006, 0x00000000, // 107 k | |
3087 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000600, 0x00080000, 0x00000000, // 108 l | |
3088 0x00000000, 0x00000000, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 109 m | |
3089 0x00000000, 0x00000000, 0x00000403, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 110 n | |
3090 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 111 o | |
3091 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00000605, 0x00000006, 0x00000007, // 112 p | |
3092 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070600, 0x00080000, 0x00090000, // 113 q | |
3093 0x00000000, 0x00000000, 0x00050003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 114 r | |
3094 0x00000000, 0x00000000, 0x00050400, 0x00000004, 0x00070600, 0x00000706, 0x00000000, // 115 s | |
3095 0x00000000, 0x00000300, 0x00050403, 0x00000500, 0x00000600, 0x00080000, 0x00000000, // 116 t | |
3096 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 117 u | |
3097 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 118 v | |
3098 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 119 w | |
3099 0x00000000, 0x00000000, 0x00050003, 0x00000500, 0x00070005, 0x00080006, 0x00000000, // 120 x | |
3100 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00000600, 0x00000700, 0x00000007, // 121 y | |
3101 0x00000000, 0x00000000, 0x00050403, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 122 z | |
3102 0x00000000, 0x00040300, 0x00000400, 0x00000504, 0x00000600, 0x00080700, 0x00000000, // 123 { | |
3103 0x00000000, 0x00000300, 0x00000400, 0x00000000, 0x00000600, 0x00000700, 0x00000000, // 124 | | |
3104 0x00000000, 0x00000302, 0x00000400, 0x00060500, 0x00000600, 0x00000706, 0x00000000, // 125 } | |
3105 0x00000000, 0x00000302, 0x00050000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 126 ~ | |
3106 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070605, 0x00000000, 0x00000000, // 127 | |
3107 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, | |
3108 }; | |
3109 | |
3110 static void PutTextInternal(const char *str, int len, short x, short y, int color, int backcolor) | |
3111 { | |
3112 int Opac = (color >> 24) & 0xFF; | |
3113 int backOpac = (backcolor >> 24) & 0xFF; | |
3114 int origX = x; | |
3115 | |
3116 if (!Opac && !backOpac) | |
3117 return; | |
3118 | |
3119 while (*str && len && y < LUA_SCREEN_HEIGHT) | |
3120 { | |
3121 int c = *str++; | |
3122 while (x > LUA_SCREEN_WIDTH && c != '\n') | |
3123 { | |
3124 c = *str; | |
3125 if (c == '\0') | |
3126 break; | |
3127 str++; | |
3128 } | |
3129 | |
3130 if (c == '\n') | |
3131 { | |
3132 x = origX; | |
3133 y += 8; | |
3134 continue; | |
3135 } | |
3136 else if (c == '\t') // just in case | |
3137 { | |
3138 const int tabSpace = 8; | |
3139 x += (tabSpace - (((x - origX) / 4) % tabSpace)) * 4; | |
3140 continue; | |
3141 } | |
3142 | |
3143 if ((unsigned int)(c - 32) >= 96) | |
3144 continue; | |
3145 | |
3146 const unsigned char *Cur_Glyph = (const unsigned char *) &Small_Font_Data + (c - 32) * 7 * 4; | |
3147 | |
3148 for (int y2 = 0; y2 < 8; y2++) | |
3149 { | |
3150 unsigned int glyphLine = *((unsigned int *)Cur_Glyph + y2); | |
3151 for (int x2 = -1; x2 < 4; x2++) | |
3152 { | |
3153 int shift = x2 << 3; | |
3154 int mask = 0xFF << shift; | |
3155 int intensity = (glyphLine & mask) >> shift; | |
3156 | |
3157 if (intensity && x2 >= 0 && y2 < 7) | |
3158 { | |
3159 //int xdraw = max(0,min(LUA_SCREEN_WIDTH - 1,x+x2)); | |
3160 //int ydraw = max(0,min(LUA_SCREEN_HEIGHT - 1,y+y2)); | |
3161 //gui_drawpixel_fast(xdraw, ydraw, color); | |
3162 gui_drawpixel_internal(x + x2, y + y2, color); | |
3163 } | |
3164 else if (backOpac) | |
3165 { | |
3166 for (int y3 = max(0, y2 - 1); y3 <= min(6, y2 + 1); y3++) | |
3167 { | |
3168 unsigned int glyphLine = *((unsigned int *)Cur_Glyph + y3); | |
3169 for (int x3 = max(0, x2 - 1); x3 <= min(3, x2 + 1); x3++) | |
3170 { | |
3171 int shift = x3 << 3; | |
3172 int mask = 0xFF << shift; | |
3173 intensity |= (glyphLine & mask) >> shift; | |
3174 if (intensity) | |
3175 goto draw_outline; // speedup? | |
3176 } | |
3177 } | |
3178 | |
3179 draw_outline: | |
3180 if (intensity) | |
3181 { | |
3182 //int xdraw = max(0,min(LUA_SCREEN_WIDTH - 1,x+x2)); | |
3183 //int ydraw = max(0,min(LUA_SCREEN_HEIGHT - 1,y+y2)); | |
3184 //gui_drawpixel_fast(xdraw, ydraw, backcolor); | |
3185 gui_drawpixel_internal(x + x2, y + y2, backcolor); | |
3186 } | |
3187 } | |
3188 } | |
3189 } | |
3190 | |
3191 x += 4; | |
3192 len--; | |
3193 } | |
3194 } | |
3195 | |
3196 static int strlinelen(const char *string) | |
3197 { | |
3198 const char *s = string; | |
3199 while (*s && *s != '\n') | |
3200 s++; | |
3201 if (*s) | |
3202 s++; | |
3203 return s - string; | |
3204 } | |
3205 | |
3206 static void LuaDisplayString(const char *string, int y, int x, uint32 color, uint32 outlineColor) | |
3207 { | |
3208 if (!string) | |
3209 return; | |
3210 | |
3211 gui_prepare(); | |
3212 | |
3213 PutTextInternal(string, strlen(string), x, y, color, outlineColor); | |
3214 | |
3215 /* | |
3216 const char* ptr = string; | |
3217 while(*ptr && y < LUA_SCREEN_HEIGHT) | |
3218 { | |
3219 int len = strlinelen(ptr); | |
3220 int skip = 0; | |
3221 if(len < 1) len = 1; | |
3222 | |
3223 // break up the line if it's too long to display otherwise | |
3224 if(len > 63) | |
3225 { | |
3226 len = 63; | |
3227 const char* ptr2 = ptr + len-1; | |
3228 for(int j = len-1; j; j--, ptr2--) | |
3229 { | |
3230 if(*ptr2 == ' ' || *ptr2 == '\t') | |
3231 { | |
3232 len = j; | |
3233 skip = 1; | |
3234 break; | |
3235 } | |
3236 } | |
3237 } | |
3238 | |
3239 int xl = 0; | |
3240 int yl = 0; | |
3241 int xh = (LUA_SCREEN_WIDTH - 1 - 1) - 4*len; | |
3242 int yh = LUA_SCREEN_HEIGHT - 1; | |
3243 int x2 = min(max(x,xl),xh); | |
3244 int y2 = min(max(y,yl),yh); | |
3245 | |
3246 PutTextInternal(ptr,len,x2,y2,color,outlineColor); | |
3247 | |
3248 ptr += len + skip; | |
3249 y += 8; | |
3250 } | |
3251 */ | |
3252 } | |
3253 | |
3254 // gui.text(int x, int y, string msg) | |
3255 // | |
3256 // Displays the given text on the screen, using the same font and techniques as the | |
3257 | |
3258 // main HUD. | |
3259 static int gui_text(lua_State *L) | |
3260 { | |
3261 //extern int font_height; | |
3262 const char *msg; | |
3263 int x, y; | |
3264 uint32 colour, borderColour; | |
3265 | |
3266 x = luaL_checkinteger(L, 1); | |
3267 y = luaL_checkinteger(L, 2); | |
3268 //msg = luaL_checkstring(L, 3); | |
3269 msg = toCString(L, 3); | |
3270 | |
3271 // if (x < 0 || x >= LUA_SCREEN_WIDTH || y < 0 || y >= (LUA_SCREEN_HEIGHT - font_height)) | |
3272 // luaL_error(L,"bad coordinates"); | |
3273 colour = gui_optcolour(L, 4, LUA_BUILD_PIXEL(255, 255, 255, 255)); | |
3274 borderColour = gui_optcolour(L, 5, LUA_BUILD_PIXEL(255, 0, 0, 0)); | |
3275 | |
3276 gui_prepare(); | |
3277 | |
3278 LuaDisplayString(msg, y, x, colour, borderColour); | |
3279 | |
3280 return 0; | |
3281 } | |
3282 | |
3283 // gui.gdoverlay([int dx=0, int dy=0,] string str [, sx=0, sy=0, sw, sh] [, float alphamul=1.0]) | |
3284 // | |
3285 // Overlays the given image on the screen. | |
3286 | |
3287 // example: gui.gdoverlay(gd.createFromPng("myimage.png"):gdStr()) | |
3288 static int gui_gdoverlay(lua_State *L) | |
3289 { | |
3290 int argCount = lua_gettop(L); | |
3291 | |
3292 int xStartDst = 0; | |
3293 int yStartDst = 0; | |
3294 int xStartSrc = 0; | |
3295 int yStartSrc = 0; | |
3296 | |
3297 int index = 1; | |
3298 if (lua_type(L, index) == LUA_TNUMBER) | |
3299 { | |
3300 xStartDst = lua_tointeger(L, index++); | |
3301 if (lua_type(L, index) == LUA_TNUMBER) | |
3302 yStartDst = lua_tointeger(L, index++); | |
3303 } | |
3304 | |
3305 luaL_checktype(L, index, LUA_TSTRING); | |
3306 | |
3307 const unsigned char *ptr = (const unsigned char *)lua_tostring(L, index++); | |
3308 | |
3309 if (ptr[0] != 255 || (ptr[1] != 254 && ptr[1] != 255)) | |
3310 luaL_error(L, "bad image data"); | |
3311 | |
3312 bool trueColor = (ptr[1] == 254); | |
3313 ptr += 2; | |
3314 | |
3315 int imgwidth = *ptr++ << 8; | |
3316 imgwidth |= *ptr++; | |
3317 | |
3318 int width = imgwidth; | |
3319 int imgheight = *ptr++ << 8; | |
3320 imgheight |= *ptr++; | |
3321 | |
3322 int height = imgheight; | |
3323 if ((!trueColor && *ptr) || (trueColor && !*ptr)) | |
3324 luaL_error(L, "bad image data"); | |
3325 ptr++; | |
3326 | |
3327 int pitch = imgwidth * (trueColor ? 4 : 1); | |
3328 | |
3329 if ((argCount - index + 1) >= 4) | |
3330 { | |
3331 xStartSrc = luaL_checkinteger(L, index++); | |
3332 yStartSrc = luaL_checkinteger(L, index++); | |
3333 width = luaL_checkinteger(L, index++); | |
3334 height = luaL_checkinteger(L, index++); | |
3335 } | |
3336 | |
3337 int alphaMul = transparencyModifier; | |
3338 if (lua_isnumber(L, index)) | |
3339 alphaMul = (int)(alphaMul * lua_tonumber(L, index++)); | |
3340 if (alphaMul <= 0) | |
3341 return 0; | |
3342 | |
3343 // since there aren't that many possible opacity levels, | |
3344 // do the opacity modification calculations beforehand instead of per pixel | |
3345 int opacMap[256]; | |
3346 for (int i = 0; i < 128; i++) | |
3347 { | |
3348 int opac = 255 - ((i << 1) | (i & 1)); // gdAlphaMax = 127, not 255 | |
3349 opac = (opac * alphaMul) / 255; | |
3350 if (opac < 0) | |
3351 opac = 0; | |
3352 if (opac > 255) | |
3353 opac = 255; | |
3354 opacMap[i] = opac; | |
3355 } | |
3356 | |
3357 for (int i = 128; i < 256; i++) | |
3358 opacMap[i] = 0; // what should we do for them, actually? | |
3359 int colorsTotal = 0; | |
3360 if (!trueColor) | |
3361 { | |
3362 colorsTotal = *ptr++ << 8; | |
3363 colorsTotal |= *ptr++; | |
3364 } | |
3365 | |
3366 int transparent = *ptr++ << 24; | |
3367 transparent |= *ptr++ << 16; | |
3368 transparent |= *ptr++ << 8; | |
3369 transparent |= *ptr++; | |
3370 struct | |
3371 { | |
3372 uint8 r, g, b, a; | |
3373 } pal[256]; | |
3374 if (!trueColor) | |
3375 for (int i = 0; i < 256; i++) | |
3376 { | |
3377 pal[i].r = *ptr++; | |
3378 pal[i].g = *ptr++; | |
3379 pal[i].b = *ptr++; | |
3380 pal[i].a = opacMap[*ptr++]; | |
3381 } | |
3382 | |
3383 // some of clippings | |
3384 if (xStartSrc < 0) | |
3385 { | |
3386 width += xStartSrc; | |
3387 xStartDst -= xStartSrc; | |
3388 xStartSrc = 0; | |
3389 } | |
3390 | |
3391 if (yStartSrc < 0) | |
3392 { | |
3393 height += yStartSrc; | |
3394 yStartDst -= yStartSrc; | |
3395 yStartSrc = 0; | |
3396 } | |
3397 | |
3398 if (xStartSrc + width >= imgwidth) | |
3399 width = imgwidth - xStartSrc; | |
3400 if (yStartSrc + height >= imgheight) | |
3401 height = imgheight - yStartSrc; | |
3402 if (xStartDst < 0) | |
3403 { | |
3404 width += xStartDst; | |
3405 if (width <= 0) | |
3406 return 0; | |
3407 xStartSrc = -xStartDst; | |
3408 xStartDst = 0; | |
3409 } | |
3410 | |
3411 if (yStartDst < 0) | |
3412 { | |
3413 height += yStartDst; | |
3414 if (height <= 0) | |
3415 return 0; | |
3416 yStartSrc = -yStartDst; | |
3417 yStartDst = 0; | |
3418 } | |
3419 | |
3420 if (xStartDst + width >= LUA_SCREEN_WIDTH) | |
3421 width = LUA_SCREEN_WIDTH - xStartDst; | |
3422 if (yStartDst + height >= LUA_SCREEN_HEIGHT) | |
3423 height = LUA_SCREEN_HEIGHT - yStartDst; | |
3424 if (width <= 0 || height <= 0) | |
3425 return 0; // out of screen or invalid size | |
3426 gui_prepare(); | |
3427 | |
3428 const uint8 *pix = (const uint8 *)(&ptr[yStartSrc * pitch + (xStartSrc * (trueColor ? 4 : 1))]); | |
3429 int bytesToNextLine = pitch - (width * (trueColor ? 4 : 1)); | |
3430 if (trueColor) | |
3431 { | |
3432 for (int y = yStartDst; y < height + yStartDst && y < LUA_SCREEN_HEIGHT; y++, pix += bytesToNextLine) | |
3433 { | |
3434 for (int x = xStartDst; x < width + xStartDst && x < LUA_SCREEN_WIDTH; x++, pix += 4) | |
3435 { | |
3436 gui_drawpixel_fast(x, y, LUA_BUILD_PIXEL(opacMap[pix[0]], pix[1], pix[2], pix[3])); | |
3437 } | |
3438 } | |
3439 } | |
3440 else | |
3441 { | |
3442 for (int y = yStartDst; y < height + yStartDst && y < LUA_SCREEN_HEIGHT; y++, pix += bytesToNextLine) | |
3443 { | |
3444 for (int x = xStartDst; x < width + xStartDst && x < LUA_SCREEN_WIDTH; x++, pix++) | |
3445 { | |
3446 gui_drawpixel_fast(x, y, LUA_BUILD_PIXEL(pal[*pix].a, pal[*pix].r, pal[*pix].g, pal[*pix].b)); | |
3447 } | |
3448 } | |
3449 } | |
3450 | |
3451 return 0; | |
3452 } | |
3453 | |
3454 // function gui.register(function f) | |
3455 // | |
3456 // This function will be called just before a graphical update. | |
3457 // More complicated, but doesn't suffer any frame delays. | |
3458 // Nil will be accepted in place of a function to erase | |
3459 // a previously registered function, and the previous function | |
3460 | |
3461 // (if any) is returned, or nil if none. | |
3462 static int gui_register(lua_State *L) | |
3463 { | |
3464 // We'll do this straight up. | |
3465 // First set up the stack. | |
3466 lua_settop(L, 1); | |
3467 | |
3468 // Verify the validity of the entry | |
3469 if (!lua_isnil(L, 1)) | |
3470 luaL_checktype(L, 1, LUA_TFUNCTION); | |
3471 | |
3472 // Get the old value | |
3473 lua_getfield(L, LUA_REGISTRYINDEX, guiCallbackTable); | |
3474 | |
3475 // Save the new value | |
3476 lua_pushvalue(L, 1); | |
3477 lua_setfield(L, LUA_REGISTRYINDEX, guiCallbackTable); | |
3478 | |
3479 // The old value is on top of the stack. Return it. | |
3480 return 1; | |
3481 } | |
3482 | |
3483 // string gui.popup(string message, [string type = "ok"]) | |
3484 // | |
3485 | |
3486 // Popup dialog! | |
3487 int gui_popup(lua_State *L) | |
3488 { | |
3489 const char *message = luaL_checkstring(L, 1); | |
3490 const char *type = luaL_optstring(L, 2, "ok"); | |
3491 | |
3492 #if (defined(WIN32) && !defined(SDL)) | |
3493 int t; | |
3494 if (strcmp(type, "ok") == 0) | |
3495 t = MB_OK; | |
3496 else if (strcmp(type, "yesno") == 0) | |
3497 t = MB_YESNO; | |
3498 else if (strcmp(type, "yesnocancel") == 0) | |
3499 t = MB_YESNOCANCEL; | |
3500 else | |
3501 return luaL_error(L, "invalid popup type \"%s\"", type); | |
3502 | |
3503 theApp.winCheckFullscreen(); | |
3504 systemSoundClearBuffer(); | |
3505 int result = AfxGetApp()->m_pMainWnd->MessageBox(message, "Lua Script Pop-up", t); | |
3506 | |
3507 lua_settop(L, 1); | |
3508 | |
3509 if (t != MB_OK) | |
3510 { | |
3511 if (result == IDYES) | |
3512 lua_pushstring(L, "yes"); | |
3513 else if (result == IDNO) | |
3514 lua_pushstring(L, "no"); | |
3515 else if (result == IDCANCEL) | |
3516 lua_pushstring(L, "cancel"); | |
3517 else | |
3518 luaL_error(L, "win32 unrecognized return value %d", result); | |
3519 return 1; | |
3520 } | |
3521 | |
3522 // else, we don't care. | |
3523 return 0; | |
3524 #else | |
3525 char *t; | |
3526 #ifdef __linux | |
3527 // The Linux backend has a "FromPause" variable. | |
3528 // If set to 1, assume some known external event has screwed with the flow of time. | |
3529 // Since this pauses the emulator waiting for a response, we set it to 1. | |
3530 // FIXME: Well, actually it doesn't | |
3531 // extern int FromPause; | |
3532 // FromPause = 1; | |
3533 | |
3534 int pid; // appease compiler | |
3535 | |
3536 // Before doing any work, verify the correctness of the parameters. | |
3537 if (strcmp(type, "ok") == 0) | |
3538 t = "OK:100"; | |
3539 else if (strcmp(type, "yesno") == 0) | |
3540 t = "Yes:100,No:101"; | |
3541 else if (strcmp(type, "yesnocancel") == 0) | |
3542 t = "Yes:100,No:101,Cancel:102"; | |
3543 else | |
3544 return luaL_error(L, "invalid popup type \"%s\"", type); | |
3545 | |
3546 // Can we find a copy of xmessage? Search the path. | |
3547 char *path = strdup(getenv("PATH")); | |
3548 | |
3549 char *current = path; | |
3550 | |
3551 char *colon; | |
3552 | |
3553 int found = 0; | |
3554 | |
3555 while (current) | |
3556 { | |
3557 colon = strchr(current, ':'); | |
3558 | |
3559 // Clip off the colon. | |
3560 *colon++ = 0; | |
3561 | |
3562 int len = strlen(current); | |
3563 char *filename = (char *)malloc(len + 12); // always give excess | |
3564 snprintf(filename, len + 12, "%s/xmessage", current); | |
3565 | |
3566 if (access(filename, X_OK) == 0) | |
3567 { | |
3568 free(filename); | |
3569 found = 1; | |
3570 break; | |
3571 } | |
3572 | |
3573 // Failed, move on. | |
3574 current = colon; | |
3575 free(filename); | |
3576 } | |
3577 | |
3578 free(path); | |
3579 | |
3580 // We've found it? | |
3581 if (!found) | |
3582 goto use_console; | |
3583 | |
3584 pid = fork(); | |
3585 if (pid == 0) | |
3586 { // I'm the virgin sacrifice | |
3587 // I'm gonna be dead in a matter of microseconds anyways, so wasted memory doesn't matter to me. | |
3588 // Go ahead and abuse strdup. | |
3589 char *parameters[] = { "xmessage", "-buttons", t, strdup(message), NULL }; | |
3590 | |
3591 execvp("xmessage", parameters); | |
3592 | |
3593 // Aw shitty | |
3594 perror("exec xmessage"); | |
3595 exit(1); | |
3596 } | |
3597 else if (pid < 0) // something went wrong!!! Oh hell... use the console | |
3598 goto use_console; | |
3599 else | |
3600 { | |
3601 // We're the parent. Watch for the child. | |
3602 int r; | |
3603 int res = waitpid(pid, &r, 0); | |
3604 if (res < 0) // wtf? | |
3605 goto use_console; | |
3606 | |
3607 // The return value gets copmlicated... | |
3608 if (!WIFEXITED(r)) | |
3609 { | |
3610 luaL_error(L, "don't screw with my xmessage process!"); | |
3611 } | |
3612 | |
3613 r = WEXITSTATUS(r); | |
3614 | |
3615 // We assume it's worked. | |
3616 if (r == 0) | |
3617 { | |
3618 return 0; // no parameters for an OK | |
3619 } | |
3620 | |
3621 if (r == 100) | |
3622 { | |
3623 lua_pushstring(L, "yes"); | |
3624 return 1; | |
3625 } | |
3626 | |
3627 if (r == 101) | |
3628 { | |
3629 lua_pushstring(L, "no"); | |
3630 return 1; | |
3631 } | |
3632 | |
3633 if (r == 102) | |
3634 { | |
3635 lua_pushstring(L, "cancel"); | |
3636 return 1; | |
3637 } | |
3638 | |
3639 // Wtf? | |
3640 return luaL_error(L, "popup failed due to unknown results involving xmessage (%d)", r); | |
3641 } | |
3642 | |
3643 use_console: | |
3644 #endif | |
3645 | |
3646 // All else has failed | |
3647 if (strcmp(type, "ok") == 0) | |
3648 t = ""; | |
3649 else if (strcmp(type, "yesno") == 0) | |
3650 t = "yn"; | |
3651 else if (strcmp(type, "yesnocancel") == 0) | |
3652 t = "ync"; | |
3653 else | |
3654 return luaL_error(L, "invalid popup type \"%s\"", type); | |
3655 | |
3656 fprintf(stderr, "Lua Message: %s\n", message); | |
3657 | |
3658 while (true) | |
3659 { | |
3660 char buffer[64]; | |
3661 | |
3662 // We don't want parameters | |
3663 if (!t[0]) | |
3664 { | |
3665 fprintf(stderr, "[Press Enter]"); | |
3666 fgets(buffer, sizeof(buffer), stdin); | |
3667 | |
3668 // We're done | |
3669 return 0; | |
3670 } | |
3671 | |
3672 fprintf(stderr, "(%s): ", t); | |
3673 fgets(buffer, sizeof(buffer), stdin); | |
3674 | |
3675 // Check if the option is in the list | |
3676 if (strchr(t, tolower(buffer[0]))) | |
3677 { | |
3678 switch (tolower(buffer[0])) | |
3679 { | |
3680 case 'y': | |
3681 lua_pushstring(L, "yes"); | |
3682 return 1; | |
3683 case 'n': | |
3684 lua_pushstring(L, "no"); | |
3685 return 1; | |
3686 case 'c': | |
3687 lua_pushstring(L, "cancel"); | |
3688 return 1; | |
3689 default: | |
3690 luaL_error(L, "internal logic error in console based prompts for gui.popup"); | |
3691 } | |
3692 } | |
3693 | |
3694 // We fell through, so we assume the user answered wrong and prompt again. | |
3695 } | |
3696 | |
3697 // Nothing here, since the only way out is in the loop. | |
3698 #endif | |
3699 } | |
3700 | |
3701 #if (defined(WIN32) && !defined(SDL)) | |
3702 const char *s_keyToName[256] = | |
3703 { | |
3704 NULL, | |
3705 "leftclick", | |
3706 "rightclick", | |
3707 NULL, | |
3708 "middleclick", | |
3709 NULL, | |
3710 NULL, | |
3711 NULL, | |
3712 "backspace", | |
3713 "tab", | |
3714 NULL, | |
3715 NULL, | |
3716 NULL, | |
3717 "enter", | |
3718 NULL, | |
3719 NULL, | |
3720 "shift", // 0x10 | |
3721 "control", | |
3722 "alt", | |
3723 "pause", | |
3724 "capslock", | |
3725 NULL, | |
3726 NULL, | |
3727 NULL, | |
3728 NULL, | |
3729 NULL, | |
3730 NULL, | |
3731 "escape", | |
3732 NULL, | |
3733 NULL, | |
3734 NULL, | |
3735 NULL, | |
3736 "space", // 0x20 | |
3737 "pageup", | |
3738 "pagedown", | |
3739 "end", | |
3740 "home", | |
3741 "left", | |
3742 "up", | |
3743 "right", | |
3744 "down", | |
3745 NULL, | |
3746 NULL, | |
3747 NULL, | |
3748 NULL, | |
3749 "insert", | |
3750 "delete", | |
3751 NULL, | |
3752 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", | |
3753 NULL, NULL, NULL, NULL, NULL, NULL, NULL, | |
3754 "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", | |
3755 "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", | |
3756 "U", "V", "W", "X", "Y", "Z", | |
3757 NULL, | |
3758 NULL, | |
3759 NULL, | |
3760 NULL, | |
3761 NULL, | |
3762 "numpad0", "numpad1", "numpad2", "numpad3", "numpad4", "numpad5", "numpad6", "numpad7", "numpad8", "numpad9", | |
3763 "numpad*", "numpad+", | |
3764 NULL, | |
3765 "numpad-", "numpad.", "numpad/", | |
3766 "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", | |
3767 "F12", | |
3768 "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", | |
3769 "F24", | |
3770 NULL, | |
3771 NULL, | |
3772 NULL, | |
3773 NULL, | |
3774 NULL, | |
3775 NULL, | |
3776 NULL, | |
3777 NULL, | |
3778 "numlock", | |
3779 "scrolllock", | |
3780 NULL, // 0x92 | |
3781 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, | |
3782 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, | |
3783 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, | |
3784 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, | |
3785 NULL, // 0xB9 | |
3786 "semicolon", | |
3787 "plus", | |
3788 "comma", | |
3789 "minus", | |
3790 "period", | |
3791 "slash", | |
3792 "tilde", | |
3793 NULL, // 0xC1 | |
3794 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, | |
3795 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, | |
3796 NULL, NULL, NULL, NULL, NULL, NULL, NULL, | |
3797 NULL, // 0xDA | |
3798 "leftbracket", | |
3799 "backslash", | |
3800 "rightbracket", | |
3801 "quote", | |
3802 }; | |
3803 #endif | |
3804 | |
3805 // input.get() | |
3806 // takes no input, returns a lua table of entries representing the current input state, | |
3807 // independent of the joypad buttons the emulated game thinks are pressed | |
3808 // for example: | |
3809 // if the user is holding the W key and the left mouse button | |
3810 // and has the mouse at the bottom-right corner of the game screen, | |
3811 | |
3812 // then this would return {W=true, leftclick=true, xmouse=255, ymouse=223} | |
3813 static int input_getcurrentinputstatus(lua_State *L) | |
3814 { | |
3815 lua_newtable(L); | |
3816 | |
3817 #if (defined(WIN32) && !defined(SDL)) | |
3818 // keyboard and mouse button status | |
3819 { | |
3820 unsigned char keys[256]; | |
3821 if (true /*!GUI.BackgroundInput*/) // TODO: background input | |
3822 { | |
3823 if (GetKeyboardState(keys)) | |
3824 { | |
3825 for (int i = 1; i < 255; i++) | |
3826 { | |
3827 int mask = (i == VK_CAPITAL || i == VK_NUMLOCK || i == VK_SCROLL) ? 0x01 : 0x80; | |
3828 if (keys[i] & mask) | |
3829 { | |
3830 const char *name = s_keyToName[i]; | |
3831 if (name) | |
3832 { | |
3833 lua_pushboolean(L, true); | |
3834 lua_setfield(L, -2, name); | |
3835 } | |
3836 } | |
3837 } | |
3838 } | |
3839 } | |
3840 else // use a slightly different method that will detect background input: | |
3841 { | |
3842 for (int i = 1; i < 255; i++) | |
3843 { | |
3844 const char *name = s_keyToName[i]; | |
3845 if (name) | |
3846 { | |
3847 int active; | |
3848 if (i == VK_CAPITAL || i == VK_NUMLOCK || i == VK_SCROLL) | |
3849 active = GetKeyState(i) & 0x01; | |
3850 else | |
3851 active = GetAsyncKeyState(i) & 0x8000; | |
3852 if (active) | |
3853 { | |
3854 lua_pushboolean(L, true); | |
3855 lua_setfield(L, -2, name); | |
3856 } | |
3857 } | |
3858 } | |
3859 } | |
3860 } | |
3861 | |
3862 // mouse position in game screen pixel coordinates | |
3863 { | |
3864 POINT mouse; | |
3865 | |
3866 int xofs = 0, yofs = 0, width = 240, height = 160; | |
3867 if (!systemIsRunningGBA()) | |
3868 { | |
3869 if (gbBorderOn) | |
3870 width = 256, height = 224, xofs = 48, yofs = 40; | |
3871 else | |
3872 width = 160, height = 144; | |
3873 } | |
3874 | |
3875 GetCursorPos(&mouse); | |
3876 AfxGetApp()->m_pMainWnd->ScreenToClient(&mouse); | |
3877 | |
3878 // game screen is always fully stretched to window size, | |
3879 // with no aspect rate correction, or something like that. | |
3880 RECT clientRect; | |
3881 AfxGetApp()->m_pMainWnd->GetClientRect(&clientRect); | |
3882 | |
3883 int wndWidth = clientRect.right - clientRect.left; | |
3884 int wndHeight = clientRect.bottom - clientRect.top; | |
3885 mouse.x = (LONG) (mouse.x * ((float)width / wndWidth)) - xofs; | |
3886 mouse.y = (LONG) (mouse.y * ((float)height / wndHeight)) - yofs; | |
3887 | |
3888 lua_pushinteger(L, mouse.x); | |
3889 lua_setfield(L, -2, "xmouse"); | |
3890 lua_pushinteger(L, mouse.y); | |
3891 lua_setfield(L, -2, "ymouse"); | |
3892 } | |
3893 | |
3894 #else | |
3895 // NYI (well, return an empty table) | |
3896 #endif | |
3897 return 1; | |
3898 } | |
3899 | |
3900 static int avi_framecount(lua_State *L) | |
3901 { | |
3902 #ifdef WIN32 | |
3903 if (theApp.aviRecorder != NULL) | |
3904 { | |
3905 lua_pushinteger(L, theApp.aviRecorder->videoFrames()); | |
3906 } | |
3907 else | |
3908 #endif | |
3909 { | |
3910 lua_pushinteger(L, 0); | |
3911 } | |
3912 return 1; | |
3913 } | |
3914 | |
3915 static int avi_pause(lua_State *L) | |
3916 { | |
3917 #ifdef WIN32 | |
3918 if (theApp.aviRecorder != NULL) | |
3919 theApp.aviRecorder->Pause(true); | |
3920 #endif | |
3921 return 1; | |
3922 } | |
3923 | |
3924 static int avi_resume(lua_State *L) | |
3925 { | |
3926 #ifdef WIN32 | |
3927 if (theApp.aviRecorder != NULL) | |
3928 theApp.aviRecorder->Pause(false); | |
3929 #endif | |
3930 return 1; | |
3931 } | |
3932 | |
3933 static int sound_get(lua_State *L) | |
3934 { | |
3935 extern int32 soundLevel1; | |
3936 extern int32 soundLevel2; | |
3937 extern int32 soundBalance; | |
3938 extern int32 soundMasterOn; | |
3939 extern int32 soundVIN; | |
3940 extern int32 sound1On; | |
3941 extern int32 sound1EnvelopeVolume; | |
3942 extern int32 sound2On; | |
3943 extern int32 sound2EnvelopeVolume; | |
3944 extern int32 sound3On; | |
3945 extern int32 sound3OutputLevel; | |
3946 extern int32 sound3Bank; | |
3947 extern int32 sound3DataSize; | |
3948 extern int32 sound3ForcedOutput; | |
3949 extern int32 sound4On; | |
3950 extern int32 sound4EnvelopeVolume; | |
3951 extern u8 sound3WaveRam[0x20]; | |
3952 | |
3953 int freqReg; | |
3954 double freq; | |
3955 double leftvolscale; | |
3956 double rightvolscale; | |
3957 double panpot; | |
3958 bool gba = systemIsRunningGBA(); | |
3959 u8* gbMem = gba ? ioMem : gbMemory; | |
3960 const int rNR10 = gba ? 0x60 : 0xff10; | |
3961 const int rNR11 = gba ? 0x62 : 0xff11; | |
3962 const int rNR12 = gba ? 0x63 : 0xff12; | |
3963 const int rNR13 = gba ? 0x64 : 0xff13; | |
3964 const int rNR14 = gba ? 0x65 : 0xff14; | |
3965 const int rNR21 = gba ? 0x68 : 0xff16; | |
3966 const int rNR22 = gba ? 0x69 : 0xff17; | |
3967 const int rNR23 = gba ? 0x6c : 0xff18; | |
3968 const int rNR24 = gba ? 0x6d : 0xff19; | |
3969 const int rNR30 = gba ? 0x70 : 0xff1a; | |
3970 const int rNR31 = gba ? 0x72 : 0xff1b; | |
3971 const int rNR32 = gba ? 0x73 : 0xff1c; | |
3972 const int rNR33 = gba ? 0x74 : 0xff1d; | |
3973 const int rNR34 = gba ? 0x75 : 0xff1e; | |
3974 const int rNR41 = gba ? 0x78 : 0xff20; | |
3975 const int rNR42 = gba ? 0x79 : 0xff21; | |
3976 const int rNR43 = gba ? 0x7c : 0xff22; | |
3977 const int rNR44 = gba ? 0x7d : 0xff23; | |
3978 const int rNR50 = gba ? 0x80 : 0xff24; | |
3979 const int rNR51 = gba ? 0x81 : 0xff25; | |
3980 const int rNR52 = gba ? 0x84 : 0xff26; | |
3981 const int rWAVE_RAM = gba ? 0x90 : 0xff30; | |
3982 | |
3983 const int32 _soundVIN = 0x88; // gba ? 0x88 : soundVIN; | |
3984 const bool soundVINLeft = ((_soundVIN & 0x80) != 0); | |
3985 const bool soundVINRight = ((_soundVIN & 0x08) != 0); | |
3986 | |
3987 lua_newtable(L); | |
3988 | |
3989 // square1 | |
3990 lua_newtable(L); | |
3991 if(sound1On == 0 || soundMasterOn == 0) | |
3992 { | |
3993 lua_pushnumber(L, 0.0); | |
3994 panpot = 0.5; | |
3995 } | |
3996 else | |
3997 { | |
3998 double envVolume = sound1EnvelopeVolume / 15.0; | |
3999 if (soundVINLeft && (soundBalance & 0x10) != 0) | |
4000 leftvolscale = ((soundLevel2 / 7.0) * envVolume); | |
4001 else | |
4002 leftvolscale = 0.0; | |
4003 if (soundVINRight && (soundBalance & 0x01) != 0) | |
4004 rightvolscale = ((soundLevel1 / 7.0) * envVolume); | |
4005 else | |
4006 rightvolscale = 0.0; | |
4007 if ((leftvolscale + rightvolscale) != 0) | |
4008 panpot = rightvolscale / (leftvolscale + rightvolscale); | |
4009 else | |
4010 panpot = 0.5; | |
4011 lua_pushnumber(L, (leftvolscale + rightvolscale) / 2.0); | |
4012 } | |
4013 lua_setfield(L, -2, "volume"); | |
4014 lua_pushnumber(L, panpot); | |
4015 lua_setfield(L, -2, "panpot"); | |
4016 freqReg = (((int)(gbMem[rNR14] & 7) << 8) | gbMem[rNR13]); | |
4017 freq = 131072.0 / (2048 - freqReg); | |
4018 lua_pushnumber(L, freq); | |
4019 lua_setfield(L, -2, "frequency"); | |
4020 lua_pushnumber(L, (log(freq / 440.0) * 12 / log(2.0)) + 69); | |
4021 lua_setfield(L, -2, "midikey"); | |
4022 lua_pushinteger(L, (gbMem[rNR11] & 0xC0) >> 6); | |
4023 lua_setfield(L, -2, "duty"); | |
4024 lua_newtable(L); | |
4025 lua_pushinteger(L, freqReg); | |
4026 lua_setfield(L, -2, "frequency"); | |
4027 lua_setfield(L, -2, "regs"); | |
4028 lua_setfield(L, -2, "square1"); | |
4029 // square2 | |
4030 lua_newtable(L); | |
4031 if(sound2On == 0 || soundMasterOn == 0) | |
4032 { | |
4033 lua_pushnumber(L, 0.0); | |
4034 panpot = 0.5; | |
4035 } | |
4036 else | |
4037 { | |
4038 double envVolume = sound2EnvelopeVolume / 15.0; | |
4039 if (soundVINLeft && (soundBalance & 0x20) != 0) | |
4040 leftvolscale = ((soundLevel2 / 7.0) * envVolume); | |
4041 else | |
4042 leftvolscale = 0.0; | |
4043 if (soundVINRight && (soundBalance & 0x02) != 0) | |
4044 rightvolscale = ((soundLevel1 / 7.0) * envVolume); | |
4045 else | |
4046 rightvolscale = 0.0; | |
4047 if ((leftvolscale + rightvolscale) != 0) | |
4048 panpot = rightvolscale / (leftvolscale + rightvolscale); | |
4049 else | |
4050 panpot = 0.5; | |
4051 lua_pushnumber(L, (leftvolscale + rightvolscale) / 2.0); | |
4052 } | |
4053 lua_setfield(L, -2, "volume"); | |
4054 lua_pushnumber(L, panpot); | |
4055 lua_setfield(L, -2, "panpot"); | |
4056 freqReg = (((int)(gbMem[rNR24] & 7) << 8) | gbMem[rNR23]); | |
4057 freq = 131072.0 / (2048 - freqReg); | |
4058 lua_pushnumber(L, freq); | |
4059 lua_setfield(L, -2, "frequency"); | |
4060 lua_pushnumber(L, (log(freq / 440.0) * 12 / log(2.0)) + 69); | |
4061 lua_setfield(L, -2, "midikey"); | |
4062 lua_pushinteger(L, (gbMem[rNR21] & 0xC0) >> 6); | |
4063 lua_setfield(L, -2, "duty"); | |
4064 lua_newtable(L); | |
4065 lua_pushinteger(L, freqReg); | |
4066 lua_setfield(L, -2, "frequency"); | |
4067 lua_setfield(L, -2, "regs"); | |
4068 lua_setfield(L, -2, "square2"); | |
4069 // wavememory | |
4070 lua_newtable(L); | |
4071 if(sound3On == 0 || soundMasterOn == 0) | |
4072 { | |
4073 lua_pushnumber(L, 0.0); | |
4074 panpot = 0.5; | |
4075 } | |
4076 else | |
4077 { | |
4078 double envVolume; | |
4079 if (gba && sound3ForcedOutput != 0) | |
4080 envVolume = 0.75; | |
4081 else | |
4082 { | |
4083 double volTable[4] = { 0.0, 1.0, 0.5, 0.25 }; | |
4084 envVolume = volTable[sound3OutputLevel & 3]; | |
4085 } | |
4086 | |
4087 if (soundVINLeft && (soundBalance & 0x40) != 0) | |
4088 leftvolscale = ((soundLevel2 / 7.0) * envVolume); | |
4089 else | |
4090 leftvolscale = 0.0; | |
4091 if (soundVINRight && (soundBalance & 0x04) != 0) | |
4092 rightvolscale = ((soundLevel1 / 7.0) * envVolume); | |
4093 else | |
4094 rightvolscale = 0.0; | |
4095 if ((leftvolscale + rightvolscale) != 0) | |
4096 panpot = rightvolscale / (leftvolscale + rightvolscale); | |
4097 else | |
4098 panpot = 0.5; | |
4099 lua_pushnumber(L, (leftvolscale + rightvolscale) / 2.0); | |
4100 } | |
4101 lua_setfield(L, -2, "volume"); | |
4102 lua_pushnumber(L, panpot); | |
4103 lua_setfield(L, -2, "panpot"); | |
4104 int waveMemSamples = 32; | |
4105 if (gba) | |
4106 { | |
4107 lua_pushlstring(L, (const char *) &sound3WaveRam[sound3Bank * 0x10], sound3DataSize ? 0x20 : 0x10); | |
4108 waveMemSamples = sound3DataSize ? 64 : 32; | |
4109 } | |
4110 else | |
4111 { | |
4112 lua_pushlstring(L, (const char *) &gbMem[rWAVE_RAM], 0x10); | |
4113 } | |
4114 lua_setfield(L, -2, "waveform"); | |
4115 freqReg = (((int)(gbMem[rNR34] & 7) << 8) | gbMem[rNR33]); | |
4116 freq = 2097152.0 / (waveMemSamples * (2048 - freqReg)); | |
4117 lua_pushnumber(L, freq); | |
4118 lua_setfield(L, -2, "frequency"); | |
4119 lua_pushnumber(L, (log(freq / 440.0) * 12 / log(2.0)) + 69); | |
4120 lua_setfield(L, -2, "midikey"); | |
4121 lua_newtable(L); | |
4122 lua_pushinteger(L, freqReg); | |
4123 lua_setfield(L, -2, "frequency"); | |
4124 lua_setfield(L, -2, "regs"); | |
4125 lua_setfield(L, -2, "wavememory"); | |
4126 // noise | |
4127 lua_newtable(L); | |
4128 if(sound4On == 0 || soundMasterOn == 0) | |
4129 { | |
4130 lua_pushnumber(L, 0.0); | |
4131 panpot = 0.5; | |
4132 } | |
4133 else | |
4134 { | |
4135 double envVolume = sound4EnvelopeVolume / 15.0; | |
4136 if (soundVINLeft && (soundBalance & 0x80) != 0) | |
4137 leftvolscale = ((soundLevel2 / 7.0) * envVolume); | |
4138 else | |
4139 leftvolscale = 0.0; | |
4140 if (soundVINRight && (soundBalance & 0x08) != 0) | |
4141 rightvolscale = ((soundLevel1 / 7.0) * envVolume); | |
4142 else | |
4143 rightvolscale = 0.0; | |
4144 if ((leftvolscale + rightvolscale) != 0) | |
4145 panpot = rightvolscale / (leftvolscale + rightvolscale); | |
4146 else | |
4147 panpot = 0.5; | |
4148 lua_pushnumber(L, (leftvolscale + rightvolscale) / 2.0); | |
4149 } | |
4150 lua_setfield(L, -2, "volume"); | |
4151 lua_pushnumber(L, panpot); | |
4152 lua_setfield(L, -2, "panpot"); | |
4153 const int gbNoiseFreqTable[8] = { 1, 2, 4, 6, 8, 10, 12, 14 }; | |
4154 freqReg = gbNoiseFreqTable[gbMem[rNR43] & 7] << (1 + (gbMem[rNR43] >> 4)); | |
4155 lua_pushboolean(L, (gbMem[rNR43] & 8) != 0); | |
4156 lua_setfield(L, -2, "short"); | |
4157 freq = 1048576.0 / freqReg; | |
4158 lua_pushnumber(L, freq); | |
4159 lua_setfield(L, -2, "frequency"); | |
4160 lua_pushnumber(L, (log(freq / 440.0) * 12 / log(2.0)) + 69); | |
4161 lua_setfield(L, -2, "midikey"); | |
4162 lua_newtable(L); | |
4163 lua_pushinteger(L, freqReg); | |
4164 lua_setfield(L, -2, "frequency"); | |
4165 lua_setfield(L, -2, "regs"); | |
4166 lua_setfield(L, -2, "noise"); | |
4167 | |
4168 return 1; | |
4169 } | |
4170 | |
4171 // same as math.random, but uses SFMT instead of C rand() | |
4172 // FIXME: this function doesn't care multi-instance, | |
4173 | |
4174 // original math.random either though (Lua 5.1) | |
4175 static int sfmt_random(lua_State *L) | |
4176 { | |
4177 lua_Number r = (lua_Number) genrand_real2(); | |
4178 switch (lua_gettop(L)) | |
4179 { // check number of arguments | |
4180 case 0: | |
4181 { // no arguments | |
4182 lua_pushnumber(L, r); // Number between 0 and 1 | |
4183 break; | |
4184 } | |
4185 | |
4186 case 1: | |
4187 { // only upper limit | |
4188 int u = luaL_checkint(L, 1); | |
4189 luaL_argcheck(L, 1 <= u, 1, "interval is empty"); | |
4190 lua_pushnumber(L, floor(r * u) + 1); // int between 1 and `u' | |
4191 break; | |
4192 } | |
4193 | |
4194 case 2: | |
4195 { // lower and upper limits | |
4196 int l = luaL_checkint(L, 1); | |
4197 int u = luaL_checkint(L, 2); | |
4198 luaL_argcheck(L, l <= u, 2, "interval is empty"); | |
4199 lua_pushnumber(L, floor(r * (u - l + 1)) + l); // int between `l' and `u' | |
4200 break; | |
4201 } | |
4202 | |
4203 default: | |
4204 return luaL_error(L, "wrong number of arguments"); | |
4205 } | |
4206 | |
4207 return 1; | |
4208 } | |
4209 | |
4210 // same as math.randomseed, but uses SFMT instead of C srand() | |
4211 // FIXME: this function doesn't care multi-instance, | |
4212 | |
4213 // original math.randomseed either though (Lua 5.1) | |
4214 static int sfmt_randomseed(lua_State *L) | |
4215 { | |
4216 init_gen_rand(luaL_checkint(L, 1)); | |
4217 return 0; | |
4218 } | |
4219 | |
4220 // the following bit operations are ported from LuaBitOp 1.0.1, | |
4221 // because it can handle the sign bit (bit 31) correctly. | |
4222 | |
4223 /* | |
4224 ** Lua BitOp -- a bit operations library for Lua 5.1. | |
4225 ** http://bitop.luajit.org/ | |
4226 ** | |
4227 ** Copyright (C) 2008-2009 Mike Pall. All rights reserved. | |
4228 ** | |
4229 ** Permission is hereby granted, free of charge, to any person obtaining | |
4230 ** a copy of this software and associated documentation files (the | |
4231 ** "Software"), to deal in the Software without restriction, including | |
4232 ** without limitation the rights to use, copy, modify, merge, publish, | |
4233 ** distribute, sublicense, and/or sell copies of the Software, and to | |
4234 ** permit persons to whom the Software is furnished to do so, subject to | |
4235 ** the following conditions: | |
4236 ** | |
4237 ** The above copyright notice and this permission notice shall be | |
4238 ** included in all copies or substantial portions of the Software. | |
4239 ** | |
4240 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
4241 ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
4242 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
4243 ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |
4244 ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
4245 ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
4246 ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
4247 ** | |
4248 ** [ MIT license: http://www.opensource.org/licenses/mit-license.php ] | |
4249 */ | |
4250 | |
4251 #ifdef _MSC_VER | |
4252 /* MSVC is stuck in the last century and doesn't have C99's stdint.h. */ | |
4253 typedef __int32 int32_t; | |
4254 typedef unsigned __int32 uint32_t; | |
4255 typedef unsigned __int64 uint64_t; | |
4256 #else | |
4257 #include <stdint.h> | |
4258 #endif | |
4259 | |
4260 typedef int32_t SBits; | |
4261 typedef uint32_t UBits; | |
4262 | |
4263 typedef union | |
4264 { | |
4265 lua_Number n; | |
4266 #ifdef LUA_NUMBER_DOUBLE | |
4267 uint64_t b; | |
4268 #else | |
4269 UBits b; | |
4270 #endif | |
4271 } BitNum; | |
4272 | |
4273 /* Convert argument to bit type. */ | |
4274 static UBits barg(lua_State *L, int idx) | |
4275 { | |
4276 BitNum bn; | |
4277 UBits b; | |
4278 bn.n = lua_tonumber(L, idx); | |
4279 #if defined(LUA_NUMBER_DOUBLE) | |
4280 bn.n += 6755399441055744.0; /* 2^52+2^51 */ | |
4281 #ifdef SWAPPED_DOUBLE | |
4282 b = (UBits)(bn.b >> 32); | |
4283 #else | |
4284 b = (UBits)(bn.b & 0xffffffff); | |
4285 #endif | |
4286 #elif defined(LUA_NUMBER_INT) || defined(LUA_NUMBER_LONG) || \ | |
4287 defined(LUA_NUMBER_LONGLONG) || defined(LUA_NUMBER_LONG_LONG) || \ | |
4288 defined(LUA_NUMBER_LLONG) | |
4289 if (sizeof(UBits) == sizeof(lua_Number)) | |
4290 b = bn.b; | |
4291 else | |
4292 b = (UBits)(SBits)bn.n; | |
4293 #elif defined(LUA_NUMBER_FLOAT) | |
4294 #error "A 'float' lua_Number type is incompatible with this library" | |
4295 #else | |
4296 #error "Unknown number type, check LUA_NUMBER_* in luaconf.h" | |
4297 #endif | |
4298 if (b == 0 && !lua_isnumber(L, idx)) | |
4299 luaL_typerror(L, idx, "number"); | |
4300 return b; | |
4301 } | |
4302 | |
4303 /* Return bit type. */ | |
4304 #define BRET(b) lua_pushnumber(L, (lua_Number)(SBits)(b)); return 1; | |
4305 | |
4306 static int bit_tobit(lua_State *L) { BRET(barg(L, 1)) } | |
4307 static int bit_bnot(lua_State *L) { BRET(~barg(L, 1)) } | |
4308 | |
4309 #define BIT_OP(func, opr) \ | |
4310 static int func(lua_State * L) { int i; UBits b = barg(L, 1); \ | |
4311 for (i = lua_gettop(L); i > 1; i--) \ | |
4312 b opr barg(L, i); BRET(b) } | |
4313 BIT_OP(bit_band, &= ) | |
4314 BIT_OP(bit_bor, |= ) | |
4315 BIT_OP(bit_bxor, ^= ) | |
4316 | |
4317 #define bshl(b, n) (b << n) | |
4318 #define bshr(b, n) (b >> n) | |
4319 #define bsar(b, n) ((SBits)b >> n) | |
4320 #define brol(b, n) ((b << n) | (b >> (32 - n))) | |
4321 #define bror(b, n) ((b << (32 - n)) | (b >> n)) | |
4322 #define BIT_SH(func, fn) \ | |
4323 static int func(lua_State * L) { \ | |
4324 UBits b = barg(L, 1); UBits n = barg(L, 2) & 31; BRET(fn(b, n)) } | |
4325 BIT_SH(bit_lshift, bshl) | |
4326 BIT_SH(bit_rshift, bshr) | |
4327 BIT_SH(bit_arshift, bsar) | |
4328 BIT_SH(bit_rol, brol) | |
4329 BIT_SH(bit_ror, bror) | |
4330 | |
4331 static int bit_bswap(lua_State *L) | |
4332 { | |
4333 UBits b = barg(L, 1); | |
4334 b = (b >> 24) | ((b >> 8) & 0xff00) | ((b & 0xff00) << 8) | (b << 24); | |
4335 BRET(b) | |
4336 } | |
4337 | |
4338 static int bit_tohex(lua_State *L) | |
4339 { | |
4340 UBits b = barg(L, 1); | |
4341 SBits n = lua_isnone(L, 2) ? 8 : (SBits)barg(L, 2); | |
4342 const char *hexdigits = "0123456789abcdef"; | |
4343 char buf[8]; | |
4344 int i; | |
4345 if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; } | |
4346 if (n > 8) n = 8; | |
4347 for (i = (int)n; --i >= 0; ) | |
4348 { | |
4349 buf[i] = hexdigits[b & 15]; b >>= 4; | |
4350 } | |
4351 lua_pushlstring(L, buf, (size_t)n); | |
4352 return 1; | |
4353 } | |
4354 | |
4355 static const struct luaL_Reg bit_funcs[] = { | |
4356 { "tobit", bit_tobit }, | |
4357 { "bnot", bit_bnot }, | |
4358 { "band", bit_band }, | |
4359 { "bor", bit_bor }, | |
4360 { "bxor", bit_bxor }, | |
4361 { "lshift", bit_lshift }, | |
4362 { "rshift", bit_rshift }, | |
4363 { "arshift", bit_arshift }, | |
4364 { "rol", bit_rol }, | |
4365 { "ror", bit_ror }, | |
4366 { "bswap", bit_bswap }, | |
4367 { "tohex", bit_tohex }, | |
4368 { NULL, NULL } | |
4369 }; | |
4370 | |
4371 /* Signed right-shifts are implementation-defined per C89/C99. | |
4372 ** But the de facto standard are arithmetic right-shifts on two's | |
4373 ** complement CPUs. This behaviour is required here, so test for it. | |
4374 */ | |
4375 #define BAD_SAR (bsar(-8, 2) != (SBits) - 2) | |
4376 | |
4377 bool luabitop_validate(lua_State *L) // originally named as luaopen_bit | |
4378 { | |
4379 UBits b; | |
4380 lua_pushnumber(L, (lua_Number)1437217655L); | |
4381 b = barg(L, -1); | |
4382 if (b != (UBits)1437217655L || BAD_SAR) /* Perform a simple self-test. */ | |
4383 { | |
4384 const char *msg = "compiled with incompatible luaconf.h"; | |
4385 #ifdef LUA_NUMBER_DOUBLE | |
4386 #ifdef WIN32 | |
4387 if (b == (UBits)1610612736L) | |
4388 msg = "use D3DCREATE_FPU_PRESERVE with DirectX"; | |
4389 #endif | |
4390 if (b == (UBits)1127743488L) | |
4391 msg = "not compiled with SWAPPED_DOUBLE"; | |
4392 #endif | |
4393 if (BAD_SAR) | |
4394 msg = "arithmetic right-shift broken"; | |
4395 luaL_error(L, "bit library self-test failed (%s)", msg); | |
4396 return false; | |
4397 } | |
4398 return true; | |
4399 } | |
4400 | |
4401 // LuaBitOp ends here | |
4402 | |
4403 static int bit_bshift_emulua(lua_State *L) | |
4404 { | |
4405 int shift = luaL_checkinteger(L, 2); | |
4406 if (shift < 0) | |
4407 { | |
4408 lua_pushinteger(L, -shift); | |
4409 lua_replace(L, 2); | |
4410 return bit_lshift(L); | |
4411 } | |
4412 else | |
4413 return bit_rshift(L); | |
4414 } | |
4415 | |
4416 static int bitbit(lua_State *L) | |
4417 { | |
4418 int rv = 0; | |
4419 int numArgs = lua_gettop(L); | |
4420 for (int i = 1; i <= numArgs; i++) | |
4421 { | |
4422 int where = luaL_checkinteger(L, i); | |
4423 if (where >= 0 && where < 32) | |
4424 rv |= (1 << where); | |
4425 } | |
4426 lua_settop(L, 0); | |
4427 BRET(rv); | |
4428 } | |
4429 | |
4430 // The function called periodically to ensure Lua doesn't run amok. | |
4431 static void VBALuaHookFunction(lua_State *L, lua_Debug *dbg) | |
4432 { | |
4433 if (numTries-- == 0) | |
4434 { | |
4435 int kill = 0; | |
4436 | |
4437 #if (defined(WIN32) && !defined(SDL)) | |
4438 // Uh oh | |
4439 theApp.winCheckFullscreen(); | |
4440 systemSoundClearBuffer(); | |
4441 int ret = AfxGetApp()->m_pMainWnd->MessageBox( | |
4442 "The Lua script running has been running a long time. It may have gone crazy. Kill it?\n\n(No = don't check anymore either)", | |
4443 "Lua Script Gone Nuts?", | |
4444 MB_YESNO); | |
4445 | |
4446 if (ret == IDYES) | |
4447 { | |
4448 kill = 1; | |
4449 } | |
4450 | |
4451 #else | |
4452 fprintf( | |
4453 stderr, | |
4454 "The Lua script running has been running a long time.\nIt may have gone crazy. Kill it? (I won't ask again if you say No)\n"); | |
4455 | |
4456 char buffer[64]; | |
4457 while (true) | |
4458 { | |
4459 fprintf(stderr, "(y/n): "); | |
4460 fgets(buffer, sizeof(buffer), stdin); | |
4461 if (buffer[0] == 'y' || buffer[0] == 'Y') | |
4462 { | |
4463 kill = 1; | |
4464 break; | |
4465 } | |
4466 | |
4467 if (buffer[0] == 'n' || buffer[0] == 'N') | |
4468 break; | |
4469 } | |
4470 #endif | |
4471 if (kill) | |
4472 { | |
4473 luaL_error(L, "Killed by user request."); | |
4474 VBALuaOnStop(); | |
4475 } | |
4476 | |
4477 // else, kill the debug hook. | |
4478 lua_sethook(L, NULL, 0, 0); | |
4479 } | |
4480 } | |
4481 | |
4482 static const struct luaL_reg vbalib[] = { | |
4483 // {"speedmode", vba_speedmode}, // TODO: NYI | |
4484 { "frameadvance", vba_frameadvance }, | |
4485 { "pause", vba_pause }, | |
4486 { "framecount", vba_framecount }, | |
4487 { "lagcount", vba_getlagcount }, | |
4488 { "lagged", vba_lagged }, | |
4489 { "emulating", vba_emulating }, | |
4490 { "registerbefore", vba_registerbefore }, | |
4491 { "registerafter", vba_registerafter }, | |
4492 { "registerexit", vba_registerexit }, | |
4493 { "message", vba_message }, | |
4494 { "print", print }, // sure, why not | |
4495 { NULL, NULL } | |
4496 }; | |
4497 | |
4498 static const struct luaL_reg memorylib[] = { | |
4499 { "readbyte", memory_readbyte }, | |
4500 { "readbytesigned", memory_readbytesigned }, | |
4501 { "readword", memory_readword }, | |
4502 { "readwordsigned", memory_readwordsigned }, | |
4503 { "readdword", memory_readdword }, | |
4504 { "readdwordsigned", memory_readdwordsigned }, | |
4505 { "readbyterange", memory_readbyterange }, | |
4506 { "writebyte", memory_writebyte }, | |
4507 { "writeword", memory_writeword }, | |
4508 { "writedword", memory_writedword }, | |
4509 { "getregister", memory_getregister }, | |
4510 { "setregister", memory_setregister }, | |
4511 { "gbromreadbyte", memory_gbromreadbyte }, | |
4512 { "gbromreadbytesigned", memory_gbromreadbytesigned }, | |
4513 { "gbromreadword", memory_gbromreadword }, | |
4514 { "gbromreadwordsigned", memory_gbromreadwordsigned }, | |
4515 { "gbromreaddword", memory_gbromreaddword }, | |
4516 { "gbromreaddwordsigned", memory_gbromreaddwordsigned }, | |
4517 { "gbromreadbyterange", memory_gbromreadbyterange }, | |
4518 | |
4519 // alternate naming scheme for word and double-word and unsigned | |
4520 { "readbyteunsigned", memory_readbyte }, | |
4521 { "readwordunsigned", memory_readword }, | |
4522 { "readdwordunsigned", memory_readdword }, | |
4523 { "readshort", memory_readword }, | |
4524 { "readshortunsigned", memory_readword }, | |
4525 { "readshortsigned", memory_readwordsigned }, | |
4526 { "readlong", memory_readdword }, | |
4527 { "readlongunsigned", memory_readdword }, | |
4528 { "readlongsigned", memory_readdwordsigned }, | |
4529 { "writeshort", memory_writeword }, | |
4530 { "writelong", memory_writedword }, | |
4531 { "gbromreadbyteunsigned", memory_gbromreadbyte }, | |
4532 { "gbromreadwordunsigned", memory_gbromreadword }, | |
4533 { "gbromreaddwordunsigned", memory_gbromreaddword }, | |
4534 { "gbromreadshort", memory_gbromreadword }, | |
4535 { "gbromreadshortunsigned", memory_gbromreadword }, | |
4536 { "gbromreadshortsigned", memory_gbromreadwordsigned }, | |
4537 { "gbromreadlong", memory_gbromreaddword }, | |
4538 { "gbromreadlongunsigned", memory_gbromreaddword }, | |
4539 { "gbromreadlongsigned", memory_gbromreaddwordsigned }, | |
4540 | |
4541 // memory hooks | |
4542 { "registerwrite", memory_registerwrite }, | |
4543 //{"registerread", memory_registerread}, | |
4544 { "registerexec", memory_registerexec }, | |
4545 // alternate names | |
4546 { "register", memory_registerwrite }, | |
4547 { "registerrun", memory_registerexec }, | |
4548 { "registerexecute", memory_registerexec }, | |
4549 | |
4550 { NULL, NULL } | |
4551 }; | |
4552 | |
4553 static const struct luaL_reg joypadlib[] = { | |
4554 { "get", joypad_get }, | |
4555 { "getdown", joypad_getdown }, | |
4556 { "getup", joypad_getup }, | |
4557 { "set", joypad_set }, | |
4558 | |
4559 // alternative names | |
4560 { "read", joypad_get }, | |
4561 { "write", joypad_set }, | |
4562 { "readdown", joypad_getdown }, | |
4563 { "readup", joypad_getup }, | |
4564 { NULL, NULL } | |
4565 }; | |
4566 | |
4567 static const struct luaL_reg savestatelib[] = { | |
4568 { "create", savestate_create }, | |
4569 { "save", savestate_save }, | |
4570 { "load", savestate_load }, | |
4571 | |
4572 { NULL, NULL } | |
4573 }; | |
4574 | |
4575 static const struct luaL_reg movielib[] = { | |
4576 { "active", movie_isactive }, | |
4577 { "recording", movie_isrecording }, | |
4578 { "playing", movie_isplaying }, | |
4579 { "mode", movie_getmode }, | |
4580 | |
4581 { "length", movie_getlength }, | |
4582 { "author", movie_getauthor }, | |
4583 { "name", movie_getfilename }, | |
4584 { "rerecordcount", movie_rerecordcount }, | |
4585 { "setrerecordcount", movie_setrerecordcount }, | |
4586 | |
4587 { "rerecordcounting", movie_rerecordcounting }, | |
4588 { "framecount", vba_framecount }, // for those familiar with | |
4589 // other emulators that have | |
4590 // movie.framecount() | |
4591 // instead of | |
4592 // emulatorname.framecount() | |
4593 | |
4594 { "stop", movie_stop }, | |
4595 | |
4596 // alternative names | |
4597 { "close", movie_stop }, | |
4598 { "getauthor", movie_getauthor }, | |
4599 { "getname", movie_getfilename }, | |
4600 { NULL, NULL } | |
4601 }; | |
4602 | |
4603 static const struct luaL_reg guilib[] = { | |
4604 { "register", gui_register }, | |
4605 { "text", gui_text }, | |
4606 { "box", gui_drawbox }, | |
4607 { "line", gui_drawline }, | |
4608 { "pixel", gui_drawpixel }, | |
4609 { "opacity", gui_setopacity }, | |
4610 { "transparency", gui_transparency }, | |
4611 { "popup", gui_popup }, | |
4612 { "parsecolor", gui_parsecolor }, | |
4613 { "gdscreenshot", gui_gdscreenshot }, | |
4614 { "gdoverlay", gui_gdoverlay }, | |
4615 { "getpixel", gui_getpixel }, | |
4616 | |
4617 // alternative names | |
4618 { "drawtext", gui_text }, | |
4619 { "drawbox", gui_drawbox }, | |
4620 { "drawline", gui_drawline }, | |
4621 { "drawpixel", gui_drawpixel }, | |
4622 { "setpixel", gui_drawpixel }, | |
4623 { "writepixel", gui_drawpixel }, | |
4624 { "rect", gui_drawbox }, | |
4625 { "drawrect", gui_drawbox }, | |
4626 { "drawimage", gui_gdoverlay }, | |
4627 { "image", gui_gdoverlay }, | |
4628 { "readpixel", gui_getpixel }, | |
4629 { NULL, NULL } | |
4630 }; | |
4631 | |
4632 static const struct luaL_reg inputlib[] = { | |
4633 { "get", input_getcurrentinputstatus }, | |
4634 | |
4635 // alternative names | |
4636 { "read", input_getcurrentinputstatus }, | |
4637 { NULL, NULL } | |
4638 }; | |
4639 | |
4640 static const struct luaL_reg soundlib[] = { | |
4641 { "get", sound_get }, | |
4642 | |
4643 // alternative names | |
4644 { NULL, NULL } | |
4645 }; | |
4646 | |
4647 // gocha: since vba dumps avi so badly, | |
4648 // I add avilib as a workaround for enhanced video encoding. | |
4649 static const struct luaL_reg avilib[] = { | |
4650 { "framecount", avi_framecount }, | |
4651 { "pause", avi_pause }, | |
4652 { "resume", avi_resume }, | |
4653 { NULL, NULL } | |
4654 }; | |
4655 | |
4656 void CallExitFunction(void) | |
4657 { | |
4658 if (!LUA) | |
4659 return; | |
4660 | |
4661 lua_settop(LUA, 0); | |
4662 lua_getfield(LUA, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]); | |
4663 | |
4664 int errorcode = 0; | |
4665 if (lua_isfunction(LUA, -1)) | |
4666 { | |
4667 errorcode = lua_pcall(LUA, 0, 0, 0); | |
4668 } | |
4669 | |
4670 if (errorcode) | |
4671 HandleCallbackError(LUA); | |
4672 } | |
4673 | |
4674 void VBALuaFrameBoundary(void) | |
4675 { | |
4676 // printf("Lua Frame\n"); | |
4677 | |
4678 lua_joypads_used = 0; | |
4679 | |
4680 // HA! | |
4681 if (!LUA || !luaRunning) | |
4682 return; | |
4683 | |
4684 // Our function needs calling | |
4685 lua_settop(LUA, 0); | |
4686 lua_getfield(LUA, LUA_REGISTRYINDEX, frameAdvanceThread); | |
4687 | |
4688 lua_State *thread = lua_tothread(LUA, 1); | |
4689 | |
4690 // Lua calling C must know that we're busy inside a frame boundary | |
4691 frameBoundary = true; | |
4692 frameAdvanceWaiting = false; | |
4693 | |
4694 numTries = 1000; | |
4695 | |
4696 int result = lua_resume(thread, 0); | |
4697 | |
4698 if (result == LUA_YIELD) | |
4699 { | |
4700 // Okay, we're fine with that. | |
4701 } | |
4702 else if (result != 0) | |
4703 { | |
4704 // Done execution by bad causes | |
4705 VBALuaOnStop(); | |
4706 lua_pushnil(LUA); | |
4707 lua_setfield(LUA, LUA_REGISTRYINDEX, frameAdvanceThread); | |
4708 lua_pushnil(LUA); | |
4709 lua_setfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable); | |
4710 | |
4711 // Error? | |
4712 //#if (defined(WIN32) && !defined(SDL)) | |
4713 // info_print(info_uid, lua_tostring(thread, -1)); //Clear_Sound_Buffer(); | |
4714 // AfxGetApp()->m_pMainWnd->MessageBox(lua_tostring(thread, -1), "Lua run error", MB_OK | MB_ICONSTOP); | |
4715 //#else | |
4716 // fprintf(stderr, "Lua thread bombed out: %s\n", lua_tostring(thread, -1)); | |
4717 //#endif | |
4718 printerror(thread, -1); | |
4719 } | |
4720 else | |
4721 { | |
4722 VBALuaOnStop(); | |
4723 printf("Script died of natural causes.\n"); | |
4724 } | |
4725 | |
4726 // Past here, VBA actually runs, so any Lua code is called mid-frame. We must | |
4727 // not do anything too stupid, so let ourselves know. | |
4728 frameBoundary = false; | |
4729 | |
4730 if (!frameAdvanceWaiting) | |
4731 { | |
4732 VBALuaOnStop(); | |
4733 } | |
4734 } | |
4735 | |
4736 /** | |
4737 * Loads and runs the given Lua script. | |
4738 * The emulator MUST be paused for this function to be | |
4739 * called. Otherwise, all frame boundary assumptions go out the window. | |
4740 * | |
4741 * Returns true on success, false on failure. | |
4742 */ | |
4743 int VBALoadLuaCode(const char *filename) | |
4744 { | |
4745 static bool sfmtInitialized = false; | |
4746 if (!sfmtInitialized) | |
4747 { | |
4748 init_gen_rand((unsigned)time(NULL)); | |
4749 sfmtInitialized = true; | |
4750 } | |
4751 | |
4752 if (filename != luaScriptName) | |
4753 { | |
4754 if (luaScriptName) | |
4755 free(luaScriptName); | |
4756 luaScriptName = strdup(filename); | |
4757 } | |
4758 | |
4759 //stop any lua we might already have had running | |
4760 VBALuaStop(); | |
4761 | |
4762 // Set current directory from filename (for dofile) | |
4763 char dir[_MAX_PATH]; | |
4764 char *slash, *backslash; | |
4765 strcpy(dir, filename); | |
4766 slash = strrchr(dir, '/'); | |
4767 backslash = strrchr(dir, '\\'); | |
4768 if (!slash || (backslash && backslash < slash)) | |
4769 slash = backslash; | |
4770 if (slash) | |
4771 { | |
4772 slash[1] = '\0'; // keep slash itself for some reasons | |
4773 chdir(dir); | |
4774 } | |
4775 | |
4776 if (!LUA) | |
4777 { | |
4778 LUA = lua_open(); | |
4779 luaL_openlibs(LUA); | |
4780 | |
4781 luaL_register(LUA, "emu", vbalib); // added for better cross-emulator compatibility | |
4782 luaL_register(LUA, "vba", vbalib); // kept for backward compatibility | |
4783 luaL_register(LUA, "memory", memorylib); | |
4784 luaL_register(LUA, "joypad", joypadlib); | |
4785 luaL_register(LUA, "savestate", savestatelib); | |
4786 luaL_register(LUA, "movie", movielib); | |
4787 luaL_register(LUA, "gui", guilib); | |
4788 luaL_register(LUA, "input", inputlib); | |
4789 luaL_register(LUA, "sound", soundlib); | |
4790 luaL_register(LUA, "bit", bit_funcs); // LuaBitOp library | |
4791 luaL_register(LUA, "avi", avilib); // workaround for enhanced video encoding | |
4792 lua_settop(LUA, 0); // clean the stack, because each call to luaL_register leaves a table on top | |
4793 | |
4794 // register a few utility functions outside of libraries (in the global namespace) | |
4795 lua_register(LUA, "print", print); | |
4796 lua_register(LUA, "tostring", tostring); | |
4797 lua_register(LUA, "addressof", addressof); | |
4798 lua_register(LUA, "copytable", copytable); | |
4799 | |
4800 // old bit operation functions | |
4801 lua_register(LUA, "AND", bit_band); | |
4802 lua_register(LUA, "OR", bit_bor); | |
4803 lua_register(LUA, "XOR", bit_bxor); | |
4804 lua_register(LUA, "SHIFT", bit_bshift_emulua); | |
4805 lua_register(LUA, "BIT", bitbit); | |
4806 | |
4807 luabitop_validate(LUA); | |
4808 | |
4809 lua_pushstring(LUA, "math"); | |
4810 lua_gettable(LUA, LUA_GLOBALSINDEX); | |
4811 lua_pushcfunction(LUA, sfmt_random); | |
4812 lua_setfield(LUA, -2, "random"); | |
4813 lua_pushcfunction(LUA, sfmt_randomseed); | |
4814 lua_setfield(LUA, -2, "randomseed"); | |
4815 lua_settop(LUA, 0); | |
4816 | |
4817 // push arrays for storing hook functions in | |
4818 for (int i = 0; i < LUAMEMHOOK_COUNT; i++) | |
4819 { | |
4820 lua_newtable(LUA); | |
4821 lua_setfield(LUA, LUA_REGISTRYINDEX, luaMemHookTypeStrings[i]); | |
4822 } | |
4823 } | |
4824 | |
4825 // We make our thread NOW because we want it at the bottom of the stack. | |
4826 // If all goes wrong, we let the garbage collector remove it. | |
4827 lua_State *thread = lua_newthread(LUA); | |
4828 | |
4829 // Load the data | |
4830 int result = luaL_loadfile(LUA, filename); | |
4831 | |
4832 if (result) | |
4833 { | |
4834 //#if (defined(WIN32) && !defined(SDL)) | |
4835 // info_print(info_uid, lua_tostring(LUA, -1)); //Clear_Sound_Buffer(); | |
4836 // AfxGetApp()->m_pMainWnd->MessageBox(lua_tostring(LUA, -1), "Lua load error", MB_OK | MB_ICONSTOP); | |
4837 //#else | |
4838 // fprintf(stderr, "Failed to compile file: %s\n", lua_tostring(LUA, -1)); | |
4839 //#endif | |
4840 printerror(LUA, -1); | |
4841 | |
4842 // Wipe the stack. Our thread | |
4843 lua_settop(LUA, 0); | |
4844 return 0; // Oh shit. | |
4845 } | |
4846 | |
4847 // Get our function into it | |
4848 lua_xmove(LUA, thread, 1); | |
4849 | |
4850 // Save the thread to the registry. This is why I make the thread FIRST. | |
4851 lua_setfield(LUA, LUA_REGISTRYINDEX, frameAdvanceThread); | |
4852 | |
4853 // Initialize settings | |
4854 luaRunning = true; | |
4855 skipRerecords = false; | |
4856 numMemHooks = 0; | |
4857 transparencyModifier = 255; // opaque | |
4858 lua_joypads_used = 0; // not used | |
4859 //wasPaused = systemIsPaused(); | |
4860 //systemSetPause(false); | |
4861 | |
4862 // Set up our protection hook to be executed once every 10,000 bytecode instructions. | |
4863 lua_sethook(thread, VBALuaHookFunction, LUA_MASKCOUNT, 10000); | |
4864 | |
4865 #ifdef WIN32 | |
4866 info_print = PrintToWindowConsole; | |
4867 info_onstart = WinLuaOnStart; | |
4868 info_onstop = WinLuaOnStop; | |
4869 if (!LuaConsoleHWnd) | |
4870 LuaConsoleHWnd = CreateDialog(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDD_LUA), | |
4871 AfxGetMainWnd()->GetSafeHwnd(), (DLGPROC) DlgLuaScriptDialog); | |
4872 info_uid = (int)LuaConsoleHWnd; | |
4873 #else | |
4874 info_print = NULL; | |
4875 info_onstart = NULL; | |
4876 info_onstop = NULL; | |
4877 #endif | |
4878 if (info_onstart) | |
4879 info_onstart(info_uid); | |
4880 | |
4881 // And run it right now. :) | |
4882 VBALuaFrameBoundary(); | |
4883 systemRenderFrame(); | |
4884 | |
4885 // We're done. | |
4886 return 1; | |
4887 } | |
4888 | |
4889 /** | |
4890 * Equivalent to repeating the last VBALoadLuaCode() call. | |
4891 */ | |
4892 int VBAReloadLuaCode(void) | |
4893 { | |
4894 if (!luaScriptName) | |
4895 { | |
4896 systemScreenMessage("There's no script to reload."); | |
4897 return 0; | |
4898 } | |
4899 else | |
4900 return VBALoadLuaCode(luaScriptName); | |
4901 } | |
4902 | |
4903 /** | |
4904 * Terminates a running Lua script by killing the whole Lua engine. | |
4905 * | |
4906 * Always safe to call, except from within a lua call itself (duh). | |
4907 * | |
4908 */ | |
4909 void VBALuaStop(void) | |
4910 { | |
4911 //already killed | |
4912 if (!LUA) | |
4913 return; | |
4914 | |
4915 //execute the user's shutdown callbacks | |
4916 CallExitFunction(); | |
4917 | |
4918 /*info.*/ numMemHooks = 0; | |
4919 for (int i = 0; i < LUAMEMHOOK_COUNT; i++) | |
4920 CalculateMemHookRegions((LuaMemHookType)i); | |
4921 | |
4922 //sometimes iup uninitializes com | |
4923 //MBG TODO - test whether this is really necessary. i dont think it is | |
4924 #if (defined(WIN32) && !defined(SDL)) | |
4925 CoInitialize(0); | |
4926 #endif | |
4927 | |
4928 if (info_onstop) | |
4929 info_onstop(info_uid); | |
4930 | |
4931 //lua_gc(LUA,LUA_GCCOLLECT,0); | |
4932 lua_close(LUA); // this invokes our garbage collectors for us | |
4933 LUA = NULL; | |
4934 VBALuaOnStop(); | |
4935 } | |
4936 | |
4937 /** | |
4938 * Returns true if there is a Lua script running. | |
4939 * | |
4940 */ | |
4941 int VBALuaRunning(void) | |
4942 { | |
4943 // FIXME: return false when no callback functions are registered. | |
4944 return (int) (LUA != NULL); // should return true if callback functions are active. | |
4945 } | |
4946 | |
4947 /** | |
4948 * Returns true if Lua would like to steal the given joypad control. | |
4949 * | |
4950 * Range is 0 through 3 | |
4951 */ | |
4952 int VBALuaUsingJoypad(int which) | |
4953 { | |
4954 if (which < 0 || which > 3) | |
4955 which = systemGetDefaultJoypad(); | |
4956 return lua_joypads_used & (1 << which); | |
4957 } | |
4958 | |
4959 /** | |
4960 * Reads the buttons Lua is feeding for the given joypad, in the same | |
4961 * format as the OS-specific code. | |
4962 * | |
4963 * <del>This function must not be called more than once per frame. </del>Ideally exactly once | |
4964 * per frame (if VBALuaUsingJoypad says it's safe to do so) | |
4965 */ | |
4966 int VBALuaReadJoypad(int which) | |
4967 { | |
4968 if (which < 0 || which > 3) | |
4969 which = systemGetDefaultJoypad(); | |
4970 | |
4971 //lua_joypads_used &= ~(1 << which); | |
4972 return lua_joypads[which]; | |
4973 } | |
4974 | |
4975 /** | |
4976 * If this function returns true, the movie code should NOT increment | |
4977 * the rerecord count for a load-state. | |
4978 * | |
4979 * This function will not return true if a script is not running. | |
4980 */ | |
4981 bool8 VBALuaRerecordCountSkip(void) | |
4982 { | |
4983 // FIXME: return true if (there are any active callback functions && skipRerecords) | |
4984 return LUA && luaRunning && skipRerecords; | |
4985 } | |
4986 | |
4987 /** | |
4988 * Given a screen with the indicated resolution, | |
4989 * draw the current GUI onto it. | |
4990 */ | |
4991 void VBALuaGui(uint8 *screen, int ppl, int width, int height) | |
4992 { | |
4993 if (!LUA /* || !luaRunning*/) | |
4994 return; | |
4995 | |
4996 // First, check if we're being called by anybody | |
4997 lua_getfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable); | |
4998 | |
4999 if (lua_isfunction(LUA, -1)) | |
5000 { | |
5001 // We call it now | |
5002 numTries = 1000; | |
5003 | |
5004 int ret = lua_pcall(LUA, 0, 0, 0); | |
5005 if (ret != 0) | |
5006 { | |
5007 // This is grounds for trashing the function | |
5008 // Note: This must be done before the messagebox pops up, | |
5009 // otherwise the messagebox will cause a paint event which causes a weird | |
5010 // infinite call sequence that makes Snes9x silently exit with error code 3, | |
5011 // if a Lua GUI function crashes. (nitsuja) | |
5012 lua_pushnil(LUA); | |
5013 lua_setfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable); | |
5014 | |
5015 //#if (defined(WIN32) && !defined(SDL)) | |
5016 // info_print(info_uid, lua_tostring(LUA, -1)); //AfxGetApp()->m_pMainWnd->MessageBox(lua_tostring(LUA, -1), "Lua Error | |
5017 // in GUI function", MB_OK); | |
5018 //#else | |
5019 // fprintf(stderr, "Lua error in gui.register function: %s\n", lua_tostring(LUA, -1)); | |
5020 //#endif | |
5021 printerror(LUA, -1); | |
5022 } | |
5023 } | |
5024 | |
5025 // And wreak the stack | |
5026 lua_settop(LUA, 0); | |
5027 | |
5028 if (!gui_used) | |
5029 return; | |
5030 | |
5031 gui_used = false; | |
5032 | |
5033 int x, y; | |
5034 | |
5035 //int pitch = (((ppl * systemColorDepth + 7)>>3)+3)&~3; | |
5036 int pitch = ppl * (systemColorDepth / 8) + (systemColorDepth == 24 ? 0 : 4); | |
5037 | |
5038 if (width > LUA_SCREEN_WIDTH) | |
5039 width = LUA_SCREEN_WIDTH; | |
5040 if (height > LUA_SCREEN_HEIGHT) | |
5041 height = LUA_SCREEN_HEIGHT; | |
5042 | |
5043 GetColorFunc getColor; | |
5044 SetColorFunc setColor; | |
5045 getColorIOFunc(systemColorDepth, &getColor, &setColor); | |
5046 | |
5047 for (y = 0; y < height; y++) | |
5048 { | |
5049 uint8 *scr = &screen[y * pitch]; | |
5050 for (x = 0; x < width; x++, scr += systemColorDepth / 8) | |
5051 { | |
5052 const uint8 gui_alpha = gui_data[(y * LUA_SCREEN_WIDTH + x) * 4 + 3]; | |
5053 if (gui_alpha == 0) | |
5054 { | |
5055 // do nothing | |
5056 continue; | |
5057 } | |
5058 | |
5059 const uint8 gui_red = gui_data[(y * LUA_SCREEN_WIDTH + x) * 4 + 2]; | |
5060 const uint8 gui_green = gui_data[(y * LUA_SCREEN_WIDTH + x) * 4 + 1]; | |
5061 const uint8 gui_blue = gui_data[(y * LUA_SCREEN_WIDTH + x) * 4]; | |
5062 int red, green, blue; | |
5063 | |
5064 if (gui_alpha == 255) | |
5065 { | |
5066 // direct copy | |
5067 red = gui_red; | |
5068 green = gui_green; | |
5069 blue = gui_blue; | |
5070 } | |
5071 else | |
5072 { | |
5073 // alpha-blending | |
5074 uint8 scr_red, scr_green, scr_blue; | |
5075 getColor(scr, &scr_red, &scr_green, &scr_blue); | |
5076 red = (((int)gui_red - scr_red) * gui_alpha / 255 + scr_red) & 255; | |
5077 green = (((int)gui_green - scr_green) * gui_alpha / 255 + scr_green) & 255; | |
5078 blue = (((int)gui_blue - scr_blue) * gui_alpha / 255 + scr_blue) & 255; | |
5079 } | |
5080 | |
5081 setColor(scr, (uint8) red, (uint8) green, (uint8) blue); | |
5082 } | |
5083 } | |
5084 | |
5085 return; | |
5086 } | |
5087 | |
5088 void VBALuaClearGui(void) | |
5089 { | |
5090 gui_used = false; | |
5091 } | |
5092 | |
5093 lua_State *VBAGetLuaState() | |
5094 { | |
5095 return LUA; | |
5096 } | |
5097 | |
5098 char *VBAGetLuaScriptName() | |
5099 { | |
5100 return luaScriptName; | |
5101 } | |
5102 |