singe/singe/singe.c

2784 lines
75 KiB
C

/*
*
* Singe 2
* Copyright (C) 2006-2020 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 <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_mixer.h>
#include <SDL2/SDL_ttf.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include "thirdparty/uthash.h"
#include "thirdparty/manymouse/manymouse.h"
#include "util.h"
#include "frameFile.h"
#include "videoPlayer.h"
#include "singe.h"
#include "embedded.h"
//#define DEBUG_TOOLS
#define AUDIO_MAX_VOLUME 63
#define MAX_TITLE_LENGTH 1024
#define MAX_MICE 128
#define SCROLLWHEEL_DISPLAY_TICKS 100
#define NOMOUSE -1
#define KEYBD_ARRAY_SIZE 15
#define MOUSE_ARRAY_SIZE 6
typedef struct MouseS {
int32_t x;
int32_t y;
int32_t relx;
int32_t rely;
char name[64];
bool connected;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpadded"
Uint32 buttons;
#pragma GCC diagnostic pop
Uint32 scrolluptick;
Uint32 scrolldowntick;
Uint32 scrolllefttick;
Uint32 scrollrighttick;
} MouseT;
typedef struct SpriteS {
int32_t id;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpadded"
SDL_Surface *surface;
#pragma GCC diagnostic pop
UT_hash_handle hh;
} SpriteT;
typedef struct SoundS {
int32_t id;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpadded"
Mix_Chunk *chunk;
#pragma GCC diagnostic pop
UT_hash_handle hh;
} SoundT;
typedef struct FontS {
int32_t id;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpadded"
TTF_Font *font;
#pragma GCC diagnostic pop
UT_hash_handle hh;
} FontT;
typedef struct MappingS {
char *name;
int32_t frameworkIndex;
int32_t inputCount;
int32_t *input;
} MappingT;
//***TODO*** Replacing these with INPUT_* defines.
enum {
SWITCH_UP,
SWITCH_LEFT,
SWITCH_DOWN,
SWITCH_RIGHT,
SWITCH_START1,
SWITCH_START2,
SWITCH_BUTTON1,
SWITCH_BUTTON2,
SWITCH_BUTTON3,
SWITCH_COIN1,
SWITCH_COIN2,
SWITCH_SKILL1,
SWITCH_SKILL2,
SWITCH_SKILL3,
SWITCH_SERVICE,
SWITCH_TEST,
SWITCH_RESET,
SWITCH_SCREENSHOT,
SWITCH_QUIT,
SWITCH_PAUSE,
SWITCH_CONSOLE,
SWITCH_TILT,
SWITCH_MOUSE_SCROLL_UP,
SWITCH_MOUSE_SCROLL_DOWN,
SWITCH_MOUSE_DISCONNECT,
SWITCH_COUNT
};
enum {
KEYBD_NORMAL = 0,
KEYBD_FULL
};
enum {
MOUSE_SINGLE = 100,
MOUSE_MANY = 200
};
enum {
LDP_ERROR = 0,
LDP_SEARCHING,
LDP_STOPPED,
LDP_PLAYING,
LDP_PAUSE,
};
// Command line options
char *_confVideoFile = NULL;
char *_confScriptFile = NULL;
char *_confDataDir = NULL;
bool _confIsFrameFile = false;
bool _confStretchVideo = false;
bool _confNoMouse = false;
bool _confNoStats = false;
bool _confNoSound = false;
bool _confFullScreen = false;
bool _confFullScreenWindow = false;
bool _confShowCalculated = false;
bool _confNoConsole = false;
int32_t _confVolumeVldp = 100;
int32_t _confVolumeNonVldp = 100;
int32_t _confScaleFactor = 100;
int32_t _confXResolution = -1;
int32_t _confYResolution = -1;
int32_t _confSindenArgc = 0;
int32_t _confSindenArgv[SINDEN_OPTION_COUNT];
// Other globals
static MouseT _mice[MAX_MICE];
static lua_State *_luaContext = NULL;
static SDL_Color _colorForeground = { 255, 255, 255, 255 };
static SDL_Color _colorBackground = { 0, 0, 0, 0 };
static SDL_Surface *_overlay = NULL;
static SDL_Window *_window = NULL;
static SDL_Renderer *_renderer = NULL;
static SDL_Texture *_videoTexture = NULL;
static SDL_Surface *_consoleFontSurface = NULL;
static int32_t _controllerDeadZone = 0;
static int32_t _consoleFontWidth = 0;
static int32_t _consoleFontHeight = 0;
static int32_t _nextSpriteId = 0;
static int32_t _nextSoundId = 0;
static int32_t _nextFontId = 0;
static int32_t _effectsVolume = AUDIO_MAX_VOLUME;
static int32_t _keyboardMode = KEYBD_NORMAL;
static int32_t _frameFileHandle = -1;
static int32_t _videoHandle = -1;
static int32_t _fontQuality = 1;
static int32_t _mouseMode = MOUSE_SINGLE;
static int32_t _mouseCount = 0;
static double _overlayScaleX = 1; // Difference between overlay and video
static double _overlayScaleY = 1; // Difference between overlay and video
static bool _pauseState = false; // by RDG2010
static bool _pauseEnabled = true; // by RDG2010
static bool _refreshDisplay = false;
static bool _running = true;
static bool _discStopped = true;
static bool _mouseEnabled = true;
static bool _requestScreenShot = false;
static SpriteT *_spriteList = NULL;
static SoundT *_soundList = NULL;
static FontT *_fontList = NULL;
static FontT *_fontCurrent = NULL;
// Index into _controlMappings array
#define INPUT_UP 0
#define INPUT_LEFT 1
#define INPUT_DOWN 2
#define INPUT_RIGHT 3
#define INPUT_1P_START 4
#define INPUT_2P_START 5
#define INPUT_ACTION_1 6
#define INPUT_ACTION_2 7
#define INPUT_ACTION_3 8
#define INPUT_1P_COIN 9
#define INPUT_2P_COIN 10
#define INPUT_SKILL_EASY 11
#define INPUT_SKILL_MEDIUM 12
#define INPUT_SKILL_HARD 13
#define INPUT_SERVICE 14
#define INPUT_TEST_MODE 15
#define INPUT_RESET_CPU 16
#define INPUT_SCREENSHOT 17
#define INPUT_QUIT 18
#define INPUT_PAUSE 19
#define INPUT_CONSOLE 20
// Added in Singe 2.x
#define INPUT_ACTION_4 21
#define INPUT_TILT 22
#define INPUT_COUNT 23
static MappingT _controlMappings[] = {
{ "INPUT_UP", 0, 0, NULL },
{ "INPUT_LEFT", 1, 0, NULL },
{ "INPUT_DOWN", 2, 0, NULL },
{ "INPUT_RIGHT", 3, 0, NULL },
{ "INPUT_1P_START", 4, 0, NULL },
{ "INPUT_2P_START", 5, 0, NULL },
{ "INPUT_ACTION_1", 6, 0, NULL },
{ "INPUT_ACTION_2", 7, 0, NULL },
{ "INPUT_ACTION_3", 8, 0, NULL },
{ "INPUT_1P_COIN", 9, 0, NULL },
{ "INPUT_2P_COIN", 10, 0, NULL },
{ "INPUT_SKILL_EASY", 11, 0, NULL },
{ "INPUT_SKILL_MEDIUM", 12, 0, NULL },
{ "INPUT_SKILL_HARD", 13, 0, NULL },
{ "INPUT_SERVICE", 14, 0, NULL },
{ "INPUT_TEST_MODE", 15, 0, NULL },
{ "INPUT_RESET_CPU", 16, 0, NULL },
{ "INPUT_SCREENSHOT", 17, 0, NULL },
{ "INPUT_QUIT", 18, 0, NULL },
{ "INPUT_PAUSE", 19, 0, NULL },
{ "INPUT_CONSOLE", 20, 0, NULL },
{ "INPUT_ACTION_4", 21, 0, NULL },
{ "INPUT_TILT", 22, 0, NULL }
};
int32_t apiColorBackground(lua_State *L);
int32_t apiColorForeground(lua_State *L);
int32_t apiDaphneGetHeight(lua_State *L);
int32_t apiDaphneGetWidth(lua_State *L);
int32_t apiDaphneScreenshot(lua_State *L);
int32_t apiDebugPrint32_t(lua_State *L);
int32_t apiDiscAudio(lua_State *L);
int32_t apiDiscChangeSpeed(lua_State *L);
int32_t apiDiscGetFrame(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 apiFontPrint32_t(lua_State *L);
int32_t apiFontQuality(lua_State *L);
int32_t apiFontSelect(lua_State *L);
int32_t apiFontToSprite(lua_State *L);
int32_t apiOverlayClear(lua_State *L);
int32_t apiOverlayGetHeight(lua_State *L);
int32_t apiOverlayGetWidth(lua_State *L);
int32_t apiOverlayPrint32_t(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 apiSpriteDraw(lua_State *L);
int32_t apiSpriteGetHeight(lua_State *L);
int32_t apiSpriteGetWidth(lua_State *L);
int32_t apiSpriteLoad(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);
int32_t apiKeyboardGetMode(lua_State *L);
int32_t apiKeyboardSetMode(lua_State *L);
int32_t apiMouseEnable(lua_State *L);
int32_t apiMouseDisable(lua_State *L);
int32_t apiMouseSetMode(lua_State *L);
int32_t apiMouseHowMany(lua_State *L);
int32_t apiDiscGetState(lua_State *L);
int32_t apiSingeGetPauseFlag(lua_State *L);
int32_t apiSingeSetPauseFlag(lua_State *L);
int32_t apiSingeEnablePauseKey(lua_State *L);
int32_t apiSingeDisablePauseKey(lua_State *L);
int32_t apiSingeQuit(lua_State *L);
int32_t apiSingeVersion(lua_State *L);
int32_t apiSingeSetGameName(lua_State *L);
int32_t apiSingeGetScriptPath(lua_State *L);
void doIndexDisplay(int32_t percent);
void doLogos(void);
void callLua(const char *func, const char *sig, ...);
void channelFinished(int channel);
void luaDie(lua_State *L, char *method, char *fmt, ...);
int32_t luaError(lua_State *L);
void luaTrace(lua_State *L, char *method, char *fmt, ...);
void processKey(bool down, int keysym, int32_t scancode);
void takeScreenshot(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); _colorBackground.r = (byte)d;
d = lua_tonumber(L, 2); _colorBackground.g = (byte)d;
d = lua_tonumber(L, 3); _colorBackground.b = (byte)d;
if (n == 3) {
_colorBackground.a = (byte)255;
} else {
if (lua_isnumber(L, 4)) {
d = lua_tonumber(L, 4); _colorBackground.a = (byte)d;
} else {
_colorBackground.a = (byte)255;
}
}
result = true;
}
}
}
}
if (result) {
luaTrace(L, "colorBackground", "%d %d %d %d", _colorBackground.r, _colorBackground.g, _colorBackground.b, _colorBackground.a);
} else {
luaTrace(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); _colorForeground.r = (byte)d;
d = lua_tonumber(L, 2); _colorForeground.g = (byte)d;
d = lua_tonumber(L, 3); _colorForeground.b = (byte)d;
if (n == 3) {
_colorForeground.a = (byte)255;
} else {
if (lua_isnumber(L, 4)) {
d = lua_tonumber(L, 4); _colorForeground.a = (byte)d;
} else {
_colorForeground.a = (byte)255;
}
}
result = true;
}
}
}
}
if (result) {
luaTrace(L, "colorForeground", "%d %d %d %d", _colorForeground.r, _colorForeground.g, _colorForeground.b, _colorForeground.a);
} else {
luaTrace(L, "colorForeground", "Failed!");
}
return 0;
}
int32_t apiDaphneGetHeight(lua_State *L) {
int32_t y;
SDL_GetWindowSize(_window, NULL, &y);
luaTrace(L, "daphneGetHeight", "%d", y);
lua_pushinteger(L, y);
return 1;
}
int32_t apiDaphneGetWidth(lua_State *L) {
int32_t x;
SDL_GetWindowSize(_window, &x, NULL);
luaTrace(L, "daphneGetWidth", "%d", x);
lua_pushinteger(L, x);
return 1;
}
int32_t apiDaphneScreenshot(lua_State *L) {
luaTrace(L, "daphneScreenshot", "Screenshot requested.");
_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 (_videoHandle >= 0) {
d = lua_tonumber(L, 1); channel = (int32_t)d;
d = lua_toboolean(L, 2); onOff = (bool)d;
videoGetVolume(_videoHandle, &left, &right);
if (channel == 1) left = (onOff ? _confVolumeVldp : 0);
if (channel == 2) right = (onOff ? _confVolumeVldp : 0);
videoSetVolume(_videoHandle, left, right);
result = true;
}
}
}
}
if (result) {
luaTrace(L, "discAudio", "%d %d", left, right);
} else {
luaTrace(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 (!_discStopped) {
if (_confIsFrameFile) {
frame = frameFileGetFrame(_frameFileHandle, _videoHandle);
} else {
if (_videoHandle >= 0) frame = videoGetFrame(_videoHandle);
}
}
luaTrace(L, "discGetFrame", "%ld", frame);
lua_pushinteger(L, frame);
return 1;
}
int32_t apiDiscPause(lua_State *L) {
(void)L;
if (!_discStopped) {
if (_videoHandle >= 0) videoPause(_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 (_videoHandle >= 0) videoPlay(_videoHandle);
_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 (_confIsFrameFile) {
frameFileSeek(_frameFileHandle, frame, &_videoHandle, &aFrame);
} else {
if (_videoHandle >= 0) videoSeek(_videoHandle, frame);
}
if (_videoHandle >= 0) videoPause(_videoHandle);
_discStopped = false;
result = true;
}
}
if (result) {
luaTrace(L, "discSearch", "%ld", frame);
} else {
luaTrace(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 (!_discStopped) {
if (n == 1) {
if (lua_isnumber(L, 1)) {
d = lua_tonumber(L, 1);
if (_videoHandle >= 0) frame = videoGetFrame(_videoHandle) - (int64_t)d;
if (_confIsFrameFile) {
frameFileSeek(_frameFileHandle, frame, &_videoHandle, &aFrame);
} else {
if (_videoHandle >= 0) videoSeek(_videoHandle, frame);
}
result = true;
}
}
} else {
luaTrace(L, "discSkipBackward", "Failed! Disc is stopped.");
return 0;
}
if (result) {
luaTrace(L, "discSkipBackward", "%ld", frame);
} else {
luaTrace(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 (!_discStopped) {
if (n == 1) {
if (lua_isnumber(L, 1)) {
d = lua_tonumber(L, 1); if (_videoHandle >= 0) frame = videoGetFrame(_videoHandle) + (int64_t)d;
if (_confIsFrameFile) {
frameFileSeek(_frameFileHandle, frame, &_videoHandle, &aFrame);
} else {
if (_videoHandle >= 0) videoSeek(_videoHandle, frame);
}
result = true;
}
}
} else {
luaTrace(L, "discSkipForward", "Failed! Disc is stopped.");
return 0;
}
if (result) {
luaTrace(L, "discSkipForward", "%ld", frame);
} else {
luaTrace(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 (_confIsFrameFile) {
frameFileSeek(_frameFileHandle, frame, &_videoHandle, &aFrame);
} else {
if (_videoHandle >= 0) videoSeek(_videoHandle, frame);
}
if (_videoHandle >= 0) videoPlay(_videoHandle);
_discStopped = false;
result = true;
}
}
if (result) {
luaTrace(L, "discSkipToFrame", "%ld", frame);
} else {
luaTrace(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 (_videoHandle >= 0) {
if (_confIsFrameFile) {
frame = frameFileGetFrame(_frameFileHandle, _videoHandle) - 1;
frameFileSeek(_frameFileHandle, frame, &_videoHandle, &aFrame);
} else {
frame = videoGetFrame(_videoHandle) - 1;
videoSeek(_videoHandle, frame);
}
videoPause(_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 (_videoHandle >= 0) {
if (_confIsFrameFile) {
frame = frameFileGetFrame(_frameFileHandle, _videoHandle) + 1;
frameFileSeek(_frameFileHandle, frame, &_videoHandle, &aFrame);
} else {
frame = videoGetFrame(_videoHandle) + 1;
videoSeek(_videoHandle, frame);
}
videoPause(_videoHandle);
}
luaTrace(L, "discStepForward", "%ld", frame);
return 0;
}
int32_t apiDiscStop(lua_State *L) {
(void)L;
if (!_discStopped) {
if (_videoHandle >= 0) videoPause(_videoHandle);
_discStopped = true;
_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 = _nextFontId;
result = _nextFontId++;
_fontCurrent = font;
HASH_ADD_INT(_fontList, id, font);
}
}
}
if (result >= 0) {
luaTrace(L, "fontLoad", "%s %d", name, result);
} else {
luaTrace(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 (_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 (_fontQuality) {
case 1:
textSurface = TTF_RenderText_Solid(_fontCurrent->font, message, _colorForeground);
break;
case 2:
textSurface = TTF_RenderText_Shaded(_fontCurrent->font, message, _colorForeground, _colorBackground);
break;
case 3:
textSurface = TTF_RenderText_Blended(_fontCurrent->font, message, _colorForeground);
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, _overlay, &dest);
SDL_FreeSurface(textSurface);
}
}
}
}
}
}
if (textSurface) {
luaTrace(L, "fontPrint", "%s", message);
} else {
luaTrace(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); _fontQuality = (int32_t)d;
result = true;
}
}
if (result) {
luaTrace(L, "fontQuality", "%d", _fontQuality);
} else {
luaTrace(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(_fontList, &id, font);
if (!font) luaDie(L, "fontSelect", "No font at index %d in apiSpriteGetWidth.", id);
_fontCurrent = font;
result = true;
}
}
if (result) {
luaTrace(L, "fontSelect", "%d", _fontCurrent->id);
} else {
luaTrace(L, "fontSelect", "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 (_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 (_fontQuality) {
case 1:
sprite->surface = TTF_RenderText_Solid(_fontCurrent->font, message, _colorForeground);
break;
case 2:
sprite->surface = TTF_RenderText_Shaded(_fontCurrent->font, message, _colorForeground, _colorBackground);
break;
case 3:
sprite->surface = TTF_RenderText_Blended(_fontCurrent->font, message, _colorForeground);
break;
}
if (!sprite->surface) {
luaDie(L, "fontToSprite", "Font surface is null!");
} else {
SDL_SetColorKey(sprite->surface, true, 0);
sprite->id = _nextSpriteId;
result = _nextSpriteId++;
HASH_ADD_INT(_spriteList, id, sprite);
}
}
}
}
if (sprite->surface) {
luaTrace(L, "fontToSprite", "%d %s", result, message);
} else {
luaTrace(L, "fontToSprite", "Failed!");
}
lua_pushinteger(L, result);
return 1;
}
int32_t apiOverlayClear(lua_State *L) {
(void)L;
SDL_FillRect(_overlay, NULL, SDL_MapRGBA(_overlay->format, _colorBackground.r, _colorBackground.g, _colorBackground.b, _colorBackground.a));
luaTrace(L, "overlayClear", "");
return 0;
}
int32_t apiOverlayGetHeight(lua_State *L) {
luaTrace(L, "overlayGetHeight", "%d", _overlay->h);
lua_pushinteger(L, _overlay->h);
return 1;
}
int32_t apiOverlayGetWidth(lua_State *L) {
luaTrace(L, "overlayGetWidth", "%d", _overlay->w);
lua_pushinteger(L, _overlay->w);
return 1;
}
int32_t apiOverlayPrint(lua_State *L) {
int32_t n = lua_gettop(L);
int32_t i = 0;
char *s = NULL;
int32_t length = 0;
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 = _consoleFontWidth;
src.h = _consoleFontHeight;
dst.x = lua_tonumber(L, 1);
dst.y = lua_tonumber(L, 2);
dst.w = _consoleFontWidth;
dst.h = _consoleFontHeight;
s = (char *)lua_tostring(L, 3);
if (strlen(s) < (uint32_t)((_overlay->w - dst.x) / _consoleFontWidth)) {
length = strlen(s);
} else {
length = (_overlay->w - dst.x) / _consoleFontWidth;
}
for (i=0; i<length; i++) {
src.x = s[i] * _consoleFontWidth;
SDL_BlitSurface(_consoleFontSurface, &src, _overlay, &dst);
dst.x += _consoleFontWidth;
}
}
}
}
}
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 = _nextSoundId;
result = _nextSoundId++;
HASH_ADD_INT(_soundList, id, sound);
}
}
if (sound) {
luaTrace(L, "soundLoad", "%d %s", result, name);
} else {
luaTrace(L, "soundLoad", "Failed!");
}
lua_pushnumber(L, result);
return 1;
}
int32_t apiSoundPlay(lua_State *L) {
int32_t n = lua_gettop(L);
int32_t result = -1;
int32_t id = -1;
double d = 0;
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(_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)
result = Mix_PlayChannel(-1, sound->chunk, 0);
if (result >= 0) {
Mix_Volume(result, _effectsVolume * 2);
}
}
}
if (sound) {
luaTrace(L, "soundPlay", "%d", result);
} else {
luaTrace(L, "soundPlay", "Failed!");
}
lua_pushnumber(L, result);
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 result = false;
if (n == 1) {
if (lua_isnumber(L, 1)) {
d = lua_tonumber(L, 1); channel = (int32_t)d;
Mix_Pause(channel);
result = true;
}
}
if (result) {
luaTrace(L, "soundPause", "%d", channel);
} else {
luaTrace(L, "soundPause", "Failed!");
}
lua_pushboolean(L, result);
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 result = false;
if (n == 1) {
if (lua_isnumber(L, 1)) {
d = lua_tonumber(L, 1); channel = (int32_t)d;
Mix_Resume(channel);
result = true;
}
}
if (result) {
luaTrace(L, "soundResume", "%d", channel);
} else {
luaTrace(L, "soundResume", "Failed!");
}
lua_pushboolean(L, result);
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 result = false;
if (n == 1) {
if (lua_isnumber(L, 1)) {
d = lua_tonumber(L, 1); channel = (int32_t)d;
result = (bool)Mix_Playing(channel);
}
}
if (result) {
luaTrace(L, "soundIsPlaying", "%d %d", channel, result);
} else {
luaTrace(L, "soundIsPlaying", "Failed!");
}
lua_pushboolean(L, result);
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 result = false;
if (n == 1) {
if (lua_isnumber(L, 1)) {
d = lua_tonumber(L, 1); channel = (int32_t)d;
Mix_HaltChannel(channel);
result = true;
}
}
if (result) {
luaTrace(L, "soundStop", "%d", channel);
} else {
luaTrace(L, "soundStop", "Failed!");
}
lua_pushboolean(L, result);
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) {
_effectsVolume = thisValue;
Mix_Volume(-1, _effectsVolume * 2);
} else {
luaDie(L, "soundSetVolume", "Invalid sound volume value.");
}
}
}
if (result) {
luaTrace(L, "soundSetVolume", "%d", _effectsVolume);
} else {
luaTrace(L, "soundSetVolume", "Failed!");
}
lua_pushboolean(L, result);
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", _effectsVolume);
lua_pushinteger(L, _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 apiSpriteDraw(lua_State *L) {
int32_t n = lua_gettop(L);
int32_t id = -1;
double d = 0;
SpriteT *sprite = NULL;
SDL_Rect dest;
if (n == 3) {
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;
d = lua_tonumber(L, 3); id = (int32_t)d;
HASH_FIND_INT(_spriteList, &id, sprite);
if (!sprite) luaDie(L, "spriteDraw", "No sprite at index %d in apiSpriteGetWidth.", id);
dest.w = sprite->surface->w;
dest.h = sprite->surface->h;
SDL_BlitSurface(sprite->surface, NULL, _overlay, &dest);
}
}
}
}
if (id >= 0) {
luaTrace(L, "spriteDraw", "%d %d %d %d %d", id, dest.x, dest.y, dest.w, dest.h);
} else {
luaTrace(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(_spriteList, &id, sprite);
if (!sprite) luaDie(L, "spriteGetHeight", "No sprite at index %d in apiSpriteGetWidth.", id);
result = sprite->surface->h;
}
}
if (result >= 0) {
luaTrace(L, "spriteGetHeight", "%d", result);
} else {
luaTrace(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;
lua_Debug ar;
lua_getstack(L, 1, &ar);
lua_getinfo(L, "nSl", &ar);
if (n == 1) {
if (lua_isstring(L, 1)) {
d = lua_tonumber(L, 1); id = (int32_t)d;
// Get our sprite structure
HASH_FIND_INT(_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 {
luaTrace(L, "spriteGetWidth", "Failed!");
}
lua_pushinteger(L, result);
return 1;
}
int32_t apiSpriteLoad(lua_State *L) {
int32_t n = lua_gettop(L);
int32_t result = -1;
const char *name = NULL;
SpriteT *sprite = NULL;
if (n == 1) {
if (lua_isstring(L, 1)) {
sprite = (SpriteT *)calloc(1, sizeof(SpriteT));
if (!sprite) luaDie(L, "spriteLoad", "Unable to allocate new sprite.");
name = lua_tostring(L, 1);
sprite->surface = IMG_Load(name);
if (!sprite->surface) luaDie(L, "spriteLoad", "%s", IMG_GetError());
SDL_SetColorKey(sprite->surface, true, 0);
sprite->id = _nextSpriteId;
result = _nextSpriteId++;
HASH_ADD_INT(_spriteList, id, sprite);
}
}
if (sprite->surface) {
luaTrace(L, "spriteLoad", "%d %s", result, name);
} else {
luaTrace(L, "spriteLoad", "Failed!");
}
lua_pushnumber(L, result);
return 1;
}
int32_t apiVldpGetHeight(lua_State *L) {
int32_t height = 0;
if (_videoHandle >= 0) height = videoGetHeight(_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 / _overlayScaleX);
d = lua_tonumber(L, 2); rect.y = (int32_t)(d / _overlayScaleY);
if (SDL_SetRenderTarget(_renderer, _videoTexture) < 0) luaDie(L, "vldpGetPixel", "%s", SDL_GetError());
if (SDL_RenderReadPixels(_renderer, &rect, SDL_PIXELFORMAT_BGRA32, pixel, SDL_BYTESPERPIXEL(SDL_PIXELFORMAT_BGRA32) * videoGetWidth(_videoHandle)) < 0) luaDie(L, "vldpGetPixel", "%s", SDL_GetError());
if (SDL_SetRenderTarget(_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]);
lua_pushinteger(L, (int32_t)pixel[2]); // R
lua_pushinteger(L, (int32_t)pixel[1]); // G
lua_pushinteger(L, (int32_t)pixel[0]); // B
} else {
luaTrace(L, "vldpGetPixel", "Failed!");
lua_pushinteger(L, -1);
lua_pushinteger(L, -1);
lua_pushinteger(L, -1);
}
return 3;
}
int32_t apiVldpGetWidth(lua_State *L) {
int32_t width = 0;
if (_videoHandle >= 0) width = videoGetWidth(_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", _keyboardMode);
lua_pushinteger(L, _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); _keyboardMode = (int32_t)d;
result = true;
}
}
if (result) {
luaTrace(L, "keyboardSetMode", "%d", _keyboardMode);
} else {
luaTrace(L, "keyboardSetMode", "Failed!");
}
return 0;
}
int32_t apiMouseEnable(lua_State *L) {
// Enables mouse monitoring
(void)L;
luaTrace(L, "mouseEnable", "%d", _confNoMouse);
_mouseEnabled = (bool)!_confNoMouse;
return 0;
}
int32_t apiMouseDisable(lua_State *L) {
// Disables mouse monitoring
(void)L;
luaTrace(L, "mouseDisable", "");
_mouseEnabled = false;
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); _mouseMode = (int32_t)d;
result = true;
}
}
if (result) {
luaTrace(L, "mouseSetMode", "%d", _mouseMode);
} else {
luaTrace(L, "mouseSetMode", "Failed!");
}
lua_pushboolean(L, result);
return 1;
}
int32_t apiMouseHowMany(lua_State *L) {
luaTrace(L, "mouseHowMany", "%d", _mouseCount);
lua_pushinteger(L, _mouseCount);
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_PAUSE = 4
*
*/
// Our player isn't as sophisticated as the one in Daphne
if (_videoHandle >= 0) isPlaying = videoIsPlaying(_videoHandle);
luaTrace(L, "discGetState", "%s", _discStopped ? "Stopped" : (isPlaying ? "Playing" : "Paused"));
lua_pushinteger(L, _discStopped ? LDP_STOPPED : (isPlaying ? LDP_PLAYING : LDP_PAUSE));
return 1;
}
int32_t apiSingeGetPauseFlag(lua_State *L) {
/*
* This function returns g_pause_state'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_pause state is an internal variable that keeps track
* of this. It's set to true whenever sep_pre_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", _pauseState);
lua_pushboolean(L, _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)) {
_pauseState = (bool)lua_toboolean(L, 1);
result = true;
}
}
if (result) {
luaTrace(L, "singeSetPauseFlag", "%d", _pauseState);
} else {
luaTrace(L, "singeSetPauseFlag", "Failed!");
}
return 0;
}
int32_t apiSingeEnablePauseKey(lua_State *L) {
(void)L;
luaTrace(L, "singeEnablePauseKey", "");
_pauseEnabled = true;
return 0;
}
int32_t apiSingeDisablePauseKey(lua_State *L) {
(void)L;
luaTrace(L, "singeDisablePauseKey", "");
_pauseEnabled = false;
return 0;
}
int32_t apiSingeQuit(lua_State *L) {
(void)L;
luaTrace(L, "singeQuit", "");
_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 apiSingeSetGameName(lua_State *L) {
// Adds the name of the singe game to the window's title bar.
// Valid value is a string no longer than 25 characters.
// e.g. lua code,
//
// singeSetGameName("My FMV game")
//
// Function returns nothing.
//
// --rdg
int32_t n = lua_gettop(L);
bool result = false;
char thisName[MAX_TITLE_LENGTH];
if (n == 1) {
if (lua_isstring(L, 1)) {
sprintf(thisName,"%.40s", lua_tostring(L, 1)); // Need a better way to do this...?
SDL_SetWindowTitle(_window, thisName);
result = true;
}
}
if (result) {
luaTrace(L, "singeSetGameName", "%s", thisName);
} else {
luaTrace(L, "singeSetGameName", "Failed!");
}
lua_pushboolean(L, result);
return 0;
}
int32_t apiSingeGetScriptPath(lua_State *L) {
// Returns the path to the singe script.
// e.g. lua code,
//
// sGameDirectory = singeGetScriptPath()
//
luaTrace(L, "singeSetScriptPath", "%s", _confScriptFile);
lua_pushstring(L, _confScriptFile);
return 1;
}
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(_luaContext);
va_start(vl, sig);
// Get Function
lua_getglobal(_luaContext, func);
if (!lua_isfunction(_luaContext, -1)) {
// Function does not exist. Bail.
lua_settop(_luaContext, top);
return;
}
utilTrace("%s", func);
// Push Arguments
narg = 0;
while ((*sig) && (!done)) {
switch (*sig++) {
case 'd': // Double
lua_pushnumber(_luaContext, va_arg(vl, double));
break;
case 'i': // Int
lua_pushinteger(_luaContext, va_arg(vl, int)); // Not sure I want to change this to int32_t
break;
case 's': // String
lua_pushstring(_luaContext, va_arg(vl, char *));
break;
case '>':
done = true;
break;
default:
utilDie("Invalid argument option (%c)", *(sig - 1));
}
if (!done) {
narg++;
luaL_checkstack(_luaContext, 1, "Too many arguments");
}
}
// Do the call
popCount = nres = (int32_t)strlen(sig); // Number of expected results
if (lua_pcall(_luaContext, narg, nres, 0) != 0) {
utilDie("Error executing function '%s': %s", func, lua_tostring(_luaContext, -1));
}
// Retrieve results
nres = -nres; // Stack index of first result
while (*sig) {
switch (*sig++) {
case 'd': // Double
if (!lua_isnumber(_luaContext, nres)) {
utilDie("Wrong result type");
}
*va_arg(vl, double *) = lua_tonumber(_luaContext, nres);
break;
case 'i': // Int
if (!lua_isnumber(_luaContext, nres)) {
utilDie("Wrong result type");
}
d = lua_tonumber(_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(_luaContext, nres)) {
utilDie("Wrong result type");
}
*va_arg(vl, const char **) = lua_tostring(_luaContext, nres);
break;
default:
utilDie("Invalid option (%c)", *(sig - 1));
}
nres++;
}
va_end(vl);
if (popCount > 0) {
lua_pop(_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(_renderer, &oldW, &oldH);
SDL_RenderSetLogicalSize(_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(_renderer, surfDisc);
if (!texDisc) utilDie("%s", SDL_GetError());
texGlass = SDL_CreateTextureFromSurface(_renderer, surfGlass);
if (!texGlass) utilDie("%s", SDL_GetError());
texIndex = SDL_CreateTextureFromSurface(_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(_renderer, oldW, oldH);
SDL_SetRenderDrawColor(_renderer, 0, 0, 0, 255);
SDL_RenderClear(_renderer);
SDL_RenderPresent(_renderer);
return;
}
// Display animation
SDL_RenderSetLogicalSize(_renderer, screenW, screenH);
SDL_SetRenderDrawColor(_renderer, 0, 0, 0, 255);
SDL_RenderClear(_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(_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(_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(_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(_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(_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(_renderer, surfKangaroo);
if (!texKangaroo) utilDie("%s", SDL_GetError());
texSinge = SDL_CreateTextureFromSurface(_renderer, surfSinge);
if (!texSinge) utilDie("%s", SDL_GetError());
// Fade in to white with Kangaroo logo
SDL_RenderSetLogicalSize(_renderer, surfKangaroo->w, surfKangaroo->h);
for (i=0; i<256; i++) {
SDL_SetRenderDrawColor(_renderer, i, i, i, 255);
SDL_RenderClear(_renderer);
SDL_SetTextureAlphaMod(texKangaroo, i);
SDL_RenderCopy(_renderer, texKangaroo, NULL, NULL);
SDL_RenderPresent(_renderer);
SDL_Delay(3);
}
SDL_Delay(750);
// Cross fade to Singe logo
for (i=0; i<256; i++) {
SDL_RenderClear(_renderer);
SDL_SetTextureAlphaMod(texKangaroo, 255 - i);
SDL_RenderCopy(_renderer, texKangaroo, NULL, NULL);
SDL_SetTextureAlphaMod(texSinge, i);
SDL_RenderCopy(_renderer, texSinge, NULL, NULL);
SDL_RenderPresent(_renderer);
SDL_Delay(3);
}
SDL_Delay(750);
// Fade to black
SDL_RenderSetLogicalSize(_renderer, surfSinge->w, surfSinge->h);
for (i=255; i>=0; i--) {
SDL_SetRenderDrawColor(_renderer, i, i, i, 255);
SDL_RenderClear(_renderer);
SDL_SetTextureAlphaMod(texSinge, i);
SDL_RenderCopy(_renderer, texSinge, NULL, NULL);
SDL_RenderPresent(_renderer);
SDL_Delay(3);
}
SDL_DestroyTexture(texSinge);
SDL_DestroyTexture(texKangaroo);
SDL_FreeSurface(surfSinge);
SDL_FreeSurface(surfKangaroo);
SDL_RenderSetLogicalSize(_renderer, w, h);
}
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);
utilTrace("%s%s", string1, string2);
utilDie("%s%s", string1, string2);
// Can't free strings - we never get here.
}
int32_t luaError(lua_State *L) {
lua_Debug ar;
int32_t level = 0;
utilSay("Singe has panicked! Very bad!");
utilSay("Error: %s", lua_tostring(L, -1));
utilSay("Stack trace:");
while (lua_getstack(L, level, &ar) != 0) {
lua_getinfo(L, "nSl", &ar);
utilSay(" %d: function `%s' at line %d %s", level, ar.name, ar.currentline, ar.short_src);
level++;
}
utilSay("Trace complete.");
return 0;
}
#ifdef DEBUG_TOOLS
void luaStackDump(lua_State *L) {
int i;
int t;
int top = lua_gettop(L);
printf("%d: ", top);
for (i=1; i<=top; i++) {
printf("%d - ", i);
t = lua_type(L, i);
switch (t) {
case LUA_TSTRING:
printf("`%s'", lua_tostring(L, i));
break;
case LUA_TBOOLEAN:
printf(lua_toboolean(L, i) ? "true" : "false");
break;
case LUA_TNUMBER:
printf("%g", lua_tonumber(L, i));
break;
default:
printf("%s", lua_typename(L, t));
break;
}
printf(" ");
}
printf("\n");
}
#endif
void luaTrace(lua_State *L, char *method, char *fmt, ...) {
va_list args;
lua_Debug ar;
char *string1 = NULL;
char *string2 = NULL;
if (utilTraceFile) {
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);
utilSay("%s%s", string1, string2);
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;
if (_keyboardMode == KEYBD_NORMAL) {
// Mappable keys
for (move=0; move<INPUT_COUNT; move++) {
if (_controlMappings[move].inputCount > 0) {
for (index=0; index<_controlMappings[move].inputCount; index++) {
if (_controlMappings[move].input[index] == scancode) {
if (!down) {
if ((move == SWITCH_PAUSE) && (_pauseEnabled)) {
//***TODO*** g_game->toggle_game_pause();
}
if (move == SWITCH_QUIT) {
_running = false;
}
if (move == SWITCH_SCREENSHOT) {
_requestScreenShot = true;
}
}
if (move != SWITCH_PAUSE) {
callLua(down ? "onInputPressed" : "onInputReleased", "ii", move, NOMOUSE);
}
}
}
}
}
} else {
// Full keyboard
callLua(down ? "onInputPressed" : "onInputReleased", "ii", keysym, NOMOUSE);
callLua(down ? "onKeyPressed" : "onKeyReleased", "i", scancode);
}
}
void singe(SDL_Window *window, SDL_Renderer *renderer) {
int32_t x = 0;
int32_t y = 0;
int32_t xr = 0;
int32_t yr = 0;
int32_t intReturn = 0;
int64_t thisFrame = -1;
int64_t lastFrame = -1;
char *temp = NULL;
char *temp2 = NULL;
SDL_Rect windowTarget;
SDL_Rect sindenWhite;
SDL_Rect sindenBlack;
SDL_Color sindenWhiteColor;
SDL_Color sindenBlackColor;
SDL_Texture *overlayTexture = NULL;
SpriteT *sprite = NULL;
SpriteT *spriteTemp = NULL;
SoundT *sound = NULL;
SoundT *soundTemp = NULL;
FontT *font = NULL;
FontT *fontTemp = NULL;
SDL_Event event;
ManyMouseEvent mouseEvent;
MouseT *mouse = NULL;
int32_t mouseButtonMap[] = {
SWITCH_BUTTON1, // Left
SWITCH_BUTTON3, // Middle
SWITCH_BUTTON2, // Right
SWITCH_BUTTON1, // Wheel Up
SWITCH_BUTTON2, // Wheel Down
SWITCH_MOUSE_DISCONNECT
};
// Hang on to some SDL stuff
_window = window;
_renderer = renderer;
// Load controller mappings
_luaContext = luaL_newstate();
luaL_openlibs(_luaContext);
lua_atpanic(_luaContext, luaError);
// Load framework
if (luaL_loadbuffer(_luaContext, (char *)Framework_singe, Framework_singe_len, "Input Mappings") || lua_pcall(_luaContext, 0, 0, 0)) utilDie("%s", lua_tostring(_luaContext, -1));
// Load default mappings
if (luaL_loadbuffer(_luaContext, (char *)controls_cfg, controls_cfg_len, "Input Mappings") || lua_pcall(_luaContext, 0, 0, 0)) utilDie("%s", lua_tostring(_luaContext, -1));
if (utilFileExists("controls.cfg") && luaL_dofile(_luaContext, "controls.cfg")) utilDie("%s", lua_tostring(_luaContext, -1));
// Load mappings in main data dir
temp = utilCreateString("%s..%ccontrols.cfg", _confDataDir, utilGetPathSeparator());
if (utilFileExists(temp) && luaL_dofile(_luaContext, temp)) utilDie("%s", lua_tostring(_luaContext, -1));
free(temp);
// Load mappings in game data dir
temp = utilCreateString("%scontrols.cfg", _confDataDir);
if (utilFileExists(temp) && luaL_dofile(_luaContext, temp)) utilDie("%s", lua_tostring(_luaContext, -1));
free(temp);
// Load mappings in game script dir
temp = strdup(_confScriptFile);
temp2 = strndup(temp, strlen(temp) - strlen(utilGetLastPathComponent(temp)));
free(temp);
temp = utilCreateString("%scontrols.cfg", temp2);
if (utilFileExists(temp) && luaL_dofile(_luaContext, temp)) utilDie("%s", lua_tostring(_luaContext, -1));
free(temp);
// Parse results
lua_getglobal(_luaContext, "DEAD_ZONEX");
if (lua_isnumber(_luaContext, -1)) {
_controllerDeadZone = (int32_t)lua_tonumber(_luaContext, -1);
}
lua_pop(_luaContext, 1);
for (x=0; x<INPUT_COUNT; x++) {
// First, count integers in Lua table and allocate memory...
lua_getglobal(_luaContext, _controlMappings[x].name);
if (lua_istable(_luaContext, -1)) {
y = 0;
lua_pushnil(_luaContext);
while (lua_next(_luaContext, -2)) {
y++;
lua_pop(_luaContext, 1);
}
if (_controlMappings[x].input != NULL) free(_controlMappings[x].input);
if (y > 0) {
_controlMappings[x].inputCount = y;
_controlMappings[x].input = (int32_t *)malloc(sizeof(int32_t) * y);
if (!_controlMappings[x].input) utilDie("Unable to allocate memory for control mappings.");
}
} else {
utilSay("Configuration option %s missing!", _controlMappings[x].name);
}
lua_pop(_luaContext, 1);
// Then load them for real.
lua_getglobal(_luaContext, _controlMappings[x].name);
if (lua_istable(_luaContext, -1)) {
lua_pushnil(_luaContext);
y = 0;
while (lua_next(_luaContext, -2)) {
_controlMappings[x].input[y++] = (int32_t)lua_tonumber(_luaContext, -1);
lua_pop(_luaContext, 1);
}
}
lua_pop(_luaContext, 1);
}
lua_close(_luaContext);
// Show splash screens
if (!_confNoConsole) {
doLogos();
}
// Start Lua for game
_luaContext = luaL_newstate();
luaL_openlibs(_luaContext);
lua_atpanic(_luaContext, luaError);
// Lua API for Singe
lua_register(_luaContext, "colorBackground", apiColorBackground);
lua_register(_luaContext, "colorForeground", apiColorForeground);
lua_register(_luaContext, "daphneGetHeight", apiDaphneGetHeight);
lua_register(_luaContext, "daphneGetWidth", apiDaphneGetWidth);
lua_register(_luaContext, "daphneScreenshot", apiDaphneScreenshot);
lua_register(_luaContext, "debugPrint", apiDebugPrint);
lua_register(_luaContext, "discAudio", apiDiscAudio);
lua_register(_luaContext, "discChangeSpeed", apiDiscChangeSpeed);
lua_register(_luaContext, "discGetFrame", apiDiscGetFrame);
lua_register(_luaContext, "discPause", apiDiscPause);
lua_register(_luaContext, "discPauseAtFrame", apiDiscPauseAtFrame);
lua_register(_luaContext, "discPlay", apiDiscPlay);
lua_register(_luaContext, "discSearch", apiDiscSearch);
lua_register(_luaContext, "discSearchBlanking", apiDiscSearchBlanking);
lua_register(_luaContext, "discSetFPS", apiDiscSetFps);
lua_register(_luaContext, "discSkipBackward", apiDiscSkipBackward);
lua_register(_luaContext, "discSkipBlanking", apiDiscSkipBlanking);
lua_register(_luaContext, "discSkipForward", apiDiscSkipForward);
lua_register(_luaContext, "discSkipToFrame", apiDiscSkipToFrame);
lua_register(_luaContext, "discStepBackward", apiDiscStepBackward);
lua_register(_luaContext, "discStepForward", apiDiscStepForward);
lua_register(_luaContext, "discStop", apiDiscStop);
lua_register(_luaContext, "fontLoad", apiFontLoad);
lua_register(_luaContext, "fontPrint", apiFontPrint);
lua_register(_luaContext, "fontQuality", apiFontQuality);
lua_register(_luaContext, "fontSelect", apiFontSelect);
lua_register(_luaContext, "fontToSprite", apiFontToSprite);
lua_register(_luaContext, "overlayClear", apiOverlayClear);
lua_register(_luaContext, "overlayGetHeight", apiOverlayGetHeight);
lua_register(_luaContext, "overlayGetWidth", apiOverlayGetWidth);
lua_register(_luaContext, "overlayPrint", apiOverlayPrint);
lua_register(_luaContext, "soundLoad", apiSoundLoad);
lua_register(_luaContext, "soundPlay", apiSoundPlay);
lua_register(_luaContext, "soundPause", apiSoundPause); // rdg
lua_register(_luaContext, "soundResume", apiSoundResume); //
lua_register(_luaContext, "soundIsPlaying", apiSoundIsPlaying); //
lua_register(_luaContext, "soundStop", apiSoundStop); //
lua_register(_luaContext, "soundSetVolume", apiSoundSetVolume);
lua_register(_luaContext, "soundGetVolume", apiSoundGetVolume);
lua_register(_luaContext, "soundFullStop", apiSoundFullStop);
lua_register(_luaContext, "spriteDraw", apiSpriteDraw);
lua_register(_luaContext, "spriteGetHeight", apiSpriteGetHeight);
lua_register(_luaContext, "spriteGetWidth", apiSpriteGetWidth);
lua_register(_luaContext, "spriteLoad", apiSpriteLoad);
lua_register(_luaContext, "vldpGetHeight", apiVldpGetHeight);
lua_register(_luaContext, "vldpGetPixel", apiVldpGetPixel);
lua_register(_luaContext, "vldpGetWidth", apiVldpGetWidth);
lua_register(_luaContext, "vldpSetVerbose", apiVldpVerbose);
// by RDG2010
lua_register(_luaContext, "keyboardGetMode", apiKeyboardGetMode);
lua_register(_luaContext, "keyboardSetMode", apiKeyboardSetMode);
lua_register(_luaContext, "mouseEnable", apiMouseEnable);
lua_register(_luaContext, "mouseDisable", apiMouseDisable);
lua_register(_luaContext, "mouseSetMode", apiMouseSetMode);
lua_register(_luaContext, "mouseHowMany", apiMouseHowMany);
lua_register(_luaContext, "discGetState", apiDiscGetState);
lua_register(_luaContext, "singeGetPauseFlag", apiSingeGetPauseFlag);
lua_register(_luaContext, "singeSetPauseFlag", apiSingeSetPauseFlag);
lua_register(_luaContext, "singeEnablePauseKey", apiSingeEnablePauseKey);
lua_register(_luaContext, "singeDisablePauseKey", apiSingeDisablePauseKey);
lua_register(_luaContext, "singeQuit", apiSingeQuit);
lua_register(_luaContext, "singeVersion", apiSingeVersion);
lua_register(_luaContext, "singeSetGameName", apiSingeSetGameName);
lua_register(_luaContext, "singeGetScriptPath", apiSingeGetScriptPath);
// Open main video file
doIndexDisplay(-1);
videoSetIndexCallback(doIndexDisplay);
if (_confIsFrameFile) {
_frameFileHandle = frameFileLoad(_confVideoFile, _confDataDir, (bool)_confStretchVideo, _renderer);
if (_frameFileHandle < 0) utilDie("Unable to load framefile: %s", _confVideoFile);
frameFileSeek(_frameFileHandle, 0, &_videoHandle, &thisFrame); // Fills in _videoHandle
} else {
_videoHandle = videoLoad(_confVideoFile, _confDataDir, (bool)_confStretchVideo, _renderer);
}
if (_videoHandle < 0) utilDie("Unable to load video file: %s", _confVideoFile);
videoSetVolume(_videoHandle, _confVolumeVldp, _confVolumeVldp);
videoSetIndexCallback(NULL);
doIndexDisplay(-2);
// Should we resize the window?
//***TODO***
// Default render location is the entire window
windowTarget.x = 0;
windowTarget.y = 0;
windowTarget.w = videoGetWidth(_videoHandle);
windowTarget.h = videoGetHeight(_videoHandle);
sindenWhite.x = -1;
sindenBlack.x = -1;
// Overscan compensation
if (_confScaleFactor < 100) {
windowTarget.w = videoGetWidth(_videoHandle) * _confScaleFactor / 100;
windowTarget.h = videoGetHeight(_videoHandle) * _confScaleFactor / 100;
windowTarget.x = (videoGetWidth(_videoHandle) - windowTarget.w) / 2;
windowTarget.y = (videoGetHeight(_videoHandle) - windowTarget.h) / 2;
}
// Sinden Light Gun Border Setup
if (_confSindenArgc > 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(_confSindenArgc) {
// WW - Just the width of the white border
case SINDEN_WHITE:
sindenWhite.x = _confSindenArgv[0];
break;
// WW WB - Width of white border and then black border
case SINDEN_WHITE_BLACK:
sindenWhite.x = _confSindenArgv[0];
sindenBlack.x = _confSindenArgv[1];
break;
// RW GW BW WW - Custom color "white" border and width
case SINDEN_CUSTOM_WHITE:
sindenWhiteColor.r = _confSindenArgv[0];
sindenWhiteColor.g = _confSindenArgv[1];
sindenWhiteColor.b = _confSindenArgv[2];
sindenWhite.x = _confSindenArgv[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 = _confSindenArgv[0];
sindenWhiteColor.g = _confSindenArgv[1];
sindenWhiteColor.b = _confSindenArgv[2];
sindenWhite.x = _confSindenArgv[3];
sindenBlack.x = _confSindenArgv[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 = _confSindenArgv[0];
sindenWhiteColor.g = _confSindenArgv[1];
sindenWhiteColor.b = _confSindenArgv[2];
sindenWhite.x = _confSindenArgv[3];
sindenBlackColor.r = _confSindenArgv[4];
sindenBlackColor.g = _confSindenArgv[5];
sindenBlackColor.b = _confSindenArgv[6];
sindenBlack.x = _confSindenArgv[7];
break;
}
if (sindenWhite.x >= 0) {
sindenWhite.y = sindenWhite.x;
sindenWhite.w = videoGetWidth(_videoHandle) - (sindenWhite.x * 2);
sindenWhite.h = videoGetHeight(_videoHandle) - (sindenWhite.y * 2);
}
if (sindenBlack.x >= 0) {
sindenBlack.y = sindenBlack.x;
sindenBlack.w = videoGetWidth(_videoHandle) - (sindenBlack.x * 2);
sindenBlack.h = videoGetHeight(_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
_overlayScaleX = 0.5;
_overlayScaleY = 0.5;
x = (int32_t)(videoGetWidth(_videoHandle) * _overlayScaleX);
y = (int32_t)(videoGetHeight(_videoHandle) * _overlayScaleY);
_overlay = SDL_CreateRGBSurfaceWithFormat(0, x, y, 32, SDL_PIXELFORMAT_BGRA32);
if (_overlay == NULL) utilDie("%s", SDL_GetError());
SDL_SetColorKey(_overlay, true, 0);
// Mouse setup
_mouseCount = ManyMouse_Init();
//utilSay("***DEBUG*** Mouse Driver: %s", ManyMouse_DriverName());
//utilSay("***DEBUG*** Mice Found: %d", _mouseCount);
if (_mouseCount < 1) utilDie("No mice detected.");
if (_mouseCount > MAX_MICE) {
_mouseCount = MAX_MICE;
}
memset(_mice, 0, sizeof(_mice));
_mouseEnabled = (bool)!_confNoMouse;
for (x=0; x<_mouseCount; x++) {
strncpy(_mice[x].name, ManyMouse_DeviceName((unsigned)x), sizeof(_mice[x].name));
_mice[x].name[sizeof(_mice[x].name) - 1] = 0;
_mice[x].x = (int32_t)(videoGetWidth(_videoHandle) * _overlayScaleX);
_mice[x].y = (int32_t)(videoGetHeight(_videoHandle) * _overlayScaleY);
//utilSay("***DEBUG*** Mouse %d: %s", x, _mice[x].name);
}
SDL_SetWindowGrab(_window, SDL_TRUE);
SDL_ShowCursor(SDL_DISABLE);
// Set volume
_effectsVolume = (int32_t)((float)AUDIO_MAX_VOLUME * (float)_confVolumeNonVldp * (float)0.01);
Mix_Volume(-1, _effectsVolume * 2);
// Let us know when sounds end
Mix_ChannelFinished(channelFinished);
// Load overlay font
_consoleFontSurface = IMG_LoadPNG_RW(SDL_RWFromMem(font_png, font_png_len));
if (_consoleFontSurface == NULL) utilDie("%s", SDL_GetError());
_consoleFontWidth = _consoleFontSurface->w / 256;
_consoleFontHeight = _consoleFontSurface->h;
SDL_SetColorKey(_consoleFontSurface, true, _consoleFontSurface->format->Rmask | _consoleFontSurface->format->Bmask);
// Start video
videoPlay(_videoHandle);
_discStopped = false;
// Start script
if (luaL_dofile(_luaContext, _confScriptFile) != 0) utilDie("Error compiling script: %s", lua_tostring(_luaContext, -1));
// Game Loop
while (_running) {
// SDL Event Loop
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
processKey(true, event.key.keysym.sym, event.key.keysym.scancode);
//***TODO*** RDG used "g" to re-grab/re-scan for mice
break;
case SDL_KEYUP:
processKey(false, event.key.keysym.sym, event.key.keysym.scancode);
break;
case SDL_MOUSEMOTION:
if ((_mouseEnabled) && (_mouseMode == MOUSE_SINGLE)) {
x = (int32_t)(event.motion.x * _overlayScaleX);
y = (int32_t)(event.motion.y * _overlayScaleY);
xr = (int32_t)(event.motion.xrel * _overlayScaleX);
yr = (int32_t)(event.motion.yrel * _overlayScaleY);
callLua("onMouseMoved", "iiiii", x, y, xr, yr, NOMOUSE);
}
break;
case SDL_MOUSEBUTTONDOWN:
for (x=0; x<MOUSE_ARRAY_SIZE; x++) {
if (event.button.button == x) {
callLua("onInputPressed", "ii", mouseButtonMap[x], NOMOUSE);
break;
}
}
break;
case SDL_MOUSEBUTTONUP:
for (x=0; x<MOUSE_ARRAY_SIZE; x++) {
if (event.button.button == x) {
callLua("onInputReleased", "ii", mouseButtonMap[x], NOMOUSE);
break;
}
}
break;
case SDL_QUIT:
_running = 0;
break;
}
// Mouse Event Loop
while (ManyMouse_PollEvent(&mouseEvent)) {
// Just run out the event queue if we're not using ManyMouse
if ((!_mouseEnabled) || (_mouseMode == MOUSE_SINGLE)) continue;
// Has this one been unplugged?
if (mouseEvent.device >= (unsigned)_mouseCount) continue;
mouse = &_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(_videoHandle);
y = videoGetHeight(_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 * _overlayScaleX);
y = (int32_t)(mouse->y * _overlayScaleY);
xr = (int32_t)(mouse->relx * _overlayScaleX);
yr = (int32_t)(mouse->relx * _overlayScaleY);
callLua("onMouseMoved", "iiiii", x, y, xr, yr, mouseEvent.device);
break;
case MANYMOUSE_EVENT_ABSMOTION:
switch (mouseEvent.item) {
case 0:
mouse->x += mouseEvent.value;
break;
case 1:
mouse->y += mouseEvent.value;
break;
}
x = (int32_t)(mouse->x * _overlayScaleX);
y = (int32_t)(mouse->y * _overlayScaleY);
xr = (int32_t)(mouse->relx * _overlayScaleX);
yr = (int32_t)(mouse->relx * _overlayScaleY);
utilSay("Mouse %d: Absolute %d, %d", mouseEvent.device, x, y);
callLua("onMouseMoved", "iiiii", x, y, xr, yr, mouseEvent.device);
break;
case MANYMOUSE_EVENT_BUTTON:
if (mouseEvent.item < 32) {
if (mouseEvent.value == 1) {
// Button pressed
callLua("onInputPressed", "ii", mouseButtonMap[mouseEvent.item], mouseEvent.device);
mouse->buttons |= (1 << mouseEvent.item);
} else {
// Button released
callLua("onInputReleased", "ii", mouseButtonMap[mouseEvent.item], mouseEvent.device);
mouse->buttons &= ~(1 << mouseEvent.item);
}
}
break;
case MANYMOUSE_EVENT_SCROLL:
if (mouseEvent.item == 0) {
if (mouseEvent.value > 0) {
// Scroll up
callLua("onInputPressed", "ii", SWITCH_MOUSE_SCROLL_UP, mouseEvent.device);
callLua("onInputReleased", "ii", SWITCH_MOUSE_SCROLL_UP, mouseEvent.device);
} else {
// Scroll down
callLua("onInputPressed", "ii", SWITCH_MOUSE_SCROLL_DOWN, mouseEvent.device);
callLua("onInputReleased", "ii", SWITCH_MOUSE_SCROLL_DOWN, mouseEvent.device);
}
}
break;
case MANYMOUSE_EVENT_DISCONNECT:
mouse->connected = false;
callLua("onInputPressed", "ii", SWITCH_MOUSE_DISCONNECT, mouseEvent.device);
callLua("onInputReleased", "ii", SWITCH_MOUSE_DISCONNECT, mouseEvent.device);
break;
case MANYMOUSE_EVENT_MAX:
// We don't use this
break;
}
}
}
// Call game code
callLua("onOverlayUpdate", ">i", &intReturn);
if (intReturn == 1) {
_refreshDisplay = true;
}
// Update video
thisFrame = videoUpdate(_videoHandle, &_videoTexture);
if (_confIsFrameFile) {
frameFileUpdate(_frameFileHandle, &_videoHandle);
}
if ((thisFrame != lastFrame) && (thisFrame >= 0)) {
lastFrame = thisFrame;
_refreshDisplay = true;
}
// Update display
if (_refreshDisplay || _discStopped) {
// Sinden Gun Border
if (sindenWhite.x >= 0) {
if (sindenBlack.x >= 0) {
// Black and White
SDL_SetRenderDrawColor(_renderer, sindenBlackColor.r, sindenBlackColor.g, sindenBlackColor.b, sindenBlackColor.a);
SDL_RenderClear(_renderer);
SDL_SetRenderDrawColor(_renderer, sindenWhiteColor.r, sindenWhiteColor.g, sindenWhiteColor.b, sindenWhiteColor.a);
SDL_RenderFillRect(_renderer, &sindenBlack);
} else {
// Only white
SDL_SetRenderDrawColor(_renderer, sindenWhiteColor.r, sindenWhiteColor.g, sindenWhiteColor.b, sindenWhiteColor.a);
SDL_RenderClear(_renderer);
}
SDL_RenderFillRect(_renderer, &windowTarget);
}
// Laserdisc Video
if (_discStopped) {
// Stopped discs display blue like the good old days
SDL_SetRenderTarget(_renderer, _videoTexture);
SDL_SetRenderDrawColor(_renderer, 0, 0, 255, 255);
SDL_RenderClear(_renderer);
SDL_SetRenderTarget(_renderer, NULL);
}
SDL_RenderCopy(_renderer, _videoTexture, NULL, &windowTarget);
// Overlay
overlayTexture = SDL_CreateTextureFromSurface(_renderer, _overlay);
if (!overlayTexture) utilDie("%s", SDL_GetError());
if (!_confStretchVideo) {
SDL_RenderSetLogicalSize(renderer, videoGetWidth(_videoHandle), videoGetHeight(_videoHandle));
}
SDL_RenderCopy(_renderer, overlayTexture, NULL, &windowTarget);
SDL_DestroyTexture(overlayTexture);
overlayTexture = NULL;
// Show it
SDL_RenderPresent(_renderer);
_refreshDisplay = false;
// Save it?
if (_requestScreenShot) {
_requestScreenShot = false;
takeScreenshot();
}
}
}
// End game
callLua("onShutdown", "");
// Stop all sounds
Mix_HaltChannel(-1);
Mix_ChannelFinished(NULL);
// Stop Lua
lua_close(_luaContext);
// Free overlay & overlay font
SDL_FreeSurface(_overlay);
SDL_FreeSurface(_consoleFontSurface);
// Unload fonts
HASH_ITER(hh, _fontList, font, fontTemp) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-align"
HASH_DEL(_fontList, font);
#pragma GCC diagnostic pop
TTF_CloseFont(font->font);
free(font);
}
// Unload sounds
HASH_ITER(hh, _soundList, sound, soundTemp) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-align"
HASH_DEL(_soundList, sound);
#pragma GCC diagnostic pop
Mix_FreeChunk(sound->chunk);
free(sound);
}
// Unload sprites
HASH_ITER(hh, _spriteList, sprite, spriteTemp) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-align"
HASH_DEL(_spriteList, sprite);
#pragma GCC diagnostic pop
SDL_FreeSurface(sprite->surface);
free(sprite);
}
// Unload video
if (_confIsFrameFile) {
frameFileUnload(_frameFileHandle);
} else {
videoUnload(_videoHandle);
}
// Stop mice
SDL_ShowCursor(SDL_ENABLE);
ManyMouse_Quit();
}
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", _confDataDir, x);
if (!utilFileExists(filename)) break;
x++;
}
if (x > 999) utilDie("Seriously? You have 1000 screenshots in this folder? Remove some.");
SDL_RenderGetViewport(_renderer, &viewport);
surface = SDL_CreateRGBSurface(0, viewport.w, viewport.h, 32, 0, 0, 0, 0);
if (!surface) utilDie("%s", SDL_GetError());
if (SDL_RenderReadPixels(_renderer, NULL, surface->format->format, surface->pixels, surface->pitch) != 0) utilDie("%s", SDL_GetError());
if (IMG_SavePNG(surface, filename) < 0) utilDie("%s", IMG_GetError());
SDL_FreeSurface(surface);
free(pixels);
}