Compare commits

...
44 Commits
Author SHA1 Message Date
Themaister 70e9f084b7 Merge branch 'master' of github.com:Themaister/SSNES 2011-10-22 20:20:03 +02:00
Themaister 824790a80d Updates for 0.9. 2011-10-22 20:19:40 +02:00
Themaister 23d23983bc Fix broken #define in header. 2011-10-21 22:12:03 +02:00
Themaister c42655ed91 Logging for paused. 2011-10-20 23:33:15 +02:00
Themaister f46ee78c76 Apply a hack and push the frame several times when changing fullscreen. 2011-10-20 23:28:46 +02:00
Themaister 5d1d188231 Hardcode hermite for 2 channels. 2011-10-20 22:26:41 +02:00
Themaister c55ce543c2 Revert "Use C99 stdio in mingw, yay" :\
This reverts commit de8bc62474.
2011-10-19 18:12:30 +02:00
Themaister de8bc62474 Use C99 stdio in mingw, yay 2011-10-19 18:10:05 +02:00
Themaister 397da887a7 Merge branch 'master' of github.com:Themaister/SSNES 2011-10-19 08:16:26 +02:00
Themaister f44733d7f6 Only show paused rewind for 1 frame. 2011-10-26 03:24:39 +02:00
Themaister 5faa28a1a2 Clarify x/yscale. 2011-10-19 08:16:01 +02:00
Themaister efaa85ff9b Cleaner. 2011-10-18 21:59:37 +02:00
Themaister 8f5580be10 Frame advance goes into pause. 2011-10-18 21:57:28 +02:00
Themaister c7acd99548 Same for SDL ... 2011-10-18 19:52:43 +02:00
Themaister 64ee13e6f4 Push window loop in .alive(). 2011-10-18 19:51:38 +02:00
Themaister e134f8afc7 And yet another ... :) 2011-10-18 19:22:20 +02:00
Themaister dd89e6fbe0 Fix build without FFmpeg. 2011-10-18 19:16:38 +02:00
Themaister a79a1b46f7 Updates for 0.9-rc4. 2011-10-18 19:12:28 +02:00
Themaister 6467f51ea8 Frame caching for more "proper" fullscreen switch. 2011-10-18 17:26:15 +02:00
Themaister 141eefb03b Fix fullscreen pause toggle for windows. 2011-10-18 14:39:54 +02:00
Themaister 068c224d83 Include windows.h properly. 2011-10-18 14:05:30 +02:00
Themaister 5bc8090ea7 Allow fullscreen toggle while paused. 2011-10-18 13:29:39 +02:00
Themaister 466ab1ef8d Frame advance. 2011-10-17 21:30:58 +02:00
Themaister a1c19ed1f5 Minor refactor. 2011-10-17 20:46:38 +02:00
Themaister cce4b7d20c Allow #include depth. 2011-10-17 20:09:57 +02:00
Themaister 80925ffc11 Some Windows fixes. 2011-10-17 19:30:52 +02:00
Themaister 9eb14e328d Add "#include" for config files. 2011-10-17 19:11:31 +02:00
Themaister 5ae0cb1821 Base size for windowed mode calculations. 2011-10-16 21:36:17 +02:00
Themaister fc594f7f0f Fix edge case on input poll. 2011-10-16 15:45:04 +02:00
Themaister 0c4e406ee7 Remove unused variable. 2011-10-16 12:51:14 +02:00
Themaister a082ec5251 Fix segfault. 2011-10-16 04:01:01 +02:00
Themaister 953b5daf0a Pause hooked up. 2011-10-16 02:00:55 +02:00
Themaister b4ff622f32 Title buf hints. Purge samplerate opts. 2011-10-15 20:05:34 +02:00
Themaister f26b54c998 Updates for RC3. 2011-10-15 18:51:58 +02:00
Themaister d7b3f39136 SSE2 optimized a bit :D 2011-10-15 18:09:44 +02:00
Themaister 5bcf601a40 Refactor out audio conversion. 2011-10-15 17:47:29 +02:00
Themaister e91ebdac97 Do not recalculate ratio all the time. 2011-10-15 16:16:13 +02:00
Themaister b2dd12d186 Rewind audio! :D 2011-10-15 14:33:41 +02:00
Themaister 9c79bf0ba2 Refactor audio flushing method. 2011-10-15 13:16:32 +02:00
Themaister f48247a18b Style nits ... 2011-10-15 12:56:48 +02:00
Themaister 48e015558e Style nits. 2011-10-15 12:54:47 +02:00
Themaister 0ff3cbc93b Nits. 2011-10-15 12:51:17 +02:00
Themaister 25f0e162a4 Purge support for libsamplerate. Obsolete. 2011-10-15 12:48:15 +02:00
Themaister 0810812cd9 Slight cleanups when pushing audio. 2011-10-15 12:42:39 +02:00
32 changed files with 735 additions and 310 deletions

No files matched your search

+1 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+83
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 },
-6
View File
@@ -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
+19 -22
View File
@@ -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);
}
}
+1
View File
@@ -41,6 +41,7 @@ enum
SSNES_REWIND,
SSNES_MOVIE_RECORD_TOGGLE,
SSNES_PAUSE_TOGGLE,
SSNES_FRAMEADVANCE,
SSNES_RESET,
SSNES_SHADER_NEXT,
SSNES_SHADER_PREV,
+3 -3
View File
@@ -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)
+1 -1
View File
@@ -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;
+36 -13
View File
@@ -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
+22 -1
View File
@@ -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;
+6 -3
View File
@@ -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;
+1 -5
View File
@@ -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;
}
+1 -1
View File
@@ -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;
}
+1
View File
@@ -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 },
+1
View File
@@ -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 },
+1 -1
View File
@@ -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;
}
+20 -16
View File
@@ -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
View File
@@ -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
View File
@@ -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
-1
View File
@@ -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
View File
@@ -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 },
};
+152 -76
View File
@@ -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);
}
}
+8 -3
View File
@@ -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
+2 -1
View File
@@ -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)