mirror of
https://github.com/libretro/RetroArch
synced 2025-03-30 16:20:27 +00:00
Merge branch 'master' of ssh://10.0.0.4:443/~/bin/ssnes
This commit is contained in:
commit
92adb4c645
47
Makefile
47
Makefile
@ -2,43 +2,44 @@ include config.mk
|
||||
|
||||
TARGET = ssnes
|
||||
|
||||
DEFINES =
|
||||
OBJ = ssnes.o file.o driver.o
|
||||
libsnes = -lsnes
|
||||
OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o
|
||||
|
||||
LIBS = -lsamplerate $(libsnes)
|
||||
LIBS = -lsamplerate
|
||||
|
||||
ifeq ($(BUILD_RSOUND), 1)
|
||||
ifeq ($(HAVE_RSOUND), 1)
|
||||
OBJ += audio/rsound.o
|
||||
LIBS += -lrsound
|
||||
endif
|
||||
ifeq ($(BUILD_OSS), 1)
|
||||
ifeq ($(HAVE_OSS), 1)
|
||||
OBJ += audio/oss.o
|
||||
endif
|
||||
ifeq ($(BUILD_ALSA), 1)
|
||||
ifeq ($(HAVE_ALSA), 1)
|
||||
OBJ += audio/alsa.o
|
||||
LIBS += -lasound
|
||||
endif
|
||||
ifeq ($(BUILD_ROAR), 1)
|
||||
ifeq ($(HAVE_ROAR), 1)
|
||||
OBJ += audio/roar.o
|
||||
LIBS += -lroar
|
||||
endif
|
||||
ifeq ($(BUILD_AL), 1)
|
||||
ifeq ($(HAVE_AL), 1)
|
||||
OBJ += audio/openal.o
|
||||
LIBS += -lopenal
|
||||
endif
|
||||
ifeq ($(HAVE_JACK),1)
|
||||
OBJ += audio/jack.o
|
||||
LIBS += -ljack
|
||||
endif
|
||||
|
||||
ifeq ($(BUILD_OPENGL), 1)
|
||||
ifeq ($(HAVE_GLFW), 1)
|
||||
OBJ += gfx/gl.o
|
||||
LIBS += -lglfw
|
||||
endif
|
||||
|
||||
ifeq ($(BUILD_CG), 1)
|
||||
ifeq ($(HAVE_CG), 1)
|
||||
LIBS += -lCg -lCgGL
|
||||
DEFINES += -DHAVE_CG
|
||||
endif
|
||||
|
||||
ifeq ($(BUILD_FILTER), 1)
|
||||
ifeq ($(HAVE_FILTER), 1)
|
||||
OBJ += hqflt/hq.o
|
||||
OBJ += hqflt/grayscale.o
|
||||
OBJ += hqflt/bleed.o
|
||||
@ -46,9 +47,19 @@ ifeq ($(BUILD_FILTER), 1)
|
||||
OBJ += hqflt/snes_ntsc/snes_ntsc.o
|
||||
endif
|
||||
|
||||
CFLAGS = -Wall -O3 -std=gnu99 -Wno-unused-variable -I. $(DEFINES)
|
||||
ifeq ($(HAVE_DYNAMIC), 1)
|
||||
LIBS += -ldl
|
||||
else
|
||||
LIBS += $(libsnes)
|
||||
endif
|
||||
|
||||
all: $(TARGET)
|
||||
CFLAGS = -Wall -O3 -g -std=gnu99 -I.
|
||||
|
||||
all: $(TARGET) config.mk
|
||||
|
||||
config.mk: configure qb/*
|
||||
@echo "config.mk is outdated or non-existing. Run ./configure again."
|
||||
@exit 1
|
||||
|
||||
ssnes: $(OBJ)
|
||||
$(CXX) -o $@ $(OBJ) $(LIBS) $(CFLAGS)
|
||||
@ -57,14 +68,16 @@ ssnes: $(OBJ)
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
install: $(TARGET)
|
||||
install -m755 $(TARGET) $(PREFIX)/bin
|
||||
install -m755 $(TARGET) $(DESTDIR)/$(PREFIX)/bin
|
||||
install -m644 ssnes.cfg $(DESTDIR)/etc/ssnes.cfg
|
||||
|
||||
uninstall: $(TARGET)
|
||||
rm -rf $(PREFIX)/bin/$(TARGET)
|
||||
rm -rf $(DESTDIR)/$(PREFIX)/bin/$(TARGET)
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f audio/*.o
|
||||
rm -f conf/*.o
|
||||
rm -f gfx/*.o
|
||||
rm -f hqflt/*.o
|
||||
rm -f hqflt/snes_ntsc/*.o
|
||||
|
28
README.md
28
README.md
@ -10,9 +10,7 @@ This enables the possibility of custom front-ends for the emulator.
|
||||
# Philosophy
|
||||
|
||||
SSNES attempts to be very small and lean, while still having all the useful core features expected from an emulator.
|
||||
It is close in spirit to suckless' DWM, in that configuring the emulator requires a recompile.
|
||||
The configuration is done through editing a C header file.
|
||||
C programming skills are not necessary to configure it (no programming involved), but some basic programming experience might be needed.
|
||||
It is used through command-line.
|
||||
|
||||
# Dependencies
|
||||
|
||||
@ -33,6 +31,7 @@ SSNES needs one of these audio driver libraries:
|
||||
- RoarAudio
|
||||
- RSound
|
||||
- OpenAL
|
||||
- JACK
|
||||
|
||||
# Building libsnes
|
||||
|
||||
@ -46,17 +45,26 @@ SSNES needs one of these audio driver libraries:
|
||||
|
||||
# Configuring
|
||||
|
||||
SSNES configuring is done through editing <tt>config.h</tt> and <tt>config.mk</tt>.
|
||||
The default configs can be found in <tt>config.h.def</tt> and <tt>config.mk.def</tt> respectively.
|
||||
Do note that you might have to edit <tt>config.mk</tt> if you edit driver and filter options!
|
||||
By default, ALSA audio driver is assumed.
|
||||
The default configuration is defined in config.def.h.
|
||||
These can later be tweaked by using the ssnes config file.
|
||||
A sample configuration file is installed to /etc/ssnes.cfg.
|
||||
This is the system-wide config file.
|
||||
Each user should create a config file in $XDG\_CONFIG\_HOME/ssnes/ssnes.cfg.
|
||||
The users only need to configure a certain option if the desired value deviates from the value defined in config.def.h.
|
||||
|
||||
Most options in <tt>config.h</tt> should be self-explanatory.
|
||||
To configure joypads, start up <tt>jstest /dev/input/js0</tt> to determine which joypad buttons (and axis) to use.
|
||||
|
||||
# Compiling and installing
|
||||
|
||||
The good old <tt>make && sudo make install</tt> should do the trick :)
|
||||
As most packages, SSNES is built using the standard <tt>./configure && make && make install</tt>
|
||||
Do note that the build system is not autotools based, but resembles it.
|
||||
|
||||
Notable options for ./configure:
|
||||
--with-libsnes=: Normally libsnes is located with -lsnes, however, this can be overridden.
|
||||
--enable-dynamic: Do not link to libsnes at compile time, but load libsnes dynamically at runtime. libsnes\_path in config file defines which library to load. Useful for development.
|
||||
|
||||
Do note that these two options are mutually exclusive.
|
||||
|
||||
|
||||
|
||||
# Filters and Cg shader support
|
||||
@ -67,3 +75,5 @@ Cg shaders are compiled at run-time, and shaders could be dropped in.
|
||||
All shaders share a common interface to pass some essential arguments such as texture size and viewport size. (Common for pixel art scalers)
|
||||
Some Cg shaders are included in hqflt/cg/ and could be used as an example.
|
||||
|
||||
While these shaders are Cg, they closely resemble the GLSL shaders found in bSNES shader pack, so porting them is trivial.
|
||||
|
||||
|
@ -190,7 +190,8 @@ const audio_driver_t audio_alsa = {
|
||||
.stop = __alsa_stop,
|
||||
.start = __alsa_start,
|
||||
.set_nonblock_state = __alsa_set_nonblock_state,
|
||||
.free = __alsa_free
|
||||
.free = __alsa_free,
|
||||
.ident = "alsa"
|
||||
};
|
||||
|
||||
|
||||
|
300
audio/jack.c
Normal file
300
audio/jack.c
Normal file
@ -0,0 +1,300 @@
|
||||
/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "driver.h"
|
||||
#include <stdlib.h>
|
||||
#include "general.h"
|
||||
|
||||
#include <jack/jack.h>
|
||||
#include <jack/types.h>
|
||||
#include <jack/ringbuffer.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define FRAMES(x) (x / (sizeof(int16_t) * 2))
|
||||
#define SAMPLES(x) (x / sizeof(int16_t))
|
||||
|
||||
typedef struct jack
|
||||
{
|
||||
jack_client_t *client;
|
||||
jack_port_t *ports[2];
|
||||
jack_ringbuffer_t *buffer[2];
|
||||
volatile bool shutdown;
|
||||
bool nonblock;
|
||||
|
||||
pthread_cond_t cond;
|
||||
pthread_mutex_t cond_lock;
|
||||
} jack_t;
|
||||
|
||||
static int process_cb(jack_nframes_t nframes, void *data)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
if (nframes <= 0)
|
||||
{
|
||||
pthread_cond_signal(&jd->cond);
|
||||
return 0;
|
||||
}
|
||||
|
||||
jack_nframes_t avail[2];
|
||||
avail[0] = jack_ringbuffer_read_space(jd->buffer[0]);
|
||||
avail[1] = jack_ringbuffer_read_space(jd->buffer[1]);
|
||||
jack_nframes_t min_avail = ((avail[0] < avail[1]) ? avail[0] : avail[1]) / sizeof(jack_default_audio_sample_t);
|
||||
|
||||
if (min_avail > nframes)
|
||||
min_avail = nframes;
|
||||
|
||||
//static int underrun = 0;
|
||||
//if (min_avail < nframes)
|
||||
//{
|
||||
// SSNES_LOG("JACK: Underrun count: %d\n", underrun++);
|
||||
// fprintf(stderr, "required %d frames, got %d.\n", (int)nframes, (int)min_avail);
|
||||
//}
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
jack_default_audio_sample_t *out = jack_port_get_buffer(jd->ports[i], nframes);
|
||||
assert(out);
|
||||
jack_ringbuffer_read(jd->buffer[i], (char*)out, min_avail * sizeof(jack_default_audio_sample_t));
|
||||
|
||||
for (jack_nframes_t f = min_avail; f < nframes; f++)
|
||||
{
|
||||
out[f] = 0.0f;
|
||||
}
|
||||
}
|
||||
pthread_cond_signal(&jd->cond);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void shutdown_cb(void *data)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
jd->shutdown = true;
|
||||
pthread_cond_signal(&jd->cond);
|
||||
}
|
||||
|
||||
static inline void s16_to_float(jack_default_audio_sample_t * restrict out, const int16_t * restrict in, size_t samples)
|
||||
{
|
||||
for (int i = 0; i < samples; i++)
|
||||
out[i] = (float)in[i] / 0x8000;
|
||||
}
|
||||
|
||||
static void parse_ports(const char **dest_ports, const char **jports)
|
||||
{
|
||||
int parsed = 0;
|
||||
|
||||
const char *con = strtok(g_settings.audio.device, ",");
|
||||
if (con)
|
||||
dest_ports[parsed++] = con;
|
||||
con = strtok(NULL, ",");
|
||||
if (con)
|
||||
dest_ports[parsed++] = con;
|
||||
|
||||
for (int i = parsed; i < 2; i++)
|
||||
dest_ports[i] = jports[i];
|
||||
}
|
||||
|
||||
static void* __jack_init(const char* device, int rate, int latency)
|
||||
{
|
||||
jack_t *jd = calloc(1, sizeof(jack_t));
|
||||
if ( jd == NULL )
|
||||
return NULL;
|
||||
|
||||
const char **jports = NULL;
|
||||
|
||||
jd->client = jack_client_open("SSNES", JackNullOption, NULL);
|
||||
if (jd->client == NULL)
|
||||
goto error;
|
||||
|
||||
g_settings.audio.out_rate = jack_get_sample_rate(jd->client);
|
||||
|
||||
jack_set_process_callback(jd->client, process_cb, jd);
|
||||
jack_on_shutdown(jd->client, shutdown_cb, jd);
|
||||
|
||||
jd->ports[0] = jack_port_register(jd->client, "left", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
|
||||
jd->ports[1] = jack_port_register(jd->client, "right", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
|
||||
if (jd->ports[0] == NULL || jd->ports[1] == NULL)
|
||||
{
|
||||
SSNES_ERR("Failed to register ports.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
jack_nframes_t bufsize;
|
||||
jack_nframes_t jack_bufsize = jack_get_buffer_size(jd->client);
|
||||
|
||||
bufsize = (latency * g_settings.audio.out_rate / 1000) > jack_bufsize * 2 ? (latency * g_settings.audio.out_rate / 1000) : jack_bufsize * 2;
|
||||
bufsize *= sizeof(jack_default_audio_sample_t);
|
||||
|
||||
//fprintf(stderr, "jack buffer size: %d\n", (int)bufsize);
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
jd->buffer[i] = jack_ringbuffer_create(bufsize);
|
||||
if (jd->buffer[i] == NULL)
|
||||
{
|
||||
SSNES_ERR("Failed to create buffers.\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
const char *dest_ports[2];
|
||||
jports = jack_get_ports(jd->client, NULL, NULL, JackPortIsPhysical | JackPortIsInput);
|
||||
if (jports == NULL)
|
||||
{
|
||||
SSNES_ERR("Failed to get ports.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
parse_ports(dest_ports, jports);
|
||||
|
||||
if (jack_activate(jd->client) < 0)
|
||||
{
|
||||
SSNES_ERR("Failed to activate Jack...\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (jack_connect(jd->client, jack_port_name(jd->ports[i]), dest_ports[i]))
|
||||
{
|
||||
SSNES_ERR("Failed to connect to Jack port.\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_cond_init(&jd->cond, NULL);
|
||||
pthread_mutex_init(&jd->cond_lock, NULL);
|
||||
|
||||
|
||||
jack_free(jports);
|
||||
return jd;
|
||||
|
||||
error:
|
||||
if (jports != NULL)
|
||||
jack_free(jports);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static size_t write_buffer(jack_t *jd, const void *buf, size_t size)
|
||||
{
|
||||
//fprintf(stderr, "write_buffer: size: %zu\n", size);
|
||||
// Convert our data to float, deinterleave and write.
|
||||
jack_default_audio_sample_t out_buffer[size / sizeof(int16_t)];
|
||||
jack_default_audio_sample_t out_deinterleaved_buffer[2][FRAMES(size)];
|
||||
s16_to_float(out_buffer, buf, SAMPLES(size));
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
for (size_t j = 0; j < FRAMES(size); j++)
|
||||
out_deinterleaved_buffer[i][j] = out_buffer[j * 2 + i];
|
||||
|
||||
for(;;)
|
||||
{
|
||||
if (jd->shutdown)
|
||||
return 0;
|
||||
|
||||
size_t avail[2];
|
||||
avail[0] = jack_ringbuffer_write_space(jd->buffer[0]);
|
||||
avail[1] = jack_ringbuffer_write_space(jd->buffer[1]);
|
||||
size_t min_avail = avail[0] < avail[1] ? avail[0] : avail[1];
|
||||
|
||||
if (jd->nonblock)
|
||||
{
|
||||
if (min_avail < FRAMES(size) * sizeof(jack_default_audio_sample_t))
|
||||
size = min_avail * 2 * sizeof(int16_t) / sizeof(jack_default_audio_sample_t);
|
||||
break;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
//fprintf(stderr, "Write avail is: %d\n", (int)min_avail);
|
||||
if (min_avail >= FRAMES(size) * sizeof(jack_default_audio_sample_t))
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&jd->cond_lock);
|
||||
pthread_cond_wait(&jd->cond, &jd->cond_lock);
|
||||
pthread_mutex_unlock(&jd->cond_lock);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
jack_ringbuffer_write(jd->buffer[i], (const char*)out_deinterleaved_buffer[i], FRAMES(size) * sizeof(jack_default_audio_sample_t));
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t __jack_write(void* data, const void* buf, size_t size)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
|
||||
return write_buffer(jd, buf, size);
|
||||
}
|
||||
|
||||
static bool __jack_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __jack_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
jd->nonblock = state;
|
||||
}
|
||||
|
||||
static bool __jack_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __jack_free(void *data)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
|
||||
jd->shutdown = true;
|
||||
|
||||
if (jd->client != NULL)
|
||||
{
|
||||
jack_deactivate(jd->client);
|
||||
jack_client_close(jd->client);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
if (jd->buffer[i] != NULL)
|
||||
jack_ringbuffer_free(jd->buffer[i]);
|
||||
|
||||
pthread_mutex_destroy(&jd->cond_lock);
|
||||
pthread_cond_destroy(&jd->cond);
|
||||
free(jd);
|
||||
}
|
||||
|
||||
const audio_driver_t audio_jack = {
|
||||
.init = __jack_init,
|
||||
.write = __jack_write,
|
||||
.stop = __jack_stop,
|
||||
.start = __jack_start,
|
||||
.set_nonblock_state = __jack_set_nonblock_state,
|
||||
.free = __jack_free,
|
||||
.ident = "jack"
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -233,7 +233,8 @@ const audio_driver_t audio_openal = {
|
||||
.stop = __al_stop,
|
||||
.start = __al_start,
|
||||
.set_nonblock_state = __al_set_nonblock_state,
|
||||
.free = __al_free
|
||||
.free = __al_free,
|
||||
.ident = "openal"
|
||||
};
|
||||
|
||||
|
||||
|
@ -136,7 +136,8 @@ const audio_driver_t audio_oss = {
|
||||
.stop = __oss_stop,
|
||||
.start = __oss_start,
|
||||
.set_nonblock_state = __oss_set_nonblock_state,
|
||||
.free = __oss_free
|
||||
.free = __oss_free,
|
||||
.ident = "oss"
|
||||
};
|
||||
|
||||
|
||||
|
@ -107,7 +107,8 @@ const audio_driver_t audio_roar = {
|
||||
.stop = __roar_stop,
|
||||
.start = __roar_start,
|
||||
.set_nonblock_state = __roar_set_nonblock_state,
|
||||
.free = __roar_free
|
||||
.free = __roar_free,
|
||||
.ident = "roar"
|
||||
};
|
||||
|
||||
|
||||
|
@ -135,7 +135,8 @@ const audio_driver_t audio_rsound = {
|
||||
.stop = __rsd_stop,
|
||||
.start = __rsd_start,
|
||||
.set_nonblock_state = __rsd_set_nonblock_state,
|
||||
.free = __rsd_free
|
||||
.free = __rsd_free,
|
||||
.ident = "rsound"
|
||||
};
|
||||
|
||||
|
||||
|
293
conf/config_file.c
Normal file
293
conf/config_file.c
Normal file
@ -0,0 +1,293 @@
|
||||
/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "config_file.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include "general.h"
|
||||
|
||||
struct entry_list
|
||||
{
|
||||
char *key;
|
||||
char *value;
|
||||
struct entry_list *next;
|
||||
};
|
||||
|
||||
struct config_file
|
||||
{
|
||||
struct entry_list *entries;
|
||||
};
|
||||
|
||||
static char *getaline(FILE *file)
|
||||
{
|
||||
char *newline = malloc(9);
|
||||
size_t cur_size = 8;
|
||||
size_t index = 0;
|
||||
|
||||
int in = getc(file);
|
||||
while (in != EOF && in != '\n')
|
||||
{
|
||||
if (index == cur_size)
|
||||
{
|
||||
cur_size *= 2;
|
||||
newline = realloc(newline, cur_size + 1);
|
||||
}
|
||||
|
||||
newline[index++] = in;
|
||||
in = getc(file);
|
||||
}
|
||||
newline[index] = '\0';
|
||||
return newline;
|
||||
}
|
||||
|
||||
static bool parse_line(struct entry_list *list, char *line)
|
||||
{
|
||||
// Remove everything after comment.
|
||||
char *comment = strchr(line, '#');
|
||||
if (comment)
|
||||
*comment = '\0';
|
||||
|
||||
// Skips to first character.
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
|
||||
char *key = malloc(9);
|
||||
size_t cur_size = 8;
|
||||
size_t index = 0;
|
||||
|
||||
while (isgraph(*line))
|
||||
{
|
||||
if (index == cur_size)
|
||||
{
|
||||
cur_size *= 2;
|
||||
key = realloc(key, cur_size + 1);
|
||||
}
|
||||
|
||||
key[index++] = *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->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;
|
||||
}
|
||||
|
||||
static void print_config(config_file_t *conf)
|
||||
{
|
||||
struct entry_list *tmp = conf->entries;
|
||||
while (tmp != NULL)
|
||||
{
|
||||
printf("Key: \"%s\", Value: \"%s\"\n", tmp->key, tmp->value);
|
||||
tmp = tmp->next;
|
||||
}
|
||||
}
|
||||
|
||||
config_file_t *config_file_new(const char *path)
|
||||
{
|
||||
FILE *file = fopen(path, "r");
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
struct config_file *conf = calloc(1, sizeof(*conf));
|
||||
if (conf == NULL)
|
||||
return NULL;
|
||||
|
||||
struct entry_list *tail = conf->entries;
|
||||
|
||||
while (!feof(file))
|
||||
{
|
||||
struct entry_list *list = calloc(1, sizeof(*list));
|
||||
char *line = getaline(file);
|
||||
|
||||
if (line)
|
||||
{
|
||||
if (parse_line(list, line))
|
||||
{
|
||||
if (conf->entries == NULL)
|
||||
{
|
||||
conf->entries = list;
|
||||
tail = list;
|
||||
}
|
||||
else
|
||||
{
|
||||
tail->next = list;
|
||||
tail = list;
|
||||
}
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
if (g_extern.verbose)
|
||||
print_config(conf);
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
void config_file_free(config_file_t *conf)
|
||||
{
|
||||
if (conf != NULL)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
bool config_get_double(config_file_t *conf, const char *key, double *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
*in = strtod(list->value, NULL);
|
||||
return true;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool config_get_int(config_file_t *conf, const char *key, int *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
*in = strtol(list->value, NULL, 0);
|
||||
return true;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool config_get_char(config_file_t *conf, const char *key, char *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
if (strlen(list->value) > 1)
|
||||
return false;
|
||||
*in = *list->value;
|
||||
return true;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool config_get_string(config_file_t *conf, const char *key, char **str)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
*str = strdup(list->value);
|
||||
return true;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool config_get_bool(config_file_t *conf, const char *key, bool *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
if (strcasecmp(list->value, "true") == 0)
|
||||
*in = true;
|
||||
else if (strcasecmp(list->value, "1") == 0)
|
||||
*in = true;
|
||||
else if (strcasecmp(list->value, "false") == 0)
|
||||
*in = false;
|
||||
else if (strcasecmp(list->value, "0") == 0)
|
||||
*in = false;
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
53
conf/config_file.h
Normal file
53
conf/config_file.h
Normal file
@ -0,0 +1,53 @@
|
||||
/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __CONFIG_FILE_H
|
||||
#define __CONFIG_FILE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct config_file config_file_t;
|
||||
|
||||
/////
|
||||
// Config file format
|
||||
// - # 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")
|
||||
|
||||
// Loads a config file. Returns NULL if file doesn't exist.
|
||||
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.
|
||||
|
||||
// Extracts a double from config file.
|
||||
bool config_get_double(config_file_t *conf, const char *entry, double *in);
|
||||
// Extracts an int from config file.
|
||||
bool config_get_int(config_file_t *conf, const char *entry, int *in);
|
||||
// Extracts a single char. If value consists of several chars, this is an error.
|
||||
bool config_get_char(config_file_t *conf, const char *entry, char *in);
|
||||
// Extracts an allocated string in *in. This must be free()-d if this function succeeds.
|
||||
bool config_get_string(config_file_t *conf, const char *entry, char **in);
|
||||
// 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);
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -37,11 +37,11 @@
|
||||
#define AUDIO_ALSA 3
|
||||
#define AUDIO_ROAR 4
|
||||
#define AUDIO_AL 5
|
||||
#define AUDIO_JACK 6
|
||||
////////////////////////
|
||||
|
||||
// Chooses which video and audio subsystem to use. Remember to update config.mk if you change these.
|
||||
#define VIDEO_DRIVER VIDEO_GL
|
||||
#define AUDIO_DRIVER AUDIO_ALSA
|
||||
#define VIDEO_DEFAULT_DRIVER VIDEO_GL
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_ALSA
|
||||
|
||||
|
||||
////////////////
|
||||
@ -53,7 +53,7 @@ static const float xscale = 3.0; // Real x res = 296 * xscale
|
||||
static const float yscale = 3.0; // Real y res = 224 * yscale
|
||||
|
||||
// Fullscreen
|
||||
#define START_FULLSCREEN false; // To start in Fullscreen or not
|
||||
static const bool fullscreen = false; // To start in Fullscreen or not
|
||||
static const unsigned fullscreen_x = 1280;
|
||||
static const unsigned fullscreen_y = 720;
|
||||
|
||||
@ -63,28 +63,9 @@ static const bool vsync = true;
|
||||
// Smooths picture
|
||||
static const bool video_smooth = true;
|
||||
|
||||
// Path to custom Cg shader. If using custom shaders, it is recommended to disable video_smooth.
|
||||
#ifdef HAVE_CG
|
||||
extern char cg_shader_path[];
|
||||
#define DEFAULT_CG_SHADER "hqflt/cg/quad.cg"
|
||||
#endif
|
||||
|
||||
// On resize and fullscreen, rendering area will stay 4:3
|
||||
static const bool force_aspect = true;
|
||||
|
||||
/////////// Video filters (CPU based)
|
||||
#define FILTER_NONE 0
|
||||
#define FILTER_HQ2X 1
|
||||
#define FILTER_HQ4X 2
|
||||
#define FILTER_GRAYSCALE 3
|
||||
#define FILTER_BLEED 4
|
||||
#define FILTER_NTSC 5
|
||||
////////////////////////
|
||||
|
||||
// If you change this to something other than FILTER_NONE, make sure that you build the filter module in config.mk.
|
||||
#define VIDEO_FILTER FILTER_NONE
|
||||
|
||||
|
||||
////////////////
|
||||
// Audio
|
||||
////////////////
|
||||
@ -124,8 +105,6 @@ static const bool audio_sync = true;
|
||||
|
||||
#define AXIS_NEG(x) ((uint32_t)(x << 16) | 0xFFFF)
|
||||
#define AXIS_POS(x) ((uint32_t)(x) | 0xFFFF0000U)
|
||||
#define AXIS_NEG_GET(x) ((x >> 16) & 0xFFFF)
|
||||
#define AXIS_POS_GET(x) (x & 0xFFFF)
|
||||
#define AXIS_NONE ((uint32_t)0xFFFFFFFFU)
|
||||
|
||||
// To figure out which joypad buttons to use, check jstest or similar.
|
@ -1,13 +0,0 @@
|
||||
|
||||
BUILD_OPENGL = 1
|
||||
BUILD_CG = 0
|
||||
BUILD_FILTER = 0
|
||||
|
||||
BUILD_RSOUND = 0
|
||||
BUILD_OSS = 0
|
||||
BUILD_ALSA = 1
|
||||
BUILD_ROAR = 0
|
||||
BUILD_AL = 0
|
||||
|
||||
|
||||
PREFIX = /usr/local
|
13
configure
vendored
Executable file
13
configure
vendored
Executable file
@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo ""
|
||||
|
||||
. qb/config.params.sh
|
||||
|
||||
parse_input "$@"
|
||||
|
||||
. qb/qb.comp.sh
|
||||
. qb/config.libs.sh
|
||||
|
||||
|
||||
|
151
driver.c
151
driver.c
@ -17,9 +17,74 @@
|
||||
|
||||
|
||||
#include "driver.h"
|
||||
#include "config.h"
|
||||
#include "general.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "hqflt/filters.h"
|
||||
#include "config.h"
|
||||
|
||||
static const audio_driver_t *audio_drivers[] = {
|
||||
#ifdef HAVE_ALSA
|
||||
&audio_alsa,
|
||||
#endif
|
||||
#ifdef HAVE_OSS
|
||||
&audio_oss,
|
||||
#endif
|
||||
#ifdef HAVE_RSOUND
|
||||
&audio_rsound,
|
||||
#endif
|
||||
#ifdef HAVE_AL
|
||||
&audio_openal,
|
||||
#endif
|
||||
#ifdef HAVE_ROAR
|
||||
&audio_roar,
|
||||
#endif
|
||||
#ifdef HAVE_JACK
|
||||
&audio_jack,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const video_driver_t *video_drivers[] = {
|
||||
#ifdef HAVE_GLFW
|
||||
&video_gl,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void find_audio_driver(void)
|
||||
{
|
||||
for (int i = 0; i < sizeof(audio_drivers) / sizeof(audio_driver_t*); i++)
|
||||
{
|
||||
if (strcasecmp(g_settings.audio.driver, audio_drivers[i]->ident) == 0)
|
||||
{
|
||||
driver.audio = audio_drivers[i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
SSNES_ERR("Couldn't find any audio driver named \"%s\"\n", g_settings.audio.driver);
|
||||
fprintf(stderr, "Available audio drivers are:\n");
|
||||
for (int i = 0; i < sizeof(audio_drivers) / sizeof(audio_driver_t*); i++)
|
||||
fprintf(stderr, "\t%s\n", audio_drivers[i]->ident);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void find_video_driver(void)
|
||||
{
|
||||
for (int i = 0; i < sizeof(video_drivers) / sizeof(video_driver_t*); i++)
|
||||
{
|
||||
if (strcasecmp(g_settings.video.driver, video_drivers[i]->ident) == 0)
|
||||
{
|
||||
driver.video = video_drivers[i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
SSNES_ERR("Couldn't find any video driver named \"%s\"\n", g_settings.video.driver);
|
||||
fprintf(stderr, "Available video drivers are:\n");
|
||||
for (int i = 0; i < sizeof(video_drivers) / sizeof(video_driver_t*); i++)
|
||||
fprintf(stderr, "\t%s\n", video_drivers[i]->ident);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void init_drivers(void)
|
||||
{
|
||||
@ -35,73 +100,76 @@ void uninit_drivers(void)
|
||||
|
||||
void init_audio(void)
|
||||
{
|
||||
if (!audio_enable)
|
||||
if (!g_settings.audio.enable)
|
||||
{
|
||||
audio_active = false;
|
||||
g_extern.audio_active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
driver.audio_data = driver.audio->init(audio_device, out_rate, out_latency);
|
||||
if ( driver.audio_data == NULL )
|
||||
audio_active = false;
|
||||
find_audio_driver();
|
||||
|
||||
if (!audio_sync && audio_active)
|
||||
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 )
|
||||
g_extern.audio_active = false;
|
||||
|
||||
if (!g_settings.audio.sync && g_extern.audio_active)
|
||||
driver.audio->set_nonblock_state(driver.audio_data, true);
|
||||
|
||||
int err;
|
||||
source = src_new(SAMPLERATE_QUALITY, 2, &err);
|
||||
if (!source)
|
||||
audio_active = false;
|
||||
g_extern.source = src_new(g_settings.audio.src_quality, 2, &err);
|
||||
if (!g_extern.source)
|
||||
g_extern.audio_active = false;
|
||||
}
|
||||
|
||||
void uninit_audio(void)
|
||||
{
|
||||
if (!audio_enable)
|
||||
if (!g_settings.audio.enable)
|
||||
{
|
||||
audio_active = false;
|
||||
g_extern.audio_active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( driver.audio_data && driver.audio )
|
||||
driver.audio->free(driver.audio_data);
|
||||
|
||||
if ( source )
|
||||
src_delete(source);
|
||||
if ( g_extern.source )
|
||||
src_delete(g_extern.source);
|
||||
}
|
||||
|
||||
void init_video_input(void)
|
||||
{
|
||||
int scale;
|
||||
int scale = 2;
|
||||
|
||||
find_video_driver();
|
||||
|
||||
// We multiply scales with 2 to allow for hi-res games.
|
||||
#if VIDEO_FILTER == FILTER_NONE
|
||||
scale = 2;
|
||||
#elif VIDEO_FILTER == FILTER_HQ2X
|
||||
scale = 4;
|
||||
#elif VIDEO_FILTER == FILTER_HQ4X
|
||||
scale = 8;
|
||||
#elif VIDEO_FILTER == FILTER_NTSC
|
||||
scale = 8;
|
||||
#elif VIDEO_FILTER == FILTER_GRAYSCALE
|
||||
scale = 2;
|
||||
#elif VIDEO_FILTER == FILTER_BLEED
|
||||
scale = 2;
|
||||
#else
|
||||
scale = 2;
|
||||
#if HAVE_FILTER
|
||||
switch (g_settings.video.filter)
|
||||
{
|
||||
case FILTER_HQ2X:
|
||||
scale = 4;
|
||||
break;
|
||||
case FILTER_HQ4X:
|
||||
case FILTER_NTSC:
|
||||
scale = 8;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
video_info_t video = {
|
||||
.width = (fullscreen) ? fullscreen_x : (296 * xscale),
|
||||
.height = (fullscreen) ? fullscreen_y : (224 * yscale),
|
||||
.fullscreen = fullscreen,
|
||||
.vsync = vsync,
|
||||
.force_aspect = force_aspect,
|
||||
.smooth = video_smooth,
|
||||
.width = (g_settings.video.fullscreen) ? g_settings.video.fullscreen_x : (296 * g_settings.video.xscale),
|
||||
.height = (g_settings.video.fullscreen) ? g_settings.video.fullscreen_y : (224 * g_settings.video.yscale),
|
||||
.fullscreen = g_settings.video.fullscreen,
|
||||
.vsync = g_settings.video.vsync,
|
||||
.force_aspect = g_settings.video.force_aspect,
|
||||
.smooth = g_settings.video.smooth,
|
||||
.input_scale = scale,
|
||||
};
|
||||
|
||||
const input_driver_t *tmp = driver.input;
|
||||
driver.video_data = driver.video->init(&video, &(driver.input));
|
||||
driver.video_data = driver.video->init(&video, &driver.input);
|
||||
|
||||
if ( driver.video_data == NULL )
|
||||
{
|
||||
@ -139,9 +207,9 @@ void uninit_video_input(void)
|
||||
driver.input->free(driver.input_data);
|
||||
}
|
||||
|
||||
bool video_active = true;
|
||||
bool audio_active = true;
|
||||
driver_t driver;
|
||||
|
||||
#if 0
|
||||
driver_t driver = {
|
||||
#if VIDEO_DRIVER == VIDEO_GL
|
||||
.video = &video_gl,
|
||||
@ -149,12 +217,12 @@ driver_t driver = {
|
||||
#error "Define a valid video driver in config.h"
|
||||
#endif
|
||||
|
||||
#if AUDIO_DRIVER == AUDIO_RSOUND
|
||||
#if AUDIO_DRIVER == AUDIO_ALSA
|
||||
.audio = &audio_alsa,
|
||||
#elif AUDIO_DRIVER == AUDIO_RSOUND
|
||||
.audio = &audio_rsound,
|
||||
#elif AUDIO_DRIVER == AUDIO_OSS
|
||||
.audio = &audio_oss,
|
||||
#elif AUDIO_DRIVER == AUDIO_ALSA
|
||||
.audio = &audio_alsa,
|
||||
#elif AUDIO_DRIVER == AUDIO_ROAR
|
||||
.audio = &audio_roar,
|
||||
#elif AUDIO_DRIVER == AUDIO_AL
|
||||
@ -163,4 +231,5 @@ driver_t driver = {
|
||||
#error "Define a valid audio driver in config.h"
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
|
9
driver.h
9
driver.h
@ -54,14 +54,19 @@ typedef struct audio_driver
|
||||
bool (*start)(void* data);
|
||||
void (*set_nonblock_state)(void* data, bool toggle); // Should we care about blocking in audio thread? Fast forwarding.
|
||||
void (*free)(void* data);
|
||||
const char *ident;
|
||||
} audio_driver_t;
|
||||
|
||||
#define AXIS_NEG_GET(x) ((x >> 16) & 0xFFFF)
|
||||
#define AXIS_POS_GET(x) (x & 0xFFFF)
|
||||
#define AXIS_NONE ((uint32_t)0xFFFFFFFFU)
|
||||
typedef struct input_driver
|
||||
{
|
||||
void* (*init)(void);
|
||||
void (*poll)(void* data);
|
||||
int16_t (*input_state)(void* data, const struct snes_keybind **snes_keybinds, bool port, unsigned device, unsigned index, unsigned id);
|
||||
void (*free)(void* data);
|
||||
const char *ident;
|
||||
} input_driver_t;
|
||||
|
||||
typedef struct video_driver
|
||||
@ -71,6 +76,7 @@ typedef struct video_driver
|
||||
bool (*frame)(void* data, const uint16_t* frame, int width, int height, int pitch);
|
||||
void (*set_nonblock_state)(void* data, bool toggle); // Should we care about syncing to vblank? Fast forwarding.
|
||||
void (*free)(void* data);
|
||||
const char *ident;
|
||||
} video_driver_t;
|
||||
|
||||
|
||||
@ -92,8 +98,6 @@ void uninit_video_input(void);
|
||||
void init_audio(void);
|
||||
void uninit_audio(void);
|
||||
|
||||
extern bool video_active;
|
||||
extern bool audio_active;
|
||||
extern driver_t driver;
|
||||
|
||||
//////////////////////////////////////////////// Backends
|
||||
@ -102,6 +106,7 @@ extern const audio_driver_t audio_oss;
|
||||
extern const audio_driver_t audio_alsa;
|
||||
extern const audio_driver_t audio_roar;
|
||||
extern const audio_driver_t audio_openal;
|
||||
extern const audio_driver_t audio_jack;
|
||||
extern const video_driver_t video_gl;
|
||||
////////////////////////////////////////////////
|
||||
|
||||
|
142
dynamic.c
Normal file
142
dynamic.c
Normal file
@ -0,0 +1,142 @@
|
||||
/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dynamic.h"
|
||||
#include "general.h"
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_DYNAMIC
|
||||
#include <dlfcn.h>
|
||||
|
||||
#define SYM(x) do { \
|
||||
p##x = dlsym(lib_handle, #x); \
|
||||
if (p##x == NULL) { SSNES_ERR("Failed to load symbol: \"%s\"\n", #x); exit(1); } \
|
||||
} while(0)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DYNAMIC
|
||||
static void *lib_handle = NULL;
|
||||
#endif
|
||||
|
||||
void (*psnes_init)(void);
|
||||
|
||||
void (*psnes_set_video_refresh)(snes_video_refresh_t);
|
||||
void (*psnes_set_audio_sample)(snes_audio_sample_t);
|
||||
void (*psnes_set_input_poll)(snes_input_poll_t);
|
||||
void (*psnes_set_input_state)(snes_input_state_t);
|
||||
|
||||
void (*psnes_run)(void);
|
||||
|
||||
unsigned (*psnes_library_revision_minor)(void);
|
||||
unsigned (*psnes_library_revision_major)(void);
|
||||
|
||||
bool (*psnes_load_cartridge_normal)(const char*, const uint8_t*, unsigned);
|
||||
|
||||
unsigned (*psnes_serialize_size)(void);
|
||||
bool (*psnes_serialize)(uint8_t*, unsigned);
|
||||
bool (*psnes_unserialize)(const uint8_t*, unsigned);
|
||||
|
||||
void (*psnes_set_cartridge_basename)(const char*);
|
||||
|
||||
uint8_t* (*psnes_get_memory_data)(unsigned);
|
||||
unsigned (*psnes_get_memory_size)(unsigned);
|
||||
|
||||
void (*psnes_unload_cartridge)(void);
|
||||
void (*psnes_term)(void);
|
||||
|
||||
#ifdef HAVE_DYNAMIC
|
||||
static void load_dynamic(void)
|
||||
{
|
||||
SSNES_LOG("Loading dynamic libsnes from: \"%s\"\n", g_settings.libsnes);
|
||||
lib_handle = dlopen(g_settings.libsnes, RTLD_LAZY);
|
||||
if (!lib_handle)
|
||||
{
|
||||
SSNES_ERR("Failed to open dynamic library: \"%s\"\n", g_settings.libsnes);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
SYM(snes_init);
|
||||
SYM(snes_set_video_refresh);
|
||||
SYM(snes_set_audio_sample);
|
||||
SYM(snes_set_input_poll);
|
||||
SYM(snes_set_input_state);
|
||||
SYM(snes_library_revision_minor);
|
||||
SYM(snes_library_revision_major);
|
||||
SYM(snes_run);
|
||||
SYM(snes_load_cartridge_normal);
|
||||
SYM(snes_serialize_size);
|
||||
SYM(snes_serialize);
|
||||
SYM(snes_unserialize);
|
||||
SYM(snes_set_cartridge_basename);
|
||||
SYM(snes_get_memory_data);
|
||||
SYM(snes_get_memory_size);
|
||||
SYM(snes_unload_cartridge);
|
||||
SYM(snes_term);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SSYM(x) do { \
|
||||
p##x = x; \
|
||||
} while(0)
|
||||
|
||||
#ifndef HAVE_DYNAMIC
|
||||
static void set_statics(void)
|
||||
{
|
||||
SSYM(snes_init);
|
||||
SSYM(snes_set_video_refresh);
|
||||
SSYM(snes_set_audio_sample);
|
||||
SSYM(snes_set_input_poll);
|
||||
SSYM(snes_set_input_state);
|
||||
SSYM(snes_library_revision_minor);
|
||||
SSYM(snes_library_revision_major);
|
||||
SSYM(snes_run);
|
||||
SSYM(snes_load_cartridge_normal);
|
||||
SSYM(snes_serialize_size);
|
||||
SSYM(snes_serialize);
|
||||
SSYM(snes_unserialize);
|
||||
SSYM(snes_set_cartridge_basename);
|
||||
SSYM(snes_get_memory_data);
|
||||
SSYM(snes_get_memory_size);
|
||||
SSYM(snes_unload_cartridge);
|
||||
SSYM(snes_term);
|
||||
}
|
||||
#endif
|
||||
|
||||
void init_dlsym(void)
|
||||
{
|
||||
#ifdef HAVE_DYNAMIC
|
||||
if (strlen(g_settings.libsnes) > 0)
|
||||
load_dynamic();
|
||||
else
|
||||
{
|
||||
SSNES_ERR("This binary is built to use runtime dynamic binding of libsnes. Set libsnes_path in config to load a libsnes library dynamically.\n");
|
||||
exit(1);
|
||||
}
|
||||
#else
|
||||
set_statics();
|
||||
#endif
|
||||
}
|
||||
|
||||
void uninit_dlsym(void)
|
||||
{
|
||||
#ifdef HAVE_DYNAMIC
|
||||
if (lib_handle)
|
||||
dlclose(lib_handle);
|
||||
#endif
|
||||
}
|
54
dynamic.h
Normal file
54
dynamic.h
Normal file
@ -0,0 +1,54 @@
|
||||
/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __DYNAMIC_H
|
||||
#define __DYNAMIC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <libsnes.hpp>
|
||||
|
||||
void init_dlsym(void);
|
||||
void uninit_dlsym(void);
|
||||
|
||||
extern void (*psnes_init)(void);
|
||||
|
||||
extern void (*psnes_set_video_refresh)(snes_video_refresh_t);
|
||||
extern void (*psnes_set_audio_sample)(snes_audio_sample_t);
|
||||
extern void (*psnes_set_input_poll)(snes_input_poll_t);
|
||||
extern void (*psnes_set_input_state)(snes_input_state_t);
|
||||
|
||||
extern unsigned (*psnes_library_revision_minor)(void);
|
||||
extern unsigned (*psnes_library_revision_major)(void);
|
||||
|
||||
extern bool (*psnes_load_cartridge_normal)(const char*, const uint8_t*, unsigned);
|
||||
|
||||
extern unsigned (*psnes_serialize_size)(void);
|
||||
extern bool (*psnes_serialize)(uint8_t*, unsigned);
|
||||
extern bool (*psnes_unserialize)(const uint8_t*, unsigned);
|
||||
|
||||
extern void (*psnes_run)(void);
|
||||
|
||||
extern void (*psnes_set_cartridge_basename)(const char*);
|
||||
|
||||
extern uint8_t* (*psnes_get_memory_data)(unsigned);
|
||||
extern unsigned (*psnes_get_memory_size)(unsigned);
|
||||
|
||||
extern void (*psnes_unload_cartridge)(void);
|
||||
extern void (*psnes_term)(void);
|
||||
|
||||
#endif
|
||||
|
13
file.c
13
file.c
@ -21,6 +21,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <libsnes.hpp>
|
||||
#include <string.h>
|
||||
#include "dynamic.h"
|
||||
|
||||
ssize_t read_file(FILE* file, void** buf)
|
||||
{
|
||||
@ -101,7 +102,7 @@ void write_file(const char* path, uint8_t* data, size_t size)
|
||||
if ( file != NULL )
|
||||
{
|
||||
SSNES_LOG("Saving state \"%s\". Size: %d bytes.\n", path, (int)size);
|
||||
snes_serialize(data, size);
|
||||
psnes_serialize(data, size);
|
||||
if ( fwrite(data, 1, size, file) != size )
|
||||
SSNES_ERR("Did not save state properly.\n");
|
||||
fclose(file);
|
||||
@ -118,7 +119,7 @@ void load_state(const char* path, uint8_t* data, size_t size)
|
||||
if ( fread(data, 1, size, file) != size )
|
||||
SSNES_ERR("Did not load state properly.\n");
|
||||
fclose(file);
|
||||
snes_unserialize(data, size);
|
||||
psnes_unserialize(data, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -136,8 +137,8 @@ void load_save_file(const char* path, int type)
|
||||
return;
|
||||
}
|
||||
|
||||
size_t size = snes_get_memory_size(type);
|
||||
uint8_t *data = snes_get_memory_data(type);
|
||||
size_t size = psnes_get_memory_size(type);
|
||||
uint8_t *data = psnes_get_memory_data(type);
|
||||
|
||||
if (size == 0 || !data)
|
||||
{
|
||||
@ -158,8 +159,8 @@ void load_save_file(const char* path, int type)
|
||||
|
||||
void save_file(const char* path, int type)
|
||||
{
|
||||
size_t size = snes_get_memory_size(type);
|
||||
uint8_t *data = snes_get_memory_data(type);
|
||||
size_t size = psnes_get_memory_size(type);
|
||||
uint8_t *data = psnes_get_memory_data(type);
|
||||
|
||||
if ( data && size > 0 )
|
||||
write_file(path, data, size);
|
||||
|
72
general.h
72
general.h
@ -21,9 +21,75 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <samplerate.h>
|
||||
#include "driver.h"
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#define MAX_PLAYERS 2
|
||||
#define MAX_BINDS 14
|
||||
struct settings
|
||||
{
|
||||
struct
|
||||
{
|
||||
char driver[32];
|
||||
float xscale;
|
||||
float yscale;
|
||||
bool fullscreen;
|
||||
unsigned fullscreen_x;
|
||||
unsigned fullscreen_y;
|
||||
bool vsync;
|
||||
bool smooth;
|
||||
bool force_aspect;
|
||||
char cg_shader_path[256];
|
||||
unsigned filter;
|
||||
} video;
|
||||
|
||||
struct
|
||||
{
|
||||
char driver[32];
|
||||
bool enable;
|
||||
unsigned out_rate;
|
||||
unsigned in_rate;
|
||||
char device[256];
|
||||
unsigned latency;
|
||||
bool sync;
|
||||
int src_quality;
|
||||
} audio;
|
||||
|
||||
struct
|
||||
{
|
||||
char driver[32];
|
||||
struct snes_keybind binds[MAX_PLAYERS][MAX_BINDS];
|
||||
int save_state_key;
|
||||
int load_state_key;
|
||||
int toggle_fullscreen_key;
|
||||
int exit_emulator_key;
|
||||
float axis_threshold;
|
||||
} input;
|
||||
|
||||
char libsnes[256];
|
||||
};
|
||||
|
||||
struct global
|
||||
{
|
||||
bool verbose;
|
||||
SRC_STATE *source;
|
||||
bool audio_active;
|
||||
bool video_active;
|
||||
|
||||
FILE *rom_file;
|
||||
char savefile_name_srm[256];
|
||||
char config_path[256];
|
||||
char basename[256];
|
||||
};
|
||||
|
||||
void parse_config(void);
|
||||
|
||||
extern struct settings g_settings;
|
||||
extern struct global g_extern;
|
||||
|
||||
#define SSNES_LOG(msg, args...) do { \
|
||||
if (verbose) \
|
||||
if (g_extern.verbose) \
|
||||
fprintf(stderr, "SSNES: " msg, ##args); \
|
||||
} while(0)
|
||||
|
||||
@ -31,8 +97,4 @@
|
||||
fprintf(stderr, "SSNES [ERROR] :: " msg, ##args); \
|
||||
} while(0)
|
||||
|
||||
extern bool verbose;
|
||||
extern SRC_STATE *source;
|
||||
extern bool fullscreen;
|
||||
|
||||
#endif
|
||||
|
161
gfx/gl.c
161
gfx/gl.c
@ -18,13 +18,15 @@
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
|
||||
#include "driver.h"
|
||||
#include "config.h"
|
||||
#include <GL/glfw.h>
|
||||
#include <GL/glext.h>
|
||||
#include <stdint.h>
|
||||
#include "libsnes.hpp"
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <string.h>
|
||||
#include "general.h"
|
||||
#include "config.h"
|
||||
|
||||
|
||||
#ifdef HAVE_CG
|
||||
@ -49,6 +51,7 @@ static const GLfloat tex_coords[] = {
|
||||
static bool keep_aspect = true;
|
||||
#ifdef HAVE_CG
|
||||
static CGparameter cg_mvp_matrix;
|
||||
static bool cg_active = false;
|
||||
#endif
|
||||
static GLuint gl_width = 0, gl_height = 0;
|
||||
typedef struct gl
|
||||
@ -65,6 +68,11 @@ typedef struct gl
|
||||
#endif
|
||||
GLuint texture;
|
||||
GLuint tex_filter;
|
||||
|
||||
unsigned last_width;
|
||||
unsigned last_height;
|
||||
unsigned tex_w, tex_h;
|
||||
GLfloat tex_coords[8];
|
||||
} gl_t;
|
||||
|
||||
|
||||
@ -116,9 +124,9 @@ static bool glfw_is_pressed(int port_num, const struct snes_keybind *key, unsign
|
||||
|
||||
if (key->joyaxis != AXIS_NONE)
|
||||
{
|
||||
if (AXIS_NEG_GET(key->joyaxis) < joypad_axes[port_num] && axes[AXIS_NEG_GET(key->joyaxis)] <= -AXIS_THRESHOLD)
|
||||
if (AXIS_NEG_GET(key->joyaxis) < joypad_axes[port_num] && axes[AXIS_NEG_GET(key->joyaxis)] <= -g_settings.input.axis_threshold)
|
||||
return true;
|
||||
if (AXIS_POS_GET(key->joyaxis) < joypad_axes[port_num] && axes[AXIS_POS_GET(key->joyaxis)] >= AXIS_THRESHOLD)
|
||||
if (AXIS_POS_GET(key->joyaxis) < joypad_axes[port_num] && axes[AXIS_POS_GET(key->joyaxis)] >= g_settings.input.axis_threshold)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -168,7 +176,8 @@ static void glfw_free_input(void *data)
|
||||
static const input_driver_t input_glfw = {
|
||||
.poll = glfw_input_poll,
|
||||
.input_state = glfw_input_state,
|
||||
.free = glfw_free_input
|
||||
.free = glfw_free_input,
|
||||
.ident = "glfw"
|
||||
};
|
||||
|
||||
static void GLFWCALL resize(int width, int height)
|
||||
@ -207,7 +216,8 @@ static void GLFWCALL resize(int width, int height)
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
#ifdef HAVE_CG
|
||||
cgGLSetStateMatrixParameter(cg_mvp_matrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
|
||||
if (cg_active)
|
||||
cgGLSetStateMatrixParameter(cg_mvp_matrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
|
||||
#endif
|
||||
gl_width = out_width;
|
||||
gl_height = out_height;
|
||||
@ -251,18 +261,43 @@ static bool gl_frame(void *data, const uint16_t* frame, int width, int height, i
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
#if HAVE_CG
|
||||
cgGLSetParameter2f(gl->cg_video_size, width, height);
|
||||
cgGLSetParameter2f(gl->cg_texture_size, width, height);
|
||||
cgGLSetParameter2f(gl->cg_output_size, gl_width, gl_height);
|
||||
if (cg_active)
|
||||
{
|
||||
cgGLSetParameter2f(gl->cg_video_size, width, height);
|
||||
cgGLSetParameter2f(gl->cg_texture_size, gl->tex_w, gl->tex_h);
|
||||
cgGLSetParameter2f(gl->cg_output_size, gl_width, gl_height);
|
||||
|
||||
cgGLSetParameter2f(gl->cg_Vvideo_size, width, height);
|
||||
cgGLSetParameter2f(gl->cg_Vtexture_size, width, height);
|
||||
cgGLSetParameter2f(gl->cg_Voutput_size, gl_width, gl_height);
|
||||
cgGLSetParameter2f(gl->cg_Vvideo_size, width, height);
|
||||
cgGLSetParameter2f(gl->cg_Vtexture_size, gl->tex_w, gl->tex_h);
|
||||
cgGLSetParameter2f(gl->cg_Voutput_size, gl_width, gl_height);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (width != gl->last_width || height != gl->last_height) // res change. need to clear out texture.
|
||||
{
|
||||
gl->last_width = width;
|
||||
gl->last_height = height;
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
|
||||
uint8_t *tmp = calloc(1, gl->tex_w * gl->tex_h * sizeof(uint16_t));
|
||||
glTexSubImage2D(GL_TEXTURE_2D,
|
||||
0, 0, 0, gl->tex_w, gl->tex_h, GL_BGRA,
|
||||
GL_UNSIGNED_SHORT_1_5_5_5_REV, tmp);
|
||||
free(tmp);
|
||||
|
||||
gl->tex_coords[0] = 0;
|
||||
gl->tex_coords[1] = (GLfloat)height / gl->tex_h;
|
||||
gl->tex_coords[2] = 0;
|
||||
gl->tex_coords[3] = 0;
|
||||
gl->tex_coords[4] = (GLfloat)width / gl->tex_w;
|
||||
gl->tex_coords[5] = 0;
|
||||
gl->tex_coords[6] = (GLfloat)width / gl->tex_w;
|
||||
gl->tex_coords[7] = (GLfloat)height / gl->tex_h;
|
||||
}
|
||||
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch >> 1);
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0, GL_RGBA, width, height, 0, GL_BGRA,
|
||||
glTexSubImage2D(GL_TEXTURE_2D,
|
||||
0, 0, 0, width, height, GL_BGRA,
|
||||
GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
|
||||
@ -276,7 +311,8 @@ static void gl_free(void *data)
|
||||
{
|
||||
gl_t *gl = data;
|
||||
#ifdef HAVE_CG
|
||||
cgDestroyContext(gl->cgCtx);
|
||||
if (cg_active)
|
||||
cgDestroyContext(gl->cgCtx);
|
||||
#endif
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
@ -298,7 +334,7 @@ static void gl_set_nonblock_state(void *data, bool state)
|
||||
|
||||
static void* gl_init(video_info_t *video, const input_driver_t **input)
|
||||
{
|
||||
gl_t *gl = malloc(sizeof(gl_t));
|
||||
gl_t *gl = calloc(1, sizeof(gl_t));
|
||||
if ( gl == NULL )
|
||||
return NULL;
|
||||
|
||||
@ -350,47 +386,65 @@ static void* gl_init(video_info_t *video, const input_driver_t **input)
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glVertexPointer(3, GL_FLOAT, 3 * sizeof(GLfloat), vertexes);
|
||||
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), tex_coords);
|
||||
|
||||
memcpy(gl->tex_coords, tex_coords, sizeof(tex_coords));
|
||||
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->tex_coords);
|
||||
|
||||
gl->tex_w = 256 * video->input_scale;
|
||||
gl->tex_h = 256 * video->input_scale;
|
||||
uint8_t *tmp = calloc(1, gl->tex_w * gl->tex_h * sizeof(uint16_t));
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0, GL_RGBA, gl->tex_w, gl->tex_h, 0, GL_BGRA,
|
||||
GL_UNSIGNED_SHORT_1_5_5_5_REV, tmp);
|
||||
free(tmp);
|
||||
gl->last_width = gl->tex_w;
|
||||
gl->last_height = gl->tex_h;
|
||||
|
||||
#ifdef HAVE_CG
|
||||
gl->cgCtx = cgCreateContext();
|
||||
if (gl->cgCtx == NULL)
|
||||
cg_active = false;
|
||||
if (strlen(g_settings.video.cg_shader_path) > 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to create Cg context\n");
|
||||
goto error;
|
||||
}
|
||||
gl->cgFProf = cgGLGetLatestProfile(CG_GL_FRAGMENT);
|
||||
gl->cgVProf = cgGLGetLatestProfile(CG_GL_VERTEX);
|
||||
if (gl->cgFProf == CG_PROFILE_UNKNOWN || gl->cgVProf == CG_PROFILE_UNKNOWN)
|
||||
{
|
||||
fprintf(stderr, "Invalid profile type\n");
|
||||
goto error;
|
||||
}
|
||||
cgGLSetOptimalOptions(gl->cgFProf);
|
||||
cgGLSetOptimalOptions(gl->cgVProf);
|
||||
gl->cgFPrg = cgCreateProgramFromFile(gl->cgCtx, CG_SOURCE, cg_shader_path, gl->cgFProf, "main_fragment", 0);
|
||||
gl->cgVPrg = cgCreateProgramFromFile(gl->cgCtx, CG_SOURCE, cg_shader_path, gl->cgVProf, "main_vertex", 0);
|
||||
if (gl->cgFPrg == NULL || gl->cgVPrg == NULL)
|
||||
{
|
||||
CGerror err = cgGetError();
|
||||
fprintf(stderr, "CG error: %s\n", cgGetErrorString(err));
|
||||
goto error;
|
||||
}
|
||||
cgGLLoadProgram(gl->cgFPrg);
|
||||
cgGLLoadProgram(gl->cgVPrg);
|
||||
cgGLEnableProfile(gl->cgFProf);
|
||||
cgGLEnableProfile(gl->cgVProf);
|
||||
cgGLBindProgram(gl->cgFPrg);
|
||||
cgGLBindProgram(gl->cgVPrg);
|
||||
SSNES_LOG("Loading Cg file: %s\n", g_settings.video.cg_shader_path);
|
||||
gl->cgCtx = cgCreateContext();
|
||||
if (gl->cgCtx == NULL)
|
||||
{
|
||||
fprintf(stderr, "Failed to create Cg context\n");
|
||||
goto error;
|
||||
}
|
||||
gl->cgFProf = cgGLGetLatestProfile(CG_GL_FRAGMENT);
|
||||
gl->cgVProf = cgGLGetLatestProfile(CG_GL_VERTEX);
|
||||
if (gl->cgFProf == CG_PROFILE_UNKNOWN || gl->cgVProf == CG_PROFILE_UNKNOWN)
|
||||
{
|
||||
fprintf(stderr, "Invalid profile type\n");
|
||||
goto error;
|
||||
}
|
||||
cgGLSetOptimalOptions(gl->cgFProf);
|
||||
cgGLSetOptimalOptions(gl->cgVProf);
|
||||
gl->cgFPrg = cgCreateProgramFromFile(gl->cgCtx, CG_SOURCE, g_settings.video.cg_shader_path, gl->cgFProf, "main_fragment", 0);
|
||||
gl->cgVPrg = cgCreateProgramFromFile(gl->cgCtx, CG_SOURCE, g_settings.video.cg_shader_path, gl->cgVProf, "main_vertex", 0);
|
||||
if (gl->cgFPrg == NULL || gl->cgVPrg == NULL)
|
||||
{
|
||||
CGerror err = cgGetError();
|
||||
fprintf(stderr, "CG error: %s\n", cgGetErrorString(err));
|
||||
goto error;
|
||||
}
|
||||
cgGLLoadProgram(gl->cgFPrg);
|
||||
cgGLLoadProgram(gl->cgVPrg);
|
||||
cgGLEnableProfile(gl->cgFProf);
|
||||
cgGLEnableProfile(gl->cgVProf);
|
||||
cgGLBindProgram(gl->cgFPrg);
|
||||
cgGLBindProgram(gl->cgVPrg);
|
||||
|
||||
gl->cg_video_size = cgGetNamedParameter(gl->cgFPrg, "IN.video_size");
|
||||
gl->cg_texture_size = cgGetNamedParameter(gl->cgFPrg, "IN.texture_size");
|
||||
gl->cg_output_size = cgGetNamedParameter(gl->cgFPrg, "IN.output_size");
|
||||
gl->cg_Vvideo_size = cgGetNamedParameter(gl->cgVPrg, "IN.video_size");
|
||||
gl->cg_Vtexture_size = cgGetNamedParameter(gl->cgVPrg, "IN.texture_size");
|
||||
gl->cg_Voutput_size = cgGetNamedParameter(gl->cgVPrg, "IN.output_size");
|
||||
cg_mvp_matrix = cgGetNamedParameter(gl->cgVPrg, "modelViewProj");
|
||||
cgGLSetStateMatrixParameter(cg_mvp_matrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
|
||||
gl->cg_video_size = cgGetNamedParameter(gl->cgFPrg, "IN.video_size");
|
||||
gl->cg_texture_size = cgGetNamedParameter(gl->cgFPrg, "IN.texture_size");
|
||||
gl->cg_output_size = cgGetNamedParameter(gl->cgFPrg, "IN.output_size");
|
||||
gl->cg_Vvideo_size = cgGetNamedParameter(gl->cgVPrg, "IN.video_size");
|
||||
gl->cg_Vtexture_size = cgGetNamedParameter(gl->cgVPrg, "IN.texture_size");
|
||||
gl->cg_Voutput_size = cgGetNamedParameter(gl->cgVPrg, "IN.output_size");
|
||||
cg_mvp_matrix = cgGetNamedParameter(gl->cgVPrg, "modelViewProj");
|
||||
cgGLSetStateMatrixParameter(cg_mvp_matrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
|
||||
cg_active = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
*input = &input_glfw;
|
||||
@ -406,7 +460,8 @@ const video_driver_t video_gl = {
|
||||
.init = gl_init,
|
||||
.frame = gl_frame,
|
||||
.set_nonblock_state = gl_set_nonblock_state,
|
||||
.free = gl_free
|
||||
.free = gl_free,
|
||||
.ident = "glfw"
|
||||
};
|
||||
|
||||
|
||||
|
@ -52,7 +52,7 @@ output main_fragment(float2 texCoord : TEXCOORD0, uniform sampler2D decal : TEXU
|
||||
float2 rubyTextureSize = IN.texture_size;
|
||||
|
||||
float2 xy = barrelDistortion(texCoord.xy);
|
||||
float2 one = 0.999/rubyTextureSize;
|
||||
float2 one = 1.0/rubyTextureSize;
|
||||
xy = xy + float2(0.0 , -0.5 * (phase + (1-phase) * rubyInputSize.y/rubyOutputSize.y) * one.y);
|
||||
float4 texels[8];
|
||||
texels[0] = TEX2D(xy + float2(-one.x,0.0));
|
||||
|
43
hqflt/filters.h
Normal file
43
hqflt/filters.h
Normal file
@ -0,0 +1,43 @@
|
||||
/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __FILTERS_H
|
||||
#define __FILTERS_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_FILTER
|
||||
|
||||
#include "pastlib.h"
|
||||
#include "grayscale.h"
|
||||
#include "bleed.h"
|
||||
#include "ntsc.h"
|
||||
|
||||
#define FILTER_HQ2X 1
|
||||
#define FILTER_HQ4X 2
|
||||
#define FILTER_GRAYSCALE 3
|
||||
#define FILTER_BLEED 4
|
||||
#define FILTER_NTSC 5
|
||||
#define FILTER_HQ2X_STR "hq2x"
|
||||
#define FILTER_HQ4X_STR "hq4x"
|
||||
#define FILTER_GRAYSCALE_STR "grayscale"
|
||||
#define FILTER_BLEED_STR "bleed"
|
||||
#define FILTER_NTSC_STR "ntsc"
|
||||
#endif
|
||||
|
||||
#endif
|
2
qb/conf.comp.sh
Normal file
2
qb/conf.comp.sh
Normal file
@ -0,0 +1,2 @@
|
||||
USE_LANG_C="yes"
|
||||
USE_LANG_CXX="yes"
|
32
qb/config.libs.sh
Normal file
32
qb/config.libs.sh
Normal file
@ -0,0 +1,32 @@
|
||||
. qb/qb.libs.sh
|
||||
|
||||
check_switch_c C99 -std=gnu99
|
||||
check_critical C99 "Cannot find C99 compatible compiler."
|
||||
|
||||
if [ $HAVE_DYNAMIC != yes ]; then
|
||||
check_lib_cxx SNES $LIBSNES snes_init -ldl
|
||||
check_critical SNES "Cannot find libsnes."
|
||||
add_define_make libsnes $LIBSNES
|
||||
fi
|
||||
|
||||
check_lib ALSA -lasound snd_pcm_open
|
||||
check_header OSS sys/soundcard.h
|
||||
check_lib AL -lopenal alcOpenDevice
|
||||
check_lib RSOUND -lrsound rsd_init
|
||||
check_lib ROAR -lroar roar_vs_new
|
||||
check_lib JACK -ljack jack_client_open
|
||||
|
||||
check_lib GLFW -lglfw glfwInit
|
||||
check_critical GLFW "Cannot find GLFW library."
|
||||
|
||||
check_lib CG -lCg cgCreateContext
|
||||
|
||||
check_lib SRC -lsamplerate src_callback_new
|
||||
|
||||
check_lib DYNAMIC -ldl dlopen
|
||||
|
||||
# Creates config.mk.
|
||||
VARS="ALSA OSS AL RSOUND ROAR JACK GLFW FILTER CG DYNAMIC"
|
||||
create_config_make config.mk $VARS
|
||||
create_config_header config.h $VARS
|
||||
|
19
qb/config.params.sh
Normal file
19
qb/config.params.sh
Normal file
@ -0,0 +1,19 @@
|
||||
. qb/qb.params.sh
|
||||
|
||||
PACKAGE_NAME=ssnes
|
||||
PACKAGE_VERSION=0.1
|
||||
|
||||
# Adds a command line opt to ./configure --help
|
||||
# $1: Variable (HAVE_ALSA, HAVE_OSS, etc)
|
||||
# $2: Comment
|
||||
# $3: Default arg. auto implies that HAVE_ALSA will be set according to library checks later on.
|
||||
add_command_line_enable DYNAMIC "Enable dynamic loading of libsnes library." no
|
||||
add_command_line_string LIBSNES "libsnes library used" "-lsnes"
|
||||
add_command_line_enable FILTER "Disable CPU filter support" yes
|
||||
add_command_line_enable CG "Enable CG shader support" auto
|
||||
add_command_line_enable ALSA "Enable ALSA support" auto
|
||||
add_command_line_enable OSS "Enable OSS support" auto
|
||||
add_command_line_enable RSOUND "Enable RSound support" auto
|
||||
add_command_line_enable ROAR "Enable RoarAudio support" auto
|
||||
add_command_line_enable AL "Enable OpenAL support" auto
|
||||
add_command_line_enable JACK "Enable JACK support" auto
|
68
qb/qb.comp.sh
Normal file
68
qb/qb.comp.sh
Normal file
@ -0,0 +1,68 @@
|
||||
. qb/conf.comp.sh
|
||||
|
||||
TEMP_C=.tmp.c
|
||||
TEMP_CXX=.tmp.cxx
|
||||
TEMP_EXE=.tmp
|
||||
|
||||
echo -n "Checking operating system ... "
|
||||
OS="Win32" # whatever ;D
|
||||
unamestr="`uname -o`"
|
||||
if [ ! -z "`echo $unamestr | grep -i Linux`" ]; then
|
||||
OS="Linux"
|
||||
elif [ ! -z "`echo $unamestr | grep -i Darwin`" ]; then
|
||||
OS="Darwin"
|
||||
elif [ ! -z "`echo $unamestr | grep -i BSD`" ]; then
|
||||
OS="BSD"
|
||||
elif [ ! -z "`echo $unamestr | grep -i NT`" ]; then
|
||||
OS="Cygwin"
|
||||
fi
|
||||
|
||||
echo $OS
|
||||
|
||||
|
||||
# Checking for working C compiler
|
||||
if [ "$USE_LANG_C" = yes ]; then
|
||||
echo "Checking for working C compiler ..."
|
||||
if [ -z $CC ]; then
|
||||
CC=`which gcc cc 2> /dev/null | grep ^/ | head -n 1`
|
||||
fi
|
||||
if [ -z $CC ]; then
|
||||
echo "Could not find C compiler in path. Exiting ..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -n "Checking if $CC is a suitable compiler ... "
|
||||
answer=no
|
||||
echo "#include <stdio.h>" > $TEMP_C
|
||||
echo "int main(void) { puts(\"Hai world!\"); return 0; }" >> $TEMP_C
|
||||
$CC -o $TEMP_EXE $TEMP_C 2>/dev/null >/dev/null && answer=yes
|
||||
echo $answer
|
||||
|
||||
rm -rf $TEMP_C $TEMP_EXE
|
||||
|
||||
[ $answer = no ] && echo "Can't find suitable C compiler. Exiting ..." && exit 1
|
||||
fi
|
||||
|
||||
# Checking for working C++ compiler
|
||||
if [ "$USE_LANG_CXX" = "yes" ]; then
|
||||
echo "Checking for working C++ compiler ..."
|
||||
if [ -z $CXX ]; then
|
||||
CXX=`which g++ c++ 2> /dev/null | grep ^/ | head -n 1`
|
||||
fi
|
||||
if [ -z $CXX ]; then
|
||||
echo "Could not find C compiler in path. Exiting ..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -n "Checking if $CXX is a suitable compiler ... "
|
||||
answer=no
|
||||
echo "#include <iostream>" > $TEMP_CXX
|
||||
echo "int main() { std::cout << \"Hai guise\" << std::endl; return 0; }" >> $TEMP_CXX
|
||||
$CXX -o $TEMP_EXE $TEMP_CXX 2>/dev/null >/dev/null && answer=yes
|
||||
echo $answer
|
||||
|
||||
rm -rf $TEMP_CXX $TEMP_EXE
|
||||
|
||||
[ $answer = no ] && echo "Can't find suitable C++ compiler. Exiting ..." && exit 1
|
||||
fi
|
||||
|
313
qb/qb.libs.sh
Normal file
313
qb/qb.libs.sh
Normal file
@ -0,0 +1,313 @@
|
||||
|
||||
PKG_CONF_PATH=""
|
||||
PKG_CONF_USED=""
|
||||
CONFIG_DEFINES=""
|
||||
MAKEFILE_DEFINES=""
|
||||
INCLUDE_DIRS=""
|
||||
LIBRARY_DIRS=""
|
||||
[ -z "$PREFIX" ] && PREFIX="/usr/local"
|
||||
|
||||
add_define_header()
|
||||
{
|
||||
CONFIG_DEFINES="$CONFIG_DEFINES:@$1@$2@:"
|
||||
}
|
||||
|
||||
add_define_make()
|
||||
{
|
||||
MAKEFILE_DEFINES="$MAKEFILE_DEFINES:@$1@$2@:"
|
||||
}
|
||||
|
||||
add_include_dirs()
|
||||
{
|
||||
while [ ! -z "$1" ]
|
||||
do
|
||||
INCLUDE_DIRS="$INCLUDE_DIRS -I$1"
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
add_library_dirs()
|
||||
{
|
||||
while [ ! -z "$1" ]
|
||||
do
|
||||
LIBRARY_DIRS="$LIBRARY_DIRS -L$1"
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
check_lib()
|
||||
{
|
||||
tmpval="HAVE_$1"
|
||||
eval tmpval=\$$tmpval
|
||||
[ "$tmpval" = "no" ] && return 0
|
||||
|
||||
echo -n "Checking function $3 in $2 ... "
|
||||
echo "void $3(void); int main(void) { $3(); return 0; }" > $TEMP_C
|
||||
|
||||
|
||||
eval HAVE_$1=no
|
||||
answer=no
|
||||
|
||||
extralibs="$4"
|
||||
|
||||
$CC -o $TEMP_EXE $TEMP_C $INCLUDE_DIRS $LIBRARY_DIRS $extralibs $2 2>/dev/null >/dev/null && answer=yes && eval HAVE_$1=yes
|
||||
|
||||
echo $answer
|
||||
|
||||
rm -rf $TEMP_C $TEMP_EXE
|
||||
if [ "$tmpval" = "yes" ] && [ "$answer" = "no" ]; then
|
||||
echo "Forced to build with library $2, but cannot locate. Exiting ..."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_lib_cxx()
|
||||
{
|
||||
tmpval="HAVE_$1"
|
||||
eval tmpval=\$$tmpval
|
||||
[ "$tmpval" = "no" ] && return 0
|
||||
|
||||
echo -n "Checking function $3 in $2 ... "
|
||||
echo "extern \"C\" { void $3(void); } int main() { $3(); }" > $TEMP_CXX
|
||||
|
||||
eval HAVE_$1=no
|
||||
answer=no
|
||||
|
||||
extralibs="$4"
|
||||
|
||||
$CXX -o $TEMP_EXE $TEMP_CXX $INCLUDE_DIRS $LIBRARY_DIRS $extralibs $2 2>/dev/null >/dev/null && answer=yes && eval HAVE_$1=yes
|
||||
|
||||
echo $answer
|
||||
|
||||
rm -rf $TEMP_CXX $TEMP_EXE
|
||||
if [ "$tmpval" = "yes" ] && [ "$answer" = "no" ]; then
|
||||
echo "Forced to build with library $2, but cannot locate. Exiting ..."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
locate_pkg_conf()
|
||||
{
|
||||
echo -n "Checking for pkg-config ... "
|
||||
PKG_CONF_PATH="`which pkg-config | grep ^/ | head -n1`"
|
||||
if [ -z $PKG_CONF_PATH ]; then
|
||||
echo "not found"
|
||||
echo "Cannot locate pkg-config. Exiting ..."
|
||||
exit 1
|
||||
fi
|
||||
echo "$PKG_CONF_PATH"
|
||||
}
|
||||
|
||||
check_pkgconf()
|
||||
{
|
||||
[ -z "$PKG_CONF_PATH" ] && locate_pkg_conf
|
||||
|
||||
tmpval="HAVE_$1"
|
||||
eval tmpval=\$$tmpval
|
||||
[ "$tmpval" = "no" ] && return 0
|
||||
|
||||
echo -n "Checking presence of package $2 ... "
|
||||
eval HAVE_$1=no
|
||||
eval $1_CFLAGS=""
|
||||
eval $1_LIBS=""
|
||||
answer=no
|
||||
minver=0.0
|
||||
[ ! -z $3 ] && minver=$3
|
||||
pkg-config --atleast-version=$minver --exists "$2" && eval HAVE_$1=yes && eval $1_CFLAGS='"`pkg-config $2 --cflags`"' && eval $1_LIBS='"`pkg-config $2 --libs`"' && answer=yes
|
||||
echo $answer
|
||||
|
||||
PKG_CONF_USED="$PKG_CONF_USED $1"
|
||||
|
||||
if [ "$tmpval" = "yes" ] && [ "$answer" = "no" ]; then
|
||||
echo "Forced to build with package $2, but cannot locate. Exiting ..."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_header()
|
||||
{
|
||||
tmpval="HAVE_$1"
|
||||
eval tmpval=\$$tmpval
|
||||
[ "$tmpval" = "no" ] && return 0
|
||||
|
||||
echo -n "Checking presence of header file $2 ... "
|
||||
echo "#include<$2>" > $TEMP_C
|
||||
echo "int main(void) { return 0; }" >> $TEMP_C
|
||||
eval HAVE_$1=no
|
||||
answer=no
|
||||
|
||||
$CC -o $TEMP_EXE $TEMP_C $INCLUDE_DIRS 2>/dev/null >/dev/null && answer=yes && eval HAVE_$1=yes
|
||||
|
||||
echo $answer
|
||||
|
||||
rm -rf $TEMP_C $TEMP_EXE
|
||||
if [ "$tmpval" = "yes" ] && [ "$answer" = "no" ]; then
|
||||
echo "Build assumed that $2 exists, but cannot locate. Exiting ..."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_switch_c()
|
||||
{
|
||||
echo -n "Checking for availability of switch $2 in $CC ... "
|
||||
if [ -z "$CC" ]; then
|
||||
echo "No C compiler, cannot check ..."
|
||||
exit 1
|
||||
fi
|
||||
echo "int main(void) { return 0; }" > $TEMP_C
|
||||
eval HAVE_$1=no
|
||||
answer=no
|
||||
$CC -o $TEMP_EXE $TEMP_C $2 2>/dev/null >/dev/null && answer=yes && eval HAVE_$1=yes
|
||||
|
||||
echo $answer
|
||||
|
||||
rm -rf $TEMP_C $TEMP_EXE
|
||||
}
|
||||
|
||||
check_switch_cxx()
|
||||
{
|
||||
echo -n "Checking for availability of switch $2 in $CXX ... "
|
||||
if [ -z "$CXX" ]; then
|
||||
echo "No C++ compiler, cannot check ..."
|
||||
exit 1
|
||||
fi
|
||||
echo "int main() { return 0; }" > $TEMP_CXX
|
||||
eval HAVE_$1=no
|
||||
answer=no
|
||||
$CXX -o $TEMP_EXE $TEMP_CXX $2 2>/dev/null >/dev/null && answer=yes && eval HAVE_$1=yes
|
||||
|
||||
echo $answer
|
||||
|
||||
rm -rf $TEMP_CXX $TEMP_EXE
|
||||
}
|
||||
|
||||
check_critical()
|
||||
{
|
||||
val=HAVE_$1
|
||||
eval val=\$$val
|
||||
if [ "$val" != "yes" ]; then
|
||||
echo "$2"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
output_define_header()
|
||||
{
|
||||
arg1="`echo $2 | sed 's|^@\([^@]*\)@\([^@]*\)@$|\1|'`"
|
||||
arg2="`echo $2 | sed 's|^@\([^@]*\)@\([^@]*\)@$|\2|'`"
|
||||
|
||||
echo "#define $arg1 $arg2" >> "$outfile"
|
||||
}
|
||||
|
||||
create_config_header()
|
||||
{
|
||||
outfile="$1"
|
||||
shift
|
||||
|
||||
echo "Creating config header: $outfile"
|
||||
|
||||
name="`echo __$outfile | sed 's|[\./]|_|g' | tr '[a-z]' '[A-Z]'`"
|
||||
echo "#ifndef $name" > "$outfile"
|
||||
echo "#define $name" >> "$outfile"
|
||||
echo "" >> "$outfile"
|
||||
echo "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >> "$outfile"
|
||||
echo "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >> "$outfile"
|
||||
|
||||
while [ ! -z "$1" ]
|
||||
do
|
||||
tmpval="HAVE_$1"
|
||||
eval tmpval=\$$tmpval
|
||||
if [ "$tmpval" = "yes" ]; then
|
||||
echo "#define HAVE_$1 1" >> "$outfile"
|
||||
elif [ "$tmpval" = "no" ]; then
|
||||
echo "/* #undef HAVE_$1 */" >> "$outfile"
|
||||
fi
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
echo "" >> "$outfile"
|
||||
|
||||
tmpdefs="$CONFIG_DEFINES"
|
||||
while [ ! -z "$tmpdefs" ]
|
||||
do
|
||||
subdefs="`echo $tmpdefs | sed 's|^:\(@[^@]*@[^@]*@\):.*$|\1|'`"
|
||||
tmpdefs="`echo $tmpdefs | sed 's|^\W*$||'`"
|
||||
tmpdefs="`echo $tmpdefs | sed 's|^:\(@[^@]*@[^@]*@\):||'`"
|
||||
output_define_header "$outfile" "$subdefs"
|
||||
done
|
||||
|
||||
echo "#endif" >> "$outfile"
|
||||
}
|
||||
|
||||
output_define_make()
|
||||
{
|
||||
arg1="`echo $2 | sed 's|^@\([^@]*\)@\([^@]*\)@$|\1|'`"
|
||||
arg2="`echo $2 | sed 's|^@\([^@]*\)@\([^@]*\)@$|\2|'`"
|
||||
|
||||
echo "$arg1 = $arg2" >> "$outfile"
|
||||
}
|
||||
|
||||
create_config_make()
|
||||
{
|
||||
|
||||
outfile="$1"
|
||||
shift
|
||||
|
||||
echo "Creating make config: $outfile"
|
||||
|
||||
rm -rf "$outfile"
|
||||
touch "$outfile"
|
||||
if [ "$USE_LANG_C" = "yes" ]; then
|
||||
echo "CC = $CC" >> "$outfile"
|
||||
echo "CFLAGS = $CFLAGS" >> "$outfile"
|
||||
fi
|
||||
if [ "$USE_LANG_CXX" = "yes" ]; then
|
||||
echo "CXX = $CXX" >> "$outfile"
|
||||
echo "CXXFLAGS = $CXXFLAGS" >> "$outfile"
|
||||
fi
|
||||
echo "LDFLAGS = $LDFLAGS" >> "$outfile"
|
||||
echo "INCLUDE_DIRS = $INCLUDE_DIRS" >> "$outfile"
|
||||
echo "LIBRARY_DIRS = $LIBRARY_DIRS" >> "$outfile"
|
||||
echo "PACKAGE_NAME = $PACKAGE_NAME" >> "$outfile"
|
||||
echo "PACKAGE_VERSION = $PACKAGE_VERSION" >> "$outfile"
|
||||
echo "PREFIX = $PREFIX" >> "$outfile"
|
||||
|
||||
while [ ! -z "$1" ]
|
||||
do
|
||||
tmpval="HAVE_$1"
|
||||
eval tmpval=\$$tmpval
|
||||
if [ "$tmpval" = yes ]; then
|
||||
echo "HAVE_$1 = 1" >> "$outfile"
|
||||
elif [ "$tmpval" = no ]; then
|
||||
echo "HAVE_$1 = 0" >> "$outfile"
|
||||
fi
|
||||
|
||||
if [ ! -z "`echo $PKG_CONF_USED | grep $1`" ]; then
|
||||
tmpval="$1_CFLAGS"
|
||||
eval tmpval=\$$tmpval
|
||||
echo "$1_CFLAGS = $tmpval" >> "$outfile"
|
||||
|
||||
tmpval="$1_LIBS"
|
||||
eval tmpval=\$$tmpval
|
||||
echo "$1_LIBS = $tmpval" >> "$outfile"
|
||||
fi
|
||||
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
echo "" >> "$outfile"
|
||||
|
||||
tmpdefs="$MAKEFILE_DEFINES"
|
||||
while [ ! -z "$tmpdefs" ]
|
||||
do
|
||||
subdefs="`echo $tmpdefs | sed 's|^:\(@[^@]*@[^@]*@\):.*$|\1|'`"
|
||||
tmpdefs="`echo $tmpdefs | sed 's|^\W*$||'`"
|
||||
tmpdefs="`echo $tmpdefs | sed 's|^:\(@[^@]*@[^@]*@\):||'`"
|
||||
output_define_make "$outfile" "$subdefs"
|
||||
done
|
||||
|
||||
}
|
||||
|
||||
|
150
qb/qb.params.sh
Normal file
150
qb/qb.params.sh
Normal file
@ -0,0 +1,150 @@
|
||||
COMMAND_LINE_OPTS_ENABLE=""
|
||||
|
||||
add_command_line_enable()
|
||||
{
|
||||
COMMAND_LINE_OPTS_ENABLE="$COMMAND_LINE_OPTS_ENABLE:\"$1\" \"$2\" \"$3\":"
|
||||
eval HAVE_$1=$3
|
||||
}
|
||||
|
||||
add_command_line_string()
|
||||
{
|
||||
COMMAND_LINE_OPTS_STRINGS="$COMMAND_LINE_OPTS_STRINGS:\"$1\" \"$2\" \"$3\":"
|
||||
eval $1=$3
|
||||
}
|
||||
|
||||
## lvl. 43 regex dragon awaits thee.
|
||||
print_help()
|
||||
{
|
||||
echo "===================="
|
||||
echo " Quickbuild script"
|
||||
echo "===================="
|
||||
echo "Package: $PACKAGE_NAME"
|
||||
echo "Version: $PACKAGE_VERSION"
|
||||
echo ""
|
||||
echo "General environment variables:"
|
||||
echo "CC: C compiler"
|
||||
echo "CFLAGS: C compiler flags"
|
||||
echo "CXX: C++ compiler"
|
||||
echo "CXXFLAGS: C++ compiler flags"
|
||||
echo "LDFLAGS: Linker flags"
|
||||
echo ""
|
||||
echo "General options:"
|
||||
echo "--prefix=\$path: Install path prefix"
|
||||
echo "--help: Show this help"
|
||||
echo ""
|
||||
echo "Custom options:"
|
||||
|
||||
tmpopts="$COMMAND_LINE_OPTS_ENABLE"
|
||||
while [ ! -z "$tmpopts" ]
|
||||
do
|
||||
subopts="`echo $tmpopts | sed 's|^:"\([^"]*\)"."\([^"]*\)"."\([^"]*\)":.*$|"\1":"\2":"\3"|'`"
|
||||
tmpopts="`echo $tmpopts | sed 's|^\W*$||'`"
|
||||
tmpopts="`echo $tmpopts | sed 's|^:"[^"]*"."[^"]*"."[^"]*":||'`"
|
||||
print_sub_opt "$subopts"
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
tmpopts="$COMMAND_LINE_OPTS_STRINGS"
|
||||
while [ ! -z "$tmpopts" ]
|
||||
do
|
||||
subopts="`echo $tmpopts | sed 's|^:"\([^"]*\)"."\([^"]*\)"."\([^"]*\)":.*$|"\1":"\2":"\3"|'`"
|
||||
tmpopts="`echo $tmpopts | sed 's|^\W*$||'`"
|
||||
tmpopts="`echo $tmpopts | sed 's|^:"[^"]*"."[^"]*"."[^"]*":||'`"
|
||||
print_sub_str_opt "$subopts"
|
||||
done
|
||||
}
|
||||
|
||||
print_sub_opt()
|
||||
{
|
||||
arg1="`echo $1 | sed 's|^"\([^"]*\)":"\([^"]*\)":"\([^"]*\)"$|\1|'`"
|
||||
arg2="`echo $1 | sed 's|^"\([^"]*\)":"\([^"]*\)":"\([^"]*\)"$|\2|'`"
|
||||
arg3="`echo $1 | sed 's|^"\([^"]*\)":"\([^"]*\)":"\([^"]*\)"$|\3|'`"
|
||||
|
||||
lowertext="`echo $arg1 | tr '[A-Z]' '[a-z]'`"
|
||||
|
||||
if [ "$arg3" = "auto" ]; then
|
||||
echo -n "--enable-$lowertext: "
|
||||
echo $arg2
|
||||
echo "--disable-$lowertext"
|
||||
elif [ "$arg3" = "yes" ]; then
|
||||
echo "--disable-$lowertext: $arg2"
|
||||
elif [ "$arg3" = "no" ]; then
|
||||
echo "--enable-$lowertext: $arg2"
|
||||
fi
|
||||
}
|
||||
|
||||
print_sub_str_opt()
|
||||
{
|
||||
arg1="`echo $1 | sed 's|^"\([^"]*\)":"\([^"]*\)":"\([^"]*\)"$|\1|'`"
|
||||
arg2="`echo $1 | sed 's|^"\([^"]*\)":"\([^"]*\)":"\([^"]*\)"$|\2|'`"
|
||||
arg3="`echo $1 | sed 's|^"\([^"]*\)":"\([^"]*\)":"\([^"]*\)"$|\3|'`"
|
||||
|
||||
lowertext="`echo $arg1 | tr '[A-Z]' '[a-z]'`"
|
||||
|
||||
echo "--with-$lowertext: $arg2 (Defaults: $arg3)"
|
||||
}
|
||||
|
||||
parse_input()
|
||||
{
|
||||
### Parse stuff :V
|
||||
|
||||
while [ ! -z "$1" ]
|
||||
do
|
||||
|
||||
case "$1" in
|
||||
|
||||
--prefix=*)
|
||||
prefix="`echo $1 | sed -e 's|^--prefix=\(\S\S*\)$|\1|' -e 's|\(\S\S*\)/$|\1|'`"
|
||||
|
||||
if [ "$prefix" != "$1" ]; then
|
||||
PREFIX="$prefix"
|
||||
fi
|
||||
;;
|
||||
|
||||
--enable-*)
|
||||
enable=`echo $1 | sed 's|^--enable-||'`
|
||||
if [ -z "`echo $COMMAND_LINE_OPTS_ENABLE | grep -i $enable`" ]; then
|
||||
print_help
|
||||
exit 1
|
||||
fi
|
||||
eval HAVE_`echo $enable | tr '[a-z]' '[A-Z]'`=yes
|
||||
;;
|
||||
|
||||
--disable-*)
|
||||
disable=`echo $1 | sed 's|^--disable-||'`
|
||||
if [ -z "`echo $COMMAND_LINE_OPTS_ENABLE | grep -i $disable`" ]; then
|
||||
print_help
|
||||
exit 1
|
||||
fi
|
||||
eval HAVE_`echo $disable | tr '[a-z]' '[A-Z]'`=no
|
||||
;;
|
||||
|
||||
--with-*)
|
||||
arg="`echo $1 | sed 's|^--with-\S\S*=||'`"
|
||||
with=`echo $1 | sed 's|^--with-\(\S\S*\)=.*$|\1|'`
|
||||
if [ -z "`echo $COMMAND_LINE_OPTS_STRINGS | grep -i $with`" ]; then
|
||||
print_help
|
||||
exit 1
|
||||
fi
|
||||
eval "`echo $with | tr '[a-z]' '[A-Z]'`=\"$arg\""
|
||||
;;
|
||||
|
||||
|
||||
-h|--help)
|
||||
print_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
print_help
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
shift
|
||||
|
||||
done
|
||||
}
|
||||
|
||||
|
461
settings.c
Normal file
461
settings.c
Normal file
@ -0,0 +1,461 @@
|
||||
/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "general.h"
|
||||
#include "conf/config_file.h"
|
||||
#include "config.def.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "hqflt/filters.h"
|
||||
#include "config.h"
|
||||
#include <ctype.h>
|
||||
|
||||
struct settings g_settings;
|
||||
|
||||
static void read_keybinds(config_file_t *conf);
|
||||
|
||||
static void set_defaults(void)
|
||||
{
|
||||
const char *def_video = NULL;
|
||||
const char *def_audio = NULL;
|
||||
|
||||
switch (VIDEO_DEFAULT_DRIVER)
|
||||
{
|
||||
case VIDEO_GL:
|
||||
def_video = "glfw";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (AUDIO_DEFAULT_DRIVER)
|
||||
{
|
||||
case AUDIO_RSOUND:
|
||||
def_audio = "rsound";
|
||||
break;
|
||||
case AUDIO_OSS:
|
||||
def_audio = "oss";
|
||||
break;
|
||||
case AUDIO_ALSA:
|
||||
def_audio = "alsa";
|
||||
break;
|
||||
case AUDIO_ROAR:
|
||||
def_audio = "roar";
|
||||
break;
|
||||
case AUDIO_AL:
|
||||
def_audio = "openal";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// No input atm ... It is in the GLFW driver.
|
||||
|
||||
if (def_video)
|
||||
strncpy(g_settings.video.driver, def_video, sizeof(g_settings.video.driver) - 1);
|
||||
if (def_audio)
|
||||
strncpy(g_settings.audio.driver, def_audio, sizeof(g_settings.audio.driver) - 1);
|
||||
|
||||
g_settings.video.xscale = xscale;
|
||||
g_settings.video.yscale = yscale;
|
||||
g_settings.video.fullscreen = fullscreen;
|
||||
g_settings.video.fullscreen_x = fullscreen_x;
|
||||
g_settings.video.fullscreen_y = fullscreen_y;
|
||||
g_settings.video.vsync = vsync;
|
||||
g_settings.video.smooth = video_smooth;
|
||||
g_settings.video.force_aspect = force_aspect;
|
||||
|
||||
g_settings.audio.enable = audio_enable;
|
||||
g_settings.audio.out_rate = out_rate;
|
||||
g_settings.audio.in_rate = in_rate;
|
||||
if (audio_device)
|
||||
strncpy(g_settings.audio.device, audio_device, sizeof(g_settings.audio.device));
|
||||
g_settings.audio.latency = out_latency;
|
||||
g_settings.audio.sync = audio_sync;
|
||||
g_settings.audio.src_quality = SAMPLERATE_QUALITY;
|
||||
|
||||
assert(sizeof(g_settings.input.binds[0]) >= sizeof(snes_keybinds_1));
|
||||
assert(sizeof(g_settings.input.binds[1]) >= sizeof(snes_keybinds_2));
|
||||
memcpy(g_settings.input.binds[0], snes_keybinds_1, sizeof(snes_keybinds_1));
|
||||
memcpy(g_settings.input.binds[1], snes_keybinds_2, sizeof(snes_keybinds_2));
|
||||
|
||||
g_settings.input.save_state_key = SAVE_STATE_KEY;
|
||||
g_settings.input.load_state_key = LOAD_STATE_KEY;
|
||||
g_settings.input.toggle_fullscreen_key = TOGGLE_FULLSCREEN;
|
||||
g_settings.input.axis_threshold = AXIS_THRESHOLD;
|
||||
g_settings.input.exit_emulator_key = GLFW_KEY_ESC;
|
||||
}
|
||||
|
||||
void parse_config(void)
|
||||
{
|
||||
memset(&g_settings, 0, sizeof(struct settings));
|
||||
config_file_t *conf = NULL;
|
||||
|
||||
if (strlen(g_extern.config_path) > 0)
|
||||
{
|
||||
conf = config_file_new(g_extern.config_path);
|
||||
if (!conf)
|
||||
{
|
||||
SSNES_ERR("Couldn't find config at path: \"%s\"\n", g_extern.config_path);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *xdg = getenv("XDG_CONFIG_HOME");
|
||||
const char *home = getenv("HOME");
|
||||
if (xdg)
|
||||
{
|
||||
char conf_path[strlen(xdg) + strlen("/ssnes/ssnes.cfg ")];
|
||||
strcpy(conf_path, xdg);
|
||||
strcat(conf_path, "/ssnes/ssnes.cfg");
|
||||
conf = config_file_new(conf_path);
|
||||
}
|
||||
else if (home)
|
||||
{
|
||||
char conf_path[strlen(home) + strlen("/.ssnesrc ")];
|
||||
strcpy(conf_path, xdg);
|
||||
strcat(conf_path, "/.ssnesrc");
|
||||
conf = config_file_new(conf_path);
|
||||
}
|
||||
// Try this as a last chance...
|
||||
if (!conf)
|
||||
conf = config_file_new("/etc/ssnes.cfg");
|
||||
}
|
||||
|
||||
set_defaults();
|
||||
if (conf == NULL)
|
||||
return;
|
||||
|
||||
int tmp_int;
|
||||
double tmp_double;
|
||||
bool tmp_bool;
|
||||
char *tmp_str;
|
||||
|
||||
// Video settings.
|
||||
if (config_get_double(conf, "video_xscale", &tmp_double))
|
||||
g_settings.video.xscale = tmp_double;
|
||||
|
||||
if (config_get_double(conf, "video_yscale", &tmp_double))
|
||||
g_settings.video.yscale = tmp_double;
|
||||
|
||||
if (config_get_int(conf, "video_fullscreen_x", &tmp_int))
|
||||
g_settings.video.fullscreen_x = tmp_int;
|
||||
|
||||
if (config_get_int(conf, "video_fullscreen_y", &tmp_int))
|
||||
g_settings.video.fullscreen_y = tmp_int;
|
||||
|
||||
if (config_get_bool(conf, "video_fullscreen", &tmp_bool))
|
||||
g_settings.video.fullscreen = tmp_bool;
|
||||
|
||||
if (config_get_bool(conf, "video_vsync", &tmp_bool))
|
||||
g_settings.video.vsync = tmp_bool;
|
||||
|
||||
if (config_get_bool(conf, "video_smooth", &tmp_bool))
|
||||
g_settings.video.smooth = tmp_bool;
|
||||
|
||||
if (config_get_bool(conf, "video_force_aspect", &tmp_bool))
|
||||
g_settings.video.force_aspect = tmp_bool;
|
||||
|
||||
if (config_get_string(conf, "video_cg_shader", &tmp_str))
|
||||
{
|
||||
strncpy(g_settings.video.cg_shader_path, tmp_str, sizeof(g_settings.video.cg_shader_path) - 1);
|
||||
free(tmp_str);
|
||||
}
|
||||
|
||||
#ifdef HAVE_FILTER
|
||||
if (config_get_string(conf, "video_filter", &tmp_str))
|
||||
{
|
||||
unsigned filter = 0;
|
||||
if (strcasecmp(FILTER_HQ2X_STR, tmp_str) == 0)
|
||||
filter = FILTER_HQ2X;
|
||||
else if (strcasecmp(FILTER_HQ4X_STR, tmp_str) == 0)
|
||||
filter = FILTER_HQ4X;
|
||||
else if (strcasecmp(FILTER_GRAYSCALE_STR, tmp_str) == 0)
|
||||
filter = FILTER_GRAYSCALE;
|
||||
else if (strcasecmp(FILTER_BLEED_STR, tmp_str) == 0)
|
||||
filter = FILTER_BLEED;
|
||||
else if (strcasecmp(FILTER_NTSC_STR, tmp_str) == 0)
|
||||
filter = FILTER_NTSC;
|
||||
else
|
||||
{
|
||||
SSNES_ERR(
|
||||
"Invalid filter... Valid filters are:\n"
|
||||
"\t%s\n"
|
||||
"\t%s\n"
|
||||
"\t%s\n"
|
||||
"\t%s\n"
|
||||
"\t%s\n",
|
||||
FILTER_HQ2X_STR, FILTER_HQ4X_STR, FILTER_GRAYSCALE_STR,
|
||||
FILTER_BLEED_STR, FILTER_NTSC_STR);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
free(tmp_str);
|
||||
g_settings.video.filter = filter;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Input Settings.
|
||||
if (config_get_double(conf, "input_axis_threshold", &tmp_double))
|
||||
g_settings.input.axis_threshold = tmp_double;
|
||||
|
||||
// Audio settings.
|
||||
if (config_get_bool(conf, "audio_enable", &tmp_bool))
|
||||
g_settings.audio.enable = tmp_bool;
|
||||
|
||||
if (config_get_int(conf, "audio_out_rate", &tmp_int))
|
||||
g_settings.audio.out_rate = tmp_int;
|
||||
|
||||
if (config_get_int(conf, "audio_in_rate", &tmp_int))
|
||||
g_settings.audio.in_rate = tmp_int;
|
||||
|
||||
if (config_get_string(conf, "audio_device", &tmp_str))
|
||||
{
|
||||
strncpy(g_settings.audio.device, tmp_str, sizeof(g_settings.audio.device) - 1);
|
||||
free(tmp_str);
|
||||
}
|
||||
|
||||
if (config_get_int(conf, "audio_latency", &tmp_int))
|
||||
g_settings.audio.latency = tmp_int;
|
||||
|
||||
if (config_get_bool(conf, "audio_sync", &tmp_bool))
|
||||
g_settings.audio.sync = tmp_bool;
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
if (config_get_string(conf, "video_driver", &tmp_str))
|
||||
{
|
||||
strncpy(g_settings.video.driver, tmp_str, sizeof(g_settings.video.driver) - 1);
|
||||
free(tmp_str);
|
||||
}
|
||||
if (config_get_string(conf, "audio_driver", &tmp_str))
|
||||
{
|
||||
strncpy(g_settings.audio.driver, tmp_str, sizeof(g_settings.audio.driver) - 1);
|
||||
free(tmp_str);
|
||||
}
|
||||
if (config_get_string(conf, "libsnes_path", &tmp_str))
|
||||
{
|
||||
strncpy(g_settings.libsnes, tmp_str, sizeof(g_settings.libsnes) - 1);
|
||||
free(tmp_str);
|
||||
}
|
||||
|
||||
read_keybinds(conf);
|
||||
|
||||
// TODO: Keybinds.
|
||||
|
||||
config_file_free(conf);
|
||||
}
|
||||
|
||||
struct bind_map
|
||||
{
|
||||
const char *key;
|
||||
const char *btn;
|
||||
const char *axis;
|
||||
int snes_key;
|
||||
};
|
||||
|
||||
// Big and nasty bind map... :)
|
||||
static const struct bind_map bind_maps[2][13] = {
|
||||
{
|
||||
{ "input_player1_a", "input_player1_a_btn", NULL, SNES_DEVICE_ID_JOYPAD_A },
|
||||
{ "input_player1_b", "input_player1_b_btn", NULL, SNES_DEVICE_ID_JOYPAD_B },
|
||||
{ "input_player1_y", "input_player1_y_btn", NULL, SNES_DEVICE_ID_JOYPAD_Y },
|
||||
{ "input_player1_x", "input_player1_x_btn", NULL, SNES_DEVICE_ID_JOYPAD_X },
|
||||
{ "input_player1_start", "input_player1_start_btn", NULL, SNES_DEVICE_ID_JOYPAD_START },
|
||||
{ "input_player1_select", "input_player1_select_btn", NULL, SNES_DEVICE_ID_JOYPAD_SELECT },
|
||||
{ "input_player1_l", "input_player1_l_btn", NULL, SNES_DEVICE_ID_JOYPAD_L },
|
||||
{ "input_player1_r", "input_player1_r_btn", NULL, SNES_DEVICE_ID_JOYPAD_R },
|
||||
{ "input_player1_left", "input_player1_left_btn", "input_player1_left_axis", SNES_DEVICE_ID_JOYPAD_LEFT },
|
||||
{ "input_player1_right", "input_player1_right_btn", "input_player1_right_axis", SNES_DEVICE_ID_JOYPAD_RIGHT },
|
||||
{ "input_player1_up", "input_player1_up_btn", "input_player1_up_axis", SNES_DEVICE_ID_JOYPAD_UP },
|
||||
{ "input_player1_down", "input_player1_down_btn", "input_player1_down_axis", SNES_DEVICE_ID_JOYPAD_DOWN },
|
||||
{ "input_toggle_fast_forward", "input_toggle_fast_forward_btn", NULL, SNES_FAST_FORWARD_KEY }
|
||||
},
|
||||
{
|
||||
{ "input_player2_a", "input_player2_a_btn", NULL, SNES_DEVICE_ID_JOYPAD_A },
|
||||
{ "input_player2_b", "input_player2_b_btn", NULL, SNES_DEVICE_ID_JOYPAD_B },
|
||||
{ "input_player2_y", "input_player2_y_btn", NULL, SNES_DEVICE_ID_JOYPAD_Y },
|
||||
{ "input_player2_x", "input_player2_x_btn", NULL, SNES_DEVICE_ID_JOYPAD_X },
|
||||
{ "input_player2_start", "input_player2_start_btn", NULL, SNES_DEVICE_ID_JOYPAD_START },
|
||||
{ "input_player2_select", "input_player2_select_btn", NULL, SNES_DEVICE_ID_JOYPAD_SELECT },
|
||||
{ "input_player2_l", "input_player2_l_btn", NULL, SNES_DEVICE_ID_JOYPAD_L },
|
||||
{ "input_player2_r", "input_player2_r_btn", NULL, SNES_DEVICE_ID_JOYPAD_R },
|
||||
{ "input_player2_left", "input_player2_left_btn", "input_player2_left_axis", SNES_DEVICE_ID_JOYPAD_LEFT },
|
||||
{ "input_player2_right", "input_player2_right_btn", "input_player2_right_axis", SNES_DEVICE_ID_JOYPAD_RIGHT },
|
||||
{ "input_player2_up", "input_player2_up_btn", "input_player2_up_axis", SNES_DEVICE_ID_JOYPAD_UP },
|
||||
{ "input_player2_down", "input_player2_down_btn", "input_player2_down_axis", SNES_DEVICE_ID_JOYPAD_DOWN },
|
||||
{ "input_toggle_fast_forward", "input_toggle_fast_forward_btn", NULL, SNES_FAST_FORWARD_KEY }
|
||||
}
|
||||
};
|
||||
|
||||
struct glfw_map
|
||||
{
|
||||
const char *str;
|
||||
int key;
|
||||
};
|
||||
|
||||
// Edit: Not portable to different input systems atm. Might move this map into the driver itself or something.
|
||||
static const struct glfw_map glfw_map[] = {
|
||||
{ "left", GLFW_KEY_LEFT },
|
||||
{ "right", GLFW_KEY_RIGHT },
|
||||
{ "up", GLFW_KEY_UP },
|
||||
{ "down", GLFW_KEY_DOWN },
|
||||
{ "enter", GLFW_KEY_ENTER },
|
||||
{ "tab", GLFW_KEY_TAB },
|
||||
{ "insert", GLFW_KEY_INSERT },
|
||||
{ "del", GLFW_KEY_DEL },
|
||||
{ "rshift", GLFW_KEY_RSHIFT },
|
||||
{ "shift", GLFW_KEY_LSHIFT },
|
||||
{ "ctrl", GLFW_KEY_LCTRL },
|
||||
{ "alt", GLFW_KEY_LALT },
|
||||
{ "space", GLFW_KEY_SPACE },
|
||||
{ "escape", GLFW_KEY_ESC },
|
||||
{ "f1", GLFW_KEY_F1 },
|
||||
{ "f2", GLFW_KEY_F2 },
|
||||
{ "f3", GLFW_KEY_F3 },
|
||||
{ "f4", GLFW_KEY_F4 },
|
||||
{ "f5", GLFW_KEY_F5 },
|
||||
{ "f6", GLFW_KEY_F6 },
|
||||
{ "f7", GLFW_KEY_F7 },
|
||||
{ "f8", GLFW_KEY_F8 },
|
||||
{ "f9", GLFW_KEY_F9 },
|
||||
{ "f10", GLFW_KEY_F10 },
|
||||
{ "f11", GLFW_KEY_F11 },
|
||||
{ "f12", GLFW_KEY_F12 },
|
||||
};
|
||||
|
||||
static struct snes_keybind *find_snes_bind(unsigned port, int id)
|
||||
{
|
||||
struct snes_keybind *binds = g_settings.input.binds[port];
|
||||
|
||||
for (int i = 0; binds[i].id != -1; i++)
|
||||
{
|
||||
if (id == binds[i].id)
|
||||
return &binds[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int find_glfw_bind(const char *str)
|
||||
{
|
||||
for (int i = 0; i < sizeof(glfw_map)/sizeof(struct glfw_map); i++)
|
||||
{
|
||||
if (strcasecmp(glfw_map[i].str, str) == 0)
|
||||
return glfw_map[i].key;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int find_glfw_key(const char *str)
|
||||
{
|
||||
// If the bind is a normal key-press ...
|
||||
if (strlen(str) == 1 && isalpha(*str))
|
||||
return toupper(*str);
|
||||
else // Check if we have a special mapping for it.
|
||||
return find_glfw_bind(str);
|
||||
}
|
||||
|
||||
static void read_keybinds(config_file_t *conf)
|
||||
{
|
||||
char *tmp_key = NULL;
|
||||
int tmp_btn;
|
||||
char *tmp_axis = NULL;
|
||||
|
||||
for (int j = 0; j < 1; j++)
|
||||
{
|
||||
for (int i = 0; i < sizeof(bind_maps[j])/sizeof(struct bind_map); i++)
|
||||
{
|
||||
struct snes_keybind *bind = find_snes_bind(j, bind_maps[j][i].snes_key);
|
||||
if (!bind)
|
||||
continue;
|
||||
|
||||
if (bind_maps[j][i].key && config_get_string(conf, bind_maps[j][i].key, &tmp_key))
|
||||
{
|
||||
int key = find_glfw_key(tmp_key);
|
||||
|
||||
if (key >= 0)
|
||||
bind->key = key;
|
||||
|
||||
free(tmp_key);
|
||||
tmp_key = NULL;
|
||||
}
|
||||
|
||||
if (bind_maps[j][i].btn && config_get_int(conf, bind_maps[j][i].btn, &tmp_btn))
|
||||
{
|
||||
if (tmp_btn >= 0)
|
||||
bind->joykey = tmp_btn;
|
||||
}
|
||||
|
||||
if (bind_maps[j][i].axis && config_get_string(conf, bind_maps[j][i].axis, &tmp_axis))
|
||||
{
|
||||
if (strlen(tmp_axis) >= 2 && (*tmp_axis == '+' || *tmp_axis == '-'))
|
||||
{
|
||||
int axis = strtol(tmp_axis + 1, NULL, 0);
|
||||
if (*tmp_axis == '+')
|
||||
bind->joyaxis = AXIS_POS(axis);
|
||||
else
|
||||
bind->joyaxis = AXIS_NEG(axis);
|
||||
|
||||
}
|
||||
free(tmp_axis);
|
||||
tmp_axis = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *tmp_str;
|
||||
if (config_get_string(conf, "input_toggle_fullscreen", &tmp_str))
|
||||
{
|
||||
int key = find_glfw_key(tmp_str);
|
||||
if (key >= 0)
|
||||
g_settings.input.toggle_fullscreen_key = key;
|
||||
free(tmp_str);
|
||||
}
|
||||
if (config_get_string(conf, "input_save_state", &tmp_str))
|
||||
{
|
||||
int key = find_glfw_key(tmp_str);
|
||||
if (key >= 0)
|
||||
g_settings.input.save_state_key = key;
|
||||
free(tmp_str);
|
||||
}
|
||||
if (config_get_string(conf, "input_load_state", &tmp_str))
|
||||
{
|
||||
int key = find_glfw_key(tmp_str);
|
||||
if (key >= 0)
|
||||
g_settings.input.load_state_key = key;
|
||||
free(tmp_str);
|
||||
}
|
||||
if (config_get_string(conf, "input_exit_emulator", &tmp_str))
|
||||
{
|
||||
int key = find_glfw_key(tmp_str);
|
||||
if (key >= 0)
|
||||
g_settings.input.exit_emulator_key = key;
|
||||
free(tmp_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
208
ssnes.c
208
ssnes.c
@ -24,15 +24,16 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include "config.h"
|
||||
#include "driver.h"
|
||||
#include "file.h"
|
||||
#include "hqflt/pastlib.h"
|
||||
#include "hqflt/grayscale.h"
|
||||
#include "hqflt/bleed.h"
|
||||
#include "hqflt/ntsc.h"
|
||||
#include "hqflt/filters.h"
|
||||
#include "general.h"
|
||||
#include "dynamic.h"
|
||||
|
||||
struct global g_extern = {
|
||||
.video_active = true,
|
||||
.audio_active = true,
|
||||
};
|
||||
|
||||
// To avoid continous switching if we hold the button down, we require that the button must go from pressed, unpressed back to pressed to be able to toggle between then.
|
||||
|
||||
@ -46,10 +47,10 @@ void set_fast_forward_button(bool new_button_state)
|
||||
if (new_button_state && !old_button_state)
|
||||
{
|
||||
syncing_state = !syncing_state;
|
||||
if (video_active)
|
||||
if (g_extern.video_active)
|
||||
driver.video->set_nonblock_state(driver.video_data, syncing_state);
|
||||
if (audio_active)
|
||||
driver.audio->set_nonblock_state(driver.audio_data, (audio_sync) ? syncing_state : true);
|
||||
if (g_extern.audio_active)
|
||||
driver.audio->set_nonblock_state(driver.audio_data, (g_settings.audio.sync) ? syncing_state : true);
|
||||
if (syncing_state)
|
||||
audio_chunk_size = AUDIO_CHUNK_SIZE_NONBLOCKING;
|
||||
else
|
||||
@ -58,7 +59,7 @@ void set_fast_forward_button(bool new_button_state)
|
||||
old_button_state = new_button_state;
|
||||
}
|
||||
|
||||
#if VIDEO_FILTER != FILTER_NONE
|
||||
#ifdef HAVE_FILTER
|
||||
static inline void process_frame (uint16_t * restrict out, const uint16_t * restrict in, unsigned width, unsigned height)
|
||||
{
|
||||
int pitch = 1024;
|
||||
@ -79,53 +80,54 @@ static inline void process_frame (uint16_t * restrict out, const uint16_t * rest
|
||||
// Format received is 16-bit 0RRRRRGGGGGBBBBB
|
||||
static void video_frame(const uint16_t *data, unsigned width, unsigned height)
|
||||
{
|
||||
if ( !video_active )
|
||||
if ( !g_extern.video_active )
|
||||
return;
|
||||
|
||||
#if VIDEO_FILTER == FILTER_HQ2X
|
||||
uint16_t outputHQ2x[width * height * 2 * 2];
|
||||
#elif VIDEO_FILTER == FILTER_HQ4X
|
||||
uint16_t outputHQ4x[width * height * 4 * 4];
|
||||
#elif VIDEO_FILTER == FILTER_NTSC
|
||||
uint16_t output_ntsc[SNES_NTSC_OUT_WIDTH(width) * height];
|
||||
#endif
|
||||
|
||||
#if VIDEO_FILTER != FILTER_NONE
|
||||
#ifdef HAVE_FILTER
|
||||
uint16_t output_filter[width * height * 4 * 4];
|
||||
uint16_t output[width * height];
|
||||
process_frame(output, data, width, height);
|
||||
#endif
|
||||
|
||||
#if VIDEO_FILTER == FILTER_HQ2X
|
||||
ProcessHQ2x(output, outputHQ2x);
|
||||
if ( !driver.video->frame(driver.video_data, outputHQ2x, width << 1, height << 1, width << 2) )
|
||||
video_active = false;
|
||||
#elif VIDEO_FILTER == FILTER_HQ4X
|
||||
ProcessHQ4x(output, outputHQ4x);
|
||||
if ( !driver.video->frame(driver.video_data, outputHQ4x, width << 2, height << 2, width << 3) )
|
||||
video_active = false;
|
||||
#elif VIDEO_FILTER == FILTER_GRAYSCALE
|
||||
grayscale_filter(output, width, height);
|
||||
if ( !driver.video->frame(driver.video_data, output, width, height, width << 1) )
|
||||
video_active = false;
|
||||
#elif VIDEO_FILTER == FILTER_BLEED
|
||||
bleed_filter(output, width, height);
|
||||
if ( !driver.video->frame(driver.video_data, output, width, height, width << 1) )
|
||||
video_active = false;
|
||||
#elif VIDEO_FILTER == FILTER_NTSC
|
||||
ntsc_filter(output_ntsc, output, width, height);
|
||||
if ( !driver.video->frame(driver.video_data, output_ntsc, SNES_NTSC_OUT_WIDTH(width), height, SNES_NTSC_OUT_WIDTH(width) << 1) )
|
||||
video_active = false;
|
||||
switch (g_settings.video.filter)
|
||||
{
|
||||
case FILTER_HQ2X:
|
||||
ProcessHQ2x(output, output_filter);
|
||||
if ( !driver.video->frame(driver.video_data, output_filter, width << 1, height << 1, width << 2) )
|
||||
g_extern.video_active = false;
|
||||
break;
|
||||
case FILTER_HQ4X:
|
||||
ProcessHQ4x(output, output_filter);
|
||||
if ( !driver.video->frame(driver.video_data, output_filter, width << 2, height << 2, width << 3) )
|
||||
g_extern.video_active = false;
|
||||
break;
|
||||
case FILTER_GRAYSCALE:
|
||||
grayscale_filter(output, width, height);
|
||||
if ( !driver.video->frame(driver.video_data, output, width, height, width << 1) )
|
||||
g_extern.video_active = false;
|
||||
break;
|
||||
case FILTER_BLEED:
|
||||
bleed_filter(output, width, height);
|
||||
if ( !driver.video->frame(driver.video_data, output, width, height, width << 1) )
|
||||
g_extern.video_active = false;
|
||||
break;
|
||||
case FILTER_NTSC:
|
||||
ntsc_filter(output_filter, output, width, height);
|
||||
if ( !driver.video->frame(driver.video_data, output_filter, SNES_NTSC_OUT_WIDTH(width), height, SNES_NTSC_OUT_WIDTH(width) << 1) )
|
||||
g_extern.video_active = false;
|
||||
break;
|
||||
default:
|
||||
if ( !driver.video->frame(driver.video_data, data, width, height, (height == 448 || height == 478) ? 1024 : 2048) )
|
||||
g_extern.video_active = false;
|
||||
}
|
||||
#else
|
||||
if ( !driver.video->frame(driver.video_data, data, width, height, (height == 448 || height == 478) ? 1024 : 2048) )
|
||||
video_active = false;
|
||||
g_extern.video_active = false;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
SRC_STATE* source = NULL;
|
||||
static void audio_sample(uint16_t left, uint16_t right)
|
||||
{
|
||||
if ( !audio_active )
|
||||
if ( !g_extern.audio_active )
|
||||
return;
|
||||
|
||||
static float data[AUDIO_CHUNK_SIZE_NONBLOCKING];
|
||||
@ -146,16 +148,16 @@ static void audio_sample(uint16_t left, uint16_t right)
|
||||
src_data.input_frames = audio_chunk_size / 2;
|
||||
src_data.output_frames = audio_chunk_size * 8;
|
||||
src_data.end_of_input = 0;
|
||||
src_data.src_ratio = (double)out_rate / (double)in_rate;
|
||||
src_data.src_ratio = (double)g_settings.audio.out_rate / (double)g_settings.audio.in_rate;
|
||||
|
||||
src_process(source, &src_data);
|
||||
src_process(g_extern.source, &src_data);
|
||||
|
||||
src_float_to_short_array(outsamples, temp_outsamples, src_data.output_frames_gen * 2);
|
||||
|
||||
if ( driver.audio->write(driver.audio_data, temp_outsamples, src_data.output_frames_gen * 4) < 0 )
|
||||
{
|
||||
fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n");
|
||||
audio_active = false;
|
||||
g_extern.audio_active = false;
|
||||
}
|
||||
|
||||
data_ptr = 0;
|
||||
@ -169,7 +171,7 @@ static void input_poll(void)
|
||||
|
||||
static int16_t input_state(bool port, unsigned device, unsigned index, unsigned id)
|
||||
{
|
||||
const struct snes_keybind *binds[] = { snes_keybinds_1, snes_keybinds_2 };
|
||||
const struct snes_keybind *binds[] = { g_settings.input.binds[0], g_settings.input.binds[1] };
|
||||
return driver.input->input_state(driver.input_data, binds, port, device, index, id);
|
||||
}
|
||||
|
||||
@ -193,20 +195,10 @@ static void print_help(void)
|
||||
puts("Usage: ssnes [rom file] [-h/--help | -s/--save]");
|
||||
puts("\t-h/--help: Show this help message");
|
||||
puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin");
|
||||
#ifdef HAVE_CG
|
||||
puts("\t-f/--shader: Path to Cg shader. Will be compiled at runtime.\n");
|
||||
#endif
|
||||
puts("\t-c/--config: Path for config file. Defaults to $XDG_CONFIG_HOME/ssnes/ssnes.cfg");
|
||||
puts("\t-v/--verbose: Verbose logging");
|
||||
}
|
||||
|
||||
bool fullscreen = START_FULLSCREEN;
|
||||
static FILE* rom_file = NULL;
|
||||
static char savefile_name_srm[256] = {0};
|
||||
bool verbose = false;
|
||||
#ifdef HAVE_CG
|
||||
char cg_shader_path[256] = DEFAULT_CG_SHADER;
|
||||
#endif
|
||||
|
||||
static void parse_input(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2)
|
||||
@ -219,18 +211,12 @@ static void parse_input(int argc, char *argv[])
|
||||
{ "help", 0, NULL, 'h' },
|
||||
{ "save", 1, NULL, 's' },
|
||||
{ "verbose", 0, NULL, 'v' },
|
||||
#ifdef HAVE_CG
|
||||
{ "shader", 1, NULL, 'f' },
|
||||
#endif
|
||||
{ "config", 0, NULL, 'c' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
int option_index = 0;
|
||||
#ifdef HAVE_CG
|
||||
char optstring[] = "hs:vf:";
|
||||
#else
|
||||
char optstring[] = "hs:v";
|
||||
#endif
|
||||
char optstring[] = "hs:vc:";
|
||||
for(;;)
|
||||
{
|
||||
int c = getopt_long(argc, argv, optstring, opts, &option_index);
|
||||
@ -245,18 +231,16 @@ static void parse_input(int argc, char *argv[])
|
||||
exit(0);
|
||||
|
||||
case 's':
|
||||
strncpy(savefile_name_srm, optarg, sizeof(savefile_name_srm));
|
||||
savefile_name_srm[sizeof(savefile_name_srm)-1] = '\0';
|
||||
strncpy(g_extern.savefile_name_srm, optarg, sizeof(g_extern.savefile_name_srm));
|
||||
g_extern.savefile_name_srm[sizeof(g_extern.savefile_name_srm)-1] = '\0';
|
||||
break;
|
||||
|
||||
#ifdef HAVE_CG
|
||||
case 'f':
|
||||
strncpy(cg_shader_path, optarg, sizeof(cg_shader_path) - 1);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 'v':
|
||||
verbose = true;
|
||||
g_extern.verbose = true;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
strncpy(g_extern.config_path, optarg, sizeof(g_extern.config_path) - 1);
|
||||
break;
|
||||
|
||||
case '?':
|
||||
@ -275,24 +259,20 @@ static void parse_input(int argc, char *argv[])
|
||||
strcpy(tmp, argv[optind]);
|
||||
char *dst = strrchr(tmp, '.');
|
||||
if (dst)
|
||||
{
|
||||
*dst = '\0';
|
||||
snes_set_cartridge_basename(tmp);
|
||||
}
|
||||
else
|
||||
snes_set_cartridge_basename(tmp);
|
||||
strncpy(g_extern.basename, tmp, sizeof(g_extern.basename) - 1);
|
||||
|
||||
SSNES_LOG("Opening file: \"%s\"\n", argv[optind]);
|
||||
rom_file = fopen(argv[optind], "rb");
|
||||
if (rom_file == NULL)
|
||||
g_extern.rom_file = fopen(argv[optind], "rb");
|
||||
if (g_extern.rom_file == NULL)
|
||||
{
|
||||
SSNES_ERR("Could not open file: \"%s\"\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
if (strlen(savefile_name_srm) == 0)
|
||||
fill_pathname(savefile_name_srm, argv[optind], ".srm");
|
||||
if (strlen(g_extern.savefile_name_srm) == 0)
|
||||
fill_pathname(g_extern.savefile_name_srm, argv[optind], ".srm");
|
||||
}
|
||||
else if (strlen(savefile_name_srm) == 0)
|
||||
else if (strlen(g_extern.savefile_name_srm) == 0)
|
||||
{
|
||||
SSNES_ERR("Need savefile argument when reading rom from stdin.\n");
|
||||
print_help();
|
||||
@ -302,35 +282,41 @@ static void parse_input(int argc, char *argv[])
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
snes_init();
|
||||
parse_input(argc, argv);
|
||||
parse_config();
|
||||
init_dlsym();
|
||||
|
||||
psnes_init();
|
||||
if (strlen(g_extern.basename) > 0)
|
||||
psnes_set_cartridge_basename(g_extern.basename);
|
||||
|
||||
SSNES_LOG("Version of libsnes API: %u.%u\n", psnes_library_revision_major(), psnes_library_revision_minor());
|
||||
void *rom_buf;
|
||||
ssize_t rom_len = 0;
|
||||
if ((rom_len = read_file(rom_file, &rom_buf)) == -1)
|
||||
if ((rom_len = read_file(g_extern.rom_file, &rom_buf)) == -1)
|
||||
{
|
||||
SSNES_ERR("Could not read ROM file.\n");
|
||||
exit(1);
|
||||
}
|
||||
SSNES_LOG("ROM size: %zi bytes\n", rom_len);
|
||||
|
||||
if (rom_file != NULL)
|
||||
fclose(rom_file);
|
||||
if (g_extern.rom_file != NULL)
|
||||
fclose(g_extern.rom_file);
|
||||
|
||||
char statefile_name[strlen(savefile_name_srm)+strlen(".state")+1];
|
||||
char savefile_name_rtc[strlen(savefile_name_srm)+strlen(".rtc")+1];
|
||||
char statefile_name[strlen(g_extern.savefile_name_srm)+strlen(".state")+1];
|
||||
char savefile_name_rtc[strlen(g_extern.savefile_name_srm)+strlen(".rtc")+1];
|
||||
|
||||
fill_pathname(statefile_name, argv[1], ".state");
|
||||
fill_pathname(savefile_name_rtc, argv[1], ".rtc");
|
||||
|
||||
init_drivers();
|
||||
|
||||
snes_set_video_refresh(video_frame);
|
||||
snes_set_audio_sample(audio_sample);
|
||||
snes_set_input_poll(input_poll);
|
||||
snes_set_input_state(input_state);
|
||||
psnes_set_video_refresh(video_frame);
|
||||
psnes_set_audio_sample(audio_sample);
|
||||
psnes_set_input_poll(input_poll);
|
||||
psnes_set_input_state(input_state);
|
||||
|
||||
if (!snes_load_cartridge_normal(NULL, rom_buf, rom_len))
|
||||
if (!psnes_load_cartridge_normal(NULL, rom_buf, rom_len))
|
||||
{
|
||||
SSNES_ERR("ROM file is not valid!\n");
|
||||
goto error;
|
||||
@ -338,7 +324,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
free(rom_buf);
|
||||
|
||||
unsigned serial_size = snes_serialize_size();
|
||||
unsigned serial_size = psnes_serialize_size();
|
||||
uint8_t *serial_data = malloc(serial_size);
|
||||
if (serial_data == NULL)
|
||||
{
|
||||
@ -346,49 +332,51 @@ int main(int argc, char *argv[])
|
||||
goto error;
|
||||
}
|
||||
|
||||
load_save_file(savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM);
|
||||
load_save_file(g_extern.savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM);
|
||||
load_save_file(savefile_name_rtc, SNES_MEMORY_CARTRIDGE_RTC);
|
||||
|
||||
///// TODO: Modular friendly!!!
|
||||
for(;;)
|
||||
{
|
||||
bool quitting = glfwGetKey(GLFW_KEY_ESC) || !glfwGetWindowParam(GLFW_OPENED);
|
||||
bool quitting = glfwGetKey(g_settings.input.exit_emulator_key) || !glfwGetWindowParam(GLFW_OPENED);
|
||||
|
||||
if ( quitting )
|
||||
break;
|
||||
|
||||
if ( glfwGetKey( SAVE_STATE_KEY ))
|
||||
if ( glfwGetKey( g_settings.input.save_state_key ))
|
||||
{
|
||||
write_file(statefile_name, serial_data, serial_size);
|
||||
}
|
||||
|
||||
else if ( glfwGetKey( LOAD_STATE_KEY ) )
|
||||
else if ( glfwGetKey( g_settings.input.load_state_key ) )
|
||||
load_state(statefile_name, serial_data, serial_size);
|
||||
|
||||
else if ( glfwGetKey( TOGGLE_FULLSCREEN ) )
|
||||
else if ( glfwGetKey( g_settings.input.toggle_fullscreen_key ) )
|
||||
{
|
||||
fullscreen = !fullscreen;
|
||||
g_settings.video.fullscreen = !g_settings.video.fullscreen;
|
||||
uninit_drivers();
|
||||
init_drivers();
|
||||
}
|
||||
|
||||
snes_run();
|
||||
psnes_run();
|
||||
}
|
||||
|
||||
save_file(savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM);
|
||||
save_file(g_extern.savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM);
|
||||
save_file(savefile_name_rtc, SNES_MEMORY_CARTRIDGE_RTC);
|
||||
|
||||
snes_unload_cartridge();
|
||||
snes_term();
|
||||
psnes_unload_cartridge();
|
||||
psnes_term();
|
||||
uninit_drivers();
|
||||
free(serial_data);
|
||||
uninit_dlsym();
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
snes_unload_cartridge();
|
||||
snes_term();
|
||||
psnes_unload_cartridge();
|
||||
psnes_term();
|
||||
uninit_drivers();
|
||||
uninit_dlsym();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
148
ssnes.cfg
Normal file
148
ssnes.cfg
Normal file
@ -0,0 +1,148 @@
|
||||
##### Config file for SSNES
|
||||
|
||||
## If enabled, load libsnes from a dynamic location.
|
||||
# libsnes_path = "/path/to/libsnes.so"
|
||||
|
||||
#### Video
|
||||
|
||||
# Windowed xscale and yscale (Real x res: 296 * xscale, real y scale: 224 * xscale)
|
||||
# video_xscale = 3.0
|
||||
# video_yscale = 3.0
|
||||
|
||||
# Fullscreen resolution
|
||||
# video_fullscreen_x = 1280
|
||||
# video_fullscreen_y = 720
|
||||
|
||||
# Start in fullscreen. Can be changed at runtime.
|
||||
# video_fullscreen = false
|
||||
|
||||
# Video vsync.
|
||||
# video_vsync = true
|
||||
|
||||
# Smoothens picture with bilinear filtering. Should be disabled if using Cg shaders.
|
||||
# video_smooth = true
|
||||
|
||||
# Forces rendering area to stay 4:3.
|
||||
# video_force_aspect = true
|
||||
|
||||
# Path to Cg shader. If enabled
|
||||
# video_cg_shader = "/path/to/cg/shader.cg"
|
||||
|
||||
# CPU-based filter. Valid ones are: hq2x, hq4x, grayscale, bleed, ntsc.
|
||||
# video_filter = ntsc
|
||||
|
||||
#### Audio
|
||||
|
||||
# Enable audio.
|
||||
# audio_enable = true
|
||||
|
||||
# Audio output samplerate.
|
||||
# audio_out_rate = 48000
|
||||
|
||||
# Audio input samplerate from libsnes.
|
||||
# Lower this (slightly) if you are experiencing frequent audio dropouts while vsync is enabled.
|
||||
# Conversely, increase this slightly if you are experiencing good audio,
|
||||
# but lots of dropped frames. Reasonable values for this is 32000 +/- 100 Hz.
|
||||
# audio_in_rate = 31950
|
||||
|
||||
# Audio driver backend. Depending on configuration possible candidates are: alsa, oss, rsound, roar, openal
|
||||
# audio_driver = alsa
|
||||
|
||||
# Override the default audio device the audio_driver uses.
|
||||
# audio_device =
|
||||
|
||||
# Will sync (block) on audio. Recommended.
|
||||
# audio_sync = true
|
||||
|
||||
# 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.
|
||||
# audio_src_quality =
|
||||
|
||||
### Input
|
||||
|
||||
# Defines axis threshold. Possible values are [0.0, 1.0]
|
||||
# input_axis_threshold = 0.6
|
||||
|
||||
# Keyboard input. Will recognize normal keypresses and special keys like "left", "right", and so on.
|
||||
# input_player1_a = x
|
||||
# input_player1_b = z
|
||||
# input_player1_y = a
|
||||
# input_player1_x = s
|
||||
# input_player1_start = enter
|
||||
# input_player1_select = rshift
|
||||
# input_player1_l = q
|
||||
# input_player1_r = w
|
||||
# input_player1_left = left
|
||||
# input_player1_right = right
|
||||
# input_player1_up = up
|
||||
# input_player1_down = down
|
||||
|
||||
# Joypad buttons. Figure these out by looking at jstest /dev/input/js0 output.
|
||||
# input_player1_a_btn = 1
|
||||
# input_player1_b_btn = 0
|
||||
# input_player1_y_btn = 2
|
||||
# input_player1_x_btn = 3
|
||||
# input_player1_start_btn = 7
|
||||
# input_player1_select_btn = 6
|
||||
# input_player1_l_btn = 4
|
||||
# input_player1_r_btn = 5
|
||||
# input_player1_left_btn = 11
|
||||
# input_player1_right_btn = 12
|
||||
# input_player1_up_btn = 13
|
||||
# input_player1_down_btn = 14
|
||||
|
||||
# Axis for DPAD. Needs to be either '+' or '-' in the first character signaling either positive or negative direction of the axis, then the axis number.
|
||||
# input_player1_left_axis = -0
|
||||
# input_player1_right_axis = +0
|
||||
# input_player1_up_axis = +1
|
||||
# input_player1_down_axis = -1
|
||||
|
||||
# Same stuff, just for player two.
|
||||
# input_player2_a =
|
||||
# input_player2_b =
|
||||
# input_player2_y =
|
||||
# input_player2_x =
|
||||
# input_player2_start =
|
||||
# input_player2_select =
|
||||
# input_player2_l =
|
||||
# input_player2_r =
|
||||
# input_player2_left =
|
||||
# input_player2_right =
|
||||
# input_player2_up =
|
||||
# input_player2_down =
|
||||
|
||||
# input_player2_a_btn = 1
|
||||
# input_player2_b_btn = 0
|
||||
# input_player2_y_btn = 2
|
||||
# input_player2_x_btn = 3
|
||||
# input_player2_start_btn = 7
|
||||
# input_player2_select_btn = 6
|
||||
# input_player2_l_btn = 4
|
||||
# input_player2_r_btn = 5
|
||||
# input_player2_left_btn = 11
|
||||
# input_player2_right_btn = 12
|
||||
# input_player2_up_btn = 13
|
||||
# input_player2_down_btn = 14
|
||||
|
||||
|
||||
# input_player2_left_axis = -0
|
||||
# input_player2_right_axis = +0
|
||||
# input_player2_up_axis = +1
|
||||
# input_player2_down_axis = -1
|
||||
|
||||
# Toggles fullscreen.
|
||||
# input_toggle_fullscreen = f
|
||||
# Saves state.
|
||||
# input_save_state = f2
|
||||
# Loads state.
|
||||
# input_load_state = f4
|
||||
|
||||
# Toggles between fast-forwarding and normal speed.
|
||||
# input_toggle_fast_forward = space
|
||||
# Same, just mapping to a joypad button.
|
||||
# input_toggle_fast_forward_btn = 10
|
||||
|
||||
# Key to exit emulator cleanly.
|
||||
# input_exit_emulator = escape
|
Loading…
x
Reference in New Issue
Block a user