mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-09 03:29:43 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2476e3dee3 | ||
|
|
c05a5c9fdd | ||
|
|
503c51d633 | ||
|
|
46054de4f8 | ||
|
|
604eb9b973 | ||
|
|
0e72120bdc | ||
|
|
b8064f5d42 | ||
|
|
379b14a0f3 | ||
|
|
2a598c0b97 | ||
|
|
aa124b4c40 | ||
|
|
bb5e31df0d | ||
|
|
e49d216884 | ||
|
|
598d06fcb5 | ||
|
|
7f13ec88e6 | ||
|
|
c5c60c6f53 | ||
|
|
81b0e6d0a3 | ||
|
|
da97b29d0b | ||
|
|
69b7f92285 | ||
|
|
3a9dbbc8b7 | ||
|
|
bf5db10459 | ||
|
|
4fa27a5f0f | ||
|
|
3e7c412738 | ||
|
|
88c870dcfa | ||
|
|
66e1179503 | ||
|
|
4ecea5d0a4 | ||
|
|
57816edfd3 | ||
|
|
17cf0985c5 | ||
|
|
776b0a1e2d | ||
|
|
4a6392aefa | ||
|
|
29dd4727b8 | ||
|
|
face42a888 | ||
|
|
45fa6795c3 | ||
|
|
a9d339efb4 | ||
|
|
60841b01a5 | ||
|
|
ad67511483 | ||
|
|
104fb4fb16 | ||
|
|
816ef095ed | ||
|
|
48e8472f9c | ||
|
|
1755500b19 | ||
|
|
4bfc827eb2 | ||
|
|
ddfeacbd83 | ||
|
|
79353f861c | ||
|
|
f3f4efe304 | ||
|
|
51721b4cb6 | ||
|
|
477091ce8f | ||
|
|
2707b87767 | ||
|
|
b3d422b5cd | ||
|
|
d1dc65f202 | ||
|
|
7815940a97 | ||
|
|
4f841c208d | ||
|
|
b4f0d9b8f8 | ||
|
|
b96ab2982c | ||
|
|
825c1de7d1 | ||
|
|
6ada4d87d3 | ||
|
|
53abc92322 | ||
|
|
a816082ac3 | ||
|
|
47e63f1c6b | ||
|
|
cdf3d05c8e | ||
|
|
389e9ba0dc | ||
|
|
a6f7a2311f | ||
|
|
f19d99d523 | ||
|
|
e7082fd6ea | ||
|
|
4806d2fe88 | ||
|
|
f85ff7124b | ||
|
|
0f010cf3d2 | ||
|
|
95636d4f04 | ||
|
|
43905e410e | ||
|
|
d23a7e3ddf | ||
|
|
652f4c5856 | ||
|
|
90febc4ecf | ||
|
|
34b5994842 | ||
|
|
0c3f6ed66b | ||
|
|
f3e487557e | ||
|
|
ee12266a2b | ||
|
|
1981b14de9 | ||
|
|
65665f36cc | ||
|
|
a48dc55c41 | ||
|
|
202a458524 | ||
|
|
b4de704a09 | ||
|
|
fef5edfac8 | ||
|
|
a6e74775b1 | ||
|
|
ee66a28d05 | ||
|
|
5e28d82af2 | ||
|
|
c2a06bfa09 | ||
|
|
66510e21aa | ||
|
|
ec5e3355b9 | ||
|
|
0522a2d6a0 | ||
|
|
fad89f9d2d | ||
|
|
97f937ec6d | ||
|
|
9647006a2b | ||
|
|
418ba455f8 | ||
|
|
197d62860b | ||
|
|
402451626f | ||
|
|
3739cbfc70 | ||
|
|
235355ffc7 | ||
|
|
4856d0cac3 | ||
|
|
77a2723bb0 | ||
|
|
41cd6e21c3 | ||
|
|
1fc99c7123 | ||
|
|
fddcc3447c |
No files matched your search
@@ -86,20 +86,24 @@ ifeq ($(HAVE_COREAUDIO), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SDL), 1)
|
||||
OBJ += gfx/sdl.o gfx/gl.o gfx/sdlwrap.o input/sdl.o audio/sdl.o fifo_buffer.o
|
||||
OBJ += gfx/sdl.o gfx/sdlwrap.o input/sdl.o audio/sdl.o fifo_buffer.o
|
||||
DEFINES += $(SDL_CFLAGS) $(BSD_LOCAL_INC)
|
||||
LIBS += $(SDL_LIBS)
|
||||
|
||||
ifeq ($(HAVE_OPENGL), 1)
|
||||
OBJ += gfx/gl.o
|
||||
ifeq ($(OSX),1)
|
||||
LIBS += -framework OpenGL
|
||||
LIBS += -framework OpenGL
|
||||
else
|
||||
LIBS += -lGL
|
||||
LIBS += -lGL
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_XVIDEO), 1)
|
||||
OBJ += gfx/xvideo.o input/x11_input.o
|
||||
LIBS += $(XVIDEO_LIBS)
|
||||
DEFINES += $(XVIDEO_CFLAGS)
|
||||
LIBS += $(XVIDEO_LIBS) $(X11_LIBS) $(XEXT_LIBS)
|
||||
DEFINES += $(XVIDEO_CFLAGS) $(X11_CFLAGS) $(XEXT_CFLAGS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_CG), 1)
|
||||
@@ -108,9 +112,13 @@ ifeq ($(HAVE_CG), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_XML), 1)
|
||||
OBJ += gfx/shader_glsl.o sha256.o cheats.o
|
||||
OBJ += sha256.o cheats.o
|
||||
LIBS += $(XML_LIBS)
|
||||
DEFINES += $(XML_CFLAGS)
|
||||
|
||||
ifeq ($(HAVE_OPENGL), 1)
|
||||
OBJ += gfx/shader_glsl.o
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_XML), 1)
|
||||
|
||||
+12
-5
@@ -10,6 +10,7 @@ HAVE_DINPUT = 1
|
||||
HAVE_XAUDIO = 1
|
||||
HAVE_DSOUND = 1
|
||||
HAVE_SDL = 1
|
||||
HAVE_OPENGL = 1
|
||||
HAVE_DYLIB = 1
|
||||
HAVE_NETPLAY = 1
|
||||
DYNAMIC = 1
|
||||
@@ -29,15 +30,20 @@ libsnes ?= -lsnes
|
||||
|
||||
LIBS = -lm -lshlwapi
|
||||
DEFINES = -I. -DHAVE_CONFIGFILE
|
||||
LDFLAGS = -L. -static-libgcc -s
|
||||
LDCXXFLAGS = -static-libstdc++
|
||||
LDFLAGS = -L. -static-libgcc
|
||||
LDCXXFLAGS = -static-libstdc++ -s
|
||||
|
||||
ifeq ($(HAVE_SDL), 1)
|
||||
OBJ += gfx/sdl.o gfx/gl.o gfx/sdlwrap.o input/sdl.o audio/sdl.o fifo_buffer.o
|
||||
LIBS += -lSDL -lopengl32
|
||||
LIBS += -lSDL
|
||||
DEFINES += -ISDL -DHAVE_SDL
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_OPENGL), 1)
|
||||
DEFINES += -DHAVE_OPENGL
|
||||
LIBS += -lopengl32
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SDL_IMAGE), 1)
|
||||
LIBS += -lSDL_image
|
||||
DEFINES += -DHAVE_SDL_IMAGE
|
||||
@@ -122,6 +128,7 @@ ifeq ($(HAVE_FFMPEG), 1)
|
||||
DEFINES += -DHAVE_FFMPEG_AVCODEC_OPEN2
|
||||
DEFINES += -DHAVE_FFMPEG_AVIO_OPEN
|
||||
DEFINES += -DHAVE_FFMPEG_AVFORMAT_WRITE_HEADER
|
||||
DEFINES += -DHAVE_FFMPEG_AVFORMAT_NEW_STREAM
|
||||
DEFINES += -DHAVE_X264RGB
|
||||
OBJ += record/ffemu.o
|
||||
endif
|
||||
@@ -166,10 +173,10 @@ clean:
|
||||
rm -f tools/*.o
|
||||
|
||||
dist_x86: all
|
||||
zip -r ssnes-win32-0.9.zip $(TARGET) $(JTARGET) ssnes.cfg snes.dll SDL.dll
|
||||
zip -r ssnes-win32-0.9.2.zip $(TARGET) $(JTARGET) ssnes.cfg snes.dll SDL.dll
|
||||
|
||||
dist_x86_64: all
|
||||
zip -r ssnes-win64-0.9.zip $(TARGET) $(JTARGET) ssnes.cfg snes.dll SDL.dll
|
||||
zip -r ssnes-win64-0.9.2.zip $(TARGET) $(JTARGET) ssnes.cfg snes.dll SDL.dll
|
||||
|
||||
libs_x86:
|
||||
wget https://github.com/downloads/Themaister/SSNES/SSNES-win32-libs.zip --no-check-certificate
|
||||
|
||||
@@ -7,6 +7,10 @@ SSNES is a simple frontend for the libsnes library.
|
||||
libsnes is the emulator core of [bSNES](http://www.byuu.org), the most accurate SNES emulator to date, in library form. The libsnes API has been implemented by SNES9x as well, allowing multiple different cores to be used for an emulator that supports libsnes.
|
||||
This enables the possibility of custom front-ends for the emulator.
|
||||
|
||||
# Binaries
|
||||
|
||||
Latest Windows binaries are currently hosted on my [homepage](http://themaister.net/ssnes.html).
|
||||
|
||||
# Philosophy
|
||||
|
||||
SSNES attempts to be very small and lean, while still having all the useful core features expected from an emulator.
|
||||
|
||||
+14
-14
@@ -32,7 +32,7 @@ typedef struct alsa
|
||||
bool has_float;
|
||||
} alsa_t;
|
||||
|
||||
static bool __alsa_use_float(void *data)
|
||||
static bool alsa_use_float(void *data)
|
||||
{
|
||||
alsa_t *alsa = data;
|
||||
return alsa->has_float;
|
||||
@@ -49,7 +49,7 @@ static bool find_float_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void* __alsa_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *alsa_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
alsa_t *alsa = calloc(1, sizeof(alsa_t));
|
||||
if (!alsa)
|
||||
@@ -117,7 +117,7 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t __alsa_write(void *data, const void *buf, size_t size)
|
||||
static ssize_t alsa_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
alsa_t *alsa = data;
|
||||
|
||||
@@ -159,23 +159,23 @@ static ssize_t __alsa_write(void *data, const void *buf, size_t size)
|
||||
return snd_pcm_frames_to_bytes(alsa->pcm, size);
|
||||
}
|
||||
|
||||
static bool __alsa_stop(void *data)
|
||||
static bool alsa_stop(void *data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __alsa_set_nonblock_state(void *data, bool state)
|
||||
static void alsa_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
alsa_t *alsa = data;
|
||||
alsa->nonblock = state;
|
||||
}
|
||||
|
||||
static bool __alsa_start(void *data)
|
||||
static bool alsa_start(void *data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __alsa_free(void *data)
|
||||
static void alsa_free(void *data)
|
||||
{
|
||||
alsa_t *alsa = data;
|
||||
if (alsa)
|
||||
@@ -190,13 +190,13 @@ static void __alsa_free(void *data)
|
||||
}
|
||||
|
||||
const audio_driver_t audio_alsa = {
|
||||
.init = __alsa_init,
|
||||
.write = __alsa_write,
|
||||
.stop = __alsa_stop,
|
||||
.start = __alsa_start,
|
||||
.use_float = __alsa_use_float,
|
||||
.set_nonblock_state = __alsa_set_nonblock_state,
|
||||
.free = __alsa_free,
|
||||
.init = alsa_init,
|
||||
.write = alsa_write,
|
||||
.stop = alsa_stop,
|
||||
.start = alsa_start,
|
||||
.use_float = alsa_use_float,
|
||||
.set_nonblock_state = alsa_set_nonblock_state,
|
||||
.free = alsa_free,
|
||||
.ident = "alsa"
|
||||
};
|
||||
|
||||
+2
-2
@@ -95,7 +95,7 @@ static OSStatus audio_cb(void *userdata, AudioUnitRenderActionFlags *action_flag
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static void* coreaudio_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *coreaudio_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
(void)device;
|
||||
|
||||
@@ -210,7 +210,7 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t coreaudio_write(void* data, const void* buf_, size_t size)
|
||||
static ssize_t coreaudio_write(void *data, const void *buf_, size_t size)
|
||||
{
|
||||
coreaudio_t *dev = data;
|
||||
|
||||
|
||||
+1
-1
@@ -249,7 +249,7 @@ static void dsound_free(void *data)
|
||||
}
|
||||
}
|
||||
|
||||
static void* dsound_init(const char *device, unsigned rate, unsigned latency)
|
||||
static void *dsound_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
dsound_t *ds = calloc(1, sizeof(*ds));
|
||||
if (!ds)
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ static void audio_ext_free(void *data)
|
||||
}
|
||||
}
|
||||
|
||||
static void* audio_ext_init(const char *device, unsigned rate, unsigned latency)
|
||||
static void *audio_ext_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
if (!(*g_settings.audio.external_driver))
|
||||
{
|
||||
|
||||
@@ -61,7 +61,7 @@ typedef struct ssnes_audio_driver_info
|
||||
typedef struct ssnes_audio_driver
|
||||
{
|
||||
// Initializes the device.
|
||||
void* (*init)(const ssnes_audio_driver_info_t *info);
|
||||
void *(*init)(const ssnes_audio_driver_info_t *info);
|
||||
|
||||
// Write data in buffer to audio driver.
|
||||
// A frame here is defined as one combined sample of left and right
|
||||
|
||||
@@ -87,7 +87,7 @@ typedef struct ssnes_dsp_input
|
||||
typedef struct ssnes_dsp_plugin
|
||||
{
|
||||
// Creates a handle of the plugin. Returns NULL if failed.
|
||||
void* (*init)(const ssnes_dsp_info_t *info);
|
||||
void *(*init)(const ssnes_dsp_info_t *info);
|
||||
|
||||
// Processes input data.
|
||||
// The plugin is allowed to return variable sizes for output data.
|
||||
|
||||
@@ -207,7 +207,7 @@ static void generate_filter_coeffs(float * restrict fir_out, float * restrict ii
|
||||
}
|
||||
|
||||
|
||||
static void* dsp_init(const ssnes_dsp_info_t *info)
|
||||
static void *dsp_init(const ssnes_dsp_info_t *info)
|
||||
{
|
||||
(void)info;
|
||||
dsp_state *state = calloc(1, sizeof(*state));
|
||||
|
||||
+10
-17
@@ -54,20 +54,19 @@ hermite_resampler_t *hermite_new(void)
|
||||
return re;
|
||||
}
|
||||
|
||||
// We make sure to allocate enough output data beforehand ... ;)
|
||||
void hermite_process(hermite_resampler_t *re, struct hermite_data *data)
|
||||
{
|
||||
double r_step = 1.0 / data->ratio;
|
||||
size_t processed_out = 0;
|
||||
size_t processed_in = 0;
|
||||
|
||||
size_t in_frames = data->input_frames;
|
||||
size_t out_frames = data->output_frames;
|
||||
const float *in_data = data->data_in;
|
||||
float *out_data = data->data_out;
|
||||
|
||||
while (processed_in < in_frames && processed_out < out_frames)
|
||||
for (size_t i = 0; i < in_frames; i++)
|
||||
{
|
||||
while (re->r_frac <= 1.0 && processed_out < out_frames)
|
||||
while (re->r_frac <= 1.0)
|
||||
{
|
||||
re->r_frac += r_step;
|
||||
for (unsigned i = 0; i < CHANNELS; i++)
|
||||
@@ -79,23 +78,17 @@ void hermite_process(hermite_resampler_t *re, struct hermite_data *data)
|
||||
processed_out++;
|
||||
}
|
||||
|
||||
if (re->r_frac >= 1.0)
|
||||
re->r_frac -= 1.0;
|
||||
for (unsigned i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
re->r_frac -= 1.0;
|
||||
|
||||
for (unsigned i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
re->chan_data[i][0] = re->chan_data[i][1];
|
||||
re->chan_data[i][1] = re->chan_data[i][2];
|
||||
re->chan_data[i][2] = re->chan_data[i][3];
|
||||
re->chan_data[i][3] = *in_data++;
|
||||
}
|
||||
processed_in++;
|
||||
re->chan_data[i][0] = re->chan_data[i][1];
|
||||
re->chan_data[i][1] = re->chan_data[i][2];
|
||||
re->chan_data[i][2] = re->chan_data[i][3];
|
||||
re->chan_data[i][3] = *in_data++;
|
||||
}
|
||||
}
|
||||
|
||||
data->input_frames_used = processed_in;
|
||||
data->output_frames_gen = processed_out;
|
||||
data->output_frames = processed_out;
|
||||
}
|
||||
|
||||
void hermite_free(hermite_resampler_t *re)
|
||||
|
||||
@@ -36,9 +36,6 @@ struct hermite_data
|
||||
size_t input_frames;
|
||||
size_t output_frames;
|
||||
|
||||
size_t input_frames_used;
|
||||
size_t output_frames_gen;
|
||||
|
||||
double ratio;
|
||||
};
|
||||
|
||||
|
||||
+14
-14
@@ -124,7 +124,7 @@ static size_t find_buffersize(jack_t *jd, int latency)
|
||||
return buffer_frames * sizeof(jack_default_audio_sample_t);
|
||||
}
|
||||
|
||||
static void* __jack_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *ja_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
jack_t *jd = calloc(1, sizeof(jack_t));
|
||||
if (!jd)
|
||||
@@ -246,32 +246,32 @@ static size_t write_buffer(jack_t *jd, const float *buf, size_t size)
|
||||
return written * sizeof(float) * 2;
|
||||
}
|
||||
|
||||
static ssize_t __jack_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t ja_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
|
||||
return write_buffer(jd, buf, size);
|
||||
}
|
||||
|
||||
static bool __jack_stop(void *data)
|
||||
static bool ja_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __jack_set_nonblock_state(void *data, bool state)
|
||||
static void ja_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
jd->nonblock = state;
|
||||
}
|
||||
|
||||
static bool __jack_start(void *data)
|
||||
static bool ja_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __jack_free(void *data)
|
||||
static void ja_free(void *data)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
|
||||
@@ -292,20 +292,20 @@ static void __jack_free(void *data)
|
||||
free(jd);
|
||||
}
|
||||
|
||||
static bool __jack_use_float(void *data)
|
||||
static bool ja_use_float(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
const audio_driver_t audio_jack = {
|
||||
.init = __jack_init,
|
||||
.write = __jack_write,
|
||||
.stop = __jack_stop,
|
||||
.start = __jack_start,
|
||||
.set_nonblock_state = __jack_set_nonblock_state,
|
||||
.free = __jack_free,
|
||||
.use_float = __jack_use_float,
|
||||
.init = ja_init,
|
||||
.write = ja_write,
|
||||
.stop = ja_stop,
|
||||
.start = ja_start,
|
||||
.set_nonblock_state = ja_set_nonblock_state,
|
||||
.free = ja_free,
|
||||
.use_float = ja_use_float,
|
||||
.ident = "jack"
|
||||
};
|
||||
|
||||
|
||||
+13
-13
@@ -56,7 +56,7 @@ typedef struct al
|
||||
|
||||
} al_t;
|
||||
|
||||
static void* __al_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *al_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
(void)device;
|
||||
al_t *al = calloc(1, sizeof(al_t));
|
||||
@@ -153,7 +153,7 @@ static bool al_get_buffer(al_t *al, ALuint *buffer)
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t al_fill_internal_buf(al_t *al, const void* buf, size_t size)
|
||||
static size_t al_fill_internal_buf(al_t *al, const void *buf, size_t size)
|
||||
{
|
||||
size_t read_size = (BUFSIZE - al->tmpbuf_ptr > size) ? size : (BUFSIZE - al->tmpbuf_ptr);
|
||||
memcpy(al->tmpbuf + al->tmpbuf_ptr, buf, read_size);
|
||||
@@ -161,7 +161,7 @@ static size_t al_fill_internal_buf(al_t *al, const void* buf, size_t size)
|
||||
return read_size;
|
||||
}
|
||||
|
||||
static ssize_t __al_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t al_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
al_t *al = data;
|
||||
|
||||
@@ -196,25 +196,25 @@ static ssize_t __al_write(void* data, const void* buf, size_t size)
|
||||
return size;
|
||||
}
|
||||
|
||||
static bool __al_stop(void *data)
|
||||
static bool al_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __al_set_nonblock_state(void *data, bool state)
|
||||
static void al_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
al_t *al = data;
|
||||
al->nonblock = state;
|
||||
}
|
||||
|
||||
static bool __al_start(void *data)
|
||||
static bool al_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __al_free(void *data)
|
||||
static void al_free(void *data)
|
||||
{
|
||||
al_t *al= data;
|
||||
if (al)
|
||||
@@ -235,12 +235,12 @@ static void __al_free(void *data)
|
||||
}
|
||||
|
||||
const audio_driver_t audio_openal = {
|
||||
.init = __al_init,
|
||||
.write = __al_write,
|
||||
.stop = __al_stop,
|
||||
.start = __al_start,
|
||||
.set_nonblock_state = __al_set_nonblock_state,
|
||||
.free = __al_free,
|
||||
.init = al_init,
|
||||
.write = al_write,
|
||||
.stop = al_stop,
|
||||
.start = al_start,
|
||||
.set_nonblock_state = al_set_nonblock_state,
|
||||
.free = al_free,
|
||||
.ident = "openal"
|
||||
};
|
||||
|
||||
+12
-12
@@ -41,7 +41,7 @@
|
||||
#define DEFAULT_OSS_DEV "/dev/dsp"
|
||||
#endif
|
||||
|
||||
static void* __oss_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *oss_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
int *fd = calloc(1, sizeof(int));
|
||||
if (fd == NULL)
|
||||
@@ -96,7 +96,7 @@ static void* __oss_init(const char* device, unsigned rate, unsigned latency)
|
||||
return fd;
|
||||
}
|
||||
|
||||
static ssize_t __oss_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t oss_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
int *fd = data;
|
||||
|
||||
@@ -114,19 +114,19 @@ static ssize_t __oss_write(void* data, const void* buf, size_t size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool __oss_stop(void *data)
|
||||
static bool oss_stop(void *data)
|
||||
{
|
||||
int *fd = data;
|
||||
ioctl(*fd, SNDCTL_DSP_RESET, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __oss_start(void *data)
|
||||
static bool oss_start(void *data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __oss_set_nonblock_state(void *data, bool state)
|
||||
static void oss_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
int *fd = data;
|
||||
int rc;
|
||||
@@ -138,7 +138,7 @@ static void __oss_set_nonblock_state(void *data, bool state)
|
||||
fprintf(stderr, "SSNES [ERROR]: Could not set nonblocking on OSS file descriptor. Will not be able to fast-forward.\n");
|
||||
}
|
||||
|
||||
static void __oss_free(void *data)
|
||||
static void oss_free(void *data)
|
||||
{
|
||||
int *fd = data;
|
||||
|
||||
@@ -148,12 +148,12 @@ static void __oss_free(void *data)
|
||||
}
|
||||
|
||||
const audio_driver_t audio_oss = {
|
||||
.init = __oss_init,
|
||||
.write = __oss_write,
|
||||
.stop = __oss_stop,
|
||||
.start = __oss_start,
|
||||
.set_nonblock_state = __oss_set_nonblock_state,
|
||||
.free = __oss_free,
|
||||
.init = oss_init,
|
||||
.write = oss_write,
|
||||
.stop = oss_stop,
|
||||
.start = oss_start,
|
||||
.set_nonblock_state = oss_set_nonblock_state,
|
||||
.free = oss_free,
|
||||
.ident = "oss"
|
||||
};
|
||||
|
||||
+15
-15
@@ -32,7 +32,7 @@ typedef struct
|
||||
bool nonblock;
|
||||
} pa_t;
|
||||
|
||||
static void __pulse_free(void *data)
|
||||
static void pulse_free(void *data)
|
||||
{
|
||||
pa_t *pa = data;
|
||||
if (pa)
|
||||
@@ -103,7 +103,7 @@ static void stream_latency_update_cb(pa_stream *s, void *data)
|
||||
pa_threaded_mainloop_signal(pa->mainloop, 0);
|
||||
}
|
||||
|
||||
static void* __pulse_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *pulse_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
pa_t *pa = calloc(1, sizeof(*pa));
|
||||
if (!pa)
|
||||
@@ -168,11 +168,11 @@ static void* __pulse_init(const char* device, unsigned rate, unsigned latency)
|
||||
unlock_error:
|
||||
pa_threaded_mainloop_unlock(pa->mainloop);
|
||||
error:
|
||||
__pulse_free(pa);
|
||||
pulse_free(pa);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t __pulse_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t pulse_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
pa_t *pa = data;
|
||||
|
||||
@@ -198,38 +198,38 @@ static ssize_t __pulse_write(void* data, const void* buf, size_t size)
|
||||
return write_size;
|
||||
}
|
||||
|
||||
static bool __pulse_stop(void *data)
|
||||
static bool pulse_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __pulse_start(void *data)
|
||||
static bool pulse_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __pulse_set_nonblock_state(void *data, bool state)
|
||||
static void pulse_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
pa_t *pa = data;
|
||||
pa->nonblock = state;
|
||||
}
|
||||
|
||||
static bool __pulse_use_float(void *data)
|
||||
static bool pulse_use_float(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
const audio_driver_t audio_pulse = {
|
||||
.init = __pulse_init,
|
||||
.write = __pulse_write,
|
||||
.stop = __pulse_stop,
|
||||
.start = __pulse_start,
|
||||
.set_nonblock_state = __pulse_set_nonblock_state,
|
||||
.use_float = __pulse_use_float,
|
||||
.free = __pulse_free,
|
||||
.init = pulse_init,
|
||||
.write = pulse_write,
|
||||
.stop = pulse_stop,
|
||||
.start = pulse_start,
|
||||
.set_nonblock_state = pulse_set_nonblock_state,
|
||||
.use_float = pulse_use_float,
|
||||
.free = pulse_free,
|
||||
.ident = "pulse"
|
||||
};
|
||||
|
||||
|
||||
+13
-12
@@ -30,8 +30,9 @@ typedef struct
|
||||
bool nonblocking;
|
||||
} roar_t;
|
||||
|
||||
static void* __roar_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *ra_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
(void)latency;
|
||||
int err;
|
||||
roar_t *roar = calloc(1, sizeof(roar_t));
|
||||
if (roar == NULL)
|
||||
@@ -51,7 +52,7 @@ static void* __roar_init(const char* device, unsigned rate, unsigned latency)
|
||||
return roar;
|
||||
}
|
||||
|
||||
static ssize_t __roar_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t ra_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
roar_t *roar = data;
|
||||
ssize_t rc;
|
||||
@@ -77,13 +78,13 @@ static ssize_t __roar_write(void* data, const void* buf, size_t size)
|
||||
return size;
|
||||
}
|
||||
|
||||
static bool __roar_stop(void *data)
|
||||
static bool ra_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __roar_set_nonblock_state(void *data, bool state)
|
||||
static void ra_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
roar_t *roar = data;
|
||||
if (roar_vs_blocking(roar->vss, (state) ? ROAR_VS_FALSE : ROAR_VS_TRUE, NULL) < 0)
|
||||
@@ -91,13 +92,13 @@ static void __roar_set_nonblock_state(void *data, bool state)
|
||||
roar->nonblocking = state;
|
||||
}
|
||||
|
||||
static bool __roar_start(void *data)
|
||||
static bool ra_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __roar_free(void *data)
|
||||
static void ra_free(void *data)
|
||||
{
|
||||
roar_t *roar = data;
|
||||
roar_vs_close(roar->vss, ROAR_VS_TRUE, NULL);
|
||||
@@ -105,12 +106,12 @@ static void __roar_free(void *data)
|
||||
}
|
||||
|
||||
const audio_driver_t audio_roar = {
|
||||
.init = __roar_init,
|
||||
.write = __roar_write,
|
||||
.stop = __roar_stop,
|
||||
.start = __roar_start,
|
||||
.set_nonblock_state = __roar_set_nonblock_state,
|
||||
.free = __roar_free,
|
||||
.init = ra_init,
|
||||
.write = ra_write,
|
||||
.stop = ra_stop,
|
||||
.start = ra_start,
|
||||
.set_nonblock_state = ra_set_nonblock_state,
|
||||
.free = ra_free,
|
||||
.ident = "roar"
|
||||
};
|
||||
|
||||
|
||||
+12
-12
@@ -54,7 +54,7 @@ static void err_cb(void *userdata)
|
||||
SDL_CondSignal(rsd->cond);
|
||||
}
|
||||
|
||||
static void* __rsd_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *rs_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
rsd_t *rsd = calloc(1, sizeof(rsd_t));
|
||||
if (!rsd)
|
||||
@@ -98,7 +98,7 @@ static void* __rsd_init(const char* device, unsigned rate, unsigned latency)
|
||||
return rsd;
|
||||
}
|
||||
|
||||
static ssize_t __rsd_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t rs_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
|
||||
@@ -144,7 +144,7 @@ static ssize_t __rsd_write(void* data, const void* buf, size_t size)
|
||||
}
|
||||
}
|
||||
|
||||
static bool __rsd_stop(void *data)
|
||||
static bool rs_stop(void *data)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
rsd_stop(rsd->rd);
|
||||
@@ -152,13 +152,13 @@ static bool __rsd_stop(void *data)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __rsd_set_nonblock_state(void *data, bool state)
|
||||
static void rs_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
rsd->nonblock = state;
|
||||
}
|
||||
|
||||
static bool __rsd_start(void *data)
|
||||
static bool rs_start(void *data)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
if (rsd_start(rsd->rd) < 0)
|
||||
@@ -167,7 +167,7 @@ static bool __rsd_start(void *data)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __rsd_free(void *data)
|
||||
static void rs_free(void *data)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
|
||||
@@ -182,12 +182,12 @@ static void __rsd_free(void *data)
|
||||
}
|
||||
|
||||
const audio_driver_t audio_rsound = {
|
||||
.init = __rsd_init,
|
||||
.write = __rsd_write,
|
||||
.stop = __rsd_stop,
|
||||
.start = __rsd_start,
|
||||
.set_nonblock_state = __rsd_set_nonblock_state,
|
||||
.free = __rsd_free,
|
||||
.init = rs_init,
|
||||
.write = rs_write,
|
||||
.stop = rs_stop,
|
||||
.start = rs_start,
|
||||
.set_nonblock_state = rs_set_nonblock_state,
|
||||
.free = rs_free,
|
||||
.ident = "rsound"
|
||||
};
|
||||
|
||||
+2
-2
@@ -58,7 +58,7 @@ static inline int find_num_frames(int rate, int latency)
|
||||
return next_pow2(frames);
|
||||
}
|
||||
|
||||
static void* sdl_audio_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *sdl_audio_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
(void)device;
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
|
||||
@@ -109,7 +109,7 @@ static void* sdl_audio_init(const char* device, unsigned rate, unsigned latency)
|
||||
return sdl;
|
||||
}
|
||||
|
||||
static ssize_t sdl_audio_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t sdl_audio_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
sdl_audio_t *sdl = data;
|
||||
|
||||
|
||||
+27
-28
@@ -3,7 +3,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __SSE2__
|
||||
#if __SSE2__
|
||||
#include <mmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
#define audio_convert_s16_to_float audio_convert_s16_to_float_SSE2
|
||||
#define audio_convert_float_to_s16 audio_convert_float_to_s16_SSE2
|
||||
@@ -29,25 +30,27 @@ static inline void audio_convert_float_to_s16_C(int16_t *out,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __SSE2__
|
||||
#if __SSE2__
|
||||
static inline void audio_convert_s16_to_float_SSE2(float *out,
|
||||
const int16_t *in, unsigned samples)
|
||||
{
|
||||
// Not aligned? FML :(
|
||||
if (((uintptr_t)in & 7) || ((uintptr_t)out & 15))
|
||||
{
|
||||
audio_convert_s16_to_float_C(out, in, samples);
|
||||
return;
|
||||
}
|
||||
|
||||
__m128 factor = _mm_set1_ps(1.0f / 0x7fff);
|
||||
__m128 factor = _mm_set1_ps(1.0f / (0x7fff * 0x10000));
|
||||
unsigned i;
|
||||
for (i = 0; i + 4 <= samples; i += 4, in += 4, out += 4)
|
||||
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||
{
|
||||
__m64 input = *(const __m64*)in;
|
||||
__m128 reg = _mm_cvtpi16_ps(input);
|
||||
__m128 res = _mm_mul_ps(reg, factor);
|
||||
*(__m128*)out = res;
|
||||
__m128i input = _mm_loadu_si128((const __m128i *)in);
|
||||
__m128i regs[2] = {
|
||||
_mm_unpacklo_epi16(_mm_setzero_si128(), input),
|
||||
_mm_unpackhi_epi16(_mm_setzero_si128(), input),
|
||||
};
|
||||
|
||||
__m128 output[2] = {
|
||||
_mm_mul_ps(_mm_cvtepi32_ps(regs[0]), factor),
|
||||
_mm_mul_ps(_mm_cvtepi32_ps(regs[1]), factor),
|
||||
};
|
||||
|
||||
_mm_storeu_ps(out + 0, output[0]);
|
||||
_mm_storeu_ps(out + 4, output[1]);
|
||||
}
|
||||
|
||||
audio_convert_s16_to_float_C(out, in, samples - i);
|
||||
@@ -56,21 +59,17 @@ static inline void audio_convert_s16_to_float_SSE2(float *out,
|
||||
static inline void audio_convert_float_to_s16_SSE2(int16_t *out,
|
||||
const float *in, unsigned samples)
|
||||
{
|
||||
// Not aligned? FML :(
|
||||
if (((uintptr_t)in & 7) || ((uintptr_t)out & 15))
|
||||
{
|
||||
audio_convert_float_to_s16_C(out, in, samples);
|
||||
return;
|
||||
}
|
||||
|
||||
__m128 factor = _mm_set1_ps(0x7fff);
|
||||
__m128 factor = _mm_set1_ps((float)0x7fff);
|
||||
unsigned i;
|
||||
for (i = 0; i + 4 <= samples; i += 4, in += 4, out += 4)
|
||||
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||
{
|
||||
__m128 input = *(const __m128*)in;
|
||||
__m128 res = _mm_mul_ps(input, factor);
|
||||
__m64 output = _mm_cvtps_pi16(res);
|
||||
*(__m64*)out = output;
|
||||
__m128 input[2] = { _mm_loadu_ps(in + 0), _mm_loadu_ps(in + 4) };
|
||||
__m128 res[2] = { _mm_mul_ps(input[0], factor), _mm_mul_ps(input[1], factor) };
|
||||
|
||||
__m128i ints[2] = { _mm_cvtps_epi32(res[0]), _mm_cvtps_epi32(res[1]) };
|
||||
__m128i packed = _mm_packs_epi32(ints[0], ints[1]);
|
||||
|
||||
_mm_storeu_si128((__m128i *)out, packed);
|
||||
}
|
||||
|
||||
audio_convert_float_to_s16_C(out, in, samples - i);
|
||||
|
||||
@@ -146,7 +146,7 @@ typedef struct XAUDIO2_BUFFER
|
||||
UINT32 LoopBegin;
|
||||
UINT32 LoopLength;
|
||||
UINT32 LoopCount;
|
||||
void* pContext;
|
||||
void *pContext;
|
||||
} XAUDIO2_BUFFER;
|
||||
|
||||
typedef struct XAUDIO2_BUFFER_WMA
|
||||
@@ -157,7 +157,7 @@ typedef struct XAUDIO2_BUFFER_WMA
|
||||
|
||||
typedef struct XAUDIO2_VOICE_STATE
|
||||
{
|
||||
void* pCurrentBufferContext;
|
||||
void *pCurrentBufferContext;
|
||||
UINT32 BuffersQueued;
|
||||
UINT64 SamplesPlayed;
|
||||
} XAUDIO2_VOICE_STATE;
|
||||
@@ -201,10 +201,10 @@ DECLARE_INTERFACE(IXAudio2VoiceCallback)
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart) (THIS_ UINT32 BytesRequired) PURE;
|
||||
STDMETHOD_(void, OnVoiceProcessingPassEnd) (THIS) PURE;
|
||||
STDMETHOD_(void, OnStreamEnd) (THIS) PURE;
|
||||
STDMETHOD_(void, OnBufferStart) (THIS_ void* pBufferContext) PURE;
|
||||
STDMETHOD_(void, OnBufferEnd) (THIS_ void* pBufferContext) PURE;
|
||||
STDMETHOD_(void, OnLoopEnd) (THIS_ void* pBufferContext) PURE;
|
||||
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) PURE;
|
||||
STDMETHOD_(void, OnBufferStart) (THIS_ void *pBufferContext) PURE;
|
||||
STDMETHOD_(void, OnBufferEnd) (THIS_ void *pBufferContext) PURE;
|
||||
STDMETHOD_(void, OnLoopEnd) (THIS_ void *pBufferContext) PURE;
|
||||
STDMETHOD_(void, OnVoiceError) (THIS_ void *pBufferContext, HRESULT Error) PURE;
|
||||
};
|
||||
|
||||
DECLARE_INTERFACE(IXAudio2Voice)
|
||||
@@ -219,10 +219,10 @@ DECLARE_INTERFACE(IXAudio2Voice)
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD_(void, GetEffectState) (THIS_ UINT32 EffectIndex, BOOL* pEnabled) PURE; \
|
||||
STDMETHOD(SetEffectParameters) (THIS_ UINT32 EffectIndex, \
|
||||
const void* pParameters, \
|
||||
const void *pParameters, \
|
||||
UINT32 ParametersByteSize, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD(GetEffectParameters) (THIS_ UINT32 EffectIndex, void* pParameters, \
|
||||
STDMETHOD(GetEffectParameters) (THIS_ UINT32 EffectIndex, void *pParameters, \
|
||||
UINT32 ParametersByteSize) PURE; \
|
||||
STDMETHOD(SetFilterParameters) (THIS_ const XAUDIO2_FILTER_PARAMETERS* pParameters, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
@@ -304,7 +304,7 @@ DECLARE_INTERFACE_(IXAudio2, IUnknown)
|
||||
STDMETHOD(CommitChanges) (THIS_ UINT32 OperationSet) PURE;
|
||||
STDMETHOD_(void, GetPerformanceData) (THIS_ XAUDIO2_PERFORMANCE_DATA* pPerfData) PURE;
|
||||
STDMETHOD_(void, SetDebugConfiguration) (THIS_ const XAUDIO2_DEBUG_CONFIGURATION* pDebugConfiguration,
|
||||
void* pReserved X2DEFAULT(NULL)) PURE;
|
||||
void *pReserved X2DEFAULT(NULL)) PURE;
|
||||
};
|
||||
|
||||
static inline HRESULT XAudio2Create(IXAudio2** ppXAudio2, UINT32 Flags X2DEFAULT(0),
|
||||
|
||||
+14
-14
@@ -26,7 +26,7 @@ typedef struct
|
||||
bool nonblock;
|
||||
} xa_t;
|
||||
|
||||
static void* __xa_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *xa_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
if (latency < 8)
|
||||
latency = 8; // Do not allow shenanigans.
|
||||
@@ -49,7 +49,7 @@ static void* __xa_init(const char* device, unsigned rate, unsigned latency)
|
||||
return xa;
|
||||
}
|
||||
|
||||
static ssize_t __xa_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t xa_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
xa_t *xa = data;
|
||||
if (xa->nonblock)
|
||||
@@ -66,31 +66,31 @@ static ssize_t __xa_write(void* data, const void* buf, size_t size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool __xa_stop(void *data)
|
||||
static bool xa_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __xa_set_nonblock_state(void *data, bool state)
|
||||
static void xa_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
xa_t *xa = data;
|
||||
xa->nonblock = state;
|
||||
}
|
||||
|
||||
static bool __xa_start(void *data)
|
||||
static bool xa_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __xa_use_float(void *data)
|
||||
static bool xa_use_float(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __xa_free(void *data)
|
||||
static void xa_free(void *data)
|
||||
{
|
||||
xa_t *xa = data;
|
||||
if (xa)
|
||||
@@ -102,13 +102,13 @@ static void __xa_free(void *data)
|
||||
}
|
||||
|
||||
const audio_driver_t audio_xa = {
|
||||
.init = __xa_init,
|
||||
.write = __xa_write,
|
||||
.stop = __xa_stop,
|
||||
.start = __xa_start,
|
||||
.set_nonblock_state = __xa_set_nonblock_state,
|
||||
.use_float = __xa_use_float,
|
||||
.free = __xa_free,
|
||||
.init = xa_init,
|
||||
.write = xa_write,
|
||||
.stop = xa_stop,
|
||||
.start = xa_start,
|
||||
.set_nonblock_state = xa_set_nonblock_state,
|
||||
.use_float = xa_use_float,
|
||||
.free = xa_free,
|
||||
.ident = "xaudio"
|
||||
};
|
||||
|
||||
@@ -51,7 +51,7 @@ struct cheat_manager
|
||||
unsigned buf_size;
|
||||
};
|
||||
|
||||
static char* strcat_alloc(char *dest, const char *input)
|
||||
static char *strcat_alloc(char *dest, const char *input)
|
||||
{
|
||||
size_t dest_len = dest ? strlen(dest) : 0;
|
||||
size_t input_len = strlen(input);
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ static char *getaline(FILE *file)
|
||||
return newline;
|
||||
}
|
||||
|
||||
static char* extract_value(char *line, bool is_value)
|
||||
static char *extract_value(char *line, bool is_value)
|
||||
{
|
||||
if (is_value)
|
||||
{
|
||||
|
||||
+16
-10
@@ -55,7 +55,7 @@
|
||||
#define INPUT_X 12
|
||||
////////////////////////
|
||||
|
||||
#if defined(HAVE_SDL)
|
||||
#if defined(HAVE_OPENGL)
|
||||
#define VIDEO_DEFAULT_DRIVER VIDEO_GL
|
||||
#elif defined(HAVE_XVIDEO)
|
||||
#define VIDEO_DEFAULT_DRIVER VIDEO_XVIDEO
|
||||
@@ -111,7 +111,6 @@
|
||||
// Windowed
|
||||
static const float xscale = 3.0; // Real x res = aspect * base_size * xscale
|
||||
static const float yscale = 3.0; // Real y res = base_size * yscale
|
||||
static const unsigned base_size = 224; // Base size for windowed mode.
|
||||
|
||||
// Fullscreen
|
||||
static const bool fullscreen = false; // To start in Fullscreen or not
|
||||
@@ -134,7 +133,7 @@ static const bool video_smooth = true;
|
||||
static const bool force_aspect = true;
|
||||
|
||||
// Crop overscanned frames (7/8 or 15/15 for interlaced frames).
|
||||
static const bool crop_overscan = false;
|
||||
static const bool crop_overscan = true;
|
||||
|
||||
// Font size for on-screen messages.
|
||||
static const unsigned font_size = 48;
|
||||
@@ -157,7 +156,18 @@ static const bool hires_record = false;
|
||||
// Record post-filtered (CPU filter) video rather than raw SNES output.
|
||||
static const bool post_filter_record = false;
|
||||
|
||||
#define SNES_ASPECT_RATIO (4.0/3)
|
||||
// OSD-messages
|
||||
static const bool font_enable = true;
|
||||
|
||||
// The accurate refresh rate of your monitor (Hz).
|
||||
// This is used to calculate audio input rate with the formula:
|
||||
// audio_input_rate = game_input_rate * display_refresh_rate / game_refresh_rate.
|
||||
// If the implementation does not report any values,
|
||||
// SNES NTSC defaults will be assumed for compatibility.
|
||||
// This value should stay close to 60Hz to avoid large pitch changes.
|
||||
// If your monitor does not run at 60Hz, or something close to it, disable VSync,
|
||||
// and leave this at its default.
|
||||
static const float refresh_rate = 59.95;
|
||||
|
||||
////////////////
|
||||
// Audio
|
||||
@@ -169,15 +179,11 @@ static const bool audio_enable = true;
|
||||
// Output samplerate
|
||||
static const unsigned out_rate = 48000;
|
||||
|
||||
// Input samplerate from libSNES.
|
||||
// Lower this (slightly) if you are experiencing frequent audio dropouts while vsync is enabled.
|
||||
static const float in_rate = 31980.0;
|
||||
|
||||
// When changing input rate on-the-fly
|
||||
static const float audio_rate_step = 0.25;
|
||||
|
||||
// Audio device (e.g. hw:0,0 or /dev/audio). If NULL, will use defaults.
|
||||
static const char* audio_device = NULL;
|
||||
static const char *audio_device = NULL;
|
||||
|
||||
// Desired audio latency in milliseconds. Might not be honored if driver can't provide given latency.
|
||||
static const int out_latency = 64;
|
||||
@@ -196,7 +202,7 @@ static const unsigned rewind_buffer_size = 20 << 20; // 20MiB
|
||||
static const unsigned rewind_granularity = 1;
|
||||
|
||||
// Pause gameplay when gameplay loses focus.
|
||||
static const bool pause_nonactive = true;
|
||||
static const bool pause_nonactive = false;
|
||||
|
||||
// Saves non-volatile SRAM at a regular interval. It is measured in seconds. A value of 0 disables autosave.
|
||||
static const unsigned autosave_interval = 0;
|
||||
|
||||
@@ -14,6 +14,12 @@ static const bool _sdl_supp = true;
|
||||
static const bool _sdl_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENGL
|
||||
static const bool _opengl_supp = true;
|
||||
#else
|
||||
static const bool _opengl_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_XVIDEO
|
||||
static const bool _xvideo_supp = true;
|
||||
#else
|
||||
|
||||
+20
-1
@@ -1,6 +1,6 @@
|
||||
.\" ssnes.1:
|
||||
|
||||
.TH "SSNES" "1" "October 2011" "SSNES" "System Manager's Manual: ssnes"
|
||||
.TH "SSNES" "1" "November 2011" "SSNES" "System Manager's Manual: ssnes"
|
||||
|
||||
.SH NAME
|
||||
|
||||
@@ -27,9 +27,16 @@ If no rom file path is defined on the command line, \fBssnes\fR will try to load
|
||||
\fB--help, -h\fR
|
||||
Prints help text.
|
||||
|
||||
.TP
|
||||
\fB--features\fR
|
||||
Prints available features compiled into SSNES, then exits.
|
||||
|
||||
.TP
|
||||
\fB-L PATH, --libsnes PATH\fR
|
||||
Path to a libsnes implementation which is to be used.
|
||||
This option will override any setting in a config file.
|
||||
This option is only available if SSNES is compiled with dynamic libsnes loading.
|
||||
|
||||
.TP
|
||||
\fB--save PATH, -s PATH\fR
|
||||
Overrides the path used for save ram (*.srm).
|
||||
@@ -94,6 +101,7 @@ 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.
|
||||
|
||||
.TP
|
||||
\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.
|
||||
|
||||
@@ -128,6 +136,17 @@ The video input is scaled with point filtering before being encoded at the corre
|
||||
Play back a movie recorded in the .bsv format (bSNES). Cart ROM and movie file need to correspond.
|
||||
It also requires to play back with the same libsnes backend that was used for recording.
|
||||
|
||||
.TP
|
||||
\fB--bsvrecord PATH, -R PATH\fR
|
||||
Start recording a .bsv video to PATH immediately after startup.
|
||||
|
||||
.TP
|
||||
\fB--sram-mode MODE, -M MODE\fR
|
||||
MODE designates how to handle SRAM.
|
||||
The format of the second parameter is {no,}load-{no,}save.
|
||||
These two boolean values tell if SRAM loading and SRAM saving should take place.
|
||||
Note that noload-save implies that the SRAM will be overwritten with new data.
|
||||
|
||||
.TP
|
||||
\fB--verbose, -v\fR
|
||||
Activates verbose logging.
|
||||
|
||||
@@ -68,8 +68,10 @@ static const audio_driver_t *audio_drivers[] = {
|
||||
};
|
||||
|
||||
static const video_driver_t *video_drivers[] = {
|
||||
#ifdef HAVE_SDL
|
||||
#ifdef HAVE_OPENGL
|
||||
&video_gl,
|
||||
#endif
|
||||
#ifdef HAVE_SDL
|
||||
&video_sdl,
|
||||
#endif
|
||||
#ifdef HAVE_XVIDEO
|
||||
@@ -91,7 +93,7 @@ static const input_driver_t *input_drivers[] = {
|
||||
|
||||
static void find_audio_driver(void)
|
||||
{
|
||||
for (int i = 0; i < sizeof(audio_drivers) / sizeof(audio_driver_t*); i++)
|
||||
for (unsigned i = 0; i < sizeof(audio_drivers) / sizeof(audio_driver_t*); i++)
|
||||
{
|
||||
if (strcasecmp(g_settings.audio.driver, audio_drivers[i]->ident) == 0)
|
||||
{
|
||||
@@ -101,7 +103,7 @@ static void find_audio_driver(void)
|
||||
}
|
||||
SSNES_ERR("Couldn't find any audio driver named \"%s\"\n", g_settings.audio.driver);
|
||||
fprintf(stderr, "Available audio drivers are:\n");
|
||||
for (int i = 0; i < sizeof(audio_drivers) / sizeof(audio_driver_t*); i++)
|
||||
for (unsigned i = 0; i < sizeof(audio_drivers) / sizeof(audio_driver_t*); i++)
|
||||
fprintf(stderr, "\t%s\n", audio_drivers[i]->ident);
|
||||
|
||||
exit(1);
|
||||
@@ -109,7 +111,7 @@ static void find_audio_driver(void)
|
||||
|
||||
static void find_video_driver(void)
|
||||
{
|
||||
for (int i = 0; i < sizeof(video_drivers) / sizeof(video_driver_t*); i++)
|
||||
for (unsigned i = 0; i < sizeof(video_drivers) / sizeof(video_driver_t*); i++)
|
||||
{
|
||||
if (strcasecmp(g_settings.video.driver, video_drivers[i]->ident) == 0)
|
||||
{
|
||||
@@ -127,7 +129,7 @@ static void find_video_driver(void)
|
||||
|
||||
static void find_input_driver(void)
|
||||
{
|
||||
for (int i = 0; i < sizeof(input_drivers) / sizeof(input_driver_t*); i++)
|
||||
for (unsigned i = 0; i < sizeof(input_drivers) / sizeof(input_driver_t*); i++)
|
||||
{
|
||||
if (strcasecmp(g_settings.input.driver, input_drivers[i]->ident) == 0)
|
||||
{
|
||||
@@ -220,23 +222,71 @@ static void deinit_dsp_plugin(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void adjust_audio_input_rate(void)
|
||||
{
|
||||
static bool first = true;
|
||||
if (!first)
|
||||
return;
|
||||
|
||||
if (g_extern.system.timing_set)
|
||||
{
|
||||
float timing_skew = fabs(1.0f - g_extern.system.timing.fps / g_settings.video.refresh_rate);
|
||||
if (timing_skew > 0.05f) // We don't want to adjust pitch too much. If we have extreme cases, just don't readjust at all.
|
||||
{
|
||||
SSNES_LOG("Timings deviate too much. Will not adjust. (Display = %.2f Hz, Game = %.2lf Hz)\n",
|
||||
g_settings.video.refresh_rate,
|
||||
g_extern.system.timing.fps);
|
||||
|
||||
g_settings.video.refresh_rate = g_extern.system.timing.fps;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_extern.system.timing_set)
|
||||
{
|
||||
g_settings.audio.in_rate = g_extern.system.timing.sample_rate *
|
||||
(g_settings.video.refresh_rate / g_extern.system.timing.fps);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_settings.audio.in_rate = 32040.5 *
|
||||
(g_settings.video.refresh_rate / (21477272.0 / 357366.0)); // SNES metrics.
|
||||
}
|
||||
|
||||
SSNES_LOG("Set audio input rate to: %.2f Hz.\n", g_settings.audio.in_rate);
|
||||
first = false;
|
||||
}
|
||||
|
||||
#define AUDIO_CHUNK_SIZE_BLOCKING 64
|
||||
#define AUDIO_CHUNK_SIZE_NONBLOCKING 2048 // So we don't get complete line-noise when fast-forwarding audio.
|
||||
#define AUDIO_MAX_RATIO 16
|
||||
void init_audio(void)
|
||||
{
|
||||
// Accomodate rewind since at some point we might have two full buffers.
|
||||
size_t max_bufsamples = AUDIO_CHUNK_SIZE_NONBLOCKING * 2;
|
||||
|
||||
// Used for recording even if audio isn't enabled.
|
||||
assert((g_extern.audio_data.conv_outsamples = malloc(max_bufsamples * sizeof(int16_t) * AUDIO_MAX_RATIO)));
|
||||
g_extern.audio_data.chunk_size = g_extern.audio_data.block_chunk_size;
|
||||
|
||||
// Needs to be able to hold full content of a full max_bufsamples in addition to its own.
|
||||
assert((g_extern.audio_data.rewind_buf = malloc(max_bufsamples * sizeof(int16_t))));
|
||||
g_extern.audio_data.rewind_size = max_bufsamples;
|
||||
|
||||
if (!g_settings.audio.enable)
|
||||
{
|
||||
g_extern.audio_active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
adjust_audio_input_rate();
|
||||
find_audio_driver();
|
||||
|
||||
g_extern.audio_data.block_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING;
|
||||
g_extern.audio_data.nonblock_chunk_size = AUDIO_CHUNK_SIZE_NONBLOCKING;
|
||||
|
||||
driver.audio_data = driver.audio->init(strlen(g_settings.audio.device) ? g_settings.audio.device : NULL, g_settings.audio.out_rate, g_settings.audio.latency);
|
||||
driver.audio_data = driver.audio->init(*g_settings.audio.device ? g_settings.audio.device : NULL,
|
||||
g_settings.audio.out_rate, g_settings.audio.latency);
|
||||
|
||||
if (!driver.audio_data)
|
||||
g_extern.audio_active = false;
|
||||
|
||||
@@ -248,27 +298,15 @@ void init_audio(void)
|
||||
driver.audio->set_nonblock_state(driver.audio_data, true);
|
||||
g_extern.audio_data.chunk_size = g_extern.audio_data.nonblock_chunk_size;
|
||||
}
|
||||
else
|
||||
g_extern.audio_data.chunk_size = g_extern.audio_data.block_chunk_size;
|
||||
|
||||
g_extern.audio_data.source = hermite_new();
|
||||
if (!g_extern.audio_data.source)
|
||||
g_extern.audio_active = false;
|
||||
|
||||
size_t max_bufsamples = g_extern.audio_data.block_chunk_size > g_extern.audio_data.nonblock_chunk_size ?
|
||||
g_extern.audio_data.block_chunk_size : g_extern.audio_data.nonblock_chunk_size;
|
||||
|
||||
max_bufsamples *= 2; // Accomodate rewind since at some point we might have two full buffers.
|
||||
|
||||
assert((g_extern.audio_data.data = malloc(max_bufsamples * sizeof(float))));
|
||||
g_extern.audio_data.data_ptr = 0;
|
||||
assert(g_settings.audio.out_rate < g_settings.audio.in_rate * AUDIO_MAX_RATIO);
|
||||
assert((g_extern.audio_data.outsamples = malloc(max_bufsamples * sizeof(float) * AUDIO_MAX_RATIO)));
|
||||
assert((g_extern.audio_data.conv_outsamples = malloc(max_bufsamples * sizeof(int16_t) * AUDIO_MAX_RATIO)));
|
||||
|
||||
// Needs to be able to hold full content of a full max_bufsamples in addition to its own.
|
||||
assert((g_extern.audio_data.rewind_buf = malloc(max_bufsamples * sizeof(int16_t))));
|
||||
g_extern.audio_data.rewind_size = max_bufsamples;
|
||||
|
||||
g_extern.audio_data.src_ratio =
|
||||
(double)g_settings.audio.out_rate / g_settings.audio.in_rate;
|
||||
@@ -278,6 +316,11 @@ void init_audio(void)
|
||||
|
||||
void uninit_audio(void)
|
||||
{
|
||||
free(g_extern.audio_data.conv_outsamples); g_extern.audio_data.conv_outsamples = NULL;
|
||||
g_extern.audio_data.data_ptr = 0;
|
||||
free(g_extern.audio_data.rewind_buf);
|
||||
g_extern.audio_data.rewind_buf = NULL;
|
||||
|
||||
if (!g_settings.audio.enable)
|
||||
{
|
||||
g_extern.audio_active = false;
|
||||
@@ -290,10 +333,10 @@ void uninit_audio(void)
|
||||
if (g_extern.audio_data.source)
|
||||
hermite_free(g_extern.audio_data.source);
|
||||
|
||||
free(g_extern.audio_data.data); g_extern.audio_data.data = NULL;
|
||||
free(g_extern.audio_data.outsamples); g_extern.audio_data.outsamples = NULL;
|
||||
free(g_extern.audio_data.conv_outsamples); g_extern.audio_data.conv_outsamples = NULL;
|
||||
free(g_extern.audio_data.rewind_buf); g_extern.audio_data.rewind_buf = NULL;
|
||||
free(g_extern.audio_data.data);
|
||||
g_extern.audio_data.data = NULL;
|
||||
free(g_extern.audio_data.outsamples);
|
||||
g_extern.audio_data.outsamples = NULL;
|
||||
|
||||
deinit_dsp_plugin();
|
||||
}
|
||||
@@ -330,17 +373,17 @@ static void init_filter(void)
|
||||
|
||||
g_extern.filter.active = true;
|
||||
|
||||
unsigned width = 512;
|
||||
unsigned height = 512;
|
||||
unsigned width = g_extern.system.geom.max_width;
|
||||
unsigned height = g_extern.system.geom.max_height;
|
||||
g_extern.filter.psize(&width, &height);
|
||||
|
||||
unsigned pow2_x = next_pow2(ceil(width));
|
||||
unsigned pow2_y = next_pow2(ceil(height));
|
||||
unsigned maxsize = pow2_x > pow2_y ? pow2_x : pow2_y;
|
||||
g_extern.filter.scale = maxsize / 256;
|
||||
g_extern.filter.scale = maxsize / SSNES_SCALE_BASE;
|
||||
|
||||
g_extern.filter.buffer = malloc(256 * 256 * g_extern.filter.scale * g_extern.filter.scale * sizeof(uint32_t));
|
||||
g_extern.filter.pitch = 256 * g_extern.filter.scale * sizeof(uint32_t);
|
||||
g_extern.filter.buffer = malloc(SSNES_SCALE_BASE * SSNES_SCALE_BASE * g_extern.filter.scale * g_extern.filter.scale * sizeof(uint32_t));
|
||||
g_extern.filter.pitch = SSNES_SCALE_BASE * g_extern.filter.scale * sizeof(uint32_t);
|
||||
assert(g_extern.filter.buffer);
|
||||
|
||||
g_extern.filter.colormap = malloc(32768 * sizeof(uint32_t));
|
||||
@@ -412,8 +455,9 @@ void init_video_input(void)
|
||||
init_shader_dir();
|
||||
#endif
|
||||
|
||||
// We use at least 512x512 textures to accomodate for hi-res games.
|
||||
unsigned scale = 2;
|
||||
unsigned max_dim = max(g_extern.system.geom.max_width, g_extern.system.geom.max_height);
|
||||
unsigned scale = max_dim / SSNES_SCALE_BASE;
|
||||
scale = max(scale, 1);
|
||||
|
||||
find_video_driver();
|
||||
find_input_driver();
|
||||
@@ -430,18 +474,26 @@ void init_video_input(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_settings.video.force_aspect)
|
||||
if (g_settings.video.force_aspect && (g_settings.video.aspect_ratio > 0.0f))
|
||||
{
|
||||
width = roundf(g_settings.video.base_size * g_settings.video.xscale * g_settings.video.aspect_ratio);
|
||||
height = roundf(g_settings.video.base_size * g_settings.video.yscale);
|
||||
width = roundf(g_extern.system.geom.base_height * g_settings.video.xscale * g_settings.video.aspect_ratio);
|
||||
height = roundf(g_extern.system.geom.base_height * g_settings.video.yscale);
|
||||
}
|
||||
else
|
||||
{
|
||||
width = roundf(8.0f / 7.0f * g_settings.video.base_size * g_settings.video.xscale); // Assume 8:7 aspect.
|
||||
height = roundf(g_settings.video.base_size * g_settings.video.yscale);
|
||||
width = roundf(g_extern.system.geom.base_width * g_settings.video.xscale);
|
||||
height = roundf(g_extern.system.geom.base_height * g_settings.video.yscale);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_settings.video.aspect_ratio < 0.0f)
|
||||
{
|
||||
g_settings.video.aspect_ratio = (float)g_extern.system.geom.base_width / g_extern.system.geom.base_height;
|
||||
SSNES_LOG("Adjusting aspect ratio to %.2f\n", g_settings.video.aspect_ratio);
|
||||
}
|
||||
|
||||
SSNES_LOG("Video @ %ux%u\n", width, height);
|
||||
|
||||
video_info_t video = {
|
||||
.width = width,
|
||||
.height = height,
|
||||
|
||||
@@ -26,9 +26,11 @@
|
||||
#include <unistd.h>
|
||||
#include "input/keysym.h"
|
||||
|
||||
// SNES has 12 buttons from 0-11 (libsnes.hpp)
|
||||
#define SSNES_FIRST_META_KEY 12
|
||||
enum
|
||||
{
|
||||
SSNES_FAST_FORWARD_KEY = 12, // SNES has 12 buttons from 0-11 (libsnes.hpp)
|
||||
SSNES_FAST_FORWARD_KEY = SSNES_FIRST_META_KEY,
|
||||
SSNES_FAST_FORWARD_HOLD_KEY,
|
||||
SSNES_LOAD_STATE_KEY,
|
||||
SSNES_SAVE_STATE_KEY,
|
||||
@@ -77,12 +79,12 @@ typedef struct video_info
|
||||
|
||||
typedef struct audio_driver
|
||||
{
|
||||
void* (*init)(const char* device, unsigned rate, unsigned latency);
|
||||
ssize_t (*write)(void* data, const void* buf, size_t size);
|
||||
bool (*stop)(void* data);
|
||||
bool (*start)(void* data);
|
||||
void (*set_nonblock_state)(void* data, bool toggle); // Should we care about blocking in audio thread? Fast forwarding.
|
||||
void (*free)(void* data);
|
||||
void *(*init)(const char *device, unsigned rate, unsigned latency);
|
||||
ssize_t (*write)(void *data, const void *buf, size_t size);
|
||||
bool (*stop)(void *data);
|
||||
bool (*start)(void *data);
|
||||
void (*set_nonblock_state)(void *data, bool toggle); // Should we care about blocking in audio thread? Fast forwarding.
|
||||
void (*free)(void *data);
|
||||
bool (*use_float)(void *data); // Defines if driver will take standard floating point samples, or int16_t samples.
|
||||
const char *ident;
|
||||
} audio_driver_t;
|
||||
@@ -108,25 +110,25 @@ typedef struct audio_driver
|
||||
|
||||
typedef struct input_driver
|
||||
{
|
||||
void* (*init)(void);
|
||||
void (*poll)(void* data);
|
||||
int16_t (*input_state)(void* data, const struct snes_keybind **snes_keybinds, bool port, unsigned device, unsigned index, unsigned id);
|
||||
bool (*key_pressed)(void* data, int key);
|
||||
void (*free)(void* data);
|
||||
void *(*init)(void);
|
||||
void (*poll)(void *data);
|
||||
int16_t (*input_state)(void *data, const struct snes_keybind **snes_keybinds, bool port, unsigned device, unsigned index, unsigned id);
|
||||
bool (*key_pressed)(void *data, int key);
|
||||
void (*free)(void *data);
|
||||
const char *ident;
|
||||
} input_driver_t;
|
||||
|
||||
typedef struct video_driver
|
||||
{
|
||||
void* (*init)(const video_info_t *video, const input_driver_t **input, void **input_data);
|
||||
void *(*init)(const video_info_t *video, const input_driver_t **input, void **input_data);
|
||||
// Should the video driver act as an input driver as well? :) The video init might preinitialize an input driver to override the settings in case the video driver relies on input driver for event handling, e.g.
|
||||
bool (*frame)(void* data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg); // msg is for showing a message on the screen along with the video frame.
|
||||
void (*set_nonblock_state)(void* data, bool toggle); // Should we care about syncing to vblank? Fast forwarding.
|
||||
bool (*frame)(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg); // msg is for showing a message on the screen along with the video frame.
|
||||
void (*set_nonblock_state)(void *data, bool toggle); // Should we care about syncing to vblank? Fast forwarding.
|
||||
// Is the window still active?
|
||||
bool (*alive)(void *data);
|
||||
bool (*focus)(void *data); // Does the window have focus?
|
||||
bool (*xml_shader)(void *data, const char *path); // Sets XML-shader. Might not be implemented.
|
||||
void (*free)(void* data);
|
||||
void (*free)(void *data);
|
||||
const char *ident;
|
||||
} video_driver_t;
|
||||
|
||||
|
||||
@@ -96,6 +96,10 @@ unsigned (*psnes_get_memory_size)(unsigned);
|
||||
void (*psnes_unload_cartridge)(void);
|
||||
void (*psnes_term)(void);
|
||||
|
||||
#ifdef HAVE_DYLIB
|
||||
static void set_environment(void);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DYNAMIC
|
||||
static void load_dynamic(void)
|
||||
{
|
||||
@@ -181,29 +185,22 @@ static void set_statics(void)
|
||||
|
||||
void init_dlsym(void)
|
||||
{
|
||||
// Guarantee that we can do "dirty" casting. Every OS that this program supports should pass this ...
|
||||
// Guarantee that we can do "dirty" casting.
|
||||
// Every OS that this program supports should pass this ...
|
||||
assert(sizeof(void*) == sizeof(void (*)(void)));
|
||||
|
||||
#ifdef HAVE_DYNAMIC
|
||||
|
||||
#ifndef _WIN32 // Try to verify that -lsnes was not linked in from other modules
|
||||
// since loading it dynamically and with -l will fail hard.
|
||||
void *lib = dlopen(NULL, RTLD_LAZY);
|
||||
if (lib)
|
||||
// Try to verify that -lsnes was not linked in from other modules
|
||||
// since loading it dynamically and with -l will fail hard.
|
||||
function_t sym = dylib_proc(NULL, "snes_init");
|
||||
if (sym)
|
||||
{
|
||||
void *sym = dlsym(lib, "snes_init");
|
||||
if (sym)
|
||||
{
|
||||
SSNES_ERR("Serious problem! SSNES wants to load libsnes dyamically, but it is already linked!\n");
|
||||
SSNES_ERR("This could happen if other modules SSNES depends on link against libsnes directly.\n");
|
||||
SSNES_ERR("Proceeding could cause a crash! Aborting ...\n");
|
||||
dlclose(lib);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
dlclose(lib);
|
||||
SSNES_ERR("Serious problem! SSNES wants to load libsnes dyamically, but it is already linked!\n");
|
||||
SSNES_ERR("This could happen if other modules SSNES depends on link against libsnes directly.\n");
|
||||
SSNES_ERR("Proceeding could cause a crash! Aborting ...\n");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!*g_settings.libsnes)
|
||||
{
|
||||
@@ -220,6 +217,10 @@ void init_dlsym(void)
|
||||
#else
|
||||
set_statics();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DYLIB
|
||||
set_environment();
|
||||
#endif
|
||||
}
|
||||
|
||||
void uninit_dlsym(void)
|
||||
@@ -243,16 +244,26 @@ dylib_t dylib_load(const char *path)
|
||||
function_t dylib_proc(dylib_t lib, const char *proc)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
function_t sym = (function_t)GetProcAddress(lib, proc);
|
||||
function_t sym = (function_t)GetProcAddress(lib ? lib : GetModuleHandle(NULL), proc);
|
||||
#else
|
||||
// Dirty hack to workaround the non-legality of void* -> fn-pointer casts.
|
||||
void *ptr_sym = dlsym(lib, proc);
|
||||
void *ptr_sym = NULL;
|
||||
if (lib)
|
||||
ptr_sym = dlsym(lib, proc);
|
||||
else
|
||||
{
|
||||
void *handle = dlopen(NULL, RTLD_LAZY);
|
||||
if (handle)
|
||||
{
|
||||
ptr_sym = dlsym(handle, proc);
|
||||
dlclose(handle);
|
||||
}
|
||||
}
|
||||
|
||||
// Dirty hack to workaround the non-legality of (void*) -> fn-pointer casts.
|
||||
function_t sym;
|
||||
memcpy(&sym, &ptr_sym, sizeof(void*));
|
||||
#endif
|
||||
|
||||
if (!sym)
|
||||
SSNES_WARN("Failed to load symbol \"%s\"\n", proc);
|
||||
return sym;
|
||||
}
|
||||
|
||||
@@ -265,3 +276,82 @@ void dylib_close(dylib_t lib)
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool environment_cb(unsigned cmd, void *data)
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case SNES_ENVIRONMENT_GET_FULLPATH:
|
||||
*(const char**)data = g_extern.system.fullpath;
|
||||
SSNES_LOG("FULLPATH: \"%s\"\n", g_extern.system.fullpath);
|
||||
break;
|
||||
|
||||
case SNES_ENVIRONMENT_SET_GEOMETRY:
|
||||
g_extern.system.geom = *(const struct snes_geometry*)data;
|
||||
g_extern.system.geom.max_width = next_pow2(g_extern.system.geom.max_width);
|
||||
g_extern.system.geom.max_height = next_pow2(g_extern.system.geom.max_height);
|
||||
SSNES_LOG("SET_GEOMETRY: (%ux%u) / (%ux%u)\n",
|
||||
g_extern.system.geom.base_width,
|
||||
g_extern.system.geom.base_height,
|
||||
g_extern.system.geom.max_width,
|
||||
g_extern.system.geom.max_height);
|
||||
break;
|
||||
|
||||
case SNES_ENVIRONMENT_SET_PITCH:
|
||||
g_extern.system.pitch = *(const unsigned*)data;
|
||||
SSNES_LOG("SET_PITCH: %u\n", g_extern.system.pitch);
|
||||
break;
|
||||
|
||||
case SNES_ENVIRONMENT_GET_OVERSCAN:
|
||||
*(bool*)data = !g_settings.video.crop_overscan;
|
||||
SSNES_LOG("GET_OVERSCAN: %u\n", (unsigned)!g_settings.video.crop_overscan);
|
||||
break;
|
||||
|
||||
case SNES_ENVIRONMENT_SET_TIMING:
|
||||
g_extern.system.timing = *(const struct snes_system_timing*)data;
|
||||
g_extern.system.timing_set = true;
|
||||
break;
|
||||
|
||||
case SNES_ENVIRONMENT_GET_CAN_DUPE:
|
||||
*(bool*)data = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DYLIB
|
||||
// Assume SNES as defaults.
|
||||
static void set_environment_defaults(void)
|
||||
{
|
||||
g_extern.system.pitch = 0; // 0 is classic libsnes semantics.
|
||||
g_extern.system.geom = (struct snes_geometry) {
|
||||
.base_width = 256,
|
||||
.base_height = 224,
|
||||
.max_width = 512,
|
||||
.max_height = 512,
|
||||
};
|
||||
}
|
||||
|
||||
// SSNES extension hooks. Totally optional 'n shizz :)
|
||||
static void set_environment(void)
|
||||
{
|
||||
#ifdef HAVE_DYNAMIC
|
||||
dylib_t lib = lib_handle;
|
||||
#else
|
||||
dylib_t lib = NULL;
|
||||
#endif
|
||||
|
||||
void (*psnes_set_environment)(snes_environment_t) =
|
||||
(void (*)(snes_environment_t))dylib_proc(lib, "snes_set_environment");
|
||||
|
||||
if (psnes_set_environment)
|
||||
psnes_set_environment(environment_cb);
|
||||
|
||||
set_environment_defaults();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ extern void (*psnes_set_input_state)(snes_input_state_t);
|
||||
extern void (*psnes_cheat_reset)(void);
|
||||
extern void (*psnes_cheat_set)(unsigned, bool, const char*);
|
||||
|
||||
extern const char* (*psnes_library_id)(void);
|
||||
extern const char *(*psnes_library_id)(void);
|
||||
extern unsigned (*psnes_library_revision_minor)(void);
|
||||
extern unsigned (*psnes_library_revision_major)(void);
|
||||
|
||||
|
||||
+11
-10
@@ -14,16 +14,17 @@
|
||||
*/
|
||||
|
||||
#include "fifo_buffer.h"
|
||||
#include <stdint.h>
|
||||
|
||||
struct fifo_buffer
|
||||
{
|
||||
char *buffer;
|
||||
uint8_t *buffer;
|
||||
size_t bufsize;
|
||||
size_t first;
|
||||
size_t end;
|
||||
};
|
||||
|
||||
fifo_buffer_t* fifo_new(size_t size)
|
||||
fifo_buffer_t *fifo_new(size_t size)
|
||||
{
|
||||
fifo_buffer_t *buf = calloc(1, sizeof(*buf));
|
||||
if (buf == NULL)
|
||||
@@ -40,13 +41,13 @@ fifo_buffer_t* fifo_new(size_t size)
|
||||
return buf;
|
||||
}
|
||||
|
||||
void fifo_free(fifo_buffer_t* buffer)
|
||||
void fifo_free(fifo_buffer_t *buffer)
|
||||
{
|
||||
free(buffer->buffer);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
size_t fifo_read_avail(fifo_buffer_t* buffer)
|
||||
size_t fifo_read_avail(fifo_buffer_t *buffer)
|
||||
{
|
||||
size_t first = buffer->first;
|
||||
size_t end = buffer->end;
|
||||
@@ -55,7 +56,7 @@ size_t fifo_read_avail(fifo_buffer_t* buffer)
|
||||
return end - first;
|
||||
}
|
||||
|
||||
size_t fifo_write_avail(fifo_buffer_t* buffer)
|
||||
size_t fifo_write_avail(fifo_buffer_t *buffer)
|
||||
{
|
||||
size_t first = buffer->first;
|
||||
size_t end = buffer->end;
|
||||
@@ -65,7 +66,7 @@ size_t fifo_write_avail(fifo_buffer_t* buffer)
|
||||
return (buffer->bufsize - 1) - (end - first);
|
||||
}
|
||||
|
||||
void fifo_write(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)
|
||||
{
|
||||
size_t first_write = size;
|
||||
size_t rest_write = 0;
|
||||
@@ -77,13 +78,13 @@ void fifo_write(fifo_buffer_t* buffer, const void* in_buf, size_t size)
|
||||
|
||||
memcpy(buffer->buffer + buffer->end, in_buf, first_write);
|
||||
if (rest_write > 0)
|
||||
memcpy(buffer->buffer, (const char*)in_buf + first_write, rest_write);
|
||||
memcpy(buffer->buffer, (const uint8_t*)in_buf + first_write, rest_write);
|
||||
|
||||
buffer->end = (buffer->end + size) % buffer->bufsize;
|
||||
}
|
||||
|
||||
|
||||
void fifo_read(fifo_buffer_t* buffer, void* in_buf, size_t size)
|
||||
void fifo_read(fifo_buffer_t *buffer, void *in_buf, size_t size)
|
||||
{
|
||||
size_t first_read = size;
|
||||
size_t rest_read = 0;
|
||||
@@ -93,9 +94,9 @@ void fifo_read(fifo_buffer_t* buffer, void* in_buf, size_t size)
|
||||
rest_read = size - first_read;
|
||||
}
|
||||
|
||||
memcpy(in_buf, (const char*)buffer->buffer + buffer->first, first_read);
|
||||
memcpy(in_buf, (const uint8_t*)buffer->buffer + buffer->first, first_read);
|
||||
if (rest_read > 0)
|
||||
memcpy((char*)in_buf + first_read, buffer->buffer, rest_read);
|
||||
memcpy((uint8_t*)in_buf + first_read, buffer->buffer, rest_read);
|
||||
|
||||
buffer->first = (buffer->first + size) % buffer->bufsize;
|
||||
}
|
||||
|
||||
+6
-6
@@ -26,11 +26,11 @@
|
||||
typedef struct fifo_buffer fifo_buffer_t;
|
||||
#endif
|
||||
|
||||
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);
|
||||
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
|
||||
@@ -510,7 +510,7 @@ void load_ram_file(const char *path, int type)
|
||||
void *buf = NULL;
|
||||
ssize_t rc = read_file(path, &buf);
|
||||
if (rc <= size)
|
||||
memcpy(data, buf, size);
|
||||
memcpy(data, buf, rc);
|
||||
|
||||
free(buf);
|
||||
}
|
||||
@@ -531,7 +531,7 @@ void save_ram_file(const char *path, int type)
|
||||
}
|
||||
}
|
||||
|
||||
static char* load_xml_map(const char *path)
|
||||
static char *load_xml_map(const char *path)
|
||||
{
|
||||
char *xml_buf = NULL;
|
||||
if (*path)
|
||||
@@ -801,7 +801,7 @@ bool init_rom_file(enum ssnes_game_type type)
|
||||
}
|
||||
|
||||
// Yep, this is C alright ;)
|
||||
char** dir_list_new(const char *dir, const char *ext)
|
||||
char **dir_list_new(const char *dir, const char *ext)
|
||||
{
|
||||
size_t cur_ptr = 0;
|
||||
size_t cur_size = 32;
|
||||
|
||||
@@ -41,7 +41,7 @@ bool init_rom_file(enum ssnes_game_type type);
|
||||
// Returns a NULL-terminated list of files in a directory with full paths.
|
||||
// If ext is NULL, any file will be picked.
|
||||
// If non-NULL, only files with extension ext are added.
|
||||
char** dir_list_new(const char *dir, const char *ext);
|
||||
char **dir_list_new(const char *dir, const char *ext);
|
||||
void dir_list_free(char **dir_list);
|
||||
|
||||
bool path_is_directory(const char *path);
|
||||
|
||||
@@ -74,7 +74,6 @@ struct settings
|
||||
char driver[32];
|
||||
float xscale;
|
||||
float yscale;
|
||||
unsigned base_size;
|
||||
bool fullscreen;
|
||||
unsigned fullscreen_x;
|
||||
unsigned fullscreen_y;
|
||||
@@ -87,6 +86,7 @@ struct settings
|
||||
char bsnes_shader_path[MAXPATHLEN];
|
||||
char filter_path[MAXPATHLEN];
|
||||
enum ssnes_shader_type shader_type;
|
||||
float refresh_rate;
|
||||
|
||||
bool render_to_texture;
|
||||
double fbo_scale_x;
|
||||
@@ -97,6 +97,7 @@ struct settings
|
||||
|
||||
char font_path[MAXPATHLEN];
|
||||
unsigned font_size;
|
||||
bool font_enable;
|
||||
float msg_pos_x;
|
||||
float msg_pos_y;
|
||||
float msg_color_r;
|
||||
@@ -143,7 +144,7 @@ struct settings
|
||||
char screenshot_directory[MAXPATHLEN];
|
||||
|
||||
bool rewind_enable;
|
||||
unsigned rewind_buffer_size;
|
||||
size_t rewind_buffer_size;
|
||||
unsigned rewind_granularity;
|
||||
|
||||
bool pause_nonactive;
|
||||
@@ -208,6 +209,15 @@ struct global
|
||||
|
||||
unsigned state_slot;
|
||||
|
||||
struct
|
||||
{
|
||||
struct snes_geometry geom;
|
||||
unsigned pitch; // If 0, has classic libsnes semantics.
|
||||
char fullpath[MAXPATHLEN];
|
||||
struct snes_system_timing timing;
|
||||
bool timing_set;
|
||||
} system;
|
||||
|
||||
struct
|
||||
{
|
||||
hermite_resampler_t *source;
|
||||
@@ -255,11 +265,22 @@ struct global
|
||||
void *state_buf;
|
||||
bool frame_is_reverse;
|
||||
|
||||
// Movie record support
|
||||
bsv_movie_t *bsv_movie;
|
||||
char bsv_movie_path[MAXPATHLEN];
|
||||
bool bsv_movie_end;
|
||||
bool bsv_movie_playback;
|
||||
// Movie playback/recording support.
|
||||
struct
|
||||
{
|
||||
bsv_movie_t *movie;
|
||||
char movie_path[MAXPATHLEN];
|
||||
bool movie_playback;
|
||||
|
||||
// Immediate playback/recording.
|
||||
char movie_start_path[MAXPATHLEN];
|
||||
bool movie_start_recording;
|
||||
bool movie_start_playback;
|
||||
bool movie_end;
|
||||
} bsv;
|
||||
|
||||
bool sram_load_disable;
|
||||
bool sram_save_disable;
|
||||
|
||||
// Pausing support
|
||||
bool is_paused;
|
||||
@@ -296,15 +317,13 @@ struct global
|
||||
|
||||
struct
|
||||
{
|
||||
char** elems;
|
||||
char **elems;
|
||||
size_t size;
|
||||
size_t ptr;
|
||||
} shader_dir;
|
||||
|
||||
char sha256[64 + 1];
|
||||
|
||||
bool do_screenshot;
|
||||
|
||||
#ifdef HAVE_XML
|
||||
cheat_manager_t *cheat;
|
||||
#endif
|
||||
@@ -331,6 +350,16 @@ extern struct global g_extern;
|
||||
fflush(stderr); \
|
||||
} while (0)
|
||||
|
||||
#ifndef max
|
||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef min
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#define SSNES_SCALE_BASE 256
|
||||
|
||||
static inline uint32_t next_pow2(uint32_t v)
|
||||
{
|
||||
v--;
|
||||
|
||||
@@ -31,6 +31,11 @@
|
||||
#include "fonts.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
#define PY_STATE_OMIT_DECLARATION
|
||||
#include "py_state/py_state.h"
|
||||
#endif
|
||||
|
||||
static bool g_input_dead = true;
|
||||
static bool g_video_dead = true;
|
||||
static dylib_t g_lib = NULL;
|
||||
@@ -43,7 +48,7 @@ typedef struct
|
||||
void *handle;
|
||||
} input_ext_t;
|
||||
|
||||
static void* input_ext_init(void)
|
||||
static void *input_ext_init(void)
|
||||
{
|
||||
g_input_dead = false;
|
||||
return calloc(1, sizeof(input_ext_t));
|
||||
@@ -199,7 +204,7 @@ static bool video_ext_frame(void *data, const void *frame, unsigned width, unsig
|
||||
return ext->driver->frame(ext->handle, frame, width, height, pitch, msg);
|
||||
}
|
||||
|
||||
static void* setup_input(ext_t *ext, const ssnes_input_driver_t *driver)
|
||||
static void *setup_input(ext_t *ext, const ssnes_input_driver_t *driver)
|
||||
{
|
||||
int joypad_index[5];
|
||||
for (unsigned i = 0; i < 5; i++)
|
||||
@@ -246,15 +251,18 @@ static bool setup_video(ext_t *ext, const video_info_t *video, const input_drive
|
||||
font_color_b = font_color_b > 255 ? 255 : (font_color_b < 0 ? 0 : font_color_b);
|
||||
|
||||
const char *font = NULL;
|
||||
if (g_settings.video.font_enable)
|
||||
{
|
||||
#ifdef HAVE_FREETYPE
|
||||
if (*g_settings.video.font_path)
|
||||
font = g_settings.video.font_path;
|
||||
else
|
||||
font = font_renderer_get_default_font();
|
||||
if (*g_settings.video.font_path)
|
||||
font = g_settings.video.font_path;
|
||||
else
|
||||
font = font_renderer_get_default_font();
|
||||
#else
|
||||
font = *g_settings.video.font_path ?
|
||||
g_settings.video.font_path : NULL;
|
||||
font = *g_settings.video.font_path ?
|
||||
g_settings.video.font_path : NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
char title_buf[128];
|
||||
gfx_window_title_reset();
|
||||
@@ -276,6 +284,12 @@ static bool setup_video(ext_t *ext, const video_info_t *video, const input_drive
|
||||
.ttf_font_size = g_settings.video.font_size,
|
||||
.ttf_font_color = (font_color_r << 16) | (font_color_g << 8) | (font_color_b << 0),
|
||||
.title_hint = title_buf,
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
.python_state_new = py_state_new,
|
||||
.python_state_get = py_state_get,
|
||||
.python_state_free = py_state_free,
|
||||
#endif
|
||||
};
|
||||
|
||||
const ssnes_input_driver_t *input_driver = NULL;
|
||||
@@ -292,7 +306,7 @@ static bool setup_video(ext_t *ext, const video_info_t *video, const input_drive
|
||||
return true;
|
||||
}
|
||||
|
||||
static void* video_ext_init(const video_info_t *video, const input_driver_t **input, void **input_data)
|
||||
static void *video_ext_init(const video_info_t *video, const input_driver_t **input, void **input_data)
|
||||
{
|
||||
ext_t *ext = calloc(1, sizeof(*ext));
|
||||
if (!ext)
|
||||
|
||||
+37
-14
@@ -6,6 +6,8 @@
|
||||
#ifndef __SSNES_VIDEO_DRIVER_H
|
||||
#define __SSNES_VIDEO_DRIVER_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -22,10 +24,10 @@ extern "C" {
|
||||
#define SSNES_API_CALLTYPE
|
||||
#endif
|
||||
|
||||
#define SSNES_GRAPHICS_API_VERSION 1
|
||||
#define SSNES_GRAPHICS_API_VERSION 2
|
||||
|
||||
// Since we don't want to rely on C++ or C99 for a proper boolean type,
|
||||
// and make sure return semantics are perfectly clear ... ;)
|
||||
// make sure return semantics are perfectly clear ... ;)
|
||||
|
||||
#ifndef SSNES_OK
|
||||
#define SSNES_OK 1
|
||||
@@ -46,6 +48,23 @@ extern "C" {
|
||||
#define SSNES_COLOR_FORMAT_XRGB1555 0
|
||||
#define SSNES_COLOR_FORMAT_ARGB8888 1
|
||||
|
||||
#define SSNES_INPUT_SCALE_BASE 256
|
||||
|
||||
typedef struct py_state py_state_t;
|
||||
|
||||
// Create a new runtime for Python.
|
||||
// py_script: The python script to be loaded. If is_file is true, this will be the full path to a file.
|
||||
// If false, it will be an UTF-8 encoded string of the script.
|
||||
// is_file: Tells if py_script is the path to a script, or a script itself.
|
||||
// py_class: name of the class being instantiated.
|
||||
typedef py_state_t *(*python_state_new_cb)(const char *py_script, unsigned is_file, const char *py_class);
|
||||
// Grabs a value from the Python runtime.
|
||||
// id: The uniform (class method) to be called.
|
||||
// frame_count: Passes frame_count as an argument to the script.
|
||||
typedef float (*python_state_get_cb)(py_state_t *handle, const char *id, unsigned frame_count);
|
||||
// Frees the runtime.
|
||||
typedef void (*python_state_free_cb)(py_state_t *handle);
|
||||
|
||||
typedef struct ssnes_video_info
|
||||
{
|
||||
// Width of window.
|
||||
@@ -79,12 +98,9 @@ typedef struct ssnes_video_info
|
||||
|
||||
// input_scale defines the maximum size of the picture that will
|
||||
// ever be used with the frame callback.
|
||||
// The maximum resolution is a multiple of 256x256 size,
|
||||
// The maximum resolution is a multiple of 256x256 size (SSNES_INPUT_SCALE_BASE),
|
||||
// so an input scale of 2
|
||||
// means you should allocate a texture or of 512x512.
|
||||
// It is normally set to 2 due to the possibility of
|
||||
// pseudo-hires on the SNES.
|
||||
// CPU filters or otherwise might increase this value.
|
||||
unsigned input_scale;
|
||||
|
||||
// Defines the coloring format used of the input frame.
|
||||
@@ -112,6 +128,13 @@ typedef struct ssnes_video_info
|
||||
|
||||
// A title that should be displayed in the title bar of the window.
|
||||
const char *title_hint;
|
||||
|
||||
// Functions to peek into the python runtime for shaders.
|
||||
// Check typedefs above for explanation.
|
||||
// These may be NULL if SSNES is not built with Python support.
|
||||
python_state_new_cb python_state_new;
|
||||
python_state_get_cb python_state_get;
|
||||
python_state_free_cb python_state_free;
|
||||
} ssnes_video_info_t;
|
||||
|
||||
// Some convenience macros.
|
||||
@@ -181,20 +204,20 @@ typedef struct ssnes_input_driver
|
||||
// The range of this is [0, 1],
|
||||
// where 0 means any displacement will register,
|
||||
// and 1 means the axis has to be pressed all the way to register.
|
||||
void* (*init)(const int joypad_index[5], float axis_threshold);
|
||||
void *(*init)(const int joypad_index[5], float axis_threshold);
|
||||
|
||||
// Polls input. Called once every frame.
|
||||
void (*poll)(void* data);
|
||||
void (*poll)(void *data);
|
||||
|
||||
// Queries input state for a certain key on a certain player.
|
||||
// Players are 1 - 5.
|
||||
// For digital inputs, pressed key is 1, not pressed key is 0.
|
||||
// Analog values have same range as a signed 16-bit integer.
|
||||
int (*input_state)(void* data, const struct ssnes_keybind *bind,
|
||||
int (*input_state)(void *data, const struct ssnes_keybind *bind,
|
||||
unsigned player);
|
||||
|
||||
// Frees the input struct.
|
||||
void (*free)(void* data);
|
||||
void (*free)(void *data);
|
||||
|
||||
// Human readable indentification string.
|
||||
const char *ident;
|
||||
@@ -208,7 +231,7 @@ typedef struct ssnes_video_driver
|
||||
// Should the video driver request that a certain input driver is used,
|
||||
// it is possible to set the driver to *input.
|
||||
// If no certain driver is desired, set *input to NULL.
|
||||
void* (*init)(const ssnes_video_info_t *video,
|
||||
void *(*init)(const ssnes_video_info_t *video,
|
||||
const ssnes_input_driver_t **input);
|
||||
|
||||
// Updates frame on the screen.
|
||||
@@ -218,13 +241,13 @@ typedef struct ssnes_video_driver
|
||||
//
|
||||
// When msg is non-NULL,
|
||||
// it's a message that should be displayed to the user.
|
||||
int (*frame)(void* data, const void* frame,
|
||||
int (*frame)(void *data, const void *frame,
|
||||
unsigned width, unsigned height, unsigned pitch, const char *msg);
|
||||
|
||||
// Requests nonblocking operation.
|
||||
// True = VSync is turned off.
|
||||
// False = VSync is turned on.
|
||||
void (*set_nonblock_state)(void* data, int toggle);
|
||||
void (*set_nonblock_state)(void *data, int toggle);
|
||||
|
||||
// This must return false when the user exits the emulator.
|
||||
int (*alive)(void *data);
|
||||
@@ -233,7 +256,7 @@ typedef struct ssnes_video_driver
|
||||
int (*focus)(void *data);
|
||||
|
||||
// Frees the video driver.
|
||||
void (*free)(void* data);
|
||||
void (*free)(void *data);
|
||||
|
||||
// A human-readable identification of the video driver.
|
||||
const char *ident;
|
||||
|
||||
@@ -142,6 +142,8 @@ static const char *font_paths[] = {
|
||||
#elif defined(__APPLE__)
|
||||
static const char *font_paths[] = {
|
||||
"/Library/Fonts/Microsoft/Candara.ttf",
|
||||
"/Library/Fonts/Verdana.ttf",
|
||||
"/Library/Fonts/Tahoma.ttf",
|
||||
#else
|
||||
static const char *font_paths[] = {
|
||||
"/usr/share/fonts/TTF/DejaVuSansMono.ttf",
|
||||
|
||||
@@ -78,6 +78,13 @@ static const GLfloat tex_coords[] = {
|
||||
1, 1
|
||||
};
|
||||
|
||||
static const GLfloat white_color[] = {
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
};
|
||||
|
||||
|
||||
#ifdef HAVE_FBO
|
||||
#ifdef _WIN32
|
||||
@@ -124,12 +131,19 @@ static inline bool load_gl_proc(void) { return true; }
|
||||
|
||||
#define MAX_SHADERS 16
|
||||
|
||||
#if defined(HAVE_XML) || defined(HAVE_CG)
|
||||
#define TEXTURES 8
|
||||
#else
|
||||
#define TEXTURES 1
|
||||
#endif
|
||||
#define TEXTURES_MASK (TEXTURES - 1)
|
||||
|
||||
typedef struct gl
|
||||
{
|
||||
bool vsync;
|
||||
GLuint texture[2];
|
||||
GLuint texture[TEXTURES];
|
||||
unsigned tex_index; // For use with PREV.
|
||||
struct gl_tex_info prev_info;
|
||||
struct gl_tex_info prev_info[TEXTURES];
|
||||
GLuint tex_filter;
|
||||
|
||||
void *empty_buf;
|
||||
@@ -158,8 +172,8 @@ typedef struct gl
|
||||
unsigned win_height;
|
||||
unsigned vp_width, vp_out_width;
|
||||
unsigned vp_height, vp_out_height;
|
||||
unsigned last_width[2];
|
||||
unsigned last_height[2];
|
||||
unsigned last_width[TEXTURES];
|
||||
unsigned last_height[TEXTURES];
|
||||
unsigned tex_w, tex_h;
|
||||
GLfloat tex_coords[8];
|
||||
#ifdef HAVE_FBO
|
||||
@@ -177,6 +191,7 @@ typedef struct gl
|
||||
void *font_tex_empty_buf;
|
||||
char font_last_msg[256];
|
||||
int font_last_width, font_last_height;
|
||||
GLfloat font_color[16];
|
||||
#endif
|
||||
|
||||
} gl_t;
|
||||
@@ -337,6 +352,9 @@ static void gl_shader_scale(unsigned index, struct gl_fbo_scale *scale)
|
||||
static inline void gl_init_font(gl_t *gl, const char *font_path, unsigned font_size)
|
||||
{
|
||||
#ifdef HAVE_FREETYPE
|
||||
if (!g_settings.video.font_enable)
|
||||
return;
|
||||
|
||||
const char *path = font_path;
|
||||
if (!*path)
|
||||
path = font_renderer_get_default_font();
|
||||
@@ -359,6 +377,15 @@ static inline void gl_init_font(gl_t *gl, const char *font_path, unsigned font_s
|
||||
}
|
||||
else
|
||||
SSNES_LOG("Did not find default font.\n");
|
||||
|
||||
for (unsigned i = 0; i < 4; i++)
|
||||
{
|
||||
gl->font_color[4 * i + 0] = g_settings.video.msg_color_r;
|
||||
gl->font_color[4 * i + 1] = g_settings.video.msg_color_g;
|
||||
gl->font_color[4 * i + 2] = g_settings.video.msg_color_b;
|
||||
gl->font_color[4 * i + 3] = 1.0;
|
||||
}
|
||||
|
||||
#else
|
||||
(void)gl;
|
||||
(void)font_path;
|
||||
@@ -764,14 +791,13 @@ static void gl_render_msg(gl_t *gl, const char *msg)
|
||||
gl_shader_use(0);
|
||||
set_viewport(gl, gl->win_width, gl->win_height, false);
|
||||
glBindTexture(GL_TEXTURE_2D, gl->font_tex);
|
||||
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), font_vertex);
|
||||
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), font_tex_coords);
|
||||
glVertexPointer(2, GL_FLOAT, 0, font_vertex);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, font_tex_coords);
|
||||
glColorPointer(4, GL_FLOAT, 0, gl->font_color);
|
||||
|
||||
// Need blending.
|
||||
// Using fixed function pipeline here since we cannot guarantee presence of shaders (would be kinda overkill anyways).
|
||||
glEnable(GL_BLEND);
|
||||
glColor4f(g_settings.video.msg_color_r, g_settings.video.msg_color_g,
|
||||
g_settings.video.msg_color_b, 1);
|
||||
|
||||
struct font_output_list out;
|
||||
|
||||
@@ -797,9 +823,9 @@ static void gl_render_msg(gl_t *gl, const char *msg)
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
|
||||
// Go back to old rendering path.
|
||||
glColor4f(1, 1, 1, 1);
|
||||
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->tex_coords);
|
||||
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes_flipped);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, gl->tex_coords);
|
||||
glVertexPointer(2, GL_FLOAT, 0, vertexes_flipped);
|
||||
glColorPointer(4, GL_FLOAT, 0, white_color);
|
||||
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
@@ -818,7 +844,7 @@ static inline void set_lut_texture_coords(const GLfloat *coords)
|
||||
// For texture images.
|
||||
pglClientActiveTexture(GL_TEXTURE1);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), coords);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, coords);
|
||||
pglClientActiveTexture(GL_TEXTURE0);
|
||||
#else
|
||||
(void)coords;
|
||||
@@ -847,7 +873,7 @@ static void check_window(gl_t *gl)
|
||||
gl->should_resize = true;
|
||||
}
|
||||
|
||||
static bool gl_frame(void *data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
|
||||
static bool gl_frame(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
|
||||
{
|
||||
gl_t *gl = data;
|
||||
|
||||
@@ -987,7 +1013,8 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
|
||||
set_texture_coords(gl->tex_coords, xamt, yamt);
|
||||
}
|
||||
#if defined(HAVE_XML) || defined(HAVE_CG)
|
||||
else if (width != gl->last_width[1 - gl->tex_index] || height != gl->last_height[1 - gl->tex_index])
|
||||
// We might have used different texture coordinates last frame. Edge case if resolution changes very rapidly.
|
||||
else if (width != gl->last_width[(gl->tex_index - 1) & TEXTURES_MASK] || height != gl->last_height[(gl->tex_index - 1) & TEXTURES_MASK])
|
||||
{
|
||||
GLfloat xamt = (GLfloat)width / gl->tex_w;
|
||||
GLfloat yamt = (GLfloat)height / gl->tex_h;
|
||||
@@ -999,7 +1026,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
|
||||
// Need to preserve the "flipped" state when in FBO as well to have
|
||||
// consistent texture coordinates.
|
||||
if (gl->render_to_tex)
|
||||
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes);
|
||||
glVertexPointer(2, GL_FLOAT, 0, vertexes);
|
||||
#endif
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch / gl->base_size);
|
||||
@@ -1018,7 +1045,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
gl_shader_set_params(width, height, gl->tex_w, gl->tex_h, gl->vp_width, gl->vp_height, gl->frame_count,
|
||||
&tex_info, &gl->prev_info, fbo_tex_info, fbo_tex_info_cnt);
|
||||
&tex_info, gl->prev_info, fbo_tex_info, fbo_tex_info_cnt);
|
||||
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
|
||||
@@ -1026,7 +1053,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
|
||||
if (gl->fbo_inited)
|
||||
{
|
||||
// Render the rest of our passes.
|
||||
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->fbo_tex_coords);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, gl->fbo_tex_coords);
|
||||
|
||||
// It's kinda handy ... :)
|
||||
const struct gl_fbo_rect *prev_rect;
|
||||
@@ -1063,7 +1090,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
|
||||
gl_shader_set_params(prev_rect->img_width, prev_rect->img_height,
|
||||
prev_rect->width, prev_rect->height,
|
||||
gl->vp_width, gl->vp_height, gl->frame_count,
|
||||
&tex_info, &gl->prev_info, fbo_tex_info, fbo_tex_info_cnt);
|
||||
&tex_info, gl->prev_info, fbo_tex_info, fbo_tex_info_cnt);
|
||||
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
|
||||
@@ -1089,18 +1116,19 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
|
||||
gl_shader_set_params(prev_rect->img_width, prev_rect->img_height,
|
||||
prev_rect->width, prev_rect->height,
|
||||
gl->vp_width, gl->vp_height, gl->frame_count,
|
||||
&tex_info, &gl->prev_info, fbo_tex_info, fbo_tex_info_cnt);
|
||||
&tex_info, gl->prev_info, fbo_tex_info, fbo_tex_info_cnt);
|
||||
|
||||
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes_flipped);
|
||||
glVertexPointer(2, GL_FLOAT, 0, vertexes_flipped);
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
|
||||
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->tex_coords);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, gl->tex_coords);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_XML) || defined(HAVE_CG)
|
||||
memcpy(&gl->prev_info, &tex_info, sizeof(tex_info));
|
||||
gl->tex_index = 1 - gl->tex_index;
|
||||
memmove(gl->prev_info + 1, gl->prev_info, sizeof(tex_info) * (TEXTURES - 1));
|
||||
memcpy(&gl->prev_info[0], &tex_info, sizeof(tex_info));
|
||||
gl->tex_index = (gl->tex_index + 1) & TEXTURES_MASK;
|
||||
#endif
|
||||
|
||||
if (msg)
|
||||
@@ -1123,7 +1151,8 @@ static void gl_free(void *data)
|
||||
gl_shader_deinit();
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDeleteTextures(2, gl->texture);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDeleteTextures(TEXTURES, gl->texture);
|
||||
|
||||
#ifdef HAVE_FBO
|
||||
if (gl->fbo_inited)
|
||||
@@ -1150,7 +1179,7 @@ static void gl_set_nonblock_state(void *data, bool state)
|
||||
}
|
||||
}
|
||||
|
||||
static void* gl_init(const video_info_t *video, const input_driver_t **input, void **input_data)
|
||||
static void *gl_init(const video_info_t *video, const input_driver_t **input, void **input_data)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
gfx_set_dwm();
|
||||
@@ -1226,7 +1255,8 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
SSNES_LOG("GL: Loaded %u program(s).\n", gl_shader_num());
|
||||
|
||||
// Set up render to texture.
|
||||
gl_init_fbo(gl, 256 * video->input_scale, 256 * video->input_scale);
|
||||
gl_init_fbo(gl, SSNES_SCALE_BASE * video->input_scale,
|
||||
SSNES_SCALE_BASE * video->input_scale);
|
||||
|
||||
gl->keep_aspect = video->force_aspect;
|
||||
|
||||
@@ -1249,15 +1279,14 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_DITHER);
|
||||
glColor4f(1, 1, 1, 1);
|
||||
glClearColor(0, 0, 0, 1);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
glGenTextures(2, gl->texture);
|
||||
glGenTextures(TEXTURES, gl->texture);
|
||||
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
for (unsigned i = 0; i < TEXTURES; i++)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, gl->texture[i]);
|
||||
|
||||
@@ -1270,20 +1299,23 @@ 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_flipped);
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glVertexPointer(2, GL_FLOAT, 0, vertexes_flipped);
|
||||
|
||||
memcpy(gl->tex_coords, tex_coords, sizeof(tex_coords));
|
||||
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->tex_coords);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, gl->tex_coords);
|
||||
|
||||
glColorPointer(4, GL_FLOAT, 0, white_color);
|
||||
|
||||
set_lut_texture_coords(tex_coords);
|
||||
|
||||
gl->tex_w = 256 * video->input_scale;
|
||||
gl->tex_h = 256 * video->input_scale;
|
||||
gl->tex_w = SSNES_SCALE_BASE * video->input_scale;
|
||||
gl->tex_h = SSNES_SCALE_BASE * video->input_scale;
|
||||
|
||||
// Empty buffer that we use to clear out the texture with on res change.
|
||||
gl->empty_buf = calloc(gl->tex_w * gl->tex_h, gl->base_size);
|
||||
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
for (unsigned i = 0; i < TEXTURES; i++)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, gl->texture[i]);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, gl->tex_w);
|
||||
@@ -1293,18 +1325,21 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
|
||||
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
for (unsigned i = 0; i < TEXTURES; i++)
|
||||
{
|
||||
gl->last_width[i] = gl->tex_w;
|
||||
gl->last_height[i] = gl->tex_h;
|
||||
}
|
||||
|
||||
gl->prev_info.tex = gl->texture[1 - gl->tex_index];
|
||||
gl->prev_info.input_size[0] = gl->tex_w;
|
||||
gl->prev_info.tex_size[0] = gl->tex_w;
|
||||
gl->prev_info.input_size[1] = gl->tex_h;
|
||||
gl->prev_info.tex_size[1] = gl->tex_h;
|
||||
memcpy(gl->prev_info.coord, tex_coords, sizeof(tex_coords));
|
||||
for (unsigned i = 0; i < TEXTURES; i++)
|
||||
{
|
||||
gl->prev_info[i].tex = gl->texture[(gl->tex_index - (i + 1)) & TEXTURES_MASK];
|
||||
gl->prev_info[i].input_size[0] = gl->tex_w;
|
||||
gl->prev_info[i].tex_size[0] = gl->tex_w;
|
||||
gl->prev_info[i].input_size[1] = gl->tex_h;
|
||||
gl->prev_info[i].tex_size[1] = gl->tex_h;
|
||||
memcpy(gl->prev_info[i].coord, tex_coords, sizeof(tex_coords));
|
||||
}
|
||||
|
||||
// Hook up SDL input driver to get SDL_QUIT events and RESIZE.
|
||||
sdl_input_t *sdl_input = input_sdl.init();
|
||||
|
||||
+107
-45
@@ -26,6 +26,7 @@
|
||||
#include "py_state.h"
|
||||
#include "general.h"
|
||||
#include "strl.h"
|
||||
#include "file.h"
|
||||
|
||||
#define PY_READ_FUNC_DECL(RAMTYPE) py_read_##RAMTYPE
|
||||
#define PY_READ_FUNC(RAMTYPE) \
|
||||
@@ -60,7 +61,7 @@ PY_READ_FUNC(APURAM)
|
||||
PY_READ_FUNC(CGRAM)
|
||||
PY_READ_FUNC(OAM)
|
||||
|
||||
static PyObject* py_read_input(PyObject *self, PyObject *args)
|
||||
static PyObject *py_read_input(PyObject *self, PyObject *args)
|
||||
{
|
||||
(void)self;
|
||||
|
||||
@@ -72,12 +73,16 @@ static PyObject* py_read_input(PyObject *self, PyObject *args)
|
||||
if (!PyArg_ParseTuple(args, "II", &player, &key))
|
||||
return NULL;
|
||||
|
||||
if (player > MAX_PLAYERS || player < 1)
|
||||
if (player > MAX_PLAYERS || player < 1 || key >= SSNES_FIRST_META_KEY)
|
||||
return NULL;
|
||||
|
||||
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, player > 1,
|
||||
@@ -85,7 +90,25 @@ static PyObject* py_read_input(PyObject *self, PyObject *args)
|
||||
player > 2 ? player - 2 : 0,
|
||||
key);
|
||||
|
||||
return PyBool_FromLong((long)res);
|
||||
return PyBool_FromLong(res);
|
||||
}
|
||||
|
||||
static PyObject *py_read_input_meta(PyObject *self, PyObject *args)
|
||||
{
|
||||
(void)self;
|
||||
|
||||
if (!driver.input_data)
|
||||
return PyBool_FromLong(0);
|
||||
|
||||
unsigned key;
|
||||
if (!PyArg_ParseTuple(args, "I", &key))
|
||||
return NULL;
|
||||
|
||||
if (key < SSNES_FIRST_META_KEY)
|
||||
return NULL;
|
||||
|
||||
bool ret = driver.input->key_pressed(driver.input_data, key);
|
||||
return PyBool_FromLong(ret);
|
||||
}
|
||||
|
||||
static PyMethodDef SNESMethods[] = {
|
||||
@@ -95,23 +118,49 @@ static PyMethodDef SNESMethods[] = {
|
||||
{ "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." },
|
||||
{ "input_meta", py_read_input_meta, METH_VARARGS, "Read SSNES specific input." },
|
||||
{ NULL, NULL, 0, NULL }
|
||||
};
|
||||
|
||||
#define DECL_ATTR_SNES(attr) PyObject_SetAttrString(mod, #attr, PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_##attr))
|
||||
#define DECL_ATTR_SSNES(attr) PyObject_SetAttrString(mod, #attr, PyLong_FromLong(SSNES_##attr))
|
||||
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));
|
||||
DECL_ATTR_SNES(B);
|
||||
DECL_ATTR_SNES(Y);
|
||||
DECL_ATTR_SNES(SELECT);
|
||||
DECL_ATTR_SNES(START);
|
||||
DECL_ATTR_SNES(UP);
|
||||
DECL_ATTR_SNES(DOWN);
|
||||
DECL_ATTR_SNES(LEFT);
|
||||
DECL_ATTR_SNES(RIGHT);
|
||||
DECL_ATTR_SNES(A);
|
||||
DECL_ATTR_SNES(X);
|
||||
DECL_ATTR_SNES(L);
|
||||
DECL_ATTR_SNES(R);
|
||||
|
||||
DECL_ATTR_SSNES(FAST_FORWARD_KEY);
|
||||
DECL_ATTR_SSNES(FAST_FORWARD_HOLD_KEY);
|
||||
DECL_ATTR_SSNES(LOAD_STATE_KEY);
|
||||
DECL_ATTR_SSNES(SAVE_STATE_KEY);
|
||||
DECL_ATTR_SSNES(FULLSCREEN_TOGGLE_KEY);
|
||||
DECL_ATTR_SSNES(QUIT_KEY);
|
||||
DECL_ATTR_SSNES(STATE_SLOT_PLUS);
|
||||
DECL_ATTR_SSNES(STATE_SLOT_MINUS);
|
||||
DECL_ATTR_SSNES(AUDIO_INPUT_RATE_PLUS);
|
||||
DECL_ATTR_SSNES(AUDIO_INPUT_RATE_MINUS);
|
||||
DECL_ATTR_SSNES(REWIND);
|
||||
DECL_ATTR_SSNES(MOVIE_RECORD_TOGGLE);
|
||||
DECL_ATTR_SSNES(PAUSE_TOGGLE);
|
||||
DECL_ATTR_SSNES(FRAMEADVANCE);
|
||||
DECL_ATTR_SSNES(RESET);
|
||||
DECL_ATTR_SSNES(SHADER_NEXT);
|
||||
DECL_ATTR_SSNES(SHADER_PREV);
|
||||
DECL_ATTR_SSNES(CHEAT_INDEX_PLUS);
|
||||
DECL_ATTR_SSNES(CHEAT_INDEX_MINUS);
|
||||
DECL_ATTR_SSNES(CHEAT_TOGGLE);
|
||||
DECL_ATTR_SSNES(SCREENSHOT);
|
||||
DECL_ATTR_SSNES(DSP_CONFIG);
|
||||
}
|
||||
|
||||
static PyModuleDef SNESModule = {
|
||||
@@ -156,7 +205,7 @@ static char *dupe_newline(const char *str)
|
||||
}
|
||||
|
||||
// Need to make sure that first-line indentation is 0. :(
|
||||
static char* align_program(const char *program)
|
||||
static char *align_program(const char *program)
|
||||
{
|
||||
char *prog = strdup(program);
|
||||
if (!prog)
|
||||
@@ -190,28 +239,38 @@ static char* align_program(const char *program)
|
||||
}
|
||||
|
||||
free(prog);
|
||||
|
||||
return new_prog;
|
||||
}
|
||||
|
||||
py_state_t *py_state_new(const char *script, bool is_file, const char *pyclass)
|
||||
py_state_t *py_state_new(const char *script, unsigned is_file, const char *pyclass)
|
||||
{
|
||||
SSNES_LOG("Initializing Python runtime ...\n");
|
||||
PyImport_AppendInittab("snes", &PyInit_SNES);
|
||||
Py_Initialize();
|
||||
SSNES_LOG("Initialized Python runtime!\n");
|
||||
|
||||
py_state_t *handle = calloc(1, sizeof(*handle));
|
||||
|
||||
handle->main = PyImport_AddModule("__main__");
|
||||
if (!handle->main)
|
||||
goto error;
|
||||
Py_INCREF(handle->main);
|
||||
|
||||
if (is_file)
|
||||
{
|
||||
FILE *file = fopen(script, "r");
|
||||
if (!file)
|
||||
// Have to hack around the fact that the
|
||||
// FILE struct isn't standardized across environments.
|
||||
// PyRun_SimpleFile() breaks on Windows.
|
||||
|
||||
char *script_ = NULL;
|
||||
if (read_file(script, (void**)&script_) < 0)
|
||||
{
|
||||
SSNES_ERR("Python: Failed to read script\n");
|
||||
goto error;
|
||||
PyRun_SimpleFile(file, script);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
PyRun_SimpleString(script_);
|
||||
free(script_);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -223,22 +282,35 @@ py_state_t *py_state_new(const char *script, bool is_file, const char *pyclass)
|
||||
}
|
||||
}
|
||||
|
||||
SSNES_LOG("Python: Script loaded.\n");
|
||||
handle->dict = PyModule_GetDict(handle->main);
|
||||
if (!handle->dict)
|
||||
{
|
||||
SSNES_ERR("Python: PyModule_GetDict() failed.\n");
|
||||
goto error;
|
||||
}
|
||||
Py_INCREF(handle->dict);
|
||||
|
||||
PyObject *hook = PyDict_GetItemString(handle->dict, pyclass);
|
||||
if (!hook)
|
||||
{
|
||||
SSNES_ERR("Python: PyDict_GetItemString() failed.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
handle->inst = PyObject_CallFunction(hook, NULL);
|
||||
if (!handle->inst)
|
||||
{
|
||||
SSNES_ERR("Python: PyObject_CallFunction() failed.\n");
|
||||
goto error;
|
||||
}
|
||||
Py_INCREF(handle->inst);
|
||||
|
||||
return handle;
|
||||
|
||||
error:
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
py_state_free(handle);
|
||||
return NULL;
|
||||
}
|
||||
@@ -247,20 +319,19 @@ 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);
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
|
||||
Py_CLEAR(handle->inst);
|
||||
Py_CLEAR(handle->dict);
|
||||
Py_CLEAR(handle->main);
|
||||
|
||||
free(handle);
|
||||
Py_Finalize();
|
||||
}
|
||||
|
||||
Py_Finalize();
|
||||
}
|
||||
|
||||
int py_state_get(py_state_t *handle, const char *id,
|
||||
float 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);
|
||||
@@ -269,19 +340,10 @@ int py_state_get(py_state_t *handle, const char *id,
|
||||
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;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float retval = (float)PyFloat_AsDouble(ret);
|
||||
Py_DECREF(ret);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -21,12 +21,14 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef PY_STATE_OMIT_DECLARATION
|
||||
typedef struct py_state py_state_t;
|
||||
#endif
|
||||
|
||||
py_state_t *py_state_new(const char *program, bool is_file, const char *pyclass);
|
||||
py_state_t *py_state_new(const char *program, unsigned is_file, const char *pyclass);
|
||||
void py_state_free(py_state_t *handle);
|
||||
|
||||
int py_state_get(py_state_t *handle,
|
||||
float py_state_get(py_state_t *handle,
|
||||
const char *id, unsigned frame_count);
|
||||
|
||||
#endif
|
||||
@@ -80,6 +80,9 @@ static void sdl_gfx_free(void *data)
|
||||
static void sdl_init_font(sdl_video_t *vid, const char *font_path, unsigned font_size)
|
||||
{
|
||||
#ifdef HAVE_FREETYPE
|
||||
if (!g_settings.video.font_enable)
|
||||
return;
|
||||
|
||||
const char *path = font_path;
|
||||
if (!*path)
|
||||
path = font_renderer_get_default_font();
|
||||
@@ -244,7 +247,7 @@ static void sdl_render_msg_32(sdl_video_t *vid, SDL_Surface *buffer, const char
|
||||
#endif
|
||||
}
|
||||
|
||||
static void* sdl_gfx_init(const video_info_t *video, const input_driver_t **input, void **input_data)
|
||||
static void *sdl_gfx_init(const video_info_t *video, const input_driver_t **input, void **input_data)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
gfx_set_dwm();
|
||||
@@ -288,13 +291,15 @@ static void* sdl_gfx_init(const video_info_t *video, const input_driver_t **inpu
|
||||
if (vid->render32)
|
||||
{
|
||||
SSNES_LOG("SDL: Creating 32-bit buffer.\n");
|
||||
vid->buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, 256 * video->input_scale, 256 * video->input_scale, 32,
|
||||
vid->buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, SSNES_SCALE_BASE * video->input_scale,
|
||||
SSNES_SCALE_BASE * video->input_scale, 32,
|
||||
fmt->Rmask, fmt->Bmask, fmt->Gmask, fmt->Amask);
|
||||
}
|
||||
else
|
||||
{
|
||||
SSNES_LOG("SDL: Creating 15-bit buffer.\n");
|
||||
vid->buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, 256 * video->input_scale, 256 * video->input_scale, 15,
|
||||
vid->buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, SSNES_SCALE_BASE * video->input_scale,
|
||||
SSNES_SCALE_BASE * video->input_scale, 15,
|
||||
fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
|
||||
}
|
||||
SSNES_LOG("[Debug]: SDL Pixel format: Rshift = %u, Gshift = %u, Bshift = %u\n",
|
||||
@@ -462,7 +467,7 @@ static void check_window(sdl_video_t *vid)
|
||||
}
|
||||
}
|
||||
|
||||
static bool sdl_gfx_frame(void *data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
|
||||
static bool sdl_gfx_frame(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
|
||||
{
|
||||
sdl_video_t *vid = data;
|
||||
|
||||
|
||||
+4
-1
@@ -56,7 +56,7 @@ void sdlwrap_set_swap_interval(unsigned interval, bool inited)
|
||||
}
|
||||
if (wgl_swap_interval) wgl_swap_interval(g_interval);
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#elif defined(__APPLE__) && defined(HAVE_OPENGL)
|
||||
GLint val = g_interval;
|
||||
CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &val);
|
||||
#else
|
||||
@@ -83,6 +83,9 @@ bool sdlwrap_init(void)
|
||||
#if SDL_MODERN
|
||||
return SDL_VideoInit(NULL) == 0;
|
||||
#else
|
||||
if (SDL_WasInit(SDL_INIT_VIDEO))
|
||||
return true;
|
||||
|
||||
return SDL_Init(SDL_INIT_VIDEO) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@
|
||||
#define SDL_MODERN 0
|
||||
#endif
|
||||
|
||||
// Not legal to cast void* to fn-pointer. Need workaround to be compliant.
|
||||
// Not legal to cast (void*) to fn-pointer. Need workaround to be compliant.
|
||||
#define SDL_SYM_WRAP(sym, symbol) { \
|
||||
assert(sizeof(void*) == sizeof(void (*)(void))); \
|
||||
void *sym__ = SDL_GL_GetProcAddress(symbol); \
|
||||
|
||||
+211
-126
@@ -32,7 +32,7 @@
|
||||
//#define SSNES_CG_DEBUG
|
||||
|
||||
// Used when we call deactivate() since just unbinding the program didn't seem to work... :(
|
||||
static const char* stock_cg_program =
|
||||
static const char *stock_cg_program =
|
||||
"void main_vertex"
|
||||
"("
|
||||
" float4 position : POSITION,"
|
||||
@@ -95,6 +95,7 @@ struct cg_fbo_params
|
||||
#define MAX_SHADERS 16
|
||||
#define MAX_TEXTURES 8
|
||||
#define MAX_VARIABLES 64
|
||||
#define PREV_TEXTURES 7
|
||||
|
||||
struct cg_program
|
||||
{
|
||||
@@ -104,15 +105,17 @@ struct cg_program
|
||||
CGparameter tex_size_f;
|
||||
CGparameter out_size_f;
|
||||
CGparameter frame_cnt_f;
|
||||
CGparameter frame_dir_f;
|
||||
CGparameter vid_size_v;
|
||||
CGparameter tex_size_v;
|
||||
CGparameter out_size_v;
|
||||
CGparameter frame_cnt_v;
|
||||
CGparameter frame_dir_v;
|
||||
CGparameter mvp;
|
||||
|
||||
struct cg_fbo_params fbo[MAX_SHADERS];
|
||||
struct cg_fbo_params orig;
|
||||
struct cg_fbo_params prev;
|
||||
struct cg_fbo_params prev[PREV_TEXTURES];
|
||||
};
|
||||
|
||||
#define FILTER_UNSPEC 0
|
||||
@@ -131,7 +134,19 @@ static unsigned lut_textures[MAX_TEXTURES];
|
||||
static unsigned lut_textures_num = 0;
|
||||
static char lut_textures_uniform[MAX_TEXTURES][64];
|
||||
|
||||
static CGparameter cg_attribs[PREV_TEXTURES + 1 + MAX_SHADERS];
|
||||
static unsigned cg_attrib_index;
|
||||
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
static snes_tracker_t *snes_tracker = NULL;
|
||||
#endif
|
||||
|
||||
static void gl_cg_reset_attrib(void)
|
||||
{
|
||||
for (unsigned i = 0; i < cg_attrib_index; i++)
|
||||
cgGLDisableClientState(cg_attribs[i]);
|
||||
cg_attrib_index = 0;
|
||||
}
|
||||
|
||||
void gl_cg_set_proj_matrix(void)
|
||||
{
|
||||
@@ -153,113 +168,124 @@ void gl_cg_set_params(unsigned width, unsigned height,
|
||||
const struct gl_tex_info *fbo_info,
|
||||
unsigned fbo_info_cnt)
|
||||
{
|
||||
if (cg_active && active_index > 0)
|
||||
if (!cg_active || (active_index == 0))
|
||||
return;
|
||||
|
||||
// Set frame.
|
||||
set_param_2f(prg[active_index].vid_size_f, width, height);
|
||||
set_param_2f(prg[active_index].tex_size_f, tex_width, tex_height);
|
||||
set_param_2f(prg[active_index].out_size_f, out_width, out_height);
|
||||
set_param_1f(prg[active_index].frame_cnt_f, (float)frame_count);
|
||||
set_param_1f(prg[active_index].frame_dir_f, g_extern.frame_is_reverse ? -1.0 : 1.0);
|
||||
|
||||
set_param_2f(prg[active_index].vid_size_v, width, height);
|
||||
set_param_2f(prg[active_index].tex_size_v, tex_width, tex_height);
|
||||
set_param_2f(prg[active_index].out_size_v, out_width, out_height);
|
||||
set_param_1f(prg[active_index].frame_cnt_v, (float)frame_count);
|
||||
set_param_1f(prg[active_index].frame_dir_v, g_extern.frame_is_reverse ? -1.0 : 1.0);
|
||||
|
||||
// Set orig texture.
|
||||
CGparameter param = prg[active_index].orig.tex;
|
||||
if (param)
|
||||
{
|
||||
// Set frame.
|
||||
set_param_2f(prg[active_index].vid_size_f, width, height);
|
||||
set_param_2f(prg[active_index].tex_size_f, tex_width, tex_height);
|
||||
set_param_2f(prg[active_index].out_size_f, out_width, out_height);
|
||||
set_param_1f(prg[active_index].frame_cnt_f, (float)frame_count);
|
||||
cgGLSetTextureParameter(param, info->tex);
|
||||
cgGLEnableTextureParameter(param);
|
||||
}
|
||||
|
||||
set_param_2f(prg[active_index].vid_size_v, width, height);
|
||||
set_param_2f(prg[active_index].tex_size_v, tex_width, tex_height);
|
||||
set_param_2f(prg[active_index].out_size_v, out_width, out_height);
|
||||
set_param_1f(prg[active_index].frame_cnt_v, (float)frame_count);
|
||||
set_param_2f(prg[active_index].orig.vid_size_v, info->input_size[0], info->input_size[1]);
|
||||
set_param_2f(prg[active_index].orig.vid_size_f, info->input_size[0], info->input_size[1]);
|
||||
set_param_2f(prg[active_index].orig.tex_size_v, info->tex_size[0], info->tex_size[1]);
|
||||
set_param_2f(prg[active_index].orig.tex_size_f, info->tex_size[0], info->tex_size[1]);
|
||||
if (prg[active_index].orig.coord)
|
||||
{
|
||||
cgGLSetParameterPointer(prg[active_index].orig.coord, 2, GL_FLOAT, 0, info->coord);
|
||||
cgGLEnableClientState(prg[active_index].orig.coord);
|
||||
cg_attribs[cg_attrib_index++] = prg[active_index].orig.coord;
|
||||
}
|
||||
|
||||
// Set orig texture.
|
||||
CGparameter param = prg[active_index].orig.tex;
|
||||
// Set prev textures.
|
||||
for (unsigned i = 0; i < PREV_TEXTURES; i++)
|
||||
{
|
||||
param = prg[active_index].prev[i].tex;
|
||||
if (param)
|
||||
{
|
||||
cgGLSetTextureParameter(param, info->tex);
|
||||
cgGLSetTextureParameter(param, prev_info[i].tex);
|
||||
cgGLEnableTextureParameter(param);
|
||||
}
|
||||
|
||||
set_param_2f(prg[active_index].orig.vid_size_v, info->input_size[0], info->input_size[1]);
|
||||
set_param_2f(prg[active_index].orig.vid_size_f, info->input_size[0], info->input_size[1]);
|
||||
set_param_2f(prg[active_index].orig.tex_size_v, info->tex_size[0], info->tex_size[1]);
|
||||
set_param_2f(prg[active_index].orig.tex_size_f, info->tex_size[0], info->tex_size[1]);
|
||||
if (prg[active_index].orig.coord)
|
||||
{
|
||||
cgGLSetParameterPointer(prg[active_index].orig.coord, 2, GL_FLOAT, 0, info->coord);
|
||||
cgGLEnableClientState(prg[active_index].orig.coord);
|
||||
}
|
||||
set_param_2f(prg[active_index].prev[i].vid_size_v, prev_info[i].input_size[0], prev_info[i].input_size[1]);
|
||||
set_param_2f(prg[active_index].prev[i].vid_size_f, prev_info[i].input_size[0], prev_info[i].input_size[1]);
|
||||
set_param_2f(prg[active_index].prev[i].tex_size_v, prev_info[i].tex_size[0], prev_info[i].tex_size[1]);
|
||||
set_param_2f(prg[active_index].prev[i].tex_size_f, prev_info[i].tex_size[0], prev_info[i].tex_size[1]);
|
||||
|
||||
// Set prev texture
|
||||
param = prg[active_index].prev.tex;
|
||||
if (prg[active_index].prev[i].coord)
|
||||
{
|
||||
cgGLSetParameterPointer(prg[active_index].prev[i].coord, 2, GL_FLOAT, 0, prev_info[i].coord);
|
||||
cgGLEnableClientState(prg[active_index].prev[i].coord);
|
||||
cg_attribs[cg_attrib_index++] = prg[active_index].prev[i].coord;
|
||||
}
|
||||
}
|
||||
|
||||
// Set lookup textures.
|
||||
for (unsigned i = 0; i < lut_textures_num; i++)
|
||||
{
|
||||
CGparameter param = cgGetNamedParameter(prg[active_index].fprg, lut_textures_uniform[i]);
|
||||
if (param)
|
||||
{
|
||||
cgGLSetTextureParameter(param, prev_info->tex);
|
||||
cgGLSetTextureParameter(param, lut_textures[i]);
|
||||
cgGLEnableTextureParameter(param);
|
||||
}
|
||||
}
|
||||
|
||||
set_param_2f(prg[active_index].prev.vid_size_v, prev_info->input_size[0], prev_info->input_size[1]);
|
||||
set_param_2f(prg[active_index].prev.vid_size_f, prev_info->input_size[0], prev_info->input_size[1]);
|
||||
set_param_2f(prg[active_index].prev.tex_size_v, prev_info->tex_size[0], prev_info->tex_size[1]);
|
||||
set_param_2f(prg[active_index].prev.tex_size_f, prev_info->tex_size[0], prev_info->tex_size[1]);
|
||||
if (prg[active_index].prev.coord)
|
||||
// Set FBO textures.
|
||||
if (active_index > 2)
|
||||
{
|
||||
for (unsigned i = 0; i < fbo_info_cnt; i++)
|
||||
{
|
||||
cgGLSetParameterPointer(prg[active_index].prev.coord, 2, GL_FLOAT, 0, prev_info->coord);
|
||||
cgGLEnableClientState(prg[active_index].prev.coord);
|
||||
}
|
||||
|
||||
// Set lookup textures.
|
||||
for (unsigned i = 0; i < lut_textures_num; i++)
|
||||
{
|
||||
CGparameter param = cgGetNamedParameter(prg[active_index].fprg, lut_textures_uniform[i]);
|
||||
if (param)
|
||||
if (prg[active_index].fbo[i].tex)
|
||||
{
|
||||
cgGLSetTextureParameter(param, lut_textures[i]);
|
||||
cgGLEnableTextureParameter(param);
|
||||
cgGLSetTextureParameter(prg[active_index].fbo[i].tex, fbo_info[i].tex);
|
||||
cgGLEnableTextureParameter(prg[active_index].fbo[i].tex);
|
||||
}
|
||||
}
|
||||
|
||||
// Set FBO textures.
|
||||
if (active_index > 2)
|
||||
{
|
||||
for (unsigned i = 0; i < fbo_info_cnt; i++)
|
||||
|
||||
set_param_2f(prg[active_index].fbo[i].vid_size_v,
|
||||
fbo_info[i].input_size[0], fbo_info[i].input_size[1]);
|
||||
set_param_2f(prg[active_index].fbo[i].vid_size_f,
|
||||
fbo_info[i].input_size[0], fbo_info[i].input_size[1]);
|
||||
|
||||
set_param_2f(prg[active_index].fbo[i].tex_size_v,
|
||||
fbo_info[i].tex_size[0], fbo_info[i].tex_size[1]);
|
||||
set_param_2f(prg[active_index].fbo[i].tex_size_f,
|
||||
fbo_info[i].tex_size[0], fbo_info[i].tex_size[1]);
|
||||
|
||||
if (prg[active_index].fbo[i].coord)
|
||||
{
|
||||
if (prg[active_index].fbo[i].tex)
|
||||
{
|
||||
cgGLSetTextureParameter(prg[active_index].fbo[i].tex, fbo_info[i].tex);
|
||||
cgGLEnableTextureParameter(prg[active_index].fbo[i].tex);
|
||||
}
|
||||
|
||||
set_param_2f(prg[active_index].fbo[i].vid_size_v,
|
||||
fbo_info[i].input_size[0], fbo_info[i].input_size[1]);
|
||||
set_param_2f(prg[active_index].fbo[i].vid_size_f,
|
||||
fbo_info[i].input_size[0], fbo_info[i].input_size[1]);
|
||||
|
||||
set_param_2f(prg[active_index].fbo[i].tex_size_v,
|
||||
fbo_info[i].tex_size[0], fbo_info[i].tex_size[1]);
|
||||
set_param_2f(prg[active_index].fbo[i].tex_size_f,
|
||||
fbo_info[i].tex_size[0], fbo_info[i].tex_size[1]);
|
||||
|
||||
if (prg[active_index].fbo[i].coord)
|
||||
{
|
||||
cgGLSetParameterPointer(prg[active_index].fbo[i].coord, 2, GL_FLOAT, 0, fbo_info[i].coord);
|
||||
cgGLEnableClientState(prg[active_index].fbo[i].coord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set state parameters
|
||||
if (snes_tracker)
|
||||
{
|
||||
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);
|
||||
CGparameter param_f = cgGetNamedParameter(prg[active_index].fprg, info[i].id);
|
||||
set_param_1f(param_v, info[i].value);
|
||||
set_param_1f(param_f, info[i].value);
|
||||
cgGLSetParameterPointer(prg[active_index].fbo[i].coord, 2, GL_FLOAT, 0, fbo_info[i].coord);
|
||||
cgGLEnableClientState(prg[active_index].fbo[i].coord);
|
||||
cg_attribs[cg_attrib_index++] = prg[active_index].fbo[i].coord;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
// Set state parameters
|
||||
if (snes_tracker)
|
||||
{
|
||||
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);
|
||||
CGparameter param_f = cgGetNamedParameter(prg[active_index].fprg, info[i].id);
|
||||
set_param_1f(param_v, info[i].value);
|
||||
set_param_1f(param_f, info[i].value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void gl_cg_deinit(void)
|
||||
@@ -267,6 +293,8 @@ void gl_cg_deinit(void)
|
||||
if (!cg_active)
|
||||
return;
|
||||
|
||||
gl_cg_reset_attrib();
|
||||
|
||||
cg_active = false;
|
||||
cg_shader_num = 0;
|
||||
memset(prg, 0, sizeof(prg));
|
||||
@@ -276,45 +304,56 @@ void gl_cg_deinit(void)
|
||||
glDeleteTextures(lut_textures_num, lut_textures);
|
||||
lut_textures_num = 0;
|
||||
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
if (snes_tracker)
|
||||
{
|
||||
snes_tracker_free(snes_tracker);
|
||||
snes_tracker = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
cgDestroyContext(cgCtx);
|
||||
}
|
||||
|
||||
#define SET_LISTING_INDEX(type, index) \
|
||||
{ \
|
||||
const char *list = cgGetLastListing(cgCtx); \
|
||||
if (list) \
|
||||
listing_##type[index] = strdup(list); \
|
||||
}
|
||||
|
||||
#define SET_LISTING(type) \
|
||||
{ \
|
||||
const char *list = cgGetLastListing(cgCtx); \
|
||||
if (list) \
|
||||
listing_##type = strdup(list); \
|
||||
}
|
||||
|
||||
static bool load_plain(const char *path)
|
||||
{
|
||||
SSNES_LOG("Loading Cg file: %s\n", path);
|
||||
|
||||
char *listing[3] = {NULL};
|
||||
const char *list = NULL;
|
||||
char *listing_v[3] = {NULL};
|
||||
char *listing_f[3] = {NULL};
|
||||
|
||||
prg[0].fprg = cgCreateProgram(cgCtx, CG_SOURCE, stock_cg_program, cgFProf, "main_fragment", 0);
|
||||
SET_LISTING_INDEX(f, 0);
|
||||
prg[0].vprg = cgCreateProgram(cgCtx, CG_SOURCE, stock_cg_program, cgVProf, "main_vertex", 0);
|
||||
|
||||
list = cgGetLastListing(cgCtx);
|
||||
if (list)
|
||||
listing[0] = strdup(list);
|
||||
SET_LISTING_INDEX(v, 0);
|
||||
|
||||
prg[1].fprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, path, cgFProf, "main_fragment", 0);
|
||||
SET_LISTING_INDEX(f, 1);
|
||||
prg[1].vprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, path, cgVProf, "main_vertex", 0);
|
||||
|
||||
list = cgGetLastListing(cgCtx);
|
||||
if (list)
|
||||
listing[1] = strdup(list);
|
||||
SET_LISTING_INDEX(v, 1);
|
||||
|
||||
if (*g_settings.video.second_pass_shader && g_settings.video.render_to_texture)
|
||||
{
|
||||
SSNES_LOG("Loading 2nd pass: %s\n", g_settings.video.second_pass_shader);
|
||||
prg[2].fprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, g_settings.video.second_pass_shader, cgFProf, "main_fragment", 0);
|
||||
SET_LISTING_INDEX(f, 2);
|
||||
prg[2].vprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, g_settings.video.second_pass_shader, cgVProf, "main_vertex", 0);
|
||||
SET_LISTING_INDEX(v, 2);
|
||||
|
||||
list = cgGetLastListing(cgCtx);
|
||||
if (list)
|
||||
listing[2] = strdup(list);
|
||||
cg_shader_num = 2;
|
||||
}
|
||||
else
|
||||
@@ -329,8 +368,10 @@ static bool load_plain(const char *path)
|
||||
{
|
||||
CGerror err = cgGetError();
|
||||
SSNES_ERR("CG error: %s\n", cgGetErrorString(err));
|
||||
if (listing[i])
|
||||
SSNES_ERR("%s\n", listing[i]);
|
||||
if (listing_v[i])
|
||||
SSNES_ERR("Vertex:\n%s\n", listing_v[i]);
|
||||
else if (listing_f[i])
|
||||
SSNES_ERR("Fragment:\n%s\n", listing_f[i]);
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -338,13 +379,19 @@ static bool load_plain(const char *path)
|
||||
cgGLLoadProgram(prg[i].vprg);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 3; i++)
|
||||
{
|
||||
free(listing_v[i]);
|
||||
free(listing_f[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
for (unsigned i = 0; i < 3; i++)
|
||||
{
|
||||
if (listing[i])
|
||||
free(listing[i]);
|
||||
free(listing_v[i]);
|
||||
free(listing_f[i]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -490,8 +537,8 @@ static bool load_imports(const char *dir_path, config_file_t *conf)
|
||||
unsigned addr = 0;
|
||||
#ifdef HAVE_PYTHON
|
||||
if (tracker_type != SSNES_STATE_PYTHON)
|
||||
{
|
||||
#endif
|
||||
{
|
||||
unsigned input_slot = 0;
|
||||
if (config_get_hex(conf, input_slot_buf, &input_slot))
|
||||
{
|
||||
@@ -525,11 +572,9 @@ static bool load_imports(const char *dir_path, config_file_t *conf)
|
||||
SSNES_ERR("No address assigned to semantic.\n");
|
||||
goto error;
|
||||
}
|
||||
#ifdef HAVE_PYTHON
|
||||
}
|
||||
#endif
|
||||
|
||||
int memtype = 0;
|
||||
unsigned memtype;
|
||||
switch (ram_type)
|
||||
{
|
||||
case SSNES_STATE_WRAM:
|
||||
@@ -549,10 +594,10 @@ static bool load_imports(const char *dir_path, config_file_t *conf)
|
||||
break;
|
||||
|
||||
default:
|
||||
memtype = SNES_MEMORY_WRAM;
|
||||
memtype = -1u;
|
||||
}
|
||||
|
||||
if (addr >= psnes_get_memory_size(memtype))
|
||||
if ((memtype != -1u) && (addr >= psnes_get_memory_size(memtype)))
|
||||
{
|
||||
SSNES_ERR("Address out of bounds.\n");
|
||||
goto error;
|
||||
@@ -581,7 +626,7 @@ static bool load_imports(const char *dir_path, config_file_t *conf)
|
||||
.oam = psnes_get_memory_data(SNES_MEMORY_OAM),
|
||||
.apuram = psnes_get_memory_data(SNES_MEMORY_APURAM),
|
||||
.info = info,
|
||||
.info_elem = info_cnt
|
||||
.info_elem = info_cnt,
|
||||
};
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
@@ -613,7 +658,6 @@ static bool load_imports(const char *dir_path, config_file_t *conf)
|
||||
#endif
|
||||
|
||||
free(imports);
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
@@ -723,8 +767,8 @@ static bool load_preset(const char *path)
|
||||
scale->type_y = SSNES_SCALE_INPUT;
|
||||
scale->scale_x = 1.0;
|
||||
scale->scale_y = 1.0;
|
||||
scale->abs_x = 256;
|
||||
scale->abs_y = 224;
|
||||
scale->abs_x = g_extern.system.geom.base_width;
|
||||
scale->abs_y = g_extern.system.geom.base_height;
|
||||
|
||||
if (strcmp(scale_type_x, "source") == 0)
|
||||
scale->type_x = SSNES_SCALE_INPUT;
|
||||
@@ -807,7 +851,7 @@ static bool load_preset(const char *path)
|
||||
}
|
||||
|
||||
// Basedir.
|
||||
char dir_path[256];
|
||||
char dir_path[MAXPATHLEN];
|
||||
strlcpy(dir_path, path, sizeof(dir_path));
|
||||
char *ptr = strrchr(dir_path, '/');
|
||||
if (!ptr) ptr = strrchr(dir_path, '\\');
|
||||
@@ -821,7 +865,7 @@ static bool load_preset(const char *path)
|
||||
{
|
||||
char *shader_path;
|
||||
char attr_buf[64];
|
||||
char path_buf[512];
|
||||
char path_buf[MAXPATHLEN];
|
||||
|
||||
print_buf(attr_buf, "shader%u", i);
|
||||
if (config_get_string(conf, attr_buf, &shader_path))
|
||||
@@ -839,19 +883,31 @@ static bool load_preset(const char *path)
|
||||
SSNES_LOG("Loading Cg shader: \"%s\".\n", path_buf);
|
||||
|
||||
struct cg_program *prog = &prg[i + 1];
|
||||
|
||||
char *listing_f = NULL;
|
||||
prog->fprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, path_buf, cgFProf, "main_fragment", 0);
|
||||
SET_LISTING(f);
|
||||
|
||||
char *listing_v = NULL;
|
||||
prog->vprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, path_buf, cgVProf, "main_vertex", 0);
|
||||
SET_LISTING(v);
|
||||
|
||||
if (!prog->fprg || !prog->vprg)
|
||||
{
|
||||
const char *listing = cgGetLastListing(cgCtx);
|
||||
CGerror err = cgGetError();
|
||||
SSNES_ERR("CG error: %s\n", cgGetErrorString(err));
|
||||
if (listing)
|
||||
SSNES_ERR("%s\n", listing);
|
||||
if (listing_v)
|
||||
SSNES_ERR("Vertex:\n%s\n", listing_v);
|
||||
else if (listing_f)
|
||||
SSNES_ERR("Fragment:\n%s\n", listing_f);
|
||||
|
||||
free(listing_f);
|
||||
free(listing_v);
|
||||
goto error;
|
||||
}
|
||||
|
||||
free(listing_f);
|
||||
free(listing_v);
|
||||
cgGLLoadProgram(prog->fprg);
|
||||
cgGLLoadProgram(prog->vprg);
|
||||
}
|
||||
@@ -932,10 +988,12 @@ bool gl_cg_init(const char *path)
|
||||
prg[i].tex_size_f = cgGetNamedParameter(prg[i].fprg, "IN.texture_size");
|
||||
prg[i].out_size_f = cgGetNamedParameter(prg[i].fprg, "IN.output_size");
|
||||
prg[i].frame_cnt_f = cgGetNamedParameter(prg[i].fprg, "IN.frame_count");
|
||||
prg[i].frame_dir_f = cgGetNamedParameter(prg[i].fprg, "IN.frame_direction");
|
||||
prg[i].vid_size_v = cgGetNamedParameter(prg[i].vprg, "IN.video_size");
|
||||
prg[i].tex_size_v = cgGetNamedParameter(prg[i].vprg, "IN.texture_size");
|
||||
prg[i].out_size_v = cgGetNamedParameter(prg[i].vprg, "IN.output_size");
|
||||
prg[i].frame_cnt_v = cgGetNamedParameter(prg[i].vprg, "IN.frame_count");
|
||||
prg[i].frame_dir_v = cgGetNamedParameter(prg[i].vprg, "IN.frame_direction");
|
||||
prg[i].mvp = cgGetNamedParameter(prg[i].vprg, "modelViewProj");
|
||||
if (prg[i].mvp)
|
||||
cgGLSetStateMatrixParameter(prg[i].mvp, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
|
||||
@@ -947,12 +1005,37 @@ bool gl_cg_init(const char *path)
|
||||
prg[i].orig.tex_size_f = cgGetNamedParameter(prg[i].fprg, "ORIG.texture_size");
|
||||
prg[i].orig.coord = cgGetNamedParameter(prg[i].vprg, "ORIG.tex_coord");
|
||||
|
||||
prg[i].prev.tex = cgGetNamedParameter(prg[i].fprg, "PREV.texture");
|
||||
prg[i].prev.vid_size_v = cgGetNamedParameter(prg[i].vprg, "PREV.video_size");
|
||||
prg[i].prev.vid_size_f = cgGetNamedParameter(prg[i].fprg, "PREV.video_size");
|
||||
prg[i].prev.tex_size_v = cgGetNamedParameter(prg[i].vprg, "PREV.texture_size");
|
||||
prg[i].prev.tex_size_f = cgGetNamedParameter(prg[i].fprg, "PREV.texture_size");
|
||||
prg[i].prev.coord = cgGetNamedParameter(prg[i].vprg, "PREV.tex_coord");
|
||||
for (unsigned j = 0; j < PREV_TEXTURES; j++)
|
||||
{
|
||||
char attr_buf_tex[64];
|
||||
char attr_buf_vid_size[64];
|
||||
char attr_buf_tex_size[64];
|
||||
char attr_buf_coord[64];
|
||||
static const char *prev_names[PREV_TEXTURES] = {
|
||||
"PREV",
|
||||
"PREV1",
|
||||
"PREV2",
|
||||
"PREV3",
|
||||
"PREV4",
|
||||
"PREV5",
|
||||
"PREV6",
|
||||
};
|
||||
|
||||
snprintf(attr_buf_tex, sizeof(attr_buf_tex), "%s.texture", prev_names[j]);
|
||||
snprintf(attr_buf_vid_size, sizeof(attr_buf_vid_size), "%s.video_size", prev_names[j]);
|
||||
snprintf(attr_buf_tex_size, sizeof(attr_buf_tex_size), "%s.texture_size", prev_names[j]);
|
||||
snprintf(attr_buf_coord, sizeof(attr_buf_coord), "%s.tex_coord", prev_names[j]);
|
||||
|
||||
prg[i].prev[j].tex = cgGetNamedParameter(prg[i].fprg, attr_buf_tex);
|
||||
|
||||
prg[i].prev[j].vid_size_v = cgGetNamedParameter(prg[i].vprg, attr_buf_vid_size);
|
||||
prg[i].prev[j].vid_size_f = cgGetNamedParameter(prg[i].fprg, attr_buf_vid_size);
|
||||
|
||||
prg[i].prev[j].tex_size_v = cgGetNamedParameter(prg[i].vprg, attr_buf_tex_size);
|
||||
prg[i].prev[j].tex_size_f = cgGetNamedParameter(prg[i].fprg, attr_buf_tex_size);
|
||||
|
||||
prg[i].prev[j].coord = cgGetNamedParameter(prg[i].vprg, attr_buf_coord);
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < i - 1; j++)
|
||||
{
|
||||
@@ -985,6 +1068,8 @@ void gl_cg_use(unsigned index)
|
||||
{
|
||||
if (cg_active && prg[index].vprg && prg[index].fprg)
|
||||
{
|
||||
gl_cg_reset_attrib();
|
||||
|
||||
active_index = index;
|
||||
cgGLBindProgram(prg[index].vprg);
|
||||
cgGLBindProgram(prg[index].fprg);
|
||||
|
||||
+202
-125
@@ -55,6 +55,7 @@
|
||||
#define pglLinkProgram glLinkProgram
|
||||
#define pglGetUniformLocation glGetUniformLocation
|
||||
#define pglUniform1i glUniform1i
|
||||
#define pglUniform1f glUniform1f
|
||||
#define pglUniform2fv glUniform2fv
|
||||
#define pglUniform4fv glUniform4fv
|
||||
#define pglGetShaderiv glGetShaderiv
|
||||
@@ -65,6 +66,7 @@
|
||||
#define pglGetAttachedShaders glGetAttachedShaders
|
||||
#define pglGetAttribLocation glGetAttribLocation
|
||||
#define pglEnableVertexAttribArray glEnableVertexAttribArray
|
||||
#define pglDisableVertexAttribArray glDisableVertexAttribArray
|
||||
#define pglVertexAttribPointer glVertexAttribPointer
|
||||
#else
|
||||
static PFNGLCREATEPROGRAMPROC pglCreateProgram = NULL;
|
||||
@@ -78,6 +80,7 @@ static PFNGLDETACHSHADERPROC pglDetachShader = NULL;
|
||||
static PFNGLLINKPROGRAMPROC pglLinkProgram = NULL;
|
||||
static PFNGLGETUNIFORMLOCATIONPROC pglGetUniformLocation = NULL;
|
||||
static PFNGLUNIFORM1IPROC pglUniform1i = NULL;
|
||||
static PFNGLUNIFORM1FPROC pglUniform1f = NULL;
|
||||
static PFNGLUNIFORM2FVPROC pglUniform2fv = NULL;
|
||||
static PFNGLUNIFORM4FVPROC pglUniform4fv = NULL;
|
||||
static PFNGLGETSHADERIVPROC pglGetShaderiv = NULL;
|
||||
@@ -88,12 +91,14 @@ static PFNGLDELETEPROGRAMPROC pglDeleteProgram = NULL;
|
||||
static PFNGLGETATTACHEDSHADERSPROC pglGetAttachedShaders = NULL;
|
||||
static PFNGLGETATTRIBLOCATIONPROC pglGetAttribLocation = NULL;
|
||||
static PFNGLENABLEVERTEXATTRIBARRAYPROC pglEnableVertexAttribArray = NULL;
|
||||
static PFNGLDISABLEVERTEXATTRIBARRAYPROC pglDisableVertexAttribArray = NULL;
|
||||
static PFNGLVERTEXATTRIBPOINTERPROC pglVertexAttribPointer = NULL;
|
||||
#endif
|
||||
|
||||
#define MAX_PROGRAMS 16
|
||||
#define MAX_TEXTURES 8
|
||||
#define MAX_VARIABLES 256
|
||||
#define PREV_TEXTURES 7
|
||||
|
||||
enum filter_type
|
||||
{
|
||||
@@ -116,10 +121,14 @@ 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[MAXPATHLEN];
|
||||
static char gl_tracker_script_class[64];
|
||||
static xmlChar *gl_script_program = NULL;
|
||||
|
||||
static GLint gl_attribs[PREV_TEXTURES + 1 + MAX_PROGRAMS];
|
||||
static unsigned gl_attrib_index = 0;
|
||||
|
||||
|
||||
struct shader_program
|
||||
{
|
||||
char *vertex;
|
||||
@@ -136,6 +145,21 @@ struct shader_program
|
||||
bool valid_scale;
|
||||
};
|
||||
|
||||
static const char *stock_vertex =
|
||||
"varying vec4 color;\n"
|
||||
"void main() {\n"
|
||||
" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
|
||||
" gl_TexCoord[0] = gl_MultiTexCoord0;\n"
|
||||
" color = gl_Color;\n"
|
||||
"}";
|
||||
|
||||
static const char *stock_fragment =
|
||||
"uniform sampler2D rubyTexture;\n"
|
||||
"varying vec4 color;\n"
|
||||
"void main() {\n"
|
||||
" gl_FragColor = color * texture2D(rubyTexture, gl_TexCoord[0].xy);\n"
|
||||
"}";
|
||||
|
||||
static bool get_xml_attrs(struct shader_program *prog, xmlNodePtr ptr)
|
||||
{
|
||||
prog->scale_x = 1.0;
|
||||
@@ -325,7 +349,7 @@ static bool get_texture_image(const char *shader_path, xmlNodePtr ptr)
|
||||
if (filter && strcmp((const char*)filter, "nearest") == 0)
|
||||
linear = false;
|
||||
|
||||
char tex_path[256];
|
||||
char tex_path[MAXPATHLEN];
|
||||
strlcpy(tex_path, shader_path, sizeof(tex_path));
|
||||
|
||||
char *last = strrchr(tex_path, '/');
|
||||
@@ -481,13 +505,13 @@ static bool get_import_value(xmlNodePtr ptr)
|
||||
goto error;
|
||||
}
|
||||
|
||||
enum snes_ram_type ram_type = SSNES_STATE_WRAM;
|
||||
enum snes_ram_type ram_type = SSNES_STATE_NONE;
|
||||
uint32_t addr = 0;
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
if (tracker_type != SSNES_STATE_PYTHON)
|
||||
{
|
||||
#endif
|
||||
{
|
||||
if (input)
|
||||
{
|
||||
unsigned slot = strtoul((const char*)input, NULL, 0);
|
||||
@@ -515,11 +539,9 @@ static bool get_import_value(xmlNodePtr ptr)
|
||||
SSNES_ERR("No RAM address specificed for import value.\n");
|
||||
goto error;
|
||||
}
|
||||
#ifdef HAVE_PYTHON
|
||||
}
|
||||
#endif
|
||||
|
||||
int memtype = 0;
|
||||
unsigned memtype;
|
||||
switch (ram_type)
|
||||
{
|
||||
case SSNES_STATE_WRAM:
|
||||
@@ -539,10 +561,10 @@ static bool get_import_value(xmlNodePtr ptr)
|
||||
break;
|
||||
|
||||
default:
|
||||
memtype = SNES_MEMORY_WRAM;
|
||||
memtype = -1u;
|
||||
}
|
||||
|
||||
if (addr >= psnes_get_memory_size(memtype))
|
||||
if ((memtype != -1u) && (addr >= psnes_get_memory_size(memtype)))
|
||||
{
|
||||
SSNES_ERR("Address out of bounds.\n");
|
||||
goto error;
|
||||
@@ -825,6 +847,13 @@ static bool compile_programs(GLuint *gl_prog, struct shader_program *progs, size
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gl_glsl_reset_attrib(void)
|
||||
{
|
||||
for (unsigned i = 0; i < gl_attrib_index; i++)
|
||||
pglDisableVertexAttribArray(gl_attribs[i]);
|
||||
gl_attrib_index = 0;
|
||||
}
|
||||
|
||||
#define LOAD_GL_SYM(SYM) if (!pgl##SYM) { SDL_SYM_WRAP(pgl##SYM, "gl" #SYM) }
|
||||
|
||||
bool gl_glsl_init(const char *path)
|
||||
@@ -842,6 +871,7 @@ bool gl_glsl_init(const char *path)
|
||||
LOAD_GL_SYM(LinkProgram);
|
||||
LOAD_GL_SYM(GetUniformLocation);
|
||||
LOAD_GL_SYM(Uniform1i);
|
||||
LOAD_GL_SYM(Uniform1f);
|
||||
LOAD_GL_SYM(Uniform2fv);
|
||||
LOAD_GL_SYM(Uniform4fv);
|
||||
LOAD_GL_SYM(GetShaderiv);
|
||||
@@ -852,6 +882,7 @@ bool gl_glsl_init(const char *path)
|
||||
LOAD_GL_SYM(GetAttachedShaders);
|
||||
LOAD_GL_SYM(GetAttribLocation);
|
||||
LOAD_GL_SYM(EnableVertexAttribArray);
|
||||
LOAD_GL_SYM(DisableVertexAttribArray);
|
||||
LOAD_GL_SYM(VertexAttribPointer);
|
||||
#endif
|
||||
|
||||
@@ -862,19 +893,26 @@ bool gl_glsl_init(const char *path)
|
||||
bool shader_support = pglCreateProgram && pglUseProgram && pglCreateShader
|
||||
&& pglDeleteShader && pglShaderSource && pglCompileShader && pglAttachShader
|
||||
&& pglDetachShader && pglLinkProgram && pglGetUniformLocation
|
||||
&& pglUniform1i && pglUniform2fv && pglUniform4fv
|
||||
&& pglUniform1i && pglUniform1f && pglUniform2fv && pglUniform4fv
|
||||
&& pglGetShaderiv && pglGetShaderInfoLog && pglGetProgramiv && pglGetProgramInfoLog
|
||||
&& pglDeleteProgram && pglGetAttachedShaders
|
||||
&& pglGetAttribLocation && pglEnableVertexAttribArray
|
||||
&& pglGetAttribLocation && pglEnableVertexAttribArray && pglDisableVertexAttribArray
|
||||
&& pglVertexAttribPointer;
|
||||
#endif
|
||||
|
||||
if (!shader_support)
|
||||
{
|
||||
SSNES_ERR("GLSL shaders aren't supported by your GL driver.\n");
|
||||
SSNES_ERR("GLSL shaders aren't supported by your OpenGL driver.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct shader_program stock_prog = {
|
||||
.vertex = strdup(stock_vertex),
|
||||
.fragment = strdup(stock_fragment),
|
||||
};
|
||||
if (!compile_programs(&gl_program[0], &stock_prog, 1))
|
||||
return false;
|
||||
|
||||
struct shader_program progs[MAX_PROGRAMS];
|
||||
unsigned num_progs = get_xml_shaders(path, progs, MAX_PROGRAMS - 1);
|
||||
|
||||
@@ -949,6 +987,10 @@ bool gl_glsl_init(const char *path)
|
||||
|
||||
glsl_enable = true;
|
||||
gl_num_programs = num_progs;
|
||||
gl_program[gl_num_programs + 1] = gl_program[0];
|
||||
|
||||
gl_glsl_reset_attrib();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -957,10 +999,10 @@ void gl_glsl_deinit(void)
|
||||
if (glsl_enable)
|
||||
{
|
||||
pglUseProgram(0);
|
||||
for (int i = 1; i < MAX_PROGRAMS; i++)
|
||||
for (int i = 0; i <= gl_num_programs; i++)
|
||||
{
|
||||
if (gl_program[i] == 0)
|
||||
break;
|
||||
continue;
|
||||
|
||||
GLsizei count;
|
||||
GLuint shaders[2];
|
||||
@@ -998,6 +1040,8 @@ void gl_glsl_deinit(void)
|
||||
snes_tracker_free(gl_snes_tracker);
|
||||
gl_snes_tracker = NULL;
|
||||
}
|
||||
|
||||
gl_glsl_reset_attrib();
|
||||
}
|
||||
|
||||
void gl_glsl_set_params(unsigned width, unsigned height,
|
||||
@@ -1009,147 +1053,178 @@ void gl_glsl_set_params(unsigned width, unsigned height,
|
||||
const struct gl_tex_info *fbo_info, unsigned fbo_info_cnt)
|
||||
{
|
||||
// We enforce a certain layout for our various texture types in the texunits.
|
||||
// Unit 0: Regular SNES frame (rubyTexture).
|
||||
// Unit 1-A: LUT textures.
|
||||
// Unit A+1: Previous texture.
|
||||
// Unit A+2: Original texture.
|
||||
// Unit A+3-B: FBO textures.
|
||||
// - Regular SNES frame (rubyTexture) (always bound).
|
||||
// - LUT textures (always bound).
|
||||
// - Original texture (always bound if meaningful).
|
||||
// - FBO textures (always bound if available).
|
||||
// - Previous textures.
|
||||
|
||||
if (glsl_enable && gl_program[active_index] > 0)
|
||||
if (!glsl_enable || (gl_program[active_index] == 0))
|
||||
return;
|
||||
|
||||
GLint location;
|
||||
|
||||
float inputSize[2] = {width, height};
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyInputSize");
|
||||
pglUniform2fv(location, 1, inputSize);
|
||||
|
||||
float outputSize[2] = {out_width, out_height};
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyOutputSize");
|
||||
pglUniform2fv(location, 1, outputSize);
|
||||
|
||||
float textureSize[2] = {tex_width, tex_height};
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyTextureSize");
|
||||
pglUniform2fv(location, 1, textureSize);
|
||||
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyFrameCount");
|
||||
pglUniform1i(location, frame_count);
|
||||
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyFrameDirection");
|
||||
pglUniform1i(location, g_extern.frame_is_reverse ? -1 : 1);
|
||||
|
||||
for (unsigned i = 0; i < gl_teximage_cnt; i++)
|
||||
{
|
||||
GLint location;
|
||||
location = pglGetUniformLocation(gl_program[active_index], gl_teximage_uniforms[i]);
|
||||
pglUniform1i(location, i + 1);
|
||||
}
|
||||
|
||||
float inputSize[2] = {width, height};
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyInputSize");
|
||||
pglUniform2fv(location, 1, inputSize);
|
||||
unsigned texunit = gl_teximage_cnt + 1;
|
||||
|
||||
float outputSize[2] = {out_width, out_height};
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyOutputSize");
|
||||
pglUniform2fv(location, 1, outputSize);
|
||||
// Set original texture unless we're in first pass (pointless).
|
||||
if (active_index > 1)
|
||||
{
|
||||
// Bind original texture.
|
||||
pglActiveTexture(GL_TEXTURE0 + texunit);
|
||||
|
||||
float textureSize[2] = {tex_width, tex_height};
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyTextureSize");
|
||||
pglUniform2fv(location, 1, textureSize);
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyOrigTexture");
|
||||
pglUniform1i(location, texunit++);
|
||||
glBindTexture(GL_TEXTURE_2D, info->tex);
|
||||
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyFrameCount");
|
||||
pglUniform1i(location, frame_count);
|
||||
|
||||
for (unsigned i = 0; i < gl_teximage_cnt; i++)
|
||||
{
|
||||
location = pglGetUniformLocation(gl_program[active_index], gl_teximage_uniforms[i]);
|
||||
pglUniform1i(location, i + 1);
|
||||
}
|
||||
|
||||
// Set previous texture.
|
||||
pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 1);
|
||||
glBindTexture(GL_TEXTURE_2D, prev_info->tex);
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyPrevTexture");
|
||||
pglUniform1i(location, gl_teximage_cnt + 1);
|
||||
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyPrevTextureSize");
|
||||
pglUniform2fv(location, 1, prev_info->tex_size);
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyPrevInputSize");
|
||||
pglUniform2fv(location, 1, prev_info->input_size);
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyOrigTextureSize");
|
||||
pglUniform2fv(location, 1, info->tex_size);
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyOrigInputSize");
|
||||
pglUniform2fv(location, 1, info->input_size);
|
||||
|
||||
// Pass texture coordinates.
|
||||
location = pglGetAttribLocation(gl_program[active_index], "rubyPrevTexCoord");
|
||||
location = pglGetAttribLocation(gl_program[active_index], "rubyOrigTexCoord");
|
||||
if (location >= 0)
|
||||
{
|
||||
pglEnableVertexAttribArray(location);
|
||||
pglVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, prev_info->coord);
|
||||
pglVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, info->coord);
|
||||
gl_attribs[gl_attrib_index++] = location;
|
||||
}
|
||||
|
||||
// Set original texture unless we're in first pass (pointless).
|
||||
if (active_index > 1)
|
||||
// Bind new texture in the chain.
|
||||
if (fbo_info_cnt > 0)
|
||||
{
|
||||
// Bind original texture.
|
||||
pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 2);
|
||||
glBindTexture(GL_TEXTURE_2D, info->tex);
|
||||
pglActiveTexture(GL_TEXTURE0 + texunit + fbo_info_cnt - 1);
|
||||
glBindTexture(GL_TEXTURE_2D, fbo_info[fbo_info_cnt - 1].tex);
|
||||
}
|
||||
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyOrigTexture");
|
||||
pglUniform1i(location, gl_teximage_cnt + 2);
|
||||
// Bind FBO textures.
|
||||
for (unsigned i = 0; i < fbo_info_cnt; i++)
|
||||
{
|
||||
char attrib_buf[64];
|
||||
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyOrigTextureSize");
|
||||
pglUniform2fv(location, 1, info->tex_size);
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyOrigInputSize");
|
||||
pglUniform2fv(location, 1, info->input_size);
|
||||
snprintf(attrib_buf, sizeof(attrib_buf), "rubyPass%uTexture", i + 1);
|
||||
location = pglGetUniformLocation(gl_program[active_index], attrib_buf);
|
||||
pglUniform1i(location, texunit++);
|
||||
|
||||
// Pass texture coordinates.
|
||||
location = pglGetAttribLocation(gl_program[active_index], "rubyOrigTexCoord");
|
||||
snprintf(attrib_buf, sizeof(attrib_buf), "rubyPass%uTextureSize", i + 1);
|
||||
location = pglGetUniformLocation(gl_program[active_index], attrib_buf);
|
||||
pglUniform2fv(location, 1, fbo_info[i].tex_size);
|
||||
|
||||
snprintf(attrib_buf, sizeof(attrib_buf), "rubyPass%uInputSize", i + 1);
|
||||
location = pglGetUniformLocation(gl_program[active_index], attrib_buf);
|
||||
pglUniform2fv(location, 1, fbo_info[i].input_size);
|
||||
|
||||
snprintf(attrib_buf, sizeof(attrib_buf), "rubyPass%uTexCoord", i + 1);
|
||||
location = pglGetAttribLocation(gl_program[active_index], attrib_buf);
|
||||
if (location >= 0)
|
||||
{
|
||||
pglEnableVertexAttribArray(location);
|
||||
pglVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, info->coord);
|
||||
}
|
||||
|
||||
GLuint base_tex = GL_TEXTURE0 + gl_teximage_cnt + 3;
|
||||
|
||||
// Bind new texture in the chain.
|
||||
if (fbo_info_cnt > 0)
|
||||
{
|
||||
pglActiveTexture(base_tex + fbo_info_cnt - 1);
|
||||
glBindTexture(GL_TEXTURE_2D, fbo_info[fbo_info_cnt - 1].tex);
|
||||
}
|
||||
|
||||
// Bind FBO textures.
|
||||
for (unsigned i = 0; i < fbo_info_cnt; i++)
|
||||
{
|
||||
char attrib_buf[64];
|
||||
|
||||
snprintf(attrib_buf, sizeof(attrib_buf), "rubyPass%uTexture", i + 1);
|
||||
location = pglGetUniformLocation(gl_program[active_index], attrib_buf);
|
||||
pglUniform1i(location, base_tex + i - GL_TEXTURE0);
|
||||
|
||||
snprintf(attrib_buf, sizeof(attrib_buf), "rubyPass%uTextureSize", i + 1);
|
||||
location = pglGetUniformLocation(gl_program[active_index], attrib_buf);
|
||||
pglUniform2fv(location, 1, fbo_info[i].tex_size);
|
||||
|
||||
snprintf(attrib_buf, sizeof(attrib_buf), "rubyPass%uInputSize", i + 1);
|
||||
location = pglGetUniformLocation(gl_program[active_index], attrib_buf);
|
||||
pglUniform2fv(location, 1, fbo_info[i].input_size);
|
||||
|
||||
snprintf(attrib_buf, sizeof(attrib_buf), "rubyPass%uTexCoord", i + 1);
|
||||
location = pglGetAttribLocation(gl_program[active_index], attrib_buf);
|
||||
if (location >= 0)
|
||||
{
|
||||
pglEnableVertexAttribArray(location);
|
||||
pglVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, fbo_info[i].coord);
|
||||
}
|
||||
pglVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, fbo_info[i].coord);
|
||||
gl_attribs[gl_attrib_index++] = location;
|
||||
}
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
// First pass, so unbind everything to avoid collitions.
|
||||
// Unbind ORIG.
|
||||
pglActiveTexture(GL_TEXTURE0 + texunit);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
GLuint base_tex = texunit + 1;
|
||||
// Unbind any lurking FBO passes.
|
||||
// Rendering to a texture that is bound to a texture unit
|
||||
// sounds very shaky ... ;)
|
||||
for (unsigned i = 0; i < gl_num_programs; i++)
|
||||
{
|
||||
// First pass, so unbind everything to avoid collitions.
|
||||
// Unbind ORIG.
|
||||
pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 2);
|
||||
pglActiveTexture(GL_TEXTURE0 + base_tex + i);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
GLuint base_tex = GL_TEXTURE0 + gl_teximage_cnt + 3;
|
||||
// Unbind any lurking FBO passes.
|
||||
// Rendering to a texture that is bound to a texture unit
|
||||
// sounds very shaky ... ;)
|
||||
for (unsigned i = 0; i < gl_num_programs; i++)
|
||||
{
|
||||
pglActiveTexture(GL_TEXTURE0 + base_tex + i);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
// Set previous textures. Only bind if they're actually used.
|
||||
for (unsigned i = 0; i < PREV_TEXTURES; i++)
|
||||
{
|
||||
char attr_buf_tex[64];
|
||||
char attr_buf_tex_size[64];
|
||||
char attr_buf_input_size[64];
|
||||
char attr_buf_coord[64];
|
||||
static const char *prev_names[PREV_TEXTURES] = {
|
||||
"Prev",
|
||||
"Prev1",
|
||||
"Prev2",
|
||||
"Prev3",
|
||||
"Prev4",
|
||||
"Prev5",
|
||||
"Prev6",
|
||||
};
|
||||
|
||||
snprintf(attr_buf_tex, sizeof(attr_buf_tex), "ruby%sTexture", prev_names[i]);
|
||||
snprintf(attr_buf_tex_size, sizeof(attr_buf_tex_size), "ruby%sTextureSize", prev_names[i]);
|
||||
snprintf(attr_buf_input_size, sizeof(attr_buf_input_size), "ruby%sInputSize", prev_names[i]);
|
||||
snprintf(attr_buf_coord, sizeof(attr_buf_coord), "ruby%sTexCoord", prev_names[i]);
|
||||
|
||||
location = pglGetUniformLocation(gl_program[active_index], attr_buf_tex);
|
||||
if (location >= 0)
|
||||
{
|
||||
pglActiveTexture(GL_TEXTURE0 + texunit);
|
||||
glBindTexture(GL_TEXTURE_2D, prev_info[i].tex);
|
||||
pglUniform1i(location, texunit++);
|
||||
}
|
||||
|
||||
pglActiveTexture(GL_TEXTURE0);
|
||||
location = pglGetUniformLocation(gl_program[active_index], attr_buf_tex_size);
|
||||
pglUniform2fv(location, 1, prev_info[i].tex_size);
|
||||
location = pglGetUniformLocation(gl_program[active_index], attr_buf_input_size);
|
||||
pglUniform2fv(location, 1, prev_info[i].input_size);
|
||||
|
||||
if (gl_snes_tracker)
|
||||
// Pass texture coordinates.
|
||||
location = pglGetAttribLocation(gl_program[active_index], attr_buf_coord);
|
||||
if (location >= 0)
|
||||
{
|
||||
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);
|
||||
pglEnableVertexAttribArray(location);
|
||||
pglVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, prev_info[i].coord);
|
||||
gl_attribs[gl_attrib_index++] = location;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < cnt; i++)
|
||||
{
|
||||
location = pglGetUniformLocation(gl_program[active_index], info[i].id);
|
||||
pglUniform1i(location, info[i].value);
|
||||
}
|
||||
pglActiveTexture(GL_TEXTURE0);
|
||||
|
||||
if (gl_snes_tracker)
|
||||
{
|
||||
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);
|
||||
pglUniform1f(location, info[i].value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1161,6 +1236,8 @@ void gl_glsl_use(unsigned index)
|
||||
{
|
||||
if (glsl_enable)
|
||||
{
|
||||
gl_glsl_reset_attrib();
|
||||
|
||||
active_index = index;
|
||||
pglUseProgram(gl_program[index]);
|
||||
}
|
||||
|
||||
+9
-9
@@ -88,32 +88,32 @@ 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) ? 0xffffffffu : info->info[i].mask;
|
||||
tracker->info[i].mask = (info->info[i].mask == 0) ? -1u : 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);
|
||||
// If we don't have a valid pointer.
|
||||
static const uint8_t empty = 0;
|
||||
|
||||
switch (info->info[i].ram_type)
|
||||
{
|
||||
case SSNES_STATE_WRAM:
|
||||
tracker->info[i].ptr = info->wram;
|
||||
tracker->info[i].ptr = info->wram ? info->wram : ∅
|
||||
break;
|
||||
case SSNES_STATE_APURAM:
|
||||
tracker->info[i].ptr = info->apuram;
|
||||
tracker->info[i].ptr = info->apuram ? info->apuram : ∅
|
||||
break;
|
||||
case SSNES_STATE_OAM:
|
||||
tracker->info[i].ptr = info->oam;
|
||||
tracker->info[i].ptr = info->oam ? info->oam : ∅
|
||||
break;
|
||||
case SSNES_STATE_CGRAM:
|
||||
tracker->info[i].ptr = info->cgram;
|
||||
tracker->info[i].ptr = info->cgram ? info->cgram : ∅
|
||||
break;
|
||||
case SSNES_STATE_VRAM:
|
||||
tracker->info[i].ptr = info->vram;
|
||||
tracker->info[i].ptr = info->vram ? info->vram : ∅
|
||||
break;
|
||||
case SSNES_STATE_INPUT_SLOT1:
|
||||
tracker->info[i].input_ptr = &tracker->input_state[0];
|
||||
@@ -125,7 +125,7 @@ snes_tracker_t* snes_tracker_init(const struct snes_tracker_info *info)
|
||||
break;
|
||||
|
||||
default:
|
||||
tracker->info[i].ptr = NULL;
|
||||
tracker->info[i].ptr = ∅
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -39,6 +39,7 @@ enum snes_tracker_type
|
||||
|
||||
enum snes_ram_type
|
||||
{
|
||||
SSNES_STATE_NONE,
|
||||
SSNES_STATE_WRAM,
|
||||
SSNES_STATE_APURAM,
|
||||
SSNES_STATE_OAM,
|
||||
@@ -78,7 +79,7 @@ struct snes_tracker_info
|
||||
struct snes_tracker_uniform
|
||||
{
|
||||
const char *id;
|
||||
int value;
|
||||
float value;
|
||||
};
|
||||
|
||||
typedef struct snes_tracker snes_tracker_t;
|
||||
|
||||
+82
-65
@@ -184,6 +184,9 @@ static void set_fullscreen(xv_t *xv)
|
||||
static void xv_init_font(xv_t *xv, const char *font_path, unsigned font_size)
|
||||
{
|
||||
#ifdef HAVE_FREETYPE
|
||||
if (!g_settings.video.font_enable)
|
||||
return;
|
||||
|
||||
const char *path = font_path;
|
||||
if (!*path)
|
||||
path = font_renderer_get_default_font();
|
||||
@@ -326,7 +329,76 @@ static void render32_uyvy(xv_t *xv, const void *input_, unsigned width, unsigned
|
||||
}
|
||||
}
|
||||
|
||||
static void* xv_init(const video_info_t *video, const input_driver_t **input, void **input_data)
|
||||
struct format_desc
|
||||
{
|
||||
void (*render_16)(xv_t *xv, const void *input,
|
||||
unsigned width, unsigned height, unsigned pitch);
|
||||
void (*render_32)(xv_t *xv, const void *input,
|
||||
unsigned width, unsigned height, unsigned pitch);
|
||||
char components[4];
|
||||
unsigned luma_index[2];
|
||||
unsigned u_index;
|
||||
unsigned v_index;
|
||||
};
|
||||
|
||||
static const struct format_desc formats[] = {
|
||||
{
|
||||
.render_16 = render16_yuy2,
|
||||
.render_32 = render32_yuy2,
|
||||
.components = { 'Y', 'U', 'Y', 'V' },
|
||||
.luma_index = { 0, 2 },
|
||||
.u_index = 1,
|
||||
.v_index = 3,
|
||||
},
|
||||
{
|
||||
.render_16 = render16_uyvy,
|
||||
.render_32 = render32_uyvy,
|
||||
.components = { 'U', 'Y', 'V', 'Y' },
|
||||
.luma_index = { 1, 3 },
|
||||
.u_index = 0,
|
||||
.v_index = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static bool adaptor_set_format(xv_t *xv, Display *dpy, XvPortID port, const video_info_t *video)
|
||||
{
|
||||
int format_count;
|
||||
XvImageFormatValues *format = XvListImageFormats(xv->display, port, &format_count);
|
||||
if (!format)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < format_count; i++)
|
||||
{
|
||||
for (unsigned j = 0; j < sizeof(formats) / sizeof(formats[0]); j++)
|
||||
{
|
||||
if (format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked)
|
||||
{
|
||||
if (format[i].component_order[0] == formats[j].components[0] &&
|
||||
format[i].component_order[1] == formats[j].components[1] &&
|
||||
format[i].component_order[2] == formats[j].components[2] &&
|
||||
format[i].component_order[3] == formats[j].components[3])
|
||||
{
|
||||
xv->fourcc = format[i].id;
|
||||
xv->render_func = video->rgb32 ? formats[j].render_32 : formats[j].render_16;
|
||||
|
||||
#ifdef HAVE_FREETYPE
|
||||
xv->luma_index[0] = formats[j].luma_index[0];
|
||||
xv->luma_index[1] = formats[j].luma_index[1];
|
||||
xv->chroma_u_index = formats[j].u_index;
|
||||
xv->chroma_v_index = formats[j].v_index;
|
||||
#endif
|
||||
XFree(format);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XFree(format);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void *xv_init(const video_info_t *video, const input_driver_t **input, void **input_data)
|
||||
{
|
||||
xv_t *xv = calloc(1, sizeof(*xv));
|
||||
if (!xv)
|
||||
@@ -353,17 +425,20 @@ static void* xv_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
if (adaptor_info[i].num_formats < 1) continue;
|
||||
if (!(adaptor_info[i].type & XvInputMask)) continue;
|
||||
if (!(adaptor_info[i].type & XvImageMask)) continue;
|
||||
if (!adaptor_set_format(xv, xv->display, adaptor_info[i].base_id, video)) continue;
|
||||
|
||||
xv->port = adaptor_info[i].base_id;
|
||||
xv->depth = adaptor_info[i].formats->depth;
|
||||
xv->visualid = adaptor_info[i].formats->visual_id;
|
||||
|
||||
SSNES_LOG("XVideo: Found suitable XvPort #%u\n", (unsigned)xv->port);
|
||||
break;
|
||||
}
|
||||
XvFreeAdaptorInfo(adaptor_info);
|
||||
|
||||
if (xv->port == 0)
|
||||
{
|
||||
SSNES_ERR("XVideo: Failed to find valid XvPort.\n");
|
||||
SSNES_ERR("XVideo: Failed to find valid XvPort or format.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -387,8 +462,8 @@ static void* xv_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
attributes.border_pixel = 0;
|
||||
attributes.event_mask = StructureNotifyMask | DestroyNotify | ClientMessage;
|
||||
|
||||
unsigned width = video->fullscreen ? ((video->width == 0) ? 256 : video->width) : video->width;
|
||||
unsigned height = video->fullscreen ? ((video->height == 0) ? 224 : video->height) : video->height;
|
||||
unsigned width = video->fullscreen ? ((video->width == 0) ? g_extern.system.geom.base_width : video->width) : video->width;
|
||||
unsigned height = video->fullscreen ? ((video->height == 0) ? g_extern.system.geom.base_height : video->height) : video->height;
|
||||
xv->window = XCreateWindow(xv->display, DefaultRootWindow(xv->display),
|
||||
0, 0, width, height,
|
||||
0, xv->depth, InputOutput, visualinfo->visual,
|
||||
@@ -413,66 +488,8 @@ static void* xv_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
Atom atom = XInternAtom(xv->display, "XV_AUTOPAINT_COLORKEY", true);
|
||||
if (atom != None) XvSetPortAttribute(xv->display, xv->port, atom, 1);
|
||||
|
||||
int format_count;
|
||||
XvImageFormatValues *format = XvListImageFormats(xv->display, xv->port, &format_count);
|
||||
|
||||
bool has_format = false;
|
||||
for (int i = 0; i < format_count; i++)
|
||||
{
|
||||
if (format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked)
|
||||
{
|
||||
if (format[i].component_order[0] == 'Y' && format[i].component_order[1] == 'U'
|
||||
&& format[i].component_order[2] == 'Y' && format[i].component_order[3] == 'V')
|
||||
{
|
||||
has_format = true;
|
||||
xv->fourcc = format[i].id;
|
||||
xv->render_func = video->rgb32 ? render32_yuy2 : render16_yuy2;
|
||||
|
||||
#ifdef HAVE_FREETYPE
|
||||
xv->luma_index[0] = 0;
|
||||
xv->luma_index[1] = 2;
|
||||
xv->chroma_u_index = 1;
|
||||
xv->chroma_v_index = 3;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_format)
|
||||
{
|
||||
for (int i = 0; i < format_count; i++)
|
||||
{
|
||||
if (format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked)
|
||||
{
|
||||
if (format[i].component_order[0] == 'U' && format[i].component_order[1] == 'Y'
|
||||
&& format[i].component_order[2] == 'V' && format[i].component_order[3] == 'Y')
|
||||
{
|
||||
has_format = true;
|
||||
xv->fourcc = format[i].id;
|
||||
xv->render_func = video->rgb32 ? render32_uyvy : render16_uyvy;
|
||||
|
||||
#ifdef HAVE_FREETYPE
|
||||
xv->luma_index[0] = 1;
|
||||
xv->luma_index[1] = 3;
|
||||
xv->chroma_u_index = 0;
|
||||
xv->chroma_v_index = 2;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(format);
|
||||
if (!has_format)
|
||||
{
|
||||
SSNES_ERR("XVideo: unable to find a supported image format.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
xv->width = 512;
|
||||
xv->height = 512;
|
||||
xv->width = g_extern.system.geom.max_width;
|
||||
xv->height = g_extern.system.geom.max_height;
|
||||
|
||||
xv->image = XvShmCreateImage(xv->display, xv->port, xv->fourcc, NULL, xv->width, xv->height, &xv->shminfo);
|
||||
if (!xv->image)
|
||||
@@ -679,7 +696,7 @@ static void xv_render_msg(xv_t *xv, const char *msg, unsigned width, unsigned he
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool xv_frame(void *data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
|
||||
static bool xv_frame(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
|
||||
{
|
||||
xv_t *xv = data;
|
||||
|
||||
|
||||
+1
-1
@@ -123,7 +123,7 @@ static void init_lut(void)
|
||||
keysym_lut[lut_binds[i].sk] = lut_binds[i].sdl;
|
||||
}
|
||||
|
||||
static void* sdl_input_init(void)
|
||||
static void *sdl_input_init(void)
|
||||
{
|
||||
init_lut();
|
||||
sdl_input_t *sdl = calloc(1, sizeof(*sdl));
|
||||
|
||||
+1
-1
@@ -133,7 +133,7 @@ static void init_lut(void)
|
||||
keysym_lut[lut_binds[i].sk] = lut_binds[i].x;
|
||||
}
|
||||
|
||||
static void* x_input_init(void)
|
||||
static void *x_input_init(void)
|
||||
{
|
||||
x11_input_t *x11 = calloc(1, sizeof(*x11));
|
||||
if (!x11)
|
||||
|
||||
+30
@@ -66,6 +66,36 @@ extern "C" {
|
||||
#define SNES_MEMORY_OAM 103
|
||||
#define SNES_MEMORY_CGRAM 104
|
||||
|
||||
// SSNES extension. Not required to be implemented for a working implementation.
|
||||
#define SNES_ENVIRONMENT_GET_FULLPATH 0 // const char ** -- Full path of game loaded.
|
||||
#define SNES_ENVIRONMENT_SET_GEOMETRY 1 // const struct snes_geometry * -- Window geometry information for the system/game.
|
||||
#define SNES_ENVIRONMENT_SET_PITCH 2 // const unsigned * -- Pitch of game image.
|
||||
#define SNES_ENVIRONMENT_GET_OVERSCAN 3 // bool * -- Boolean value whether or not the implementation should use overscan.
|
||||
#define SNES_ENVIRONMENT_SET_TIMING 4 // const struct snes_system_timing * -- Set exact timings of the system.
|
||||
// Used primarily for video recording.
|
||||
#define SNES_ENVIRONMENT_GET_CAN_DUPE 5 // bool * -- Boolean value whether or not SSNES supports frame duping,
|
||||
// passing NULL to video frame callback.
|
||||
|
||||
struct snes_geometry
|
||||
{
|
||||
unsigned base_width; // Nominal video width of system.
|
||||
unsigned base_height; // Nominal video height of system.
|
||||
unsigned max_width; // Maximum possible width of system.
|
||||
unsigned max_height; // Maximum possible height of system.
|
||||
};
|
||||
|
||||
struct snes_system_timing
|
||||
{
|
||||
double fps;
|
||||
double sample_rate;
|
||||
};
|
||||
|
||||
typedef bool (*snes_environment_t)(unsigned cmd, void *data);
|
||||
|
||||
// Must be called before calling snes_init().
|
||||
void snes_set_environment(snes_environment_t);
|
||||
////
|
||||
|
||||
typedef void (*snes_video_refresh_t)(const uint16_t *data, unsigned width, unsigned height);
|
||||
typedef void (*snes_audio_sample_t)(uint16_t left, uint16_t right);
|
||||
typedef void (*snes_input_poll_t)(void);
|
||||
|
||||
@@ -88,13 +88,12 @@ struct bsv_movie
|
||||
FILE *file;
|
||||
uint8_t *state;
|
||||
|
||||
long *frame_pos; // A ring buffer keeping track of many key presses were requested per frame.
|
||||
size_t *frame_pos; // A ring buffer keeping track of positions in the file for each frame.
|
||||
size_t frame_mask;
|
||||
uint16_t current_frame_count;
|
||||
size_t frame_ptr;
|
||||
|
||||
bool playback;
|
||||
unsigned min_file_pos;
|
||||
size_t min_file_pos;
|
||||
|
||||
bool first_rewind;
|
||||
bool did_rewind;
|
||||
@@ -103,7 +102,7 @@ struct bsv_movie
|
||||
#define BSV_MAGIC 0x42535631
|
||||
|
||||
#define MAGIC_INDEX 0
|
||||
#define SERIALIZER_INDEX 1
|
||||
#define SERIALIZER_INDEX 1 // Not current used.
|
||||
#define CRC_INDEX 2
|
||||
#define STATE_SIZE_INDEX 3
|
||||
|
||||
@@ -154,25 +153,32 @@ static bool init_playback(bsv_movie_t *handle, const char *path)
|
||||
}
|
||||
|
||||
uint32_t state_size = swap_if_big32(header[STATE_SIZE_INDEX]);
|
||||
handle->state = malloc(state_size);
|
||||
if (!handle->state)
|
||||
return false;
|
||||
|
||||
// If we're playing back from the start, state_size is 0.
|
||||
if (state_size)
|
||||
{
|
||||
handle->state = malloc(state_size);
|
||||
if (!handle->state)
|
||||
return false;
|
||||
|
||||
if (fread(handle->state, 1, state_size, handle->file) != state_size)
|
||||
{
|
||||
SSNES_ERR("Couldn't read state from movie.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (psnes_serialize_size() != state_size)
|
||||
{
|
||||
SSNES_ERR("Movie format seems to have a different serializer version. Cannot continue.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Unserialize to start playback.
|
||||
psnes_unserialize(handle->state, state_size);
|
||||
}
|
||||
|
||||
handle->min_file_pos = sizeof(header) + state_size;
|
||||
|
||||
if (fread(handle->state, 1, state_size, handle->file) != state_size)
|
||||
{
|
||||
SSNES_ERR("Couldn't read state from movie.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (psnes_serialize_size() != state_size)
|
||||
{
|
||||
SSNES_ERR("Movie format seems to have a different serializer version. Cannot continue.\n");
|
||||
exit(1);
|
||||
}
|
||||
// Unserialize to start playback.
|
||||
psnes_unserialize(handle->state, state_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -187,10 +193,10 @@ static bool init_record(bsv_movie_t *handle, const char *path)
|
||||
|
||||
uint32_t header[4] = {0};
|
||||
header[MAGIC_INDEX] = swap_if_big32(BSV_MAGIC);
|
||||
//header[SERIALIZER_INDEX] = 0; // TODO: Fix this.
|
||||
header[CRC_INDEX] = swap_if_big32(g_extern.cart_crc);
|
||||
|
||||
uint32_t state_size = psnes_serialize_size();
|
||||
|
||||
header[STATE_SIZE_INDEX] = swap_if_big32(state_size);
|
||||
fwrite(header, 4, sizeof(uint32_t), handle->file);
|
||||
|
||||
@@ -200,8 +206,12 @@ static bool init_record(bsv_movie_t *handle, const char *path)
|
||||
|
||||
handle->min_file_pos = sizeof(header) + state_size;
|
||||
|
||||
psnes_serialize(handle->state, state_size);
|
||||
fwrite(handle->state, 1, state_size, handle->file);
|
||||
if (state_size > 0)
|
||||
{
|
||||
psnes_serialize(handle->state, state_size);
|
||||
fwrite(handle->state, 1, state_size, handle->file);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -245,8 +255,11 @@ bsv_movie_t *bsv_movie_init(const char *path, enum ssnes_movie_type type)
|
||||
else if (!init_record(handle, path))
|
||||
goto error;
|
||||
|
||||
if (!(handle->frame_pos = calloc((1 << 20), sizeof(long)))) // Just pick something really large :D
|
||||
// Just pick something really large :D ~1 million frames rewind should do the trick.
|
||||
if (!(handle->frame_pos = calloc((1 << 20), sizeof(size_t))))
|
||||
goto error;
|
||||
|
||||
handle->frame_pos[0] = handle->min_file_pos;
|
||||
handle->frame_mask = (1 << 20) - 1;
|
||||
|
||||
return handle;
|
||||
@@ -258,14 +271,11 @@ error:
|
||||
|
||||
void bsv_movie_set_frame_start(bsv_movie_t *handle)
|
||||
{
|
||||
//fprintf(stderr, "Starting frame: %u, Pos: %ld\n", (unsigned)handle->frame_ptr, ftell(handle->file));
|
||||
handle->frame_pos[handle->frame_ptr] = ftell(handle->file);
|
||||
|
||||
}
|
||||
|
||||
void bsv_movie_set_frame_end(bsv_movie_t *handle)
|
||||
{
|
||||
//fprintf(stderr, "Frame++\n");
|
||||
handle->frame_ptr = (handle->frame_ptr + 1) & handle->frame_mask;
|
||||
|
||||
handle->first_rewind = !handle->did_rewind;
|
||||
@@ -274,22 +284,27 @@ void bsv_movie_set_frame_end(bsv_movie_t *handle)
|
||||
|
||||
void bsv_movie_frame_rewind(bsv_movie_t *handle)
|
||||
{
|
||||
//fprintf(stderr, "Frame--\n");
|
||||
|
||||
handle->did_rewind = true;
|
||||
if (handle->frame_ptr <= 1)
|
||||
|
||||
// If we're at the beginning ... :)
|
||||
if ((handle->frame_ptr <= 1) && (handle->frame_pos[0] == handle->min_file_pos))
|
||||
{
|
||||
handle->frame_ptr = 0;
|
||||
fseek(handle->file, handle->min_file_pos, SEEK_SET);
|
||||
}
|
||||
else
|
||||
{
|
||||
// First time rewind is performed, the old frame is simply replayed.
|
||||
// However, playing back that frame caused us to read data, and push data to the ring buffer.
|
||||
// Sucessively rewinding frames, we need to rewind past the read data, plus another.
|
||||
handle->frame_ptr = (handle->frame_ptr - (handle->first_rewind ? 1 : 2)) & handle->frame_mask;
|
||||
fseek(handle->file, handle->frame_pos[handle->frame_ptr], SEEK_SET);
|
||||
}
|
||||
|
||||
// We rewound past the beginning. :O
|
||||
if (ftell(handle->file) <= (long)handle->min_file_pos)
|
||||
{
|
||||
// If recording, we simply reset the starting point. Nice and easy.
|
||||
if (!handle->playback)
|
||||
{
|
||||
fseek(handle->file, 4 * sizeof(uint32_t), SEEK_SET);
|
||||
|
||||
@@ -69,7 +69,7 @@ struct delta_frame
|
||||
bool used_real;
|
||||
};
|
||||
|
||||
#define UDP_FRAME_PACKETS 8
|
||||
#define UDP_FRAME_PACKETS 16
|
||||
|
||||
struct netplay
|
||||
{
|
||||
@@ -99,6 +99,8 @@ struct netplay
|
||||
struct addrinfo *addr;
|
||||
struct sockaddr_storage their_addr;
|
||||
bool has_client_addr;
|
||||
|
||||
unsigned timeout_cnt;
|
||||
};
|
||||
|
||||
static void warn_hangup(void)
|
||||
@@ -331,9 +333,9 @@ static bool get_info(netplay_t *handle)
|
||||
SSNES_ERR("Cart CRC32s differ! Cannot use different games!\n");
|
||||
return false;
|
||||
}
|
||||
if (psnes_serialize_size() != ntohl(header[1]))
|
||||
if (implementation_magic_value() != ntohl(header[1]))
|
||||
{
|
||||
SSNES_ERR("Serialization sizes differ, make sure you're using exact same libsnes implementations!\n");
|
||||
SSNES_ERR("Implementations differ, make sure you're using exact same libsnes implementations!\n");
|
||||
return false;
|
||||
}
|
||||
if (psnes_get_memory_size(SNES_MEMORY_CARTRIDGE_RAM) != ntohl(header[2]))
|
||||
@@ -373,6 +375,9 @@ static void init_buffers(netplay_t *handle)
|
||||
|
||||
netplay_t *netplay_new(const char *server, uint16_t port, unsigned frames, const struct snes_callbacks *cb)
|
||||
{
|
||||
if (frames > UDP_FRAME_PACKETS)
|
||||
frames = UDP_FRAME_PACKETS;
|
||||
|
||||
netplay_t *handle = calloc(1, sizeof(*handle));
|
||||
if (!handle)
|
||||
return NULL;
|
||||
@@ -439,26 +444,32 @@ static bool send_chunk(netplay_t *handle)
|
||||
return true;
|
||||
}
|
||||
|
||||
#define MAX_RETRIES 64
|
||||
#define MAX_RETRIES 16
|
||||
#define RETRY_MS 500
|
||||
|
||||
static int poll_input(netplay_t *handle, bool block)
|
||||
{
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(handle->udp_fd, &fds);
|
||||
FD_SET(handle->fd, &fds);
|
||||
int max_fd = (handle->fd > handle->udp_fd ? handle->fd : handle->udp_fd) + 1;
|
||||
|
||||
struct timeval tv = {
|
||||
const struct timeval tv = {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = block ? (RETRY_MS * 1000) : 0
|
||||
};
|
||||
|
||||
int i = 0;
|
||||
do
|
||||
{
|
||||
if (select(max_fd, &fds, NULL, NULL, &tv) < 0)
|
||||
{
|
||||
handle->timeout_cnt++;
|
||||
|
||||
// select() does not take pointer to const struct timeval.
|
||||
// Technically possible for select() to modify tmp_tv, so we go paranoia mode.
|
||||
struct timeval tmp_tv = tv;
|
||||
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(handle->udp_fd, &fds);
|
||||
FD_SET(handle->fd, &fds);
|
||||
|
||||
if (select(max_fd, &fds, NULL, NULL, &tmp_tv) < 0)
|
||||
return -1;
|
||||
|
||||
// Somewhat hacky, but we aren't using the TCP connection for anything useful atm.
|
||||
@@ -477,14 +488,11 @@ static int poll_input(netplay_t *handle, bool block)
|
||||
}
|
||||
|
||||
if (block)
|
||||
SSNES_LOG("Network lag of %d ms, resending packet... Attempt %d of %d ...\n", RETRY_MS, i, MAX_RETRIES);
|
||||
|
||||
// Seems to be necessary on Win32.
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(handle->udp_fd, &fds);
|
||||
FD_SET(handle->fd, &fds);
|
||||
|
||||
} while ((i++ < MAX_RETRIES) && block);
|
||||
{
|
||||
SSNES_LOG("Network is stalling, resending packet... Count %u of %d ...\n",
|
||||
handle->timeout_cnt, MAX_RETRIES);
|
||||
}
|
||||
} while ((handle->timeout_cnt < MAX_RETRIES) && block);
|
||||
|
||||
if (block)
|
||||
return -1;
|
||||
@@ -541,7 +549,7 @@ static void parse_packet(netplay_t *handle, uint32_t *buffer, unsigned size)
|
||||
|
||||
for (unsigned i = 0; i < size && handle->read_frame_count <= handle->frame_count; i++)
|
||||
{
|
||||
uint32_t frame = buffer[2 * i];
|
||||
uint32_t frame = buffer[2 * i + 0];
|
||||
uint32_t state = buffer[2 * i + 1];
|
||||
|
||||
if (frame == handle->read_frame_count)
|
||||
@@ -550,6 +558,7 @@ static void parse_packet(netplay_t *handle, uint32_t *buffer, unsigned size)
|
||||
handle->buffer[handle->read_ptr].real_input_state = state;
|
||||
handle->read_ptr = NEXT_PTR(handle->read_ptr);
|
||||
handle->read_frame_count++;
|
||||
handle->timeout_cnt = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-10
@@ -12,16 +12,17 @@ if [ -d /opt/local/lib ]; then
|
||||
add_library_dirs /opt/local/lib
|
||||
fi
|
||||
|
||||
if [ "$OS" = BSD ]; then
|
||||
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."
|
||||
exit 1
|
||||
if [ -z "$LIBSNES" ]; then
|
||||
LIBSNES="-lsnes"
|
||||
else
|
||||
echo "Explicit libsnes used, disabling dynamic libsnes loading ..."
|
||||
HAVE_DYNAMIC=no
|
||||
fi
|
||||
|
||||
if [ $HAVE_DYNAMIC != yes ]; then
|
||||
@@ -33,18 +34,23 @@ fi
|
||||
check_lib DYLIB $DYLIB dlopen
|
||||
check_lib NETPLAY -lc socket
|
||||
|
||||
if [ $HAVE_DYLIB = no ] && [ $HAVE_DYNAMIC = yes ]; then
|
||||
echo "Dynamic loading of libsnes is enabled, but your platform does not appear to have dlopen(), use --disable-dynamic or --with-libsnes=\"-lsnes\"".
|
||||
exit 1
|
||||
fi
|
||||
|
||||
check_pkgconf ALSA alsa
|
||||
check_header OSS sys/soundcard.h
|
||||
check_header OSS_BSD soundcard.h
|
||||
check_lib OSS_LIB -lossaudio
|
||||
|
||||
if [ "$OS" = "Darwin" ]; then
|
||||
if [ $OS = Darwin ]; then
|
||||
check_lib AL "-framework OpenAL" alcOpenDevice
|
||||
else
|
||||
check_lib AL -lopenal alcOpenDevice
|
||||
fi
|
||||
|
||||
if [ "$OS" = "Darwin" ]; then
|
||||
if [ $OS = Darwin ]; then
|
||||
check_lib FBO "-framework OpenGL" glFramebufferTexture2D
|
||||
else
|
||||
check_lib FBO -lGL glFramebufferTexture2D
|
||||
@@ -61,7 +67,12 @@ check_pkgconf SDL sdl 1.2.10
|
||||
check_critical SDL "Cannot find SDL library."
|
||||
|
||||
# On some distros, -lCg doesn't link against -lstdc++ it seems ...
|
||||
check_lib_cxx CG -lCg cgCreateContext
|
||||
if [ $HAVE_OPENGL != no ]; then
|
||||
check_lib_cxx CG -lCg cgCreateContext
|
||||
else
|
||||
echo "Ignoring Cg. OpenGL is not enabled."
|
||||
HAVE_CG=no
|
||||
fi
|
||||
|
||||
check_pkgconf XML libxml-2.0
|
||||
check_pkgconf SDL_IMAGE SDL_image
|
||||
@@ -80,12 +91,24 @@ if [ $HAVE_FFMPEG = yes ]; then
|
||||
check_lib FFMPEG_AVCODEC_OPEN2 "$AVCODEC_LIBS" avcodec_open2
|
||||
check_lib FFMPEG_AVIO_OPEN "$AVFORMAT_LIBS" avio_open
|
||||
check_lib FFMPEG_AVFORMAT_WRITE_HEADER "$AVFORMAT_LIBS" avformat_write_header
|
||||
check_lib FFMPEG_AVFORMAT_NEW_STREAM "$AVFORMAT_LIBS" avformat_new_stream
|
||||
fi
|
||||
|
||||
if [ $HAVE_FFMPEG = no ] && [ $HAVE_X264RGB = yes ]; then
|
||||
echo "x264 RGB recording is enabled, but FFmpeg is not. --enable-x264rgb will not have any effect."
|
||||
fi
|
||||
|
||||
check_lib DYNAMIC $DYLIB dlopen
|
||||
|
||||
check_pkgconf FREETYPE freetype2
|
||||
check_pkgconf XVIDEO xv
|
||||
check_pkgconf X11 x11
|
||||
check_pkgconf XEXT xext
|
||||
if [ $HAVE_X11 = yes ] && [ $HAVE_XEXT = yes ]; then
|
||||
check_pkgconf XVIDEO xv
|
||||
else
|
||||
echo "X11 or Xext not present. Skipping XVideo."
|
||||
HAVE_XVIDEO=no
|
||||
fi
|
||||
|
||||
check_lib STRL -lc strlcpy
|
||||
|
||||
@@ -94,7 +117,7 @@ check_pkgconf PYTHON python3
|
||||
add_define_make OS $OS
|
||||
|
||||
# Creates config.mk and config.h.
|
||||
VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL DYLIB CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE CONFIGFILE FREETYPE XVIDEO NETPLAY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER X264RGB"
|
||||
VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL DYLIB CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE CONFIGFILE FREETYPE XVIDEO X11 XEXT NETPLAY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM X264RGB"
|
||||
create_config_make config.mk $VARS
|
||||
create_config_header config.h $VARS
|
||||
|
||||
+6
-5
@@ -1,19 +1,20 @@
|
||||
. qb/qb.params.sh
|
||||
|
||||
PACKAGE_NAME=ssnes
|
||||
PACKAGE_VERSION=0.9
|
||||
PACKAGE_VERSION=0.9.2
|
||||
|
||||
# Adds a command line opt to ./configure --help
|
||||
# $1: Variable (HAVE_ALSA, HAVE_OSS, etc)
|
||||
# $2: Comment
|
||||
# $3: Default arg. auto implies that HAVE_ALSA will be set according to library checks later on.
|
||||
add_command_line_enable DYNAMIC "Enable dynamic loading of libsnes library" no
|
||||
add_command_line_string LIBSNES "libsnes library used" "-lsnes"
|
||||
add_command_line_enable FFMPEG "Enable FFmpeg recording support" no
|
||||
add_command_line_enable DYNAMIC "Disable dynamic loading of libsnes library" yes
|
||||
add_command_line_string LIBSNES "libsnes library used" ""
|
||||
add_command_line_enable FFMPEG "Enable FFmpeg recording support" auto
|
||||
add_command_line_enable X264RGB "Enable lossless X264 RGB recording" no
|
||||
add_command_line_enable DYLIB "Enable dynamic loading support" auto
|
||||
add_command_line_enable NETPLAY "Enable netplay support" auto
|
||||
add_command_line_enable CONFIGFILE "Disable support for config file" yes
|
||||
add_command_line_enable OPENGL "Disable OpenGL support" yes
|
||||
add_command_line_enable CG "Enable Cg shader support" auto
|
||||
add_command_line_enable XML "Enable bSNES-style XML shader support" auto
|
||||
add_command_line_enable FBO "Enable render-to-texture (FBO) support" auto
|
||||
@@ -28,4 +29,4 @@ 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 SDL_IMAGE "Enable SDL_image support" auto
|
||||
add_command_line_enable PYTHON "Enable Python 3 support for shaders" no
|
||||
add_command_line_enable PYTHON "Enable Python 3 support for shaders" auto
|
||||
+1
-1
@@ -81,7 +81,7 @@ print_sub_str_opt()
|
||||
|
||||
lowertext="`echo $arg1 | tr '[A-Z]' '[a-z]'`"
|
||||
|
||||
echo "--with-$lowertext: $arg2 (Defaults: $arg3)"
|
||||
echo "--with-$lowertext: $arg2"
|
||||
}
|
||||
|
||||
parse_input()
|
||||
|
||||
+84
-55
@@ -22,6 +22,7 @@
|
||||
struct video_info
|
||||
{
|
||||
AVCodecContext *codec;
|
||||
AVCodec *encoder;
|
||||
|
||||
AVFrame *conv_frame;
|
||||
uint8_t *conv_frame_buf;
|
||||
@@ -32,7 +33,7 @@ struct video_info
|
||||
|
||||
int fmt;
|
||||
int pix_fmt;
|
||||
int pix_size;
|
||||
size_t pix_size;
|
||||
|
||||
AVFormatContext *format;
|
||||
|
||||
@@ -42,6 +43,7 @@ struct video_info
|
||||
struct audio_info
|
||||
{
|
||||
AVCodecContext *codec;
|
||||
AVCodec *encoder;
|
||||
|
||||
int16_t *buffer;
|
||||
size_t frames_in_buffer;
|
||||
@@ -85,6 +87,8 @@ static bool init_audio(struct audio_info *audio, struct ffemu_params *param)
|
||||
if (!codec)
|
||||
return false;
|
||||
|
||||
audio->encoder = codec;
|
||||
|
||||
// FFmpeg just loves to deprecate stuff :)
|
||||
#ifdef HAVE_FFMPEG_ALLOC_CONTEXT3
|
||||
audio->codec = avcodec_alloc_context3(codec);
|
||||
@@ -94,7 +98,7 @@ static bool init_audio(struct audio_info *audio, struct ffemu_params *param)
|
||||
#endif
|
||||
|
||||
audio->codec->sample_rate = param->samplerate;
|
||||
audio->codec->time_base = (AVRational) { 1, param->samplerate };
|
||||
audio->codec->time_base = av_d2q(1.0 / param->samplerate, 1000000);
|
||||
audio->codec->channels = param->channels;
|
||||
audio->codec->sample_fmt = AV_SAMPLE_FMT_S16;
|
||||
|
||||
@@ -129,6 +133,8 @@ static bool init_video(struct video_info *video, const struct ffemu_params *para
|
||||
if (!codec)
|
||||
return false;
|
||||
|
||||
video->encoder = codec;
|
||||
|
||||
#if AV_HAVE_BIGENDIAN
|
||||
video->fmt = PIX_FMT_RGB555BE;
|
||||
#else
|
||||
@@ -156,15 +162,17 @@ static bool init_video(struct video_info *video, const struct ffemu_params *para
|
||||
|
||||
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->time_base = av_d2q(1.0 / param->fps, 1000000); // Arbitrary big number.
|
||||
video->codec->sample_aspect_ratio = av_d2q(param->aspect_ratio * param->out_height / param->out_width, 255);
|
||||
video->codec->pix_fmt = video->pix_fmt;
|
||||
|
||||
#ifdef HAVE_FFMPEG_AVCODEC_OPEN2
|
||||
AVDictionary *opts = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_X264RGB
|
||||
video->codec->thread_count = 3;
|
||||
av_dict_set(&opts, "crf", "0", 0);
|
||||
av_dict_set(&opts, "qp", "0", 0);
|
||||
#else
|
||||
video->codec->thread_count = 2;
|
||||
#endif
|
||||
@@ -222,8 +230,12 @@ static bool init_muxer(ffemu_t *handle)
|
||||
return false;
|
||||
}
|
||||
|
||||
int stream_cnt = 0;
|
||||
#ifdef HAVE_FFMPEG_AVFORMAT_NEW_STREAM
|
||||
AVStream *stream = avformat_new_stream(ctx, handle->video.encoder);
|
||||
#else
|
||||
unsigned stream_cnt = 0;
|
||||
AVStream *stream = av_new_stream(ctx, stream_cnt++);
|
||||
#endif
|
||||
stream->codec = handle->video.codec;
|
||||
|
||||
if (ctx->oformat->flags & AVFMT_GLOBALHEADER)
|
||||
@@ -231,7 +243,11 @@ static bool init_muxer(ffemu_t *handle)
|
||||
handle->muxer.vstream = stream;
|
||||
handle->muxer.vstream->sample_aspect_ratio = handle->video.codec->sample_aspect_ratio;
|
||||
|
||||
#ifdef HAVE_FFMPEG_AVFORMAT_NEW_STREAM
|
||||
stream = avformat_new_stream(ctx, handle->audio.encoder);
|
||||
#else
|
||||
stream = av_new_stream(ctx, stream_cnt++);
|
||||
#endif
|
||||
stream->codec = handle->audio.codec;
|
||||
|
||||
if (ctx->oformat->flags & AVFMT_GLOBALHEADER)
|
||||
@@ -242,6 +258,8 @@ static bool init_muxer(ffemu_t *handle)
|
||||
ctx->oformat->flags |= AVFMT_TS_NONSTRICT;
|
||||
#endif
|
||||
|
||||
av_dict_set(&ctx->metadata, "title", "SSNES video dump", 0);
|
||||
|
||||
#ifdef HAVE_FFMPEG_AVFORMAT_WRITE_HEADER
|
||||
if (avformat_write_header(ctx, NULL) < 0)
|
||||
#else
|
||||
@@ -381,7 +399,7 @@ void ffemu_free(ffemu_t *handle)
|
||||
}
|
||||
}
|
||||
|
||||
int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
|
||||
bool ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
@@ -390,7 +408,7 @@ int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
|
||||
slock_unlock(handle->lock);
|
||||
|
||||
if (!handle->alive)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
if (avail >= sizeof(*data))
|
||||
break;
|
||||
@@ -412,21 +430,25 @@ int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
|
||||
|
||||
// Tightly pack our frame to conserve memory. libsnes tends to use a very large pitch.
|
||||
struct ffemu_video_data attr_data = *data;
|
||||
attr_data.pitch = attr_data.width * handle->video.pix_size;
|
||||
|
||||
if (attr_data.is_dupe)
|
||||
attr_data.width = attr_data.height = attr_data.pitch = 0;
|
||||
else
|
||||
attr_data.pitch = attr_data.width * handle->video.pix_size;
|
||||
|
||||
fifo_write(handle->attr_fifo, &attr_data, sizeof(attr_data));
|
||||
|
||||
unsigned offset = 0;
|
||||
for (unsigned y = 0; y < data->height; y++, offset += data->pitch)
|
||||
for (unsigned y = 0; y < attr_data.height; y++, offset += data->pitch)
|
||||
fifo_write(handle->video_fifo, (const uint8_t*)data->data + offset, attr_data.pitch);
|
||||
|
||||
slock_unlock(handle->lock);
|
||||
scond_signal(handle->cond);
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
|
||||
bool ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
@@ -435,7 +457,7 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
|
||||
slock_unlock(handle->lock);
|
||||
|
||||
if (!handle->alive)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
if (avail >= data->frames * handle->params.channels * sizeof(int16_t))
|
||||
break;
|
||||
@@ -458,19 +480,22 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
|
||||
slock_unlock(handle->lock);
|
||||
scond_signal(handle->cond);
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_data *data)
|
||||
static bool 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, handle->video.fmt,
|
||||
handle->params.out_width, handle->params.out_height, handle->video.pix_fmt, SWS_POINT,
|
||||
NULL, NULL, NULL);
|
||||
if (!data->is_dupe)
|
||||
{
|
||||
handle->video.sws_ctx = sws_getCachedContext(handle->video.sws_ctx, data->width, data->height, handle->video.fmt,
|
||||
handle->params.out_width, handle->params.out_height, handle->video.pix_fmt, SWS_POINT,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
int linesize = data->pitch;
|
||||
int linesize = data->pitch;
|
||||
|
||||
sws_scale(handle->video.sws_ctx, (const uint8_t* const*)&data->data, &linesize, 0,
|
||||
data->height, handle->video.conv_frame->data, handle->video.conv_frame->linesize);
|
||||
sws_scale(handle->video.sws_ctx, (const uint8_t* const*)&data->data, &linesize, 0,
|
||||
data->height, handle->video.conv_frame->data, handle->video.conv_frame->linesize);
|
||||
}
|
||||
|
||||
handle->video.conv_frame->pts = handle->video.frame_cnt;
|
||||
|
||||
@@ -478,7 +503,7 @@ static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_dat
|
||||
handle->video.outbuf_size, handle->video.conv_frame);
|
||||
|
||||
if (outsize < 0)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
AVPacket pkt;
|
||||
av_init_packet(&pkt);
|
||||
@@ -495,15 +520,15 @@ static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_dat
|
||||
if (pkt.size > 0)
|
||||
{
|
||||
if (av_interleaved_write_frame(handle->muxer.ctx, &pkt) < 0)
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
handle->video.frame_cnt++;
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int ffemu_push_audio_thread(ffemu_t *handle, const struct ffemu_audio_data *data)
|
||||
static bool ffemu_push_audio_thread(ffemu_t *handle, const struct ffemu_audio_data *data)
|
||||
{
|
||||
size_t written_frames = 0;
|
||||
while (written_frames < data->frames)
|
||||
@@ -525,13 +550,15 @@ static int ffemu_push_audio_thread(ffemu_t *handle, const struct ffemu_audio_dat
|
||||
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);
|
||||
int out_size = avcodec_encode_audio(handle->audio.codec,
|
||||
handle->audio.outbuf, handle->audio.outbuf_size, handle->audio.buffer);
|
||||
if (out_size < 0)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
pkt.size = out_size;
|
||||
|
||||
pkt.pts = av_rescale_q(handle->audio.codec->coded_frame->pts, handle->audio.codec->time_base, handle->muxer.astream->time_base);
|
||||
pkt.pts = av_rescale_q(handle->audio.codec->coded_frame->pts,
|
||||
handle->audio.codec->time_base, handle->muxer.astream->time_base);
|
||||
|
||||
pkt.flags |= AV_PKT_FLAG_KEY;
|
||||
handle->audio.frames_in_buffer = 0;
|
||||
@@ -540,25 +567,30 @@ static int ffemu_push_audio_thread(ffemu_t *handle, const struct ffemu_audio_dat
|
||||
if (pkt.size > 0)
|
||||
{
|
||||
if (av_interleaved_write_frame(handle->muxer.ctx, &pkt) < 0)
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int ffemu_finalize(ffemu_t *handle)
|
||||
bool ffemu_finalize(ffemu_t *handle)
|
||||
{
|
||||
deinit_thread(handle);
|
||||
|
||||
// Push out audio still in queue.
|
||||
size_t audio_buf_size = 512 * handle->params.channels * sizeof(int16_t);
|
||||
int16_t *audio_buf = av_malloc(audio_buf_size);
|
||||
if (audio_buf)
|
||||
void *video_buf = av_malloc(2 * handle->params.fb_width * handle->params.fb_height * handle->video.pix_size);
|
||||
|
||||
// Try pushing data in an interleaving pattern to ease the work of the muxer a bit.
|
||||
bool did_work;
|
||||
do
|
||||
{
|
||||
while (fifo_read_avail(handle->audio_fifo) >= audio_buf_size)
|
||||
did_work = false;
|
||||
|
||||
if (fifo_read_avail(handle->audio_fifo) >= audio_buf_size)
|
||||
{
|
||||
fifo_read(handle->audio_fifo, audio_buf, sizeof(audio_buf_size));
|
||||
fifo_read(handle->audio_fifo, audio_buf, audio_buf_size);
|
||||
|
||||
struct ffemu_audio_data aud = {
|
||||
.frames = 512,
|
||||
@@ -566,38 +598,35 @@ int ffemu_finalize(ffemu_t *handle)
|
||||
};
|
||||
|
||||
ffemu_push_audio_thread(handle, &aud);
|
||||
did_work = true;
|
||||
}
|
||||
|
||||
size_t avail = fifo_read_avail(handle->audio_fifo);
|
||||
fifo_read(handle->audio_fifo, audio_buf, avail);
|
||||
struct ffemu_audio_data aud = {
|
||||
.frames = avail / (sizeof(int16_t) * handle->params.channels),
|
||||
.data = audio_buf
|
||||
};
|
||||
|
||||
ffemu_push_audio_thread(handle, &aud);
|
||||
|
||||
av_free(audio_buf);
|
||||
}
|
||||
|
||||
// Push out frames still stuck in queue.
|
||||
void *video_buf = av_malloc(2 * handle->params.fb_width * handle->params.fb_height * handle->video.pix_size);
|
||||
if (video_buf)
|
||||
{
|
||||
struct ffemu_video_data attr_buf;
|
||||
while (fifo_read_avail(handle->attr_fifo) >= sizeof(attr_buf))
|
||||
if (fifo_read_avail(handle->attr_fifo) >= sizeof(attr_buf))
|
||||
{
|
||||
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);
|
||||
|
||||
did_work = true;
|
||||
}
|
||||
av_free(video_buf);
|
||||
}
|
||||
} while (did_work);
|
||||
|
||||
// Flush out last audio.
|
||||
size_t avail = fifo_read_avail(handle->audio_fifo);
|
||||
fifo_read(handle->audio_fifo, audio_buf, avail);
|
||||
struct ffemu_audio_data aud = {
|
||||
.frames = avail / (sizeof(int16_t) * handle->params.channels),
|
||||
.data = audio_buf
|
||||
};
|
||||
ffemu_push_audio_thread(handle, &aud);
|
||||
|
||||
deinit_thread_buf(handle);
|
||||
av_free(audio_buf);
|
||||
av_free(video_buf);
|
||||
|
||||
// Push out delayed frames. (MPEG codecs)
|
||||
// Flush out last video.
|
||||
AVPacket pkt;
|
||||
av_init_packet(&pkt);
|
||||
pkt.stream_index = handle->muxer.vstream->index;
|
||||
@@ -624,7 +653,7 @@ int ffemu_finalize(ffemu_t *handle)
|
||||
// Write final data.
|
||||
av_write_trailer(handle->muxer.ctx);
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ffemu_thread(void *data)
|
||||
|
||||
+12
-34
@@ -8,15 +8,14 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ffemu_rational
|
||||
{
|
||||
unsigned num;
|
||||
unsigned den;
|
||||
};
|
||||
|
||||
// Parameters passed to ffemu_new()
|
||||
struct ffemu_params
|
||||
{
|
||||
// FPS of input video.
|
||||
double fps;
|
||||
// Sample rate of input audio.
|
||||
double samplerate;
|
||||
|
||||
// Desired output resolution.
|
||||
unsigned out_width;
|
||||
unsigned out_height;
|
||||
@@ -24,38 +23,17 @@ struct ffemu_params
|
||||
// Total size of framebuffer used in input.
|
||||
unsigned fb_width;
|
||||
unsigned fb_height;
|
||||
|
||||
// Aspect ratio of input video. Parameters are passed to the muxer,
|
||||
// the video itself is not scaled.
|
||||
float aspect_ratio;
|
||||
|
||||
// FPS of video input.
|
||||
struct ffemu_rational fps;
|
||||
|
||||
// Relative video quality. 0 is lossless (if available), 10 is very low quality.
|
||||
// A value over 10 is codec defined if it will give even worse quality.
|
||||
unsigned videoq;
|
||||
|
||||
// Define some video codec dependent option. (E.g. h264 profiles)
|
||||
uint64_t video_opt;
|
||||
|
||||
// Audio sample rate.
|
||||
unsigned samplerate;
|
||||
|
||||
// Audio channels.
|
||||
unsigned channels;
|
||||
|
||||
// If input is ARGB or XRGB1555.
|
||||
bool rgb32;
|
||||
|
||||
// Audio bits. Sample format is always signed PCM in native byte order.
|
||||
//unsigned bits;
|
||||
|
||||
// Relative audio quality. 0 is lossless (if available), 10 is very low quality.
|
||||
// A value over 10 is codec defined if it will give even worse quality.
|
||||
// Some codecs might ignore this (lossless codecs such as FLAC).
|
||||
unsigned audioq;
|
||||
|
||||
// Define some audio codec dependent option.
|
||||
uint64_t audio_opt;
|
||||
|
||||
// Filename to dump to.
|
||||
const char *filename;
|
||||
};
|
||||
@@ -66,6 +44,7 @@ struct ffemu_video_data
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned pitch;
|
||||
bool is_dupe;
|
||||
};
|
||||
|
||||
struct ffemu_audio_data
|
||||
@@ -79,10 +58,9 @@ typedef struct ffemu ffemu_t;
|
||||
ffemu_t *ffemu_new(const struct ffemu_params *params);
|
||||
void ffemu_free(ffemu_t* handle);
|
||||
|
||||
int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data);
|
||||
int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data);
|
||||
int ffemu_finalize(ffemu_t *handle);
|
||||
|
||||
bool ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data);
|
||||
bool ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data);
|
||||
bool ffemu_finalize(ffemu_t *handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -21,13 +21,14 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
//#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include "general.h"
|
||||
|
||||
struct state_manager
|
||||
{
|
||||
uint64_t *buffer;
|
||||
size_t buf_size;
|
||||
size_t buf_size_mask;
|
||||
uint32_t *tmp_state;
|
||||
size_t top_ptr;
|
||||
size_t bottom_ptr;
|
||||
@@ -35,19 +36,51 @@ struct state_manager
|
||||
bool first_pop;
|
||||
};
|
||||
|
||||
static inline size_t nearest_pow2_size(size_t v)
|
||||
{
|
||||
size_t orig = v;
|
||||
v--;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
#if SIZE_MAX >= 0xffffu
|
||||
v |= v >> 8;
|
||||
#endif
|
||||
#if SIZE_MAX >= 0xfffffffflu
|
||||
v |= v >> 16;
|
||||
#endif
|
||||
#if SIZE_MAX >= 0xffffffffffffffffllu
|
||||
v |= v >> 32;
|
||||
#endif
|
||||
v++;
|
||||
|
||||
size_t next = v;
|
||||
size_t prev = v >> 1;
|
||||
|
||||
if ((next - orig) < (orig - prev))
|
||||
return next;
|
||||
else
|
||||
return prev;
|
||||
}
|
||||
|
||||
state_manager_t *state_manager_new(size_t state_size, size_t buffer_size, void *init_buffer)
|
||||
{
|
||||
if (buffer_size <= state_size * 2 + 1) // Need a sufficient buffer size.
|
||||
if (buffer_size <= state_size * 4) // Need a sufficient buffer size.
|
||||
return NULL;
|
||||
|
||||
state_manager_t *state = calloc(1, sizeof(*state));
|
||||
if (!state)
|
||||
return NULL;
|
||||
|
||||
assert(state_size % 4 == 0); // We need 4-byte aligned state_size to avoid having to enforce this with unneeded memcpy's!
|
||||
// We need 4-byte aligned state_size to avoid having to enforce this with unneeded memcpy's!
|
||||
assert(state_size % 4 == 0);
|
||||
state->top_ptr = 1;
|
||||
state->state_size = (state_size + 3) >> 2; // Multiple of 4.
|
||||
state->buf_size = (buffer_size + 7) >> 3; // Multiple of 8.
|
||||
|
||||
state->state_size = state_size / sizeof(uint32_t); // Works in multiple of 4.
|
||||
state->buf_size = nearest_pow2_size(buffer_size) / sizeof(uint64_t); // Works in multiple of 8.
|
||||
state->buf_size_mask = state->buf_size - 1;
|
||||
SSNES_LOG("Readjusted rewind buffer size to %u MiB\n", (unsigned)(sizeof(uint64_t) * (state->buf_size >> 20)));
|
||||
|
||||
if (!(state->buffer = calloc(1, state->buf_size * sizeof(uint64_t))))
|
||||
goto error;
|
||||
if (!(state->tmp_state = calloc(1, state->state_size * sizeof(uint32_t))))
|
||||
@@ -83,14 +116,11 @@ bool state_manager_pop(state_manager_t *state, void **data)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (state->top_ptr == 0)
|
||||
state->top_ptr = state->buf_size - 1;
|
||||
else
|
||||
state->top_ptr--;
|
||||
state->top_ptr = (state->top_ptr - 1) & state->buf_size_mask;
|
||||
|
||||
if (state->top_ptr == state->bottom_ptr) // Our stack is completely empty... :v
|
||||
{
|
||||
state->top_ptr = (state->top_ptr + 1) % state->buf_size;
|
||||
state->top_ptr = (state->top_ptr + 1) & state->buf_size_mask;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -101,15 +131,12 @@ bool state_manager_pop(state_manager_t *state, void **data)
|
||||
uint32_t xor = state->buffer[state->top_ptr] & 0xFFFFFFFFU;
|
||||
state->tmp_state[addr] ^= xor;
|
||||
|
||||
if (state->top_ptr == 0)
|
||||
state->top_ptr = state->buf_size - 1;
|
||||
else
|
||||
state->top_ptr--;
|
||||
state->top_ptr = (state->top_ptr - 1) & state->buf_size_mask;
|
||||
}
|
||||
|
||||
if (state->top_ptr == state->bottom_ptr) // Our stack is completely empty... :v
|
||||
{
|
||||
state->top_ptr = (state->top_ptr + 1) % state->buf_size;
|
||||
state->top_ptr = (state->top_ptr + 1) & state->buf_size_mask;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -118,33 +145,36 @@ bool state_manager_pop(state_manager_t *state, void **data)
|
||||
|
||||
static void reassign_bottom(state_manager_t *state)
|
||||
{
|
||||
state->bottom_ptr = (state->top_ptr + 1) % state->buf_size;
|
||||
state->bottom_ptr = (state->top_ptr + 1) & state->buf_size_mask;
|
||||
while (state->buffer[state->bottom_ptr]) // Skip ahead until we find the first 0 (boundary for state delta).
|
||||
state->bottom_ptr = (state->bottom_ptr + 1) % state->buf_size;
|
||||
state->bottom_ptr = (state->bottom_ptr + 1) & state->buf_size_mask;
|
||||
}
|
||||
|
||||
static void generate_delta(state_manager_t *state, const void *data)
|
||||
{
|
||||
//unsigned patch_size = 0;
|
||||
bool crossed = false;
|
||||
const uint32_t *old_state = state->tmp_state;
|
||||
const uint32_t *new_state = data;
|
||||
|
||||
state->buffer[state->top_ptr++] = 0; // For each separate delta, we have a 0 value sentinel in between.
|
||||
if (state->top_ptr == state->bottom_ptr) // Check if top_ptr and bottom_ptr crossed each other, which means we need to delete old cruft.
|
||||
state->top_ptr &= state->buf_size_mask;
|
||||
|
||||
// Check if top_ptr and bottom_ptr crossed each other, which means we need to delete old cruft.
|
||||
if (state->top_ptr == state->bottom_ptr)
|
||||
crossed = true;
|
||||
|
||||
for (uint64_t i = 0; i < state->state_size; i++)
|
||||
{
|
||||
uint64_t xor = old_state[i] ^ new_state[i];
|
||||
|
||||
// If the data differs (xor != 0), we push that xor on the stack with index and xor. This can be reversed by reapplying the xor.
|
||||
// This, if states don't really differ much, we'll save lots of space :) Hopefully this will work really well with save states.
|
||||
// If the data differs (xor != 0), we push that xor on the stack with index and xor.
|
||||
// This can be reversed by reapplying the xor.
|
||||
// This, if states don't really differ much, we'll save lots of space :)
|
||||
// Hopefully this will work really well with save states.
|
||||
if (xor)
|
||||
{
|
||||
//patch_size++;
|
||||
state->buffer[state->top_ptr] = (i << 32) | xor;
|
||||
state->top_ptr = (state->top_ptr + 1) % state->buf_size;
|
||||
state->top_ptr = (state->top_ptr + 1) & state->buf_size_mask;
|
||||
|
||||
if (state->top_ptr == state->bottom_ptr)
|
||||
crossed = true;
|
||||
@@ -153,8 +183,6 @@ static void generate_delta(state_manager_t *state, const void *data)
|
||||
|
||||
if (crossed)
|
||||
reassign_bottom(state);
|
||||
|
||||
//fprintf(stderr, "DELTA SIZE: %u, ORIG SIZE: %u\n", (unsigned)patch_size << 3, (unsigned)state->state_size << 2);
|
||||
}
|
||||
|
||||
bool state_manager_push(state_manager_t *state, const void *data)
|
||||
|
||||
+6
-6
@@ -69,12 +69,12 @@ static void dump_content(FILE *file, const uint16_t *frame, unsigned width, unsi
|
||||
for (unsigned i = 0; i < width; i++)
|
||||
{
|
||||
uint16_t pixel = *src++;
|
||||
uint8_t b = (pixel & 0x1f) << 3;
|
||||
uint8_t g = (pixel & 0x03e0) >> 2;
|
||||
uint8_t r = (pixel & 0x7c00) >> 7;
|
||||
*dst++ = b;
|
||||
*dst++ = g;
|
||||
*dst++ = r;
|
||||
uint8_t b = (pixel >> 0) & 0x1f;
|
||||
uint8_t g = (pixel >> 5) & 0x1f;
|
||||
uint8_t r = (pixel >> 10) & 0x1f;
|
||||
*dst++ = (b << 3) | (b >> 2);
|
||||
*dst++ = (g << 3) | (g >> 2);
|
||||
*dst++ = (r << 3) | (r >> 2);
|
||||
}
|
||||
|
||||
fwrite(line, 1, sizeof(line), file);
|
||||
|
||||
+11
-7
@@ -120,7 +120,6 @@ static void set_defaults(void)
|
||||
|
||||
g_settings.video.xscale = xscale;
|
||||
g_settings.video.yscale = yscale;
|
||||
g_settings.video.base_size = base_size;
|
||||
g_settings.video.fullscreen = g_extern.force_fullscreen ? true : fullscreen;
|
||||
g_settings.video.fullscreen_x = fullscreen_x;
|
||||
g_settings.video.fullscreen_y = fullscreen_y;
|
||||
@@ -130,8 +129,9 @@ static void set_defaults(void)
|
||||
g_settings.video.smooth = video_smooth;
|
||||
g_settings.video.force_aspect = force_aspect;
|
||||
g_settings.video.crop_overscan = crop_overscan;
|
||||
g_settings.video.aspect_ratio = SNES_ASPECT_RATIO;
|
||||
g_settings.video.aspect_ratio = -1.0f; // Automatic
|
||||
g_settings.video.shader_type = SSNES_SHADER_AUTO;
|
||||
g_settings.video.font_enable = font_enable;
|
||||
|
||||
#ifdef HAVE_FREETYPE
|
||||
g_settings.video.font_size = font_size;
|
||||
@@ -150,12 +150,12 @@ static void set_defaults(void)
|
||||
g_settings.video.second_pass_smooth = second_pass_smooth;
|
||||
#endif
|
||||
|
||||
g_settings.video.refresh_rate = refresh_rate;
|
||||
g_settings.video.hires_record = hires_record;
|
||||
g_settings.video.post_filter_record = post_filter_record;
|
||||
|
||||
g_settings.audio.enable = audio_enable;
|
||||
g_settings.audio.out_rate = out_rate;
|
||||
g_settings.audio.in_rate = in_rate;
|
||||
g_settings.audio.rate_step = audio_rate_step;
|
||||
if (audio_device)
|
||||
strlcpy(g_settings.audio.device, audio_device, sizeof(g_settings.audio.device));
|
||||
@@ -302,7 +302,6 @@ static void parse_config_file(void)
|
||||
|
||||
CONFIG_GET_DOUBLE(video.xscale, "video_xscale");
|
||||
CONFIG_GET_DOUBLE(video.yscale, "video_yscale");
|
||||
CONFIG_GET_INT(video.base_size, "video_base_size");
|
||||
CONFIG_GET_INT(video.fullscreen_x, "video_fullscreen_x");
|
||||
CONFIG_GET_INT(video.fullscreen_y, "video_fullscreen_y");
|
||||
|
||||
@@ -318,6 +317,7 @@ static void parse_config_file(void)
|
||||
CONFIG_GET_BOOL(video.force_aspect, "video_force_aspect");
|
||||
CONFIG_GET_BOOL(video.crop_overscan, "video_crop_overscan");
|
||||
CONFIG_GET_DOUBLE(video.aspect_ratio, "video_aspect_ratio");
|
||||
CONFIG_GET_DOUBLE(video.refresh_rate, "video_refresh_rate");
|
||||
|
||||
CONFIG_GET_STRING(video.cg_shader_path, "video_cg_shader");
|
||||
CONFIG_GET_STRING(video.bsnes_shader_path, "video_bsnes_shader");
|
||||
@@ -330,6 +330,7 @@ static void parse_config_file(void)
|
||||
#ifdef HAVE_FREETYPE
|
||||
CONFIG_GET_STRING(video.font_path, "video_font_path");
|
||||
CONFIG_GET_INT(video.font_size, "video_font_size");
|
||||
CONFIG_GET_BOOL(video.font_enable, "video_font_enable");
|
||||
CONFIG_GET_DOUBLE(video.msg_pos_x, "video_message_pos_x");
|
||||
CONFIG_GET_DOUBLE(video.msg_pos_y, "video_message_pos_y");
|
||||
|
||||
@@ -380,7 +381,6 @@ static void parse_config_file(void)
|
||||
// Audio settings.
|
||||
CONFIG_GET_BOOL(audio.enable, "audio_enable");
|
||||
CONFIG_GET_INT(audio.out_rate, "audio_out_rate");
|
||||
CONFIG_GET_DOUBLE(audio.in_rate, "audio_in_rate");
|
||||
CONFIG_GET_DOUBLE(audio.rate_step, "audio_rate_step");
|
||||
CONFIG_GET_STRING(audio.device, "audio_device");
|
||||
CONFIG_GET_INT(audio.latency, "audio_latency");
|
||||
@@ -390,7 +390,11 @@ static void parse_config_file(void)
|
||||
CONFIG_GET_STRING(audio.driver, "audio_driver");
|
||||
CONFIG_GET_STRING(audio.dsp_plugin, "audio_dsp_plugin");
|
||||
CONFIG_GET_STRING(input.driver, "input_driver");
|
||||
CONFIG_GET_STRING(libsnes, "libsnes_path");
|
||||
|
||||
if (!*g_settings.libsnes)
|
||||
{
|
||||
CONFIG_GET_STRING(libsnes, "libsnes_path");
|
||||
}
|
||||
|
||||
CONFIG_GET_STRING(screenshot_directory, "screenshot_directory");
|
||||
if (*g_settings.screenshot_directory && !path_is_directory(g_settings.screenshot_directory))
|
||||
@@ -402,7 +406,7 @@ static void parse_config_file(void)
|
||||
CONFIG_GET_BOOL(rewind_enable, "rewind_enable");
|
||||
|
||||
if (config_get_int(conf, "rewind_buffer_size", &tmp_int))
|
||||
g_settings.rewind_buffer_size = tmp_int * 1000000;
|
||||
g_settings.rewind_buffer_size = tmp_int * 1000000LLU;
|
||||
|
||||
CONFIG_GET_INT(rewind_granularity, "rewind_granularity");
|
||||
|
||||
|
||||
@@ -85,13 +85,31 @@ static void set_fast_forward_button(bool new_button_state, bool new_hold_button_
|
||||
old_hold_button_state = new_hold_button_state;
|
||||
}
|
||||
|
||||
static void take_screenshot(const uint16_t *frame, unsigned width, unsigned height)
|
||||
static inline unsigned lines_to_pitch(unsigned height)
|
||||
{
|
||||
if (g_extern.system.pitch == 0) // SNES semantics
|
||||
return ((height == 448) || (height == 478)) ? 1024 : 2048;
|
||||
else
|
||||
return g_extern.system.pitch;
|
||||
}
|
||||
|
||||
static void video_cached_frame(void);
|
||||
static void take_screenshot(void)
|
||||
{
|
||||
if (!(*g_settings.screenshot_directory))
|
||||
return;
|
||||
|
||||
bool ret = screenshot_dump(g_settings.screenshot_directory, frame,
|
||||
width, height, (height == 448 || height == 478) ? 1024 : 2048);
|
||||
bool ret = false;
|
||||
if (g_extern.frame_cache.data)
|
||||
{
|
||||
const uint16_t *data = g_extern.frame_cache.data;
|
||||
unsigned width = g_extern.frame_cache.width;
|
||||
unsigned height = g_extern.frame_cache.height;
|
||||
ret = screenshot_dump(g_settings.screenshot_directory,
|
||||
data,
|
||||
width, height,
|
||||
lines_to_pitch(height));
|
||||
}
|
||||
|
||||
const char *msg = NULL;
|
||||
if (ret)
|
||||
@@ -106,7 +124,34 @@ static void take_screenshot(const uint16_t *frame, unsigned width, unsigned heig
|
||||
}
|
||||
|
||||
msg_queue_clear(g_extern.msg_queue);
|
||||
msg_queue_push(g_extern.msg_queue, msg, 1, 180);
|
||||
|
||||
if (g_extern.is_paused)
|
||||
{
|
||||
msg_queue_push(g_extern.msg_queue, msg, 1, 1);
|
||||
video_cached_frame();
|
||||
}
|
||||
else
|
||||
msg_queue_push(g_extern.msg_queue, msg, 1, 180);
|
||||
}
|
||||
|
||||
|
||||
static inline void adjust_crop(const uint16_t **data, unsigned *height)
|
||||
{
|
||||
// Rather SNES specific.
|
||||
unsigned pixel_pitch = lines_to_pitch(*height) >> 1;
|
||||
if (g_settings.video.crop_overscan)
|
||||
{
|
||||
if (*height == 239)
|
||||
{
|
||||
*data += 7 * pixel_pitch; // Skip 7 top scanlines.
|
||||
*height = 224;
|
||||
}
|
||||
else if (*height == 478)
|
||||
{
|
||||
*data += 15 * pixel_pitch; // Skip 15 top scanlines.
|
||||
*height = 448;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// libsnes: 0.065
|
||||
@@ -116,38 +161,28 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
|
||||
if (!g_extern.video_active)
|
||||
return;
|
||||
|
||||
if (g_settings.video.crop_overscan)
|
||||
{
|
||||
if (height == 239)
|
||||
{
|
||||
data += 7 * 1024; // Skip 7 top scanlines.
|
||||
height = 224;
|
||||
}
|
||||
else if (height == 478)
|
||||
{
|
||||
data += 15 * 512; // Skip 15 top scanlines.
|
||||
height = 448;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_extern.do_screenshot)
|
||||
take_screenshot(data, width, height);
|
||||
bool is_dupe = !data;
|
||||
adjust_crop(&data, &height);
|
||||
|
||||
// Slightly messy code,
|
||||
// but we really need to do processing before blocking on VSync for best possible scheduling.
|
||||
#ifdef HAVE_FFMPEG
|
||||
if (g_extern.recording && (!g_extern.filter.active || !g_settings.video.post_filter_record))
|
||||
if (g_extern.recording && (!g_extern.filter.active || !g_settings.video.post_filter_record || is_dupe))
|
||||
{
|
||||
struct ffemu_video_data ffemu_data = {
|
||||
.data = data,
|
||||
.pitch = height == 448 || height == 478 ? 1024 : 2048,
|
||||
.pitch = lines_to_pitch(height),
|
||||
.width = width,
|
||||
.height = height,
|
||||
.is_dupe = is_dupe
|
||||
};
|
||||
ffemu_push_video(g_extern.rec, &ffemu_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (is_dupe)
|
||||
return;
|
||||
|
||||
const char *msg = msg_queue_pull(g_extern.msg_queue);
|
||||
|
||||
#ifdef HAVE_DYLIB
|
||||
@@ -157,7 +192,7 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
|
||||
unsigned oheight = height;
|
||||
g_extern.filter.psize(&owidth, &oheight);
|
||||
g_extern.filter.prender(g_extern.filter.colormap, g_extern.filter.buffer,
|
||||
g_extern.filter.pitch, data, (height == 448 || height == 478) ? 1024 : 2048, width, height);
|
||||
g_extern.filter.pitch, data, lines_to_pitch(height), width, height);
|
||||
|
||||
#ifdef HAVE_FFMPEG
|
||||
if (g_extern.recording && g_settings.video.post_filter_record)
|
||||
@@ -175,10 +210,10 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
|
||||
if (!driver.video->frame(driver.video_data, g_extern.filter.buffer, owidth, oheight, g_extern.filter.pitch, msg))
|
||||
g_extern.video_active = false;
|
||||
}
|
||||
else if (!driver.video->frame(driver.video_data, data, width, height, (height == 448 || height == 478) ? 1024 : 2048, msg))
|
||||
else if (!driver.video->frame(driver.video_data, data, width, height, lines_to_pitch(height), msg))
|
||||
g_extern.video_active = false;
|
||||
#else
|
||||
if (!driver.video->frame(driver.video_data, data, width, height, (height == 448 || height == 478) ? 1024 : 2048, msg))
|
||||
if (!driver.video->frame(driver.video_data, data, width, height, lines_to_pitch(height), msg))
|
||||
g_extern.video_active = false;
|
||||
#endif
|
||||
|
||||
@@ -227,6 +262,8 @@ static bool audio_flush(const int16_t *data, unsigned samples)
|
||||
|
||||
if (g_extern.is_paused)
|
||||
return true;
|
||||
if (!g_extern.audio_active)
|
||||
return false;
|
||||
|
||||
const float *output_data = NULL;
|
||||
unsigned output_frames = 0;
|
||||
@@ -245,20 +282,19 @@ static bool audio_flush(const int16_t *data, unsigned samples)
|
||||
if (g_extern.audio_data.dsp_plugin)
|
||||
g_extern.audio_data.dsp_plugin->process(g_extern.audio_data.dsp_handle, &dsp_output, &dsp_input);
|
||||
|
||||
struct hermite_data src_data = {
|
||||
.data_in = dsp_output.samples ? dsp_output.samples : g_extern.audio_data.data,
|
||||
.data_out = g_extern.audio_data.outsamples,
|
||||
.input_frames = dsp_output.samples ? dsp_output.frames : (samples / 2),
|
||||
.ratio = g_extern.audio_data.src_ratio,
|
||||
.output_frames = samples * g_extern.audio_data.src_ratio,
|
||||
};
|
||||
|
||||
if (dsp_output.should_resample)
|
||||
{
|
||||
struct hermite_data src_data = {
|
||||
.data_in = dsp_output.samples ? dsp_output.samples : g_extern.audio_data.data,
|
||||
.data_out = g_extern.audio_data.outsamples,
|
||||
.input_frames = dsp_output.samples ? dsp_output.frames : (samples / 2),
|
||||
.ratio = g_extern.audio_data.src_ratio,
|
||||
};
|
||||
|
||||
hermite_process(g_extern.audio_data.source, &src_data);
|
||||
|
||||
output_data = g_extern.audio_data.outsamples;
|
||||
output_frames = src_data.output_frames_gen;
|
||||
output_frames = src_data.output_frames;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -279,7 +315,8 @@ static bool audio_flush(const int16_t *data, unsigned samples)
|
||||
audio_convert_float_to_s16(g_extern.audio_data.conv_outsamples,
|
||||
output_data, output_frames * 2);
|
||||
|
||||
if (driver.audio->write(driver.audio_data, g_extern.audio_data.conv_outsamples, output_frames * sizeof(int16_t) * 2) < 0)
|
||||
if (driver.audio->write(driver.audio_data, g_extern.audio_data.conv_outsamples,
|
||||
output_frames * sizeof(int16_t) * 2) < 0)
|
||||
{
|
||||
fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n");
|
||||
return false;
|
||||
@@ -291,18 +328,12 @@ static bool audio_flush(const int16_t *data, unsigned samples)
|
||||
|
||||
static void audio_sample_rewind(uint16_t left, uint16_t right)
|
||||
{
|
||||
if (!g_extern.audio_active)
|
||||
return;
|
||||
|
||||
g_extern.audio_data.rewind_buf[--g_extern.audio_data.rewind_ptr] = right;
|
||||
g_extern.audio_data.rewind_buf[--g_extern.audio_data.rewind_ptr] = left;
|
||||
}
|
||||
|
||||
static void audio_sample(uint16_t left, uint16_t right)
|
||||
{
|
||||
if (!g_extern.audio_active)
|
||||
return;
|
||||
|
||||
g_extern.audio_data.conv_outsamples[g_extern.audio_data.data_ptr++] = left;
|
||||
g_extern.audio_data.conv_outsamples[g_extern.audio_data.data_ptr++] = right;
|
||||
|
||||
@@ -310,7 +341,7 @@ static void audio_sample(uint16_t left, uint16_t right)
|
||||
return;
|
||||
|
||||
g_extern.audio_active = audio_flush(g_extern.audio_data.conv_outsamples,
|
||||
g_extern.audio_data.data_ptr);
|
||||
g_extern.audio_data.data_ptr) && g_extern.audio_active;
|
||||
|
||||
g_extern.audio_data.data_ptr = 0;
|
||||
}
|
||||
@@ -322,16 +353,13 @@ static void input_poll(void)
|
||||
|
||||
static int16_t input_state(bool port, unsigned device, unsigned index, unsigned id)
|
||||
{
|
||||
if (g_extern.bsv_movie && g_extern.bsv_movie_playback)
|
||||
if (g_extern.bsv.movie && g_extern.bsv.movie_playback)
|
||||
{
|
||||
int16_t ret;
|
||||
if (bsv_movie_get_input(g_extern.bsv_movie, &ret))
|
||||
if (bsv_movie_get_input(g_extern.bsv.movie, &ret))
|
||||
return ret;
|
||||
else
|
||||
{
|
||||
g_extern.bsv_movie_end = true;
|
||||
return 0;
|
||||
}
|
||||
g_extern.bsv.movie_end = true;
|
||||
}
|
||||
|
||||
static const struct snes_keybind *binds[MAX_PLAYERS] = {
|
||||
@@ -343,11 +371,11 @@ static int16_t input_state(bool port, unsigned device, unsigned index, unsigned
|
||||
};
|
||||
|
||||
int16_t res = 0;
|
||||
if (id < 12)
|
||||
if (id < SSNES_FIRST_META_KEY)
|
||||
res = driver.input->input_state(driver.input_data, binds, port, device, index, id);
|
||||
|
||||
if (g_extern.bsv_movie && !g_extern.bsv_movie_playback)
|
||||
bsv_movie_set_input(g_extern.bsv_movie, res);
|
||||
if (g_extern.bsv.movie && !g_extern.bsv.movie_playback)
|
||||
bsv_movie_set_input(g_extern.bsv.movie, res);
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -361,7 +389,7 @@ static int16_t input_state(bool port, unsigned device, unsigned index, unsigned
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define PACKAGE_VERSION "0.9"
|
||||
#define PACKAGE_VERSION "0.9.2"
|
||||
#endif
|
||||
|
||||
#include "config.features.h"
|
||||
@@ -372,6 +400,7 @@ static void print_features(void)
|
||||
puts("");
|
||||
puts("Features:");
|
||||
_PSUPP(sdl, "SDL", "SDL drivers");
|
||||
_PSUPP(opengl, "OpenGL", "OpenGL driver");
|
||||
_PSUPP(xvideo, "XVideo", "XVideo output");
|
||||
_PSUPP(alsa, "ALSA", "audio driver");
|
||||
_PSUPP(oss, "OSS", "audio driver");
|
||||
@@ -409,6 +438,9 @@ static void print_help(void)
|
||||
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);
|
||||
#endif
|
||||
#ifdef HAVE_DYNAMIC
|
||||
puts("\t-L/--libsnes: Path to libsnes implementation. Overrides any config setting.");
|
||||
#endif
|
||||
puts("\t-g/--gameboy: Path to Gameboy ROM. Load SuperGameBoy as the regular rom.");
|
||||
puts("\t-b/--bsx: Path to BSX rom. Load BSX BIOS as the regular rom.");
|
||||
@@ -416,13 +448,18 @@ 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-N/--nodevice: Disconnects the controller device connected to the emulated SNES (1 or 2).");
|
||||
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.");
|
||||
puts("\t-J/--justifiers: Daisy chain two virtual Konami Justifiers into port 2 of the SNES.");
|
||||
puts("\t-4/--multitap: Connect a multitap to port 2 of the SNES.");
|
||||
puts("\t-P/--bsvplay: Playback a BSV movie file.");
|
||||
puts("\t-R/--bsvrecord: Start recording a BSV movie file from the beginning.");
|
||||
puts("\t-M/--sram-mode: Takes an argument telling how SRAM should be handled in the session.");
|
||||
puts("\t\t{no,}load-{no,}save describes if SRAM should be loaded, and if SRAM should be saved.");
|
||||
puts("\t\tDo note that noload-save implies that save files will be deleted and overwritten.");
|
||||
|
||||
#ifdef HAVE_NETPLAY
|
||||
puts("\t-H/--host: Host netplay as player 1.");
|
||||
puts("\t-C/--connect: Connect to netplay as player 2.");
|
||||
@@ -431,8 +468,8 @@ static void print_help(void)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FFMPEG
|
||||
puts("\t-r/--record: Path to record video file.\n\t\tUsing .mkv extension is recommended, and codecs used are FFV1/FLAC.");
|
||||
puts("\t--size: Overrides output video size when recording with FFmpeg (format: widthxheight).");
|
||||
puts("\t-r/--record: Path to record video file.\n\t\tUsing .mkv extension is recommended.");
|
||||
puts("\t--size: Overrides output video size when recording with FFmpeg (format: WIDTHxHEIGHT).");
|
||||
#endif
|
||||
puts("\t-v/--verbose: Verbose logging.");
|
||||
puts("\t-U/--ups: Specifies path for UPS patch that will be applied to ROM.");
|
||||
@@ -443,6 +480,8 @@ static void print_help(void)
|
||||
|
||||
static void set_basename(const char *path)
|
||||
{
|
||||
strlcpy(g_extern.system.fullpath, path, sizeof(g_extern.system.fullpath));
|
||||
|
||||
strlcpy(g_extern.basename, path, sizeof(g_extern.basename));
|
||||
char *dst = strrchr(g_extern.basename, '.');
|
||||
if (dst)
|
||||
@@ -476,6 +515,8 @@ static void set_paths(const char *path)
|
||||
fill_pathname_dir(g_extern.savestate_name, g_extern.basename, ".state", sizeof(g_extern.savestate_name));
|
||||
SSNES_LOG("Redirecting save state to \"%s\".\n", g_extern.savestate_name);
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
if (*g_extern.config_path && path_is_directory(g_extern.config_path))
|
||||
{
|
||||
fill_pathname_dir(g_extern.config_path, g_extern.basename, ".cfg", sizeof(g_extern.config_path));
|
||||
@@ -486,6 +527,7 @@ static void set_paths(const char *path)
|
||||
SSNES_LOG("Did not find config file. Using system default.\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void verify_stdin_paths(void)
|
||||
@@ -515,12 +557,15 @@ static void verify_stdin_paths(void)
|
||||
print_help();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
else if (path_is_directory(g_extern.config_path))
|
||||
{
|
||||
SSNES_ERR("Cannot specify directory for config file (--config) when reading from stdin.\n");
|
||||
print_help();
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void parse_input(int argc, char *argv[])
|
||||
@@ -534,6 +579,9 @@ static void parse_input(int argc, char *argv[])
|
||||
int val = 0;
|
||||
|
||||
struct option opts[] = {
|
||||
#ifdef HAVE_DYNAMIC
|
||||
{ "libsnes", 1, NULL, 'L' },
|
||||
#endif
|
||||
{ "help", 0, NULL, 'h' },
|
||||
{ "save", 1, NULL, 's' },
|
||||
{ "fullscreen", 0, NULL, 'f' },
|
||||
@@ -558,6 +606,8 @@ static void parse_input(int argc, char *argv[])
|
||||
{ "sufamiA", 1, NULL, 'Y' },
|
||||
{ "sufamiB", 1, NULL, 'Z' },
|
||||
{ "bsvplay", 1, NULL, 'P' },
|
||||
{ "bsvrecord", 1, NULL, 'R' },
|
||||
{ "sram-mode", 1, NULL, 'M' },
|
||||
{ "host", 0, NULL, 'H' },
|
||||
{ "connect", 1, NULL, 'C' },
|
||||
{ "frames", 1, NULL, 'F' },
|
||||
@@ -584,7 +634,13 @@ static void parse_input(int argc, char *argv[])
|
||||
#define CONFIG_FILE_ARG
|
||||
#endif
|
||||
|
||||
char optstring[] = "hs:fvS:m:p4jJg:b:B:Y:Z:P:HC:F:U:DN:X:" FFMPEG_RECORD_ARG CONFIG_FILE_ARG;
|
||||
#ifdef HAVE_DYNAMIC
|
||||
#define DYNAMIC_ARG "L:"
|
||||
#else
|
||||
#define DYNAMIC_ARG
|
||||
#endif
|
||||
|
||||
char optstring[] = "hs:fvS:m:p4jJg:b:B:Y:Z:P:R:M:HC:F:U:DN:X:" DYNAMIC_ARG FFMPEG_RECORD_ARG CONFIG_FILE_ARG;
|
||||
for (;;)
|
||||
{
|
||||
val = 0;
|
||||
@@ -694,9 +750,36 @@ static void parse_input(int argc, char *argv[])
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DYNAMIC
|
||||
case 'L':
|
||||
strlcpy(g_settings.libsnes, optarg, sizeof(g_settings.libsnes));
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 'P':
|
||||
strlcpy(g_extern.bsv_movie_path, optarg, sizeof(g_extern.bsv_movie_path));
|
||||
g_extern.bsv_movie_playback = true;
|
||||
case 'R':
|
||||
strlcpy(g_extern.bsv.movie_start_path, optarg,
|
||||
sizeof(g_extern.bsv.movie_start_path));
|
||||
g_extern.bsv.movie_start_playback = c == 'P';
|
||||
g_extern.bsv.movie_start_recording = c == 'R';
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
if (strcmp(optarg, "noload-nosave") == 0)
|
||||
{
|
||||
g_extern.sram_load_disable = true;
|
||||
g_extern.sram_save_disable = true;
|
||||
}
|
||||
else if (strcmp(optarg, "noload-save") == 0)
|
||||
g_extern.sram_load_disable = true;
|
||||
else if (strcmp(optarg, "load-nosave") == 0)
|
||||
g_extern.sram_save_disable = true;
|
||||
else if (strcmp(optarg, "load-save") != 0)
|
||||
{
|
||||
SSNES_ERR("Invalid argument in --sram-mode!\n");
|
||||
print_help();
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
@@ -710,8 +793,8 @@ static void parse_input(int argc, char *argv[])
|
||||
|
||||
case 'F':
|
||||
g_extern.netplay_sync_frames = strtol(optarg, NULL, 0);
|
||||
if (g_extern.netplay_sync_frames > 32)
|
||||
g_extern.netplay_sync_frames = 32;
|
||||
if (g_extern.netplay_sync_frames > 16)
|
||||
g_extern.netplay_sync_frames = 16;
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
@@ -899,62 +982,67 @@ static inline void save_files(void)
|
||||
#ifdef HAVE_FFMPEG
|
||||
static void init_recording(void)
|
||||
{
|
||||
if (g_extern.recording)
|
||||
if (!g_extern.recording)
|
||||
return;
|
||||
|
||||
// Canonical values.
|
||||
double fps = psnes_get_region() == SNES_REGION_NTSC ? 60.00 : 50.00;
|
||||
double samplerate = 32000.0;
|
||||
if (g_extern.system.timing_set)
|
||||
{
|
||||
// Not perfectly SNES accurate, but this can be adjusted later during processing
|
||||
// and playback losslessly, and we please the containers more by
|
||||
// using "sane" values.
|
||||
struct ffemu_rational ntsc_fps = {60000, 1000};
|
||||
struct ffemu_rational pal_fps = {50000, 1000};
|
||||
struct ffemu_params params = {
|
||||
.out_width = 256,
|
||||
.out_height = 224,
|
||||
.fb_width = 512,
|
||||
.fb_height = 512,
|
||||
.channels = 2,
|
||||
.samplerate = 32000,
|
||||
.filename = g_extern.record_path,
|
||||
.fps = psnes_get_region() == SNES_REGION_NTSC ? ntsc_fps : pal_fps,
|
||||
.rgb32 = false,
|
||||
};
|
||||
fps = g_extern.system.timing.fps;
|
||||
samplerate = g_extern.system.timing.sample_rate;
|
||||
SSNES_LOG("Custom timing given: FPS: %.4lf, Sample rate: %.4lf\n", fps, samplerate);
|
||||
}
|
||||
|
||||
if (g_extern.record_width || g_extern.record_height)
|
||||
{
|
||||
params.out_width = g_extern.record_width;
|
||||
params.out_height = g_extern.record_height;
|
||||
}
|
||||
else if (g_settings.video.hires_record)
|
||||
{
|
||||
params.out_width = 512;
|
||||
params.out_height = 448;
|
||||
}
|
||||
struct ffemu_params params = {
|
||||
.out_width = g_extern.system.geom.base_width,
|
||||
.out_height = g_extern.system.geom.base_height,
|
||||
.fb_width = g_extern.system.geom.max_width,
|
||||
.fb_height = g_extern.system.geom.max_height,
|
||||
.channels = 2,
|
||||
.filename = g_extern.record_path,
|
||||
|
||||
if (g_settings.video.force_aspect)
|
||||
params.aspect_ratio = g_settings.video.aspect_ratio;
|
||||
else
|
||||
params.aspect_ratio = (float)params.out_width / params.out_height;
|
||||
.fps = fps,
|
||||
.samplerate = samplerate,
|
||||
|
||||
if (g_settings.video.post_filter_record && g_extern.filter.active)
|
||||
{
|
||||
g_extern.filter.psize(¶ms.out_width, ¶ms.out_height);
|
||||
params.rgb32 = true;
|
||||
.rgb32 = false,
|
||||
};
|
||||
|
||||
unsigned max_width = 512;
|
||||
unsigned max_height = 512;
|
||||
g_extern.filter.psize(&max_width, &max_height);
|
||||
params.fb_width = next_pow2(max_width);
|
||||
params.fb_height = next_pow2(max_height);
|
||||
}
|
||||
if (g_extern.record_width || g_extern.record_height)
|
||||
{
|
||||
params.out_width = g_extern.record_width;
|
||||
params.out_height = g_extern.record_height;
|
||||
}
|
||||
else if (g_settings.video.hires_record)
|
||||
{
|
||||
params.out_width *= 2;
|
||||
params.out_height *= 2;
|
||||
}
|
||||
|
||||
SSNES_LOG("Recording with FFmpeg to %s @ %ux%u. (FB size: %ux%u 32-bit: %s)\n", g_extern.record_path, params.out_width, params.out_height, params.fb_width, params.fb_height, params.rgb32 ? "yes" : "no");
|
||||
g_extern.rec = ffemu_new(¶ms);
|
||||
if (!g_extern.rec)
|
||||
{
|
||||
SSNES_ERR("Failed to start FFmpeg recording.\n");
|
||||
g_extern.recording = false;
|
||||
}
|
||||
else
|
||||
g_settings.video.crop_overscan = true;
|
||||
if (g_settings.video.force_aspect && (g_settings.video.aspect_ratio > 0.0f))
|
||||
params.aspect_ratio = g_settings.video.aspect_ratio;
|
||||
else
|
||||
params.aspect_ratio = (float)params.out_width / params.out_height;
|
||||
|
||||
if (g_settings.video.post_filter_record && g_extern.filter.active)
|
||||
{
|
||||
g_extern.filter.psize(¶ms.out_width, ¶ms.out_height);
|
||||
params.rgb32 = true;
|
||||
|
||||
unsigned max_width = params.fb_width;
|
||||
unsigned max_height = params.fb_height;
|
||||
g_extern.filter.psize(&max_width, &max_height);
|
||||
params.fb_width = next_pow2(max_width);
|
||||
params.fb_height = next_pow2(max_height);
|
||||
}
|
||||
|
||||
SSNES_LOG("Recording with FFmpeg to %s @ %ux%u. (FB size: %ux%u 32-bit: %s)\n", g_extern.record_path, params.out_width, params.out_height, params.fb_width, params.fb_height, params.rgb32 ? "yes" : "no");
|
||||
g_extern.rec = ffemu_new(¶ms);
|
||||
if (!g_extern.rec)
|
||||
{
|
||||
SSNES_ERR("Failed to start FFmpeg recording.\n");
|
||||
g_extern.recording = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -999,9 +1087,9 @@ static void init_rewind(void)
|
||||
if (g_settings.rewind_enable)
|
||||
{
|
||||
size_t serial_size = psnes_serialize_size();
|
||||
g_extern.state_buf = malloc((serial_size + 3) & ~3); // Make sure we allocate at least 4-byte multiple.
|
||||
g_extern.state_buf = calloc(1, (serial_size + 3) & ~3); // Make sure we allocate at least 4-byte multiple.
|
||||
psnes_serialize(g_extern.state_buf, serial_size);
|
||||
SSNES_LOG("Initing rewind buffer with size: %u MB\n", (unsigned)g_settings.rewind_buffer_size / 1000000);
|
||||
SSNES_LOG("Initing rewind buffer with size: %u MB\n", (unsigned)(g_settings.rewind_buffer_size / 1000000));
|
||||
g_extern.state_manager = state_manager_new((serial_size + 3) & ~3, g_settings.rewind_buffer_size, g_extern.state_buf);
|
||||
if (!g_extern.state_manager)
|
||||
SSNES_WARN("Failed to init rewind buffer. Rewinding will be disabled!\n");
|
||||
@@ -1018,25 +1106,43 @@ static void deinit_rewind(void)
|
||||
|
||||
static void init_movie(void)
|
||||
{
|
||||
if (g_extern.bsv_movie_playback)
|
||||
g_settings.rewind_granularity = 1;
|
||||
|
||||
if (g_extern.bsv.movie_start_playback)
|
||||
{
|
||||
g_extern.bsv_movie = bsv_movie_init(g_extern.bsv_movie_path, SSNES_MOVIE_PLAYBACK);
|
||||
if (!g_extern.bsv_movie)
|
||||
g_extern.bsv.movie = bsv_movie_init(g_extern.bsv.movie_start_path, SSNES_MOVIE_PLAYBACK);
|
||||
if (!g_extern.bsv.movie)
|
||||
{
|
||||
SSNES_ERR("Failed to load movie file: \"%s\"!\n", g_extern.bsv_movie_path);
|
||||
SSNES_ERR("Failed to load movie file: \"%s\"!\n", g_extern.bsv.movie_start_path);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
g_extern.bsv.movie_playback = true;
|
||||
msg_queue_push(g_extern.msg_queue, "Starting movie playback!", 2, 180);
|
||||
g_settings.rewind_granularity = 1;
|
||||
SSNES_LOG("Starting movie playback!\n");
|
||||
}
|
||||
else if (g_extern.bsv.movie_start_recording)
|
||||
{
|
||||
char msg[MAXPATHLEN];
|
||||
snprintf(msg, sizeof(msg), "Starting movie record to \"%s\"!",
|
||||
g_extern.bsv.movie_start_path);
|
||||
|
||||
g_extern.bsv.movie = bsv_movie_init(g_extern.bsv.movie_start_path, SSNES_MOVIE_RECORD);
|
||||
msg_queue_clear(g_extern.msg_queue);
|
||||
msg_queue_push(g_extern.msg_queue,
|
||||
g_extern.bsv.movie ? msg : "Failed to start movie record!", 1, 180);
|
||||
|
||||
if (g_extern.bsv.movie)
|
||||
SSNES_LOG("Starting movie record to \"%s\"!\n", g_extern.bsv.movie_start_path);
|
||||
else
|
||||
SSNES_ERR("Failed to start movie record!\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void deinit_movie(void)
|
||||
{
|
||||
if (g_extern.bsv_movie)
|
||||
bsv_movie_free(g_extern.bsv_movie);
|
||||
if (g_extern.bsv.movie)
|
||||
bsv_movie_free(g_extern.bsv.movie);
|
||||
}
|
||||
|
||||
#define SSNES_DEFAULT_PORT 55435
|
||||
@@ -1044,27 +1150,37 @@ static void deinit_movie(void)
|
||||
#ifdef HAVE_NETPLAY
|
||||
static void init_netplay(void)
|
||||
{
|
||||
if (g_extern.netplay_enable)
|
||||
if (!g_extern.netplay_enable)
|
||||
return;
|
||||
|
||||
struct snes_callbacks cbs = {
|
||||
.frame_cb = video_frame,
|
||||
.sample_cb = audio_sample,
|
||||
.state_cb = input_state
|
||||
};
|
||||
|
||||
if (*g_extern.netplay_server)
|
||||
{
|
||||
struct snes_callbacks cbs = {
|
||||
.frame_cb = video_frame,
|
||||
.sample_cb = audio_sample,
|
||||
.state_cb = input_state
|
||||
};
|
||||
SSNES_LOG("Connecting to netplay host...\n");
|
||||
g_extern.netplay_is_client = true;
|
||||
}
|
||||
else
|
||||
SSNES_LOG("Waiting for client...\n");
|
||||
|
||||
if (strlen(g_extern.netplay_server) > 0)
|
||||
{
|
||||
SSNES_LOG("Connecting to netplay host...\n");
|
||||
g_extern.netplay_is_client = true;
|
||||
}
|
||||
else
|
||||
SSNES_LOG("Waiting for client...\n");
|
||||
g_extern.netplay = netplay_new(g_extern.netplay_is_client ? g_extern.netplay_server : NULL,
|
||||
g_extern.netplay_port ? g_extern.netplay_port : SSNES_DEFAULT_PORT,
|
||||
g_extern.netplay_sync_frames, &cbs);
|
||||
|
||||
g_extern.netplay = netplay_new(g_extern.netplay_is_client ? g_extern.netplay_server : NULL, g_extern.netplay_port ? g_extern.netplay_port : SSNES_DEFAULT_PORT, g_extern.netplay_sync_frames, &cbs);
|
||||
if (!g_extern.netplay)
|
||||
if (!g_extern.netplay)
|
||||
{
|
||||
g_extern.netplay_is_client = false;
|
||||
SSNES_WARN("Failed to init netplay ...\n");
|
||||
|
||||
if (g_extern.msg_queue)
|
||||
{
|
||||
g_extern.netplay_is_client = false;
|
||||
SSNES_WARN("Failed to init netplay...\n");
|
||||
msg_queue_push(g_extern.msg_queue,
|
||||
"Failed to init netplay ...",
|
||||
0, 180);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1221,8 +1337,7 @@ static void fill_pathnames(void)
|
||||
fill_pathname(g_extern.savefile_name_rtc, g_extern.savefile_name_srm, ".rtc", sizeof(g_extern.savefile_name_rtc));
|
||||
}
|
||||
|
||||
if (!g_extern.bsv_movie_playback)
|
||||
fill_pathname(g_extern.bsv_movie_path, g_extern.savefile_name_srm, "", sizeof(g_extern.bsv_movie_path));
|
||||
fill_pathname(g_extern.bsv.movie_path, g_extern.savefile_name_srm, "", sizeof(g_extern.bsv.movie_path));
|
||||
|
||||
if (*g_extern.basename)
|
||||
{
|
||||
@@ -1234,6 +1349,16 @@ static void fill_pathnames(void)
|
||||
|
||||
if (!(*g_extern.xml_name))
|
||||
fill_pathname_noext(g_extern.xml_name, g_extern.basename, ".xml", sizeof(g_extern.xml_name));
|
||||
|
||||
if (!(*g_settings.screenshot_directory))
|
||||
{
|
||||
strlcpy(g_settings.screenshot_directory, g_extern.basename, sizeof(g_settings.screenshot_directory));
|
||||
char *dir = strrchr(g_settings.screenshot_directory, '/');
|
||||
if (!dir)
|
||||
dir = strrchr(g_settings.screenshot_directory, '\\');
|
||||
if (dir)
|
||||
*dir = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1378,10 +1503,10 @@ static void check_input_rate(void)
|
||||
|
||||
static inline void flush_rewind_audio(void)
|
||||
{
|
||||
if (g_extern.frame_is_reverse && g_extern.audio_active) // We just rewound. Flush rewind audio buffer.
|
||||
if (g_extern.frame_is_reverse) // We just rewound. Flush rewind audio buffer.
|
||||
{
|
||||
g_extern.audio_active = audio_flush(g_extern.audio_data.rewind_buf + g_extern.audio_data.rewind_ptr,
|
||||
g_extern.audio_data.rewind_size - g_extern.audio_data.rewind_ptr);
|
||||
g_extern.audio_data.rewind_size - g_extern.audio_data.rewind_ptr) && g_extern.audio_active;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1428,8 +1553,8 @@ static void check_rewind(void)
|
||||
msg_queue_push(g_extern.msg_queue, "Rewinding!", 0, g_extern.is_paused ? 1 : 30);
|
||||
psnes_unserialize(buf, psnes_serialize_size());
|
||||
|
||||
if (g_extern.bsv_movie)
|
||||
bsv_movie_frame_rewind(g_extern.bsv_movie);
|
||||
if (g_extern.bsv.movie)
|
||||
bsv_movie_frame_rewind(g_extern.bsv.movie);
|
||||
}
|
||||
else
|
||||
msg_queue_push(g_extern.msg_queue, "Reached end of rewind buffer!", 0, 30);
|
||||
@@ -1438,7 +1563,7 @@ static void check_rewind(void)
|
||||
{
|
||||
static unsigned cnt = 0;
|
||||
cnt = (cnt + 1) % (g_settings.rewind_granularity ? g_settings.rewind_granularity : 1); // Avoid possible SIGFPE.
|
||||
if (cnt == 0 || g_extern.bsv_movie)
|
||||
if (cnt == 0 || g_extern.bsv.movie)
|
||||
{
|
||||
psnes_serialize(g_extern.state_buf, psnes_serialize_size());
|
||||
state_manager_push(g_extern.state_manager, g_extern.state_buf);
|
||||
@@ -1454,29 +1579,39 @@ static void check_movie_record(void)
|
||||
bool new_button;
|
||||
if ((new_button = driver.input->key_pressed(driver.input_data, SSNES_MOVIE_RECORD_TOGGLE)) && !old_button)
|
||||
{
|
||||
if (g_extern.bsv_movie)
|
||||
if (g_extern.bsv.movie)
|
||||
{
|
||||
msg_queue_clear(g_extern.msg_queue);
|
||||
msg_queue_push(g_extern.msg_queue, "Stopping movie record!", 2, 180);
|
||||
SSNES_LOG("Stopping movie record!\n");
|
||||
bsv_movie_free(g_extern.bsv_movie);
|
||||
g_extern.bsv_movie = NULL;
|
||||
bsv_movie_free(g_extern.bsv.movie);
|
||||
g_extern.bsv.movie = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_settings.rewind_granularity = 1;
|
||||
char path[512];
|
||||
|
||||
char path[MAXPATHLEN];
|
||||
if (g_extern.state_slot > 0)
|
||||
snprintf(path, sizeof(path), "%s%d.bsv", g_extern.bsv_movie_path, g_extern.state_slot);
|
||||
{
|
||||
snprintf(path, sizeof(path), "%s%u.bsv",
|
||||
g_extern.bsv.movie_path, g_extern.state_slot);
|
||||
}
|
||||
else
|
||||
snprintf(path, sizeof(path), "%s.bsv", g_extern.bsv_movie_path);
|
||||
{
|
||||
snprintf(path, sizeof(path), "%s.bsv",
|
||||
g_extern.bsv.movie_path);
|
||||
}
|
||||
|
||||
g_extern.bsv_movie = bsv_movie_init(path, SSNES_MOVIE_RECORD);
|
||||
char msg[MAXPATHLEN];
|
||||
snprintf(msg, sizeof(msg), "Starting movie record to \"%s\"!", path);
|
||||
|
||||
g_extern.bsv.movie = bsv_movie_init(path, SSNES_MOVIE_RECORD);
|
||||
msg_queue_clear(g_extern.msg_queue);
|
||||
msg_queue_push(g_extern.msg_queue, g_extern.bsv_movie ? "Starting movie record!" : "Failed to start movie record!", 1, 180);
|
||||
msg_queue_push(g_extern.msg_queue, g_extern.bsv.movie ? msg : "Failed to start movie record!", 1, 180);
|
||||
|
||||
if (g_extern.bsv_movie)
|
||||
SSNES_LOG("Starting movie record!\n");
|
||||
if (g_extern.bsv.movie)
|
||||
SSNES_LOG("Starting movie record to \"%s\"!\n", path);
|
||||
else
|
||||
SSNES_ERR("Failed to start movie record!\n");
|
||||
}
|
||||
@@ -1485,6 +1620,20 @@ static void check_movie_record(void)
|
||||
old_button = new_button;
|
||||
}
|
||||
|
||||
static void check_movie_playback(void)
|
||||
{
|
||||
if (g_extern.bsv.movie_end)
|
||||
{
|
||||
msg_queue_push(g_extern.msg_queue, "Movie playback ended!", 1, 180);
|
||||
SSNES_LOG("Movie playback ended!\n");
|
||||
|
||||
bsv_movie_free(g_extern.bsv.movie);
|
||||
g_extern.bsv.movie = NULL;
|
||||
g_extern.bsv.movie_end = false;
|
||||
g_extern.bsv.movie_playback = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void check_pause(void)
|
||||
{
|
||||
static bool old_state = false;
|
||||
@@ -1563,7 +1712,9 @@ static void check_oneshot(void)
|
||||
|
||||
static void check_reset(void)
|
||||
{
|
||||
if (driver.input->key_pressed(driver.input_data, SSNES_RESET))
|
||||
static bool old_state = false;
|
||||
bool new_state = driver.input->key_pressed(driver.input_data, SSNES_RESET);
|
||||
if (new_state && !old_state)
|
||||
{
|
||||
SSNES_LOG("Resetting game!\n");
|
||||
msg_queue_clear(g_extern.msg_queue);
|
||||
@@ -1571,6 +1722,8 @@ static void check_reset(void)
|
||||
psnes_reset();
|
||||
init_controllers(); // bSNES since v073r01 resets controllers to JOYPAD after a reset, so just enforce it here.
|
||||
}
|
||||
|
||||
old_state = new_state;
|
||||
}
|
||||
|
||||
#ifdef HAVE_XML
|
||||
@@ -1650,7 +1803,9 @@ static void check_screenshot(void)
|
||||
{
|
||||
static bool old_pressed = false;
|
||||
bool pressed = driver.input->key_pressed(driver.input_data, SSNES_SCREENSHOT);
|
||||
g_extern.do_screenshot = pressed && !old_pressed;
|
||||
if (pressed && !old_pressed)
|
||||
take_screenshot();
|
||||
|
||||
old_pressed = pressed;
|
||||
}
|
||||
|
||||
@@ -1671,6 +1826,7 @@ static void check_dsp_config(void)
|
||||
|
||||
static void do_state_checks(void)
|
||||
{
|
||||
check_screenshot();
|
||||
if (!g_extern.netplay)
|
||||
{
|
||||
check_pause();
|
||||
@@ -1686,14 +1842,17 @@ static void do_state_checks(void)
|
||||
driver.input->key_pressed(driver.input_data, SSNES_FAST_FORWARD_KEY),
|
||||
driver.input->key_pressed(driver.input_data, SSNES_FAST_FORWARD_HOLD_KEY));
|
||||
|
||||
if (!g_extern.bsv_movie)
|
||||
if (!g_extern.bsv.movie)
|
||||
{
|
||||
check_stateslots();
|
||||
check_savestates();
|
||||
}
|
||||
|
||||
check_rewind();
|
||||
if (!g_extern.bsv_movie_playback)
|
||||
|
||||
if (g_extern.bsv.movie_playback)
|
||||
check_movie_playback();
|
||||
else
|
||||
check_movie_record();
|
||||
|
||||
#ifdef HAVE_XML
|
||||
@@ -1714,8 +1873,6 @@ static void do_state_checks(void)
|
||||
if (!g_extern.audio_data.dsp_plugin)
|
||||
#endif
|
||||
check_input_rate();
|
||||
|
||||
check_screenshot();
|
||||
}
|
||||
|
||||
static void fill_title_buf(void)
|
||||
@@ -1755,10 +1912,13 @@ int main(int argc, char *argv[])
|
||||
goto error;
|
||||
|
||||
init_msg_queue();
|
||||
init_movie();
|
||||
|
||||
if (!g_extern.bsv_movie)
|
||||
if (!g_extern.sram_load_disable)
|
||||
load_save_files();
|
||||
else
|
||||
SSNES_LOG("Skipping SRAM load!\n");
|
||||
|
||||
init_movie();
|
||||
|
||||
#ifdef HAVE_NETPLAY
|
||||
init_netplay();
|
||||
@@ -1769,9 +1929,12 @@ int main(int argc, char *argv[])
|
||||
init_rewind();
|
||||
|
||||
#ifdef HAVE_NETPLAY
|
||||
psnes_set_video_refresh(g_extern.netplay ? video_frame_net : video_frame);
|
||||
psnes_set_audio_sample(g_extern.netplay ? audio_sample_net : audio_sample);
|
||||
psnes_set_input_state(g_extern.netplay ? input_state_net : input_state);
|
||||
psnes_set_video_refresh(g_extern.netplay ?
|
||||
video_frame_net : video_frame);
|
||||
psnes_set_audio_sample(g_extern.netplay ?
|
||||
audio_sample_net : audio_sample);
|
||||
psnes_set_input_state(g_extern.netplay ?
|
||||
input_state_net : input_state);
|
||||
#else
|
||||
psnes_set_video_refresh(video_frame);
|
||||
psnes_set_audio_sample(audio_sample);
|
||||
@@ -1785,11 +1948,17 @@ int main(int argc, char *argv[])
|
||||
init_recording();
|
||||
#endif
|
||||
|
||||
if (!g_extern.bsv_movie_playback && !g_extern.netplay_is_client)
|
||||
bool use_sram = !g_extern.netplay_is_client &&
|
||||
!g_extern.sram_save_disable;
|
||||
|
||||
if (!use_sram)
|
||||
SSNES_LOG("SRAM will not be saved!\n");
|
||||
|
||||
if (use_sram)
|
||||
init_autosave();
|
||||
|
||||
#ifdef HAVE_XML
|
||||
if (!g_extern.netplay && !g_extern.bsv_movie)
|
||||
if (!g_extern.netplay && !g_extern.bsv.movie)
|
||||
init_cheats();
|
||||
#endif
|
||||
|
||||
@@ -1802,7 +1971,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Time to drop?
|
||||
if (driver.input->key_pressed(driver.input_data, SSNES_QUIT_KEY) ||
|
||||
!driver.video->alive(driver.video_data) || g_extern.bsv_movie_end)
|
||||
!driver.video->alive(driver.video_data))
|
||||
break;
|
||||
|
||||
// Checks for stuff like fullscreen, save states, etc.
|
||||
@@ -1817,13 +1986,13 @@ int main(int argc, char *argv[])
|
||||
if (g_extern.netplay)
|
||||
netplay_pre_frame(g_extern.netplay);
|
||||
#endif
|
||||
if (g_extern.bsv_movie)
|
||||
bsv_movie_set_frame_start(g_extern.bsv_movie);
|
||||
if (g_extern.bsv.movie)
|
||||
bsv_movie_set_frame_start(g_extern.bsv.movie);
|
||||
|
||||
psnes_run();
|
||||
|
||||
if (g_extern.bsv_movie)
|
||||
bsv_movie_set_frame_end(g_extern.bsv_movie);
|
||||
if (g_extern.bsv.movie)
|
||||
bsv_movie_set_frame_end(g_extern.bsv.movie);
|
||||
#ifdef HAVE_NETPLAY
|
||||
if (g_extern.netplay)
|
||||
netplay_post_frame(g_extern.netplay);
|
||||
@@ -1842,14 +2011,14 @@ int main(int argc, char *argv[])
|
||||
deinit_netplay();
|
||||
#endif
|
||||
|
||||
if (!g_extern.bsv_movie_playback && !g_extern.netplay_is_client)
|
||||
if (use_sram)
|
||||
deinit_autosave();
|
||||
|
||||
#ifdef HAVE_FFMPEG
|
||||
deinit_recording();
|
||||
#endif
|
||||
|
||||
if (!g_extern.bsv_movie_playback && !g_extern.netplay_is_client)
|
||||
if (use_sram)
|
||||
save_files();
|
||||
|
||||
if (!g_extern.netplay)
|
||||
|
||||
@@ -24,9 +24,6 @@
|
||||
# video_xscale = 3.0
|
||||
# video_yscale = 3.0
|
||||
|
||||
# Base size for windowed mode calculations. Generally 224 for SNES, but can be modified.
|
||||
# video_base_size = 224
|
||||
|
||||
# Fullscreen resolution. Resolution of 0 uses the resolution of the desktop.
|
||||
# video_fullscreen_x = 0
|
||||
# video_fullscreen_y = 0
|
||||
@@ -92,6 +89,9 @@
|
||||
# Size of the TTF font rendered.
|
||||
# video_font_size = 48
|
||||
|
||||
# Enable usage of OSD messages.
|
||||
# video_font_enable = true
|
||||
|
||||
# Offset for where messages will be placed on screen. Values are in range 0.0 to 1.0 for both x and y values.
|
||||
# [0.0, 0.0] maps to the lower left corner of the screen.
|
||||
# video_message_pos_x = 0.05
|
||||
@@ -101,6 +101,10 @@
|
||||
# It is a regular RGB hex number, i.e. red is "ff0000".
|
||||
# video_message_color = ffffff
|
||||
|
||||
# Video refresh rate of your monitor.
|
||||
# Used to calculate a suitable audio input rate.
|
||||
# video_refresh_rate = 59.95
|
||||
|
||||
#### Audio
|
||||
|
||||
# Enable audio.
|
||||
@@ -109,12 +113,6 @@
|
||||
# Audio output samplerate.
|
||||
# audio_out_rate = 48000
|
||||
|
||||
# Audio input samplerate from libsnes.
|
||||
# Lower this (slightly) if you are experiencing frequent audio dropouts while vsync is enabled.
|
||||
# Conversely, increase this slightly if you are experiencing good audio,
|
||||
# but lots of dropped frames. Reasonable values for this is 32000 +/- 100 Hz.
|
||||
# audio_in_rate = 31980
|
||||
|
||||
# When altering audio_in_rate on-the-fly, define by how much each time.
|
||||
# audio_rate_step = 0.25
|
||||
|
||||
|
||||
Reference in new issue
Block a user