Compare commits

...
65 Commits
Author SHA1 Message Date
Themaister db3e1ae053 Updates for 0.6. 2011-06-21 22:37:48 +02:00
Themaister ec1401e657 Fix build error on Win32. 2011-06-21 02:33:59 +02:00
Themaister f85fd50fa1 Add --nodevice. 2011-06-19 11:11:04 +02:00
Themaister c39b0ab09b Merge branch 'master' of github.com:Themaister/SSNES
Conflicts:
	Makefile
	qb/config.libs.sh
2011-06-16 23:54:40 +02:00
Themaister 4b5ed0fa41 Do not force -lossaudio. 2011-06-16 23:50:33 +02:00
Themaister 75d1155a23 Fix OSS build for BSD. 2011-06-16 23:20:12 +02:00
Themaister 753d2057bb Fix BSD build. Convert strcpy()/strcat() to strl* variants. 2011-06-16 22:47:37 +02:00
Themaister b06d814d9c Fix OSS build for BSD. 2011-06-16 21:20:59 +02:00
Themaister eb085a8068 Fix BSD build. Convert strcpy()/strcat() to strl* variants. 2011-06-16 20:59:48 +02:00
Themaister 45904ccd73 Fix issue when input_slot is 2. 2011-06-16 15:00:04 +02:00
Themaister 3b0606bbd6 Ifdef for AVIO. 2011-06-15 12:20:51 +02:00
Themaister 0f1a76f932 POSIX stuff for Quickbuild. 2011-06-15 04:34:28 +02:00
Themaister d3e0140f01 Holy crap, now that was painful. 2011-06-15 04:16:53 +02:00
Themaister ea7dded915 Fix up alignment issues. 2011-06-15 03:04:20 +02:00
Themaister f187f3db29 Small regex change. 2011-06-15 00:42:09 +02:00
Themaister 3099a79bac Should fix grep error. 2011-06-15 00:26:44 +02:00
Themaister 7cdea39c95 Can build with FFmpeg if desired :) 2011-06-14 23:59:00 +02:00
Themaister 95f6a5116a Just to be sure ... ;) 2011-06-14 22:59:47 +02:00
Themaister da94877155 Less magic numbers ... ;) 2011-06-14 22:44:54 +02:00
Themaister b1feed9d2b Allocate from heap. 2011-06-14 22:20:09 +02:00
Themaister a3816bffa1 Force rewind granularity to 1 when we're using movies. 2011-06-14 22:09:16 +02:00
Themaister 612270ec9b Aww yeah :3 2011-06-14 21:52:31 +02:00
Themaister 54bc0a8d96 Heyhey :D 2011-06-14 21:35:31 +02:00
Themaister f761811189 Move buffer to top-level as it's not audio only. 2011-06-14 19:58:12 +02:00
Themaister 0e964c5cc2 More generic naming for FIFO buffer. 2011-06-14 19:55:08 +02:00
Themaister c34a944fcf Fix deprecation, yay. :) 2011-06-14 18:52:02 +02:00
Themaister 3feade6b6e Update FFemu a bit. Convert to lossless HuffYUV/FLAC. 2011-06-14 18:45:30 +02:00
Themaister f79eafe449 Should perhaps continue if one joypad failed. 2011-06-12 10:42:42 +02:00
Themaister 111b159373 Should fix dinput. 2011-06-12 10:37:04 +02:00
Themaister d744167865 Small cleanup. 2011-06-11 22:46:51 +02:00
Themaister a2fea86c92 Fixes 2-5 player. (Damn o.O) 2011-06-11 22:25:55 +02:00
Themaister 5385806e74 Hrm ... 2011-06-11 22:02:05 +02:00
Themaister 99e85684db Input capturing semantic. 2011-06-11 20:02:17 +02:00
Themaister 45727bf9f5 Fix mask value. (32-bit). 2011-06-11 19:01:34 +02:00
Themaister 2a0abb0618 Unsigned mask. 2011-06-11 18:57:16 +02:00
Themaister 192e3303a1 Update dist and version. 2011-06-11 18:19:00 +02:00
Themaister b6051add6a SDL_image for Win32 2011-06-11 18:13:12 +02:00
Themaister 24d0cda5fd SDL_image seems to work :) 2011-06-11 18:05:52 +02:00
Themaister d0ba59f8b5 Start moving over to SDL_image rather than imlib2.
Imlib2 takes more space, and was a pure bitch to compile for Win32,
so I gave up ... ;)
2011-06-11 16:55:53 +02:00
Themaister 99a1784aaf Move window logic away from SDL input.
We weren't using the event loop anyways ...
2011-06-11 12:54:14 +02:00
Themaister b6ff98cf18 Should fix DInput crash. 2011-06-10 18:01:44 +02:00
Themaister 2dcccc7cda Fix some includes and stuff. 2011-06-10 17:55:40 +02:00
Themaister d03855c655 Add some sanity logging. 2011-06-10 17:47:16 +02:00
Themaister 69227bc1ca Alter design. Use DInput as piggyback only. 2011-06-10 17:34:04 +02:00
Themaister 851debf1e4 Start working on DInput. 2011-06-10 16:55:05 +02:00
Themaister 8c1db1c407 Fix shenanigans. 2011-06-09 23:17:10 +02:00
Themaister 576894e0e8 Build Python in Windows. 2011-06-09 13:56:29 +02:00
Themaister 631c3ca393 Fix link for Clang. 2011-06-08 19:52:03 +02:00
Themaister fd70a1c875 Make sure we only call semantics once per frame. 2011-06-08 17:59:29 +02:00
Themaister 30cb2e4c07 Slight cleanup. 2011-06-08 17:55:32 +02:00
Themaister 7d87cc2d24 Add input hook API to python. 2011-06-08 17:53:15 +02:00
Themaister 0342dcb169 Fix transition_prev. 2011-06-07 21:07:00 +02:00
Themaister f339560550 Put all scripts in <script> 2011-06-07 16:28:58 +02:00
Themaister 11b58d8c69 Clean up error handling a bit. 2011-06-07 15:58:30 +02:00
Themaister 3717f7442c Inline XML python. 2011-06-07 15:33:29 +02:00
Themaister e02d9f41e8 Transition count semantic. 2011-06-06 21:32:10 +02:00
Themaister f62120861a Show if python is compiled in or not. 2011-06-06 20:21:26 +02:00
Themaister 884e2881a0 Add script support to Cg. 2011-06-06 20:18:31 +02:00
Themaister a605628dff :v 2011-06-06 18:50:59 +02:00
Themaister 4d96fbba6a Python works :P 2011-06-06 18:50:36 +02:00
Themaister 9bfeb88e24 Start implementing Python support for game-aware shaders. 2011-06-06 17:44:05 +02:00
Themaister 712f463022 Disable audio sync properly in external driver. 2011-06-06 02:01:21 +02:00
Themaister 713e8624a0 Fixed typo. 2011-06-05 22:23:50 +02:00
Themaister 5c8a549125 Cleanups. 2011-06-05 02:21:35 +02:00
Themaister c3e7a35b62 Fixes case where backdrops are used in second or later pass. 2011-06-05 02:01:44 +02:00
37 changed files with 1881 additions and 540 deletions

No files matched your search

