mirror of
https://github.com/libretro/RetroArch
synced 2025-03-20 10:20:51 +00:00
Start implementing some minor extensions to libsnes.
This commit is contained in:
parent
fad89f9d2d
commit
0522a2d6a0
@ -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
|
||||
|
25
driver.c
25
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,
|
||||
|
86
dynamic.c
86
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,30 +183,23 @@ 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
|
||||
// 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)
|
||||
{
|
||||
void *sym = dlsym(lib, "snes_init");
|
||||
function_t sym = dylib_proc(NULL, "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);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!*g_settings.libsnes)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
18
general.h
18
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--;
|
||||
|
18
libsnes.hpp
18
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);
|
||||
|
||||
|
@ -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");
|
||||
|
||||
|
69
ssnes.c
69
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);
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user