singe/src/singe.c
2023-11-19 18:44:24 -06:00

4750 lines
157 KiB
C

/*
*
* Singe 2
* Copyright (C) 2006-2024 Scott Duensing <scott@kangaroopunch.com>
*
* 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 <string.h>
#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/SDL2_gfx/SDL2_rotozoom.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"
// We have to do the embedding here so the Lua module
// definitions can find their lenght properly. They
// can't be external to this source file.
#define EMBED_HERE
#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 {
char *name;
union {
char *source;
lua_CFunction openf;
};
size_t length;
} luaModuleT;
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 *originalSurface;
SDL_Surface *surface;
#pragma GCC diagnostic pop
double angle;
double scaleX;
double scaleY;
int smooth;
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;
#define MODL(name, array) { name, { (char *)array }, sizeof(array) }
#define MODC(name, openf) { name, { (char *)openf }, 0 }
// Lua Modules
static const luaModuleT luaModules[] = {
// LuaFileSystem
MODC("lfs", luaopen_lfs),
// LuaSocket
MODC("mime.core", luaopen_mime_core),
MODC("socket.core", luaopen_socket_core),
MODL("ltn12", ltn12_lua),
MODL("mbox", mbox_lua),
MODL("mime", mime_lua),
MODL("socket", socket_lua),
MODL("socket.ftp", ftp_lua),
MODL("socket.headers", headers_lua),
MODL("socket.http", http_lua),
MODL("socket.smtp", smtp_lua),
MODL("socket.tp", tp_lua),
MODL("socket.url", url_lua),
#ifndef _WIN32
MODC("socket.unix", luaopen_socket_unix),
MODC("socket.serial", luaopen_socket_serial),
#endif
// LuaSec
MODC("ssl.core", luaopen_ssl_core),
MODC("ssl.context", luaopen_ssl_context),
MODC("ssl.x509", luaopen_ssl_x509),
MODC("ssl.config", luaopen_ssl_config),
MODL("ssl.https", https_lua),
MODL("ssl", ssl_lua),
// LuaRS232
MODC("rs232.core", luaopen_luars232),
MODL("rs232", rs232_lua),
};
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 apiSpriteQuality(lua_State *L);
int32_t apiSpriteRotate(lua_State *L);
int32_t apiSpriteScale(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);
int luaSearcher(lua_State *L);
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);
SDL_Surface *surfaceCopy(SDL_Surface *source);
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->originalSurface = TTF_RenderText_Solid(_global.fontCurrent->font, message, _global.colorForeground);
break;
case 2:
sprite->originalSurface = TTF_RenderText_Shaded(_global.fontCurrent->font, message, _global.colorForeground, _global.colorBackground);
break;
case 3:
sprite->originalSurface = TTF_RenderText_Blended(_global.fontCurrent->font, message, _global.colorForeground);
break;
default:
luaDie(L, "fontToSprite", "Unknown font quality!");
break;
}
if (!sprite->originalSurface) {
luaDie(L, "fontToSprite", "Font surface is null!");
} else {
SDL_SetColorKey(sprite->originalSurface, true, 0);
sprite->surface = surfaceCopy(sprite->originalSurface);
sprite->scaleX = 1.0;
sprite->scaleY = 1.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; i<length; i++) {
src.x = s[i] * _global.consoleFontWidth;
SDL_BlitSurface(_global.consoleFontSurface, &src, _global.overlay, &dst);
dst.x += _global.consoleFontWidth;
}
result = true;
}
}
}
}
if (!result) luaDie(L, "overlayPrint", "Failed!");
return 0;
}
int32_t apiOverlaySetResolution(lua_State *L) {
int32_t n = lua_gettop(L);
int32_t x = 0;
int32_t y = 0;
double d = 0;
bool result = false;
if (n == 2) {
if (lua_isnumber(L, 1)) {
if (lua_isnumber(L, 2)) {
// New resolution. We don't check it. Might go boom.
d = lua_tonumber(L, 1); x = (int32_t)d;
d = lua_tonumber(L, 2); y = (int32_t)d;
// Release existing surface.
SDL_FreeSurface(_global.overlay);
// Create the new one.
_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);
// Update some variables.
_global.overlayScaleX = x / videoGetWidth(_global.videoHandle);
_global.overlayScaleY = y / videoGetHeight(_global.videoHandle);
result = true;
}
}
}
if (result) {
luaTrace(L, "overlaySetResolution", "%d %d", x, y);
} else {
luaDie(L, "overlaySetResolution", "Failed!");
}
return 0;
}
int32_t apiSoundLoad(lua_State *L) {
int32_t n = lua_gettop(L);
int32_t result = -1;
const char *name = NULL;
SoundT *sound = NULL;
if (n == 1) {
if (lua_isstring(L, 1)) {
sound = (SoundT *)calloc(1, sizeof(SoundT));
if (!sound) luaDie(L, "soundLoad", "Unable to allocate new sound.");
name = lua_tostring(L, 1);
sound->chunk = 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;
bool center = false;
SpriteT *sprite = NULL;
SDL_Rect dest;
int ox;
int oy;
// spriteDraw(x, y, id) - Simple draw
// spriteDraw(x, y, c, id) - Centered draw
// spriteDraw(x, y, x2, y2, id) - Scaled draw
// spriteDraw(x, y, x2, y2, c, id) - Centered scaled draw
if ((n >= 3) && (n <= 6)) {
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;
// Centered?
if ((n == 3) || (n == 4)) {
if (n == 4) {
if (lua_isnumber(L, 4)) {
d = lua_tonumber(L, 3); center = (int32_t)d != 0;
d = lua_tonumber(L, 4); id = (int32_t)d;
}
} else {
d = lua_tonumber(L, 3); id = (int32_t)d;
}
}
if ((n == 5) || (n == 6)) {
// 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;
// Centered?
if (n == 6) {
if (lua_isnumber(L, 6)) {
d = lua_tonumber(L, 5); center = (int32_t)d != 0;
d = lua_tonumber(L, 6); id = (int32_t)d;
}
} else {
d = lua_tonumber(L, 5); id = (int32_t)d;
}
}
}
}
//utilSay("spriteDraw: x=%d y=%d c=%d id=%d", dest.x, dest.y, center, id);
HASH_FIND_INT(_global.spriteList, &id, sprite);
if (!sprite) luaDie(L, "spriteDraw", "No sprite at index %d in apiSpriteDraw.", id);
if ((n == 3) || (n == 4)) {
// No scaling, find width
dest.w = sprite->surface->w;
dest.h = sprite->surface->h;
}
if (center) {
// Move sprite so the drawing coordinate is the center of the sprite
dest.x -= dest.w * 0.5;
dest.y -= dest.w * 0.5;
}
if ((n == 3) || (n == 4)) {
// No scaling
SDL_BlitSurface(sprite->surface, NULL, _global.overlay, &dest);
} else {
// Scaled
SDL_BlitScaled(sprite->surface, NULL, _global.overlay, &dest);
}
}
}
}
}
if (id >= 0) {
luaTrace(L, "spriteDraw", "%d %d %d %d %d %d", id, dest.x, dest.y, dest.w, dest.h, center);
} 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->originalSurface = IMG_Load(name);
if (!sprite->originalSurface) luaDie(L, "spriteLoad", "%s", IMG_GetError());
SDL_SetColorKey(sprite->originalSurface, true, 0);
sprite->surface = surfaceCopy(sprite->originalSurface);
sprite->scaleX = 1.0;
sprite->scaleY = 1.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 apiSpriteQuality(lua_State *L) {
int32_t n = lua_gettop(L);
int32_t id = -1;
int32_t s;
double d;
SpriteT *sprite = NULL;
if (n == 2) {
if (lua_isnumber(L, 1)) {
if (lua_isnumber(L, 2)) {
d = lua_tonumber(L, 1); s = (int32_t)d;
d = lua_tonumber(L, 2); id = (int32_t)d;
HASH_FIND_INT(_global.spriteList, &id, sprite);
if (!sprite) luaDie(L, "spriteQuality", "No sprite at index %d in apiSpriteQuality.", id);
sprite->smooth = s;
SDL_FreeSurface(sprite->surface);
sprite->surface = rotozoomSurfaceXY(sprite->originalSurface, 360 - sprite->angle, sprite->scaleX, sprite->scaleY, sprite->smooth);
}
}
}
if (id >= 0) {
luaTrace(L, "spriteQuality", "%d %d", id, sprite->smooth);
} else {
luaDie(L, "spriteQuality", "Failed!");
}
return 0;
}
int32_t apiSpriteRotate(lua_State *L) {
int32_t n = lua_gettop(L);
int32_t id = -1;
double d;
double a;
SpriteT *sprite = NULL;
if (n == 2) {
if (lua_isnumber(L, 1)) {
if (lua_isnumber(L, 2)) {
d = lua_tonumber(L, 1); a = fmod(d, 360.0);
d = lua_tonumber(L, 2); id = (int32_t)d;
HASH_FIND_INT(_global.spriteList, &id, sprite);
if (!sprite) luaDie(L, "spriteRotate", "No sprite at index %d in apiSpriteRotate.", id);
sprite->angle = a;
SDL_FreeSurface(sprite->surface);
sprite->surface = rotozoomSurfaceXY(sprite->originalSurface, 360 - sprite->angle, sprite->scaleX, sprite->scaleY, sprite->smooth);
}
}
}
if (id >= 0) {
luaTrace(L, "spriteRotate", "%d %f", id, sprite->angle);
} else {
luaDie(L, "spriteRotate", "Failed!");
}
return 0;
}
int32_t apiSpriteScale(lua_State *L) {
int32_t n = lua_gettop(L);
int32_t id = -1;
double d;
double x;
double y;
SpriteT *sprite = NULL;
if ((n == 2) || (n == 3)) {
if (lua_isnumber(L, 1)) {
if (lua_isnumber(L, 2)) {
d = lua_tonumber(L, 1); x = d;
if (n == 2) {
y = x;
d = lua_tonumber(L, 2); id = (int32_t)d;
} else {
d = lua_tonumber(L, 2); y = d;
d = lua_tonumber(L, 3); id = (int32_t)d;
}
HASH_FIND_INT(_global.spriteList, &id, sprite);
if (!sprite) luaDie(L, "spriteScale", "No sprite at index %d in apiSpriteScale.", id);
sprite->scaleX = x;
sprite->scaleY = y;
SDL_FreeSurface(sprite->surface);
sprite->surface = rotozoomSurfaceXY(sprite->originalSurface, 360 - sprite->angle, sprite->scaleX, sprite->scaleY, sprite->smooth);
}
}
}
if (id >= 0) {
luaTrace(L, "spriteScale", "%d %f %f", id, sprite->scaleX, sprite->scaleY);
} else {
luaDie(L, "spriteScale", "Failed!");
}
return 0;
}
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);
SDL_FreeSurface(sprite->originalSurface);
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
int luaSearcher(lua_State *L) {
// https://leiradel.github.io/2020/03/01/Embedding-Lua-Modules.html
char *modname = (char *)lua_tostring(L, 1);
size_t i;
int res;
// Iterates over all modules we know.
for (i = 0; i < sizeof(luaModules) / sizeof(luaModules[0]); i++) {
if (strcmp(modname, luaModules[i].name) == 0) {
// Found the module.
if (luaModules[i].length != 0) {
// It's a Lua module, return the chunk that defines the module.
res = luaL_loadbufferx(L, luaModules[i].source, luaModules[i].length, modname, "t");
if (res != LUA_OK) {
// Compilation error.
return lua_error(L);
}
} else {
// It's a native module, return the native function that defines the module.
lua_pushcfunction(L, luaModules[i].openf);
}
return 1;
}
}
// Oops...
lua_pushfstring(L, "Unknown Lua module: \"%s\"", modname);
return 1;
}
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<INPUT_COUNT; move++) {
if (_global.controlMappings[move].inputCount > 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; x<INPUT_COUNT; x++) {
_global.controlMappings[x].frameworkIndex = x;
}
// Hang on to some SDL stuff
_global.window = window;
_global.renderer = renderer;
// Load controller mappings
progTrace("Creating Lua context for Singe setup");
_global.luaContext = luaL_newstate();
startLuaContext(_global.luaContext);
// Load framework
progTrace("Loading Singe framework");
if (luaL_loadbuffer(_global.luaContext, (char *)Framework_singe, Framework_singe_len, "Input Mappings") || lua_pcall(_global.luaContext, 0, 0, 0)) utilDie("%s", lua_tostring(_global.luaContext, -1));
// Load default mappings
progTrace("Loading default control mappings");
if (luaL_loadbuffer(_global.luaContext, (char *)controls_cfg, controls_cfg_len, "Input Mappings") || lua_pcall(_global.luaContext, 0, 0, 0)) utilDie("%s", lua_tostring(_global.luaContext, -1));
if (utilFileExists("controls.cfg")) {
progTrace("Loading controls.cfg");
if (luaL_dofile(_global.luaContext, "controls.cfg")) utilDie("%s", lua_tostring(_global.luaContext, -1));
}
// Load mappings in main data dir
temp = utilCreateString("%s..%ccontrols.cfg", _global.conf->dataDir, 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<INPUT_COUNT; x++) {
// First, count integers in Lua table and allocate memory...
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)) {
y++;
lua_pop(_global.luaContext, 1);
}
if (_global.controlMappings[x].input != NULL) free(_global.controlMappings[x].input);
if (y > 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, "spriteQuality", apiSpriteQuality);
lua_register(_global.luaContext, "spriteRotate", apiSpriteRotate);
lua_register(_global.luaContext, "spriteScale", apiSpriteScale);
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; x<AXIS_COUNT; x++) {
_global.axisCache[x] = 0;
lastAnalogDirection[x] = AXIS_KEY_UP;
}
// Controllers are started by the event loop only for the first script in
// the queue - so kick 'em here to be sure they're going.
startControllers();
// Set volume
_global.effectsVolume = (int32_t)((float)AUDIO_MAX_VOLUME * (float)_global.conf->volumeNonVldp * (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);
SDL_FreeSurface(sprite->originalSurface);
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<INPUT_COUNT; x++) {
free(_global.controlMappings[x].input);
}
}
void startControllers(void) {
int32_t x;
stopControllers();
// Clamp to the first four controllers found for now
_global.controllerCount = SDL_NumJoysticks();
if (_global.controllerCount > 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) {
size_t length;
luaL_openlibs(L);
lua_atpanic(L, luaError);
// Get the package global table.
lua_getglobal(L, "package");
// Get the list of searchers in the package table.
lua_getfield(L, -1, "searchers");
// Get the number of existing searchers in the table.
length = lua_rawlen(L, -1);
// Add our own searcher to the list.
lua_pushcfunction(L, luaSearcher);
lua_rawseti(L, -2, length + 1);
// Remove the seachers and the package tables from the stack.
lua_pop(L, 2);
/*
--[[
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;
}
}
SDL_Surface *surfaceCopy(SDL_Surface *source) {
SDL_Surface *destination = NULL;
destination = SDL_CreateRGBSurface(
source->flags,
source->w,
source->h,
source->format->BitsPerPixel,
source->format->Rmask,
source->format->Gmask,
source->format->Bmask,
source->format->Amask);
if (destination != NULL) SDL_BlitSurface(source, NULL, destination, NULL);
return destination;
}
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);
}
}