annotate src/common/lua-engine.cpp @ 534:3f2072b8e457

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