+31 -10
View File
@@ -16,6 +16,13 @@ else
OSX := 0
endif
BSD_LOCAL_INC =
DYLIB_LIB = -ldl
ifneq ($(findstring BSD,$(shell uname -a)),)
BSD_LOCAL_INC = -I/usr/local/include
DYLIB_LIB = -lc
endif
ifeq ($(HAVE_SRC), 1)
LIBS += $(SRC_LIBS)
DEFINES += $(SRC_CFLAGS)
@@ -39,6 +46,13 @@ endif
ifeq ($(HAVE_OSS), 1)
OBJ += audio/oss.o
endif
ifeq ($(HAVE_OSS_BSD), 1)
OBJ += audio/oss.o
endif
ifeq ($(HAVE_OSS_LIB), 1)
LIBS += -lossaudio
endif
ifeq ($(HAVE_ALSA), 1)
OBJ += audio/alsa.o
LIBS += -lasound
@@ -67,8 +81,8 @@ ifeq ($(HAVE_PULSE), 1)
endif
ifeq ($(HAVE_SDL), 1)
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o
DEFINES += $(SDL_CFLAGS)
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o fifo_buffer.o
DEFINES += $(SDL_CFLAGS) $(BSD_LOCAL_INC)
LIBS += $(SDL_LIBS)
ifeq ($(OSX),1)
LIBS += -framework OpenGL
@@ -79,7 +93,7 @@ endif
ifeq ($(HAVE_XVIDEO), 1)
OBJ += gfx/xvideo.o input/x11_input.o
LIBS += -lXv -lX11
LIBS += -lX11 -lXext -lXv
endif
ifeq ($(HAVE_CG), 1)
@@ -95,7 +109,7 @@ endif
ifeq ($(HAVE_DYLIB), 1)
OBJ += gfx/ext.o audio/ext.o
LIBS += -ldl
LIBS += $(DYLIB_LIB)
endif
ifeq ($(HAVE_FREETYPE), 1)
@@ -104,19 +118,19 @@ ifeq ($(HAVE_FREETYPE), 1)
DEFINES += $(FREETYPE_CFLAGS)
endif
ifeq ($(HAVE_IMLIB), 1)
LIBS += $(IMLIB_LIBS)
DEFINES += $(IMLIB_CFLAGS)
ifeq ($(HAVE_SDL_IMAGE), 1)
LIBS += $(SDL_IMAGE_LIBS)
DEFINES += $(SDL_IMAGE_CFLAGS)
endif
ifeq ($(HAVE_FFMPEG), 1)
OBJ += record/ffemu.o
LIBS += $(AVCODEC_LIBS) $(AVCORE_LIBS) $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS)
DEFINES += $(AVCODEC_CFLAGS) $(AVCORE_CFLAGS) $(AVFORMAT_CFLAGS) $(AVUTIL_CFLAGS) $(SWSCALE_CFLAGS)
LIBS += $(AVCODEC_LIBS) $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS)
DEFINES += $(AVCODEC_CFLAGS) $(AVFORMAT_CFLAGS) $(AVUTIL_CFLAGS) $(SWSCALE_CFLAGS)
endif
ifeq ($(HAVE_DYNAMIC), 1)
LIBS += -ldl
LIBS += $(DYLIB_LIB)
else
LIBS += $(libsnes)
endif
@@ -125,6 +139,12 @@ ifeq ($(HAVE_STRL), 1)
DEFINES += -DHAVE_STRL
endif
ifeq ($(HAVE_PYTHON), 1)
DEFINES += $(PYTHON_CFLAGS) -Wno-unused-parameter
LIBS += $(PYTHON_LIBS)
OBJ += gfx/py_state/py_state.o
endif
ifneq ($(V),1)
Q := @
endif
@@ -177,6 +197,7 @@ clean:
rm -f audio/*.o
rm -f conf/*.o
rm -f gfx/*.o
rm -f gfx/py_state/*.o
rm -f record/*.o
rm -f input/*.o
rm -f tools/*.o
+28 -7
View File
@@ -6,8 +6,10 @@ JOBJ = conf/config_file.o tools/main-stub.o tools/ssnes-joyconfig.o
CC = gcc
CXX = g++
HAVE_DINPUT = 1
HAVE_SRC = 0
HAVE_SDL = 1
HAVE_SDL_IMAGE = 1
HAVE_XML = 1
HAVE_FREETYPE = 1
HAVE_XAUDIO = 1
@@ -15,8 +17,9 @@ HAVE_RSOUND = 1
HAVE_DYLIB = 1
HAVE_NETPLAY = 1
HAVE_FBO = 1
HAVE_CG = 0
HAVE_IMLIB = 0
HAVE_CG = 1
HAVE_PYTHON = 1
HAVE_FFMPEG = 0
libsnes ?= -lsnes
LIBS = -lm
@@ -35,11 +38,16 @@ else
endif
ifeq ($(HAVE_SDL), 1)
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o fifo_buffer.o
LIBS += $(SDL_LIBS) -lopengl32
DEFINES += $(SDL_CFLAGS) -DHAVE_SDL
endif
ifeq ($(HAVE_SDL_IMAGE), 1)
LIBS += -lSDL_image
DEFINES += -DHAVE_SDL_IMAGE
endif
ifeq ($(HAVE_CG), 1)
OBJ += gfx/shader_cg.o
LIBS += -lcg -lcgGL
@@ -93,11 +101,23 @@ ifeq ($(HAVE_DYLIB), 1)
OBJ += gfx/ext.o audio/ext.o
endif
ifeq ($(HAVE_IMLIB), 1)
LIBS += -lImlib2
DEFINES += -DHAVE_IMLIB
ifeq ($(HAVE_PYTHON), 1)
LIBS += -lpython32
DEFINES += -DHAVE_PYTHON -Ipython
OBJ += gfx/py_state/py_state.o
endif
ifeq ($(HAVE_DINPUT), 1)
LIBS += -ldinput8 -ldxguid -lole32
DEFINES += -DHAVE_DINPUT -Iffmpeg
OBJ += input/dinput.o
endif
ifeq ($(HAVE_FFMPEG), 1)
LIBS += -lavformat -lavcodec -lavutil -lswscale -lws2_32 -lzlib1
DEFINES += -DHAVE_FFMPEG
OBJ += record/ffemu.o
endif
ifneq ($(V),1)
Q := @
@@ -125,6 +145,7 @@ clean:
rm -f audio/*.o
rm -f conf/*.o
rm -f gfx/*.o
rm -f gfx/py_state/*.o
rm -f record/*.o
rm -f hqflt/*.o
rm -f input/*.o
@@ -134,7 +155,7 @@ clean:
rm -f tools/*.o
dist: all
zip -r ssnes-win32-0.5.1.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll freetype6.dll xaudio-c.dll rsound.dll pthreadGC2.dll README.win32.txt $(JTARGET)
zip -r ssnes-win32-0.6.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll freetype6.dll xaudio-c.dll rsound.dll pthreadGC2.dll cg.dll cgGL.dll libjpeg-8.dll libpng15-15.dll python32.dll SDL_image.dll $(JTARGET)
libs:
wget https://github.com/downloads/Themaister/SSNES/SSNES-win32-libs.zip --no-check-certificate
+15 -6
View File
@@ -6,6 +6,7 @@ JOBJ = conf/config_file.o tools/main-stub.o tools/ssnes-joyconfig.o
CC = gcc
CXX = g++
HAVE_DINPUT = 1
HAVE_SRC = 0
HAVE_SDL = 1
HAVE_XML = 1
@@ -16,7 +17,7 @@ HAVE_DYLIB = 1
HAVE_NETPLAY = 1
HAVE_FBO = 1
HAVE_CG = 0
HAVE_IMLIB = 0
HAVE_PYTHON = 0
libsnes ?= -lsnes
LIBS = -lm
@@ -35,7 +36,7 @@ else
endif
ifeq ($(HAVE_SDL), 1)
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o fifo_buffer.o
LIBS += $(SDL_LIBS) -lopengl32
DEFINES += $(SDL_CFLAGS) -DHAVE_SDL
endif
@@ -93,11 +94,19 @@ ifeq ($(HAVE_DYLIB), 1)
OBJ += gfx/ext.o audio/ext.o
endif
ifeq ($(HAVE_IMLIB), 1)
LIBS += -lImlib2
DEFINES += -DHAVE_IMLIB
ifeq ($(HAVE_PYTHON), 1)
LIBS += -lpython32
DEFINES += -DHAVE_PYTHON -Ipython
OBJ += gfx/py_state/py_state.o
endif
ifeq ($(HAVE_DINPUT), 1)
LIBS += -ldinput8 -ldxguid -lole32
DEFINES += -DHAVE_DINPUT
OBJ += input/dinput.o
endif
ifneq ($(V),1)
Q := @
endif
@@ -133,7 +142,7 @@ clean:
rm -f tools/*.o
dist: all
zip -r ssnes-win64-0.5.zip $(TARGET) ssnes.cfg snes.dll xaudio-c.dll README.win32.txt $(JTARGET)
zip -r ssnes-win64-0.6.zip $(TARGET) ssnes.cfg snes.dll xaudio-c.dll README.win32.txt $(JTARGET)
libs:
wget https://github.com/downloads/Themaister/SSNES/ssnes-win64-libs.zip --no-check-certificate
+4 -1
View File
@@ -83,7 +83,7 @@ static void* audio_ext_init(const char *device, unsigned rate, unsigned latency)
if (ext->driver->api_version != SSNES_AUDIO_API_VERSION)
{
SSNES_ERR("API mismatch in external video plugin. SSNES: %d, Plugin: %d ...\n", SSNES_AUDIO_API_VERSION, ext->driver->api_version);
SSNES_ERR("API mismatch in external audio plugin. SSNES: %d, Plugin: %d ...\n", SSNES_AUDIO_API_VERSION, ext->driver->api_version);
goto error;
}
@@ -103,6 +103,9 @@ static void* audio_ext_init(const char *device, unsigned rate, unsigned latency)
if (ext->driver->sample_rate)
g_settings.audio.out_rate = ext->driver->sample_rate(ext->handle);
if (!g_settings.audio.sync)
ext->driver->set_nonblock_state(ext->handle, SSNES_TRUE);
return ext;
error:
+26 -16
View File
@@ -15,28 +15,43 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "driver.h"
#include <stdlib.h>
#ifdef HAVE_OSS_BSD
#include <soundcard.h>
#else
#include <sys/soundcard.h>
#endif
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#ifdef HAVE_OSS_BSD
#define DEFAULT_OSS_DEV "/dev/audio"
#else
#define DEFAULT_OSS_DEV "/dev/dsp"
#endif
static void* __oss_init(const char* device, unsigned rate, unsigned latency)
{
int *fd = calloc(1, sizeof(int));
if ( fd == NULL )
if (fd == NULL)
return NULL;
const char *oss_device = "/dev/dsp";
const char *oss_device = DEFAULT_OSS_DEV;
if ( device != NULL )
if (device != NULL)
oss_device = device;
if ( (*fd = open(oss_device, O_WRONLY)) < 0 )
if ((*fd = open(oss_device, O_WRONLY)) < 0)
{
free(fd);
return NULL;
@@ -45,7 +60,7 @@ static void* __oss_init(const char* device, unsigned rate, unsigned latency)
int frags = (latency * rate * 4)/(1000 * (1 << 9));
int frag = (frags << 16) | 9;
if ( ioctl(*fd, SNDCTL_DSP_SETFRAGMENT, &frag) < 0 )
if (ioctl(*fd, SNDCTL_DSP_SETFRAGMENT, &frag) < 0)
{
close(*fd);
free(fd);
@@ -55,21 +70,21 @@ static void* __oss_init(const char* device, unsigned rate, unsigned latency)
int channels = 2;
int format = AFMT_S16_LE;
if ( ioctl(*fd, SNDCTL_DSP_CHANNELS, &channels) < 0 )
if (ioctl(*fd, SNDCTL_DSP_CHANNELS, &channels) < 0)
{
close(*fd);
free(fd);
return NULL;
}
if ( ioctl(*fd, SNDCTL_DSP_SETFMT, &format) < 0 )
if (ioctl(*fd, SNDCTL_DSP_SETFMT, &format) < 0)
{
close(*fd);
free(fd);
return NULL;
}
if ( ioctl(*fd, SNDCTL_DSP_SPEED, &rate) < 0 )
if (ioctl(*fd, SNDCTL_DSP_SPEED, &rate) < 0)
{
close(*fd);
free(fd);
@@ -83,13 +98,13 @@ static ssize_t __oss_write(void* data, const void* buf, size_t size)
{
int *fd = data;
if ( size == 0 )
if (size == 0)
return 0;
ssize_t ret;
if ( (ret = write(*fd, buf, size)) <= 0 )
if ((ret = write(*fd, buf, size)) <= 0)
{
if ( (fcntl(*fd, F_GETFL) & O_NONBLOCK) && errno == EAGAIN )
if ((fcntl(*fd, F_GETFL) & O_NONBLOCK) && errno == EAGAIN)
return 0;
return -1;
}
@@ -139,9 +154,4 @@ const audio_driver_t audio_oss = {
.free = __oss_free,
.ident = "oss"
};
+10 -10
View File
@@ -19,7 +19,7 @@
#include "driver.h"
#include <stdlib.h>
#include <rsound.h>
#include "buffer.h"
#include "fifo_buffer.h"
#include <stdbool.h>
#include "SDL.h"
@@ -29,7 +29,7 @@ typedef struct rsd
bool nonblock;
volatile bool has_error;
rsound_fifo_buffer_t *buffer;
fifo_buffer_t *buffer;
SDL_mutex *cond_lock;
SDL_cond *cond;
@@ -39,9 +39,9 @@ static ssize_t audio_cb(void *data, size_t bytes, void *userdata)
{
rsd_t *rsd = userdata;
size_t avail = rsnd_fifo_read_avail(rsd->buffer);
size_t avail = fifo_read_avail(rsd->buffer);
size_t write_size = bytes > avail ? avail : bytes;
rsnd_fifo_read(rsd->buffer, data, write_size);
fifo_read(rsd->buffer, data, write_size);
SDL_CondSignal(rsd->cond);
return write_size;
@@ -71,7 +71,7 @@ static void* __rsd_init(const char* device, unsigned rate, unsigned latency)
rsd->cond_lock = SDL_CreateMutex();
rsd->cond = SDL_CreateCond();
rsd->buffer = rsnd_fifo_new(1024 * 4);
rsd->buffer = fifo_new(1024 * 4);
int channels = 2;
int format = RSD_S16_NE;
@@ -108,9 +108,9 @@ static ssize_t __rsd_write(void* data, const void* buf, size_t size)
if (rsd->nonblock)
{
rsd_callback_lock(rsd->rd);
size_t avail = rsnd_fifo_write_avail(rsd->buffer);
size_t avail = fifo_write_avail(rsd->buffer);
size_t write_amt = avail > size ? size : avail;
rsnd_fifo_write(rsd->buffer, buf, write_amt);
fifo_write(rsd->buffer, buf, write_amt);
rsd_callback_unlock(rsd->rd);
return write_amt;
}
@@ -120,7 +120,7 @@ static ssize_t __rsd_write(void* data, const void* buf, size_t size)
while (written < size && !rsd->has_error)
{
rsd_callback_lock(rsd->rd);
size_t avail = rsnd_fifo_write_avail(rsd->buffer);
size_t avail = fifo_write_avail(rsd->buffer);
if (avail == 0)
{
@@ -135,7 +135,7 @@ static ssize_t __rsd_write(void* data, const void* buf, size_t size)
else
{
size_t write_amt = size - written > avail ? avail : size - written;
rsnd_fifo_write(rsd->buffer, (const char*)buf + written, write_amt);
fifo_write(rsd->buffer, (const char*)buf + written, write_amt);
rsd_callback_unlock(rsd->rd);
written += write_amt;
}
@@ -174,7 +174,7 @@ static void __rsd_free(void *data)
rsd_stop(rsd->rd);
rsd_free(rsd->rd);
rsnd_fifo_free(rsd->buffer);
fifo_free(rsd->buffer);
SDL_DestroyMutex(rsd->cond_lock);
SDL_DestroyCond(rsd->cond);
+11 -11
View File
@@ -27,7 +27,7 @@
#include "SDL_audio.h"
#include "SDL_thread.h"
#include "general.h"
#include "buffer.h"
#include "fifo_buffer.h"
typedef struct sdl_audio
{
@@ -35,16 +35,16 @@ typedef struct sdl_audio
SDL_mutex *lock;
SDL_cond *cond;
rsound_fifo_buffer_t *buffer;
fifo_buffer_t *buffer;
} sdl_audio_t;
static void sdl_audio_cb(void *data, Uint8 *stream, int len)
{
sdl_audio_t *sdl = data;
size_t avail = rsnd_fifo_read_avail(sdl->buffer);
size_t avail = fifo_read_avail(sdl->buffer);
size_t write_size = len > avail ? avail : len;
rsnd_fifo_read(sdl->buffer, stream, write_size);
fifo_read(sdl->buffer, stream, write_size);
SDL_CondSignal(sdl->cond);
// If underrun, fill rest with silence.
@@ -98,10 +98,10 @@ static void* sdl_audio_init(const char* device, unsigned rate, unsigned latency)
// Create a buffer twice as big as needed and prefill the buffer.
size_t bufsize = out.samples * 4 * sizeof(int16_t);
void *tmp = calloc(1, bufsize);
sdl->buffer = rsnd_fifo_new(bufsize);
sdl->buffer = fifo_new(bufsize);
if (tmp)
{
rsnd_fifo_write(sdl->buffer, tmp, bufsize);
fifo_write(sdl->buffer, tmp, bufsize);
free(tmp);
}
@@ -117,9 +117,9 @@ static ssize_t sdl_audio_write(void* data, const void* buf, size_t size)
if (sdl->nonblock)
{
SDL_LockAudio();
size_t avail = rsnd_fifo_write_avail(sdl->buffer);
size_t avail = fifo_write_avail(sdl->buffer);
size_t write_amt = avail > size ? size : avail;
rsnd_fifo_write(sdl->buffer, buf, write_amt);
fifo_write(sdl->buffer, buf, write_amt);
SDL_UnlockAudio();
ret = write_amt;
}
@@ -129,7 +129,7 @@ static ssize_t sdl_audio_write(void* data, const void* buf, size_t size)
while (written < size)
{
SDL_LockAudio();
size_t avail = rsnd_fifo_write_avail(sdl->buffer);
size_t avail = fifo_write_avail(sdl->buffer);
if (avail == 0)
{
@@ -141,7 +141,7 @@ static ssize_t sdl_audio_write(void* data, const void* buf, size_t size)
else
{
size_t write_amt = size - written > avail ? avail : size - written;
rsnd_fifo_write(sdl->buffer, (const char*)buf + written, write_amt);
fifo_write(sdl->buffer, (const char*)buf + written, write_amt);
SDL_UnlockAudio();
written += write_amt;
}
@@ -180,7 +180,7 @@ static void sdl_audio_free(void *data)
sdl_audio_t *sdl = data;
if (sdl)
{
rsnd_fifo_free(sdl->buffer);
fifo_free(sdl->buffer);
SDL_DestroyMutex(sdl->lock);
SDL_DestroyCond(sdl->cond);
}
+3
View File
@@ -72,6 +72,9 @@ static void* __xa_init(const char* device, unsigned rate, unsigned latency)
if (!init_lib())
return NULL;
if (latency < 8)
latency = 8; // Do not allow shenanigans.
xa_t *xa = calloc(1, sizeof(xa_t));
if (xa == NULL)
return NULL;
+12
View File
@@ -253,6 +253,18 @@ static const struct snes_keybind snes_keybinds_1[] = {
// Player 2-5
static const struct snes_keybind snes_keybinds_rest[] = {
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
{ -1 }
};
+10 -4
View File
@@ -20,7 +20,7 @@ static const bool _alsa_supp = true;
static const bool _alsa_supp = false;
#endif
#ifdef HAVE_OSS
#if defined(HAVE_OSS) || defined(HAVE_OSS_BSD)
static const bool _oss_supp = true;
#else
static const bool _oss_supp = false;
@@ -80,10 +80,10 @@ static const bool _xml_supp = true;
static const bool _xml_supp = false;
#endif
#ifdef HAVE_IMLIB
static const bool _imlib_supp = true;
#ifdef HAVE_SDL_IMAGE
static const bool _sdl_image_supp = true;
#else
static const bool _imlib_supp = false;
static const bool _sdl_image_supp = false;
#endif
#ifdef HAVE_FBO
@@ -128,4 +128,10 @@ static const bool _netplay_supp = true;
static const bool _netplay_supp = false;
#endif
#ifdef HAVE_PYTHON
static const bool _python_supp = true;
#else
static const bool _python_supp = false;
#endif
#endif
+3
View File
@@ -80,6 +80,9 @@ When using Sufami, save ram paths will be inferred from the Sufami BIOS path, no
\fB--mouse PORT, -m PORT\fR
Connects a Super Nintendo Mouse into port number PORT of the emulated SNES. Possible values for PORT are 1 and 2.
\fB--nodevice PORT, -N PORT\fR
Disconnects an input device from port number PORT of the emulated SNES. Possible values for PORT are 1 and 2. This may be needed for some odd games to run properly.
.TP
\fB--scope, -p\fR
Connects a Super Scope into port 2 of the SNES. It is controlled with your mouse.
+3 -3
View File
@@ -32,7 +32,7 @@ static const audio_driver_t *audio_drivers[] = {
#ifdef HAVE_ALSA
&audio_alsa,
#endif
#ifdef HAVE_OSS
#if defined(HAVE_OSS) || defined(HAVE_OSS_BSD)
&audio_oss,
#endif
#ifdef HAVE_RSOUND
@@ -132,7 +132,7 @@ static void find_input_driver(void)
SSNES_ERR("Couldn't find any input driver named \"%s\"\n", g_settings.input.driver);
fprintf(stderr, "Available input drivers are:\n");
for (int i = 0; i < sizeof(input_drivers) / sizeof(input_driver_t*); i++)
fprintf(stderr, "\t%s\n", video_drivers[i]->ident);
fprintf(stderr, "\t%s\n", input_drivers[i]->ident);
exit(1);
}
@@ -452,7 +452,7 @@ void init_video_input(void)
if ( driver.video_data == NULL )
{
SSNES_ERR("Cannot open video driver... Exiting ...\n");
SSNES_ERR("Cannot open video driver ... Exiting ...\n");
exit(1);
}
+11 -11
View File
@@ -13,9 +13,9 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "buffer.h"
#include "fifo_buffer.h"
struct rsound_fifo_buffer
struct fifo_buffer
{
char *buffer;
size_t bufsize;
@@ -23,9 +23,9 @@ struct rsound_fifo_buffer
size_t end;
};
rsound_fifo_buffer_t* rsnd_fifo_new(size_t size)
fifo_buffer_t* fifo_new(size_t size)
{
rsound_fifo_buffer_t *buf = calloc(1, sizeof(*buf));
fifo_buffer_t *buf = calloc(1, sizeof(*buf));
if (buf == NULL)
return NULL;
@@ -40,7 +40,7 @@ rsound_fifo_buffer_t* rsnd_fifo_new(size_t size)
return buf;
}
void rsnd_fifo_free(rsound_fifo_buffer_t* buffer)
void fifo_free(fifo_buffer_t* buffer)
{
assert(buffer);
assert(buffer->buffer);
@@ -49,7 +49,7 @@ void rsnd_fifo_free(rsound_fifo_buffer_t* buffer)
free(buffer);
}
size_t rsnd_fifo_read_avail(rsound_fifo_buffer_t* buffer)
size_t fifo_read_avail(fifo_buffer_t* buffer)
{
assert(buffer);
assert(buffer->buffer);
@@ -61,7 +61,7 @@ size_t rsnd_fifo_read_avail(rsound_fifo_buffer_t* buffer)
return end - first;
}
size_t rsnd_fifo_write_avail(rsound_fifo_buffer_t* buffer)
size_t fifo_write_avail(fifo_buffer_t* buffer)
{
assert(buffer);
assert(buffer->buffer);
@@ -74,12 +74,12 @@ size_t rsnd_fifo_write_avail(rsound_fifo_buffer_t* buffer)
return (buffer->bufsize - 1) - (end - first);
}
void rsnd_fifo_write(rsound_fifo_buffer_t* buffer, const void* in_buf, size_t size)
void fifo_write(fifo_buffer_t* buffer, const void* in_buf, size_t size)
{
assert(buffer);
assert(buffer->buffer);
assert(in_buf);
assert(rsnd_fifo_write_avail(buffer) >= size);
assert(fifo_write_avail(buffer) >= size);
size_t first_write = size;
size_t rest_write = 0;
@@ -97,12 +97,12 @@ void rsnd_fifo_write(rsound_fifo_buffer_t* buffer, const void* in_buf, size_t si
}
void rsnd_fifo_read(rsound_fifo_buffer_t* buffer, void* in_buf, size_t size)
void fifo_read(fifo_buffer_t* buffer, void* in_buf, size_t size)
{
assert(buffer);
assert(buffer->buffer);
assert(in_buf);
assert(rsnd_fifo_read_avail(buffer) >= size);
assert(fifo_read_avail(buffer) >= size);
size_t first_read = size;
size_t rest_read = 0;
+9 -9
View File
@@ -21,16 +21,16 @@
#include <stdio.h>
#include <string.h>
#ifndef RSD_FIFO_BUF_TYPEDEF
#define RSD_FIFO_BUF_TYPEDEF
typedef struct rsound_fifo_buffer rsound_fifo_buffer_t;
#ifndef FIFO_BUF_TYPEDEF
#define FIFO_BUF_TYPEDEF
typedef struct fifo_buffer fifo_buffer_t;
#endif
rsound_fifo_buffer_t* rsnd_fifo_new(size_t size);
void rsnd_fifo_write(rsound_fifo_buffer_t* buffer, const void* in_buf, size_t size);
void rsnd_fifo_read(rsound_fifo_buffer_t* buffer, void* in_buf, size_t size);
void rsnd_fifo_free(rsound_fifo_buffer_t* buffer);
size_t rsnd_fifo_read_avail(rsound_fifo_buffer_t* buffer);
size_t rsnd_fifo_write_avail(rsound_fifo_buffer_t* buffer);
fifo_buffer_t* fifo_new(size_t size);
void fifo_write(fifo_buffer_t* buffer, const void* in_buf, size_t size);
void fifo_read(fifo_buffer_t* buffer, void* in_buf, size_t size);
void fifo_free(fifo_buffer_t* buffer);
size_t fifo_read_avail(fifo_buffer_t* buffer);
size_t fifo_write_avail(fifo_buffer_t* buffer);
#endif
+4 -6
View File
@@ -572,15 +572,13 @@ char** dir_list_new(const char *dir, const char *ext)
if (!dir_list[cur_ptr])
goto error;
strcpy(dir_list[cur_ptr], dir);
strlcpy(dir_list[cur_ptr], dir, final_off);
strlcat(dir_list[cur_ptr], "/", final_off);
#ifdef _WIN32
dir_list[cur_ptr][path_len] = '\\';
strcpy(&dir_list[cur_ptr][path_len + 1], utf8_buf);
strlcat(dir_list[cur_ptr], utf8_buf, final_off);
#else
dir_list[cur_ptr][path_len] = '/';
strcpy(&dir_list[cur_ptr][path_len + 1], entry->d_name);
strlcat(dir_list[cur_ptr], entry->d_name, final_off);
#endif
dir_list[cur_ptr][final_off - 1] = '\0';
cur_ptr++;
if (cur_ptr + 1 == cur_size) // Need to reserve for NULL.
+1
View File
@@ -153,6 +153,7 @@ struct global
bool has_justifier;
bool has_justifiers;
bool has_multitap;
bool disconnect_device[2];
FILE *rom_file;
enum ssnes_game_type game_type;
+77 -50
View File
@@ -52,14 +52,17 @@
#include "fonts.h"
#endif
static const GLfloat vertexes[] = {
// Used for the last pass when rendering to the back buffer.
static const GLfloat vertexes_flipped[] = {
0, 0,
0, 1,
1, 1,
1, 0
};
static const GLfloat vertexes_flipped[] = {
// Used when rendering to an FBO.
// Texture coords have to be aligned with vertex coordinates.
static const GLfloat vertexes[] = {
0, 1,
0, 0,
1, 0,
@@ -73,12 +76,6 @@ static const GLfloat tex_coords[] = {
1, 1
};
static const GLfloat fbo_tex_coords[] = {
0, 0,
0, 1,
1, 1,
1, 0
};
#ifdef HAVE_FBO
#ifdef _WIN32
@@ -655,18 +652,64 @@ static void gl_render_msg(gl_t *gl, const char *msg)
// Go back to old rendering path.
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->tex_coords);
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes);
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes_flipped);
glBindTexture(GL_TEXTURE_2D, gl->texture);
glDisable(GL_BLEND);
#endif
}
static inline void set_lut_texture_coords(const GLfloat *coords)
{
#if defined(HAVE_XML) || defined(HAVE_CG)
// For texture images.
pglClientActiveTexture(GL_TEXTURE1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), coords);
pglClientActiveTexture(GL_TEXTURE0);
#else
(void)coords;
#endif
}
static inline void set_texture_coords(GLfloat *coords, GLfloat xamt, GLfloat yamt)
{
coords[1] = yamt;
coords[4] = xamt;
coords[6] = xamt;
coords[7] = yamt;
}
static void check_window(gl_t *gl)
{
SDL_Event event;
// Search for events we care about ...
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
gl->quitting = true;
break;
case SDL_VIDEORESIZE:
gl->should_resize = true;
gl->win_width = event.resize.w;
gl->win_height = event.resize.h;
break;
default:
break;
}
}
}
static bool gl_frame(void *data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
{
gl_t *gl = data;
check_window(gl);
gl_shader_use(1);
gl->frame_count++;
#ifdef HAVE_FBO
@@ -791,13 +834,10 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
0, 0, 0, gl->tex_w, gl->tex_h, gl->texture_type,
gl->texture_fmt, gl->empty_buf);
GLfloat x = (GLfloat)width / gl->tex_w;
GLfloat y = (GLfloat)height / gl->tex_h;
GLfloat xamt = (GLfloat)width / gl->tex_w;
GLfloat yamt = (GLfloat)height / gl->tex_h;
gl->tex_coords[1] = y;
gl->tex_coords[4] = x;
gl->tex_coords[6] = x;
gl->tex_coords[7] = y;
set_texture_coords(gl->tex_coords, xamt, yamt);
}
// Work around a certain issue a Cg where not using TEXUNIT0
@@ -812,7 +852,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
// Need to preserve the "flipped" state when in FBO too to have
// consistent texture coordinates.
if (gl->render_to_tex)
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes_flipped);
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes);
#endif
glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch / gl->base_size);
@@ -838,10 +878,6 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
#ifdef HAVE_FBO
if (gl->fbo_inited)
{
// Go back to non-flipped.
if (gl->render_to_tex)
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes);
// Render the rest of our passes.
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->fbo_tex_coords);
@@ -860,10 +896,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
GLfloat xamt = (GLfloat)prev_rect->img_width / prev_rect->width;
GLfloat yamt = (GLfloat)prev_rect->img_height / prev_rect->height;
gl->fbo_tex_coords[3] = yamt;
gl->fbo_tex_coords[4] = xamt;
gl->fbo_tex_coords[5] = yamt;
gl->fbo_tex_coords[6] = xamt;
set_texture_coords(gl->fbo_tex_coords, xamt, yamt);
fbo_info->tex = gl->fbo_texture[i - 1];
fbo_info->input_size[0] = prev_rect->img_width;
@@ -895,10 +928,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
GLfloat xamt = (GLfloat)prev_rect->img_width / prev_rect->width;
GLfloat yamt = (GLfloat)prev_rect->img_height / prev_rect->height;
gl->fbo_tex_coords[3] = yamt;
gl->fbo_tex_coords[4] = xamt;
gl->fbo_tex_coords[5] = yamt;
gl->fbo_tex_coords[6] = xamt;
set_texture_coords(gl->fbo_tex_coords, xamt, yamt);
// Render our FBO texture to back buffer.
pglBindFramebuffer(GL_FRAMEBUFFER, 0);
@@ -915,10 +945,9 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
&tex_info, fbo_tex_info, fbo_tex_info_cnt);
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes_flipped);
glDrawArrays(GL_QUADS, 0, 4);
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->tex_coords);
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes);
}
#endif
@@ -1007,7 +1036,7 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
SDL_ShowCursor(SDL_DISABLE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#if (defined(HAVE_XML) || defined(HAVE_CG)) && defined(_WIN32)
#if (defined(HAVE_XML) || defined(HAVE_CG)) && defined(_WIN32)
// Win32 GL lib doesn't have some functions needed for XML shaders.
// Need to load dynamically :(
if (!load_gl_proc())
@@ -1065,7 +1094,7 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
// Set up render to texture.
gl_init_fbo(gl, 256 * video->input_scale, 256 * video->input_scale);
gl->keep_aspect = video->force_aspect;
// Apparently need to set viewport for passes when we aren't using FBOs.
@@ -1109,18 +1138,12 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes);
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes_flipped);
memcpy(gl->tex_coords, tex_coords, sizeof(tex_coords));
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->tex_coords);
#ifdef HAVE_XML
// For texture images.
pglClientActiveTexture(GL_TEXTURE1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), tex_coords);
pglClientActiveTexture(GL_TEXTURE0);
#endif
set_lut_texture_coords(tex_coords);
gl->tex_w = 256 * video->input_scale;
gl->tex_h = 256 * video->input_scale;
@@ -1139,10 +1162,6 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
sdl_input_t *sdl_input = input_sdl.init();
if (sdl_input)
{
sdl_input->quitting = &gl->quitting;
sdl_input->should_resize = &gl->should_resize;
sdl_input->new_width = &gl->win_width;
sdl_input->new_height = &gl->win_height;
*input = &input_sdl;
*input_data = sdl_input;
}
@@ -1150,7 +1169,7 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
*input = NULL;
gl_init_font(gl, g_settings.video.font_path, g_settings.video.font_size);
if (!gl_check_error())
{
SDL_QuitSubSystem(SDL_INIT_VIDEO);
@@ -1177,7 +1196,13 @@ static bool gl_focus(void *data)
static bool gl_xml_shader(void *data, const char *path)
{
gl_t *gl = data;
gl_shader_deinit();
//if (!gl_check_error())
// SSNES_WARN("Error happened before deinit!\n");
//if (!gl_check_error())
// SSNES_WARN("Error happened in deinit!\n");
#ifdef HAVE_FBO
if (gl->fbo_inited)
@@ -1190,13 +1215,15 @@ static bool gl_xml_shader(void *data, const char *path)
gl->render_to_tex = false;
gl->fbo_pass = 0;
if (!gl_check_error())
SSNES_WARN("Failed to deinit FBO properly!\n");
glBindTexture(GL_TEXTURE_2D, gl->texture);
}
#endif
gl_shader_deinit();
//if (!gl_check_error())
// SSNES_WARN("Failed to deinit rendering path properly!\n");
if (!gl_glsl_init(path))
return false;
+55 -14
View File
@@ -27,35 +27,76 @@
#include <stddef.h>
#include "general.h"
#ifdef HAVE_IMLIB
#ifdef HAVE_SDL_IMAGE
#include <Imlib2.h>
#include "SDL_image.h"
bool texture_image_load(const char *path, struct texture_image *out_img)
{
Imlib_Image img;
img = imlib_load_image(path);
SDL_Surface *img = IMG_Load(path);
if (!img)
return false;
imlib_context_set_image(img);
out_img->width = imlib_image_get_width();
out_img->height = imlib_image_get_height();
out_img->width = img->w;
out_img->height = img->h;
size_t size = out_img->width * out_img->height * sizeof(uint32_t);
out_img->pixels = malloc(size);
if (!out_img->pixels)
{
imlib_free_image();
SDL_FreeSurface(img);
return false;
}
const uint32_t *read = imlib_image_get_data_for_reading_only();
// Convert ARGB -> RGBA.
for (unsigned i = 0; i < size / sizeof(uint32_t); i++)
out_img->pixels[i] = (read[i] >> 24) | (read[i] << 8);
const SDL_PixelFormat *fmt = img->format;
SSNES_LOG("SDL_image: %dx%d @ %d bpp\n", img->w, img->h, img->format->BitsPerPixel);
if (img->format->BitsPerPixel == 32)
{
for (unsigned y = 0; y < img->h; y++)
{
uint32_t *dst = out_img->pixels + y * img->w;
const uint32_t *src = (const uint32_t*)img->pixels + y * img->pitch / sizeof(uint32_t);
for (unsigned x = 0; x < img->w; x++)
{
uint32_t r = (src[x] & fmt->Rmask) >> fmt->Rshift;
uint32_t g = (src[x] & fmt->Gmask) >> fmt->Gshift;
uint32_t b = (src[x] & fmt->Bmask) >> fmt->Bshift;
uint32_t a = (src[x] & fmt->Amask) >> fmt->Ashift;
dst[x] = (r << 24) | (g << 16) | (b << 8) | a;
}
}
}
else if (img->format->BitsPerPixel == 24)
{
for (unsigned y = 0; y < img->h; y++)
{
uint32_t *dst = out_img->pixels + y * img->w;
const uint8_t *src = (const uint8_t*)img->pixels + y * img->pitch;
for (unsigned x = 0; x < img->w; x++)
{
// Correct?
uint32_t color = 0;
color |= src[3 * x + 0] << 0;
color |= src[3 * x + 1] << 8;
color |= src[3 * x + 2] << 16;
uint32_t r = (color & fmt->Rmask) >> fmt->Rshift;
uint32_t g = (color & fmt->Gmask) >> fmt->Gshift;
uint32_t b = (color & fmt->Bmask) >> fmt->Bshift;
dst[x] = (r << 24) | (g << 16) | (b << 8) | 0xff;
}
}
}
else
{
SSNES_ERR("8-bit and 16-bit image support are not implemented.\n");
SDL_FreeSurface(img);
return false;
}
SDL_FreeSurface(img);
imlib_free_image();
return true;
}
+288
View File
@@ -0,0 +1,288 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES 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 Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES 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 SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <Python.h>
#include <stdbool.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "dynamic.h"
#include "libsnes.hpp"
#include "py_state.h"
#include "general.h"
#include "strl.h"
#define PY_READ_FUNC_DECL(RAMTYPE) py_read_##RAMTYPE
#define PY_READ_FUNC(RAMTYPE) \
static PyObject* PY_READ_FUNC_DECL(RAMTYPE) (PyObject *self, PyObject *args) \
{ \
(void)self; \
\
const uint8_t *data = psnes_get_memory_data(SNES_MEMORY_##RAMTYPE); \
if (!data) \
{ \
Py_INCREF(Py_None); \
return Py_None; \
} \
unsigned max = psnes_get_memory_size(SNES_MEMORY_##RAMTYPE); \
\
unsigned addr; \
if (!PyArg_ParseTuple(args, "I", &addr)) \
return NULL; \
\
if (addr >= max || addr < 0) \
{ \
Py_INCREF(Py_None); \
return Py_None; \
} \
\
return PyLong_FromLong((long)data[addr]); \
}
PY_READ_FUNC(WRAM)
PY_READ_FUNC(VRAM)
PY_READ_FUNC(APURAM)
PY_READ_FUNC(CGRAM)
PY_READ_FUNC(OAM)
static PyObject* py_read_input(PyObject *self, PyObject *args)
{
(void)self;
if (!driver.input_data)
return PyBool_FromLong(0);
unsigned player;
unsigned key;
if (!PyArg_ParseTuple(args, "II", &player, &key))
return NULL;
if (player > MAX_PLAYERS || player < 1)
return NULL;
const struct snes_keybind *binds[MAX_PLAYERS];
for (int i = 0; i < MAX_PLAYERS; i++)
binds[i] = g_settings.input.binds[i];
int16_t res = driver.input->input_state(driver.input_data,
binds, player > 1,
player > 2 ? SNES_DEVICE_MULTITAP : SNES_DEVICE_JOYPAD,
player > 2 ? player - 2 : 0,
key);
return PyBool_FromLong((long)res);
}
static PyMethodDef SNESMethods[] = {
{ "read_wram", PY_READ_FUNC_DECL(WRAM), METH_VARARGS, "Read WRAM from SNES." },
{ "read_vram", PY_READ_FUNC_DECL(VRAM), METH_VARARGS, "Read VRAM from SNES." },
{ "read_apuram", PY_READ_FUNC_DECL(APURAM), METH_VARARGS, "Read APURAM from SNES." },
{ "read_cgram", PY_READ_FUNC_DECL(CGRAM), METH_VARARGS, "Read CGRAM from SNES." },
{ "read_oam", PY_READ_FUNC_DECL(OAM), METH_VARARGS, "Read OAM from SNES." },
{ "input", py_read_input, METH_VARARGS, "Read input state from SNES." },
{ NULL, NULL, 0, NULL }
};
static void py_set_attrs(PyObject *mod)
{
PyObject_SetAttrString(mod, "B", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_B));
PyObject_SetAttrString(mod, "Y", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_Y));
PyObject_SetAttrString(mod, "SELECT", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_SELECT));
PyObject_SetAttrString(mod, "START", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_START));
PyObject_SetAttrString(mod, "UP", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_UP));
PyObject_SetAttrString(mod, "DOWN", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_DOWN));
PyObject_SetAttrString(mod, "LEFT", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_LEFT));
PyObject_SetAttrString(mod, "RIGHT", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_RIGHT));
PyObject_SetAttrString(mod, "A", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_A));
PyObject_SetAttrString(mod, "X", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_X));
PyObject_SetAttrString(mod, "L", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_L));
PyObject_SetAttrString(mod, "R", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_R));
}
static PyModuleDef SNESModule = {
PyModuleDef_HEAD_INIT, "snes", NULL, -1, SNESMethods,
NULL, NULL, NULL, NULL
};
static PyObject* PyInit_SNES(void)
{
PyObject *mod = PyModule_Create(&SNESModule);
if (!mod)
return NULL;
py_set_attrs(mod);
return mod;
}
struct py_state
{
PyObject *main;
PyObject *dict;
PyObject *inst;
bool warned_ret;
bool warned_type;
};
static char *dupe_newline(const char *str)
{
if (!str)
return NULL;
unsigned size = strlen(str) + 2;
char *ret = malloc(size);
if (!ret)
return NULL;
strlcpy(ret, str, size);
ret[size - 2] = '\n';
ret[size - 1] = '\0';
return ret;
}
// Need to make sure that first-line indentation is 0. :(
static char* align_program(const char *program)
{
char *prog = strdup(program);
if (!prog)
return NULL;
size_t prog_size = strlen(program) + 1;
char *new_prog = calloc(1, prog_size);
if (!new_prog)
return NULL;
char *line = dupe_newline(strtok(prog, "\n"));
if (!line)
{
free(prog);
return NULL;
}
unsigned skip_chars = 0;
while (isblank(line[skip_chars]) && line[skip_chars])
skip_chars++;
while (line)
{
unsigned length = strlen(line);
unsigned skip_len = skip_chars > length ? length : skip_chars;
strlcat(new_prog, line + skip_len, prog_size);
free(line);
line = dupe_newline(strtok(NULL, "\n"));
}
free(prog);
return new_prog;
}
py_state_t *py_state_new(const char *script, bool is_file, const char *pyclass)
{
PyImport_AppendInittab("snes", &PyInit_SNES);
Py_Initialize();
py_state_t *handle = calloc(1, sizeof(*handle));
handle->main = PyImport_AddModule("__main__");
if (!handle->main)
goto error;
if (is_file)
{
FILE *file = fopen(script, "r");
if (!file)
goto error;
PyRun_SimpleFile(file, script);
fclose(file);
}
else
{
char *script_ = align_program(script);
if (script_)
{
PyRun_SimpleString(script_);
free(script_);
}
}
handle->dict = PyModule_GetDict(handle->main);
if (!handle->dict)
goto error;
PyObject *hook = PyDict_GetItemString(handle->dict, pyclass);
if (!hook)
goto error;
handle->inst = PyObject_CallFunction(hook, NULL);
if (!handle->inst)
goto error;
return handle;
error:
PyErr_Print();
py_state_free(handle);
return NULL;
}
void py_state_free(py_state_t *handle)
{
if (handle)
{
if (handle->main)
Py_DECREF(handle->main);
if (handle->dict)
Py_DECREF(handle->dict);
if (handle->inst)
Py_DECREF(handle->inst);
free(handle);
}
Py_Finalize();
}
int py_state_get(py_state_t *handle, const char *id,
unsigned frame_count)
{
PyObject *ret = PyObject_CallMethod(handle->inst, (char*)id, (char*)"I", frame_count);
if (!ret)
{
if (!handle->warned_ret)
SSNES_WARN("Didn't get return value from script! Bug?\n");
handle->warned_ret = true;
return 0;
}
int retval = 0;
if (PyLong_Check(ret))
retval = (int)PyLong_AsLong(ret);
else
{
if (!handle->warned_type)
SSNES_WARN("Didn't get long compatible value from script! Bug?\n");
handle->warned_type = true;
}
Py_DECREF(ret);
return retval;
}
+32
View File
@@ -0,0 +1,32 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES 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 Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES 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 SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __SSNES_PY_STATE_H
#define __SSNES_PY_STATE_H
#include <stdint.h>
#include <stdbool.h>
typedef struct py_state py_state_t;
py_state_t *py_state_new(const char *program, bool is_file, const char *pyclass);
void py_state_free(py_state_t *handle);
int py_state_get(py_state_t *handle,
const char *id, unsigned frame_count);
#endif
+18 -1
View File
@@ -270,7 +270,6 @@ static void* sdl_gfx_init(const video_info_t *video, const input_driver_t **inpu
sdl_input_t *sdl_input = input_sdl.init();
if (sdl_input)
{
sdl_input->quitting = &vid->quitting;
*input = &input_sdl;
*input_data = sdl_input;
}
@@ -407,9 +406,27 @@ static void convert_32bit_32bit_shift(uint32_t *out, unsigned outpitch, const ui
}
}
static void check_window(sdl_video_t *vid)
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
vid->quitting = true;
break;
default:
break;
}
}
}
static bool sdl_gfx_frame(void *data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
{
sdl_video_t *vid = data;
check_window(vid);
if (SDL_MUSTLOCK(vid->buffer))
SDL_LockSurface(vid->buffer);
+87 -22
View File
@@ -227,8 +227,12 @@ void gl_cg_set_params(unsigned width, unsigned height,
// Set state parameters
if (snes_tracker)
{
struct snes_tracker_uniform info[MAX_VARIABLES];
unsigned cnt = snes_get_uniform(snes_tracker, info, MAX_VARIABLES, frame_count);
static struct snes_tracker_uniform info[MAX_VARIABLES];
static unsigned cnt = 0;
if (active_index == 1)
cnt = snes_get_uniform(snes_tracker, info, MAX_VARIABLES, frame_count);
for (unsigned i = 0; i < cnt; i++)
{
CGparameter param_v = cgGetNamedParameter(prg[active_index].vprg, info[i].id);
@@ -242,8 +246,9 @@ void gl_cg_set_params(unsigned width, unsigned height,
void gl_cg_deinit(void)
{
if (cg_active)
cgDestroyContext(cgCtx);
if (!cg_active)
return;
cg_active = false;
cg_shader_num = 0;
memset(prg, 0, sizeof(prg));
@@ -258,6 +263,8 @@ void gl_cg_deinit(void)
snes_tracker_free(snes_tracker);
snes_tracker = NULL;
}
cgDestroyContext(cgCtx);
}
static bool load_plain(const char *path)
@@ -374,7 +381,7 @@ error:
return false;
}
static bool load_imports(config_file_t *conf)
static bool load_imports(const char *dir_path, config_file_t *conf)
{
char *imports = NULL;
@@ -389,6 +396,7 @@ static bool load_imports(config_file_t *conf)
{
char semantic_buf[64];
char wram_buf[64];
char input_slot_buf[64];
char apuram_buf[64];
char oam_buf[64];
char cgram_buf[64];
@@ -397,6 +405,7 @@ static bool load_imports(config_file_t *conf)
print_buf(semantic_buf, "%s_semantic", id);
print_buf(wram_buf, "%s_wram", id);
print_buf(input_slot_buf, "%s_input_slot", id);
print_buf(apuram_buf, "%s_apuram", id);
print_buf(oam_buf, "%s_oam", id);
print_buf(cgram_buf, "%s_cgram", id);
@@ -414,38 +423,69 @@ static bool load_imports(config_file_t *conf)
}
enum snes_tracker_type tracker_type;
enum snes_ram_type ram_type;
enum snes_ram_type ram_type = SNES_MEMORY_WRAM;
if (strcmp(semantic, "capture") == 0)
tracker_type = SSNES_STATE_CAPTURE;
else if (strcmp(semantic, "transition") == 0)
tracker_type = SSNES_STATE_TRANSITION;
else if (strcmp(semantic, "transition_count") == 0)
tracker_type = SSNES_STATE_TRANSITION_COUNT;
else if (strcmp(semantic, "capture_previous") == 0)
tracker_type = SSNES_STATE_CAPTURE_PREV;
else if (strcmp(semantic, "transition_previous") == 0)
tracker_type = SSNES_STATE_TRANSITION_PREV;
#ifdef HAVE_PYTHON
else if (strcmp(semantic, "python") == 0)
tracker_type = SSNES_STATE_PYTHON;
#endif
else
{
SSNES_ERR("Invalid semantic.\n");
goto error;
}
unsigned addr;
if (config_get_hex(conf, wram_buf, &addr))
ram_type = SSNES_STATE_WRAM;
else if (config_get_hex(conf, apuram_buf, &addr))
ram_type = SSNES_STATE_APURAM;
else if (config_get_hex(conf, oam_buf, &addr))
ram_type = SSNES_STATE_OAM;
else if (config_get_hex(conf, cgram_buf, &addr))
ram_type = SSNES_STATE_CGRAM;
else if (config_get_hex(conf, vram_buf, &addr))
ram_type = SSNES_STATE_VRAM;
else
unsigned addr = 0;
#ifdef HAVE_PYTHON
if (tracker_type != SSNES_STATE_PYTHON)
{
SSNES_ERR("No address assigned to semantic.\n");
goto error;
#endif
unsigned input_slot = 0;
if (config_get_hex(conf, input_slot_buf, &input_slot))
{
switch (input_slot)
{
case 1:
ram_type = SSNES_STATE_INPUT_SLOT1;
break;
case 2:
ram_type = SSNES_STATE_INPUT_SLOT2;
break;
default:
SSNES_ERR("Invalid input slot for import.\n");
goto error;
}
}
else if (config_get_hex(conf, wram_buf, &addr))
ram_type = SSNES_STATE_WRAM;
else if (config_get_hex(conf, apuram_buf, &addr))
ram_type = SSNES_STATE_APURAM;
else if (config_get_hex(conf, oam_buf, &addr))
ram_type = SSNES_STATE_OAM;
else if (config_get_hex(conf, cgram_buf, &addr))
ram_type = SSNES_STATE_CGRAM;
else if (config_get_hex(conf, vram_buf, &addr))
ram_type = SSNES_STATE_VRAM;
else
{
SSNES_ERR("No address assigned to semantic.\n");
goto error;
}
#ifdef HAVE_PYTHON
}
#endif
int memtype = 0;
switch (ram_type)
@@ -465,8 +505,9 @@ static bool load_imports(config_file_t *conf)
case SSNES_STATE_CGRAM:
memtype = SNES_MEMORY_CGRAM;
break;
default:
break;
memtype = SNES_MEMORY_WRAM;
}
if (addr >= psnes_get_memory_size(memtype))
@@ -501,10 +542,34 @@ static bool load_imports(config_file_t *conf)
.info_elem = info_cnt
};
#ifdef HAVE_PYTHON
char script_path[128];
char *script = NULL;
char *script_class = NULL;
if (config_get_string(conf, "import_script", &script))
{
strlcpy(script_path, dir_path, sizeof(script_path));
strlcat(script_path, script, sizeof(script_path));
tracker_info.script = script_path;
}
if (config_get_string(conf, "import_script_class", &script_class))
tracker_info.script_class = script_class;
tracker_info.script_is_file = true;
#endif
snes_tracker = snes_tracker_init(&tracker_info);
if (!snes_tracker)
SSNES_WARN("Failed to init SNES tracker.\n");
#ifdef HAVE_PYTHON
if (script)
free(script);
if (script_class)
free(script_class);
#endif
free(imports);
return true;
@@ -752,7 +817,7 @@ static bool load_preset(const char *path)
goto error;
}
if (!load_imports(conf))
if (!load_imports(dir_path, conf))
{
SSNES_ERR("Failed to load imports ...\n");
goto error;
+179 -40
View File
@@ -116,6 +116,9 @@ static char gl_teximage_uniforms[MAX_TEXTURES][64];
static snes_tracker_t *gl_snes_tracker = NULL;
static struct snes_tracker_uniform_info gl_tracker_info[MAX_VARIABLES];
static unsigned gl_tracker_info_cnt = 0;
static char gl_tracker_script[256];
static char gl_tracker_script_class[64];
static xmlChar *gl_script_program = NULL;
struct shader_program
{
@@ -378,6 +381,60 @@ error:
return false;
}
#ifdef HAVE_PYTHON
static bool get_script(const char *path, xmlNodePtr ptr)
{
if (*gl_tracker_script || gl_script_program)
{
SSNES_ERR("Script already imported.\n");
return false;
}
xmlChar *script_class = xmlGetProp(ptr, (const xmlChar*)"class");
if (script_class)
{
strlcpy(gl_tracker_script_class, (const char*)script_class, sizeof(gl_tracker_script_class));
xmlFree(script_class);
}
xmlChar *language = xmlGetProp(ptr, (const xmlChar*)"language");
if (!language || strcmp((const char*)language, "python") != 0)
{
SSNES_ERR("Script language is not Python!\n");
if (language)
xmlFree(language);
return false;
}
if (language)
xmlFree(language);
xmlChar *src = xmlGetProp(ptr, (const xmlChar*)"src");
if (src)
{
strlcpy(gl_tracker_script, path, sizeof(gl_tracker_script));
char *dir_ptr = strrchr(gl_tracker_script, '/');
if (!dir_ptr) dir_ptr = strrchr(gl_tracker_script, '\\');
if (dir_ptr) dir_ptr[1] = '\0';
strlcat(gl_tracker_script, (const char*)src, sizeof(gl_tracker_script));
xmlFree(src);
}
else
{
xmlChar *script = xmlNodeGetContent(ptr);
if (!script)
{
SSNES_ERR("No content in script!\n");
return false;
}
gl_script_program = script;
}
return true;
}
#endif
static bool get_import_value(xmlNodePtr ptr)
{
if (gl_tracker_info_cnt >= MAX_VARIABLES)
@@ -389,6 +446,7 @@ static bool get_import_value(xmlNodePtr ptr)
xmlChar *id = xmlGetProp(ptr, (const xmlChar*)"id");
xmlChar *semantic = xmlGetProp(ptr, (const xmlChar*)"semantic");
xmlChar *wram = xmlGetProp(ptr, (const xmlChar*)"wram");
xmlChar *input = xmlGetProp(ptr, (const xmlChar*)"input_slot");
xmlChar *apuram = xmlGetProp(ptr, (const xmlChar*)"apuram");
xmlChar *vram = xmlGetProp(ptr, (const xmlChar*)"vram");
xmlChar *oam = xmlGetProp(ptr, (const xmlChar*)"oam");
@@ -400,22 +458,67 @@ static bool get_import_value(xmlNodePtr ptr)
SSNES_ERR("No semantic or ID for import value!\n");
goto error;
}
enum snes_ram_type ram_type;
enum snes_tracker_type tracker_type;
uint32_t addr = 0;
if (wram) { addr = strtoul((const char*)wram, NULL, 16); ram_type = SSNES_STATE_WRAM; }
else if (apuram) { addr = strtoul((const char*)apuram, NULL, 16); ram_type = SSNES_STATE_APURAM; }
else if (vram) { addr = strtoul((const char*)vram, NULL, 16); ram_type = SSNES_STATE_VRAM; }
else if (oam) { addr = strtoul((const char*)oam, NULL, 16); ram_type = SSNES_STATE_OAM; }
else if (cgram) { addr = strtoul((const char*)cgram, NULL, 16); ram_type = SSNES_STATE_CGRAM; }
enum snes_tracker_type tracker_type;
if (strcmp((const char*)semantic, "capture") == 0)
tracker_type = SSNES_STATE_CAPTURE;
else if (strcmp((const char*)semantic, "capture_previous") == 0)
tracker_type = SSNES_STATE_CAPTURE_PREV;
else if (strcmp((const char*)semantic, "transition") == 0)
tracker_type = SSNES_STATE_TRANSITION;
else if (strcmp((const char*)semantic, "transition_count") == 0)
tracker_type = SSNES_STATE_TRANSITION_COUNT;
else if (strcmp((const char*)semantic, "transition_previous") == 0)
tracker_type = SSNES_STATE_TRANSITION_PREV;
#ifdef HAVE_PYTHON
else if (strcmp((const char*)semantic, "python") == 0)
tracker_type = SSNES_STATE_PYTHON;
#endif
else
{
SSNES_ERR("No RAM address specificed for import value.\n");
SSNES_ERR("Invalid semantic for import value.\n");
goto error;
}
enum snes_ram_type ram_type = SSNES_STATE_WRAM;
uint32_t addr = 0;
#ifdef HAVE_PYTHON
if (tracker_type != SSNES_STATE_PYTHON)
{
#endif
if (input)
{
unsigned slot = strtoul((const char*)input, NULL, 0);
switch (slot)
{
case 1:
ram_type = SSNES_STATE_INPUT_SLOT1;
break;
case 2:
ram_type = SSNES_STATE_INPUT_SLOT2;
break;
default:
SSNES_ERR("Invalid input slot for import!\n");
goto error;
}
}
else if (wram) { addr = strtoul((const char*)wram, NULL, 16); ram_type = SSNES_STATE_WRAM; }
else if (apuram) { addr = strtoul((const char*)apuram, NULL, 16); ram_type = SSNES_STATE_APURAM; }
else if (vram) { addr = strtoul((const char*)vram, NULL, 16); ram_type = SSNES_STATE_VRAM; }
else if (oam) { addr = strtoul((const char*)oam, NULL, 16); ram_type = SSNES_STATE_OAM; }
else if (cgram) { addr = strtoul((const char*)cgram, NULL, 16); ram_type = SSNES_STATE_CGRAM; }
else
{
SSNES_ERR("No RAM address specificed for import value.\n");
goto error;
}
#ifdef HAVE_PYTHON
}
#endif
int memtype = 0;
switch (ram_type)
{
@@ -434,8 +537,9 @@ static bool get_import_value(xmlNodePtr ptr)
case SSNES_STATE_CGRAM:
memtype = SNES_MEMORY_CGRAM;
break;
default:
break;
memtype = SNES_MEMORY_WRAM;
}
if (addr >= psnes_get_memory_size(memtype))
@@ -444,20 +548,6 @@ static bool get_import_value(xmlNodePtr ptr)
goto error;
}
if (strcmp((const char*)semantic, "capture") == 0)
tracker_type = SSNES_STATE_CAPTURE;
else if (strcmp((const char*)semantic, "capture_previous") == 0)
tracker_type = SSNES_STATE_CAPTURE_PREV;
else if (strcmp((const char*)semantic, "transition") == 0)
tracker_type = SSNES_STATE_TRANSITION;
else if (strcmp((const char*)semantic, "transition_previous") == 0)
tracker_type = SSNES_STATE_TRANSITION_PREV;
else
{
SSNES_ERR("Invalid semantic for import value.\n");
goto error;
}
unsigned mask_value = 0;
if (bitmask)
mask_value = strtoul((const char*)bitmask, NULL, 16);
@@ -472,6 +562,7 @@ static bool get_import_value(xmlNodePtr ptr)
if (id) xmlFree(id);
if (semantic) xmlFree(semantic);
if (wram) xmlFree(wram);
if (input) xmlFree(input);
if (apuram) xmlFree(apuram);
if (vram) xmlFree(vram);
if (oam) xmlFree(oam);
@@ -483,6 +574,7 @@ error:
if (id) xmlFree(id);
if (semantic) xmlFree(semantic);
if (wram) xmlFree(wram);
if (input) xmlFree(input);
if (apuram) xmlFree(apuram);
if (vram) xmlFree(vram);
if (oam) xmlFree(oam);
@@ -588,6 +680,16 @@ static unsigned get_xml_shaders(const char *path, struct shader_program *prog, s
goto error;
}
}
#ifdef HAVE_PYTHON
else if (strcmp((const char*)cur->name, "script") == 0)
{
if (!get_script(path, cur))
{
SSNES_ERR("Script is invalid.\n");
goto error;
}
}
#endif
}
if (num == 0)
@@ -648,13 +750,30 @@ static bool compile_shader(GLuint shader, const char *program)
return status == GL_TRUE;
}
static bool link_program(GLuint prog)
{
pglLinkProgram(prog);
GLint status;
pglGetProgramiv(prog, GL_LINK_STATUS, &status);
print_linker_log(prog);
if (status == GL_TRUE)
{
pglUseProgram(prog);
return true;
}
else
return false;
}
static bool compile_programs(GLuint *gl_prog, struct shader_program *progs, size_t num)
{
for (unsigned i = 0; i < num; i++)
{
gl_prog[i] = pglCreateProgram();
if (!gl_check_error() || gl_prog[i] == 0)
if (gl_prog[i] == 0)
{
SSNES_ERR("Failed to create GL program #%u.\n", i);
return false;
@@ -691,20 +810,16 @@ static bool compile_programs(GLuint *gl_prog, struct shader_program *progs, size
if (progs[i].vertex || progs[i].fragment)
{
SSNES_LOG("Linking GLSL program.\n");
pglLinkProgram(gl_prog[i]);
pglUseProgram(gl_prog[i]);
print_linker_log(gl_prog[i]);
if (!link_program(gl_prog[i]))
{
SSNES_ERR("Failed to link program #%u\n", i);
return false;
}
GLint location = pglGetUniformLocation(gl_prog[i], "rubyTexture");
pglUniform1i(location, 0);
pglUseProgram(0);
}
if (!gl_check_error())
{
SSNES_ERR("Failed to link program #%u\n", i);
return false;
}
}
return true;
@@ -800,8 +915,8 @@ bool gl_glsl_init(const char *path)
}
}
if (!gl_check_error())
return false;
//if (!gl_check_error())
// SSNES_WARN("Detected GL error!\n");
if (gl_tracker_info_cnt > 0)
{
@@ -812,8 +927,21 @@ bool gl_glsl_init(const char *path)
.apuram = psnes_get_memory_data(SNES_MEMORY_APURAM),
.oam = psnes_get_memory_data(SNES_MEMORY_OAM),
.info = gl_tracker_info,
.info_elem = gl_tracker_info_cnt
.info_elem = gl_tracker_info_cnt,
};
#ifdef HAVE_PYTHON
if (*gl_tracker_script)
info.script = gl_tracker_script;
else if (gl_script_program)
info.script = (const char*)gl_script_program;
else
info.script = NULL;
info.script_class = *gl_tracker_script_class ? gl_tracker_script_class : NULL;
info.script_is_file = *gl_tracker_script;
#endif
gl_snes_tracker = snes_tracker_init(&info);
if (!gl_snes_tracker)
SSNES_WARN("Failed to init SNES tracker!\n");
@@ -858,6 +986,13 @@ void gl_glsl_deinit(void)
gl_tracker_info_cnt = 0;
memset(gl_tracker_info, 0, sizeof(gl_tracker_info));
memset(gl_tracker_script, 0, sizeof(gl_tracker_script));
memset(gl_tracker_script_class, 0, sizeof(gl_tracker_script_class));
if (gl_script_program)
{
xmlFree(gl_script_program);
gl_script_program = NULL;
}
if (gl_snes_tracker)
{
snes_tracker_free(gl_snes_tracker);
@@ -976,8 +1111,12 @@ void gl_glsl_set_params(unsigned width, unsigned height,
if (gl_snes_tracker)
{
struct snes_tracker_uniform info[MAX_VARIABLES];
unsigned cnt = snes_get_uniform(gl_snes_tracker, info, MAX_VARIABLES, frame_count);
static struct snes_tracker_uniform info[MAX_VARIABLES];
static unsigned cnt = 0;
if (active_index == 1)
cnt = snes_get_uniform(gl_snes_tracker, info, MAX_VARIABLES, frame_count);
for (unsigned i = 0; i < cnt; i++)
{
location = pglGetUniformLocation(gl_program[active_index], info[i].id);
+113 -11
View File
@@ -19,26 +19,46 @@
#include <stdlib.h>
#include <assert.h>
#include "strl.h"
#include "general.h"
#include <libsnes.hpp>
#ifdef HAVE_PYTHON
#include "py_state/py_state.h"
#endif
struct snes_tracker_internal
{
char id[64];
bool is_input;
const uint16_t *input_ptr;
const uint8_t *ptr;
#ifdef HAVE_PYTHON
py_state_t *py;
#endif
uint32_t addr;
uint8_t mask;
unsigned mask;
enum snes_tracker_type type;
uint8_t prev[2];
uint32_t prev[2];
int frame_count;
uint8_t old_value;
int frame_count_prev;
uint32_t old_value;
int transition_count;
};
struct snes_tracker
{
struct snes_tracker_internal *info;
unsigned info_elem;
uint16_t input_state[2];
#ifdef HAVE_PYTHON
py_state_t *py;
#endif
};
snes_tracker_t* snes_tracker_init(const struct snes_tracker_info *info)
@@ -47,6 +67,19 @@ snes_tracker_t* snes_tracker_init(const struct snes_tracker_info *info)
if (!tracker)
return NULL;
#ifdef HAVE_PYTHON
if (info->script)
{
tracker->py = py_state_new(info->script, info->script_is_file, info->script_class ? info->script_class : "GameAware");
if (!tracker->py)
{
free(tracker);
SSNES_ERR("Failed to init Python script.\n");
return NULL;
}
}
#endif
tracker->info = calloc(info->info_elem, sizeof(struct snes_tracker_internal));
tracker->info_elem = info->info_elem;
@@ -55,7 +88,12 @@ snes_tracker_t* snes_tracker_init(const struct snes_tracker_info *info)
strlcpy(tracker->info[i].id, info->info[i].id, sizeof(tracker->info[i].id));
tracker->info[i].addr = info->info[i].addr;
tracker->info[i].type = info->info[i].type;
tracker->info[i].mask = (info->info[i].mask == 0) ? 0xff : info->info[i].mask;
tracker->info[i].mask = (info->info[i].mask == 0) ? 0xffffffffu : info->info[i].mask;
#ifdef HAVE_PYTHON
if (info->info[i].type == SSNES_STATE_PYTHON)
tracker->info[i].py = tracker->py;
#endif
assert(info->wram && info->vram && info->cgram &&
info->oam && info->apuram);
@@ -77,23 +115,33 @@ snes_tracker_t* snes_tracker_init(const struct snes_tracker_info *info)
case SSNES_STATE_VRAM:
tracker->info[i].ptr = info->vram;
break;
case SSNES_STATE_INPUT_SLOT1:
tracker->info[i].input_ptr = &tracker->input_state[0];
tracker->info[i].is_input = true;
break;
case SSNES_STATE_INPUT_SLOT2:
tracker->info[i].input_ptr = &tracker->input_state[1];
tracker->info[i].is_input = true;
break;
default:
tracker->info[i].ptr = NULL;
}
}
return tracker;
}
void snes_tracker_free(snes_tracker_t *tracker)
{
free(tracker->info);
#ifdef HAVE_PYTHON
py_state_free(tracker->py);
#endif
free(tracker);
}
#define fetch() (info->ptr[info->addr] & info->mask)
#define fetch() ((info->is_input ? *info->input_ptr : info->ptr[info->addr]) & info->mask)
static void update_element(
struct snes_tracker_uniform *uniform,
@@ -124,21 +172,33 @@ static void update_element(
info->frame_count = frame_count;
}
uniform->value = info->frame_count;
break;
case SSNES_STATE_TRANSITION_COUNT:
if (info->old_value != fetch())
{
info->old_value = fetch();
info->transition_count++;
}
uniform->value = info->transition_count;
break;
case SSNES_STATE_TRANSITION_PREV:
if (info->prev[0] != fetch())
if (info->old_value != fetch())
{
info->old_value = fetch();
info->prev[1] = info->prev[0];
info->prev[0] = frame_count;
info->frame_count_prev = info->frame_count;
info->frame_count = frame_count;
}
uniform->value = info->prev[1];
uniform->value = info->frame_count_prev;
break;
#ifdef HAVE_PYTHON
case SSNES_STATE_PYTHON:
uniform->value = py_state_get(info->py, info->id, frame_count);
break;
#endif
default:
break;
}
@@ -146,10 +206,52 @@ static void update_element(
#undef fetch
// Updates 16-bit input in same format as SNES itself.
static void update_input(snes_tracker_t *tracker)
{
if (!driver.input_data)
return;
static const unsigned buttons[] = {
SNES_DEVICE_ID_JOYPAD_R,
SNES_DEVICE_ID_JOYPAD_L,
SNES_DEVICE_ID_JOYPAD_X,
SNES_DEVICE_ID_JOYPAD_A,
SNES_DEVICE_ID_JOYPAD_RIGHT,
SNES_DEVICE_ID_JOYPAD_LEFT,
SNES_DEVICE_ID_JOYPAD_DOWN,
SNES_DEVICE_ID_JOYPAD_UP,
SNES_DEVICE_ID_JOYPAD_START,
SNES_DEVICE_ID_JOYPAD_SELECT,
SNES_DEVICE_ID_JOYPAD_Y,
SNES_DEVICE_ID_JOYPAD_B
};
static const struct snes_keybind *binds[MAX_PLAYERS] = {
g_settings.input.binds[0],
g_settings.input.binds[1],
g_settings.input.binds[2],
g_settings.input.binds[3],
g_settings.input.binds[4]
};
uint16_t state[2] = {0};
for (unsigned i = 4; i < 16; i++)
{
state[0] |= (driver.input->input_state(driver.input_data, binds, SNES_PORT_1, SNES_DEVICE_JOYPAD, 0, buttons[i - 4]) ? 1 : 0) << i;
state[1] |= (driver.input->input_state(driver.input_data, binds, SNES_PORT_2, SNES_DEVICE_JOYPAD, 0, buttons[i - 4]) ? 1 : 0) << i;
}
for (unsigned i = 0; i < 2; i++)
tracker->input_state[i] = state[i];
}
unsigned snes_get_uniform(snes_tracker_t *tracker, struct snes_tracker_uniform *uniforms, unsigned elem, unsigned frame_count)
{
unsigned elems = tracker->info_elem < elem ? tracker->info_elem : elem;
update_input(tracker);
for (unsigned i = 0; i < elems; i++)
update_element(&uniforms[i], &tracker->info[i], frame_count);
+21 -4
View File
@@ -19,13 +19,22 @@
#define __SSNES_SNES_STATE_H
#include <stdint.h>
#include <stdbool.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
enum snes_tracker_type
{
SSNES_STATE_CAPTURE,
SSNES_STATE_TRANSITION,
SSNES_STATE_CAPTURE_PREV,
SSNES_STATE_TRANSITION_PREV
SSNES_STATE_TRANSITION,
SSNES_STATE_TRANSITION_COUNT,
SSNES_STATE_TRANSITION_PREV,
#ifdef HAVE_PYTHON
SSNES_STATE_PYTHON
#endif
};
enum snes_ram_type
@@ -34,7 +43,9 @@ enum snes_ram_type
SSNES_STATE_APURAM,
SSNES_STATE_OAM,
SSNES_STATE_CGRAM,
SSNES_STATE_VRAM
SSNES_STATE_VRAM,
SSNES_STATE_INPUT_SLOT1,
SSNES_STATE_INPUT_SLOT2
};
struct snes_tracker_uniform_info
@@ -43,7 +54,7 @@ struct snes_tracker_uniform_info
uint32_t addr;
enum snes_tracker_type type;
enum snes_ram_type ram_type;
uint8_t mask;
unsigned mask;
};
struct snes_tracker_info
@@ -56,6 +67,12 @@ struct snes_tracker_info
const struct snes_tracker_uniform_info *info;
unsigned info_elem;
#ifdef HAVE_PYTHON
const char *script;
const char *script_class;
bool script_is_file;
#endif
};
struct snes_tracker_uniform
+249
View File
@@ -0,0 +1,249 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES 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 Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES 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 SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "SDL.h"
#include "SDL_syswm.h"
#include <stdbool.h>
#include "ssnes_dinput.h"
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
void sdl_dinput_free(sdl_dinput_t *di)
{
if (di)
{
for (unsigned i = 0; i < MAX_PLAYERS; i++)
{
if (di->joypad[i])
{
IDirectInputDevice8_Unacquire(di->joypad[i]);
IDirectInputDevice8_Release(di->joypad[i]);
}
}
if (di->ctx)
IDirectInput8_Release(di->ctx);
free(di);
}
}
static BOOL CALLBACK enum_axes_cb(const DIDEVICEOBJECTINSTANCE *inst, void *p)
{
IDirectInputDevice8 *joypad = p;
DIPROPRANGE range;
memset(&range, 0, sizeof(range));
range.diph.dwSize = sizeof(DIPROPRANGE);
range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
range.diph.dwHow = DIPH_BYID;
range.diph.dwObj = inst->dwType;
range.lMin = -32768;
range.lMax = 32767;
IDirectInputDevice8_SetProperty(joypad, DIPROP_RANGE, &range.diph);
return DIENUM_CONTINUE;
}
static BOOL CALLBACK enum_joypad_cb(const DIDEVICEINSTANCE *inst, void *p)
{
sdl_dinput_t *di = p;
unsigned active = 0;
unsigned n;
for (n = 0; n < MAX_PLAYERS; n++)
{
if (!di->joypad[n] && g_settings.input.joypad_map[n] == di->joypad_cnt)
break;
if (di->joypad[n])
active++;
}
if (active == MAX_PLAYERS) return DIENUM_STOP;
di->joypad_cnt++;
if (n == MAX_PLAYERS)
return DIENUM_CONTINUE;
if (FAILED(IDirectInput8_CreateDevice(di->ctx, &inst->guidInstance, &di->joypad[n], NULL)))
return DIENUM_CONTINUE;
IDirectInputDevice8_SetDataFormat(di->joypad[n], &c_dfDIJoystick2);
IDirectInputDevice8_SetCooperativeLevel(di->joypad[n], di->hWnd,
DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
IDirectInputDevice8_EnumObjects(di->joypad[n], enum_axes_cb,
di->joypad[n], DIDFT_ABSAXIS);
return DIENUM_CONTINUE;
}
sdl_dinput_t* sdl_dinput_init(void)
{
sdl_dinput_t *di = calloc(1, sizeof(*di));
if (!di)
return NULL;
CoInitialize(NULL);
SDL_SysWMinfo info;
if (!SDL_GetWMInfo(&info))
{
SSNES_ERR("Failed to get SysWM info.\n");
goto error;
}
di->hWnd = info.window;
if (FAILED(DirectInput8Create(
GetModuleHandle(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8,
(void**)&di->ctx, NULL)))
{
SSNES_ERR("Failed to init DirectInput.\n");
goto error;
}
SSNES_LOG("Enumerating DInput devices ...\n");
IDirectInput8_EnumDevices(di->ctx, DI8DEVCLASS_GAMECTRL, enum_joypad_cb, di, DIEDFL_ATTACHEDONLY);
SSNES_LOG("Done enumerating DInput devices ...\n");
return di;
error:
sdl_dinput_free(di);
return NULL;
}
static bool dinput_joykey_pressed(sdl_dinput_t *di, unsigned port_num, uint16_t joykey)
{
if (joykey == NO_BTN)
return false;
// Check hat.
if (GET_HAT_DIR(joykey))
{
unsigned hat = GET_HAT(joykey);
unsigned elems = sizeof(di->joy_state[0].rgdwPOV) / sizeof(di->joy_state[0].rgdwPOV[0]);
if (hat >= elems)
return false;
unsigned pov = di->joy_state[port_num].rgdwPOV[hat];
// Magic numbers I'm not sure where originate from.
if (pov < 36000)
{
switch (GET_HAT_DIR(joykey))
{
case HAT_UP_MASK:
return (pov >= 31500) || (pov <= 4500);
case HAT_RIGHT_MASK:
return (pov >= 4500) && (pov <= 13500);
case HAT_DOWN_MASK:
return (pov >= 13500) && (pov <= 22500);
case HAT_LEFT_MASK:
return (pov >= 22500) && (pov <= 31500);
}
}
return false;
}
else
{
unsigned elems = sizeof(di->joy_state[0].rgbButtons) / sizeof(di->joy_state[0].rgbButtons[0]);
if (joykey < elems)
return di->joy_state[port_num].rgbButtons[joykey];
}
return false;
}
static bool dinput_joyaxis_pressed(sdl_dinput_t *di, unsigned port_num, uint32_t joyaxis)
{
if (joyaxis == AXIS_NONE)
return false;
int min = -32678 * g_settings.input.axis_threshold;
int max = 32677 * g_settings.input.axis_threshold;
switch (AXIS_NEG_GET(joyaxis))
{
case 0:
return di->joy_state[port_num].lX <= min;
case 1:
return di->joy_state[port_num].lY <= min;
case 2:
return di->joy_state[port_num].lZ <= min;
case 3:
return di->joy_state[port_num].lRx <= min;
case 4:
return di->joy_state[port_num].lRy <= min;
case 5:
return di->joy_state[port_num].lRz <= min;
}
switch (AXIS_POS_GET(joyaxis))
{
case 0:
return di->joy_state[port_num].lX >= max;
case 1:
return di->joy_state[port_num].lY >= max;
case 2:
return di->joy_state[port_num].lZ >= max;
case 3:
return di->joy_state[port_num].lRx >= max;
case 4:
return di->joy_state[port_num].lRy >= max;
case 5:
return di->joy_state[port_num].lRz >= max;
}
return false;
}
bool sdl_dinput_pressed(sdl_dinput_t *di, unsigned port_num, const struct snes_keybind *key)
{
if (di->joypad[port_num] == NULL)
return false;
if (dinput_joykey_pressed(di, port_num, key->joykey))
return true;
if (dinput_joyaxis_pressed(di, port_num, key->joyaxis))
return true;
return false;
}
void sdl_dinput_poll(sdl_dinput_t *di)
{
memset(di->joy_state, 0, sizeof(di->joy_state));
for (unsigned i = 0; i < MAX_PLAYERS; i++)
{
if (di->joypad[i])
{
if (FAILED(IDirectInputDevice8_Poll(di->joypad[i])))
{
IDirectInputDevice8_Acquire(di->joypad[i]);
continue;
}
IDirectInputDevice8_GetDeviceState(di->joypad[i], sizeof(DIJOYSTATE2), &di->joy_state[i]);
}
}
}
+59 -58
View File
@@ -25,12 +25,22 @@
#include <libsnes.hpp>
#include "ssnes_sdl_input.h"
static void* sdl_input_init(void)
{
sdl_input_t *sdl = calloc(1, sizeof(*sdl));
if (!sdl)
return NULL;
#ifdef HAVE_DINPUT
sdl->di = sdl_dinput_init();
if (!sdl->di)
{
free(sdl);
SSNES_ERR("Failed to init SDL/DInput.\n");
return NULL;
}
#else
if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
return NULL;
@@ -60,9 +70,9 @@ static void* sdl_input_init(void)
sdl->num_hats[i] = SDL_JoystickNumHats(sdl->joysticks[i]);
}
}
#endif
sdl->use_keyboard = true;
return sdl;
}
@@ -78,8 +88,12 @@ static bool sdl_key_pressed(int key)
return keymap[key];
}
#ifndef HAVE_DINPUT
static bool sdl_joykey_pressed(sdl_input_t *sdl, int port_num, uint16_t joykey)
{
if (joykey == NO_BTN)
return false;
// Check hat.
if (GET_HAT_DIR(joykey))
{
@@ -120,37 +134,43 @@ static bool sdl_joykey_pressed(sdl_input_t *sdl, int port_num, uint16_t joykey)
static bool sdl_axis_pressed(sdl_input_t *sdl, int port_num, uint32_t joyaxis)
{
if (joyaxis != AXIS_NONE)
if (joyaxis == AXIS_NONE)
return false;
if (AXIS_NEG_GET(joyaxis) < sdl->num_axes[port_num])
{
if (AXIS_NEG_GET(joyaxis) < sdl->num_axes[port_num])
{
Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_NEG_GET(joyaxis));
float scaled = (float)val / 0x8000;
if (scaled < -g_settings.input.axis_threshold)
return true;
}
if (AXIS_POS_GET(joyaxis) < sdl->num_axes[port_num])
{
Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_POS_GET(joyaxis));
float scaled = (float)val / 0x8000;
if (scaled > g_settings.input.axis_threshold)
return true;
}
Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_NEG_GET(joyaxis));
float scaled = (float)val / 0x8000;
if (scaled < -g_settings.input.axis_threshold)
return true;
}
if (AXIS_POS_GET(joyaxis) < sdl->num_axes[port_num])
{
Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_POS_GET(joyaxis));
float scaled = (float)val / 0x8000;
if (scaled > g_settings.input.axis_threshold)
return true;
}
return false;
}
#endif
static bool sdl_is_pressed(sdl_input_t *sdl, int port_num, const struct snes_keybind *key)
static bool sdl_is_pressed(sdl_input_t *sdl, unsigned port_num, const struct snes_keybind *key)
{
if (sdl->use_keyboard && sdl_key_pressed(key->key))
return true;
#ifdef HAVE_DINPUT
return sdl_dinput_pressed(sdl->di, port_num, key);
#else
if (sdl->joysticks[port_num] == NULL)
return false;
if (sdl_joykey_pressed(sdl, port_num, key->joykey))
return true;
if (sdl_axis_pressed(sdl, port_num, key->joyaxis))
return true;
#endif
return false;
}
@@ -159,7 +179,7 @@ static bool sdl_bind_button_pressed(void *data, int key)
{
// Only let player 1 use special binds called from main loop.
const struct snes_keybind *binds = g_settings.input.binds[0];
for (int i = 0; binds[i].id != -1; i++)
for (unsigned i = 0; binds[i].id != -1; i++)
{
if (binds[i].id == key)
return sdl_is_pressed(data, 0, &binds[i]);
@@ -168,23 +188,21 @@ static bool sdl_bind_button_pressed(void *data, int key)
}
static int16_t sdl_joypad_device_state(sdl_input_t *sdl, const struct snes_keybind **binds,
int port_num, unsigned device, unsigned index, unsigned id)
unsigned port_num, int id)
{
const struct snes_keybind *snes_keybinds = binds[port_num];
for (int i = 0; snes_keybinds[i].id != -1; i++)
for (unsigned i = 0; snes_keybinds[i].id != -1; i++)
{
if (snes_keybinds[i].id == (int)id)
return sdl_is_pressed(sdl, port_num, &snes_keybinds[i]);
if (snes_keybinds[i].id == id)
return sdl_is_pressed(sdl, port_num, &snes_keybinds[i]) ? 1 : 0;
}
return false;
return 0;
}
static int16_t sdl_mouse_device_state(sdl_input_t *sdl, bool port, unsigned id)
static int16_t sdl_mouse_device_state(sdl_input_t *sdl, unsigned id)
{
// Might implement support for joypad mapping later.
(void)port;
switch (id)
{
case SNES_DEVICE_ID_MOUSE_LEFT:
@@ -248,11 +266,11 @@ static int16_t sdl_input_state(void *data, const struct snes_keybind **binds, bo
switch (device)
{
case SNES_DEVICE_JOYPAD:
return sdl_joypad_device_state(data, binds, (port == SNES_PORT_1) ? 0 : 1, device, index, id);
return sdl_joypad_device_state(data, binds, (port == SNES_PORT_1) ? 0 : 1, id);
case SNES_DEVICE_MULTITAP:
return sdl_joypad_device_state(data, binds, (port == SNES_PORT_2) ? 1 + index : 0, device, index, id);
return sdl_joypad_device_state(data, binds, (port == SNES_PORT_2) ? 1 + index : 0, id);
case SNES_DEVICE_MOUSE:
return sdl_mouse_device_state(data, port, id);
return sdl_mouse_device_state(data, id);
case SNES_DEVICE_SUPER_SCOPE:
return sdl_scope_device_state(data, id);
case SNES_DEVICE_JUSTIFIER:
@@ -273,14 +291,19 @@ static void sdl_input_free(void *data)
while (SDL_PollEvent(&event));
sdl_input_t *sdl = data;
#ifdef HAVE_DINPUT
sdl_dinput_free(sdl->di);
#else
for (int i = 0; i < MAX_PLAYERS; i++)
{
if (sdl->joysticks[i])
SDL_JoystickClose(sdl->joysticks[i]);
}
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
#endif
free(data);
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
}
@@ -298,37 +321,15 @@ static void sdl_poll_mouse(sdl_input_t *sdl)
static void sdl_input_poll(void *data)
{
SDL_PumpEvents();
SDL_Event event;
SDL_JoystickUpdate();
sdl_poll_mouse(data);
sdl_input_t *sdl = data;
// Search for events...
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
if (sdl->quitting)
{
*sdl->quitting = true;
return;
}
break;
case SDL_VIDEORESIZE:
if (sdl->should_resize)
{
*sdl->new_width = event.resize.w;
*sdl->new_height = event.resize.h;
*sdl->should_resize = true;
}
break;
#ifdef HAVE_DINPUT
sdl_dinput_poll(sdl->di);
#else
SDL_JoystickUpdate();
#endif
default:
break;
}
}
sdl_poll_mouse(sdl);
}
const input_driver_t input_sdl = {
+48
View File
@@ -0,0 +1,48 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES 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 Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES 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 SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __SSNES_DINPUT_H
#define __SSNES_DINPUT_H
#undef DIRECTINPUT_VERSION
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#include <stdbool.h>
#include "general.h"
// Piggyback joypad driver for SDL.
typedef struct sdl_dinput
{
HWND hWnd;
IDirectInput8 *ctx;
IDirectInputDevice8 *joypad[MAX_PLAYERS];
unsigned joypad_cnt;
DIJOYSTATE2 joy_state[MAX_PLAYERS];
} sdl_dinput_t;
sdl_dinput_t *sdl_dinput_init(void);
void sdl_dinput_free(sdl_dinput_t *di);
bool sdl_dinput_pressed(sdl_dinput_t *di, unsigned port_num,
const struct snes_keybind *key);
void sdl_dinput_poll(sdl_dinput_t *di);
#endif
+10 -5
View File
@@ -20,20 +20,25 @@
#include "SDL.h"
#include "general.h"
#ifdef HAVE_DINPUT
#include "ssnes_dinput.h"
#endif
typedef struct sdl_input
{
#ifdef HAVE_DINPUT
sdl_dinput_t *di;
#else
SDL_Joystick *joysticks[MAX_PLAYERS];
unsigned num_axes[MAX_PLAYERS];
unsigned num_buttons[MAX_PLAYERS];
unsigned num_hats[MAX_PLAYERS];
unsigned num_joysticks;
#endif
bool use_keyboard;
// A video driver could pre-init with the SDL driver and have it handle resizing events...
bool *quitting;
bool *should_resize;
unsigned *new_width;
unsigned *new_height;
int16_t mouse_x, mouse_y;
int16_t mouse_l, mouse_r, mouse_m;
} sdl_input_t;
+16 -7
View File
@@ -3,6 +3,12 @@
check_switch_c C99 -std=gnu99
check_critical C99 "Cannot find C99 compatible compiler."
if [ "$OS" = BSD ]; then
DYLIB=-lc
else
DYLIB=-ldl
fi
if [ "$HAVE_DYNAMIC" = "yes" ] && [ "$HAVE_CONFIGFILE" = "no" ]; then
echo "Cannot have dynamic loading of libsnes and no configfile support."
echo "Dynamic loading requires config file support."
@@ -10,16 +16,18 @@ if [ "$HAVE_DYNAMIC" = "yes" ] && [ "$HAVE_CONFIGFILE" = "no" ]; then
fi
if [ $HAVE_DYNAMIC != yes ]; then
check_lib_cxx SNES $LIBSNES snes_init -ldl
check_lib_cxx SNES $LIBSNES snes_init $DYLIB
check_critical SNES "Cannot find libsnes."
add_define_make libsnes $LIBSNES
fi
check_lib DYLIB -ldl dlopen
check_lib DYLIB $DYLIB dlopen
check_lib NETPLAY -lc socket
check_lib ALSA -lasound snd_pcm_open
check_header OSS sys/soundcard.h
check_header OSS_BSD soundcard.h
check_lib OSS_LIB -lossaudio
if [ "$OS" = "Darwin" ]; then
check_lib AL "-framework OpenAL" alcOpenDevice
@@ -43,29 +51,30 @@ check_critical SDL "Cannot find SDL library."
check_lib CG -lCg cgCreateContext
check_pkgconf XML libxml-2.0
check_pkgconf IMLIB imlib2
check_pkgconf SDL_IMAGE SDL_image
if [ $HAVE_FFMPEG != no ]; then
check_pkgconf AVCODEC libavcodec
check_pkgconf AVFORMAT libavformat
check_pkgconf AVCORE libavcore
check_pkgconf AVUTIL libavutil
check_pkgconf SWSCALE libswscale
( [ $HAVE_FFMPEG = auto ] && ( [ $HAVE_AVCODEC = no ] || [ $HAVE_AVFORMAT = no ] || [ $HAVE_AVCORE = no ] || [ $HAVE_AVUTIL = no ] || [ $HAVE_SWSCALE = no ] ) && HAVE_FFMPEG=no ) || HAVE_FFMPEG=yes
( [ $HAVE_FFMPEG = auto ] && ( [ $HAVE_AVCODEC = no ] || [ $HAVE_AVFORMAT = no ] || [ $HAVE_AVUTIL = no ] || [ $HAVE_SWSCALE = no ] ) && HAVE_FFMPEG=no ) || HAVE_FFMPEG=yes
fi
check_pkgconf SRC samplerate
check_lib DYNAMIC -ldl dlopen
check_lib DYNAMIC $DYLIB dlopen
check_pkgconf FREETYPE freetype2
check_lib XVIDEO -lXv XvShmCreateImage
check_lib STRL -lc strlcpy
check_pkgconf PYTHON python3
# Creates config.mk and config.h.
VARS="ALSA OSS AL RSOUND ROAR JACK PULSE SDL DYLIB CG XML IMLIB DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE SRC CONFIGFILE FREETYPE XVIDEO NETPLAY FBO STRL"
VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK PULSE SDL DYLIB CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE SRC CONFIGFILE FREETYPE XVIDEO NETPLAY FBO STRL PYTHON"
create_config_make config.mk $VARS
create_config_header config.h $VARS
+3 -2
View File
@@ -1,7 +1,7 @@
. qb/qb.params.sh
PACKAGE_NAME=ssnes
PACKAGE_VERSION=0.5.1
PACKAGE_VERSION=0.6
# Adds a command line opt to ./configure --help
# $1: Variable (HAVE_ALSA, HAVE_OSS, etc)
@@ -26,4 +26,5 @@ add_command_line_enable JACK "Enable JACK support" auto
add_command_line_enable PULSE "Enable PulseAudio support" auto
add_command_line_enable FREETYPE "Enable FreeType support" auto
add_command_line_enable XVIDEO "Enable XVideo support" auto
add_command_line_enable IMLIB "Enable imlib2 support" auto
add_command_line_enable SDL_IMAGE "Enable SDL_image support" auto
add_command_line_enable PYTHON "Enable Python 3 support for shaders" no
+14 -5
View File
@@ -41,9 +41,13 @@ check_lib()
eval tmpval=\$$tmpval
[ "$tmpval" = "no" ] && return 0
echo -n "Checking function $3 in $2 ... "
echo "void $3(void); int main(void) { $3(); return 0; }" > $TEMP_C
if [ -z "$3" ]; then
echo -n "Checking existence of $2 ..."
echo "int main(void) { return 0; }" > $TEMP_C
else
echo -n "Checking function $3 in $2 ... "
echo "void $3(void); int main(void) { $3(); return 0; }" > $TEMP_C
fi
eval HAVE_$1=no
answer=no
@@ -67,8 +71,13 @@ check_lib_cxx()
eval tmpval=\$$tmpval
[ "$tmpval" = "no" ] && return 0
echo -n "Checking function $3 in $2 ... "
echo "extern \"C\" { void $3(void); } int main() { $3(); }" > $TEMP_CXX
if [ -z "$3" ]; then
echo -n "Checking existence of $2 ..."
echo "int main(void) { return 0; }" > $TEMP_C
else
echo -n "Checking function $3 in $2 ... "
echo "extern \"C\" { void $3(void); } int main() { $3(); }" > $TEMP_CXX
fi
eval HAVE_$1=no
answer=no
+11 -7
View File
@@ -103,8 +103,9 @@ parse_input()
;;
--enable-*)
enable=`echo $1 | sed 's|^--enable-||'`
if [ -z "`echo $COMMAND_LINE_OPTS_ENABLE | grep -i $enable`" ]; then
tmp="$1"
enable="${tmp#--enable-}"
if [ -z "`echo $COMMAND_LINE_OPTS_ENABLE | grep -i -- $enable`" ]; then
print_help
exit 1
fi
@@ -112,8 +113,9 @@ parse_input()
;;
--disable-*)
disable=`echo $1 | sed 's|^--disable-||'`
if [ -z "`echo $COMMAND_LINE_OPTS_ENABLE | grep -i $disable`" ]; then
tmp="$1"
disable="${tmp#--disable-}"
if [ -z "`echo $COMMAND_LINE_OPTS_ENABLE | grep -i -- $disable`" ]; then
print_help
exit 1
fi
@@ -121,9 +123,11 @@ parse_input()
;;
--with-*)
arg="`echo $1 | sed 's|^--with-\S\S*=||'`"
with=`echo $1 | sed 's|^--with-\(\S\S*\)=.*$|\1|'`
if [ -z "`echo $COMMAND_LINE_OPTS_STRINGS | grep -i $with`" ]; then
tmp="$1"
arg="${tmp#--with-*=}"
tmp="${tmp#--with-}"
with="${tmp%%=*}"
if [ -z "`echo $COMMAND_LINE_OPTS_STRINGS | grep -i -- $with`" ]; then
print_help
exit 1
fi
+339 -155
View File
@@ -2,6 +2,7 @@
#include <libavutil/mathematics.h>
#include <libavutil/avutil.h>
#include <libavutil/avstring.h>
#include <libavutil/opt.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <stdint.h>
@@ -10,10 +11,11 @@
#include <stdlib.h>
#include <stdbool.h>
#include "ffemu.h"
#include "fifo_buffer.h"
#include "SDL_thread.h"
struct video_info
{
bool enabled;
AVCodecContext *codec;
AVFrame *conv_frame;
@@ -25,11 +27,11 @@ struct video_info
AVFormatContext *format;
struct SwsContext *sws_ctx;
} video;
struct audio_info
{
bool enabled;
AVCodecContext *codec;
int16_t *buffer;
@@ -39,6 +41,8 @@ struct audio_info
void *outbuf;
size_t outbuf_size;
int pix_fmt;
} audio;
struct muxer_info
@@ -55,158 +59,172 @@ struct ffemu
struct muxer_info muxer;
struct ffemu_params params;
SDL_cond *cond;
SDL_mutex *cond_lock;
SDL_mutex *lock;
fifo_buffer_t *audio_fifo;
fifo_buffer_t *video_fifo;
fifo_buffer_t *attr_fifo;
SDL_Thread *thread;
volatile bool alive;
volatile bool can_sleep;
};
// Currently hardcoded atm. :)
static int map_audio_codec(ffemu_audio_codec codec)
static bool init_audio(struct audio_info *audio, struct ffemu_params *param)
{
(void)codec;
return CODEC_ID_VORBIS;
}
// Currently hardcoded atm. :)
static int map_video_codec(ffemu_video_codec codec)
{
(void)codec;
return CODEC_ID_H264;
}
static int init_audio(struct audio_info *audio, struct ffemu_params *param)
{
AVCodec *codec = avcodec_find_encoder(map_audio_codec(param->acodec));
AVCodec *codec = avcodec_find_encoder(CODEC_ID_FLAC);
if (!codec)
return -1;
return false;
audio->codec = avcodec_alloc_context();
avcodec_get_context_defaults(audio->codec);
// Hardcode this atm.
audio->codec->global_quality = 100000;
audio->codec->flags |= CODEC_FLAG_QSCALE;
audio->codec->sample_rate = param->samplerate;
audio->codec->time_base = (AVRational) { 1, param->samplerate };
audio->codec->channels = param->channels;
audio->codec->sample_fmt = AV_SAMPLE_FMT_S16;
if (avcodec_open(audio->codec, codec) != 0)
return -1;
return false;
audio->buffer = av_malloc(audio->codec->frame_size * param->channels * sizeof(int16_t));
if (!audio->buffer)
return -1;
return false;
audio->outbuf_size = 50000;
audio->outbuf_size = 200000;
audio->outbuf = av_malloc(audio->outbuf_size);
if (!audio->outbuf)
return -1;
return false;
return 0;
return true;
}
// Hardcode.
static void init_x264_param(AVCodecContext *c)
static bool init_video(struct video_info *video, struct ffemu_params *param)
{
c->coder_type = 1; // coder = 1
c->flags|=CODEC_FLAG_LOOP_FILTER; // flags=+loop
c->me_cmp|= 1; // cmp=+chroma, where CHROMA = 1
c->partitions|=X264_PART_I8X8+X264_PART_I4X4+X264_PART_P8X8+X264_PART_B8X8; // partitions=+parti8x8+parti4x4+partp8x8+partb8x8
c->me_method=ME_HEX; // me_method=hex
c->me_subpel_quality = 7; // subq=7
c->me_range = 16; // me_range=16
c->gop_size = 250; // g=250
c->keyint_min = 25; // keyint_min=25
c->scenechange_threshold = 40; // sc_threshold=40
c->i_quant_factor = 0.71; // i_qfactor=0.71
c->b_frame_strategy = 1; // b_strategy=1
c->qcompress = 0.6; // qcomp=0.6
c->qmin = 10; // qmin=10
c->qmax = 51; // qmax=51
c->max_qdiff = 4; // qdiff=4
c->max_b_frames = 3; // bf=3
c->refs = 3; // refs=3
c->directpred = 1; // directpred=1
c->trellis = 1; // trellis=1
c->flags2|=CODEC_FLAG2_BPYRAMID+CODEC_FLAG2_MIXED_REFS+CODEC_FLAG2_WPRED+CODEC_FLAG2_8X8DCT+CODEC_FLAG2_FASTPSKIP; // flags2=+bpyramid+mixed_refs+wpred+dct8x8+fastpskip
c->weighted_p_pred = 2; // wpredp=2
// libx264-main.ffpreset preset
c->flags2|=CODEC_FLAG2_8X8DCT;
c->flags2^=CODEC_FLAG2_8X8DCT;
}
static int init_video(struct video_info *video, struct ffemu_params *param)
{
AVCodec *codec = avcodec_find_encoder(map_video_codec(param->vcodec));
AVCodec *codec = avcodec_find_encoder(CODEC_ID_FFV1);
if (!codec)
return -1;
return false;
video->codec = avcodec_alloc_context();
video->codec->width = param->out_width;
video->codec->height = param->out_height;
video->codec->time_base = (AVRational) {param->fps.den, param->fps.num};
video->codec->crf = 25;
// Might have to change this later to account for RGB codecs.
video->codec->pix_fmt = PIX_FMT_YUV420P;
///// Is this element in all recent ffmpeg versions?
video->codec->thread_count = 4;
/////
video->codec->pix_fmt = PIX_FMT_RGB32;
video->codec->sample_aspect_ratio = av_d2q(param->aspect_ratio * param->out_height / param->out_width, 255);
init_x264_param(video->codec);
if (avcodec_open(video->codec, codec) != 0)
return -1;
return false;
// Allocate a big buffer :p ffmpeg API doesn't seem to give us some clues how big this buffer should be.
video->outbuf_size = 1000000;
video->outbuf_size = 2000000;
video->outbuf = av_malloc(video->outbuf_size);
int size = avpicture_get_size(PIX_FMT_YUV420P, param->out_width, param->out_height);
// Just to make sure we can handle the biggest frames. Seemed to crash with just 256 * 224.
int size = avpicture_get_size(PIX_FMT_RGB32, param->fb_width, param->fb_height);
video->conv_frame_buf = av_malloc(size);
video->conv_frame = avcodec_alloc_frame();
avpicture_fill((AVPicture*)video->conv_frame, video->conv_frame_buf, PIX_FMT_YUV420P, param->out_width, param->out_height);
avpicture_fill((AVPicture*)video->conv_frame, video->conv_frame_buf, PIX_FMT_RGB32, param->fb_width, param->fb_height);
return 0;
return true;
}
static int init_muxer(ffemu_t *handle)
static bool init_muxer(ffemu_t *handle)
{
AVFormatContext *ctx = avformat_alloc_context();
av_strlcpy(ctx->filename, handle->params.filename, sizeof(ctx->filename));
ctx->oformat = av_guess_format(NULL, ctx->filename, NULL);
#ifdef AVIO_FLAG_WRITE
if (avio_open(&ctx->pb, ctx->filename, AVIO_FLAG_WRITE) < 0)
#else
if (url_fopen(&ctx->pb, ctx->filename, URL_WRONLY) < 0)
#endif
{
av_free(ctx);
return -1;
return false;
}
int stream_cnt = 0;
if (handle->video.enabled)
{
AVStream *stream = av_new_stream(ctx, stream_cnt++);
stream->codec = handle->video.codec;
AVStream *stream = av_new_stream(ctx, stream_cnt++);
stream->codec = handle->video.codec;
if (ctx->oformat->flags & AVFMT_GLOBALHEADER)
handle->video.codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
handle->muxer.vstream = stream;
handle->muxer.vstream->sample_aspect_ratio = handle->video.codec->sample_aspect_ratio;
}
if (ctx->oformat->flags & AVFMT_GLOBALHEADER)
handle->video.codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
handle->muxer.vstream = stream;
handle->muxer.vstream->sample_aspect_ratio = handle->video.codec->sample_aspect_ratio;
if (handle->audio.enabled)
{
AVStream *stream = av_new_stream(ctx, stream_cnt++);
stream->codec = handle->audio.codec;
stream = av_new_stream(ctx, stream_cnt++);
stream->codec = handle->audio.codec;
if (ctx->oformat->flags & AVFMT_GLOBALHEADER)
handle->audio.codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
handle->muxer.astream = stream;
}
if (ctx->oformat->flags & AVFMT_GLOBALHEADER)
handle->audio.codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
handle->muxer.astream = stream;
if (av_write_header(ctx) < 0)
return -1;
return false;
handle->muxer.ctx = ctx;
return 0;
return true;
}
#define MAX_FRAMES 64
static int SDLCALL ffemu_thread(void *data);
static bool init_thread(ffemu_t *handle)
{
assert(handle->lock = SDL_CreateMutex());
assert(handle->cond_lock = SDL_CreateMutex());
assert(handle->cond = SDL_CreateCond());
assert(handle->audio_fifo = fifo_new(32000 * sizeof(int16_t) * handle->params.channels * MAX_FRAMES / 60));
assert(handle->attr_fifo = fifo_new(sizeof(struct ffemu_video_data) * MAX_FRAMES));
assert(handle->video_fifo = fifo_new(handle->params.fb_width * handle->params.fb_height * sizeof(int16_t) * MAX_FRAMES));
handle->alive = true;
handle->can_sleep = true;
assert(handle->thread = SDL_CreateThread(ffemu_thread, handle));
return true;
}
static void deinit_thread(ffemu_t *handle)
{
if (handle->thread)
{
SDL_mutexP(handle->cond_lock);
handle->alive = false;
handle->can_sleep = false;
SDL_mutexV(handle->cond_lock);
SDL_CondSignal(handle->cond);
SDL_WaitThread(handle->thread, NULL);
SDL_DestroyMutex(handle->lock);
SDL_DestroyMutex(handle->cond_lock);
SDL_DestroyCond(handle->cond);
handle->thread = NULL;
}
}
static void deinit_thread_buf(ffemu_t *handle)
{
if (handle->audio_fifo)
{
fifo_free(handle->audio_fifo);
handle->audio_fifo = NULL;
}
if (handle->attr_fifo)
{
fifo_free(handle->attr_fifo);
handle->attr_fifo = NULL;
}
if (handle->video_fifo)
{
fifo_free(handle->video_fifo);
handle->video_fifo = NULL;
}
}
ffemu_t *ffemu_new(const struct ffemu_params *params)
@@ -219,20 +237,17 @@ ffemu_t *ffemu_new(const struct ffemu_params *params)
goto error;
handle->params = *params;
if (handle->params.vcodec != FFEMU_VIDEO_NONE)
handle->video.enabled = true;
if (handle->params.acodec != FFEMU_AUDIO_NONE)
handle->audio.enabled = true;
if (handle->video.enabled)
if (init_video(&handle->video, &handle->params) < 0)
goto error;
if (!init_video(&handle->video, &handle->params))
goto error;
if (handle->audio.enabled)
if (init_audio(&handle->audio, &handle->params) < 0)
goto error;
if (!init_audio(&handle->audio, &handle->params))
goto error;
if (init_muxer(handle) < 0)
if (!init_muxer(handle))
goto error;
if (!init_thread(handle))
goto error;
return handle;
@@ -246,6 +261,9 @@ void ffemu_free(ffemu_t *handle)
{
if (handle)
{
deinit_thread(handle);
deinit_thread_buf(handle);
if (handle->audio.codec)
{
avcodec_close(handle->audio.codec);
@@ -267,25 +285,93 @@ void ffemu_free(ffemu_t *handle)
if (handle->video.conv_frame_buf)
av_free(handle->video.conv_frame_buf);
if (handle->video.sws_ctx)
sws_freeContext(handle->video.sws_ctx);
free(handle);
}
}
// Need to make this thread based, but hey.
int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
{
if (!handle->video.enabled)
return -1;
for (;;)
{
SDL_mutexP(handle->lock);
unsigned avail = fifo_write_avail(handle->attr_fifo);
SDL_mutexV(handle->lock);
// This is deprecated, can't find a replacement... :(
// Hardcode pixel format for now (SNES)
struct SwsContext *conv_ctx = sws_getContext(data->width, data->height, PIX_FMT_RGB555LE,
handle->params.out_width, handle->params.out_height, PIX_FMT_YUV420P, handle->params.rescaler == FFEMU_RESCALER_LANCZOS ? SWS_LANCZOS : SWS_POINT,
if (!handle->alive)
return -1;
if (avail >= sizeof(*data))
break;
SDL_mutexP(handle->cond_lock);
if (handle->can_sleep)
{
handle->can_sleep = false;
SDL_CondWait(handle->cond, handle->cond_lock);
handle->can_sleep = true;
}
else
SDL_CondSignal(handle->cond);
SDL_mutexV(handle->cond_lock);
}
SDL_mutexP(handle->lock);
fifo_write(handle->attr_fifo, data, sizeof(*data));
fifo_write(handle->video_fifo, data->data, data->pitch * data->height);
SDL_mutexV(handle->lock);
SDL_CondSignal(handle->cond);
return 0;
}
int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
{
for (;;)
{
SDL_mutexP(handle->lock);
unsigned avail = fifo_write_avail(handle->audio_fifo);
SDL_mutexV(handle->lock);
if (!handle->alive)
return -1;
if (avail >= data->frames * handle->params.channels * sizeof(int16_t))
break;
SDL_mutexP(handle->cond_lock);
if (handle->can_sleep)
{
handle->can_sleep = false;
SDL_CondWait(handle->cond, handle->cond_lock);
handle->can_sleep = true;
}
else
SDL_CondSignal(handle->cond);
SDL_mutexV(handle->cond_lock);
}
SDL_mutexP(handle->lock);
fifo_write(handle->audio_fifo, data->data, data->frames * handle->params.channels * sizeof(int16_t));
SDL_mutexV(handle->lock);
SDL_CondSignal(handle->cond);
return 0;
}
static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_data *data)
{
handle->video.sws_ctx = sws_getCachedContext(handle->video.sws_ctx, data->width, data->height, PIX_FMT_RGB555LE,
handle->params.out_width, handle->params.out_height, PIX_FMT_RGB32, SWS_POINT,
NULL, NULL, NULL);
int linesize = data->pitch;
sws_scale(conv_ctx, (const uint8_t* const*)&data->data, &linesize, 0, handle->params.out_width, handle->video.conv_frame->data, handle->video.conv_frame->linesize);
sws_scale(handle->video.sws_ctx, (const uint8_t* const*)&data->data, &linesize, 0, handle->params.out_width, handle->video.conv_frame->data, handle->video.conv_frame->linesize);
handle->video.conv_frame->pts = handle->video.frame_cnt;
handle->video.conv_frame->display_picture_number = handle->video.frame_cnt;
@@ -295,8 +381,6 @@ int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
if (outsize < 0)
return -1;
sws_freeContext(conv_ctx);
AVPacket pkt;
av_init_packet(&pkt);
pkt.stream_index = handle->muxer.vstream->index;
@@ -319,16 +403,8 @@ int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
return 0;
}
int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
static int ffemu_push_audio_thread(ffemu_t *handle, const struct ffemu_audio_data *data)
{
if (!handle->audio.enabled)
return -1;
AVPacket pkt;
av_init_packet(&pkt);
pkt.stream_index = handle->muxer.astream->index;
pkt.data = handle->audio.outbuf;
size_t written_frames = 0;
while (written_frames < data->frames)
{
@@ -344,6 +420,10 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
if (handle->audio.frames_in_buffer == (size_t)handle->audio.codec->frame_size)
{
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = handle->audio.outbuf;
pkt.stream_index = handle->muxer.astream->index;
int out_size = avcodec_encode_audio(handle->audio.codec, handle->audio.outbuf, handle->audio.outbuf_size, handle->audio.buffer);
if (out_size < 0)
@@ -351,13 +431,9 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
pkt.size = out_size;
if (handle->audio.codec->coded_frame && handle->audio.codec->coded_frame->pts != AV_NOPTS_VALUE)
{
pkt.pts = av_rescale_q(handle->audio.codec->coded_frame->pts, handle->audio.codec->time_base, handle->muxer.astream->time_base);
}
else
{
pkt.pts = av_rescale_q(handle->audio.frame_cnt, handle->audio.codec->time_base, handle->muxer.astream->time_base);
}
pkt.flags |= AV_PKT_FLAG_KEY;
handle->audio.frames_in_buffer = 0;
@@ -375,38 +451,146 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
int ffemu_finalize(ffemu_t *handle)
{
// Push out delayed frames. (MPEG codecs)
if (handle->video.enabled)
deinit_thread(handle);
// Push out frames still stuck in queue.
uint16_t *video_buf = av_malloc(handle->params.fb_width * handle->params.fb_height * sizeof(uint16_t));
if (video_buf)
{
AVPacket pkt;
av_init_packet(&pkt);
pkt.stream_index = handle->muxer.vstream->index;
pkt.data = handle->video.outbuf;
int out_size = 0;
do
struct ffemu_video_data attr_buf;
while (fifo_read_avail(handle->attr_fifo) >= sizeof(attr_buf))
{
out_size = avcodec_encode_video(handle->video.codec, handle->video.outbuf, handle->video.outbuf_size, NULL);
pkt.pts = av_rescale_q(handle->video.codec->coded_frame->pts, handle->video.codec->time_base, handle->muxer.vstream->time_base);
if (handle->video.codec->coded_frame->key_frame)
pkt.flags |= AV_PKT_FLAG_KEY;
pkt.size = out_size;
if (pkt.size > 0)
{
int err = av_interleaved_write_frame(handle->muxer.ctx, &pkt);
if (err < 0)
break;
}
} while (out_size > 0);
fifo_read(handle->attr_fifo, &attr_buf, sizeof(attr_buf));
fifo_read(handle->video_fifo, video_buf, attr_buf.height * attr_buf.pitch);
attr_buf.data = video_buf;
ffemu_push_video_thread(handle, &attr_buf);
}
av_free(video_buf);
}
size_t audio_buf_size = 128 * handle->params.channels * sizeof(int16_t);
int16_t *audio_buf = av_malloc(audio_buf_size);
if (audio_buf)
{
while (fifo_read_avail(handle->audio_fifo) >= audio_buf_size)
{
fifo_read(handle->audio_fifo, audio_buf, sizeof(audio_buf_size));
struct ffemu_audio_data aud = {
.frames = 128,
.data = audio_buf
};
ffemu_push_audio_thread(handle, &aud);
}
av_free(audio_buf);
}
deinit_thread_buf(handle);
// Push out delayed frames. (MPEG codecs)
AVPacket pkt;
av_init_packet(&pkt);
pkt.stream_index = handle->muxer.vstream->index;
pkt.data = handle->video.outbuf;
int out_size = 0;
do
{
out_size = avcodec_encode_video(handle->video.codec, handle->video.outbuf, handle->video.outbuf_size, NULL);
pkt.pts = av_rescale_q(handle->video.codec->coded_frame->pts, handle->video.codec->time_base, handle->muxer.vstream->time_base);
if (handle->video.codec->coded_frame->key_frame)
pkt.flags |= AV_PKT_FLAG_KEY;
pkt.size = out_size;
if (pkt.size > 0)
{
int err = av_interleaved_write_frame(handle->muxer.ctx, &pkt);
if (err < 0)
break;
}
} while (out_size > 0);
// Write final data.
av_write_trailer(handle->muxer.ctx);
return 0;
}
static int SDLCALL ffemu_thread(void *data)
{
ffemu_t *ff = data;
uint16_t *video_buf = av_malloc(ff->params.fb_width * ff->params.fb_height * sizeof(uint16_t));
assert(video_buf);
size_t audio_buf_size = 128 * ff->params.channels * sizeof(int16_t);
int16_t *audio_buf = av_malloc(audio_buf_size);
assert(audio_buf);
struct ffemu_video_data attr_buf;
while (ff->alive)
{
bool avail_video = false;
bool avail_audio = false;
SDL_mutexP(ff->lock);
if (fifo_read_avail(ff->attr_fifo) >= sizeof(attr_buf))
avail_video = true;
if (fifo_read_avail(ff->audio_fifo) >= audio_buf_size)
avail_audio = true;
SDL_mutexV(ff->lock);
if (!avail_video && !avail_audio)
{
SDL_mutexP(ff->cond_lock);
if (ff->can_sleep)
{
ff->can_sleep = false;
SDL_CondWait(ff->cond, ff->cond_lock);
ff->can_sleep = true;
}
else
SDL_CondSignal(ff->cond);
SDL_mutexV(ff->cond_lock);
}
if (avail_video)
{
SDL_mutexP(ff->lock);
fifo_read(ff->attr_fifo, &attr_buf, sizeof(attr_buf));
fifo_read(ff->video_fifo, video_buf, attr_buf.height * attr_buf.pitch);
SDL_mutexV(ff->lock);
SDL_CondSignal(ff->cond);
attr_buf.data = video_buf;
ffemu_push_video_thread(ff, &attr_buf);
}
if (avail_audio)
{
SDL_mutexP(ff->lock);
fifo_read(ff->audio_fifo, audio_buf, audio_buf_size);
SDL_mutexV(ff->lock);
SDL_CondSignal(ff->cond);
struct ffemu_audio_data aud = {
.frames = 128,
.data = audio_buf
};
ffemu_push_audio_thread(ff, &aud);
}
}
av_free(video_buf);
av_free(audio_buf);
return 0;
}
+5 -17
View File
@@ -11,30 +11,23 @@ extern "C" {
// Available video codecs
typedef enum ffemu_video_codec
{
FFEMU_VIDEO_NONE,
FFEMU_VIDEO_H264,
FFEMU_VIDEO_MPEG4,
FFEMU_VIDEO_FFV1,
} ffemu_video_codec;
// Available audio codecs
typedef enum ffemu_audio_codec
{
FFEMU_AUDIO_NONE,
FFEMU_AUDIO_VORBIS,
FFEMU_AUDIO_MP3,
FFEMU_AUDIO_AAC,
} ffemu_audio_codec;
// Available pixel formats
typedef enum ffemu_pixel_format
{
FFEMU_FMT_XBGR1555,
FFEMU_FMT_RGB888,
} ffemu_pixel_format;
typedef enum ffemu_rescaler
{
FFEMU_RESCALER_LANCZOS,
FFEMU_RESCALER_POINT
} ffemu_rescaler;
@@ -53,14 +46,12 @@ struct ffemu_params
// Desired output resolution.
unsigned out_width;
unsigned out_height;
// Total size of framebuffer used in input.
unsigned fb_width;
unsigned fb_height;
float aspect_ratio;
// Rescaler for video.
ffemu_rescaler rescaler;
// Pixel format for video input.
ffemu_pixel_format format;
// FPS of video input.
struct ffemu_rational fps;
@@ -71,9 +62,6 @@ struct ffemu_params
// Define some video codec dependent option. (E.g. h264 profiles)
uint64_t video_opt;
// Audio codec. If not recording audio, select FFEMU_AUDIO_NONE.
ffemu_audio_codec acodec;
// Audio sample rate.
unsigned samplerate;
+8 -8
View File
@@ -194,8 +194,8 @@ static config_file_t *open_default_config_file(void)
if (appdata)
{
char conf_path[strlen(appdata) + strlen("/ssnes.cfg ")];
strcpy(conf_path, appdata);
strcat(conf_path, "/ssnes.cfg");
strlcpy(conf_path, appdata, sizeof(conf_path));
strlcat(conf_path, "/ssnes.cfg", sizeof(conf_path));
conf = config_file_new(conf_path);
}
}
@@ -204,8 +204,8 @@ static config_file_t *open_default_config_file(void)
if (home)
{
char conf_path[strlen(home) + strlen("/.ssnes.cfg ")];
strcpy(conf_path, home);
strcat(conf_path, "/.ssnes.cfg");
strlcpy(conf_path, home, sizeof(conf_path));
strlcat(conf_path, "/.ssnes.cfg", sizeof(conf_path));
conf = config_file_new(conf_path);
}
if (!conf)
@@ -219,15 +219,15 @@ static config_file_t *open_default_config_file(void)
if (xdg)
{
char conf_path[strlen(xdg) + strlen("/ssnes/ssnes.cfg ")];
strcpy(conf_path, xdg);
strcat(conf_path, "/ssnes/ssnes.cfg");
strlcpy(conf_path, xdg, sizeof(conf_path));
strlcat(conf_path, "/ssnes/ssnes.cfg", sizeof(conf_path));
conf = config_file_new(conf_path);
}
else if (home)
{
char conf_path[strlen(home) + strlen("/.ssnes.cfg ")];
strcpy(conf_path, home);
strcat(conf_path, "/.ssnes.cfg");
strlcpy(conf_path, home, sizeof(conf_path));
strlcat(conf_path, "/.ssnes.cfg", sizeof(conf_path));
conf = config_file_new(conf_path);
}
// Try this as a last chance...
+68 -40
View File
@@ -178,32 +178,35 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
static void audio_sample(uint16_t left, uint16_t right)
{
if ( !g_extern.audio_active )
if (!g_extern.audio_active)
return;
const float *output_data = NULL;
unsigned output_frames = 0;
#ifdef HAVE_FFMPEG
g_extern.audio_data.conv_outsamples[g_extern.audio_data.data_ptr] = left;
#endif
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(int16_t)left/0x8000;
#ifdef HAVE_FFMPEG
g_extern.audio_data.conv_outsamples[g_extern.audio_data.data_ptr] = right;
#endif
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(int16_t)right/0x8000;
if (g_extern.audio_data.data_ptr < g_extern.audio_data.chunk_size)
return;
#ifdef HAVE_FFMPEG
if (g_extern.recording)
{
static int16_t static_data[2];
static_data[0] = left;
static_data[1] = right;
struct ffemu_audio_data ffemu_data = {
.data = static_data,
.frames = 1
.data = g_extern.audio_data.conv_outsamples,
.frames = g_extern.audio_data.data_ptr / 2
};
ffemu_push_audio(g_extern.rec, &ffemu_data);
}
#endif
const float *output_data = NULL;
unsigned output_frames = 0;
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(int16_t)left/0x8000;
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(int16_t)right/0x8000;
if (g_extern.audio_data.data_ptr < g_extern.audio_data.chunk_size)
return;
ssnes_dsp_input_t dsp_input = {
.samples = g_extern.audio_data.data,
.frames = g_extern.audio_data.data_ptr / 2
@@ -295,9 +298,13 @@ static int16_t input_state(bool port, unsigned device, unsigned index, unsigned
}
}
const struct snes_keybind *binds[MAX_PLAYERS];
for (int i = 0; i < MAX_PLAYERS; i++)
binds[i] = g_settings.input.binds[i];
static const struct snes_keybind *binds[MAX_PLAYERS] = {
g_settings.input.binds[0],
g_settings.input.binds[1],
g_settings.input.binds[2],
g_settings.input.binds[3],
g_settings.input.binds[4]
};
int16_t res = driver.input->input_state(driver.input_data, binds, port, device, index, id);
if (g_extern.bsv_movie && !g_extern.bsv_movie_playback)
@@ -325,15 +332,15 @@ static void fill_pathname_noext(char *out_path, const char *in_path, const char
}
#ifdef _WIN32
#define SSNES_DEFAULT_CONF_PATH_STR "\n\tDefaults to ssnes.cfg in same directory as ssnes.exe"
#define SSNES_DEFAULT_CONF_PATH_STR "\n\tDefaults to ssnes.cfg in same directory as ssnes.exe."
#elif defined(__APPLE__)
#define SSNES_DEFAULT_CONF_PATH_STR " Defaults to $HOME/.ssnes.cfg"
#define SSNES_DEFAULT_CONF_PATH_STR " Defaults to $HOME/.ssnes.cfg."
#else
#define SSNES_DEFAULT_CONF_PATH_STR " Defaults to $XDG_CONFIG_HOME/ssnes/ssnes.cfg"
#define SSNES_DEFAULT_CONF_PATH_STR " Defaults to $XDG_CONFIG_HOME/ssnes/ssnes.cfg,\n\t\tor $HOME/.ssnes.cfg, if $XDG_CONFIG_HOME is not defined."
#endif
#ifdef _WIN32
#define PACKAGE_VERSION "0.5.1"
#define PACKAGE_VERSION "0.6"
#endif
#include "config.features.h"
@@ -355,7 +362,7 @@ static void print_features(void)
_PSUPP(dylib, "External", "External filter and driver support");
_PSUPP(cg, "Cg", "Cg pixel shaders");
_PSUPP(xml, "XML", "bSNES XML pixel shaders");
_PSUPP(imlib, "Imlib2", "Imlib2 image loading");
_PSUPP(sdl_image, "SDL_image", "SDL_image image loading");
_PSUPP(fbo, "FBO", "OpenGL render-to-texture (multi-pass shaders)");
_PSUPP(dynamic, "Dynamic", "Dynamic run-time loading of libsnes library");
_PSUPP(ffmpeg, "FFmpeg", "On-the-fly recording of gameplay with libavcodec");
@@ -363,6 +370,7 @@ static void print_features(void)
_PSUPP(configfile, "Config file", "Configuration file support");
_PSUPP(freetype, "FreeType", "TTF font rendering with FreeType");
_PSUPP(netplay, "Netplay", "Peer-to-peer netplay");
_PSUPP(python, "Python", "Script support in shaders");
}
#undef _PSUPP
@@ -372,8 +380,8 @@ static void print_help(void)
puts("ssnes: Simple Super Nintendo Emulator (libsnes) -- v" PACKAGE_VERSION " --");
puts("===================================================================");
puts("Usage: ssnes [rom file] [options...]");
puts("\t-h/--help: Show this help message");
puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin");
puts("\t-h/--help: Show this help message.");
puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin.");
puts("\t-S/--savestate: Path to use for save states. If not selected, *.state will be assumed.");
#ifdef HAVE_CONFIGFILE
puts("\t-c/--config: Path for config file." SSNES_DEFAULT_CONF_PATH_STR);
@@ -384,6 +392,7 @@ static void print_help(void)
puts("\t--sufamiA: Path to A slot of Sufami Turbo. Load Sufami base cart as regular rom.");
puts("\t--sufamiB: Path to B slot of Sufami Turbo.");
puts("\t-m/--mouse: Connect a virtual mouse into designated port of the SNES (1 or 2).");
puts("\t-N/--nodevice: Disconnects the controller device connected to the emulated SNES (1 or 2).");
puts("\t\tThis argument can be specified several times to connect more mice.");
puts("\t-p/--scope: Connect a virtual SuperScope into port 2 of the SNES.");
puts("\t-j/--justifier: Connect a virtual Konami Justifier into port 2 of the SNES.");
@@ -400,7 +409,7 @@ static void print_help(void)
#ifdef HAVE_FFMPEG
puts("\t-r/--record: Path to record video file. Settings for video/audio codecs are found in config file.");
#endif
puts("\t-v/--verbose: Verbose logging");
puts("\t-v/--verbose: Verbose logging.");
puts("\t-U/--ups: Specifies path for UPS patch that will be applied to ROM.");
puts("\t-D/--detach: Detach SSNES from the running console. Not relevant for all platforms.\n");
@@ -410,7 +419,7 @@ static void print_help(void)
static void set_basename(const char *path)
{
char tmp[strlen(path) + 1];
strcpy(tmp, path);
strlcpy(tmp, path, sizeof(tmp));
char *dst = strrchr(tmp, '.');
if (dst)
*dst = '\0';
@@ -439,6 +448,7 @@ static void parse_input(int argc, char *argv[])
{ "config", 0, NULL, 'c' },
#endif
{ "mouse", 1, NULL, 'm' },
{ "nodevice", 1, NULL, 'N' },
{ "scope", 0, NULL, 'p' },
{ "savestate", 1, NULL, 'S' },
{ "bsx", 1, NULL, 'b' },
@@ -472,7 +482,7 @@ static void parse_input(int argc, char *argv[])
#define CONFIG_FILE_ARG
#endif
char optstring[] = "hs:vS:m:p4jJg:b:B:Y:Z:P:HC:F:U:D" FFMPEG_RECORD_ARG CONFIG_FILE_ARG;
char optstring[] = "hs:vS:m:p4jJg:b:B:Y:Z:P:HC:F:U:DN:" FFMPEG_RECORD_ARG CONFIG_FILE_ARG;
for(;;)
{
val = 0;
@@ -550,6 +560,17 @@ static void parse_input(int argc, char *argv[])
g_extern.has_mouse[port - 1] = true;
break;
case 'N':
port = strtol(optarg, NULL, 0);
if (port < 1 || port > 2)
{
SSNES_ERR("Disconnected device from port 1 or 2.\n");
print_help();
exit(1);
}
g_extern.disconnect_device[port - 1] = true;
break;
case 'p':
g_extern.has_scope[1] = true;
break;
@@ -664,21 +685,26 @@ static void init_controllers(void)
}
else if (g_extern.has_multitap)
{
SSNES_LOG("Connecting multitap to port 2.\n");
SSNES_LOG("Connecting Multitap to port 2.\n");
psnes_set_controller_port_device(SNES_PORT_2, SNES_DEVICE_MULTITAP);
}
else
{
for (int i = 0; i < 2; i++)
for (unsigned i = 0; i < 2; i++)
{
if (g_extern.has_mouse[i])
if (g_extern.disconnect_device[i])
{
SSNES_LOG("Connecting mouse to port %d\n", i + 1);
SSNES_LOG("Disconnecting device from port %u.\n", i + 1);
psnes_set_controller_port_device(i, SNES_DEVICE_NONE);
}
else if (g_extern.has_mouse[i])
{
SSNES_LOG("Connecting mouse to port %u.\n", i + 1);
psnes_set_controller_port_device(i, SNES_DEVICE_MOUSE);
}
else if (g_extern.has_scope[i])
{
SSNES_LOG("Connecting scope to port %d\n", i + 1);
SSNES_LOG("Connecting scope to port %u.\n", i + 1);
psnes_set_controller_port_device(i, SNES_DEVICE_SUPER_SCOPE);
}
}
@@ -759,11 +785,10 @@ static void init_recording(void)
struct ffemu_rational ntsc_fps = {60000, 1000};
struct ffemu_rational pal_fps = {50000, 1000};
struct ffemu_params params = {
.vcodec = FFEMU_VIDEO_H264,
.acodec = FFEMU_AUDIO_VORBIS,
.rescaler = FFEMU_RESCALER_POINT,
.out_width = 512,
.out_height = 448,
.out_width = 256,
.out_height = 224,
.fb_width = 1024,
.fb_height = 512,
.channels = 2,
.samplerate = 32000,
.filename = g_extern.record_path,
@@ -777,6 +802,8 @@ static void init_recording(void)
SSNES_ERR("Failed to start FFmpeg recording.\n");
g_extern.recording = false;
}
else
g_settings.video.crop_overscan = true;
}
}
@@ -850,6 +877,7 @@ static void init_movie(void)
}
msg_queue_push(g_extern.msg_queue, "Starting movie playback!", 2, 180);
g_settings.rewind_granularity = 1;
SSNES_LOG("Starting movie playback!\n");
}
}
@@ -1155,8 +1183,7 @@ static void check_rewind(void)
if (g_extern.bsv_movie)
{
for (unsigned i = 0; i < (g_settings.rewind_granularity ? g_settings.rewind_granularity : 1); i++)
bsv_movie_frame_rewind(g_extern.bsv_movie);
bsv_movie_frame_rewind(g_extern.bsv_movie);
}
}
else
@@ -1190,6 +1217,7 @@ static void check_movie_record(void)
}
else
{
g_settings.rewind_granularity = 1;
char path[512];
if (g_extern.state_slot > 0)
snprintf(path, sizeof(path), "%s%d.bsv", g_extern.bsv_movie_path, g_extern.state_slot);