/* * * Singe 2 * Copyright (C) 2006-2024 Scott Duensing * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include "include/SDL2/SDL.h" #include "include/SDL2/SDL_image.h" #include "include/SDL2/SDL_mixer.h" #include "include/SDL2/SDL_ttf.h" #include "../thirdparty/lua/src/lua.h" #include "../thirdparty/lua/src/lualib.h" #include "../thirdparty/lua/src/lauxlib.h" #include "../thirdparty/uthash/src/uthash.h" #include "../thirdparty/manymouse/manymouse.h" #include "../thirdparty/luafilesystem/src/lfs.h" #include "../thirdparty/luasec/src/context.h" #include "../thirdparty/luasec/src/ssl.h" #include "../thirdparty/luasec/src/x509.h" #include "../thirdparty/luasocket/src/luasocket.h" #include "../thirdparty/luasocket/src/mime.h" #ifndef _WIN32 #include "../thirdparty/luasocket/src/unix.h" // There is no serial.h, so fake it. LUASOCKET_API int luaopen_socket_serial(lua_State *L); #endif // There is no header for rs232 binding. Make our own. int luaopen_luars232(lua_State *L); // There is no header for ssl.config binding. Make our own. LSEC_API int luaopen_ssl_config(lua_State *L); #include "main.h" #include "util.h" #include "frameFile.h" #include "videoPlayer.h" #include "singe.h" #include "embedded.h" //#define DEBUG_TOOLS #define AUDIO_MAX_VOLUME 63 #define MAX_TITLE_LENGTH 1024 #define MAX_MICE 4 #define MOUSE_AXIS_COUNT 2 #define MAX_CONTROLLERS 4 #define CONTROLLER_AXIS_COUNT 6 #define AXIS_COUNT (MAX_CONTROLLERS * CONTROLLER_AXIS_COUNT + MAX_MICE * MOUSE_AXIS_COUNT) #define AXIS_KEY_DOWN 0 #define AXIS_KEY_UP 1 typedef struct MouseS { int32_t x; int32_t y; int32_t relx; int32_t rely; char name[64]; bool connected; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpadded" Uint32 buttons; #pragma GCC diagnostic pop Uint32 scrolluptick; Uint32 scrolldowntick; Uint32 scrolllefttick; Uint32 scrollrighttick; } MouseT; typedef struct SpriteS { int32_t id; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpadded" SDL_Surface *surface; #pragma GCC diagnostic pop UT_hash_handle hh; } SpriteT; typedef struct SoundS { int32_t id; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpadded" Mix_Chunk *chunk; #pragma GCC diagnostic pop UT_hash_handle hh; } SoundT; typedef struct FontS { int32_t id; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpadded" TTF_Font *font; #pragma GCC diagnostic pop UT_hash_handle hh; } FontT; typedef struct VideoS { int32_t id; int32_t handle; int64_t lastFrame; bool wasPlayingBeforePause; SDL_Texture *texture; SDL_Surface *surface; UT_hash_handle hh; } VideoT; typedef struct MappingS { char *name; int32_t frameworkIndex; int32_t inputCount; int32_t *input; } MappingT; enum { KEYBD_NORMAL = 0, KEYBD_FULL }; enum { MOUSE_SINGLE = 100, MOUSE_MANY = 200 }; enum { LDP_ERROR = 0, LDP_SEARCHING, LDP_STOPPED, LDP_PLAYING, LDP_PAUSED }; enum { FONT_QUALITY_SOLID = 1, FONT_QUALITY_SHADED, FONT_QUALITY_BLENDED }; enum { // Index into _global.controlMappings array INPUT_UP = 0, INPUT_LEFT, INPUT_DOWN, INPUT_RIGHT, INPUT_1P_START, INPUT_2P_START, INPUT_ACTION_1, INPUT_ACTION_2, INPUT_ACTION_3, INPUT_1P_COIN, INPUT_2P_COIN, INPUT_SKILL_EASY, INPUT_SKILL_MEDIUM, INPUT_SKILL_HARD, INPUT_SERVICE, INPUT_TEST_MODE, INPUT_RESET_CPU, INPUT_SCREENSHOT, INPUT_QUIT, INPUT_PAUSE, INPUT_CONSOLE, // Added in Singe 2.x INPUT_ACTION_4, INPUT_TILT, INPUT_GRAB, INPUT_COUNT }; typedef struct GlobalS { MouseT mice[MAX_MICE]; lua_State *luaContext; SDL_Color colorForeground; SDL_Color colorBackground; SDL_Surface *overlay; SDL_Window *window; SDL_Renderer *renderer; SDL_Texture *videoTexture; SDL_Surface *consoleFontSurface; SDL_GameController **controllers; int32_t controllerCount; int32_t controllerDeadZone; int32_t consoleFontWidth; int32_t consoleFontHeight; int32_t nextSpriteId; int32_t nextSoundId; int32_t nextFontId; int32_t nextVideoId; int32_t effectsVolume; int32_t keyboardMode; int32_t frameFileHandle; int32_t videoHandle; int32_t fontQuality; int32_t mouseMode; int32_t mouseCount; int32_t axisCache[AXIS_COUNT]; double overlayScaleX; // Difference between overlay and video double overlayScaleY; // Difference between overlay and video bool pauseState; // by RDG2010 bool pauseEnabled; // by RDG2010 bool refreshDisplay; bool running; bool discStopped; bool mouseEnabled; bool mouseGrabbed; bool requestScreenShot; bool wasPlayingBeforePause; VideoT *videoList; SpriteT *spriteList; SoundT *soundList; FontT *fontList; FontT *fontCurrent; MappingT controlMappings[INPUT_COUNT]; ConfigT *conf; // Local copy of command line options } GlobalT; // Other globals GlobalT _global; int32_t apiColorBackground(lua_State *L); int32_t apiColorForeground(lua_State *L); int32_t apiControllerGetAxis(lua_State *L); int32_t apiDebugPrint(lua_State *L); int32_t apiDiscAudio(lua_State *L); int32_t apiDiscChangeSpeed(lua_State *L); int32_t apiDiscGetFrame(lua_State *L); int32_t apiDiscGetHeight(lua_State *L); int32_t apiDiscGetState(lua_State *L); int32_t apiDiscGetWidth(lua_State *L); int32_t apiDiscPause(lua_State *L); int32_t apiDiscPauseAtFrame(lua_State *L); int32_t apiDiscPlay(lua_State *L); int32_t apiDiscSearch(lua_State *L); int32_t apiDiscSearchBlanking(lua_State *L); int32_t apiDiscSetFps(lua_State *L); int32_t apiDiscSkipBackward(lua_State *L); int32_t apiDiscSkipBlanking(lua_State *L); int32_t apiDiscSkipForward(lua_State *L); int32_t apiDiscSkipToFrame(lua_State *L); int32_t apiDiscStepBackward(lua_State *L); int32_t apiDiscStepForward(lua_State *L); int32_t apiDiscStop(lua_State *L); int32_t apiFontLoad(lua_State *L); int32_t apiFontPrint(lua_State *L); int32_t apiFontQuality(lua_State *L); int32_t apiFontSelect(lua_State *L); int32_t apiFontToSprite(lua_State *L); int32_t apiFontUnload(lua_State *L); int32_t apiKeyboardGetMode(lua_State *L); int32_t apiKeyboardSetMode(lua_State *L); int32_t apiMouseDisable(lua_State *L); int32_t apiMouseEnable(lua_State *L); int32_t apiMouseGetPosition(lua_State *L); int32_t apiMouseHowMany(lua_State *L); int32_t apiMouseSetCaptured(lua_State *L); int32_t apiMouseSetMode(lua_State *L); int32_t apiOverlayBox(lua_State *L); int32_t apiOverlayCircle(lua_State *L); int32_t apiOverlayClear(lua_State *L); int32_t apiOverlayEllipse(lua_State *L); int32_t apiOverlayGetHeight(lua_State *L); int32_t apiOverlayGetWidth(lua_State *L); int32_t apiOverlayLine(lua_State *L); int32_t apiOverlayPlot(lua_State *L); int32_t apiOverlayPrint(lua_State *L); int32_t apiOverlaySetResolution(lua_State *L); int32_t apiScriptExecute(lua_State *L); int32_t apiScriptPush(lua_State *L); int32_t apiSingeDisablePauseKey(lua_State *L); int32_t apiSingeEnablePauseKey(lua_State *L); int32_t apiSingeGetDataPath(lua_State *L); int32_t apiSingeGetHeight(lua_State *L); int32_t apiSingeGetPauseFlag(lua_State *L); int32_t apiSingeGetScriptPath(lua_State *L); int32_t apiSingeGetWidth(lua_State *L); int32_t apiSingeScreenshot(lua_State *L); int32_t apiSingeSetGameName(lua_State *L); int32_t apiSingeSetPauseFlag(lua_State *L); int32_t apiSingeQuit(lua_State *L); int32_t apiSingeVersion(lua_State *L); int32_t apiSingeWantsCrosshairs(lua_State *L); int32_t apiSoundLoad(lua_State *L); int32_t apiSoundPlay(lua_State *L); int32_t apiSoundPause(lua_State *L); int32_t apiSoundResume(lua_State *L); int32_t apiSoundIsPlaying(lua_State *L); int32_t apiSoundStop(lua_State *L); int32_t apiSoundSetVolume(lua_State *L); int32_t apiSoundGetVolume(lua_State *L); int32_t apiSoundFullStop(lua_State *L); int32_t apiSoundUnload(lua_State *L); int32_t apiSpriteDraw(lua_State *L); int32_t apiSpriteGetHeight(lua_State *L); int32_t apiSpriteGetWidth(lua_State *L); int32_t apiSpriteLoad(lua_State *L); int32_t apiSpriteUnload(lua_State *L); int32_t apiVideoDraw(lua_State *L); int32_t apiVideoGetFrame(lua_State *L); int32_t apiVideoGetFrameCount(lua_State *L); int32_t apiVideoGetHeight(lua_State *L); int32_t apiVideoGetVolume(lua_State *L); int32_t apiVideoGetWidth(lua_State *L); int32_t apiVideoIsPlaying(lua_State *L); int32_t apiVideoLoad(lua_State *L); int32_t apiVideoPause(lua_State *L); int32_t apiVideoPlay(lua_State *L); int32_t apiVideoSeek(lua_State *L); int32_t apiVideoSetVolume(lua_State *L); int32_t apiVideoUnload(lua_State *L); int32_t apiVldpGetHeight(lua_State *L); int32_t apiVldpGetPixel(lua_State *L); int32_t apiVldpGetWidth(lua_State *L); int32_t apiVldpVerbose(lua_State *L); ConfigT *buildConfFromTable(lua_State *L); void doIndexDisplay(int32_t percent); void doLogos(void); void callLua(const char *func, const char *sig, ...); void channelFinished(int channel); void line(int32_t x1, int32_t y1, int32_t x2, int32_t y2, SDL_Color *c); void luaDie(lua_State *L, char *method, char *fmt, ...); int32_t luaError(lua_State *L); void luaPreload(lua_State *L, const char *name, lua_CFunction func); void luaTrace(lua_State *L, char *method, char *fmt, ...); void processKey(bool down, int keysym, int32_t scancode); void progTrace(char *fmt, ...); void putPixel(int32_t x, int32_t y, SDL_Color *c); void startControllers(void); void startLuaContext(lua_State *L); void stopControllers(void); void takeScreenshot(void); void updatePauseState(void); #ifdef DEBUG_TOOLS void luaStackDump(lua_State *L); #endif int32_t apiColorBackground(lua_State *L) { int32_t n = lua_gettop(L); double d = 0; bool result = false; if ((n == 3) || (n == 4)) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { if (lua_isnumber(L, 3)) { d = lua_tonumber(L, 1); _global.colorBackground.r = (byte)d; d = lua_tonumber(L, 2); _global.colorBackground.g = (byte)d; d = lua_tonumber(L, 3); _global.colorBackground.b = (byte)d; if (n == 3) { _global.colorBackground.a = (byte)0; // Default to transparent. } else { if (lua_isnumber(L, 4)) { d = lua_tonumber(L, 4); _global.colorBackground.a = (byte)d; } else { _global.colorBackground.a = (byte)0; // Default to transparent. } } result = true; } } } } if (result) { luaTrace(L, "colorBackground", "%d %d %d %d", _global.colorBackground.r, _global.colorBackground.g, _global.colorBackground.b, _global.colorBackground.a); } else { luaDie(L, "colorBackground", "Failed!"); } return 0; } int32_t apiColorForeground(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; double d = 0; if ((n == 3) || (n == 4)) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { if (lua_isnumber(L, 3)) { d = lua_tonumber(L, 1); _global.colorForeground.r = (byte)d; d = lua_tonumber(L, 2); _global.colorForeground.g = (byte)d; d = lua_tonumber(L, 3); _global.colorForeground.b = (byte)d; if (n == 3) { _global.colorForeground.a = (byte)255; // Default to opaque. } else { if (lua_isnumber(L, 4)) { d = lua_tonumber(L, 4); _global.colorForeground.a = (byte)d; } else { _global.colorForeground.a = (byte)255; // Default to opaque. } } result = true; } } } } if (result) { luaTrace(L, "colorForeground", "%d %d %d %d", _global.colorForeground.r, _global.colorForeground.g, _global.colorForeground.b, _global.colorForeground.a); } else { luaDie(L, "colorForeground", "Failed!"); } return 0; } int32_t apiControllerGetAxis(lua_State *L) { int32_t n = lua_gettop(L); int32_t c = 0; int32_t a = 0; int32_t v = 0; double d = 0; bool result = false; if (n == 2) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { d = lua_tonumber(L, 1); c = (int32_t)d; d = lua_tonumber(L, 2); a = (int32_t)d; if ((c < 0) || (c >= MAX_CONTROLLERS)) luaDie(L, "controllerGetAxis", "Invalid controller index: %d", c); if ((a < 0) || (a >= CONTROLLER_AXIS_COUNT)) luaDie(L, "controllerGetAxis", "Invalid controller axis: %d", a); v = _global.axisCache[c * CONTROLLER_AXIS_COUNT + a]; result = true; } } } if (result) { luaTrace(L, "controllerGetAxis", "%d %d %d", c, a, v); lua_pushinteger(L, v); } else { luaDie(L, "controllerGetAxis", "Failed!"); } return 1; } int32_t apiSingeGetHeight(lua_State *L) { int32_t y; SDL_GetWindowSize(_global.window, NULL, &y); luaTrace(L, "singeGetHeight", "%d", y); lua_pushinteger(L, y); return 1; } int32_t apiSingeGetWidth(lua_State *L) { int32_t x; SDL_GetWindowSize(_global.window, &x, NULL); luaTrace(L, "singeGetWidth", "%d", x); lua_pushinteger(L, x); return 1; } int32_t apiSingeScreenshot(lua_State *L) { luaTrace(L, "singeScreenshot", "Screenshot requested."); _global.requestScreenShot = true; return 0; } int32_t apiDebugPrint(lua_State *L) { int32_t n = lua_gettop(L); if (n == 1) { if (lua_isstring(L, 1)) { luaTrace(L, "DebugPrint", "%s", lua_tostring(L, 1)); utilSay("%s", lua_tostring(L, 1)); } } return 0; } int32_t apiDiscAudio(lua_State *L) { int32_t n = lua_gettop(L); int32_t channel = 0; int32_t left = 0; int32_t right = 0; bool onOff = false; bool result = false; double d = 0; if (n == 2) { if (lua_isnumber(L, 1)) { if (lua_isboolean(L, 2)) { if (_global.videoHandle >= 0) { d = lua_tonumber(L, 1); channel = (int32_t)d; d = lua_toboolean(L, 2); onOff = (bool)d; videoGetVolume(_global.videoHandle, &left, &right); if (channel == 1) left = (onOff ? _global.conf->volumeVldp : 0); if (channel == 2) right = (onOff ? _global.conf->volumeVldp : 0); videoSetVolume(_global.videoHandle, left, right); result = true; } } } } if (result) { luaTrace(L, "discAudio", "%d %d", left, right); } else { luaDie(L, "discAudio", "Failed!"); } return 0; } int32_t apiDiscChangeSpeed(lua_State *L) { (void)L; //***REMOVED*** luaTrace(L, "discChangeSpeed", "Unimplemented"); return 0; } int32_t apiDiscGetFrame(lua_State *L) { int64_t frame = 0; if (!_global.discStopped) { if (_global.conf->isFrameFile) { frame = frameFileGetFrame(_global.frameFileHandle, _global.videoHandle); } else { if (_global.videoHandle >= 0) frame = videoGetFrame(_global.videoHandle); } } luaTrace(L, "discGetFrame", "%ld", frame); lua_pushinteger(L, frame); return 1; } int32_t apiDiscPause(lua_State *L) { (void)L; if (!_global.discStopped) { if (_global.videoHandle >= 0) videoPause(_global.videoHandle); luaTrace(L, "discPause", ""); } else { luaTrace(L, "discPause", "Failed! Disc is stopped."); } return 0; } int32_t apiDiscPauseAtFrame(lua_State *L) { // More RDG oddness. This appears to be identical to discSearch. return apiDiscSearch(L); } int32_t apiDiscPlay(lua_State *L) { (void)L; if (_global.videoHandle >= 0) videoPlay(_global.videoHandle); _global.discStopped = false; luaTrace(L, "discPlay", ""); return 0; } int32_t apiDiscSearch(lua_State *L) { int32_t n = lua_gettop(L); int64_t frame = 0; int64_t aFrame = 0; bool result = false; double d = 0; // No matter the disc state, seek to the frame, display it, and pause. if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); frame = (int64_t)d; if (_global.conf->isFrameFile) { frameFileSeek(_global.frameFileHandle, frame, &_global.videoHandle, &aFrame); } else { if (_global.videoHandle >= 0) videoSeek(_global.videoHandle, frame); } if (_global.videoHandle >= 0) videoPause(_global.videoHandle); _global.discStopped = false; result = true; } } if (result) { luaTrace(L, "discSearch", "%ld", frame); } else { luaDie(L, "discSearch", "Failed!"); } return 0; } int32_t apiDiscSearchBlanking(lua_State *L) { (void)L; //***REMOVED*** luaTrace(L, "discSearchBlanking", "Unimplemented"); return 0; } int32_t apiDiscSetFps(lua_State *L) { (void)L; //***REMOVED*** luaTrace(L, "discSetFPS", "Unimplemented"); return 0; } int32_t apiDiscSkipBackward(lua_State *L) { int32_t n = lua_gettop(L); int64_t frame = 0; int64_t aFrame = 0; bool result = false; double d = 0; // If disc is not stopped, seek backwards to given frame. Do not change play/pause state. if (!_global.discStopped) { if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); if (_global.videoHandle >= 0) frame = videoGetFrame(_global.videoHandle) - (int64_t)d; if (_global.conf->isFrameFile) { frameFileSeek(_global.frameFileHandle, frame, &_global.videoHandle, &aFrame); } else { if (_global.videoHandle >= 0) videoSeek(_global.videoHandle, frame); } result = true; } } } else { luaDie(L, "discSkipBackward", "Failed! Disc is stopped."); return 0; } if (result) { luaTrace(L, "discSkipBackward", "%ld", frame); } else { luaDie(L, "discSkipBackward", "Failed!"); } return 0; } int32_t apiDiscSkipBlanking(lua_State *L) { (void)L; //***REMOVED*** luaTrace(L, "discSkipBlanking", "Unimplemented"); return 0; } int32_t apiDiscSkipForward(lua_State *L) { int32_t n = lua_gettop(L); int64_t frame = 0; int64_t aFrame = 0; bool result = false; double d = 0; // If disc is not stopped, seek ahead to given frame. Do not change play/pause state. if (!_global.discStopped) { if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); if (_global.videoHandle >= 0) frame = videoGetFrame(_global.videoHandle) + (int64_t)d; if (_global.conf->isFrameFile) { frameFileSeek(_global.frameFileHandle, frame, &_global.videoHandle, &aFrame); } else { if (_global.videoHandle >= 0) videoSeek(_global.videoHandle, frame); } result = true; } } } else { luaTrace(L, "discSkipForward", "Failed! Disc is stopped."); return 0; } if (result) { luaTrace(L, "discSkipForward", "%ld", frame); } else { luaDie(L, "discSkipForward", "Failed!"); } return 0; } int32_t apiDiscSkipToFrame(lua_State *L) { int32_t n = lua_gettop(L); int64_t frame = 0; int64_t aFrame = 0; bool result = false; double d = 0; // No matter disc state, seek to given frame and play. if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); frame = (int64_t)d; if (_global.conf->isFrameFile) { frameFileSeek(_global.frameFileHandle, frame, &_global.videoHandle, &aFrame); } else { if (_global.videoHandle >= 0) videoSeek(_global.videoHandle, frame); } if (_global.videoHandle >= 0) videoPlay(_global.videoHandle); _global.discStopped = false; result = true; } } if (result) { luaTrace(L, "discSkipToFrame", "%ld", frame); } else { luaDie(L, "discSkipToFrame", "Failed!"); } return 0; } int32_t apiDiscStepBackward(lua_State *L) { int64_t frame = 0; int64_t aFrame = 0; (void)L; // No matter disc state, go back a frame. If playing, pause. if (_global.videoHandle >= 0) { if (_global.conf->isFrameFile) { frame = frameFileGetFrame(_global.frameFileHandle, _global.videoHandle) - 1; frameFileSeek(_global.frameFileHandle, frame, &_global.videoHandle, &aFrame); } else { frame = videoGetFrame(_global.videoHandle) - 1; videoSeek(_global.videoHandle, frame); } videoPause(_global.videoHandle); } luaTrace(L, "discStepBackward", "%ld", frame); return 0; } int32_t apiDiscStepForward(lua_State *L) { int64_t frame = 0; int64_t aFrame = 0; (void)L; // No matter disc state, go forward a frame. If playing, pause. if (_global.videoHandle >= 0) { if (_global.conf->isFrameFile) { frame = frameFileGetFrame(_global.frameFileHandle, _global.videoHandle) + 1; frameFileSeek(_global.frameFileHandle, frame, &_global.videoHandle, &aFrame); } else { frame = videoGetFrame(_global.videoHandle) + 1; videoSeek(_global.videoHandle, frame); } videoPause(_global.videoHandle); } luaTrace(L, "discStepForward", "%ld", frame); return 0; } int32_t apiDiscStop(lua_State *L) { (void)L; if (!_global.discStopped) { if (_global.videoHandle >= 0) videoPause(_global.videoHandle); _global.discStopped = true; _global.refreshDisplay = true; luaTrace(L, "discStop", ""); } else { luaTrace(L, "discStop", "Failed! Disc is stopped."); } return 0; } int32_t apiFontLoad(lua_State *L) { int32_t n = lua_gettop(L); int32_t result = -1; int32_t points = 0; const char *name = NULL; double d = 0; FontT *font = NULL; if (n == 2) { if (lua_isstring(L, 1)) { if (lua_isnumber(L, 2)) { name = lua_tostring(L, 1); d = lua_tonumber(L, 2); points = (int32_t)d; font = (FontT *)calloc(1, sizeof(FontT)); if (!font) luaDie(L, "fontLoad", "Unable to allocate new font."); // Load this font. font->font = TTF_OpenFont(name, points); if (!font->font) luaDie(L, "fontLoad", "%s", TTF_GetError()); // Make it the current font and mark it as loaded. font->id = _global.nextFontId; result = _global.nextFontId++; _global.fontCurrent = font; HASH_ADD_INT(_global.fontList, id, font); } } } if (result >= 0) { luaTrace(L, "fontLoad", "%s %d", name, result); } else { luaDie(L, "fontLoad", "Failed!"); } lua_pushnumber(L, result); return 1; } int32_t apiFontPrint(lua_State *L) { int32_t n = lua_gettop(L); const char *message = NULL; double d = 0; SDL_Surface *textSurface = NULL; SDL_Rect dest; if (n == 3) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { if (lua_isstring(L, 3)) { if (_global.fontCurrent) { d = lua_tonumber(L, 1); dest.x = (int32_t)d; d = lua_tonumber(L, 2); dest.y = (int32_t)d; textSurface = NULL; message = lua_tostring(L, 3); switch (_global.fontQuality) { case FONT_QUALITY_SOLID: textSurface = TTF_RenderText_Solid(_global.fontCurrent->font, message, _global.colorForeground); break; case FONT_QUALITY_SHADED: textSurface = TTF_RenderText_Shaded(_global.fontCurrent->font, message, _global.colorForeground, _global.colorBackground); break; case FONT_QUALITY_BLENDED: textSurface = TTF_RenderText_Blended(_global.fontCurrent->font, message, _global.colorForeground); break; default: luaDie(L, "fontPrint", "Unknown font quality!"); break; } if (!textSurface) { luaDie(L, "fontPrint", "Font surface is null!"); } else { SDL_SetColorKey(textSurface, true, 0); dest.w = textSurface->w; dest.h = textSurface->h; SDL_BlitSurface(textSurface, NULL, _global.overlay, &dest); SDL_FreeSurface(textSurface); } } } } } } if (textSurface) { luaTrace(L, "fontPrint", "%s", message); } else { luaDie(L, "fontPrint", "Failed!"); } return 0; } int32_t apiFontQuality(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; double d = 0; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); _global.fontQuality = (int32_t)d; result = true; } } if (result) { luaTrace(L, "fontQuality", "%d", _global.fontQuality); } else { luaDie(L, "fontQuality", "Failed!"); } return 0; } int32_t apiFontSelect(lua_State *L) { int32_t n = lua_gettop(L); int32_t id = -1; double d = 0; bool result = false; FontT *font = NULL; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); id = (int32_t)d; HASH_FIND_INT(_global.fontList, &id, font); if (!font) luaDie(L, "fontSelect", "No font at index %d in apiFontSelect.", id); _global.fontCurrent = font; result = true; } } if (result) { luaTrace(L, "fontSelect", "%d", _global.fontCurrent->id); } else { luaDie(L, "fontSelect", "Failed!"); } return 0; } int32_t apiFontUnload(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; int32_t id = -1; double d; FontT *font = NULL; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); id = (int32_t)d; // Get our font structure HASH_FIND_INT(_global.fontList, &id, font); if (!font) luaDie(L, "fontUnload", "No font at index %d in apiFontUnload.", id); HASH_DEL(_global.fontList, font); TTF_CloseFont(font->font); free(font); result = true; } } if (result) { luaTrace(L, "fontUnload", "%d", id); } else { luaDie(L, "fontUnload", "Failed!"); } return 0; } int32_t apiFontToSprite(lua_State *L) { int32_t n = lua_gettop(L); int32_t result = -1; const char *message = NULL; SpriteT *sprite = NULL; if (n == 1) { if (lua_isstring(L, 1)) { if (_global.fontCurrent) { // Create spirte sprite = (SpriteT *)calloc(1, sizeof(SpriteT)); if (!sprite) luaDie(L, "fontToSprite", "Unable to allocate new text sprite."); message = lua_tostring(L, 1); switch (_global.fontQuality) { case 1: sprite->surface = TTF_RenderText_Solid(_global.fontCurrent->font, message, _global.colorForeground); break; case 2: sprite->surface = TTF_RenderText_Shaded(_global.fontCurrent->font, message, _global.colorForeground, _global.colorBackground); break; case 3: sprite->surface = TTF_RenderText_Blended(_global.fontCurrent->font, message, _global.colorForeground); break; default: luaDie(L, "fontToSprite", "Unknown font quality!"); break; } if (!sprite->surface) { luaDie(L, "fontToSprite", "Font surface is null!"); } else { SDL_SetColorKey(sprite->surface, true, 0); sprite->id = _global.nextSpriteId; result = _global.nextSpriteId++; HASH_ADD_INT(_global.spriteList, id, sprite); } } } } if (sprite->surface) { luaTrace(L, "fontToSprite", "%d %s", result, message); } else { luaDie(L, "fontToSprite", "Failed!"); } lua_pushinteger(L, result); return 1; } int32_t apiOverlayBox(lua_State *L) { int32_t n = lua_gettop(L); int32_t x1 = 0; int32_t y1 = 0; int32_t x2 = 0; int32_t y2 = 0; //SDL_Rect r; double d = 0; bool result = false; if (n == 4) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { if (lua_isnumber(L, 3)) { if (lua_isnumber(L, 4)) { d = lua_tonumber(L, 1); x1 = (int32_t)d; d = lua_tonumber(L, 2); y1 = (int32_t)d; d = lua_tonumber(L, 3); x2 = (int32_t)d; d = lua_tonumber(L, 4); y2 = (int32_t)d; /* r.x = x1; r.y = y1; r.w = abs(x2 - x1) + 1; r.h = abs(y2 - y1) + 1; */ SDL_LockSurface(_global.overlay); //***TODO*** No filling until I can find an efficient way to blend individual pixels. //SDL_FillRect(_global.overlay, &r, SDL_MapRGBA(_global.overlay->format, _global.colorBackground.r, _global.colorBackground.g, _global.colorBackground.b, _global.colorBackground.a)); line(x1, y1, x2, y1, &_global.colorForeground); line(x2, y1, x2, y2, &_global.colorForeground); line(x2, y2, x1, y2, &_global.colorForeground); line(x1, y2, x1, y1, &_global.colorForeground); SDL_UnlockSurface(_global.overlay); result = true; } } } } } if (result) { luaTrace(L, "overlayBox", "%d %d %d %d", x1, y1, x2, y2); } else { luaDie(L, "overlayBox", "Failed!"); } return 0; } int32_t apiOverlayCircle(lua_State *L) { int32_t n = lua_gettop(L); int32_t x0 = 0; int32_t y0 = 0; int32_t r = 0; int32_t x = 0; int32_t y = 0; int32_t ro = 0; int32_t xo = 0; int32_t yo = 0; int32_t dx = 1; int32_t dy = 1; int32_t err = 0; double d = 0; bool result = false; if (n == 3) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { if (lua_isnumber(L, 3)) { d = lua_tonumber(L, 1); xo = x = (int32_t)d; d = lua_tonumber(L, 2); yo = y = (int32_t)d; d = lua_tonumber(L, 3); ro = r = (int32_t)d; x = r - 1; err = dx - (int32_t)(r << 1); SDL_LockSurface(_global.overlay); while (x >= y) { putPixel(x0 + x, y0 + y, &_global.colorForeground); putPixel(x0 + y, y0 + x, &_global.colorForeground); putPixel(x0 - y, y0 + x, &_global.colorForeground); putPixel(x0 - x, y0 + y, &_global.colorForeground); putPixel(x0 - x, y0 - y, &_global.colorForeground); putPixel(x0 - y, y0 - x, &_global.colorForeground); putPixel(x0 + y, y0 - x, &_global.colorForeground); putPixel(x0 + x, y0 - y, &_global.colorForeground); if (err <= 0) { y++; err += dy; dy += 2; } if (err > 0) { x--; dx += 2; err += dx - (r << 1); } } SDL_UnlockSurface(_global.overlay); result = true; } } } } if (result) { luaTrace(L, "overlayCircle", "%d %d %d", xo, yo, ro); } else { luaDie(L, "overlayCircle", "Failed!"); } return 0; } int32_t apiOverlayClear(lua_State *L) { (void)L; SDL_FillRect(_global.overlay, NULL, SDL_MapRGBA(_global.overlay->format, _global.colorBackground.r, _global.colorBackground.g, _global.colorBackground.b, _global.colorBackground.a)); luaTrace(L, "overlayClear", ""); return 0; } int32_t apiOverlayEllipse(lua_State *L) { int32_t n = lua_gettop(L); int32_t x0 = 0; int32_t y0 = 0; int32_t x1 = 0; int32_t y1 = 0; int32_t x0o = 0; int32_t y0o = 0; int32_t x1o = 0; int32_t y1o = 0; int32_t a = 0; int32_t b = 0; int32_t b1 = 0; int32_t dx = 0; int32_t dy = 0; int32_t err = 0; int32_t e2 = 0; double d = 0; bool result = false; if (n == 4) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { if (lua_isnumber(L, 3)) { if (lua_isnumber(L, 4)) { d = lua_tonumber(L, 1); x0o = x0 = (int32_t)d; d = lua_tonumber(L, 2); y0o = y0 = (int32_t)d; d = lua_tonumber(L, 3); x1o = x1 = (int32_t)d; d = lua_tonumber(L, 4); y1o = y1 = (int32_t)d; SDL_LockSurface(_global.overlay); a = abs(x1 - x0); b = abs(y1 - y0); b1 = b & 1; // values of diameter dx = 4 * (1 - a) * b * b; dy = 4 * (b1 + 1) * a * a; // error increment err = dx + dy + b1 * a * a; if (x0 > x1) { // if called with swapped points x0 = x1; x1 += a; } if (y0 > y1) { //exchange them y0 = y1; } y0 += (b + 1) / 2; // starting pixel y1 = y0 - b1; a *= 8 * a; b1 = 8 * b * b; do { putPixel(x1, y0, &_global.colorForeground); // I. Quadrant putPixel(x0, y0, &_global.colorForeground); // II. Quadrant putPixel(x0, y1, &_global.colorForeground); // III. Quadrant putPixel(x1, y1, &_global.colorForeground); // IV. Quadrant e2 = 2 * err; if (e2 <= dy) { // y step y0++; y1--; err += dy += a; } if (e2 >= dx || 2*err > dy) { // x step x0++; x1--; err += dx += b1; } } while (x0 <= x1); while (y0-y1 < b) { // too early stop of flat ellipses a = 1 putPixel(x0-1, y0, &_global.colorForeground); // -> finish tip of ellipse putPixel(x1+1, y0++, &_global.colorForeground); putPixel(x0-1, y1, &_global.colorForeground); putPixel(x1+1, y1--, &_global.colorForeground); } SDL_UnlockSurface(_global.overlay); result = true; } } } } } if (result) { luaTrace(L, "overlayEllipse", "%d %d %d %d", x0o, y0o, x1o, y1o); } else { luaDie(L, "overlayEllipse", "Failed!"); } return 0; } int32_t apiOverlayGetHeight(lua_State *L) { luaTrace(L, "overlayGetHeight", "%d", _global.overlay->h); lua_pushinteger(L, _global.overlay->h); return 1; } int32_t apiOverlayGetWidth(lua_State *L) { luaTrace(L, "overlayGetWidth", "%d", _global.overlay->w); lua_pushinteger(L, _global.overlay->w); return 1; } int32_t apiOverlayLine(lua_State *L) { int32_t n = lua_gettop(L); int32_t x1 = 0; int32_t y1 = 0; int32_t x2 = 0; int32_t y2 = 0; double d = 0; bool result = false; if (n == 4) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { if (lua_isnumber(L, 3)) { if (lua_isnumber(L, 4)) { d = lua_tonumber(L, 1); x1 = (int32_t)d; d = lua_tonumber(L, 2); y1 = (int32_t)d; d = lua_tonumber(L, 3); x2 = (int32_t)d; d = lua_tonumber(L, 4); y2 = (int32_t)d; SDL_LockSurface(_global.overlay); line(x1, y1, x2, y2, &_global.colorForeground); SDL_UnlockSurface(_global.overlay); result = true; } } } } } if (result) { luaTrace(L, "overlayLine", "%d %d %d %d", x1, y1, x2, y2); } else { luaDie(L, "overlayLine", "Failed!"); } return 0; } int32_t apiOverlayPlot(lua_State *L) { int32_t n = lua_gettop(L); int32_t x1 = 0; int32_t y1 = 0; double d = 0; bool result = false; if (n == 2) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { d = lua_tonumber(L, 1); x1 = (int32_t)d; d = lua_tonumber(L, 2); y1 = (int32_t)d; SDL_LockSurface(_global.overlay); putPixel(x1, y1, &_global.colorForeground); SDL_UnlockSurface(_global.overlay); result = true; } } } if (result) { luaTrace(L, "overlayPlot", "%d %d", x1, y1); } else { luaDie(L, "overlayPlot", "Failed!"); } return 0; } int32_t apiOverlayPrint(lua_State *L) { int32_t n = lua_gettop(L); int32_t i = 0; char *s = NULL; int32_t length = 0; bool result = false; SDL_Rect src; SDL_Rect dst; if (n == 3) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { if (lua_isstring(L, 3)) { src.y = 0; src.w = _global.consoleFontWidth; src.h = _global.consoleFontHeight; dst.x = lua_tonumber(L, 1) * _global.consoleFontWidth; dst.y = lua_tonumber(L, 2) * _global.consoleFontHeight; dst.w = _global.consoleFontWidth; dst.h = _global.consoleFontHeight; s = (char *)lua_tostring(L, 3); if (strlen(s) < (uint32_t)((_global.overlay->w - dst.x) / _global.consoleFontWidth)) { length = strlen(s); } else { length = (_global.overlay->w - dst.x) / _global.consoleFontWidth; } for (i=0; ichunk = Mix_LoadWAV(name); if (!sound->chunk) luaDie(L, "soundLoad", "%s", Mix_GetError()); sound->id = _global.nextSoundId; result = _global.nextSoundId++; HASH_ADD_INT(_global.soundList, id, sound); } } if (sound) { luaTrace(L, "soundLoad", "%d %s", result, name); } else { luaDie(L, "soundLoad", "Failed!"); } lua_pushnumber(L, result); return 1; } int32_t apiSoundPlay(lua_State *L) { int32_t n = lua_gettop(L); int32_t r = -1; int32_t id = -1; double d = 0; bool result = false; SoundT *sound = NULL; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); id = (int32_t)d; // Get our sound structure HASH_FIND_INT(_global.soundList, &id, sound); if (!sound) luaDie(L, "soundPlay", "No sound at index %d in apiSoundPlay.", id); // Play it (can gracefully fail if we run out of channels) r = Mix_PlayChannel(-1, sound->chunk, 0); if (r >= 0) { Mix_Volume(r, _global.effectsVolume * 2); } result = true; } } if (result) { luaTrace(L, "soundPlay", "%d", r); } else { luaDie(L, "soundPlay", "Failed!"); } lua_pushnumber(L, r); return 1; } int32_t apiSoundPause(lua_State *L) { // Instructs Daphne to pause a given sample from playing. // User must feed the sound handle on the lua side. // e.g. lua code, // // thisHandle = soundPlay(mySound) // soundPause(thisHandle) // // Function returns true if sample was paused, false otherwise. // // --rdg int32_t n = lua_gettop(L); int32_t channel = -1; double d = 0; bool r = false; bool result = false; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); channel = (int32_t)d; r = Mix_Playing(channel); Mix_Pause(channel); result = true; } } if (result) { luaTrace(L, "soundPause", "%d %d", channel, r); } else { luaDie(L, "soundPause", "Failed!"); } lua_pushboolean(L, r); return 1; } int32_t apiSoundResume(lua_State *L) { // Instructs Daphne to unpause a sound that was previously paused. // User must feed the sound handle on the lua side. // e.g. lua code, // // thisHandle = soundPlay(mySound) // soundPause(thisHandle) // soundResume(thisHandle) // // Function returns true if sample was unpaused, false otherwise. // // --rdg int32_t n = lua_gettop(L); int32_t channel = -1; double d = 0; bool r = false; bool result = false; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); channel = (int32_t)d; r = Mix_Paused(channel); Mix_Resume(channel); result = true; } } if (result) { luaTrace(L, "soundResume", "%d %d", channel, r); } else { luaDie(L, "soundResume", "Failed!"); } lua_pushboolean(L, r); return 1; } int32_t apiSoundIsPlaying(lua_State *L) { // Checks to see if a certain sound has finished playing. // User must feed the sound handle on the lua side. // e.g. lua code, // // thisHandle = soundPlay(mySound) // if (soundIsPlaying(thisSound)) then do something ... end // // Function returns true if sample is still playing, false otherwise. // // --rdg int32_t n = lua_gettop(L); int32_t channel = -1; double d = 0; bool r = false; bool result = false; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); channel = (int32_t)d; r = (bool)Mix_Playing(channel); result = true; } } if (result) { luaTrace(L, "soundIsPlaying", "%d %d", channel, r); } else { luaDie(L, "soundIsPlaying", "Failed!"); } lua_pushboolean(L, r); return 1; } int32_t apiSoundStop(lua_State *L) { // Instructs Daphne to end a sound early. // User must feed the sound handle on the lua side. // e.g. lua code, // // thisHandle = soundPlay(mySound) // soundStop(thisHandle) // // Function returns true if sample was stopped, false otherwise. // NOTE: thisHandle will be invalidated as a result of this function. // Lua doesn't do variables by reference, so it is // up to the user to keep track of sound handles on the lua script. // // --rdg int32_t n = lua_gettop(L); int32_t channel = -1; double d = 0; bool r = false; bool result = false; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); channel = (int32_t)d; r = Mix_Playing(channel); Mix_HaltChannel(channel); result = true; } } if (result) { luaTrace(L, "soundStop", "%d %d", channel, r); } else { luaDie(L, "soundStop", "Failed!"); } lua_pushboolean(L, r); return 1; } int32_t apiSoundSetVolume(lua_State *L) { // Allows manipulation of sample volume. // Valid values range from 0 to 63 // e.g. lua code, // // soundSetVolume(32) // // Function returns nothing. // // --rdg int32_t n = lua_gettop(L); int32_t thisValue = 0; double d = 0; bool result = false; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); thisValue = (int32_t)d; if (thisValue >= 0 && thisValue <= AUDIO_MAX_VOLUME) { _global.effectsVolume = thisValue; Mix_Volume(-1, _global.effectsVolume * 2); } else { luaDie(L, "soundSetVolume", "Invalid sound volume value."); } } result = true; } if (result) { luaTrace(L, "soundSetVolume", "%d", _global.effectsVolume); } else { luaDie(L, "soundSetVolume", "Failed!"); } return 0; } int32_t apiSoundGetVolume(lua_State *L) { // Returns the current sample volume value. // e.g. lua code, // // local iVolume = soundGetVolume() // // Function returns an integer value ranging from 0 to 63. // // --rdg luaTrace(L, "soundGetVolume", "%d", _global.effectsVolume); lua_pushinteger(L, _global.effectsVolume); return 1; } int32_t apiSoundFullStop(lua_State *L) { // Clears the audio queue of any samples actively playing. // No parameters needed. Function returns nothing. // e.g. lua code, // // soundFullStop() (void)L; luaTrace(L, "soundFullStop", ""); Mix_HaltChannel(-1); return 0; } int32_t apiSoundUnload(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; int32_t id = -1; double d; SoundT *sound = NULL; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); id = (int32_t)d; // Get our sound structure HASH_FIND_INT(_global.soundList, &id, sound); if (!sound) luaDie(L, "soundUnload", "No sound at index %d in apiSoundUnload.", id); HASH_DEL(_global.soundList, sound); Mix_FreeChunk(sound->chunk); free(sound); result = true; } } if (result) { luaTrace(L, "soundUnload", "%d", id); } else { luaDie(L, "soundUnload", "Failed!"); } return 0; } int32_t apiSpriteDraw(lua_State *L) { int32_t n = lua_gettop(L); int32_t id = -1; double d = 0; SpriteT *sprite = NULL; SDL_Rect dest; if ((n == 3) || (n == 5)) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { if (lua_isnumber(L, 3)) { d = lua_tonumber(L, 1); dest.x = (int32_t)d; d = lua_tonumber(L, 2); dest.y = (int32_t)d; if (n == 5) { // Target is scaled if (lua_isnumber(L, 4)) { if (lua_isnumber(L, 5)) { d = lua_tonumber(L, 3); dest.w = (int32_t)d - dest.x + 1; d = lua_tonumber(L, 4); dest.h = (int32_t)d - dest.y + 1; d = lua_tonumber(L, 5); id = (int32_t)d; } } } else { // Target is same size as sprite d = lua_tonumber(L, 3); id = (int32_t)d; } HASH_FIND_INT(_global.spriteList, &id, sprite); if (!sprite) luaDie(L, "spriteDraw", "No sprite at index %d in apiSpriteDraw.", id); if (n == 5) { // Target is scaled SDL_BlitScaled(sprite->surface, NULL, _global.overlay, &dest); } else { // Target is same size as sprite dest.w = sprite->surface->w; dest.h = sprite->surface->h; SDL_BlitSurface(sprite->surface, NULL, _global.overlay, &dest); } } } } } if (id >= 0) { luaTrace(L, "spriteDraw", "%d %d %d %d %d", id, dest.x, dest.y, dest.w, dest.h); } else { luaDie(L, "spriteDraw", "Failed!"); } return 0; } int32_t apiSpriteGetHeight(lua_State *L) { int32_t n = lua_gettop(L); int32_t result = -1; int32_t id = -1; double d; SpriteT *sprite = NULL; if (n == 1) { if (lua_isstring(L, 1)) { d = lua_tonumber(L, 1); id = (int32_t)d; // Get our sprite structure HASH_FIND_INT(_global.spriteList, &id, sprite); if (!sprite) luaDie(L, "spriteGetHeight", "No sprite at index %d in apiSpriteGetHeight.", id); result = sprite->surface->h; } } if (result >= 0) { luaTrace(L, "spriteGetHeight", "%d", result); } else { luaDie(L, "spriteGetHeight", "Failed!"); } lua_pushinteger(L, result); return 1; } int32_t apiSpriteGetWidth(lua_State *L) { int32_t n = lua_gettop(L); int32_t result = -1; int32_t id = -1; double d; SpriteT *sprite = NULL; if (n == 1) { if (lua_isstring(L, 1)) { d = lua_tonumber(L, 1); id = (int32_t)d; // Get our sprite structure HASH_FIND_INT(_global.spriteList, &id, sprite); if (!sprite) luaDie(L, "spriteGetWidth", "No sprite at index %d in apiSpriteGetWidth.", id); result = sprite->surface->w; } } if (result >= 0) { luaTrace(L, "spriteGetWidth", "%d", result); } else { luaDie(L, "spriteGetWidth", "Failed!"); } lua_pushinteger(L, result); return 1; } int32_t apiSpriteLoad(lua_State *L) { int32_t n = lua_gettop(L); int32_t result = -1; const char *name = NULL; SpriteT *sprite = NULL; if (n == 1) { if (lua_isstring(L, 1)) { sprite = (SpriteT *)calloc(1, sizeof(SpriteT)); if (!sprite) luaDie(L, "spriteLoad", "Unable to allocate new sprite."); name = lua_tostring(L, 1); sprite->surface = IMG_Load(name); if (!sprite->surface) luaDie(L, "spriteLoad", "%s", IMG_GetError()); SDL_SetColorKey(sprite->surface, true, 0); sprite->id = _global.nextSpriteId; result = _global.nextSpriteId++; HASH_ADD_INT(_global.spriteList, id, sprite); } } if (sprite->surface) { luaTrace(L, "spriteLoad", "%d %s", result, name); } else { luaDie(L, "spriteLoad", "Failed!"); } lua_pushnumber(L, result); return 1; } int32_t apiSpriteUnload(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; int32_t id = -1; double d; SpriteT *sprite = NULL; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); id = (int32_t)d; // Get our sprite structure HASH_FIND_INT(_global.spriteList, &id, sprite); if (!sprite) luaDie(L, "spriteUnload", "No sprite at index %d in apiSpriteUnload.", id); HASH_DEL(_global.spriteList, sprite); SDL_FreeSurface(sprite->surface); free(sprite); result = true; } } if (result) { luaTrace(L, "spriteUnload", "%d", id); } else { luaDie(L, "spriteUnload", "Failed!"); } return 0; } int32_t apiVideoDraw(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; int32_t id = -1; int32_t w = 0; int32_t h = 0; int64_t frame = 0; double d = 0.0; VideoT *video = NULL; SDL_Rect dest; if (n == 5) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { if (lua_isnumber(L, 3)) { if (lua_isnumber(L, 4)) { if (lua_isnumber(L, 5)) { d = lua_tonumber(L, 1); id = (int32_t)d; d = lua_tonumber(L, 2); dest.x = (int32_t)d; d = lua_tonumber(L, 3); dest.y = (int32_t)d; d = lua_tonumber(L, 4); dest.w = (int32_t)d - dest.x + 1; d = lua_tonumber(L, 5); dest.h = (int32_t)d - dest.y + 1; // Get our video structure HASH_FIND_INT(_global.videoList, &id, video); if (!video) luaDie(L, "videoDraw", "No video at index %d in apiVideoDraw.", id); frame = videoUpdate(video->handle, &video->texture); // New Frame? if (frame != video->lastFrame) { // Get new frame into a surface - this is slow if (video->surface) SDL_FreeSurface(video->surface); SDL_QueryTexture(video->texture, NULL, NULL, &w, &h); video->surface = SDL_CreateRGBSurface(0, w, h, 32, 0, 0, 0, 255); if (!video->surface) utilDie("%s", SDL_GetError()); if (SDL_SetRenderTarget(_global.renderer, video->texture) < 0) luaDie(L, "videoDraw", "%s", SDL_GetError()); if (SDL_RenderReadPixels(_global.renderer, NULL, video->surface->format->format, video->surface->pixels, video->surface->pitch) != 0) luaDie(L, "videoDraw", "%s", SDL_GetError()); if (SDL_SetRenderTarget(_global.renderer, NULL) < 0) luaDie(L, "videoDraw", "%s", SDL_GetError()); } // Render frame into overlay if (SDL_BlitScaled(video->surface, NULL, _global.overlay, &dest) != 0) luaDie(L, "videoDraw", "%s", SDL_GetError()); result = true; } } } } } } if (result) { luaTrace(L, "videoDraw", "%d %d %d %d %d %ld", id, dest.x, dest.y, dest.x + dest.w, dest.y + dest.h, frame); } else { luaDie(L, "videoDraw", "Failed!"); } return 0; } int32_t apiVideoGetFrame(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; int64_t r = 0; int32_t id = -1; double d; VideoT *video = NULL; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); id = (int32_t)d; // Get our video structure HASH_FIND_INT(_global.videoList, &id, video); if (!video) luaDie(L, "videoGetFrame", "No video at index %d in apiVideoGetFrame.", id); r = videoGetFrame(video->handle); result = true; } } if (result) { luaTrace(L, "videoGetFrame", "%d %ld", id, r); } else { luaDie(L, "videoGetFrame", "Failed!"); } lua_pushnumber(L, r); return 1; } int32_t apiVideoGetFrameCount(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; int64_t r = 0; int32_t id = -1; double d; VideoT *video = NULL; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); id = (int32_t)d; // Get our video structure HASH_FIND_INT(_global.videoList, &id, video); if (!video) luaDie(L, "videoGetFrameCount", "No video at index %d in apiVideoGetFrameCount.", id); r = videoGetFrameCount(video->handle); result = true; } } if (result) { luaTrace(L, "videoGetFrameCount", "%d %ld", id, r); } else { luaDie(L, "videoGetFrameCount", "Failed!"); } lua_pushnumber(L, r); return 1; } int32_t apiVideoGetHeight(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; int32_t r = 0; int32_t id = -1; double d; VideoT *video = NULL; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); id = (int32_t)d; // Get our video structure HASH_FIND_INT(_global.videoList, &id, video); if (!video) luaDie(L, "videoGetHeight", "No video at index %d in apiVideoGetHeight.", id); r = videoGetHeight(video->handle); result = true; } } if (result) { luaTrace(L, "videoGetHeight", "%d %d", id, r); } else { luaDie(L, "videoGetHeight", "Failed!"); } lua_pushnumber(L, r); return 1; } int32_t apiVideoGetVolume(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; int32_t left = 0; int32_t right = 0; int32_t id = -1; double d; VideoT *video = NULL; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); id = (int32_t)d; // Get our video structure HASH_FIND_INT(_global.videoList, &id, video); if (!video) luaDie(L, "videoGetVolume", "No video at index %d in apiVideoGetVolume.", id); videoGetVolume(video->handle, &left, &right); result = true; } } if (result) { luaTrace(L, "videoGetVolume", "%d %d %d", id, left, right); } else { luaDie(L, "videoGetVolume", "Failed!"); } lua_pushnumber(L, left); lua_pushnumber(L, right); return 2; } int32_t apiVideoGetWidth(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; int32_t r = 0; int32_t id = -1; double d; VideoT *video = NULL; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); id = (int32_t)d; // Get our video structure HASH_FIND_INT(_global.videoList, &id, video); if (!video) luaDie(L, "videoGetWidth", "No video at index %d in apiVideoGetWidth.", id); r = videoGetWidth(video->handle); result = true; } } if (result) { luaTrace(L, "videoGetWidth", "%d %d", id, r); } else { luaDie(L, "videoGetWidth", "Failed!"); } lua_pushnumber(L, r); return 1; } int32_t apiVideoIsPlaying(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; int32_t r = 0; int32_t id = -1; double d; VideoT *video = NULL; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); id = (int32_t)d; // Get our video structure HASH_FIND_INT(_global.videoList, &id, video); if (!video) luaDie(L, "videoIsPlaying", "No video at index %d in apiVideoIsPlaying.", id); r = videoIsPlaying(video->handle); result = true; } } if (result) { luaTrace(L, "videoIsPlaying", "%d %d", id, r); } else { luaDie(L, "videoIsPlaying", "Failed!"); } lua_pushnumber(L, r); return 1; } int32_t apiVideoLoad(lua_State *L) { int32_t n = lua_gettop(L); int32_t result = -1; const char *name = NULL; char *data = NULL; char *temp = NULL; VideoT *video = NULL; if (n == 1) { if (lua_isstring(L, 1)) { name = lua_tostring(L, 1); // Create data directory based on video path. temp = utilGetUpToLastPathComponent((char *)name); data = utilCreateString("%s%s", _global.conf->dataDirBase, temp); free(temp); temp = NULL; utilFixPathSeparators(&data, false); // Be sure it exists. utilMkDirP(data, 0777); if (!utilPathExists(data)) { luaDie(L, "videoLoad", "Unable to create data directory: %s", data); } // Load this video. video = (VideoT *)calloc(1, sizeof(VideoT)); if (!video) luaDie(L, "videoLoad", "Unable to allocate new video."); video->handle = videoLoad((char *)name, data, false, _global.renderer); if (video->handle < 0) luaDie(L, "videoLoad", "Failed to load video: %s", name); video->id = _global.nextVideoId; video->lastFrame = -1; result = _global.nextVideoId++; HASH_ADD_INT(_global.videoList, id, video); } } if (result >= 0) { luaTrace(L, "videoLoad", "%s %s %d", name, data, result); } else { luaDie(L, "videoLoad", "Failed!"); } free(data); lua_pushnumber(L, result); return 1; } int32_t apiVideoPause(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; int32_t id = -1; double d; VideoT *video = NULL; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); id = (int32_t)d; // Get our video structure HASH_FIND_INT(_global.videoList, &id, video); if (!video) luaDie(L, "videoPause", "No video at index %d in apiVideoPause.", id); videoPause(video->handle); result = true; } } if (result) { luaTrace(L, "videoPause", "%d", id); } else { luaDie(L, "videoPause", "Failed!"); } return 0; } int32_t apiVideoPlay(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; int32_t id = -1; double d; VideoT *video = NULL; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); id = (int32_t)d; // Get our video structure HASH_FIND_INT(_global.videoList, &id, video); if (!video) luaDie(L, "videoPlay", "No video at index %d in apiVideoPlay.", id); videoPlay(video->handle); result = true; } } if (result) { luaTrace(L, "videoPlay", "%d", id); } else { luaDie(L, "videoPlay", "Failed!"); } return 0; } int32_t apiVideoSeek(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; int32_t id = -1; int64_t frame = 0; double d; VideoT *video = NULL; if (n == 2) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { d = lua_tonumber(L, 1); id = (int32_t)d; d = lua_tonumber(L, 2); frame = (int64_t)d; // Get our video structure HASH_FIND_INT(_global.videoList, &id, video); if (!video) luaDie(L, "videoSeek", "No video at index %d in apiVideoSeek.", id); videoSeek(video->handle, frame); result = true; } } } if (result) { luaTrace(L, "videoSeek", "%d %ld", id, frame); } else { luaDie(L, "videoSeek", "Failed!"); } return 0; } int32_t apiVideoSetVolume(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; int32_t id = -1; int32_t left = 0; int32_t right = 0; double d; VideoT *video = NULL; if (n == 3) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { if (lua_isnumber(L, 3)) { d = lua_tonumber(L, 1); id = (int32_t)d; d = lua_tonumber(L, 2); left = (int32_t)d; d = lua_tonumber(L, 3); right = (int32_t)d; // Get our video structure HASH_FIND_INT(_global.videoList, &id, video); if (!video) luaDie(L, "videoSetVolume", "No video at index %d in apiVideoSetVolume.", id); if (left < 0) left = 0; if (left > 100) left = 100; if (right < 0) right = 0; if (right > 100) right = 100; videoSetVolume(video->handle, left, right); result = true; } } } } if (result) { luaTrace(L, "videoSetVolume", "%d", id, left, right); } else { luaDie(L, "videoSetVolume", "Failed!"); } return 0; } int32_t apiVideoUnload(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; int32_t id = -1; double d; VideoT *video = NULL; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); id = (int32_t)d; // Get our video structure HASH_FIND_INT(_global.videoList, &id, video); if (!video) luaDie(L, "videoUnload", "No video at index %d in apiVideoUnload.", id); HASH_DEL(_global.videoList, video); videoUnload(video->handle); if (video->surface) SDL_FreeSurface(video->surface); free(video); result = true; } } if (result) { luaTrace(L, "videoUnload", "%d", id); } else { luaDie(L, "videoUnload", "Failed!"); } return 0; } int32_t apiVldpGetHeight(lua_State *L) { int32_t height = 0; if (_global.videoHandle >= 0) height = videoGetHeight(_global.videoHandle); luaTrace(L, "vldpGetHeight", "%d", height); lua_pushinteger(L, height); return 1; } int32_t apiVldpGetPixel(lua_State *L) { int32_t n = lua_gettop(L); double d = 0; bool result = false; byte pixel[SDL_BYTESPERPIXEL(SDL_PIXELFORMAT_BGRA32)]; SDL_Rect rect; if (n == 2) { if (lua_isnumber(L, 1)) { if (lua_isnumber(L, 2)) { rect.h = 1; rect.w = 1; d = lua_tonumber(L, 1); rect.x = (int32_t)(d / _global.overlayScaleX); d = lua_tonumber(L, 2); rect.y = (int32_t)(d / _global.overlayScaleY); if (SDL_SetRenderTarget(_global.renderer, _global.videoTexture) < 0) luaDie(L, "vldpGetPixel", "%s", SDL_GetError()); if (SDL_RenderReadPixels(_global.renderer, &rect, SDL_PIXELFORMAT_BGRA32, pixel, SDL_BYTESPERPIXEL(SDL_PIXELFORMAT_BGRA32) * videoGetWidth(_global.videoHandle)) < 0) luaDie(L, "vldpGetPixel", "%s", SDL_GetError()); if (SDL_SetRenderTarget(_global.renderer, NULL) < 0) luaDie(L, "vldpGetPixel", "%s", SDL_GetError()); result = true; } } } if (result) { luaTrace(L, "vldpGetPixel", "%d %d %d %d %d", rect.x, rect.y, pixel[2], pixel[1], pixel[0]); } else { luaDie(L, "vldpGetPixel", "Failed!"); } lua_pushinteger(L, (int32_t)pixel[2]); // R lua_pushinteger(L, (int32_t)pixel[1]); // G lua_pushinteger(L, (int32_t)pixel[0]); // B return 3; } int32_t apiVldpGetWidth(lua_State *L) { int32_t width = 0; if (_global.videoHandle >= 0) width = videoGetWidth(_global.videoHandle); luaTrace(L, "vldpGetHeight", "%d", width); lua_pushinteger(L, width); return 1; } int32_t apiVldpVerbose(lua_State *L) { /* * Enables/Disables writing of VLDP playback activity to daphne_log.txt * Enabled by default. */ (void)L; //***REMOVED*** luaTrace(L, "vldpVerbose", "Unimplemented"); return 0; } int32_t apiKeyboardGetMode(lua_State *L) { luaTrace(L, "keyboardGetMode", "%d", _global.keyboardMode); lua_pushinteger(L, _global.keyboardMode); return 1; } int32_t apiKeyboardSetMode(lua_State *L) { /* * Singe can scan keyboard input in two ways: * * MODE_NORMAL - Singe will only check for keys defined * in daphne.ini. This is the default behavior. * * MODE_FULL - Singe will scan the keyboard for most keypresses. * */ int32_t n = lua_gettop(L); double d = 0; bool result = false; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); _global.keyboardMode = (int32_t)d; result = true; } } if (result) { luaTrace(L, "keyboardSetMode", "%d", _global.keyboardMode); } else { luaDie(L, "keyboardSetMode", "Failed!"); } return 0; } int32_t apiMouseEnable(lua_State *L) { // Enables mouse monitoring (void)L; luaTrace(L, "mouseEnable", "%d", _global.conf->noMouse); _global.mouseEnabled = (bool)!_global.conf->noMouse; return 0; } int32_t apiMouseDisable(lua_State *L) { // Disables mouse monitoring (void)L; luaTrace(L, "mouseDisable", ""); _global.mouseEnabled = false; return 0; } int32_t apiMouseSetCaptured(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; if (n == 1) { if (lua_isboolean(L, 1)) { _global.mouseGrabbed = (bool)lua_toboolean(L, 1); if (_global.mouseGrabbed) { // Grab mouse SDL_SetWindowGrab(_global.window, SDL_TRUE); SDL_ShowCursor(SDL_DISABLE); } else { // Ungrab mouse SDL_SetWindowGrab(_global.window, SDL_FALSE); SDL_ShowCursor(SDL_ENABLE); } result = true; } } if (result) { luaTrace(L, "mouseSetCaptured", "%d", _global.mouseGrabbed); } else { luaDie(L, "mouseSetCaptured", "Failed!"); } return 0; } int32_t apiMouseSetMode(lua_State *L) { // Sets the scanning mode for mouse input. // Can be one of two values: // // SINGLE_MOUSE = 100 // MANY_MOUSE = 200 // // Be sure to add these constant declarations to your framework.singe // By default Singe starts in single mouse mode. // Use this command if you need to scan multiple mice. // e.g. lua code, // // mouseSetMode(MANY_MOUSE) // // Function returns TRUE is mode set was successful, FALSE otherwise. // // --rdg int32_t n = lua_gettop(L); double d = 0; bool result = false; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); _global.mouseMode = (int32_t)d; result = true; } } //utilSay("MouseMode now %d", _global.mouseMode); if (result) { luaTrace(L, "mouseSetMode", "%d", _global.mouseMode); } else { luaDie(L, "mouseSetMode", "Failed!"); } lua_pushboolean(L, result); return 1; } int32_t apiMouseGetPosition(lua_State *L) { int32_t n = lua_gettop(L); int32_t m = 0; int32_t x = 0; int32_t y = 0; double d = 0; bool result = false; if (n == 1) { if (lua_isnumber(L, 1)) { d = lua_tonumber(L, 1); m = (int32_t)d; if ((m < 0) || (m >= MAX_MICE)) luaDie(L, "mouseGetPosition", "Invalid mouse index: %d", m); x = _global.axisCache[MAX_CONTROLLERS * CONTROLLER_AXIS_COUNT + m * MOUSE_AXIS_COUNT]; y = _global.axisCache[MAX_CONTROLLERS * CONTROLLER_AXIS_COUNT + m * MOUSE_AXIS_COUNT + 1]; result = true; } } if (result) { luaTrace(L, "mouseGetPosition", "%d %d %d", m, x, y); } else { luaDie(L, "mouseGetPosition", "Failed!"); } lua_pushinteger(L, x); lua_pushinteger(L, y); return 2; } int32_t apiMouseHowMany(lua_State *L) { luaTrace(L, "mouseHowMany", "%d", _global.mouseCount); lua_pushinteger(L, _global.mouseCount); return 1; } int32_t apiDiscGetHeight(lua_State *L) { int32_t r = 0; if (_global.videoHandle >= 0) r = videoGetHeight(_global.videoHandle); luaTrace(L, "discGetHeight", "%d", r); lua_pushinteger(L, r); return 1; } int32_t apiDiscGetWidth(lua_State *L) { int32_t r = 0; if (_global.videoHandle >= 0) r = videoGetWidth(_global.videoHandle); luaTrace(L, "discGetWidth", "%d", r); lua_pushinteger(L, r); return 1; } int32_t apiDiscGetState(lua_State *L) { int32_t isPlaying = -1; /* * Returns the status of the vldp * Values returned are * based on the following enumeration (found in ldp.h). * * LDP_ERROR = 0 * LDP_SEARCHING = 1 * LDP_STOPPED = 2 * LDP_PLAYING = 3 * LDP_PAUSED = 4 * */ // Our player isn't as sophisticated as the one in Daphne if (_global.videoHandle >= 0) isPlaying = videoIsPlaying(_global.videoHandle); luaTrace(L, "discGetState", "%s", _global.discStopped ? "Stopped" : (isPlaying ? "Playing" : "Paused")); lua_pushinteger(L, _global.discStopped ? LDP_STOPPED : (isPlaying ? LDP_PLAYING : LDP_PAUSED)); return 1; } int32_t apiScriptExecute(lua_State *L) { int32_t n = lua_gettop(L); ConfigT *conf = NULL; bool result = false; if (n == 1) { if (lua_istable(L, 1)) { // Push next script. conf = buildConfFromTable(L); queueScript(conf); destroyConf(&conf); // Stop this script running. _global.running = false; result = true; } } if (result) { luaTrace(L, "scriptExecute", "Success."); } else { luaDie(L, "scriptExecute", "Failed!"); } return 0; } int32_t apiScriptPush(lua_State *L) { int32_t n = lua_gettop(L); ConfigT *conf = NULL; bool result = false; if (n == 1) { if (lua_istable(L, 1)) { // Push next script. conf = buildConfFromTable(L); queueScript(conf); destroyConf(&conf); // Push this script. queueScript(_global.conf); // Stop this script running. _global.running = false; result = true; } } if (result) { luaTrace(L, "scriptExecute", "Success."); } else { luaDie(L, "scriptExecute", "Failed!"); } return 0; } int32_t apiSingeGetPauseFlag(lua_State *L) { /* * This function returns _global.pauseState's value to the lua script. * * Sometimes game logic pauses the game (which implies pausing video playback). * When implementing a pause state it is possible for the player * to resume playblack at moments where the game is not intended to. * Boolean g_global.pause state is an internal variable that keeps track * of this. It's set to true whenever sep_pre_global.pause is called. * It's set to false whenever sep_pre_play or sep_skip_to_frame is called. * * A lua programmer can use this to prevent resuming playback accidentally. */ luaTrace(L, "singeGetPauseFlag", "%d", _global.pauseState); lua_pushboolean(L, _global.pauseState); return 1; } int32_t apiSingeSetPauseFlag(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; if (n == 1) { if (lua_isboolean(L, 1)) { _global.pauseState = (bool)lua_toboolean(L, 1); updatePauseState(); result = true; } } if (result) { luaTrace(L, "singeSetPauseFlag", "%d", _global.pauseState); } else { luaDie(L, "singeSetPauseFlag", "Failed!"); } return 0; } int32_t apiSingeEnablePauseKey(lua_State *L) { (void)L; luaTrace(L, "singeEnablePauseKey", ""); _global.pauseEnabled = true; return 0; } int32_t apiSingeDisablePauseKey(lua_State *L) { (void)L; luaTrace(L, "singeDisablePauseKey", ""); _global.pauseEnabled = false; return 0; } int32_t apiSingeQuit(lua_State *L) { (void)L; luaTrace(L, "singeQuit", ""); _global.running = false; return 0; } int32_t apiSingeVersion(lua_State *L) { luaTrace(L, "singeVersion", "%f", SINGE_VERSION); lua_pushnumber(L, SINGE_VERSION); return 1; } int32_t apiSingeWantsCrosshairs(lua_State *L) { luaTrace(L, "singeWantsCrosshairs", "%f", !_global.conf->noCrosshair); lua_pushboolean(L, !_global.conf->noCrosshair); return 1; } int32_t apiSingeGetDataPath(lua_State *L) { luaTrace(L, "singeGetDataPath", "%s", _global.conf->dataDir); lua_pushstring(L, _global.conf->dataDir); return 1; } int32_t apiSingeSetGameName(lua_State *L) { int32_t n = lua_gettop(L); bool result = false; if (n == 1) { if (lua_isstring(L, 1)) { SDL_SetWindowTitle(_global.window, lua_tostring(L, 1)); result = true; } } if (result) { luaTrace(L, "singeSetGameName", "%s", lua_tostring(L, 1)); } else { luaDie(L, "singeSetGameName", "Failed!"); } return 0; } int32_t apiSingeGetScriptPath(lua_State *L) { luaTrace(L, "singeGetScriptPath", "%s", _global.conf->scriptFile); lua_pushstring(L, _global.conf->scriptFile); return 1; } ConfigT *buildConfFromTable(lua_State *L) { char *sindenString = NULL; const char *confKey = NULL; const char *valueString = NULL; bool valueBoolean = false; int64_t valueNumber = 0; ConfigT *c = NULL; // Start with current config. c = cloneConf(_global.conf); // Update with data in the table on the top of the Lua stack. lua_pushnil(L); while (lua_next(L, 1)) { // Get key confKey = lua_tostring(L, 2); // Get value switch (lua_type(L, 3)) { case LUA_TSTRING: valueString = lua_tostring(L, 3); valueBoolean = false; valueNumber = 0; break; case LUA_TBOOLEAN: valueString = NULL; valueBoolean = lua_toboolean(L, 3); valueNumber = 0; break; case LUA_TNUMBER: valueString = NULL; valueBoolean = false; valueNumber = lua_tonumber(L, 3); break; default: valueString = NULL; valueBoolean = false; valueNumber = 0; break; } // Update config with new data if (strcmp(confKey, "SCRIPT") == 0) { if (c->scriptFile) free (c->scriptFile); c->scriptFile = strdup(valueString); utilFixPathSeparators(&c->scriptFile, false); } else if (strcmp(confKey, "VIDEO") == 0) { if (c->videoFile) free(c->videoFile); c->videoFile = strdup(valueString); utilFixPathSeparators(&c->videoFile, false); // Is it a framefile? if (strncmp(utilGetFileExtension(c->videoFile), "txt", 3) == 0) { c->isFrameFile = true; } } else if (strcmp(confKey, "STRETCH") == 0) { c->stretchVideo = valueBoolean; } else if (strcmp(confKey, "NO_MOUSE") == 0) { c->noMouse = valueBoolean; } else if (strcmp(confKey, "RESOLUTION_X") == 0) { c->xResolution = valueNumber; } else if (strcmp(confKey, "RESOLUTION_Y") == 0) { c->yResolution = valueNumber; } else if (strcmp(confKey, "SINDEN_GUN") == 0) { if (sindenString) free(sindenString); sindenString = strdup(valueString); if (!parseSindenString(&sindenString, c)) c->sindenArgc = 0; free(sindenString); } // Clean up for next pair lua_pop(L, 1); } // Create new data dir location based on script location. if (c->dataDir) free(c->dataDir); c->dataDir = utilCreateString("%s%s", c->dataDirBase, utilGetUpToLastPathComponent(c->scriptFile)); // Try to create data directory to ensure it exists. utilMkDirP(c->dataDir, 0777); // Does it exist? if (!utilPathExists(c->dataDir)) { utilDie("Unable to create data directory: %s", c->dataDir); } return c; } void callLua(const char *func, const char *sig, ...) { va_list vl; bool done = false; int32_t narg; int32_t nres; int32_t popCount; double d; const int32_t top = lua_gettop(_global.luaContext); va_start(vl, sig); // Get Function lua_getglobal(_global.luaContext, func); if (!lua_isfunction(_global.luaContext, -1)) { // Function does not exist. Bail. lua_settop(_global.luaContext, top); return; } if (_global.conf->scriptTracing) utilTrace("%s", func); // Push Arguments narg = 0; while ((*sig) && (!done)) { switch (*sig++) { case 'd': // Double lua_pushnumber(_global.luaContext, va_arg(vl, double)); break; case 'i': // Int lua_pushinteger(_global.luaContext, va_arg(vl, int)); // Not sure I want to change this to int32_t break; case 's': // String lua_pushstring(_global.luaContext, va_arg(vl, char *)); break; case '>': done = true; break; default: utilDie("Invalid argument option (%c)", *(sig - 1)); } if (!done) { narg++; luaL_checkstack(_global.luaContext, 1, "Too many arguments"); } } // Do the call popCount = nres = (int32_t)strlen(sig); // Number of expected results if (lua_pcall(_global.luaContext, narg, nres, 0) != 0) { utilSay("Error executing function '%s': %s", func, lua_tostring(_global.luaContext, -1)); return; } // Retrieve results nres = -nres; // Stack index of first result while (*sig) { switch (*sig++) { case 'd': // Double if (!lua_isnumber(_global.luaContext, nres)) { utilDie("Wrong result type"); } *va_arg(vl, double *) = lua_tonumber(_global.luaContext, nres); break; case 'i': // Int if (!lua_isnumber(_global.luaContext, nres)) { utilDie("Wrong result type"); } d = lua_tonumber(_global.luaContext, nres); *va_arg(vl, int *) = (int32_t)d; // Not sure I want to change this to int32_t break; case 's': // String if (!lua_isstring(_global.luaContext, nres)) { utilDie("Wrong result type"); } *va_arg(vl, const char **) = lua_tostring(_global.luaContext, nres); break; default: utilDie("Invalid option (%c)", *(sig - 1)); } nres++; } va_end(vl); if (popCount > 0) { lua_pop(_global.luaContext, popCount); } } void channelFinished(int channel) { callLua("onSoundCompleted", "i", channel); } void doIndexDisplay(int32_t percent) { static int32_t oldW = 0; static int32_t oldH = 0; static int32_t screenW = 1280; static int32_t screenH = 720; static int32_t radius = 0; static int32_t angle = 0; static uint32_t nextUpdate = 0; static uint32_t updateTicks = 0; static SDL_Surface *surfDisc = NULL; static SDL_Surface *surfGlass = NULL; static SDL_Surface *surfIndex = NULL; static SDL_Texture *texDisc = NULL; static SDL_Texture *texGlass = NULL; static SDL_Texture *texIndex = NULL; SDL_Rect target; int32_t vShift; // If 'percent' is -1, we're setting this display up. // 'percent' 0 to 100 is the actual display. // 'percent' -2 shuts the display down. if (percent == -1) { // Setup SDL_RenderGetLogicalSize(_global.renderer, &oldW, &oldH); SDL_RenderSetLogicalSize(_global.renderer, screenW, screenH); surfGlass = IMG_LoadPNG_RW(SDL_RWFromMem(magnifyingGlass_png, magnifyingGlass_png_len)); if (!surfGlass) utilDie("%s", IMG_GetError()); surfDisc = IMG_LoadPNG_RW(SDL_RWFromMem(laserDisc_png, laserDisc_png_len)); if (!surfDisc) utilDie("%s", IMG_GetError()); surfIndex = IMG_LoadPNG_RW(SDL_RWFromMem(indexing_png, indexing_png_len)); if (!surfIndex) utilDie("%s", IMG_GetError()); texDisc = SDL_CreateTextureFromSurface(_global.renderer, surfDisc); if (!texDisc) utilDie("%s", SDL_GetError()); texGlass = SDL_CreateTextureFromSurface(_global.renderer, surfGlass); if (!texGlass) utilDie("%s", SDL_GetError()); texIndex = SDL_CreateTextureFromSurface(_global.renderer, surfIndex); if (!texIndex) utilDie("%s", SDL_GetError()); radius = surfDisc->w * 0.3; updateTicks = 5; nextUpdate = SDL_GetTicks() + updateTicks; return; } if (percent == -2) { // Shutdown SDL_DestroyTexture(texDisc); SDL_DestroyTexture(texGlass); SDL_DestroyTexture(texIndex); texDisc = NULL; texGlass = NULL; texIndex = NULL; SDL_FreeSurface(surfDisc); SDL_FreeSurface(surfGlass); SDL_FreeSurface(surfIndex); surfDisc = NULL; surfGlass = NULL; surfIndex = NULL; SDL_RenderSetLogicalSize(_global.renderer, oldW, oldH); SDL_SetRenderDrawColor(_global.renderer, 0, 0, 0, 255); SDL_RenderClear(_global.renderer); SDL_RenderPresent(_global.renderer); return; } // Display animation SDL_RenderSetLogicalSize(_global.renderer, screenW, screenH); SDL_SetRenderDrawColor(_global.renderer, 0, 0, 0, 255); SDL_RenderClear(_global.renderer); // Draw "Indexing" text vShift = surfIndex->h - 5; target.x = (screenW * 0.5) - (surfIndex->w * 0.5); target.y = screenH - vShift; target.w = surfIndex->w; target.h = surfIndex->h; SDL_RenderCopy(_global.renderer, texIndex, NULL, &target); // Draw laserdisc target.x = (screenW * 0.5) - (surfDisc->w * 0.5); target.y = (screenH * 0.5) - (surfDisc->h * 0.5) - vShift; target.w = surfDisc->w; target.h = surfDisc->h; SDL_RenderCopy(_global.renderer, texDisc, NULL, &target); // Draw magnifying glass target.x = (surfDisc->w * 0.15) + (screenW * 0.5) - (surfGlass->w * 0.5) + cos(angle * M_PI / 180) * radius; target.y = (surfDisc->h * 0.1) + (screenH * 0.5) - vShift - (surfGlass->h * 0.5) + sin(angle * M_PI / 180) * radius; target.w = surfGlass->w; target.h = surfGlass->h; SDL_RenderCopy(_global.renderer, texGlass, NULL, &target); // Update animation if (SDL_GetTicks() > nextUpdate) { //angle += (SDL_GetTicks() - nextUpdate) / updateTicks; angle++; if (angle > 359) angle -= 360; nextUpdate = SDL_GetTicks() + updateTicks; } SDL_RenderPresent(_global.renderer); } void doLogos(void) { int32_t i = 0; int32_t w = 0; int32_t h = 0; SDL_Surface *surfKangaroo = NULL; SDL_Surface *surfSinge = NULL; SDL_Texture *texKangaroo = NULL; SDL_Texture *texSinge = NULL; SDL_RenderGetLogicalSize(_global.renderer, &w, &h); surfKangaroo = IMG_LoadPNG_RW(SDL_RWFromMem(kangarooPunchLogo_png, kangarooPunchLogo_png_len)); if (!surfKangaroo) utilDie("%s", IMG_GetError()); surfSinge = IMG_LoadPNG_RW(SDL_RWFromMem(singeLogo_png, singeLogo_png_len)); if (!surfSinge) utilDie("%s", IMG_GetError()); texKangaroo = SDL_CreateTextureFromSurface(_global.renderer, surfKangaroo); if (!texKangaroo) utilDie("%s", SDL_GetError()); texSinge = SDL_CreateTextureFromSurface(_global.renderer, surfSinge); if (!texSinge) utilDie("%s", SDL_GetError()); // Fade in to white with Kangaroo logo SDL_RenderSetLogicalSize(_global.renderer, surfKangaroo->w, surfKangaroo->h); for (i=0; i<256; i++) { SDL_SetRenderDrawColor(_global.renderer, i, i, i, 255); SDL_RenderClear(_global.renderer); SDL_SetTextureAlphaMod(texKangaroo, i); SDL_RenderCopy(_global.renderer, texKangaroo, NULL, NULL); SDL_RenderPresent(_global.renderer); SDL_Delay(3); } SDL_Delay(750); // Cross fade to Singe logo for (i=0; i<256; i++) { SDL_RenderClear(_global.renderer); SDL_SetTextureAlphaMod(texKangaroo, 255 - i); SDL_RenderCopy(_global.renderer, texKangaroo, NULL, NULL); SDL_SetTextureAlphaMod(texSinge, i); SDL_RenderCopy(_global.renderer, texSinge, NULL, NULL); SDL_RenderPresent(_global.renderer); SDL_Delay(3); } SDL_Delay(750); // Fade to black SDL_RenderSetLogicalSize(_global.renderer, surfSinge->w, surfSinge->h); for (i=255; i>=0; i--) { SDL_SetRenderDrawColor(_global.renderer, i, i, i, 255); SDL_RenderClear(_global.renderer); SDL_SetTextureAlphaMod(texSinge, i); SDL_RenderCopy(_global.renderer, texSinge, NULL, NULL); SDL_RenderPresent(_global.renderer); SDL_Delay(3); } SDL_DestroyTexture(texSinge); SDL_DestroyTexture(texKangaroo); SDL_FreeSurface(surfSinge); SDL_FreeSurface(surfKangaroo); SDL_RenderSetLogicalSize(_global.renderer, w, h); } void line(int32_t x1, int32_t y1, int32_t x2, int32_t y2, SDL_Color *c) { int32_t x = 0; int32_t y = 0; int32_t dx = 0; int32_t dy = 0; int32_t incX = 0; int32_t incY = 0; int32_t balance = 0; if (x2 >= x1) { dx = x2 - x1; incX = 1; } else { dx = x1 - x2; incX = -1; } if (y2 >= y1) { dy = y2 - y1; incY = 1; } else { dy = y1 - y2; incY = -1; } x = x1; y = y1; if (dx >= dy) { dy <<= 1; balance = dy - dx; dx <<= 1; while (x != x2) { putPixel(x, y, c); if (balance >= 0) { y += incY; balance -= dx; } balance += dy; x += incX; } putPixel(x, y, c); } else { dx <<= 1; balance = dx - dy; dy <<= 1; while (y != y2) { putPixel(x, y, c); if (balance >= 0) { x += incX; balance -= dy; } balance += dx; y += incY; } putPixel(x, y, c); } } void luaDie(lua_State *L, char *method, char *fmt, ...) { va_list args; lua_Debug ar; char *string1 = NULL; char *string2 = NULL; lua_getstack(L, 1, &ar); lua_getinfo(L, "nSl", &ar); string1 = utilCreateString("%d:%s: ", ar.currentline, method); if (!string1) utilDie("Unable to allocate first trace string."); va_start(args, fmt); string2 = utilCreateStringVArgs(fmt, args); if (!string2) utilDie("Unable to allocate second trace string."); va_end(args); if (_global.conf->scriptTracing) utilTrace("%s%s", string1, string2); utilDie("%s%s", string1, string2); // Can't free strings - we never get here. } int32_t luaError(lua_State *L) { lua_Debug ar; int32_t level = 0; utilSay("Singe has panicked! Very bad!"); utilSay("Error: %s", lua_tostring(L, -1)); utilSay("Stack trace:"); while (lua_getstack(L, level, &ar) != 0) { lua_getinfo(L, "nSl", &ar); utilSay(" %d: function `%s' at line %d %s", level, ar.name, ar.currentline, ar.short_src); level++; } utilSay("Trace complete."); return 0; } #ifdef DEBUG_TOOLS void luaStackDump(lua_State *L) { int i; int t; int top = lua_gettop(L); printf("%d: ", top); for (i=1; i<=top; i++) { printf("%d - ", i); t = lua_type(L, i); switch (t) { case LUA_TSTRING: printf("`%s'", lua_tostring(L, i)); break; case LUA_TBOOLEAN: printf(lua_toboolean(L, i) ? "true" : "false"); break; case LUA_TNUMBER: printf("%g", lua_tonumber(L, i)); break; default: printf("%s", lua_typename(L, t)); break; } printf(" "); } printf("\n"); } #endif void luaPreload(lua_State *L, const char *name, lua_CFunction func) { luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); lua_pushcfunction(L, func); lua_setfield(L, -2, name); lua_pop(L, 1); // remove PRELOAD table } void luaTrace(lua_State *L, char *method, char *fmt, ...) { va_list args; lua_Debug ar; char *string1 = NULL; char *string2 = NULL; if (_global.conf->scriptTracing) { lua_getstack(L, 1, &ar); lua_getinfo(L, "nSl", &ar); string1 = utilCreateString("%d:%s: ", ar.currentline, method); if (!string1) utilDie("Unable to allocate first trace string."); va_start(args, fmt); string2 = utilCreateStringVArgs(fmt, args); if (!string2) utilDie("Unable to allocate second trace string."); va_end(args); utilTrace("%s%s", string1, string2); free(string2); free(string1); } } void processKey(bool down, int32_t keysym, int32_t scancode) { int32_t move; int32_t index; //utilSay("U:%d SY:%d SC:%d", down, keysym, scancode); if (_global.keyboardMode == KEYBD_NORMAL) { // Mappable keys for (move=0; move 0) { for (index=0; index<_global.controlMappings[move].inputCount; index++) { //utilSay("Checking %s %d = %d", _global.controlMappings[move].name, _global.controlMappings[move].input[index], scancode); if (_global.controlMappings[move].input[index] == scancode) { //utilSay("Sending move %d - %s %d - %s", move, _global.controlMappings[move].name, _global.controlMappings[move].input[index], down ? "down" : "up"); if (!down) { if ((move == INPUT_PAUSE) && (_global.pauseEnabled)) { _global.pauseState = _global.pauseState ? false : true; updatePauseState(); } if (move == INPUT_GRAB) { if (_global.mouseGrabbed) { // Ungrab mouse _global.mouseGrabbed = false; SDL_SetWindowGrab(_global.window, SDL_FALSE); SDL_ShowCursor(SDL_ENABLE); } else { // Grab mouse _global.mouseGrabbed = true; SDL_SetWindowGrab(_global.window, SDL_TRUE); SDL_ShowCursor(SDL_DISABLE); } } if (move == INPUT_QUIT) { _global.running = false; } if (move == INPUT_SCREENSHOT) { _global.requestScreenShot = true; } } callLua(down ? "onInputPressed" : "onInputReleased", "i", move); } } } } } else { // Full keyboard callLua(down ? "onInputPressed" : "onInputReleased", "i", keysym); callLua(down ? "onKeyPressed" : "onKeyReleased", "ii", keysym, scancode); } } void progTrace(char *fmt, ...) { va_list args; if (_global.conf->programTracing) { va_start(args, fmt); utilTraceVArgs(fmt, args); va_end(args); } } void putPixel(int32_t x, int32_t y, SDL_Color *c) { SDL_Surface *surface = _global.overlay; int32_t bpp = surface->format->BytesPerPixel; Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp; Uint32 pixel = SDL_MapRGBA(surface->format, c->r, c->g, c->b, c->a); if ((x < 0) || (x >= _global.overlay->w) || (y < 0) || (y >= _global.overlay->h)) return; switch (bpp) { case 1: *p = (Uint8)pixel; break; case 2: *(Uint16 *)p = (Uint16)pixel; break; case 3: if (SDL_BYTEORDER == SDL_BIG_ENDIAN) { p[0] = (pixel >> 16) & 0xff; p[1] = (pixel >> 8) & 0xff; p[2] = pixel & 0xff; } else { p[0] = pixel & 0xff; p[1] = (pixel >> 8) & 0xff; p[2] = (pixel >> 16) & 0xff; } break; case 4: *(Uint32 *)p = pixel; break; } } void singe(SDL_Window *window, SDL_Renderer *renderer, ConfigT *conf) { int32_t x = 0; int32_t y = 0; int32_t xr = 0; int32_t yr = 0; int32_t intReturn = 0; int32_t axisIndex = 0; int64_t thisFrame = -1; int64_t lastFrame = -1; uint32_t frameClock = 0; bool changed = false; char *temp = NULL; char *temp2 = NULL; SDL_Rect windowTarget; SDL_Rect sindenWhite; SDL_Rect sindenBlack; SDL_Color sindenWhiteColor = { 255, 255, 255, 255 }; SDL_Color sindenBlackColor = { 0, 0, 0, 255 };; SDL_Texture *overlayTexture = NULL; SpriteT *sprite = NULL; SpriteT *spriteTemp = NULL; SoundT *sound = NULL; SoundT *soundTemp = NULL; FontT *font = NULL; FontT *fontTemp = NULL; VideoT *video = NULL; VideoT *videoTemp = NULL; SDL_Event event; ManyMouseEvent mouseEvent; MouseT *mouse = NULL; int32_t lastAnalogDirection[AXIS_COUNT]; //float val = 0; //float maxval = 0; int32_t lastMouseX = 0; int32_t lastMouseY = 0; // Set up globals memset(&_global, 0, sizeof(GlobalT)); _global.colorForeground.r = 255; _global.colorForeground.g = 255; _global.colorForeground.b = 255; _global.colorForeground.a = 255; _global.effectsVolume = AUDIO_MAX_VOLUME; _global.keyboardMode = KEYBD_NORMAL; _global.frameFileHandle = -1; _global.videoHandle = -1; _global.fontQuality = FONT_QUALITY_SOLID; _global.mouseMode = MOUSE_SINGLE; _global.overlayScaleX = 1; _global.overlayScaleY = 1; _global.pauseEnabled = true; _global.running = true; _global.discStopped = true; _global.mouseEnabled = true; // Local copy of config _global.conf = cloneConf(conf); // Input mappings _global.controlMappings[INPUT_UP].name = "INPUT_UP"; _global.controlMappings[INPUT_LEFT].name = "INPUT_LEFT"; _global.controlMappings[INPUT_DOWN].name = "INPUT_DOWN"; _global.controlMappings[INPUT_RIGHT].name = "INPUT_RIGHT"; _global.controlMappings[INPUT_1P_START].name = "INPUT_1P_START"; _global.controlMappings[INPUT_2P_START].name = "INPUT_2P_START"; _global.controlMappings[INPUT_ACTION_1].name = "INPUT_ACTION_1"; _global.controlMappings[INPUT_ACTION_2].name = "INPUT_ACTION_2"; _global.controlMappings[INPUT_ACTION_3].name = "INPUT_ACTION_3"; _global.controlMappings[INPUT_1P_COIN].name = "INPUT_1P_COIN"; _global.controlMappings[INPUT_2P_COIN].name = "INPUT_2P_COIN"; _global.controlMappings[INPUT_SKILL_EASY].name = "INPUT_SKILL_EASY"; _global.controlMappings[INPUT_SKILL_MEDIUM].name = "INPUT_SKILL_MEDIUM"; _global.controlMappings[INPUT_SKILL_HARD].name = "INPUT_SKILL_HARD"; _global.controlMappings[INPUT_SERVICE].name = "INPUT_SERVICE"; _global.controlMappings[INPUT_TEST_MODE].name = "INPUT_TEST_MODE"; _global.controlMappings[INPUT_RESET_CPU].name = "INPUT_RESET_CPU"; _global.controlMappings[INPUT_SCREENSHOT].name = "INPUT_SCREENSHOT"; _global.controlMappings[INPUT_QUIT].name = "INPUT_QUIT"; _global.controlMappings[INPUT_PAUSE].name = "INPUT_PAUSE"; _global.controlMappings[INPUT_CONSOLE].name = "INPUT_CONSOLE"; _global.controlMappings[INPUT_ACTION_4].name = "INPUT_ACTION_4"; _global.controlMappings[INPUT_TILT].name = "INPUT_TILT"; _global.controlMappings[INPUT_GRAB].name = "INPUT_GRAB"; for (x=0; xdataDir, utilGetPathSeparator()); if (utilFileExists(temp)) { progTrace("Loading %s", temp); if (luaL_dofile(_global.luaContext, temp)) utilDie("%s", lua_tostring(_global.luaContext, -1)); } free(temp); // Load mappings in game data dir temp = utilCreateString("%scontrols.cfg", _global.conf->dataDir); if (utilFileExists(temp)) { progTrace("Loading %s", temp); if (luaL_dofile(_global.luaContext, temp)) utilDie("%s", lua_tostring(_global.luaContext, -1)); } free(temp); // Load mappings in game script dir temp = strdup(_global.conf->scriptFile); temp2 = utilStrndup(temp, strlen(temp) - strlen(utilGetLastPathComponent(temp))); free(temp); temp = utilCreateString("%scontrols.cfg", temp2); if (utilFileExists(temp)) { progTrace("Loading %s", temp); if (luaL_dofile(_global.luaContext, temp)) utilDie("%s", lua_tostring(_global.luaContext, -1)); } free(temp); free(temp2); // Parse results lua_getglobal(_global.luaContext, "DEAD_ZONE"); if (lua_isnumber(_global.luaContext, -1)) { _global.controllerDeadZone = (int32_t)lua_tonumber(_global.luaContext, -1); progTrace("Controller dead zone is %d", _global.controllerDeadZone); } lua_pop(_global.luaContext, 1); for (x=0; x 0) { _global.controlMappings[x].inputCount = y; _global.controlMappings[x].input = (int32_t *)malloc(sizeof(int32_t) * y); if (!_global.controlMappings[x].input) utilDie("Unable to allocate memory for control mappings."); } } else { utilSay("Configuration option %s missing!", _global.controlMappings[x].name); } lua_pop(_global.luaContext, 1); // Then load them for real. lua_getglobal(_global.luaContext, _global.controlMappings[x].name); if (lua_istable(_global.luaContext, -1)) { y = 0; lua_pushnil(_global.luaContext); while (lua_next(_global.luaContext, -2)) { if (lua_istable(_global.luaContext, -1)) { lua_pushnil(_global.luaContext); while (lua_next(_global.luaContext, -2)) { if (lua_type(_global.luaContext, -2) == LUA_TSTRING) { if (utilStricmp((char *)lua_tostring(_global.luaContext, -2), "value") == 0) { _global.controlMappings[x].input[y++] = (int32_t)lua_tonumber(_global.luaContext, -1); } } lua_pop(_global.luaContext, 1); } } lua_pop(_global.luaContext, 1); } } lua_pop(_global.luaContext, 1); } lua_close(_global.luaContext); // Show splash screens if (!_global.conf->noLogos) { progTrace("Showing splash screens"); doLogos(); } // Start Lua for game progTrace("Creating Lua context for script"); _global.luaContext = luaL_newstate(); startLuaContext(_global.luaContext); // Lua API for Singe lua_register(_global.luaContext, "colorBackground", apiColorBackground); lua_register(_global.luaContext, "colorForeground", apiColorForeground); lua_register(_global.luaContext, "controllerGetAxis", apiControllerGetAxis); lua_register(_global.luaContext, "debugPrint", apiDebugPrint); lua_register(_global.luaContext, "discAudio", apiDiscAudio); lua_register(_global.luaContext, "discChangeSpeed", apiDiscChangeSpeed); lua_register(_global.luaContext, "discGetFrame", apiDiscGetFrame); lua_register(_global.luaContext, "discGetHeight", apiDiscGetHeight); lua_register(_global.luaContext, "discGetState", apiDiscGetState); lua_register(_global.luaContext, "discGetWidth", apiDiscGetWidth); lua_register(_global.luaContext, "discPause", apiDiscPause); lua_register(_global.luaContext, "discPauseAtFrame", apiDiscPauseAtFrame); lua_register(_global.luaContext, "discPlay", apiDiscPlay); lua_register(_global.luaContext, "discSearch", apiDiscSearch); lua_register(_global.luaContext, "discSearchBlanking", apiDiscSearchBlanking); lua_register(_global.luaContext, "discSetFPS", apiDiscSetFps); lua_register(_global.luaContext, "discSkipBackward", apiDiscSkipBackward); lua_register(_global.luaContext, "discSkipBlanking", apiDiscSkipBlanking); lua_register(_global.luaContext, "discSkipForward", apiDiscSkipForward); lua_register(_global.luaContext, "discSkipToFrame", apiDiscSkipToFrame); lua_register(_global.luaContext, "discStepBackward", apiDiscStepBackward); lua_register(_global.luaContext, "discStepForward", apiDiscStepForward); lua_register(_global.luaContext, "discStop", apiDiscStop); lua_register(_global.luaContext, "fontLoad", apiFontLoad); lua_register(_global.luaContext, "fontPrint", apiFontPrint); lua_register(_global.luaContext, "fontQuality", apiFontQuality); lua_register(_global.luaContext, "fontSelect", apiFontSelect); lua_register(_global.luaContext, "fontToSprite", apiFontToSprite); lua_register(_global.luaContext, "fontUnload", apiFontUnload); lua_register(_global.luaContext, "keyboardGetMode", apiKeyboardGetMode); lua_register(_global.luaContext, "keyboardSetMode", apiKeyboardSetMode); lua_register(_global.luaContext, "mouseEnable", apiMouseEnable); lua_register(_global.luaContext, "mouseDisable", apiMouseDisable); lua_register(_global.luaContext, "mouseGetPosition", apiMouseGetPosition); lua_register(_global.luaContext, "mouseHowMany", apiMouseHowMany); lua_register(_global.luaContext, "mouseSetCaptured", apiMouseSetCaptured); lua_register(_global.luaContext, "mouseSetMode", apiMouseSetMode); lua_register(_global.luaContext, "overlayBox", apiOverlayBox); lua_register(_global.luaContext, "overlayCircle", apiOverlayCircle); lua_register(_global.luaContext, "overlayClear", apiOverlayClear); lua_register(_global.luaContext, "overlayEllipse", apiOverlayEllipse); lua_register(_global.luaContext, "overlayGetHeight", apiOverlayGetHeight); lua_register(_global.luaContext, "overlayGetWidth", apiOverlayGetWidth); lua_register(_global.luaContext, "overlayLine", apiOverlayLine); lua_register(_global.luaContext, "overlayPlot", apiOverlayPlot); lua_register(_global.luaContext, "overlayPrint", apiOverlayPrint); lua_register(_global.luaContext, "overlaySetResolution", apiOverlaySetResolution); lua_register(_global.luaContext, "scriptExecute", apiScriptExecute); lua_register(_global.luaContext, "scriptPush", apiScriptPush); lua_register(_global.luaContext, "singeDisablePauseKey", apiSingeDisablePauseKey); lua_register(_global.luaContext, "singeEnablePauseKey", apiSingeEnablePauseKey); lua_register(_global.luaContext, "singeGetDataPath", apiSingeGetDataPath); lua_register(_global.luaContext, "singeGetHeight", apiSingeGetHeight); lua_register(_global.luaContext, "singeGetPauseFlag", apiSingeGetPauseFlag); lua_register(_global.luaContext, "singeGetScriptPath", apiSingeGetScriptPath); lua_register(_global.luaContext, "singeGetWidth", apiSingeGetWidth); lua_register(_global.luaContext, "singeScreenshot", apiSingeScreenshot); lua_register(_global.luaContext, "singeSetGameName", apiSingeSetGameName); lua_register(_global.luaContext, "singeSetPauseFlag", apiSingeSetPauseFlag); lua_register(_global.luaContext, "singeQuit", apiSingeQuit); lua_register(_global.luaContext, "singeVersion", apiSingeVersion); lua_register(_global.luaContext, "singeWantsCrosshairs", apiSingeWantsCrosshairs); lua_register(_global.luaContext, "soundFullStop", apiSoundFullStop); lua_register(_global.luaContext, "soundGetVolume", apiSoundGetVolume); lua_register(_global.luaContext, "soundIsPlaying", apiSoundIsPlaying); lua_register(_global.luaContext, "soundLoad", apiSoundLoad); lua_register(_global.luaContext, "soundPause", apiSoundPause); lua_register(_global.luaContext, "soundPlay", apiSoundPlay); lua_register(_global.luaContext, "soundResume", apiSoundResume); lua_register(_global.luaContext, "soundSetVolume", apiSoundSetVolume); lua_register(_global.luaContext, "soundStop", apiSoundStop); lua_register(_global.luaContext, "soundUnload", apiSoundUnload); lua_register(_global.luaContext, "spriteDraw", apiSpriteDraw); lua_register(_global.luaContext, "spriteGetHeight", apiSpriteGetHeight); lua_register(_global.luaContext, "spriteGetWidth", apiSpriteGetWidth); lua_register(_global.luaContext, "spriteLoad", apiSpriteLoad); lua_register(_global.luaContext, "spriteUnload", apiSpriteUnload); lua_register(_global.luaContext, "videoDraw", apiVideoDraw); lua_register(_global.luaContext, "videoGetFrame", apiVideoGetFrame); lua_register(_global.luaContext, "videoGetFrameCount", apiVideoGetFrameCount); lua_register(_global.luaContext, "videoGetHeight", apiVideoGetHeight); lua_register(_global.luaContext, "videoGetVolume", apiVideoGetVolume); lua_register(_global.luaContext, "videoGetWidth", apiVideoGetWidth); lua_register(_global.luaContext, "videoIsPlaying", apiVideoIsPlaying); lua_register(_global.luaContext, "videoLoad", apiVideoLoad); lua_register(_global.luaContext, "videoPause", apiVideoPause); lua_register(_global.luaContext, "videoPlay", apiVideoPlay); lua_register(_global.luaContext, "videoSeek", apiVideoSeek); lua_register(_global.luaContext, "videoSetVolume", apiVideoSetVolume); lua_register(_global.luaContext, "videoUnload", apiVideoUnload); lua_register(_global.luaContext, "vldpGetHeight", apiVldpGetHeight); lua_register(_global.luaContext, "vldpGetPixel", apiVldpGetPixel); lua_register(_global.luaContext, "vldpGetWidth", apiVldpGetWidth); lua_register(_global.luaContext, "vldpSetVerbose", apiVldpVerbose); // Open main video file progTrace("Opening main video file"); doIndexDisplay(-1); videoSetIndexCallback(doIndexDisplay); if (_global.conf->isFrameFile) { _global.frameFileHandle = frameFileLoad(_global.conf->videoFile, _global.conf->dataDir, (bool)_global.conf->stretchVideo, _global.renderer, _global.conf->showCalculated); if (_global.frameFileHandle < 0) utilDie("Unable to load framefile: %s", _global.conf->videoFile); frameFileSeek(_global.frameFileHandle, 0, &_global.videoHandle, &thisFrame); // Fills in _global.videoHandle } else { _global.videoHandle = videoLoad(_global.conf->videoFile, _global.conf->dataDir, (bool)_global.conf->stretchVideo, _global.renderer); } if (_global.videoHandle < 0) utilDie("Unable to load video file: %s", _global.conf->videoFile); videoSetVolume(_global.videoHandle, _global.conf->volumeVldp, _global.conf->volumeVldp); videoSetIndexCallback(NULL); doIndexDisplay(-2); // Should we resize the window? if (conf->resolutionWasCalculated && !conf->fullScreen && !conf->fullScreenWindow) { // Is the video wider than the display window? if ((videoGetWidth(_global.videoHandle) / videoGetHeight(_global.videoHandle)) > (conf->xResolution / conf->yResolution)) { // Find new window height conf->yResolution = ((float)conf->xResolution / (float)videoGetWidth(_global.videoHandle) * (float)videoGetHeight(_global.videoHandle)); changed = true; } else { // Find new window width conf->xResolution = ((float)conf->yResolution / (float)videoGetHeight(_global.videoHandle) * (float)videoGetWidth(_global.videoHandle)); changed = true; } if (changed) { progTrace("Resizing window to %dx%d based on main video file", conf->xResolution, conf->yResolution); SDL_SetWindowSize(_global.window, conf->xResolution, conf->yResolution); progTrace("Destroying old renderer"); SDL_DestroyRenderer(_global.renderer); // Recreate an accelerated renderer. progTrace("Creating new renderer"); _global.renderer = SDL_CreateRenderer(_global.window, -1, SDL_RENDERER_ACCELERATED); if (_global.renderer == NULL) utilDie("%s", SDL_GetError()); // Clear screen with black SDL_SetRenderDrawColor(_global.renderer, 0, 0, 0, 255); SDL_RenderClear(_global.renderer); changed = false; } } // Default render location is the entire window windowTarget.x = 0; windowTarget.y = 0; windowTarget.w = videoGetWidth(_global.videoHandle); windowTarget.h = videoGetHeight(_global.videoHandle); sindenWhite.x = -1; sindenBlack.x = -1; // Overscan compensation if (_global.conf->scaleFactor < 100) { windowTarget.w = videoGetWidth(_global.videoHandle) * _global.conf->scaleFactor / 100; windowTarget.h = videoGetHeight(_global.videoHandle) * _global.conf->scaleFactor / 100; windowTarget.x = (videoGetWidth(_global.videoHandle) - windowTarget.w) / 2; windowTarget.y = (videoGetHeight(_global.videoHandle) - windowTarget.h) / 2; } // Sinden Light Gun Border Setup if (_global.conf->sindenArgc > 0) { //***TODO*** ADD MOUSE SCALING TO COMPENSATE FOR BORDER sindenWhiteColor.r = 255; sindenWhiteColor.g = 255; sindenWhiteColor.b = 255; sindenWhiteColor.a = 255; sindenBlackColor.r = 0; sindenBlackColor.g = 0; sindenBlackColor.b = 0; sindenBlackColor.a = 255; // Ok, this thing can have a mess of different arguments: switch(_global.conf->sindenArgc) { // WW - Just the width of the white border case SINDEN_WHITE: sindenWhite.x = _global.conf->sindenArgv[0]; break; // WW WB - Width of white border and then black border case SINDEN_WHITE_BLACK: sindenWhite.x = _global.conf->sindenArgv[0]; sindenBlack.x = _global.conf->sindenArgv[1]; break; // RW GW BW WW - Custom color "white" border and width case SINDEN_CUSTOM_WHITE: sindenWhiteColor.r = _global.conf->sindenArgv[0]; sindenWhiteColor.g = _global.conf->sindenArgv[1]; sindenWhiteColor.b = _global.conf->sindenArgv[2]; sindenWhite.x = _global.conf->sindenArgv[3]; break; // RW GW BW WW WB - Custom color "white" border and width then width of black border case SINDEN_CUSTOM_WHITE_BLACK: sindenWhiteColor.r = _global.conf->sindenArgv[0]; sindenWhiteColor.g = _global.conf->sindenArgv[1]; sindenWhiteColor.b = _global.conf->sindenArgv[2]; sindenWhite.x = _global.conf->sindenArgv[3]; sindenBlack.x = _global.conf->sindenArgv[4]; break; // RW GW BW WW RB GB BB WB - Custom color "white" border and width then custom color "black" border and width case SINDEN_CUSTOM_WHITE_CUSTOM_BLACK: sindenWhiteColor.r = _global.conf->sindenArgv[0]; sindenWhiteColor.g = _global.conf->sindenArgv[1]; sindenWhiteColor.b = _global.conf->sindenArgv[2]; sindenWhite.x = _global.conf->sindenArgv[3]; sindenBlackColor.r = _global.conf->sindenArgv[4]; sindenBlackColor.g = _global.conf->sindenArgv[5]; sindenBlackColor.b = _global.conf->sindenArgv[6]; sindenBlack.x = _global.conf->sindenArgv[7]; break; } if (sindenWhite.x >= 0) { sindenWhite.y = sindenWhite.x; sindenWhite.w = videoGetWidth(_global.videoHandle) - (sindenWhite.x * 2); sindenWhite.h = videoGetHeight(_global.videoHandle) - (sindenWhite.y * 2); } if (sindenBlack.x >= 0) { sindenBlack.y = sindenBlack.x; sindenBlack.w = videoGetWidth(_global.videoHandle) - (sindenBlack.x * 2); sindenBlack.h = videoGetHeight(_global.videoHandle) - (sindenBlack.y * 2); sindenWhite.x += sindenBlack.x; sindenWhite.y += sindenBlack.y; sindenWhite.w -= (sindenBlack.x * 2); sindenWhite.h -= (sindenBlack.y * 2); } windowTarget = sindenWhite; //***TODO*** We don't really need sindenWhite } // Create overlay surface _global.overlayScaleX = 0.5; _global.overlayScaleY = 0.5; x = (int32_t)(videoGetWidth(_global.videoHandle) * _global.overlayScaleX); y = (int32_t)(videoGetHeight(_global.videoHandle) * _global.overlayScaleY); progTrace("Creating overlay of %dx%d", x, y); _global.overlay = SDL_CreateRGBSurfaceWithFormat(0, x, y, 32, SDL_PIXELFORMAT_BGRA32); if (_global.overlay == NULL) utilDie("%s", SDL_GetError()); SDL_SetSurfaceBlendMode(_global.overlay, SDL_BLENDMODE_BLEND); // Mouse setup _global.mouseEnabled = (bool)!_global.conf->noMouse; progTrace("Initializing ManyMouse"); _global.mouseCount = ManyMouse_Init(); progTrace("Mouse Driver: %s", ManyMouse_DriverName()); progTrace("Mice Found: %d", _global.mouseCount); if ((_global.mouseCount < 1) && _global.mouseEnabled) utilDie("No mice detected."); if (_global.mouseCount > MAX_MICE) { _global.mouseCount = MAX_MICE; } memset(_global.mice, 0, sizeof(_global.mice)); for (x=0; x<_global.mouseCount; x++) { strncpy(_global.mice[x].name, ManyMouse_DeviceName((unsigned)x), sizeof(_global.mice[x].name)); _global.mice[x].name[sizeof(_global.mice[x].name) - 1] = 0; _global.mice[x].x = (int32_t)(videoGetWidth(_global.videoHandle) * _global.overlayScaleX); _global.mice[x].y = (int32_t)(videoGetHeight(_global.videoHandle) * _global.overlayScaleY); progTrace("Mouse %d: %s", x, _global.mice[x].name); } // Grab mouse progTrace("Grabbing mouse"); _global.mouseGrabbed = true; SDL_SetWindowGrab(_global.window, SDL_TRUE); progTrace("Disabling mouse pointer"); SDL_ShowCursor(SDL_DISABLE); // Clear axis caches for (x=0; xvolumeNonVldp * (float)0.01); progTrace("Setting up sound effects mixer"); Mix_Volume(-1, _global.effectsVolume * 2); // Let us know when sounds end Mix_ChannelFinished(channelFinished); // Load overlay font progTrace("Loading console font"); _global.consoleFontSurface = IMG_LoadPNG_RW(SDL_RWFromMem(font_png, font_png_len)); if (_global.consoleFontSurface == NULL) utilDie("%s", SDL_GetError()); _global.consoleFontWidth = _global.consoleFontSurface->w / 256; _global.consoleFontHeight = _global.consoleFontSurface->h; SDL_SetColorKey(_global.consoleFontSurface, true, _global.consoleFontSurface->format->Rmask | _global.consoleFontSurface->format->Bmask); // Start video progTrace("Starting laserdisc video in stopped state"); videoPlay(_global.videoHandle); _global.discStopped = false; // Start script progTrace("Compiling %s", _global.conf->scriptFile); if (luaL_dofile(_global.luaContext, _global.conf->scriptFile) != 0) utilDie("Error compiling script: %s", lua_tostring(_global.luaContext, -1)); // Game Loop progTrace("Script is running"); while (_global.running) { // SDL Event Loop while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_CONTROLLERAXISMOTION: axisIndex = event.caxis.which * 2 + event.caxis.axis; // Is this in a range we care about? if (abs(event.caxis.value) > _global.controllerDeadZone) { // Determine which "scancode" to process - see Framework.singe // Controller codes begin at 500 and increment in 100 x = event.caxis.which * 100 + 500; // The axis value lines up with the enumeration used by SDL * 3 x += event.caxis.axis * 3; // Finally we add the particular direction we're interested in x += (event.caxis.value < 0) ? 1 : 2; // Fire the down/up events for the axis direction if (lastAnalogDirection[axisIndex] != AXIS_KEY_DOWN) { processKey(true, 0, x); lastAnalogDirection[axisIndex] = AXIS_KEY_DOWN; } } else { // Handle "up" events for controller inside dead zone if (lastAnalogDirection[axisIndex] != AXIS_KEY_UP) { processKey(false, 0, x); lastAnalogDirection[axisIndex] = AXIS_KEY_UP; } } // Remember this change _global.axisCache[axisIndex] = event.caxis.value; // Fire analog event callLua("onControllerMoved", "iii", event.caxis.axis, event.caxis.value, event.caxis.which); break; case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERBUTTONUP: // Determine which "scancode" to process - see Framework.singe // Controller codes begin at 500 and increment in 100 x = event.cbutton.which * 100 + 500; // The button values line up with the enumeration used by SDL + 18 x += event.cbutton.button + 18; // Fire down event processKey((event.type == SDL_CONTROLLERBUTTONDOWN) ? true : false, 0, x); break; case SDL_CONTROLLERDEVICEADDED: case SDL_CONTROLLERDEVICEREMOVED: stopControllers(); startControllers(); break; case SDL_KEYDOWN: case SDL_KEYUP: processKey((event.type == SDL_KEYDOWN) ? true : false, event.key.keysym.sym, event.key.keysym.scancode); break; case SDL_MOUSEMOTION: if ((_global.mouseEnabled) && (_global.mouseMode == MOUSE_SINGLE)) { x = (int32_t)(event.motion.x * _global.overlayScaleX); y = (int32_t)(event.motion.y * _global.overlayScaleY); xr = (int32_t)(event.motion.xrel * _global.overlayScaleX); yr = (int32_t)(event.motion.yrel * _global.overlayScaleY); // Remember this change _global.axisCache[MAX_CONTROLLERS * 2] = x; _global.axisCache[MAX_CONTROLLERS * 2 + 1] = y; // Fire event callLua("onMouseMoved", "iiiii", x, y, xr, yr, 0); } break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: if ((_global.mouseEnabled) && (_global.mouseMode == MOUSE_SINGLE)) { // Mouse events start at "scancode" 1000 x = 1000; // SDL maps buttons L,M,R,X1,X2 starting at 1 switch (event.button.button) { case 2: y = 2; break; case 3: y = 1; break; default: y = event.button.button - 1; break; } x += y; // Fire event processKey((event.type == SDL_MOUSEBUTTONDOWN) ? true : false, 0, x); } break; case SDL_MOUSEWHEEL: if ((_global.mouseEnabled) && (_global.mouseMode == MOUSE_SINGLE)) { // Mouse events start at "scancode" 1000 x = 1000; // Scroll events start at 1005 and are mapped UP then DOWN x += 5 + (event.wheel.y > 0 ? 0 : 1); // Fire events processKey(true, 0, x); processKey(false, 0, x); } break; case SDL_QUIT: progTrace("Quit requested"); _global.running = 0; break; } // Mouse Event Loop while (ManyMouse_PollEvent(&mouseEvent)) { // Just run out the event queue if we're not using ManyMouse if ((!_global.mouseEnabled) || (_global.mouseMode == MOUSE_SINGLE)) continue; // Has this one been unplugged? if (mouseEvent.device >= (unsigned)_global.mouseCount) continue; mouse = &_global.mice[mouseEvent.device]; switch (mouseEvent.type) { case MANYMOUSE_EVENT_RELMOTION: switch (mouseEvent.item) { case 0: mouse->relx += mouseEvent.value; break; case 1: mouse->rely += mouseEvent.value; break; } // Clamp to video size x = videoGetWidth(_global.videoHandle); y = videoGetHeight(_global.videoHandle); if (mouse->relx < 0) mouse->relx = 0; if (mouse->relx >= x) mouse->relx = x - 1; if (mouse->rely < 0) mouse->rely = 0; if (mouse->rely >= y) mouse->rely = y - 1; x = (int32_t)(mouse->x * _global.overlayScaleX); y = (int32_t)(mouse->y * _global.overlayScaleY); xr = (int32_t)(mouse->relx * _global.overlayScaleX); yr = (int32_t)(mouse->rely * _global.overlayScaleY); //utilSay("ManyMouse %d: Relative %dx%d r=%dx%d", mouseEvent.device, x, y, xr, yr); // Remember this change _global.axisCache[MAX_CONTROLLERS * 2 + mouseEvent.device * 2] = xr; _global.axisCache[MAX_CONTROLLERS * 2 + mouseEvent.device * 2 + 1] = yr; // Fire event //callLua("onMouseMoved", "iiiii", x, y, xr, yr, mouseEvent.device); // We return relative coords for all parameters since we have no actual X & Y callLua("onMouseMoved", "iiiii", xr, yr, xr, yr, mouseEvent.device); break; //***TODO*** Doesn't ever seem used? case MANYMOUSE_EVENT_ABSMOTION: /* val = (float)(mouseEvent.value - mouseEvent.minval); maxval = (float)(mouseEvent.maxval - mouseEvent.minval); switch (mouseEvent.item) { case 0: mouse->x = (val / maxval) * videoGetWidth(_global.videoHandle); //mouse->x += mouseEvent.value; break; case 1: mouse->y = (val / maxval) * videoGetHeight(_global.videoHandle); //mouse->y += mouseEvent.value; break; } x = (int32_t)(mouse->x * _global.overlayScaleX); y = (int32_t)(mouse->y * _global.overlayScaleY); xr = (int32_t)(mouse->relx * _global.overlayScaleX); yr = (int32_t)(mouse->rely * _global.overlayScaleY); //utilSay("ManyMouse %d: Absolute %dx%d r=%dx%d", mouseEvent.device, x, y, xr, yr); // Remember this change _global.axisCache[MAX_CONTROLLERS * 2 + mouseEvent.device * 2] = x; _global.axisCache[MAX_CONTROLLERS * 2 + mouseEvent.device * 2 + 1] = y; // Fire event callLua("onMouseMoved", "iiiii", x, y, xr, yr, mouseEvent.device); */ //progTrace("Unimplemented MANYMOUSE_EVENT_ABSMOTION called"); break; case MANYMOUSE_EVENT_BUTTON: if (mouseEvent.item < 5 /* 32 */) { // Limited to 5 buttons so it matches single-mouse mode //utilSay("ManyMouse %d Button: %d", mouseEvent.device, mouseEvent.item); // Mouse events start at "scancode" 1000 with 100 spacing x = mouseEvent.device * 100 + 1000; // ManyMouse maps buttons L,R,M,X1,X2 starting at 0 x += mouseEvent.item; if (mouseEvent.value == 1) { // Button pressed processKey(true, 0, x); mouse->buttons |= (1 << mouseEvent.item); } else { // Button released processKey(false, 0, x); mouse->buttons &= ~(1 << mouseEvent.item); } } break; case MANYMOUSE_EVENT_SCROLL: if (mouseEvent.item == 0) { // Mouse events start at "scancode" 1000 with 100 spacing x = mouseEvent.device * 100 + 1000; // Scroll events start at 1005 and are mapped UP then DOWN x += 5 + (mouseEvent.value > 0 ? 0 : 1); // Fire events processKey(true, 0, x); processKey(false, 0, x); } break; case MANYMOUSE_EVENT_DISCONNECT: mouse->connected = false; break; case MANYMOUSE_EVENT_MAX: // We don't use this break; } } } // Update video thisFrame = videoUpdate(_global.videoHandle, &_global.videoTexture); if (_global.conf->isFrameFile) { frameFileUpdate(_global.frameFileHandle, &_global.videoHandle); } // Did we get a new video frame? if ((thisFrame != lastFrame) && (thisFrame >= 0)) { lastFrame = thisFrame; frameClock = 0; _global.refreshDisplay = true; } // Call game code if (SDL_GetTicks() > frameClock) { callLua("onOverlayUpdate", ">i", &intReturn); if (intReturn == 1) { _global.refreshDisplay = true; } frameClock = SDL_GetTicks() + 15; // Don't eat all the CPU. } // Update display if (_global.refreshDisplay || _global.discStopped) { // Clear entire display to black SDL_SetRenderDrawColor(_global.renderer, 0, 0, 0, 255); SDL_RenderClear(_global.renderer); // Sinden Gun Border if (sindenWhite.x >= 0) { if (sindenBlack.x >= 0) { // Black and White SDL_SetRenderDrawColor(_global.renderer, sindenBlackColor.r, sindenBlackColor.g, sindenBlackColor.b, sindenBlackColor.a); SDL_RenderClear(_global.renderer); SDL_SetRenderDrawColor(_global.renderer, sindenWhiteColor.r, sindenWhiteColor.g, sindenWhiteColor.b, sindenWhiteColor.a); SDL_RenderFillRect(_global.renderer, &sindenBlack); } else { // Only white SDL_SetRenderDrawColor(_global.renderer, sindenWhiteColor.r, sindenWhiteColor.g, sindenWhiteColor.b, sindenWhiteColor.a); SDL_RenderFillRect(_global.renderer, &sindenBlack); //SDL_RenderClear(_global.renderer); } //SDL_RenderFillRect(_global.renderer, &windowTarget); } // Laserdisc Video if (_global.discStopped) { // Stopped discs display blue like the good old days SDL_SetRenderTarget(_global.renderer, _global.videoTexture); SDL_SetRenderDrawColor(_global.renderer, 0, 0, 255, 255); SDL_RenderFillRect(_global.renderer, &windowTarget); SDL_SetRenderTarget(_global.renderer, NULL); } // Copy current video frame into display SDL_RenderCopy(_global.renderer, _global.videoTexture, NULL, &windowTarget); // Overlay overlayTexture = SDL_CreateTextureFromSurface(_global.renderer, _global.overlay); if (!overlayTexture) utilDie("%s", SDL_GetError()); if (!_global.conf->stretchVideo) { SDL_RenderSetLogicalSize(renderer, videoGetWidth(_global.videoHandle), videoGetHeight(_global.videoHandle)); } SDL_RenderCopy(_global.renderer, overlayTexture, NULL, &windowTarget); SDL_DestroyTexture(overlayTexture); overlayTexture = NULL; // Show it SDL_RenderPresent(_global.renderer); _global.refreshDisplay = false; // Save it? if (_global.requestScreenShot) { _global.requestScreenShot = false; progTrace("Taking screenshot"); takeScreenshot(); } } SDL_Delay(1); } // End game progTrace("Script is shutting down"); callLua("onShutdown", ""); // Stop all sounds progTrace("Stopping all audio"); Mix_HaltChannel(-1); Mix_ChannelFinished(NULL); // Stop Lua progTrace("Stopping Lua"); lua_close(_global.luaContext); // Free overlay & overlay font progTrace("Destroying overlay"); SDL_FreeSurface(_global.overlay); progTrace("Destroying console font"); SDL_FreeSurface(_global.consoleFontSurface); // Unload fonts HASH_ITER(hh, _global.fontList, font, fontTemp) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" HASH_DEL(_global.fontList, font); #pragma GCC diagnostic pop progTrace("Unloading font handle %d", font->id); TTF_CloseFont(font->font); free(font); } // Unload sounds HASH_ITER(hh, _global.soundList, sound, soundTemp) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" HASH_DEL(_global.soundList, sound); #pragma GCC diagnostic pop progTrace("Unloading sound handle %d", sound->id); Mix_FreeChunk(sound->chunk); free(sound); } // Unload sprites HASH_ITER(hh, _global.spriteList, sprite, spriteTemp) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" HASH_DEL(_global.spriteList, sprite); #pragma GCC diagnostic pop progTrace("Unloading sprite handle %d", sprite->id); SDL_FreeSurface(sprite->surface); free(sprite); } // Unload videos HASH_ITER(hh, _global.videoList, video, videoTemp) { HASH_DEL(_global.videoList, video); progTrace("Unloading video handle %d", video->id); if (video->surface) SDL_FreeSurface(video->surface); free(video); } // Unload background video progTrace("Unloading main video file"); if (_global.conf->isFrameFile) { frameFileUnload(_global.frameFileHandle); } else { videoUnload(_global.videoHandle); } // Stop controllers progTrace("Stopping controllers"); stopControllers(); // Stop mice progTrace("Re-enabling mouse pointer"); SDL_ShowCursor(SDL_ENABLE); progTrace("Stopping ManyMouse"); ManyMouse_Quit(); // Release global conf memory destroyConf(&_global.conf); // Release control mappings for (x=0; x 4) _global.controllerCount = 4; _global.controllers = (SDL_GameController **)malloc(sizeof(SDL_GameController *) * (size_t)_global.controllerCount); for (x=0; x<_global.controllerCount; x++) { _global.controllers[x] = NULL; if (SDL_IsGameController(x)) { _global.controllers[x] = SDL_GameControllerOpen(x); if (_global.controllers[x]) { progTrace("Found %d - %s", x, SDL_GameControllerName(_global.controllers[x])); } else { progTrace("Controller %d not opened", x); } } else { progTrace("Device %d is not a controller", x); } } SDL_GameControllerEventState(SDL_ENABLE); } void startLuaContext(lua_State *L) { luaL_openlibs(L); lua_atpanic(L, luaError); luaPreload(L, "lfs", luaopen_lfs); luaPreload(L, "socket.core", luaopen_socket_core); luaPreload(L, "mime.core", luaopen_mime_core); luaPreload(L, "rs232", luaopen_luars232); #ifndef _WIN32 luaPreload(L, "socket.unix", luaopen_socket_unix); luaPreload(L, "socket.serial", luaopen_socket_serial); #endif luaPreload(L, "ssl.core", luaopen_ssl_core); luaPreload(L, "ssl.context", luaopen_ssl_context); luaPreload(L, "ssl.x509", luaopen_ssl_x509); luaPreload(L, "ssl.config", luaopen_ssl_config); /* ***TODO*** Load these Lua modules: Lua Socket: socket.lua ltn12.lua tp.lua url.lua ftp.lua mime.lua headers.lua http.lua mbox.lua smtp.lua luasec: https.lua ssl.lua librs232: rs232.lua --[[ http = require("socket.http") http.request{ url = "http://www.columbia.edu/~fdc/sample.html", sink = ltn12.sink.file(io.stdout) } --]] */ } void stopControllers(void) { int32_t x; if (_global.controllerCount > 0) { for (x=0; x<_global.controllerCount; x++) { if (_global.controllers[x] != NULL) { SDL_GameControllerClose(_global.controllers[x]); _global.controllers[x] = NULL; } } free(_global.controllers); _global.controllers = NULL; _global.controllerCount = 0; } } void takeScreenshot(void) { int32_t x = 0; char filename[1024]; void *pixels = NULL; SDL_Surface *surface = NULL; SDL_Rect viewport; while (x <= 999) { snprintf(filename, 1024, "%ssinge%03d.png", _global.conf->dataDir, x); if (!utilFileExists(filename)) break; x++; } if (x > 999) utilDie("Seriously? You have 1000 screenshots in this folder? Remove some."); SDL_RenderGetViewport(_global.renderer, &viewport); surface = SDL_CreateRGBSurface(0, viewport.w, viewport.h, 32, 0, 0, 0, 0); if (!surface) utilDie("%s", SDL_GetError()); if (SDL_RenderReadPixels(_global.renderer, NULL, surface->format->format, surface->pixels, surface->pitch) != 0) utilDie("%s", SDL_GetError()); //***FIX*** This crashes with a SEGFAULT sometimes. Not sure why. if (IMG_SavePNG(surface, filename) < 0) utilDie("%s", IMG_GetError()); SDL_FreeSurface(surface); free(pixels); } void updatePauseState(void) { VideoT *video = NULL; VideoT *videoTemp = NULL; if (_global.pauseState) { // Pause laserdisc if (!_global.discStopped) { if (videoIsPlaying(_global.videoHandle)) { _global.wasPlayingBeforePause = true; videoPause(_global.videoHandle); } } // Pause videos HASH_ITER(hh, _global.videoList, video, videoTemp) { if (videoIsPlaying(video->handle)) { video->wasPlayingBeforePause = true; videoPause(video->handle); } } // Pause sounds Mix_Pause(-1); } else { // Resume laserdisc if ((!_global.discStopped) && (_global.wasPlayingBeforePause)) { _global.wasPlayingBeforePause = false; videoPlay(_global.videoHandle); } // Resume videos HASH_ITER(hh, _global.videoList, video, videoTemp) { if (video->wasPlayingBeforePause) { video->wasPlayingBeforePause = false; videoPlay(video->handle); } } // Resume laserdisc Mix_Resume(-1); } }