From 73203b75c17ec5aec7e929604a678081b2bc9e16 Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 30 Dec 2010 13:54:49 +0100 Subject: [PATCH] Dynamic loading of libsnes. --- Makefile | 6 ++- dynamic.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++ dynamic.h | 54 +++++++++++++++++++ file.c | 13 ++--- general.h | 2 + qb/config.libs.sh | 4 +- settings.c | 5 ++ ssnes.c | 34 +++++++----- 8 files changed, 228 insertions(+), 22 deletions(-) create mode 100644 dynamic.c create mode 100644 dynamic.h diff --git a/Makefile b/Makefile index 33598ae5dd..39fd4a4c5e 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ include config.mk TARGET = ssnes -OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o +OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o LIBS = -lsamplerate $(libsnes) @@ -43,6 +43,10 @@ ifeq ($(HAVE_FILTER), 1) OBJ += hqflt/snes_ntsc/snes_ntsc.o endif +ifeq ($(HAVE_DL), 1) + LIBS += -ldl +endif + CFLAGS = -Wall -O3 -std=gnu99 -I. all: $(TARGET) config.mk diff --git a/dynamic.c b/dynamic.c new file mode 100644 index 0000000000..d819887c48 --- /dev/null +++ b/dynamic.c @@ -0,0 +1,132 @@ +/* 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 . + */ + +#include "dynamic.h" +#include "general.h" +#include +#include "config.h" + +#ifdef HAVE_DL +#include + +#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 + +static void *lib_handle = NULL; + +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_DL +static void load_dynamic(void) +{ + 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) + +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); +} + +void init_dlsym(void) +{ +#ifdef HAVE_DL + if (strlen(g_settings.libsnes) > 0) + load_dynamic(); + else +#endif + set_statics(); +} + +void uninit_dlsym(void) +{ +#ifdef HAVE_DL + if (lib_handle) + dlclose(lib_handle); +#endif +} diff --git a/dynamic.h b/dynamic.h new file mode 100644 index 0000000000..a804da26bc --- /dev/null +++ b/dynamic.h @@ -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 . + */ + +#ifndef __DYNAMIC_H +#define __DYNAMIC_H + +#include +#include + +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 + diff --git a/file.c b/file.c index 6fc517592c..262eb4983e 100644 --- a/file.c +++ b/file.c @@ -21,6 +21,7 @@ #include #include #include +#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); diff --git a/general.h b/general.h index 1a5a2acfd7..9ef3dfa4a7 100644 --- a/general.h +++ b/general.h @@ -66,6 +66,8 @@ struct settings int exit_emulator_key; float axis_threshold; } input; + + char libsnes[256]; }; struct global diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 5dd6cf36f2..8eb41c6ed3 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -20,8 +20,10 @@ check_lib CG -lCg cgCreateContext check_lib SRC -lsamplerate src_callback_new +check_lib DL -ldl dlopen + # Creates config.mk. -VARS="ALSA OSS AL RSOUND ROAR GLFW FILTER CG" +VARS="ALSA OSS AL RSOUND ROAR GLFW FILTER CG DL" create_config_make config.mk $VARS create_config_header config.h $VARS diff --git a/settings.c b/settings.c index 307cf6b73b..42e0da94f6 100644 --- a/settings.c +++ b/settings.c @@ -255,6 +255,11 @@ void parse_config(void) 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); diff --git a/ssnes.c b/ssnes.c index 7ed7223f08..465cbc3be9 100644 --- a/ssnes.c +++ b/ssnes.c @@ -28,6 +28,7 @@ #include "file.h" #include "hqflt/filters.h" #include "general.h" +#include "dynamic.h" struct global g_extern = { .video_active = true, @@ -260,10 +261,10 @@ static void parse_input(int argc, char *argv[]) if (dst) { *dst = '\0'; - snes_set_cartridge_basename(tmp); + psnes_set_cartridge_basename(tmp); } else - snes_set_cartridge_basename(tmp); + psnes_set_cartridge_basename(tmp); SSNES_LOG("Opening file: \"%s\"\n", argv[optind]); g_extern.rom_file = fopen(argv[optind], "rb"); @@ -285,10 +286,13 @@ 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(); + 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(g_extern.rom_file, &rom_buf)) == -1) @@ -309,12 +313,12 @@ int main(int argc, char *argv[]) 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; @@ -322,7 +326,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) { @@ -356,23 +360,25 @@ int main(int argc, char *argv[]) init_drivers(); } - snes_run(); + psnes_run(); } 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; }