mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-11 23:42:58 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70e9f084b7 | ||
|
|
824790a80d | ||
|
|
23d23983bc | ||
|
|
c42655ed91 | ||
|
|
f46ee78c76 | ||
|
|
5d1d188231 | ||
|
|
c55ce543c2 | ||
|
|
de8bc62474 | ||
|
|
397da887a7 | ||
|
|
f44733d7f6 | ||
|
|
5faa28a1a2 | ||
|
|
efaa85ff9b | ||
|
|
8f5580be10 | ||
|
|
c7acd99548 | ||
|
|
64ee13e6f4 | ||
|
|
e134f8afc7 | ||
|
|
dd89e6fbe0 | ||
|
|
a79a1b46f7 | ||
|
|
6467f51ea8 | ||
|
|
141eefb03b | ||
|
|
068c224d83 | ||
|
|
5bc8090ea7 | ||
|
|
466ab1ef8d | ||
|
|
a1c19ed1f5 | ||
|
|
cce4b7d20c | ||
|
|
80925ffc11 | ||
|
|
9eb14e328d | ||
|
|
5ae0cb1821 | ||
|
|
fc594f7f0f | ||
|
|
0c4e406ee7 | ||
|
|
a082ec5251 | ||
|
|
953b5daf0a | ||
|
|
b4ff622f32 | ||
|
|
f26b54c998 | ||
|
|
d7b3f39136 | ||
|
|
5bcf601a40 | ||
|
|
e91ebdac97 | ||
|
|
b2dd12d186 | ||
|
|
9c79bf0ba2 | ||
|
|
f48247a18b | ||
|
|
48e015558e | ||
|
|
0ff3cbc93b | ||
|
|
25f0e162a4 | ||
|
|
0810812cd9 |
No files matched your search
@@ -2,7 +2,7 @@ include config.mk
|
||||
|
||||
TARGET = ssnes tools/ssnes-joyconfig
|
||||
|
||||
OBJ = ssnes.o file.o driver.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o bps.o strl.o screenshot.o thread.o
|
||||
OBJ = ssnes.o file.o driver.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o bps.o strl.o screenshot.o thread.o audio/hermite.o
|
||||
JOYCONFIG_OBJ = tools/ssnes-joyconfig.o conf/config_file.o strl.o
|
||||
HEADERS = $(wildcard */*.h) $(wildcard *.h)
|
||||
|
||||
@@ -27,13 +27,6 @@ ifneq ($(findstring Linux,$(OS)),)
|
||||
LIBS += -lrt
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SRC), 1)
|
||||
LIBS += $(SRC_LIBS)
|
||||
DEFINES += $(SRC_CFLAGS)
|
||||
else
|
||||
OBJ += audio/hermite.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_CONFIGFILE), 1)
|
||||
OBJ += conf/config_file.o
|
||||
endif
|
||||
|
||||
+2
-2
@@ -166,10 +166,10 @@ clean:
|
||||
rm -f tools/*.o
|
||||
|
||||
dist_x86: all
|
||||
zip -r ssnes-win32-0.9-rc2.zip $(TARGET) $(JTARGET) ssnes.cfg snes.dll SDL.dll
|
||||
zip -r ssnes-win32-0.9.zip $(TARGET) $(JTARGET) ssnes.cfg snes.dll SDL.dll
|
||||
|
||||
dist_x86_64: all
|
||||
zip -r ssnes-win64-0.9-rc2.zip $(TARGET) $(JTARGET) ssnes.cfg snes.dll SDL.dll
|
||||
zip -r ssnes-win64-0.9.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
|
||||
|
||||
+10
-15
@@ -21,7 +21,7 @@
|
||||
#include <asoundlib.h>
|
||||
#include "general.h"
|
||||
|
||||
#define TRY_ALSA(x) if ( x < 0 ) { \
|
||||
#define TRY_ALSA(x) if (x < 0) { \
|
||||
goto error; \
|
||||
}
|
||||
|
||||
@@ -52,14 +52,14 @@ static bool find_float_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
||||
static void* __alsa_init(const char* device, unsigned rate, unsigned latency)
|
||||
{
|
||||
alsa_t *alsa = calloc(1, sizeof(alsa_t));
|
||||
if ( alsa == NULL )
|
||||
if (!alsa)
|
||||
return NULL;
|
||||
|
||||
snd_pcm_hw_params_t *params = NULL;
|
||||
snd_pcm_sw_params_t *sw_params = NULL;
|
||||
|
||||
const char *alsa_dev = "default";
|
||||
if ( device != NULL )
|
||||
if (device)
|
||||
alsa_dev = device;
|
||||
|
||||
TRY_ALSA(snd_pcm_open(&alsa->pcm, alsa_dev, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK));
|
||||
@@ -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;
|
||||
|
||||
@@ -141,16 +141,16 @@ static ssize_t __alsa_write(void* data, const void* buf, size_t size)
|
||||
|
||||
frames = snd_pcm_writei(alsa->pcm, (const char*)buf + written * 2 * (alsa->has_float ? sizeof(float) : sizeof(int16_t)), size - written);
|
||||
|
||||
if ( frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE )
|
||||
if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE)
|
||||
{
|
||||
if ( snd_pcm_recover(alsa->pcm, frames, 1) < 0 )
|
||||
if (snd_pcm_recover(alsa->pcm, frames, 1) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
else if ( frames == -EAGAIN && alsa->nonblock )
|
||||
else if (frames == -EAGAIN && alsa->nonblock)
|
||||
return 0;
|
||||
else if ( frames < 0 )
|
||||
else if (frames < 0)
|
||||
return -1;
|
||||
|
||||
written += frames;
|
||||
@@ -178,9 +178,9 @@ static bool __alsa_start(void *data)
|
||||
static void __alsa_free(void *data)
|
||||
{
|
||||
alsa_t *alsa = data;
|
||||
if ( alsa )
|
||||
if (alsa)
|
||||
{
|
||||
if ( alsa->pcm )
|
||||
if (alsa->pcm)
|
||||
{
|
||||
snd_pcm_drop(alsa->pcm);
|
||||
snd_pcm_close(alsa->pcm);
|
||||
@@ -200,8 +200,3 @@ const audio_driver_t audio_alsa = {
|
||||
.ident = "alsa"
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+6
-11
@@ -20,13 +20,12 @@
|
||||
#include "hermite.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX_CHANS 8
|
||||
#define CHANNELS 2
|
||||
|
||||
struct hermite_resampler
|
||||
{
|
||||
float chan_data[MAX_CHANS][4];
|
||||
float chan_data[CHANNELS][4];
|
||||
double r_frac;
|
||||
unsigned chans;
|
||||
};
|
||||
|
||||
static inline float hermite_kernel(float mu1, float a, float b, float c, float d)
|
||||
@@ -47,21 +46,17 @@ static inline float hermite_kernel(float mu1, float a, float b, float c, float d
|
||||
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
|
||||
}
|
||||
|
||||
hermite_resampler_t *hermite_new(unsigned channels)
|
||||
hermite_resampler_t *hermite_new(void)
|
||||
{
|
||||
if (channels > MAX_CHANS)
|
||||
return NULL;
|
||||
|
||||
hermite_resampler_t *re = calloc(1, sizeof(*re));
|
||||
if (!re)
|
||||
return NULL;
|
||||
re->chans = channels;
|
||||
return re;
|
||||
}
|
||||
|
||||
void hermite_process(hermite_resampler_t *re, struct hermite_data *data)
|
||||
{
|
||||
double r_step = 1.0 / data->src_ratio;
|
||||
double r_step = 1.0 / data->ratio;
|
||||
size_t processed_out = 0;
|
||||
size_t processed_in = 0;
|
||||
|
||||
@@ -75,7 +70,7 @@ void hermite_process(hermite_resampler_t *re, struct hermite_data *data)
|
||||
while (re->r_frac <= 1.0 && processed_out < out_frames)
|
||||
{
|
||||
re->r_frac += r_step;
|
||||
for (unsigned i = 0; i < re->chans; i++)
|
||||
for (unsigned i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
float res = hermite_kernel(re->r_frac,
|
||||
re->chan_data[i][0], re->chan_data[i][1], re->chan_data[i][2], re->chan_data[i][3]);
|
||||
@@ -88,7 +83,7 @@ void hermite_process(hermite_resampler_t *re, struct hermite_data *data)
|
||||
{
|
||||
re->r_frac -= 1.0;
|
||||
|
||||
for (unsigned i = 0; i < re->chans; i++)
|
||||
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];
|
||||
|
||||
+2
-3
@@ -26,7 +26,7 @@
|
||||
|
||||
typedef struct hermite_resampler hermite_resampler_t;
|
||||
|
||||
hermite_resampler_t *hermite_new(unsigned channels);
|
||||
hermite_resampler_t *hermite_new(void);
|
||||
|
||||
struct hermite_data
|
||||
{
|
||||
@@ -39,8 +39,7 @@ struct hermite_data
|
||||
size_t input_frames_used;
|
||||
size_t output_frames_gen;
|
||||
|
||||
bool end_of_input; // Just used to clone the SRC API.
|
||||
double src_ratio;
|
||||
double ratio;
|
||||
};
|
||||
|
||||
void hermite_process(hermite_resampler_t *re, struct hermite_data *data);
|
||||
|
||||
+3
-4
@@ -60,7 +60,7 @@ static void* __al_init(const char* device, unsigned rate, unsigned latency)
|
||||
{
|
||||
(void)device;
|
||||
al_t *al = calloc(1, sizeof(al_t));
|
||||
if ( al == NULL )
|
||||
if (!al)
|
||||
return NULL;
|
||||
|
||||
al->handle = alcOpenDevice(NULL);
|
||||
@@ -133,7 +133,7 @@ static bool al_get_buffer(al_t *al, ALuint *buffer)
|
||||
};
|
||||
#endif
|
||||
|
||||
for(;;)
|
||||
for (;;)
|
||||
{
|
||||
if (al_unqueue_buffers(al))
|
||||
break;
|
||||
@@ -221,7 +221,7 @@ static void __al_free(void *data)
|
||||
{
|
||||
alSourceStop(al->source);
|
||||
alDeleteSources(1, &al->source);
|
||||
if ( al->buffers )
|
||||
if (al->buffers)
|
||||
{
|
||||
alDeleteBuffers(al->num_buffers, al->buffers);
|
||||
free(al->buffers);
|
||||
@@ -244,4 +244,3 @@ const audio_driver_t audio_openal = {
|
||||
.ident = "openal"
|
||||
};
|
||||
|
||||
|
||||
+5
-5
@@ -57,12 +57,12 @@ static void err_cb(void *userdata)
|
||||
static void* __rsd_init(const char* device, unsigned rate, unsigned latency)
|
||||
{
|
||||
rsd_t *rsd = calloc(1, sizeof(rsd_t));
|
||||
if ( rsd == NULL )
|
||||
if (!rsd)
|
||||
return NULL;
|
||||
|
||||
rsound_t *rd;
|
||||
|
||||
if ( rsd_init(&rd) < 0 )
|
||||
if (rsd_init(&rd) < 0)
|
||||
{
|
||||
free(rsd);
|
||||
return NULL;
|
||||
@@ -80,14 +80,14 @@ static void* __rsd_init(const char* device, unsigned rate, unsigned latency)
|
||||
rsd_set_param(rd, RSD_SAMPLERATE, &rate);
|
||||
rsd_set_param(rd, RSD_LATENCY, &latency);
|
||||
|
||||
if ( device != NULL )
|
||||
if (device)
|
||||
rsd_set_param(rd, RSD_HOST, (void*)device);
|
||||
|
||||
rsd_set_param(rd, RSD_FORMAT, &format);
|
||||
|
||||
rsd_set_callback(rd, audio_cb, err_cb, 256, rsd);
|
||||
|
||||
if ( rsd_start(rd) < 0 )
|
||||
if (rsd_start(rd) < 0)
|
||||
{
|
||||
free(rsd);
|
||||
rsd_free(rd);
|
||||
@@ -161,7 +161,7 @@ static void __rsd_set_nonblock_state(void *data, bool state)
|
||||
static bool __rsd_start(void *data)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
if ( rsd_start(rsd->rd) < 0)
|
||||
if (rsd_start(rsd->rd) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
#ifndef AUDIO_UTILS_H
|
||||
#define AUDIO_UTILS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __SSE2__
|
||||
#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
|
||||
#else
|
||||
#define audio_convert_s16_to_float audio_convert_s16_to_float_C
|
||||
#define audio_convert_float_to_s16 audio_convert_float_to_s16_C
|
||||
#endif
|
||||
|
||||
static inline void audio_convert_s16_to_float_C(float *out,
|
||||
const int16_t *in, unsigned samples)
|
||||
{
|
||||
for (unsigned i = 0; i < samples; i++)
|
||||
out[i] = (float)in[i] / 0x8000;
|
||||
}
|
||||
|
||||
static inline void audio_convert_float_to_s16_C(int16_t *out,
|
||||
const float *in, unsigned samples)
|
||||
{
|
||||
for (unsigned i = 0; i < samples; i++)
|
||||
{
|
||||
int32_t val = in[i] * 0x8000;
|
||||
out[i] = (val > 0x7FFF) ? 0x7FFF : (val < -0x8000 ? -0x8000 : (int16_t)val);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __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);
|
||||
unsigned i;
|
||||
for (i = 0; i + 4 <= samples; i += 4, in += 4, out += 4)
|
||||
{
|
||||
__m64 input = *(const __m64*)in;
|
||||
__m128 reg = _mm_cvtpi16_ps(input);
|
||||
__m128 res = _mm_mul_ps(reg, factor);
|
||||
*(__m128*)out = res;
|
||||
}
|
||||
|
||||
audio_convert_s16_to_float_C(out, in, samples - i);
|
||||
}
|
||||
|
||||
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);
|
||||
unsigned i;
|
||||
for (i = 0; i + 4 <= samples; i += 4, in += 4, out += 4)
|
||||
{
|
||||
__m128 input = *(const __m128*)in;
|
||||
__m128 res = _mm_mul_ps(input, factor);
|
||||
__m64 output = _mm_cvtps_pi16(res);
|
||||
*(__m64*)out = output;
|
||||
}
|
||||
|
||||
audio_convert_float_to_s16_C(out, in, samples - i);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+314
-72
@@ -24,18 +24,49 @@
|
||||
#include <errno.h>
|
||||
#include "strl.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/param.h> // MAXPATHLEN
|
||||
#else
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifndef MAXPATHLEN
|
||||
#ifdef PATH_MAX
|
||||
#define MAXPATHLEN PATH_MAX
|
||||
#else
|
||||
#define MAXPATHLEN 512
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define MAX_INCLUDE_DEPTH 16
|
||||
|
||||
struct entry_list
|
||||
{
|
||||
bool readonly; // If we got this from an #include, do not allow write.
|
||||
char *key;
|
||||
char *value;
|
||||
struct entry_list *next;
|
||||
};
|
||||
|
||||
struct include_list
|
||||
{
|
||||
char *path;
|
||||
struct include_list *next;
|
||||
};
|
||||
|
||||
struct config_file
|
||||
{
|
||||
char *path;
|
||||
struct entry_list *entries;
|
||||
struct entry_list *tail;
|
||||
unsigned include_depth;
|
||||
|
||||
struct include_list *includes;
|
||||
};
|
||||
|
||||
static config_file_t *config_file_new_internal(const char *path, unsigned depth);
|
||||
|
||||
static char *getaline(FILE *file)
|
||||
{
|
||||
char *newline = malloc(9);
|
||||
@@ -58,13 +89,189 @@ static char *getaline(FILE *file)
|
||||
return newline;
|
||||
}
|
||||
|
||||
static bool parse_line(struct entry_list *list, char *line)
|
||||
static char* extract_value(char *line, bool is_value)
|
||||
{
|
||||
if (is_value)
|
||||
{
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
|
||||
// If we don't have an equal sign here, we've got an invalid string...
|
||||
if (*line != '=')
|
||||
return false;
|
||||
|
||||
line++;
|
||||
}
|
||||
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
|
||||
// We have a full string. Read until next ".
|
||||
if (*line == '"')
|
||||
{
|
||||
line++;
|
||||
char *tok = strtok(line, "\"");
|
||||
if (!tok)
|
||||
return false;
|
||||
return strdup(tok);
|
||||
}
|
||||
else if (*line == '\0') // Nothing :(
|
||||
return NULL;
|
||||
else // We don't have that... Read till next space.
|
||||
{
|
||||
char *tok = strtok(line, " \n\t\f\r\v");
|
||||
if (tok)
|
||||
return strdup(tok);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_list_readonly(struct entry_list *list)
|
||||
{
|
||||
while (list)
|
||||
{
|
||||
list->readonly = true;
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
// Move semantics? :)
|
||||
static void add_child_list(config_file_t *parent, config_file_t *child)
|
||||
{
|
||||
if (parent->entries)
|
||||
{
|
||||
struct entry_list *head = parent->entries;
|
||||
while (head->next)
|
||||
head = head->next;
|
||||
|
||||
set_list_readonly(child->entries);
|
||||
head->next = child->entries;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_list_readonly(child->entries);
|
||||
parent->entries = child->entries;
|
||||
}
|
||||
|
||||
child->entries = NULL;
|
||||
|
||||
// Rebase tail.
|
||||
if (parent->entries)
|
||||
{
|
||||
struct entry_list *head = parent->entries;
|
||||
while (head->next)
|
||||
head = head->next;
|
||||
parent->tail = head;
|
||||
}
|
||||
else
|
||||
parent->tail = NULL;
|
||||
}
|
||||
|
||||
static void add_include_list(config_file_t *conf, const char *path)
|
||||
{
|
||||
struct include_list *head = conf->includes;
|
||||
struct include_list *node = calloc(1, sizeof(*node));
|
||||
node->path = strdup(path);
|
||||
|
||||
if (head)
|
||||
{
|
||||
while (head->next)
|
||||
head = head->next;
|
||||
|
||||
head->next = node;
|
||||
}
|
||||
else
|
||||
conf->includes = node;
|
||||
}
|
||||
|
||||
static void add_sub_conf(config_file_t *conf, char *line)
|
||||
{
|
||||
char *path = extract_value(line, false);
|
||||
if (!path)
|
||||
return;
|
||||
|
||||
add_include_list(conf, path);
|
||||
|
||||
char real_path[MAXPATHLEN];
|
||||
|
||||
#ifndef _WIN32
|
||||
if (*path == '/')
|
||||
{
|
||||
strlcpy(real_path, path, sizeof(real_path));
|
||||
}
|
||||
else if (*path == '~')
|
||||
{
|
||||
const char *home = getenv("HOME");
|
||||
strlcpy(real_path, home ? home : "/", sizeof(real_path));
|
||||
strlcat(real_path, path + 1, sizeof(real_path));
|
||||
}
|
||||
else
|
||||
{
|
||||
strlcpy(real_path, conf->path, sizeof(real_path));
|
||||
char *split = strrchr(real_path, '/');
|
||||
if (split)
|
||||
{
|
||||
split[1] = '\0';
|
||||
strlcat(real_path, path, sizeof(real_path));
|
||||
}
|
||||
else
|
||||
strlcpy(real_path, path, sizeof(real_path));
|
||||
}
|
||||
#else
|
||||
// Accomodate POSIX systems on Win32.
|
||||
bool is_full_path = *path == '/';
|
||||
|
||||
if (is_full_path)
|
||||
strlcpy(real_path, path, sizeof(real_path));
|
||||
else
|
||||
GetFullPathNameA(path, sizeof(real_path), real_path, NULL);
|
||||
|
||||
if (strcmp(path, real_path) != 0)
|
||||
{
|
||||
strlcpy(real_path, conf->path, sizeof(real_path));
|
||||
char *split = strrchr(real_path, '/');
|
||||
if (!split)
|
||||
split = strrchr(real_path, '\\');
|
||||
|
||||
split[1] = '\0';
|
||||
strlcat(real_path, path, sizeof(real_path));
|
||||
}
|
||||
#endif
|
||||
|
||||
config_file_t *sub_conf = config_file_new_internal(real_path, conf->include_depth + 1);
|
||||
if (!sub_conf)
|
||||
{
|
||||
free(path);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pilfer internal list! :D
|
||||
add_child_list(conf, sub_conf);
|
||||
config_file_free(sub_conf);
|
||||
free(path);
|
||||
}
|
||||
|
||||
static bool parse_line(config_file_t *conf, struct entry_list *list, char *line)
|
||||
{
|
||||
// Remove everything after comment.
|
||||
char *comment = strchr(line, '#');
|
||||
if (comment)
|
||||
*comment = '\0';
|
||||
|
||||
// Starting line with # and include includes config files. :)
|
||||
if ((comment == line) && (conf->include_depth < MAX_INCLUDE_DEPTH))
|
||||
{
|
||||
comment++;
|
||||
if (strstr(comment, "include ") == comment)
|
||||
{
|
||||
add_sub_conf(conf, comment + strlen("include "));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (conf->include_depth >= MAX_INCLUDE_DEPTH)
|
||||
fprintf(stderr, "!!! #include depth exceeded for config! Might be a cycle.\n");
|
||||
|
||||
// Skips to first character.
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
@@ -86,66 +293,42 @@ static bool parse_line(struct entry_list *list, char *line)
|
||||
key[index] = '\0';
|
||||
list->key = key;
|
||||
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
|
||||
// If we don't have an equal sign here, we've got an invalid string...
|
||||
if (*line != '=')
|
||||
list->value = extract_value(line, true);
|
||||
if (!list->value)
|
||||
{
|
||||
list->key = NULL;
|
||||
free(key);
|
||||
return false;
|
||||
}
|
||||
line++;
|
||||
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
|
||||
// We have a full string. Read until next ".
|
||||
if (*line == '"')
|
||||
{
|
||||
char *tok = strtok(line + 1, "\"");
|
||||
if (tok == NULL)
|
||||
{
|
||||
list->key = NULL;
|
||||
free(key);
|
||||
return false;
|
||||
}
|
||||
list->value = strdup(tok);
|
||||
}
|
||||
else // We don't have that... Read till next space.
|
||||
{
|
||||
char *tok = strtok(line, " \t\f");
|
||||
if (tok == NULL)
|
||||
{
|
||||
list->key = NULL;
|
||||
free(key);
|
||||
return false;
|
||||
}
|
||||
list->value = strdup(tok);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
config_file_t *config_file_new(const char *path)
|
||||
static config_file_t *config_file_new_internal(const char *path, unsigned depth)
|
||||
{
|
||||
|
||||
struct config_file *conf = calloc(1, sizeof(*conf));
|
||||
if (conf == NULL)
|
||||
if (!conf)
|
||||
return NULL;
|
||||
|
||||
if (path == NULL)
|
||||
if (!path)
|
||||
return conf;
|
||||
|
||||
FILE *file = fopen(path, "r");
|
||||
if (!file)
|
||||
conf->path = strdup(path);
|
||||
if (!conf->path)
|
||||
{
|
||||
free(conf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct entry_list *tail = conf->entries;
|
||||
conf->include_depth = depth;
|
||||
|
||||
FILE *file = fopen(path, "r");
|
||||
if (!file)
|
||||
{
|
||||
free(conf->path);
|
||||
free(conf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (!feof(file))
|
||||
{
|
||||
@@ -154,50 +337,69 @@ config_file_t *config_file_new(const char *path)
|
||||
|
||||
if (line)
|
||||
{
|
||||
if (parse_line(list, line))
|
||||
if (parse_line(conf, list, line))
|
||||
{
|
||||
if (conf->entries == NULL)
|
||||
if (conf->entries)
|
||||
{
|
||||
conf->entries = list;
|
||||
tail = list;
|
||||
conf->tail->next = list;
|
||||
conf->tail = list;
|
||||
}
|
||||
else
|
||||
{
|
||||
tail->next = list;
|
||||
tail = list;
|
||||
conf->entries = list;
|
||||
conf->tail = list;
|
||||
}
|
||||
}
|
||||
|
||||
free(line);
|
||||
}
|
||||
|
||||
if (list != conf->tail)
|
||||
free(list);
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
config_file_t *config_file_new(const char *path)
|
||||
{
|
||||
return config_file_new_internal(path, 0);
|
||||
}
|
||||
|
||||
void config_file_free(config_file_t *conf)
|
||||
{
|
||||
if (conf != NULL)
|
||||
if (!conf)
|
||||
return;
|
||||
|
||||
struct entry_list *tmp = conf->entries;
|
||||
while (tmp)
|
||||
{
|
||||
struct entry_list *tmp = conf->entries;
|
||||
struct entry_list *old = tmp;
|
||||
while (tmp != NULL)
|
||||
{
|
||||
free(tmp->key);
|
||||
free(tmp->value);
|
||||
old = tmp;
|
||||
tmp = tmp->next;
|
||||
free(old);
|
||||
}
|
||||
free(conf);
|
||||
free(tmp->key);
|
||||
free(tmp->value);
|
||||
struct entry_list *hold = tmp;
|
||||
tmp = tmp->next;
|
||||
free(hold);
|
||||
}
|
||||
|
||||
struct include_list *inc_tmp = conf->includes;
|
||||
while (inc_tmp)
|
||||
{
|
||||
free(inc_tmp->path);
|
||||
struct include_list *hold = inc_tmp;
|
||||
inc_tmp = inc_tmp->next;
|
||||
free(hold);
|
||||
}
|
||||
|
||||
free(conf->path);
|
||||
free(conf);
|
||||
}
|
||||
|
||||
bool config_get_double(config_file_t *conf, const char *key, double *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
@@ -213,7 +415,7 @@ bool config_get_int(config_file_t *conf, const char *key, int *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
@@ -236,7 +438,7 @@ bool config_get_hex(config_file_t *conf, const char *key, unsigned *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
@@ -259,7 +461,7 @@ bool config_get_char(config_file_t *conf, const char *key, char *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
@@ -277,7 +479,7 @@ bool config_get_string(config_file_t *conf, const char *key, char **str)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
@@ -293,7 +495,7 @@ bool config_get_array(config_file_t *conf, const char *key, char *buf, size_t si
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
@@ -309,7 +511,7 @@ bool config_get_bool(config_file_t *conf, const char *key, bool *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
@@ -335,14 +537,15 @@ void config_set_string(config_file_t *conf, const char *key, const char *val)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
struct entry_list *last = list;
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
if (!list->readonly && (strcmp(key, list->key) == 0))
|
||||
{
|
||||
free(list->value);
|
||||
list->value = strdup(val);
|
||||
return;
|
||||
}
|
||||
|
||||
last = list;
|
||||
list = list->next;
|
||||
}
|
||||
@@ -406,11 +609,50 @@ bool config_file_write(config_file_t *conf, const char *path)
|
||||
|
||||
void config_file_dump(config_file_t *conf, FILE *file)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
struct include_list *includes = conf->includes;
|
||||
while (includes)
|
||||
{
|
||||
fprintf(file, "%s = \"%s\"\n", list->key, list->value);
|
||||
fprintf(file, "#include \"%s\"\n", includes->path);
|
||||
includes = includes->next;
|
||||
}
|
||||
|
||||
struct entry_list *list = conf->entries;
|
||||
while (list)
|
||||
{
|
||||
if (!list->readonly)
|
||||
fprintf(file, "%s = \"%s\"\n", list->key, list->value);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
void config_file_dump_all(config_file_t *conf, FILE *file)
|
||||
{
|
||||
struct include_list *includes = conf->includes;
|
||||
while (includes)
|
||||
{
|
||||
fprintf(file, "#include \"%s\"\n", includes->path);
|
||||
includes = includes->next;
|
||||
}
|
||||
|
||||
struct entry_list *list = conf->entries;
|
||||
while (list)
|
||||
{
|
||||
fprintf(file, "%s = \"%s\" %s\n", list->key, list->value, list->readonly ? "(included)" : "");
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
bool config_entry_exists(config_file_t *conf, const char *entry)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(entry, list->key) == 0)
|
||||
return true;
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
+15
-5
@@ -31,13 +31,21 @@ typedef struct config_file config_file_t;
|
||||
// - # are treated as comments. Rest of the line is ignored.
|
||||
// - Format is: key = value. There can be as many spaces as you like in-between.
|
||||
// - Value can be wrapped inside "" for multiword strings. (foo = "hai u")
|
||||
//
|
||||
// - #include includes a config file in-place.
|
||||
// Path is relative to where config file was loaded unless an absolute path is chosen.
|
||||
// Key/value pairs from an #include are read-only, and cannot be modified.
|
||||
|
||||
// Loads a config file. Returns NULL if file doesn't exist. NULL path will create an empty config file.
|
||||
// Loads a config file. Returns NULL if file doesn't exist.
|
||||
// NULL path will create an empty config file.
|
||||
config_file_t *config_file_new(const char *path);
|
||||
// Frees config file.
|
||||
void config_file_free(config_file_t *conf);
|
||||
|
||||
// All extract functions return true when value is valid and exists. Returns false otherwise.
|
||||
// All extract functions return true when value is valid and exists.
|
||||
// Returns false otherwise.
|
||||
|
||||
bool config_entry_exists(config_file_t *conf, const char *entry);
|
||||
|
||||
// Extracts a double from config file.
|
||||
bool config_get_double(config_file_t *conf, const char *entry, double *in);
|
||||
@@ -54,7 +62,8 @@ bool config_get_array(config_file_t *conf, const char *entry, char *in, size_t s
|
||||
// Extracts a boolean from config. Valid boolean true are "true" and "1". Valid false are "false" and "0". Other values will be treated as an error.
|
||||
bool config_get_bool(config_file_t *conf, const char *entry, bool *in);
|
||||
|
||||
// Setters. Similiar to the getters.
|
||||
// Setters. Similar to the getters. Will not write to entry if the entry
|
||||
// was obtained from an #include.
|
||||
void config_set_double(config_file_t *conf, const char *entry, double value);
|
||||
void config_set_int(config_file_t *conf, const char *entry, int val);
|
||||
void config_set_char(config_file_t *conf, const char *entry, char val);
|
||||
@@ -66,7 +75,8 @@ bool config_file_write(config_file_t *conf, const char *path);
|
||||
|
||||
// Dump the current config to an already opened file. Does not close the file.
|
||||
void config_file_dump(config_file_t *conf, FILE *file);
|
||||
|
||||
|
||||
// Also dumps inherited values, useful for logging.
|
||||
void config_file_dump_all(config_file_t *conf, FILE *file);
|
||||
|
||||
#endif
|
||||
|
||||
+4
-11
@@ -32,10 +32,6 @@
|
||||
|
||||
#include "input/keysym.h"
|
||||
|
||||
#ifdef HAVE_SRC
|
||||
#include <samplerate.h>
|
||||
#endif
|
||||
|
||||
///////////////// Drivers
|
||||
#define VIDEO_GL 0
|
||||
#define VIDEO_XVIDEO 11
|
||||
@@ -113,8 +109,9 @@
|
||||
////////////////
|
||||
|
||||
// Windowed
|
||||
static const float xscale = 3.0; // Real x res = 296 * xscale
|
||||
static const float yscale = 3.0; // Real y res = 224 * yscale
|
||||
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
|
||||
@@ -188,11 +185,6 @@ static const int out_latency = 64;
|
||||
// Will sync audio. (recommended)
|
||||
static const bool audio_sync = true;
|
||||
|
||||
// Defines the quality (and cpu reqirements) of samplerate conversion.
|
||||
#ifdef HAVE_SRC
|
||||
#define SAMPLERATE_QUALITY SRC_LINEAR
|
||||
#endif
|
||||
|
||||
// Enables use of rewind. This will incur some memory footprint depending on the save state buffer.
|
||||
// This rewind only works when using bSNES core atm.
|
||||
static const bool rewind_enable = false;
|
||||
@@ -260,6 +252,7 @@ static const struct snes_keybind snes_keybinds_1[] = {
|
||||
{ SSNES_REWIND, SK_r, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_MOVIE_RECORD_TOGGLE, SK_o, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_PAUSE_TOGGLE, SK_p, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_FRAMEADVANCE, SK_k, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_RESET, SK_h, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_SHADER_NEXT, SK_m, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_SHADER_PREV, SK_n, NO_BTN, AXIS_NONE },
|
||||
|
||||
@@ -116,12 +116,6 @@ static const bool _x264rgb_supp = true;
|
||||
static const bool _x264rgb_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SRC
|
||||
static const bool _src_supp = true;
|
||||
#else
|
||||
static const bool _src_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
static const bool _configfile_supp = true;
|
||||
#else
|
||||
|
||||
@@ -237,7 +237,7 @@ void init_audio(void)
|
||||
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);
|
||||
if ( driver.audio_data == NULL )
|
||||
if (!driver.audio_data)
|
||||
g_extern.audio_active = false;
|
||||
|
||||
if (g_extern.audio_active && driver.audio->use_float && driver.audio->use_float(driver.audio_data))
|
||||
@@ -251,26 +251,28 @@ void init_audio(void)
|
||||
else
|
||||
g_extern.audio_data.chunk_size = g_extern.audio_data.block_chunk_size;
|
||||
|
||||
#ifdef HAVE_SRC
|
||||
int err;
|
||||
g_extern.audio_data.source = src_new(g_settings.audio.src_quality, 2, &err);
|
||||
g_extern.audio_data.source = hermite_new();
|
||||
if (!g_extern.audio_data.source)
|
||||
g_extern.audio_active = false;
|
||||
#else
|
||||
g_extern.audio_data.source = hermite_new(2);
|
||||
if (!g_extern.audio_data.source)
|
||||
g_extern.audio_active = false;
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
assert(g_settings.audio.out_rate < g_settings.audio.in_rate * AUDIO_MAX_RATIO);
|
||||
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;
|
||||
|
||||
init_dsp_plugin();
|
||||
}
|
||||
|
||||
@@ -282,21 +284,16 @@ void uninit_audio(void)
|
||||
return;
|
||||
}
|
||||
|
||||
if ( driver.audio_data && driver.audio )
|
||||
if (driver.audio_data && driver.audio)
|
||||
driver.audio->free(driver.audio_data);
|
||||
|
||||
if ( g_extern.audio_data.source )
|
||||
{
|
||||
#ifdef HAVE_SRC
|
||||
src_delete(g_extern.audio_data.source);
|
||||
#else
|
||||
if (g_extern.audio_data.source)
|
||||
hermite_free(g_extern.audio_data.source);
|
||||
#endif
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
deinit_dsp_plugin();
|
||||
}
|
||||
@@ -435,13 +432,13 @@ void init_video_input(void)
|
||||
{
|
||||
if (g_settings.video.force_aspect)
|
||||
{
|
||||
width = 224 * g_settings.video.xscale * g_settings.video.aspect_ratio;
|
||||
height = 224 * g_settings.video.yscale;
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
width = 256 * g_settings.video.xscale;
|
||||
height = 224 * g_settings.video.yscale;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ enum
|
||||
SSNES_REWIND,
|
||||
SSNES_MOVIE_RECORD_TOGGLE,
|
||||
SSNES_PAUSE_TOGGLE,
|
||||
SSNES_FRAMEADVANCE,
|
||||
SSNES_RESET,
|
||||
SSNES_SHADER_NEXT,
|
||||
SSNES_SHADER_PREV,
|
||||
|
||||
@@ -39,11 +39,11 @@
|
||||
#define SYM(type, x) do { \
|
||||
p##x = (type)DLSYM(lib_handle, x); \
|
||||
if (p##x == NULL) { SSNES_ERR("Failed to load symbol: \"%s\"\n", #x); exit(1); } \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define OPT_SYM(type, x) do { \
|
||||
p##x = (type)DLSYM(lib_handle, x); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
static dylib_t lib_handle = NULL;
|
||||
#endif
|
||||
@@ -144,7 +144,7 @@ static void load_dynamic(void)
|
||||
|
||||
#define SSYM(x) do { \
|
||||
p##x = x; \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#ifndef HAVE_DYNAMIC
|
||||
static void set_statics(void)
|
||||
|
||||
@@ -251,7 +251,7 @@ static ssize_t read_rom_file(FILE* file, void** buf)
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(;;)
|
||||
for (;;)
|
||||
{
|
||||
size_t ret = fread(rom_buf + buf_ptr, 1, buf_size - buf_ptr, stdin);
|
||||
buf_ptr += ret;
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
#define __SSNES_GENERAL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "driver.h"
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include "driver.h"
|
||||
#include "record/ffemu.h"
|
||||
#include "message.h"
|
||||
#include "rewind.h"
|
||||
@@ -36,14 +37,13 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SRC
|
||||
#include <samplerate.h>
|
||||
#else
|
||||
#include "audio/hermite.h"
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/param.h> // MAXPATHLEN
|
||||
#else
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifndef MAXPATHLEN
|
||||
@@ -74,6 +74,7 @@ struct settings
|
||||
char driver[32];
|
||||
float xscale;
|
||||
float yscale;
|
||||
unsigned base_size;
|
||||
bool fullscreen;
|
||||
unsigned fullscreen_x;
|
||||
unsigned fullscreen_y;
|
||||
@@ -121,7 +122,6 @@ struct settings
|
||||
char device[MAXPATHLEN];
|
||||
unsigned latency;
|
||||
bool sync;
|
||||
int src_quality;
|
||||
|
||||
char dsp_plugin[MAXPATHLEN];
|
||||
char external_driver[MAXPATHLEN];
|
||||
@@ -210,11 +210,7 @@ struct global
|
||||
|
||||
struct
|
||||
{
|
||||
#ifdef HAVE_SRC
|
||||
SRC_STATE *source;
|
||||
#else
|
||||
hermite_resampler_t *source;
|
||||
#endif
|
||||
|
||||
float *data;
|
||||
size_t data_ptr;
|
||||
@@ -222,11 +218,17 @@ struct global
|
||||
size_t nonblock_chunk_size;
|
||||
size_t block_chunk_size;
|
||||
|
||||
double src_ratio;
|
||||
|
||||
bool use_float;
|
||||
|
||||
float *outsamples;
|
||||
int16_t *conv_outsamples;
|
||||
|
||||
int16_t *rewind_buf;
|
||||
size_t rewind_ptr;
|
||||
size_t rewind_size;
|
||||
|
||||
dylib_t dsp_lib;
|
||||
const ssnes_dsp_plugin_t *dsp_plugin;
|
||||
void *dsp_handle;
|
||||
@@ -261,6 +263,7 @@ struct global
|
||||
|
||||
// Pausing support
|
||||
bool is_paused;
|
||||
bool is_oneshot;
|
||||
|
||||
// Autosave support.
|
||||
autosave_t *autosave[2];
|
||||
@@ -282,6 +285,13 @@ struct global
|
||||
unsigned record_height;
|
||||
#endif
|
||||
|
||||
struct
|
||||
{
|
||||
const uint16_t *data;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
} frame_cache;
|
||||
|
||||
char title_buf[64];
|
||||
|
||||
struct
|
||||
@@ -309,17 +319,17 @@ extern struct global g_extern;
|
||||
if (g_extern.verbose) \
|
||||
fprintf(stderr, "SSNES: " __VA_ARGS__); \
|
||||
fflush(stderr); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define SSNES_ERR(...) do { \
|
||||
fprintf(stderr, "SSNES [ERROR] :: " __VA_ARGS__); \
|
||||
fflush(stderr); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define SSNES_WARN(...) do { \
|
||||
fprintf(stderr, "SSNES [WARN] :: " __VA_ARGS__); \
|
||||
fflush(stderr); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
static inline uint32_t next_pow2(uint32_t v)
|
||||
{
|
||||
@@ -345,6 +355,19 @@ static inline uint8_t is_little_endian(void)
|
||||
return u.y[0];
|
||||
}
|
||||
|
||||
static inline void ssnes_sleep(unsigned usec)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
Sleep(10);
|
||||
#else
|
||||
struct timespec tv = {
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = 10000000
|
||||
};
|
||||
nanosleep(&tv, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -25,6 +25,11 @@
|
||||
#include "dynamic.h"
|
||||
#include "general.h"
|
||||
#include "sdlwrap.h"
|
||||
#include "gfx_common.h"
|
||||
|
||||
#ifdef HAVE_FREETYPE
|
||||
#include "fonts.h"
|
||||
#endif
|
||||
|
||||
static bool g_input_dead = true;
|
||||
static bool g_video_dead = true;
|
||||
@@ -240,6 +245,21 @@ static bool setup_video(ext_t *ext, const video_info_t *video, const input_drive
|
||||
font_color_g = font_color_g > 255 ? 255 : (font_color_g < 0 ? 0 : font_color_g);
|
||||
font_color_b = font_color_b > 255 ? 255 : (font_color_b < 0 ? 0 : font_color_b);
|
||||
|
||||
const char *font = NULL;
|
||||
#ifdef HAVE_FREETYPE
|
||||
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;
|
||||
#endif
|
||||
|
||||
char title_buf[128];
|
||||
gfx_window_title_reset();
|
||||
gfx_window_title(title_buf, sizeof(title_buf));
|
||||
|
||||
ssnes_video_info_t info = {
|
||||
.width = video->width,
|
||||
.height = video->height,
|
||||
@@ -252,9 +272,10 @@ static bool setup_video(ext_t *ext, const video_info_t *video, const input_drive
|
||||
.color_format = video->rgb32 ? SSNES_COLOR_FORMAT_ARGB8888 : SSNES_COLOR_FORMAT_XRGB1555,
|
||||
.xml_shader = xml_shader,
|
||||
.cg_shader = cg_shader,
|
||||
.ttf_font = *g_settings.video.font_path ? g_settings.video.font_path : NULL,
|
||||
.ttf_font = font,
|
||||
.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,
|
||||
};
|
||||
|
||||
const ssnes_input_driver_t *input_driver = NULL;
|
||||
|
||||
@@ -108,7 +108,10 @@ typedef struct ssnes_video_info
|
||||
// Can be disregarded.
|
||||
const char *ttf_font;
|
||||
unsigned ttf_font_size;
|
||||
unsigned ttf_font_color; // Font color, in format RGB888.
|
||||
unsigned ttf_font_color; // Font color, in format ARGB8888. Alpha should be disregarded.
|
||||
|
||||
// A title that should be displayed in the title bar of the window.
|
||||
const char *title_hint;
|
||||
} ssnes_video_info_t;
|
||||
|
||||
// Some convenience macros.
|
||||
@@ -119,7 +122,7 @@ typedef struct ssnes_video_info
|
||||
|
||||
// I hope no joypad will ever have this many buttons or axes ... ;)
|
||||
// If joykey is this value, do not check that button.
|
||||
#define SSNES_NO_AXIS ((unsigned short)0xFFFFU)
|
||||
#define SSNES_NO_AXIS (0xFFFFFFFFU)
|
||||
#define SSNES_NO_BTN ((unsigned short)0xFFFFU)
|
||||
|
||||
// Masks to test on joykey which hat direction is to be tested for.
|
||||
@@ -236,7 +239,7 @@ typedef struct ssnes_video_driver
|
||||
const char *ident;
|
||||
|
||||
// Needs to be defined to SSNES_GRAPHICS_API_VERSION.
|
||||
// This is used to detect API mismatches.
|
||||
// This is used to detect API/ABI mismatches.
|
||||
int api_version;
|
||||
} ssnes_video_driver_t;
|
||||
|
||||
|
||||
@@ -842,19 +842,14 @@ static void check_window(gl_t *gl)
|
||||
gl->frame_count);
|
||||
|
||||
if (quit)
|
||||
{
|
||||
gl->quitting = true;
|
||||
}
|
||||
else if (resize)
|
||||
{
|
||||
gl->should_resize = true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool gl_frame(void *data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
|
||||
{
|
||||
gl_t *gl = data;
|
||||
check_window(gl);
|
||||
|
||||
gl_shader_use(1);
|
||||
gl->frame_count++;
|
||||
@@ -1336,6 +1331,7 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
static bool gl_alive(void *data)
|
||||
{
|
||||
gl_t *gl = data;
|
||||
check_window(gl);
|
||||
return !gl->quitting;
|
||||
}
|
||||
|
||||
|
||||
@@ -465,7 +465,6 @@ 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)
|
||||
{
|
||||
sdl_video_t *vid = data;
|
||||
check_window(vid);
|
||||
|
||||
if (SDL_MUSTLOCK(vid->buffer))
|
||||
SDL_LockSurface(vid->buffer);
|
||||
@@ -537,6 +536,7 @@ static void sdl_gfx_set_nonblock_state(void *data, bool state)
|
||||
static bool sdl_gfx_alive(void *data)
|
||||
{
|
||||
sdl_video_t *vid = data;
|
||||
check_window(vid);
|
||||
return !vid->quitting;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ static const struct key_bind lut_binds[] = {
|
||||
{ SDLK_KP_MULTIPLY, SK_KP_MULTIPLY },
|
||||
{ SDLK_KP_DIVIDE, SK_KP_DIVIDE },
|
||||
{ SDLK_BACKQUOTE, SK_BACKQUOTE },
|
||||
{ SDLK_PAUSE, SK_PAUSE },
|
||||
{ SDLK_KP0, SK_KP0 },
|
||||
{ SDLK_KP1, SK_KP1 },
|
||||
{ SDLK_KP2, SK_KP2 },
|
||||
|
||||
@@ -65,6 +65,7 @@ static const struct key_bind lut_binds[] = {
|
||||
{ XK_KP_Multiply, SK_KP_MULTIPLY },
|
||||
{ XK_KP_Divide, SK_KP_DIVIDE },
|
||||
{ XK_grave, SK_BACKQUOTE },
|
||||
{ XK_Pause, SK_PAUSE },
|
||||
{ XK_KP_0, SK_KP0 },
|
||||
{ XK_KP_1, SK_KP1 },
|
||||
{ XK_KP_2, SK_KP2 },
|
||||
|
||||
@@ -78,7 +78,7 @@ uint32_t crc32_adjust(uint32_t crc32, uint8_t input)
|
||||
uint32_t crc32_calculate(const uint8_t *data, unsigned length)
|
||||
{
|
||||
uint32_t crc32 = ~0;
|
||||
for(unsigned i = 0; i < length; i++)
|
||||
for (unsigned i = 0; i < length; i++)
|
||||
crc32 = crc32_adjust(crc32, data[i]);
|
||||
return ~crc32;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,25 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#endif
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include "netplay.h"
|
||||
#include "general.h"
|
||||
#include "autosave.h"
|
||||
@@ -25,32 +44,17 @@
|
||||
#include <sys/time.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#endif
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
// Woohoo, Winsock has headers from the STONE AGE! :D
|
||||
#define close(x) closesocket(x)
|
||||
#define CONST_CAST (const char*)
|
||||
#define NONCONST_CAST (char*)
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <signal.h>
|
||||
#define CONST_CAST
|
||||
#define NONCONST_CAST
|
||||
#endif
|
||||
|
||||
|
||||
#define PREV_PTR(x) ((x) == 0 ? handle->buffer_size - 1 : (x) - 1)
|
||||
#define NEXT_PTR(x) ((x + 1) % handle->buffer_size)
|
||||
|
||||
|
||||
+1
-3
@@ -82,8 +82,6 @@ if [ $HAVE_FFMPEG = yes ]; then
|
||||
check_lib FFMPEG_AVFORMAT_WRITE_HEADER "$AVFORMAT_LIBS" avformat_write_header
|
||||
fi
|
||||
|
||||
check_pkgconf SRC samplerate
|
||||
|
||||
check_lib DYNAMIC $DYLIB dlopen
|
||||
|
||||
check_pkgconf FREETYPE freetype2
|
||||
@@ -96,7 +94,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 SRC 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 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"
|
||||
create_config_make config.mk $VARS
|
||||
create_config_header config.h $VARS
|
||||
|
||||
+1
-2
@@ -1,7 +1,7 @@
|
||||
. qb/qb.params.sh
|
||||
|
||||
PACKAGE_NAME=ssnes
|
||||
PACKAGE_VERSION=0.9-rc2
|
||||
PACKAGE_VERSION=0.9
|
||||
|
||||
# Adds a command line opt to ./configure --help
|
||||
# $1: Variable (HAVE_ALSA, HAVE_OSS, etc)
|
||||
@@ -13,7 +13,6 @@ add_command_line_enable FFMPEG "Enable FFmpeg recording support" no
|
||||
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 SRC "Enable libsamplerate support" no
|
||||
add_command_line_enable CONFIGFILE "Disable support for config file" yes
|
||||
add_command_line_enable CG "Enable Cg shader support" auto
|
||||
add_command_line_enable XML "Enable bSNES-style XML shader support" auto
|
||||
|
||||
@@ -29,7 +29,6 @@ struct state_manager
|
||||
uint64_t *buffer;
|
||||
size_t buf_size;
|
||||
uint32_t *tmp_state;
|
||||
uint32_t *scratch_buf;
|
||||
size_t top_ptr;
|
||||
size_t bottom_ptr;
|
||||
size_t state_size;
|
||||
|
||||
+13
-16
@@ -120,6 +120,7 @@ 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;
|
||||
@@ -161,10 +162,6 @@ static void set_defaults(void)
|
||||
g_settings.audio.latency = out_latency;
|
||||
g_settings.audio.sync = audio_sync;
|
||||
|
||||
#ifdef HAVE_SRC
|
||||
g_settings.audio.src_quality = SAMPLERATE_QUALITY;
|
||||
#endif
|
||||
|
||||
g_settings.rewind_enable = rewind_enable;
|
||||
g_settings.rewind_buffer_size = rewind_buffer_size;
|
||||
g_settings.rewind_granularity = rewind_granularity;
|
||||
@@ -292,7 +289,11 @@ static void parse_config_file(void)
|
||||
return;
|
||||
|
||||
if (g_extern.verbose)
|
||||
config_file_dump(conf, stderr);
|
||||
{
|
||||
fprintf(stderr, "=== Config ===\n");
|
||||
config_file_dump_all(conf, stderr);
|
||||
fprintf(stderr, "=== Config end ===\n");
|
||||
}
|
||||
|
||||
int tmp_int;
|
||||
double tmp_double;
|
||||
@@ -301,6 +302,7 @@ 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");
|
||||
|
||||
@@ -384,17 +386,6 @@ static void parse_config_file(void)
|
||||
CONFIG_GET_INT(audio.latency, "audio_latency");
|
||||
CONFIG_GET_BOOL(audio.sync, "audio_sync");
|
||||
|
||||
#ifdef HAVE_SRC
|
||||
if (config_get_int(conf, "audio_src_quality", &tmp_int))
|
||||
{
|
||||
int quals[] = { SRC_ZERO_ORDER_HOLD, SRC_LINEAR, SRC_SINC_FASTEST,
|
||||
SRC_SINC_MEDIUM_QUALITY, SRC_SINC_BEST_QUALITY };
|
||||
|
||||
if (tmp_int > 0 && tmp_int < 6)
|
||||
g_settings.audio.src_quality = quals[tmp_int];
|
||||
}
|
||||
#endif
|
||||
|
||||
CONFIG_GET_STRING(video.driver, "video_driver");
|
||||
CONFIG_GET_STRING(audio.driver, "audio_driver");
|
||||
CONFIG_GET_STRING(audio.dsp_plugin, "audio_dsp_plugin");
|
||||
@@ -488,6 +479,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
|
||||
DECLARE_BIND(rewind, SSNES_REWIND)
|
||||
DECLARE_BIND(movie_record_toggle, SSNES_MOVIE_RECORD_TOGGLE)
|
||||
DECLARE_BIND(pause_toggle, SSNES_PAUSE_TOGGLE)
|
||||
DECLARE_BIND(frame_advance, SSNES_FRAMEADVANCE)
|
||||
DECLARE_BIND(reset, SSNES_RESET)
|
||||
DECLARE_BIND(shader_next, SSNES_SHADER_NEXT)
|
||||
DECLARE_BIND(shader_prev, SSNES_SHADER_PREV)
|
||||
@@ -523,6 +515,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
|
||||
DECLARE_BIND(rewind, SSNES_REWIND)
|
||||
DECLARE_BIND(movie_record_toggle, SSNES_MOVIE_RECORD_TOGGLE)
|
||||
DECLARE_BIND(pause_toggle, SSNES_PAUSE_TOGGLE)
|
||||
DECLARE_BIND(frame_advance, SSNES_FRAMEADVANCE)
|
||||
DECLARE_BIND(reset, SSNES_RESET)
|
||||
DECLARE_BIND(shader_next, SSNES_SHADER_NEXT)
|
||||
DECLARE_BIND(shader_prev, SSNES_SHADER_PREV)
|
||||
@@ -558,6 +551,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
|
||||
DECLARE_BIND(rewind, SSNES_REWIND)
|
||||
DECLARE_BIND(movie_record_toggle, SSNES_MOVIE_RECORD_TOGGLE)
|
||||
DECLARE_BIND(pause_toggle, SSNES_PAUSE_TOGGLE)
|
||||
DECLARE_BIND(frame_advance, SSNES_FRAMEADVANCE)
|
||||
DECLARE_BIND(reset, SSNES_RESET)
|
||||
DECLARE_BIND(shader_next, SSNES_SHADER_NEXT)
|
||||
DECLARE_BIND(shader_prev, SSNES_SHADER_PREV)
|
||||
@@ -593,6 +587,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
|
||||
DECLARE_BIND(rewind, SSNES_REWIND)
|
||||
DECLARE_BIND(movie_record_toggle, SSNES_MOVIE_RECORD_TOGGLE)
|
||||
DECLARE_BIND(pause_toggle, SSNES_PAUSE_TOGGLE)
|
||||
DECLARE_BIND(frame_advance, SSNES_FRAMEADVANCE)
|
||||
DECLARE_BIND(reset, SSNES_RESET)
|
||||
DECLARE_BIND(shader_next, SSNES_SHADER_NEXT)
|
||||
DECLARE_BIND(shader_prev, SSNES_SHADER_PREV)
|
||||
@@ -628,6 +623,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
|
||||
DECLARE_BIND(rewind, SSNES_REWIND)
|
||||
DECLARE_BIND(movie_record_toggle, SSNES_MOVIE_RECORD_TOGGLE)
|
||||
DECLARE_BIND(pause_toggle, SSNES_PAUSE_TOGGLE)
|
||||
DECLARE_BIND(frame_advance, SSNES_FRAMEADVANCE)
|
||||
DECLARE_BIND(reset, SSNES_RESET)
|
||||
DECLARE_BIND(shader_next, SSNES_SHADER_NEXT)
|
||||
DECLARE_BIND(shader_prev, SSNES_SHADER_PREV)
|
||||
@@ -711,6 +707,7 @@ static const struct key_map sk_map[] = {
|
||||
{ "scroll_lock", SK_SCROLLOCK },
|
||||
{ "tilde", SK_BACKQUOTE },
|
||||
{ "backquote", SK_BACKQUOTE },
|
||||
{ "pause", SK_PAUSE },
|
||||
{ "nul", SK_UNKNOWN },
|
||||
};
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <libsnes.hpp>
|
||||
#include <stdio.h>
|
||||
@@ -23,7 +22,6 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include "driver.h"
|
||||
#include "file.h"
|
||||
@@ -36,17 +34,14 @@
|
||||
#include "strl.h"
|
||||
#include "screenshot.h"
|
||||
#include "cheats.h"
|
||||
#include "audio/utils.h"
|
||||
#include <assert.h>
|
||||
#ifdef HAVE_SRC
|
||||
#include <samplerate.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "SDL.h"
|
||||
// OSX seems to really need -lSDLmain,
|
||||
@@ -186,42 +181,61 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
|
||||
if (!driver.video->frame(driver.video_data, data, width, height, (height == 448 || height == 478) ? 1024 : 2048, msg))
|
||||
g_extern.video_active = false;
|
||||
#endif
|
||||
|
||||
g_extern.frame_cache.data = data;
|
||||
g_extern.frame_cache.width = width;
|
||||
g_extern.frame_cache.height = height;
|
||||
}
|
||||
|
||||
static void audio_sample(uint16_t left, uint16_t right)
|
||||
static void video_cached_frame(void)
|
||||
{
|
||||
if (!g_extern.audio_active)
|
||||
return;
|
||||
#ifdef HAVE_FFMPEG
|
||||
// Cannot allow FFmpeg recording when pushing duped frames.
|
||||
bool recording = g_extern.recording;
|
||||
g_extern.recording = false;
|
||||
#endif
|
||||
|
||||
const float *output_data = NULL;
|
||||
unsigned output_frames = 0;
|
||||
// Not 100% safe, since the library might have
|
||||
// freed the memory, but no known implementations do this :D
|
||||
// It would be really stupid at any rate ...
|
||||
if (g_extern.frame_cache.data)
|
||||
{
|
||||
// Push the pipeline through. A hack sort of.
|
||||
SSNES_LOG("Pushing cached frame.\n");
|
||||
video_frame(g_extern.frame_cache.data,
|
||||
g_extern.frame_cache.width,
|
||||
g_extern.frame_cache.height);
|
||||
}
|
||||
|
||||
#ifdef HAVE_FFMPEG
|
||||
g_extern.audio_data.conv_outsamples[g_extern.audio_data.data_ptr] = left;
|
||||
g_extern.recording = recording;
|
||||
#endif
|
||||
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(int16_t)left/0x8000;
|
||||
#ifdef HAVE_FFMPEG
|
||||
g_extern.audio_data.conv_outsamples[g_extern.audio_data.data_ptr] = right;
|
||||
#endif
|
||||
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(int16_t)right/0x8000;
|
||||
|
||||
if (g_extern.audio_data.data_ptr < g_extern.audio_data.chunk_size)
|
||||
return;
|
||||
}
|
||||
|
||||
static bool audio_flush(const int16_t *data, unsigned samples)
|
||||
{
|
||||
#ifdef HAVE_FFMPEG
|
||||
if (g_extern.recording)
|
||||
{
|
||||
struct ffemu_audio_data ffemu_data = {
|
||||
.data = g_extern.audio_data.conv_outsamples,
|
||||
.frames = g_extern.audio_data.data_ptr / 2
|
||||
.data = data,
|
||||
.frames = samples / 2
|
||||
};
|
||||
ffemu_push_audio(g_extern.rec, &ffemu_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (g_extern.is_paused)
|
||||
return true;
|
||||
|
||||
const float *output_data = NULL;
|
||||
unsigned output_frames = 0;
|
||||
|
||||
audio_convert_s16_to_float(g_extern.audio_data.data, data, samples);
|
||||
|
||||
ssnes_dsp_input_t dsp_input = {
|
||||
.samples = g_extern.audio_data.data,
|
||||
.frames = g_extern.audio_data.data_ptr / 2
|
||||
.frames = samples / 2
|
||||
};
|
||||
|
||||
ssnes_dsp_output_t dsp_output = {
|
||||
@@ -231,29 +245,17 @@ static void audio_sample(uint16_t left, uint16_t right)
|
||||
if (g_extern.audio_data.dsp_plugin)
|
||||
g_extern.audio_data.dsp_plugin->process(g_extern.audio_data.dsp_handle, &dsp_output, &dsp_input);
|
||||
|
||||
if (g_extern.frame_is_reverse) // Disable fucked up audio when rewinding...
|
||||
memset(g_extern.audio_data.data, 0, g_extern.audio_data.chunk_size * sizeof(float));
|
||||
|
||||
#ifdef HAVE_SRC
|
||||
SRC_DATA src_data = {
|
||||
#else
|
||||
struct hermite_data src_data = {
|
||||
#endif
|
||||
.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 : (g_extern.audio_data.data_ptr / 2),
|
||||
.output_frames = g_extern.audio_data.chunk_size * 8,
|
||||
.end_of_input = 0,
|
||||
.src_ratio = (double)g_settings.audio.out_rate / (double)g_settings.audio.in_rate,
|
||||
.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)
|
||||
{
|
||||
#ifdef HAVE_SRC
|
||||
src_process(g_extern.audio_data.source, &src_data);
|
||||
#else
|
||||
hermite_process(g_extern.audio_data.source, &src_data);
|
||||
#endif
|
||||
|
||||
output_data = g_extern.audio_data.outsamples;
|
||||
output_frames = src_data.output_frames_gen;
|
||||
@@ -264,30 +266,52 @@ static void audio_sample(uint16_t left, uint16_t right)
|
||||
output_frames = dsp_output.frames;
|
||||
}
|
||||
|
||||
|
||||
if (g_extern.audio_data.use_float)
|
||||
{
|
||||
if (driver.audio->write(driver.audio_data, output_data, output_frames * sizeof(float) * 2) < 0)
|
||||
{
|
||||
fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n");
|
||||
g_extern.audio_active = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned i = 0; i < output_frames * 2; i++)
|
||||
{
|
||||
int32_t val = output_data[i] * 0x8000;
|
||||
g_extern.audio_data.conv_outsamples[i] = (val > 0x7FFF) ? 0x7FFF : (val < -0x8000 ? -0x8000 : (int16_t)val);
|
||||
}
|
||||
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)
|
||||
{
|
||||
fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n");
|
||||
g_extern.audio_active = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (g_extern.audio_data.data_ptr < g_extern.audio_data.chunk_size)
|
||||
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 = 0;
|
||||
}
|
||||
|
||||
@@ -318,7 +342,10 @@ static int16_t input_state(bool port, unsigned device, unsigned index, unsigned
|
||||
g_settings.input.binds[4]
|
||||
};
|
||||
|
||||
int16_t res = driver.input->input_state(driver.input_data, binds, port, device, index, id);
|
||||
int16_t res = 0;
|
||||
if (id < 12)
|
||||
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);
|
||||
|
||||
@@ -334,7 +361,7 @@ static int16_t input_state(bool port, unsigned device, unsigned index, unsigned
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define PACKAGE_VERSION "0.9-rc2"
|
||||
#define PACKAGE_VERSION "0.9"
|
||||
#endif
|
||||
|
||||
#include "config.features.h"
|
||||
@@ -362,7 +389,6 @@ static void print_features(void)
|
||||
_PSUPP(dynamic, "Dynamic", "Dynamic run-time loading of libsnes library");
|
||||
_PSUPP(ffmpeg, "FFmpeg", "On-the-fly recording of gameplay with libavcodec");
|
||||
_PSUPP(x264rgb, "x264 RGB", "x264 lossless RGB recording for FFmpeg");
|
||||
_PSUPP(src, "SRC", "libsamplerate audio resampling");
|
||||
_PSUPP(configfile, "Config file", "Configuration file support");
|
||||
_PSUPP(freetype, "FreeType", "TTF font rendering with FreeType");
|
||||
_PSUPP(netplay, "Netplay", "Peer-to-peer netplay");
|
||||
@@ -559,7 +585,7 @@ static void parse_input(int argc, char *argv[])
|
||||
#endif
|
||||
|
||||
char optstring[] = "hs:fvS:m:p4jJg:b:B:Y:Z:P:HC:F:U:DN:X:" FFMPEG_RECORD_ARG CONFIG_FILE_ARG;
|
||||
for(;;)
|
||||
for (;;)
|
||||
{
|
||||
val = 0;
|
||||
int c = getopt_long(argc, argv, optstring, opts, &option_index);
|
||||
@@ -1270,19 +1296,25 @@ static void check_savestates(void)
|
||||
old_should_loadstate = should_loadstate;
|
||||
}
|
||||
|
||||
static void check_fullscreen(void)
|
||||
static bool check_fullscreen(void)
|
||||
{
|
||||
static bool was_pressed = false;
|
||||
bool pressed;
|
||||
// If we go fullscreen we drop all drivers and reinit to be safe.
|
||||
if ((pressed = driver.input->key_pressed(driver.input_data, SSNES_FULLSCREEN_TOGGLE_KEY)) && !was_pressed)
|
||||
static bool was_pressed = false;
|
||||
bool pressed = driver.input->key_pressed(driver.input_data, SSNES_FULLSCREEN_TOGGLE_KEY);
|
||||
bool toggle = pressed && !was_pressed;
|
||||
if (toggle)
|
||||
{
|
||||
g_settings.video.fullscreen = !g_settings.video.fullscreen;
|
||||
uninit_drivers();
|
||||
init_drivers();
|
||||
|
||||
// Poll input to avoid possibly stale data to corrupt things.
|
||||
if (driver.input)
|
||||
driver.input->poll(driver.input_data);
|
||||
}
|
||||
|
||||
was_pressed = pressed;
|
||||
return toggle;
|
||||
}
|
||||
|
||||
static void check_stateslots(void)
|
||||
@@ -1338,12 +1370,42 @@ static void check_input_rate(void)
|
||||
msg_queue_clear(g_extern.msg_queue);
|
||||
msg_queue_push(g_extern.msg_queue, msg, 0, 180);
|
||||
SSNES_LOG("%s\n", msg);
|
||||
|
||||
g_extern.audio_data.src_ratio =
|
||||
(double)g_settings.audio.out_rate / g_settings.audio.in_rate;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void flush_rewind_audio(void)
|
||||
{
|
||||
if (g_extern.frame_is_reverse && g_extern.audio_active) // 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);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void setup_rewind_audio(void)
|
||||
{
|
||||
// Push audio ready to be played.
|
||||
g_extern.audio_data.rewind_ptr = g_extern.audio_data.rewind_size;
|
||||
for (unsigned i = 0; i < g_extern.audio_data.data_ptr; i += 2)
|
||||
{
|
||||
g_extern.audio_data.rewind_buf[--g_extern.audio_data.rewind_ptr] =
|
||||
g_extern.audio_data.conv_outsamples[i + 1];
|
||||
|
||||
g_extern.audio_data.rewind_buf[--g_extern.audio_data.rewind_ptr] =
|
||||
g_extern.audio_data.conv_outsamples[i + 0];
|
||||
}
|
||||
|
||||
g_extern.audio_data.data_ptr = 0;
|
||||
}
|
||||
|
||||
static void check_rewind(void)
|
||||
{
|
||||
flush_rewind_audio();
|
||||
g_extern.frame_is_reverse = false;
|
||||
|
||||
static bool first = true;
|
||||
if (first)
|
||||
{
|
||||
@@ -1361,13 +1423,13 @@ static void check_rewind(void)
|
||||
if (state_manager_pop(g_extern.state_manager, &buf))
|
||||
{
|
||||
g_extern.frame_is_reverse = true;
|
||||
msg_queue_push(g_extern.msg_queue, "Rewinding!", 0, 30);
|
||||
setup_rewind_audio();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
msg_queue_push(g_extern.msg_queue, "Reached end of rewind buffer!", 0, 30);
|
||||
@@ -1382,6 +1444,8 @@ static void check_rewind(void)
|
||||
state_manager_push(g_extern.state_manager, g_extern.state_buf);
|
||||
}
|
||||
}
|
||||
|
||||
psnes_set_audio_sample(g_extern.frame_is_reverse ? audio_sample_rewind : audio_sample);
|
||||
}
|
||||
|
||||
static void check_movie_record(void)
|
||||
@@ -1426,6 +1490,9 @@ static void check_pause(void)
|
||||
static bool old_state = false;
|
||||
bool new_state = driver.input->key_pressed(driver.input_data, SSNES_PAUSE_TOGGLE);
|
||||
|
||||
// FRAMEADVANCE will set us into pause mode.
|
||||
new_state |= !g_extern.is_paused && driver.input->key_pressed(driver.input_data, SSNES_FRAMEADVANCE);
|
||||
|
||||
static bool old_focus = true;
|
||||
bool focus = true;
|
||||
|
||||
@@ -1480,6 +1547,20 @@ static void check_pause(void)
|
||||
old_state = new_state;
|
||||
}
|
||||
|
||||
static void check_oneshot(void)
|
||||
{
|
||||
static bool old_state = false;
|
||||
bool new_state = driver.input->key_pressed(driver.input_data, SSNES_FRAMEADVANCE);
|
||||
g_extern.is_oneshot = (new_state && !old_state);
|
||||
old_state = new_state;
|
||||
|
||||
// Rewind buttons works like FRAMEREWIND when paused. We will one-shot in that case.
|
||||
static bool old_rewind_state = false;
|
||||
bool new_rewind_state = driver.input->key_pressed(driver.input_data, SSNES_REWIND);
|
||||
g_extern.is_oneshot |= new_rewind_state && !old_rewind_state;
|
||||
old_rewind_state = new_rewind_state;
|
||||
}
|
||||
|
||||
static void check_reset(void)
|
||||
{
|
||||
if (driver.input->key_pressed(driver.input_data, SSNES_RESET))
|
||||
@@ -1592,12 +1673,13 @@ static void do_state_checks(void)
|
||||
{
|
||||
if (!g_extern.netplay)
|
||||
{
|
||||
check_reset();
|
||||
#ifdef HAVE_XML
|
||||
check_cheats();
|
||||
#endif
|
||||
check_pause();
|
||||
if (g_extern.is_paused)
|
||||
check_oneshot();
|
||||
|
||||
if (check_fullscreen() && g_extern.is_paused)
|
||||
video_cached_frame();
|
||||
|
||||
if (g_extern.is_paused && !g_extern.is_oneshot)
|
||||
return;
|
||||
|
||||
set_fast_forward_button(
|
||||
@@ -1609,21 +1691,23 @@ static void do_state_checks(void)
|
||||
check_stateslots();
|
||||
check_savestates();
|
||||
}
|
||||
check_rewind();
|
||||
|
||||
check_rewind();
|
||||
if (!g_extern.bsv_movie_playback)
|
||||
check_movie_record();
|
||||
|
||||
|
||||
#ifdef HAVE_XML
|
||||
check_shader_dir();
|
||||
check_cheats();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DYLIB
|
||||
check_dsp_config();
|
||||
#endif
|
||||
check_reset();
|
||||
}
|
||||
|
||||
check_fullscreen();
|
||||
else
|
||||
check_fullscreen();
|
||||
|
||||
#ifdef HAVE_DYLIB
|
||||
// DSP plugin doesn't use variable input rate.
|
||||
@@ -1725,7 +1809,7 @@ int main(int argc, char *argv[])
|
||||
do_state_checks();
|
||||
|
||||
// Run libsnes for one frame.
|
||||
if (!g_extern.is_paused)
|
||||
if (!g_extern.is_paused || g_extern.is_oneshot)
|
||||
{
|
||||
lock_autosave();
|
||||
|
||||
@@ -1750,15 +1834,7 @@ int main(int argc, char *argv[])
|
||||
else
|
||||
{
|
||||
input_poll();
|
||||
#ifdef _WIN32
|
||||
Sleep(10);
|
||||
#else
|
||||
struct timespec tv = {
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = 10000000
|
||||
};
|
||||
nanosleep(&tv, NULL);
|
||||
#endif
|
||||
ssnes_sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,14 @@
|
||||
# Path to external video driver using the SSNES driver API.
|
||||
# video_external_driver =
|
||||
|
||||
# Windowed xscale and yscale (Real x res: 224 * xscale * aspect_ratio, real y scale: 224 * xscale)
|
||||
# Windowed xscale and yscale
|
||||
# (Real x res: base_size * xscale * aspect_ratio, real y res: base_size * yscale)
|
||||
# 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
|
||||
@@ -132,8 +136,6 @@
|
||||
# Desired audio latency in milliseconds. Might not be honored if driver can't provide given latency.
|
||||
# audio_latency = 64
|
||||
|
||||
# libsamplerate quality. Valid values are from 1 to 5. These values map to zero_order_hold, linear, sinc_fastest, sinc_medium and sinc_best respectively.
|
||||
# audio_src_quality =
|
||||
|
||||
### Input
|
||||
|
||||
@@ -275,6 +277,9 @@
|
||||
# Toggle between paused and non-paused state
|
||||
# input_pause_toggle = p
|
||||
|
||||
# Frame advance when game is paused
|
||||
# input_frame_advance = k
|
||||
|
||||
# Reset the emulated SNES.
|
||||
# input_reset = h
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@ static struct bind binds[] = {
|
||||
MISC_BIND("Rewind", rewind)
|
||||
MISC_BIND("Movie recording toggle", movie_record_toggle)
|
||||
MISC_BIND("Pause", pause_toggle)
|
||||
MISC_BIND("Frame advance", frame_advance)
|
||||
MISC_BIND("Reset", reset)
|
||||
MISC_BIND("Next shader", shader_next)
|
||||
MISC_BIND("Previous shader", shader_prev)
|
||||
@@ -235,7 +236,7 @@ static void parse_input(int argc, char *argv[])
|
||||
};
|
||||
|
||||
int option_index = 0;
|
||||
for(;;)
|
||||
for (;;)
|
||||
{
|
||||
int c = getopt_long(argc, argv, optstring, opts, &option_index);
|
||||
if (c == -1)
|
||||
|
||||
Reference in new issue
Block a user