mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 17:28:49 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
336adb9f85 | ||
|
|
7053006e77 | ||
|
|
bbf4804d12 | ||
|
|
7c16424ba3 | ||
|
|
de9dc686ac | ||
|
|
069e9b574f | ||
|
|
d5319a879a | ||
|
|
f64880aee1 | ||
|
|
fed1857918 | ||
|
|
9aeb40155e | ||
|
|
e49aeb1071 | ||
|
|
13f8ec6f3b | ||
|
|
f75c6df270 | ||
|
|
60b0afce95 | ||
|
|
f24653768f | ||
|
|
f135972a6b | ||
|
|
42c68e21a2 | ||
|
|
e8865060ca | ||
|
|
b07827a856 |
No files matched your search
@@ -113,7 +113,7 @@ ifneq ($(V),1)
|
||||
Q := @
|
||||
endif
|
||||
|
||||
CFLAGS = -Wall -O3 -g -std=gnu99 -I.
|
||||
CFLAGS += -Wall -O3 -g -std=gnu99 -I.
|
||||
|
||||
all: $(TARGET) config.mk
|
||||
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ clean:
|
||||
rm -f tools/*.o
|
||||
|
||||
dist: all
|
||||
zip -r ssnes-win32-0.3.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll freetype6.dll xaudio-c.dll rsound.dll pthreadGC2.dll README.win32.txt $(JTARGET)
|
||||
zip -r ssnes-win32-0.3.1.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll freetype6.dll xaudio-c.dll rsound.dll pthreadGC2.dll README.win32.txt $(JTARGET)
|
||||
|
||||
libs:
|
||||
wget https://github.com/downloads/Themaister/SSNES/SSNES-win32-libs.zip --no-check-certificate
|
||||
|
||||
+2
-2
@@ -94,8 +94,8 @@ static const float yscale = 3.0; // Real y res = 224 * yscale
|
||||
|
||||
// Fullscreen
|
||||
static const bool fullscreen = false; // To start in Fullscreen or not
|
||||
static const unsigned fullscreen_x = 1280;
|
||||
static const unsigned fullscreen_y = 720;
|
||||
static const unsigned fullscreen_x = 0; // Fullscreen resolution. A value of 0 uses the desktop resolution.
|
||||
static const unsigned fullscreen_y = 0;
|
||||
|
||||
// Video VSYNC (recommended)
|
||||
static const bool vsync = true;
|
||||
|
||||
@@ -41,6 +41,7 @@ enum
|
||||
SSNES_PAUSE_TOGGLE,
|
||||
};
|
||||
|
||||
|
||||
struct snes_keybind
|
||||
{
|
||||
int id;
|
||||
@@ -78,7 +79,6 @@ typedef struct audio_driver
|
||||
|
||||
#define AXIS_NEG_GET(x) (((uint32_t)(x) >> 16) & 0xFFFFU)
|
||||
#define AXIS_POS_GET(x) ((uint32_t)(x) & 0xFFFFU)
|
||||
#define AXIS_NONE (0xFFFFFFFFU)
|
||||
|
||||
#define NO_BTN ((uint16_t)0xFFFFU) // I hope no joypad will ever have this many buttons ... ;)
|
||||
|
||||
|
||||
@@ -44,6 +44,14 @@
|
||||
#define MAX_BINDS 25 // Needs to be increased every time there are new binds added.
|
||||
#define SSNES_NO_JOYPAD 0xFFFF
|
||||
|
||||
enum ssnes_shader_type
|
||||
{
|
||||
SSNES_SHADER_CG,
|
||||
SSNES_SHADER_BSNES,
|
||||
SSNES_SHADER_AUTO,
|
||||
SSNES_SHADER_NONE
|
||||
};
|
||||
|
||||
// All config related settings go here.
|
||||
struct settings
|
||||
{
|
||||
@@ -62,6 +70,7 @@ struct settings
|
||||
char cg_shader_path[256];
|
||||
char bsnes_shader_path[256];
|
||||
unsigned filter;
|
||||
enum ssnes_shader_type shader_type;
|
||||
|
||||
char font_path[256];
|
||||
unsigned font_size;
|
||||
@@ -111,6 +120,7 @@ enum ssnes_game_type
|
||||
SSNES_CART_SUFAMI,
|
||||
};
|
||||
|
||||
|
||||
// All run-time- / command line flag-related globals go here.
|
||||
struct global
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <sys/time.h>
|
||||
#include <string.h>
|
||||
#include "general.h"
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
@@ -82,6 +83,8 @@ typedef struct gl
|
||||
bool quitting;
|
||||
bool keep_aspect;
|
||||
|
||||
unsigned full_x, full_y;
|
||||
|
||||
unsigned win_width;
|
||||
unsigned win_height;
|
||||
unsigned vp_width;
|
||||
@@ -99,20 +102,46 @@ typedef struct gl
|
||||
} gl_t;
|
||||
|
||||
////////////////// Shaders
|
||||
static inline bool gl_shader_init(void)
|
||||
static bool gl_shader_init(void)
|
||||
{
|
||||
if (strlen(g_settings.video.cg_shader_path) > 0 && strlen(g_settings.video.bsnes_shader_path) > 0)
|
||||
SSNES_WARN("Both Cg and bSNES XML shader are defined in config file. Cg shader will be selected by default.\n");
|
||||
switch (g_settings.video.shader_type)
|
||||
{
|
||||
case SSNES_SHADER_AUTO:
|
||||
{
|
||||
if (strlen(g_settings.video.cg_shader_path) > 0 && strlen(g_settings.video.bsnes_shader_path) > 0)
|
||||
SSNES_WARN("Both Cg and bSNES XML shader are defined in config file. Cg shader will be selected by default.\n");
|
||||
|
||||
#ifdef HAVE_CG
|
||||
if (strlen(g_settings.video.cg_shader_path) > 0)
|
||||
return gl_cg_init(g_settings.video.cg_shader_path);
|
||||
if (strlen(g_settings.video.cg_shader_path) > 0)
|
||||
return gl_cg_init(g_settings.video.cg_shader_path);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_XML
|
||||
if (strlen(g_settings.video.bsnes_shader_path) > 0)
|
||||
return gl_glsl_init(g_settings.video.bsnes_shader_path);
|
||||
if (strlen(g_settings.video.bsnes_shader_path) > 0)
|
||||
return gl_glsl_init(g_settings.video.bsnes_shader_path);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CG
|
||||
case SSNES_SHADER_CG:
|
||||
{
|
||||
return gl_cg_init(g_settings.video.cg_shader_path);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_XML
|
||||
case SSNES_SHADER_BSNES:
|
||||
{
|
||||
return gl_glsl_init(g_settings.video.bsnes_shader_path);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -266,6 +295,7 @@ static void gl_render_msg(gl_t *gl, const char *msg)
|
||||
GL_UNSIGNED_BYTE, head->output);
|
||||
|
||||
head = head->next;
|
||||
glFlush();
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
}
|
||||
font_renderer_free_output(&out);
|
||||
@@ -395,6 +425,8 @@ static bool gl_frame(void *data, const uint16_t* frame, unsigned width, unsigned
|
||||
glTexSubImage2D(GL_TEXTURE_2D,
|
||||
0, 0, 0, width, height, GL_BGRA,
|
||||
GL_UNSIGNED_SHORT_1_5_5_5_REV, frame);
|
||||
|
||||
glFlush();
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
|
||||
if (msg)
|
||||
@@ -442,6 +474,12 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0)
|
||||
return NULL;
|
||||
|
||||
const SDL_VideoInfo *video_info = SDL_GetVideoInfo();
|
||||
assert(video_info);
|
||||
unsigned full_x = video_info->current_w;
|
||||
unsigned full_y = video_info->current_h;
|
||||
SSNES_LOG("Detecting desktop resolution %ux%u.\n", full_x, full_y);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, video->vsync ? 1 : 0);
|
||||
|
||||
@@ -462,8 +500,21 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i
|
||||
if (!gl)
|
||||
return NULL;
|
||||
|
||||
gl->win_width = video->width;
|
||||
gl->win_height = video->height;
|
||||
gl->full_x = full_x;
|
||||
gl->full_y = full_y;
|
||||
|
||||
if (video->fullscreen)
|
||||
{
|
||||
gl->win_width = video->width ? video->width : gl->full_x;
|
||||
gl->win_height = video->height ? video->height : gl->full_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
gl->win_width = video->width;
|
||||
gl->win_height = video->height;
|
||||
}
|
||||
SSNES_LOG("GL: Using resolution %ux%u\n", gl->win_width, gl->win_height);
|
||||
|
||||
gl->vsync = video->vsync;
|
||||
gl->keep_aspect = video->force_aspect;
|
||||
set_viewport(gl);
|
||||
|
||||
+27
-1
@@ -25,7 +25,6 @@
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <OpenGL/gl.h>
|
||||
#include <OpenGL/glext.h>
|
||||
#else
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
@@ -42,6 +41,25 @@
|
||||
|
||||
#include "gl_common.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define pglCreateProgram glCreateProgram
|
||||
#define pglUseProgram glUseProgram
|
||||
#define pglCreateShader glCreateShader
|
||||
#define pglDeleteShader glDeleteShader
|
||||
#define pglShaderSource glShaderSource
|
||||
#define pglCompileShader glCompileShader
|
||||
#define pglAttachShader glAttachShader
|
||||
#define pglDetachShader glDetachShader
|
||||
#define pglLinkProgram glLinkProgram
|
||||
#define pglGetUniformLocation glGetUniformLocation
|
||||
#define pglUniform1i glUniform1i
|
||||
#define pglUniform2fv glUniform2fv
|
||||
#define pglUniform4fv glUniform4fv
|
||||
#define pglGetShaderiv glGetShaderiv
|
||||
#define pglGetShaderInfoLog glGetShaderInfoLog
|
||||
#define pglGetProgramiv glGetProgramiv
|
||||
#define pglGetProgramInfoLog glGetProgramInfoLog
|
||||
#else
|
||||
static PFNGLCREATEPROGRAMPROC pglCreateProgram = NULL;
|
||||
static PFNGLUSEPROGRAMPROC pglUseProgram = NULL;
|
||||
static PFNGLCREATESHADERPROC pglCreateShader = NULL;
|
||||
@@ -59,6 +77,7 @@ static PFNGLGETSHADERIVPROC pglGetShaderiv = NULL;
|
||||
static PFNGLGETSHADERINFOLOGPROC pglGetShaderInfoLog = NULL;
|
||||
static PFNGLGETPROGRAMIVPROC pglGetProgramiv = NULL;
|
||||
static PFNGLGETPROGRAMINFOLOGPROC pglGetProgramInfoLog = NULL;
|
||||
#endif
|
||||
|
||||
static bool glsl_enable = false;
|
||||
static GLuint gl_program;
|
||||
@@ -180,6 +199,8 @@ static void print_linker_log(GLuint obj)
|
||||
|
||||
bool gl_glsl_init(const char *path)
|
||||
{
|
||||
|
||||
#ifndef __APPLE__
|
||||
// Load shader functions.
|
||||
pglCreateProgram = SDL_GL_GetProcAddress("glCreateProgram");
|
||||
pglUseProgram = SDL_GL_GetProcAddress("glUseProgram");
|
||||
@@ -198,13 +219,18 @@ bool gl_glsl_init(const char *path)
|
||||
pglGetShaderInfoLog = SDL_GL_GetProcAddress("glGetShaderInfoLog");
|
||||
pglGetProgramiv = SDL_GL_GetProcAddress("glGetProgramiv");
|
||||
pglGetProgramInfoLog = SDL_GL_GetProcAddress("glGetProgramInfoLog");
|
||||
#endif
|
||||
|
||||
SSNES_LOG("Checking GLSL shader support ...\n");
|
||||
#ifdef __APPLE__
|
||||
const bool shader_support = true;
|
||||
#else
|
||||
bool shader_support = pglCreateProgram && pglUseProgram && pglCreateShader
|
||||
&& pglDeleteShader && pglShaderSource && pglCompileShader && pglAttachShader
|
||||
&& pglDetachShader && pglLinkProgram && pglGetUniformLocation
|
||||
&& pglUniform1i && pglUniform2fv && pglUniform4fv
|
||||
&& pglGetShaderiv && pglGetShaderInfoLog && pglGetProgramiv && pglGetProgramInfoLog;
|
||||
#endif
|
||||
|
||||
if (!shader_support)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
Author: Themaister
|
||||
License: Public domain
|
||||
*/
|
||||
|
||||
|
||||
/* Default Vertex shader */
|
||||
void main_vertex
|
||||
(
|
||||
float4 position : POSITION,
|
||||
float4 color : COLOR,
|
||||
float2 texCoord : TEXCOORD0,
|
||||
|
||||
uniform float4x4 modelViewProj,
|
||||
|
||||
out float4 oPosition : POSITION,
|
||||
out float4 oColor : COLOR,
|
||||
out float2 otexCoord : TEXCOORD
|
||||
)
|
||||
{
|
||||
oPosition = mul(modelViewProj, position);
|
||||
oColor = color;
|
||||
otexCoord = texCoord;
|
||||
}
|
||||
|
||||
|
||||
struct output
|
||||
{
|
||||
float4 color : COLOR;
|
||||
};
|
||||
|
||||
|
||||
struct input
|
||||
{
|
||||
float2 video_size;
|
||||
float2 texture_size;
|
||||
float2 output_size;
|
||||
};
|
||||
|
||||
|
||||
output main_fragment (float2 tex : TEXCOORD0, uniform input IN, uniform sampler2D s0 : TEXUNIT0)
|
||||
{
|
||||
float2 texsize = IN.texture_size;
|
||||
const float scale_factor = 1.0;
|
||||
float2 delta = 0.5 / (texsize * scale_factor);
|
||||
float dx = delta.x;
|
||||
float dy = delta.y;
|
||||
|
||||
float3 c00 = tex2D(s0, tex + float2(-dx, -dy)).xyz;
|
||||
float3 c01 = tex2D(s0, tex + float2(-dx, 0)).xyz;
|
||||
float3 c02 = tex2D(s0, tex + float2(-dx, dy)).xyz;
|
||||
float3 c10 = tex2D(s0, tex + float2(0, -dy)).xyz;
|
||||
float3 c11 = tex2D(s0, tex + float2(0, 0)).xyz;
|
||||
float3 c12 = tex2D(s0, tex + float2(0, dy)).xyz;
|
||||
float3 c20 = tex2D(s0, tex + float2(dx, -dy)).xyz;
|
||||
float3 c21 = tex2D(s0, tex + float2(dx, 0)).xyz;
|
||||
float3 c22 = tex2D(s0, tex + float2(dx, dy)).xyz;
|
||||
|
||||
output OUT;
|
||||
|
||||
float3 first = lerp(c00, c20, frac(scale_factor * tex.x * texsize.x + 0.5));
|
||||
float3 second = lerp(c02, c22, frac(scale_factor * tex.x * texsize.x + 0.5));
|
||||
|
||||
float3 mid_horiz = lerp(c01, c21, frac(scale_factor * tex.x * texsize.x + 0.5));
|
||||
float3 mid_vert = lerp(c10, c12, frac(scale_factor * tex.y * texsize.y + 0.5));
|
||||
|
||||
float3 res = lerp(first, second, frac(scale_factor * tex.y * texsize.y + 0.5));
|
||||
OUT.color = float4(0.28 * (res + mid_horiz + mid_vert) + 4.7 * abs(res - lerp(mid_horiz, mid_vert, 0.5)), 1.0);
|
||||
|
||||
return OUT;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Author: Themaister
|
||||
License: Public domain
|
||||
*/
|
||||
|
||||
/* Default Vertex shader */
|
||||
void main_vertex
|
||||
(
|
||||
float4 position : POSITION,
|
||||
float4 color : COLOR,
|
||||
float2 texCoord : TEXCOORD0,
|
||||
|
||||
uniform float4x4 modelViewProj,
|
||||
|
||||
out float4 oPosition : POSITION,
|
||||
out float4 oColor : COLOR,
|
||||
out float2 otexCoord : TEXCOORD
|
||||
)
|
||||
{
|
||||
oPosition = mul(modelViewProj, position);
|
||||
oColor = color;
|
||||
otexCoord = texCoord;
|
||||
}
|
||||
|
||||
struct output
|
||||
{
|
||||
float4 color : COLOR;
|
||||
};
|
||||
|
||||
struct input
|
||||
{
|
||||
float2 video_size;
|
||||
float2 texture_size;
|
||||
float2 output_size;
|
||||
};
|
||||
|
||||
float4 compress(float4 in_color, float threshold, float ratio)
|
||||
{
|
||||
float4 diff = in_color - float4(threshold);
|
||||
diff = clamp(diff, 0.0, 100.0);
|
||||
return in_color - (diff * (1.0 - 1.0/ratio));
|
||||
}
|
||||
|
||||
output main_fragment (float2 tex : TEXCOORD0, uniform input IN, uniform sampler2D s0 : TEXUNIT0)
|
||||
{
|
||||
float2 texsize = IN.texture_size;
|
||||
const float scale_factor = 1.0;
|
||||
float2 delta = 0.5 / (texsize * scale_factor);
|
||||
float dx = delta.x;
|
||||
float dy = delta.y;
|
||||
|
||||
float3 c00 = tex2D(s0, tex + float2(-dx, -dy)).xyz;
|
||||
float3 c01 = tex2D(s0, tex + float2(-dx, 0)).xyz;
|
||||
float3 c02 = tex2D(s0, tex + float2(-dx, dy)).xyz;
|
||||
float3 c10 = tex2D(s0, tex + float2(0, -dy)).xyz;
|
||||
float3 c11 = tex2D(s0, tex + float2(0, 0)).xyz;
|
||||
float3 c12 = tex2D(s0, tex + float2(0, dy)).xyz;
|
||||
float3 c20 = tex2D(s0, tex + float2(dx, -dy)).xyz;
|
||||
float3 c21 = tex2D(s0, tex + float2(dx, 0)).xyz;
|
||||
float3 c22 = tex2D(s0, tex + float2(dx, dy)).xyz;
|
||||
|
||||
output OUT;
|
||||
|
||||
float3 first = lerp(c00, c20, frac(scale_factor * tex.x * texsize.x + 0.5));
|
||||
float3 second = lerp(c02, c22, frac(scale_factor * tex.x * texsize.x + 0.5));
|
||||
|
||||
float3 mid_horiz = lerp(c01, c21, frac(scale_factor * tex.x * texsize.x + 0.5));
|
||||
float3 mid_vert = lerp(c10, c12, frac(scale_factor * tex.y * texsize.y + 0.5));
|
||||
|
||||
float3 res = lerp(first, second, frac(scale_factor * tex.y * texsize.y + 0.5));
|
||||
float4 final = float4(0.26 * (res + mid_horiz + mid_vert) + 3.5 * abs(res - lerp(mid_horiz, mid_vert, 0.5)), 1.0);
|
||||
OUT.color = compress(final, 0.8, 5.0);
|
||||
|
||||
return OUT;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <stdlib.h>
|
||||
#include "general.h"
|
||||
#include "dynamic.h"
|
||||
#include <assert.h>
|
||||
|
||||
// CRC32 implementation taken from BSNES source :)
|
||||
|
||||
@@ -87,6 +88,17 @@ struct bsv_movie
|
||||
{
|
||||
FILE *file;
|
||||
uint8_t *state;
|
||||
|
||||
long *frame_pos; // A ring buffer keeping track of many key presses were requested per frame.
|
||||
size_t frame_mask;
|
||||
uint16_t current_frame_count;
|
||||
size_t frame_ptr;
|
||||
|
||||
bool playback;
|
||||
unsigned min_file_pos;
|
||||
|
||||
bool first_rewind;
|
||||
bool did_rewind;
|
||||
};
|
||||
|
||||
#define BSV_MAGIC 0x42535631
|
||||
@@ -127,6 +139,7 @@ static inline uint16_t swap_if_big16(uint16_t val)
|
||||
|
||||
static bool init_playback(bsv_movie_t *handle, const char *path)
|
||||
{
|
||||
handle->playback = true;
|
||||
handle->file = fopen(path, "rb");
|
||||
if (!handle->file)
|
||||
{
|
||||
@@ -158,6 +171,8 @@ static bool init_playback(bsv_movie_t *handle, const char *path)
|
||||
if (!handle->state)
|
||||
return false;
|
||||
|
||||
handle->min_file_pos = sizeof(header) + state_size;
|
||||
|
||||
if (fread(handle->state, 1, state_size, handle->file) != state_size)
|
||||
{
|
||||
SSNES_ERR("Couldn't read state from movie.\n");
|
||||
@@ -196,6 +211,8 @@ static bool init_record(bsv_movie_t *handle, const char *path)
|
||||
if (!handle->state)
|
||||
return false;
|
||||
|
||||
handle->min_file_pos = sizeof(header) + state_size;
|
||||
|
||||
psnes_serialize(handle->state, state_size);
|
||||
fwrite(handle->state, 1, state_size, handle->file);
|
||||
return true;
|
||||
@@ -208,6 +225,7 @@ void bsv_movie_free(bsv_movie_t *handle)
|
||||
if (handle->file)
|
||||
fclose(handle->file);
|
||||
free(handle->state);
|
||||
free(handle->frame_pos);
|
||||
free(handle);
|
||||
}
|
||||
}
|
||||
@@ -240,9 +258,58 @@ bsv_movie_t *bsv_movie_init(const char *path, enum ssnes_movie_type type)
|
||||
else if (!init_record(handle, path))
|
||||
goto error;
|
||||
|
||||
if (!(handle->frame_pos = calloc((1 << 20), sizeof(long)))) // Just pick something really large :D
|
||||
goto error;
|
||||
handle->frame_mask = (1 << 20) - 1;
|
||||
|
||||
return handle;
|
||||
|
||||
error:
|
||||
bsv_movie_free(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void bsv_movie_set_frame_start(bsv_movie_t *handle)
|
||||
{
|
||||
//fprintf(stderr, "Starting frame: %u, Pos: %ld\n", (unsigned)handle->frame_ptr, ftell(handle->file));
|
||||
handle->frame_pos[handle->frame_ptr] = ftell(handle->file);
|
||||
|
||||
}
|
||||
|
||||
void bsv_movie_set_frame_end(bsv_movie_t *handle)
|
||||
{
|
||||
//fprintf(stderr, "Frame++\n");
|
||||
handle->frame_ptr = (handle->frame_ptr + 1) & handle->frame_mask;
|
||||
|
||||
handle->first_rewind = !handle->did_rewind;
|
||||
handle->did_rewind = false;
|
||||
}
|
||||
|
||||
void bsv_movie_frame_rewind(bsv_movie_t *handle)
|
||||
{
|
||||
//fprintf(stderr, "Frame--\n");
|
||||
|
||||
handle->did_rewind = true;
|
||||
if (handle->frame_ptr <= 1)
|
||||
{
|
||||
handle->frame_ptr = 0;
|
||||
fseek(handle->file, handle->min_file_pos, SEEK_SET);
|
||||
}
|
||||
else
|
||||
{
|
||||
handle->frame_ptr = (handle->frame_ptr - (handle->first_rewind ? 1 : 2)) & handle->frame_mask;
|
||||
fseek(handle->file, handle->frame_pos[handle->frame_ptr], SEEK_SET);
|
||||
}
|
||||
|
||||
if (ftell(handle->file) <= (long)handle->min_file_pos)
|
||||
{
|
||||
if (!handle->playback)
|
||||
{
|
||||
fseek(handle->file, 4 * sizeof(uint32_t), SEEK_SET);
|
||||
psnes_serialize(handle->state, psnes_serialize_size());
|
||||
fwrite(handle->state, 1, psnes_serialize_size(), handle->file);
|
||||
}
|
||||
else
|
||||
fseek(handle->file, handle->min_file_pos, SEEK_SET);
|
||||
}
|
||||
}
|
||||
@@ -37,12 +37,14 @@ bool bsv_movie_get_input(bsv_movie_t *handle, int16_t *input);
|
||||
// Recording
|
||||
void bsv_movie_set_input(bsv_movie_t *handle, int16_t input);
|
||||
|
||||
// Used for rewinding while playback/record.
|
||||
void bsv_movie_set_frame_start(bsv_movie_t *handle); // Debugging purposes.
|
||||
void bsv_movie_set_frame_end(bsv_movie_t *handle);
|
||||
void bsv_movie_frame_rewind(bsv_movie_t *handle);
|
||||
|
||||
void bsv_movie_free(bsv_movie_t *handle);
|
||||
|
||||
uint32_t crc32_calculate(const uint8_t *data, unsigned length);
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -408,25 +408,33 @@ static bool send_chunk(netplay_t *handle)
|
||||
return true;
|
||||
}
|
||||
|
||||
#define MAX_RETRIES 16
|
||||
#define MAX_RETRIES 64
|
||||
#define RETRY_MS 500
|
||||
|
||||
static int poll_input(netplay_t *handle, bool block)
|
||||
{
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(handle->udp_fd, &fds);
|
||||
FD_SET(handle->fd, &fds);
|
||||
int max_fd = (handle->fd > handle->udp_fd ? handle->fd : handle->udp_fd) + 1;
|
||||
|
||||
struct timeval tv = {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = block ? 500000 : 0
|
||||
.tv_usec = block ? (RETRY_MS * 1000) : 0
|
||||
};
|
||||
|
||||
int i = 0;
|
||||
do
|
||||
{
|
||||
if (select(handle->udp_fd + 1, &fds, NULL, NULL, &tv) < 0)
|
||||
if (select(max_fd, &fds, NULL, NULL, &tv) < 0)
|
||||
return -1;
|
||||
|
||||
// Somewhat hacky, but we aren't using the TCP connection for anything useful atm.
|
||||
// Will probably add some proper messaging system here later.
|
||||
if (FD_ISSET(handle->fd, &fds))
|
||||
return -1;
|
||||
|
||||
if (FD_ISSET(handle->udp_fd, &fds))
|
||||
return 1;
|
||||
|
||||
@@ -434,10 +442,18 @@ static int poll_input(netplay_t *handle, bool block)
|
||||
{
|
||||
SSNES_WARN("Netplay connection hung up. Will continue without netplay.\n");
|
||||
handle->has_connection = false;
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
} while (i++ < MAX_RETRIES && block);
|
||||
if (block)
|
||||
SSNES_LOG("Network lag of %d ms, resending packet... Attempt %d of %d ...\n", RETRY_MS, i, MAX_RETRIES);
|
||||
|
||||
// Seems to be necessary on Win32.
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(handle->udp_fd, &fds);
|
||||
FD_SET(handle->fd, &fds);
|
||||
|
||||
} while ((i++ < MAX_RETRIES) && block);
|
||||
|
||||
if (block)
|
||||
return -1;
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
. qb/qb.params.sh
|
||||
|
||||
PACKAGE_NAME=ssnes
|
||||
PACKAGE_VERSION=0.3
|
||||
PACKAGE_VERSION=0.3.1
|
||||
|
||||
# Adds a command line opt to ./configure --help
|
||||
# $1: Variable (HAVE_ALSA, HAVE_OSS, etc)
|
||||
|
||||
+2
-2
@@ -50,7 +50,7 @@ check_lib()
|
||||
|
||||
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
|
||||
$CC -o $TEMP_EXE $TEMP_C $INCLUDE_DIRS $LIBRARY_DIRS $CFLAGS $LDFLAGS $extralibs $2 2>/dev/null >/dev/null && answer=yes && eval HAVE_$1=yes
|
||||
|
||||
echo $answer
|
||||
|
||||
@@ -75,7 +75,7 @@ check_lib_cxx()
|
||||
|
||||
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
|
||||
$CXX -o $TEMP_EXE $TEMP_CXX $INCLUDE_DIRS $LIBRARY_DIRS $CFLAGS $LDFLAGS $extralibs $2 2>/dev/null >/dev/null && answer=yes && eval HAVE_$1=yes
|
||||
|
||||
echo $answer
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ struct state_manager
|
||||
size_t top_ptr;
|
||||
size_t bottom_ptr;
|
||||
size_t state_size;
|
||||
bool first_pop;
|
||||
};
|
||||
|
||||
state_manager_t *state_manager_new(size_t state_size, size_t buffer_size, void *init_buffer)
|
||||
@@ -77,6 +78,11 @@ void state_manager_free(state_manager_t *state)
|
||||
bool state_manager_pop(state_manager_t *state, void **data)
|
||||
{
|
||||
*data = state->tmp_state;
|
||||
if (state->first_pop)
|
||||
{
|
||||
state->first_pop = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (state->top_ptr == 0)
|
||||
state->top_ptr = state->buf_size - 1;
|
||||
@@ -126,7 +132,7 @@ static void generate_delta(state_manager_t *state, const void *data)
|
||||
const uint32_t *new_state = data;
|
||||
|
||||
state->buffer[state->top_ptr++] = 0; // For each separate delta, we have a 0 value sentinel in between.
|
||||
if (state->top_ptr == state->bottom_ptr) // Check if top_ptr and bottom_ptr crossed eachother, which means we need to delete old cruft.
|
||||
if (state->top_ptr == state->bottom_ptr) // Check if top_ptr and bottom_ptr crossed each other, which means we need to delete old cruft.
|
||||
crossed = true;
|
||||
|
||||
for (uint64_t i = 0; i < state->state_size; i++)
|
||||
@@ -156,6 +162,7 @@ bool state_manager_push(state_manager_t *state, const void *data)
|
||||
{
|
||||
generate_delta(state, data);
|
||||
memcpy(state->tmp_state, data, state->state_size * sizeof(uint32_t));
|
||||
state->first_pop = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+61
-23
@@ -107,6 +107,7 @@ static void set_defaults(void)
|
||||
#ifdef HAVE_FILTER
|
||||
g_settings.video.filter = FILTER_NONE;
|
||||
#endif
|
||||
g_settings.video.shader_type = SSNES_SHADER_AUTO;
|
||||
|
||||
#ifdef HAVE_FREETYPE
|
||||
g_settings.video.font_size = font_size;
|
||||
@@ -320,6 +321,22 @@ static void parse_config_file(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_CG) || defined(HAVE_XML)
|
||||
if (config_get_string(conf, "video_shader_type", &tmp_str))
|
||||
{
|
||||
if (strcmp("cg", tmp_str) == 0)
|
||||
g_settings.video.shader_type = SSNES_SHADER_CG;
|
||||
else if (strcmp("bsnes", tmp_str) == 0)
|
||||
g_settings.video.shader_type = SSNES_SHADER_BSNES;
|
||||
else if (strcmp("auto", tmp_str) == 0)
|
||||
g_settings.video.shader_type = SSNES_SHADER_AUTO;
|
||||
else if (strcmp("none", tmp_str) == 0)
|
||||
g_settings.video.shader_type = SSNES_SHADER_NONE;
|
||||
|
||||
free(tmp_str);
|
||||
}
|
||||
#endif
|
||||
|
||||
CONFIG_GET_DOUBLE(input.axis_threshold, "input_axis_threshold");
|
||||
CONFIG_GET_BOOL(input.netplay_client_swap_input, "netplay_client_swap_input");
|
||||
CONFIG_GET_INT(input.joypad_map[0], "input_player1_joypad_index");
|
||||
@@ -405,6 +422,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
|
||||
DECLARE_BIND(rate_step_down, SSNES_AUDIO_INPUT_RATE_MINUS)
|
||||
DECLARE_BIND(rewind, SSNES_REWIND)
|
||||
DECLARE_BIND(movie_record_toggle, SSNES_MOVIE_RECORD_TOGGLE)
|
||||
DECLARE_BIND(pause_toggle, SSNES_PAUSE_TOGGLE)
|
||||
},
|
||||
{
|
||||
DECLARE_BIND(player2_a, SNES_DEVICE_ID_JOYPAD_A)
|
||||
@@ -430,6 +448,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
|
||||
DECLARE_BIND(rate_step_down, SSNES_AUDIO_INPUT_RATE_MINUS)
|
||||
DECLARE_BIND(rewind, SSNES_REWIND)
|
||||
DECLARE_BIND(movie_record_toggle, SSNES_MOVIE_RECORD_TOGGLE)
|
||||
DECLARE_BIND(pause_toggle, SSNES_PAUSE_TOGGLE)
|
||||
},
|
||||
{
|
||||
DECLARE_BIND(player3_a, SNES_DEVICE_ID_JOYPAD_A)
|
||||
@@ -455,6 +474,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
|
||||
DECLARE_BIND(rate_step_down, SSNES_AUDIO_INPUT_RATE_MINUS)
|
||||
DECLARE_BIND(rewind, SSNES_REWIND)
|
||||
DECLARE_BIND(movie_record_toggle, SSNES_MOVIE_RECORD_TOGGLE)
|
||||
DECLARE_BIND(pause_toggle, SSNES_PAUSE_TOGGLE)
|
||||
},
|
||||
{
|
||||
DECLARE_BIND(player4_a, SNES_DEVICE_ID_JOYPAD_A)
|
||||
@@ -480,6 +500,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
|
||||
DECLARE_BIND(rate_step_down, SSNES_AUDIO_INPUT_RATE_MINUS)
|
||||
DECLARE_BIND(rewind, SSNES_REWIND)
|
||||
DECLARE_BIND(movie_record_toggle, SSNES_MOVIE_RECORD_TOGGLE)
|
||||
DECLARE_BIND(pause_toggle, SSNES_PAUSE_TOGGLE)
|
||||
},
|
||||
{
|
||||
DECLARE_BIND(player5_a, SNES_DEVICE_ID_JOYPAD_A)
|
||||
@@ -505,6 +526,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
|
||||
DECLARE_BIND(rate_step_down, SSNES_AUDIO_INPUT_RATE_MINUS)
|
||||
DECLARE_BIND(rewind, SSNES_REWIND)
|
||||
DECLARE_BIND(movie_record_toggle, SSNES_MOVIE_RECORD_TOGGLE)
|
||||
DECLARE_BIND(pause_toggle, SSNES_PAUSE_TOGGLE)
|
||||
},
|
||||
};
|
||||
|
||||
@@ -545,6 +567,7 @@ static const struct key_map sdlk_map[] = {
|
||||
{ "f10", SDLK_F10 },
|
||||
{ "f11", SDLK_F11 },
|
||||
{ "f12", SDLK_F12 },
|
||||
{ "nul", SDLK_UNKNOWN },
|
||||
};
|
||||
|
||||
static struct snes_keybind *find_snes_bind(unsigned port, int id)
|
||||
@@ -578,6 +601,7 @@ static int find_sdlk_key(const char *str)
|
||||
return find_sdlk_bind(str);
|
||||
}
|
||||
|
||||
// Yes, this function needs a good refactor :)
|
||||
static void read_keybinds(config_file_t *conf)
|
||||
{
|
||||
char *tmp_key = NULL;
|
||||
@@ -592,6 +616,7 @@ static void read_keybinds(config_file_t *conf)
|
||||
if (!bind)
|
||||
continue;
|
||||
|
||||
// Check keybind
|
||||
if (bind_maps[j][i].key && config_get_string(conf, bind_maps[j][i].key, &tmp_key))
|
||||
{
|
||||
int key = find_sdlk_key(tmp_key);
|
||||
@@ -603,40 +628,53 @@ static void read_keybinds(config_file_t *conf)
|
||||
tmp_key = NULL;
|
||||
}
|
||||
|
||||
// Check joybutton bind (hats too)
|
||||
if (bind_maps[j][i].btn && config_get_string(conf, bind_maps[j][i].btn, &tmp_btn))
|
||||
{
|
||||
const char *btn = tmp_btn;
|
||||
if (*btn++ == 'h')
|
||||
if (strcmp(tmp_btn, "nul") == 0)
|
||||
{
|
||||
if (isdigit(*btn))
|
||||
{
|
||||
char *dir = NULL;
|
||||
int hat = strtol(btn, &dir, 0);
|
||||
int hat_dir = 0;
|
||||
if (dir)
|
||||
{
|
||||
if (strcasecmp(dir, "up") == 0)
|
||||
hat_dir = HAT_UP_MASK;
|
||||
else if (strcasecmp(dir, "down") == 0)
|
||||
hat_dir = HAT_DOWN_MASK;
|
||||
else if (strcasecmp(dir, "left") == 0)
|
||||
hat_dir = HAT_LEFT_MASK;
|
||||
else if (strcasecmp(dir, "right") == 0)
|
||||
hat_dir = HAT_RIGHT_MASK;
|
||||
|
||||
if (hat_dir)
|
||||
bind->joykey = HAT_MAP(hat, hat_dir);
|
||||
}
|
||||
}
|
||||
bind->joykey = NO_BTN;
|
||||
}
|
||||
else
|
||||
bind->joykey = strtol(tmp_btn, NULL, 0);
|
||||
{
|
||||
if (*btn++ == 'h')
|
||||
{
|
||||
if (isdigit(*btn))
|
||||
{
|
||||
char *dir = NULL;
|
||||
int hat = strtol(btn, &dir, 0);
|
||||
int hat_dir = 0;
|
||||
if (dir)
|
||||
{
|
||||
if (strcasecmp(dir, "up") == 0)
|
||||
hat_dir = HAT_UP_MASK;
|
||||
else if (strcasecmp(dir, "down") == 0)
|
||||
hat_dir = HAT_DOWN_MASK;
|
||||
else if (strcasecmp(dir, "left") == 0)
|
||||
hat_dir = HAT_LEFT_MASK;
|
||||
else if (strcasecmp(dir, "right") == 0)
|
||||
hat_dir = HAT_RIGHT_MASK;
|
||||
|
||||
if (hat_dir)
|
||||
bind->joykey = HAT_MAP(hat, hat_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
bind->joykey = strtol(tmp_btn, NULL, 0);
|
||||
}
|
||||
free(tmp_btn);
|
||||
}
|
||||
|
||||
// Check joyaxis binds.
|
||||
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 == '-'))
|
||||
if (strcmp(tmp_axis, "nul") == 0)
|
||||
{
|
||||
bind->joyaxis = AXIS_NONE;
|
||||
}
|
||||
else if (strlen(tmp_axis) >= 2 && (*tmp_axis == '+' || *tmp_axis == '-'))
|
||||
{
|
||||
int axis = strtol(tmp_axis + 1, NULL, 0);
|
||||
if (*tmp_axis == '+')
|
||||
|
||||
@@ -285,7 +285,7 @@ static void fill_pathname(char *out_path, char *in_path, const char *replace)
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define PACKAGE_VERSION "0.3"
|
||||
#define PACKAGE_VERSION "0.3.1"
|
||||
#endif
|
||||
|
||||
#include "config.features.h"
|
||||
@@ -1037,6 +1037,13 @@ static void check_input_rate(void)
|
||||
static void check_rewind(void)
|
||||
{
|
||||
g_extern.frame_is_reverse = false;
|
||||
static bool first = true;
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_extern.state_manager)
|
||||
return;
|
||||
|
||||
@@ -1046,9 +1053,15 @@ static void check_rewind(void)
|
||||
void *buf;
|
||||
if (state_manager_pop(g_extern.state_manager, &buf))
|
||||
{
|
||||
g_extern.frame_is_reverse = true;
|
||||
msg_queue_push(g_extern.msg_queue, "Rewinding!", 0, 30);
|
||||
psnes_unserialize(buf, psnes_serialize_size());
|
||||
g_extern.frame_is_reverse = true;
|
||||
|
||||
if (g_extern.bsv_movie)
|
||||
{
|
||||
for (unsigned i = 0; i < (g_settings.rewind_granularity ? g_settings.rewind_granularity : 1); i++)
|
||||
bsv_movie_frame_rewind(g_extern.bsv_movie);
|
||||
}
|
||||
}
|
||||
else
|
||||
msg_queue_push(g_extern.msg_queue, "Reached end of rewind buffer!", 0, 30);
|
||||
@@ -1057,7 +1070,7 @@ static void check_rewind(void)
|
||||
{
|
||||
static unsigned cnt = 0;
|
||||
cnt = (cnt + 1) % (g_settings.rewind_granularity ? g_settings.rewind_granularity : 1); // Avoid possible SIGFPE.
|
||||
if (cnt == 0)
|
||||
if (cnt == 0 || g_extern.bsv_movie)
|
||||
{
|
||||
psnes_serialize(g_extern.state_buf, psnes_serialize_size());
|
||||
state_manager_push(g_extern.state_manager, g_extern.state_buf);
|
||||
@@ -1174,8 +1187,8 @@ static void do_state_checks(void)
|
||||
{
|
||||
check_stateslots();
|
||||
check_savestates();
|
||||
check_rewind();
|
||||
}
|
||||
check_rewind();
|
||||
|
||||
if (!g_extern.bsv_movie_playback)
|
||||
check_movie_record();
|
||||
@@ -1207,14 +1220,15 @@ int main(int argc, char *argv[])
|
||||
init_movie();
|
||||
|
||||
if (!g_extern.bsv_movie)
|
||||
{
|
||||
load_save_files();
|
||||
init_rewind();
|
||||
}
|
||||
|
||||
|
||||
init_netplay();
|
||||
init_drivers();
|
||||
|
||||
if (!g_extern.netplay)
|
||||
init_rewind();
|
||||
|
||||
psnes_set_video_refresh(g_extern.netplay ? video_frame_net : video_frame);
|
||||
psnes_set_audio_sample(g_extern.netplay ? audio_sample_net : audio_sample);
|
||||
psnes_set_input_poll(g_extern.netplay ? input_poll_net : input_poll);
|
||||
@@ -1247,9 +1261,13 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (g_extern.netplay)
|
||||
netplay_pre_frame(g_extern.netplay);
|
||||
if (g_extern.bsv_movie)
|
||||
bsv_movie_set_frame_start(g_extern.bsv_movie);
|
||||
|
||||
psnes_run();
|
||||
|
||||
if (g_extern.bsv_movie)
|
||||
bsv_movie_set_frame_end(g_extern.bsv_movie);
|
||||
if (g_extern.netplay)
|
||||
netplay_post_frame(g_extern.netplay);
|
||||
|
||||
@@ -1280,10 +1298,10 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
|
||||
if (!g_extern.bsv_movie_playback && !g_extern.netplay_is_client)
|
||||
{
|
||||
deinit_rewind();
|
||||
save_files();
|
||||
}
|
||||
|
||||
if (!g_extern.netplay)
|
||||
deinit_rewind();
|
||||
|
||||
deinit_movie();
|
||||
deinit_msg_queue();
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
# video_xscale = 3.0
|
||||
# video_yscale = 3.0
|
||||
|
||||
# Fullscreen resolution
|
||||
# video_fullscreen_x = 1920
|
||||
# video_fullscreen_y = 1200
|
||||
# Fullscreen resolution. Resolution of 0 uses the resolution of the desktop.
|
||||
# video_fullscreen_x = 0
|
||||
# video_fullscreen_y = 0
|
||||
|
||||
# Start in fullscreen. Can be changed at runtime.
|
||||
# video_fullscreen = false
|
||||
@@ -31,9 +31,13 @@
|
||||
# Path to Cg shader.
|
||||
# video_cg_shader = "/path/to/cg/shader.cg"
|
||||
|
||||
# Path to bSNES-style XML shader (GLSL only). If both Cg shader path and XML shader path are defined, Cg shader will take priority.
|
||||
# Path to bSNES-style XML shader (GLSL only). If both Cg shader path and XML shader path are defined,
|
||||
# Cg shader will take priority unless overridden in video_shader_type.
|
||||
# video_bsnes_shader = "/path/to/bsnes/xml/shader.shader"
|
||||
|
||||
# Which shader type to use. Valid values are "cg", "bsnes", "none" and "auto"
|
||||
# video_shader_type = auto
|
||||
|
||||
# CPU-based filter. Valid ones are: hq2x, hq4x, grayscale, bleed, ntsc.
|
||||
# video_filter = ntsc
|
||||
|
||||
@@ -90,6 +94,8 @@
|
||||
# input_axis_threshold = 0.5
|
||||
|
||||
# Keyboard input. Will recognize normal keypresses and special keys like "left", "right", and so on.
|
||||
# Keyboard input, Joypad and Joyaxis will all obey the "nul" bind, which disables the bind completely,
|
||||
# rather than relying on a default.
|
||||
# input_player1_a = x
|
||||
# input_player1_b = z
|
||||
# input_player1_y = a
|
||||
|
||||
Reference in new issue
Block a user