From 0522a2d6a0f2435e5e9e7d32d131ca1365dfb1bc Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 27 Oct 2011 23:40:34 +0200 Subject: [PATCH] Start implementing some minor extensions to libsnes. --- config.def.h | 1 - driver.c | 25 ++++++++------ dynamic.c | 98 +++++++++++++++++++++++++++++++++++++++++----------- general.h | 18 +++++++++- libsnes.hpp | 18 ++++++++++ settings.c | 2 -- ssnes.c | 69 ++++++++++++++++++++++-------------- ssnes.cfg | 3 -- 8 files changed, 169 insertions(+), 65 deletions(-) diff --git a/config.def.h b/config.def.h index 9bd17f9bc3..cc4840f213 100644 --- a/config.def.h +++ b/config.def.h @@ -111,7 +111,6 @@ // Windowed static const float xscale = 3.0; // Real x res = aspect * base_size * xscale static const float yscale = 3.0; // Real y res = base_size * yscale -static const unsigned base_size = 224; // Base size for windowed mode. // Fullscreen static const bool fullscreen = false; // To start in Fullscreen or not diff --git a/driver.c b/driver.c index 030f17b37e..5a9f286802 100644 --- a/driver.c +++ b/driver.c @@ -330,17 +330,17 @@ static void init_filter(void) g_extern.filter.active = true; - unsigned width = 512; - unsigned height = 512; + unsigned width = g_extern.system.geom.max_width; + unsigned height = g_extern.system.geom.max_height; g_extern.filter.psize(&width, &height); unsigned pow2_x = next_pow2(ceil(width)); unsigned pow2_y = next_pow2(ceil(height)); unsigned maxsize = pow2_x > pow2_y ? pow2_x : pow2_y; - g_extern.filter.scale = maxsize / 256; + g_extern.filter.scale = maxsize / SSNES_SCALE_BASE; - g_extern.filter.buffer = malloc(256 * 256 * g_extern.filter.scale * g_extern.filter.scale * sizeof(uint32_t)); - g_extern.filter.pitch = 256 * g_extern.filter.scale * sizeof(uint32_t); + g_extern.filter.buffer = malloc(SSNES_SCALE_BASE * SSNES_SCALE_BASE * g_extern.filter.scale * g_extern.filter.scale * sizeof(uint32_t)); + g_extern.filter.pitch = SSNES_SCALE_BASE * g_extern.filter.scale * sizeof(uint32_t); assert(g_extern.filter.buffer); g_extern.filter.colormap = malloc(32768 * sizeof(uint32_t)); @@ -412,8 +412,9 @@ void init_video_input(void) init_shader_dir(); #endif - // We use at least 512x512 textures to accomodate for hi-res games. - unsigned scale = 2; + unsigned max_dim = max(g_extern.system.geom.max_width, g_extern.system.geom.max_height); + unsigned scale = max_dim / SSNES_SCALE_BASE; + scale = max(scale, 1); find_video_driver(); find_input_driver(); @@ -432,16 +433,18 @@ void init_video_input(void) { if (g_settings.video.force_aspect) { - width = roundf(g_settings.video.base_size * g_settings.video.xscale * g_settings.video.aspect_ratio); - height = roundf(g_settings.video.base_size * g_settings.video.yscale); + width = roundf(g_extern.system.geom.base_height * g_settings.video.xscale * g_settings.video.aspect_ratio); + height = roundf(g_extern.system.geom.base_height * g_settings.video.yscale); } else { - width = roundf(8.0f / 7.0f * g_settings.video.base_size * g_settings.video.xscale); // Assume 8:7 aspect. - height = roundf(g_settings.video.base_size * g_settings.video.yscale); + width = roundf(g_extern.system.geom.base_width * g_settings.video.xscale); + height = roundf(g_extern.system.geom.base_height * g_settings.video.yscale); } } + SSNES_LOG("Video @ %ux%u\n", width, height); + video_info_t video = { .width = width, .height = height, diff --git a/dynamic.c b/dynamic.c index 44a6887b90..9dee2002f1 100644 --- a/dynamic.c +++ b/dynamic.c @@ -96,6 +96,8 @@ unsigned (*psnes_get_memory_size)(unsigned); void (*psnes_unload_cartridge)(void); void (*psnes_term)(void); +static void set_environment(void); + #ifdef HAVE_DYNAMIC static void load_dynamic(void) { @@ -181,29 +183,22 @@ static void set_statics(void) void init_dlsym(void) { - // Guarantee that we can do "dirty" casting. Every OS that this program supports should pass this ... + // Guarantee that we can do "dirty" casting. + // Every OS that this program supports should pass this ... assert(sizeof(void*) == sizeof(void (*)(void))); #ifdef HAVE_DYNAMIC -#ifndef _WIN32 // Try to verify that -lsnes was not linked in from other modules - // since loading it dynamically and with -l will fail hard. - void *lib = dlopen(NULL, RTLD_LAZY); - if (lib) + // Try to verify that -lsnes was not linked in from other modules + // since loading it dynamically and with -l will fail hard. + function_t sym = dylib_proc(NULL, "snes_init"); + if (sym) { - void *sym = dlsym(lib, "snes_init"); - if (sym) - { - SSNES_ERR("Serious problem! SSNES wants to load libsnes dyamically, but it is already linked!\n"); - SSNES_ERR("This could happen if other modules SSNES depends on link against libsnes directly.\n"); - SSNES_ERR("Proceeding could cause a crash! Aborting ...\n"); - dlclose(lib); - exit(1); - } - - dlclose(lib); + SSNES_ERR("Serious problem! SSNES wants to load libsnes dyamically, but it is already linked!\n"); + SSNES_ERR("This could happen if other modules SSNES depends on link against libsnes directly.\n"); + SSNES_ERR("Proceeding could cause a crash! Aborting ...\n"); + exit(1); } -#endif if (!*g_settings.libsnes) { @@ -220,6 +215,8 @@ void init_dlsym(void) #else set_statics(); #endif + + set_environment(); } void uninit_dlsym(void) @@ -243,16 +240,26 @@ dylib_t dylib_load(const char *path) function_t dylib_proc(dylib_t lib, const char *proc) { #ifdef _WIN32 - function_t sym = (function_t)GetProcAddress(lib, proc); + function_t sym = (function_t)GetProcAddress(lib ? lib : GetModuleHandle(NULL), proc); #else + void *ptr_sym = NULL; + if (lib) + ptr_sym = dlsym(lib, proc); + else + { + void *handle = dlopen(NULL, RTLD_LAZY); + if (handle) + { + ptr_sym = dlsym(handle, proc); + dlclose(handle); + } + } + // Dirty hack to workaround the non-legality of void* -> fn-pointer casts. - void *ptr_sym = dlsym(lib, proc); function_t sym; memcpy(&sym, &ptr_sym, sizeof(void*)); #endif - if (!sym) - SSNES_WARN("Failed to load symbol \"%s\"\n", proc); return sym; } @@ -265,3 +272,52 @@ void dylib_close(dylib_t lib) #endif } +static bool environment_cb(unsigned cmd, void *data) +{ + switch (cmd) + { + case SNES_ENVIRONMENT_GET_FULLPATH: + *(const char**)data = g_extern.system.fullpath; + break; + + case SNES_ENVIRONMENT_SET_GEOMETRY: + g_extern.system.geom = *(const struct snes_geometry*)data; + g_extern.system.geom.max_width = next_pow2(g_extern.system.geom.max_width); + g_extern.system.geom.max_height = next_pow2(g_extern.system.geom.max_height); + break; + + case SNES_ENVIRONMENT_SET_PITCH: + g_extern.system.pitch = *(const unsigned*)data; + break; + + default: + return false; + } + + return true; +} + +// Assume SNES as defaults. +static void set_environment_defaults(void) +{ + g_extern.system.pitch = 0; // 0 is classic libsnes semantics. + g_extern.system.geom = (struct snes_geometry) { + .base_width = 256, + .base_height = 224, + .max_width = 512, + .max_height = 512, + }; +} + +// SSNES extension hooks. Totally optional 'n shizz :) +static void set_environment(void) +{ + void (*psnes_set_environment)(snes_environment_t) = + (void (*)(snes_environment_t))dylib_proc(lib_handle, "snes_set_environment"); + + if (psnes_set_environment) + psnes_set_environment(environment_cb); + + set_environment_defaults(); +} + diff --git a/general.h b/general.h index 668492a42d..813075993e 100644 --- a/general.h +++ b/general.h @@ -74,7 +74,6 @@ struct settings char driver[32]; float xscale; float yscale; - unsigned base_size; bool fullscreen; unsigned fullscreen_x; unsigned fullscreen_y; @@ -208,6 +207,13 @@ struct global unsigned state_slot; + struct + { + struct snes_geometry geom; + unsigned pitch; // If 0, has classic libsnes semantics. + char fullpath[MAXPATHLEN]; + } system; + struct { hermite_resampler_t *source; @@ -329,6 +335,16 @@ extern struct global g_extern; fflush(stderr); \ } while (0) +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#define SSNES_SCALE_BASE 256 + static inline uint32_t next_pow2(uint32_t v) { v--; diff --git a/libsnes.hpp b/libsnes.hpp index f191ec6b60..a2bc2b9687 100755 --- a/libsnes.hpp +++ b/libsnes.hpp @@ -66,11 +66,29 @@ extern "C" { #define SNES_MEMORY_OAM 103 #define SNES_MEMORY_CGRAM 104 +// SSNES extension. Not required to be implemented for a working implementation. +#define SNES_ENVIRONMENT_GET_FULLPATH 0 // const char ** +#define SNES_ENVIRONMENT_SET_GEOMETRY 1 // const struct snes_geometry * +#define SNES_ENVIRONMENT_SET_PITCH 2 // const unsigned * + +struct snes_geometry +{ + unsigned base_width; // Nominal video width of system. + unsigned base_height; // Nominal video height of system. + unsigned max_width; // Maximum possible width of system. + unsigned max_height; // Maximum possible height of system. +}; + +typedef bool (*snes_environment_t)(unsigned cmd, void *data); +void snes_set_environment(snes_environment_t); +//// + typedef void (*snes_video_refresh_t)(const uint16_t *data, unsigned width, unsigned height); typedef void (*snes_audio_sample_t)(uint16_t left, uint16_t right); typedef void (*snes_input_poll_t)(void); typedef int16_t (*snes_input_state_t)(bool port, unsigned device, unsigned index, unsigned id); + unsigned snes_library_revision_major(void); unsigned snes_library_revision_minor(void); diff --git a/settings.c b/settings.c index d24da50d54..fc11472215 100644 --- a/settings.c +++ b/settings.c @@ -120,7 +120,6 @@ static void set_defaults(void) g_settings.video.xscale = xscale; g_settings.video.yscale = yscale; - g_settings.video.base_size = base_size; g_settings.video.fullscreen = g_extern.force_fullscreen ? true : fullscreen; g_settings.video.fullscreen_x = fullscreen_x; g_settings.video.fullscreen_y = fullscreen_y; @@ -302,7 +301,6 @@ static void parse_config_file(void) CONFIG_GET_DOUBLE(video.xscale, "video_xscale"); CONFIG_GET_DOUBLE(video.yscale, "video_yscale"); - CONFIG_GET_INT(video.base_size, "video_base_size"); CONFIG_GET_INT(video.fullscreen_x, "video_fullscreen_x"); CONFIG_GET_INT(video.fullscreen_y, "video_fullscreen_y"); diff --git a/ssnes.c b/ssnes.c index a92001ea62..837541888f 100644 --- a/ssnes.c +++ b/ssnes.c @@ -85,6 +85,14 @@ static void set_fast_forward_button(bool new_button_state, bool new_hold_button_ old_hold_button_state = new_hold_button_state; } +static inline unsigned lines_to_pitch(unsigned height) +{ + if (g_extern.system.pitch == 0) // SNES semantics + return ((height == 448) || (height == 478)) ? 1024 : 2048; + else + return g_extern.system.pitch; +} + static void video_cached_frame(void); static void take_screenshot(void) { @@ -100,7 +108,7 @@ static void take_screenshot(void) ret = screenshot_dump(g_settings.screenshot_directory, data, width, height, - (height == 448 || height == 478) ? 1024 : 2048); + lines_to_pitch(height)); } const char *msg = NULL; @@ -126,6 +134,25 @@ static void take_screenshot(void) msg_queue_push(g_extern.msg_queue, msg, 1, 180); } + +static inline void adjust_crop(const uint16_t **data, unsigned *height) +{ + // Rather SNES specific. + if (g_settings.video.crop_overscan) + { + if (*height == 239) + { + *data += 7 * 1024; // Skip 7 top scanlines. + *height = 224; + } + else if (*height == 478) + { + *data += 15 * 512; // Skip 15 top scanlines. + *height = 448; + } + } +} + // libsnes: 0.065 // Format received is 16-bit 0RRRRRGGGGGBBBBB static void video_frame(const uint16_t *data, unsigned width, unsigned height) @@ -133,19 +160,7 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height) if (!g_extern.video_active) return; - if (g_settings.video.crop_overscan) - { - if (height == 239) - { - data += 7 * 1024; // Skip 7 top scanlines. - height = 224; - } - else if (height == 478) - { - data += 15 * 512; // Skip 15 top scanlines. - height = 448; - } - } + adjust_crop(&data, &height); // Slightly messy code, // but we really need to do processing before blocking on VSync for best possible scheduling. @@ -154,7 +169,7 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height) { struct ffemu_video_data ffemu_data = { .data = data, - .pitch = height == 448 || height == 478 ? 1024 : 2048, + .pitch = lines_to_pitch(height), .width = width, .height = height, }; @@ -171,7 +186,7 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height) unsigned oheight = height; g_extern.filter.psize(&owidth, &oheight); g_extern.filter.prender(g_extern.filter.colormap, g_extern.filter.buffer, - g_extern.filter.pitch, data, (height == 448 || height == 478) ? 1024 : 2048, width, height); + g_extern.filter.pitch, data, lines_to_pitch(height), width, height); #ifdef HAVE_FFMPEG if (g_extern.recording && g_settings.video.post_filter_record) @@ -189,10 +204,10 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height) if (!driver.video->frame(driver.video_data, g_extern.filter.buffer, owidth, oheight, g_extern.filter.pitch, msg)) g_extern.video_active = false; } - else if (!driver.video->frame(driver.video_data, data, width, height, (height == 448 || height == 478) ? 1024 : 2048, msg)) + else if (!driver.video->frame(driver.video_data, data, width, height, lines_to_pitch(height), msg)) g_extern.video_active = false; #else - if (!driver.video->frame(driver.video_data, data, width, height, (height == 448 || height == 478) ? 1024 : 2048, msg)) + if (!driver.video->frame(driver.video_data, data, width, height, lines_to_pitch(height), msg)) g_extern.video_active = false; #endif @@ -457,6 +472,8 @@ static void print_help(void) static void set_basename(const char *path) { + strlcpy(g_extern.system.fullpath, path, sizeof(g_extern.system.fullpath)); + strlcpy(g_extern.basename, path, sizeof(g_extern.basename)); char *dst = strrchr(g_extern.basename, '.'); if (dst) @@ -921,10 +938,10 @@ static void init_recording(void) struct ffemu_rational ntsc_fps = {60000, 1000}; struct ffemu_rational pal_fps = {50000, 1000}; struct ffemu_params params = { - .out_width = 256, - .out_height = 224, - .fb_width = 512, - .fb_height = 512, + .out_width = g_extern.system.geom.base_width, + .out_height = g_extern.system.geom.base_height, + .fb_width = g_extern.system.geom.max_width, + .fb_height = g_extern.system.geom.max_height, .channels = 2, .samplerate = 32000, .filename = g_extern.record_path, @@ -939,8 +956,8 @@ static void init_recording(void) } else if (g_settings.video.hires_record) { - params.out_width = 512; - params.out_height = 448; + params.out_width *= 2; + params.out_height *= 2; } if (g_settings.video.force_aspect) @@ -953,8 +970,8 @@ static void init_recording(void) g_extern.filter.psize(¶ms.out_width, ¶ms.out_height); params.rgb32 = true; - unsigned max_width = 512; - unsigned max_height = 512; + unsigned max_width = params.fb_width; + unsigned max_height = params.fb_height; g_extern.filter.psize(&max_width, &max_height); params.fb_width = next_pow2(max_width); params.fb_height = next_pow2(max_height); diff --git a/ssnes.cfg b/ssnes.cfg index 376899d561..c44d25adc8 100644 --- a/ssnes.cfg +++ b/ssnes.cfg @@ -24,9 +24,6 @@ # video_xscale = 3.0 # video_yscale = 3.0 -# Base size for windowed mode calculations. Generally 224 for SNES, but can be modified. -# video_base_size = 224 - # Fullscreen resolution. Resolution of 0 uses the resolution of the desktop. # video_fullscreen_x = 0 # video_fullscreen_y = 0