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