2011-01-17 19:54:58 +00:00
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
2012-01-08 00:08:18 +00:00
* Copyright ( C ) 2010 - 2012 - Hans - Kristian Arntzen
2010-05-28 16:21:33 +00:00
*
* 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/>.
*/
2011-12-24 12:46:12 +00:00
# include "boolean.h"
2011-11-30 16:32:30 +00:00
# include "libsnes.hpp"
2010-05-26 19:27:37 +00:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
2011-09-27 13:31:25 +00:00
# include <ctype.h>
2011-10-06 21:43:48 +00:00
# include <errno.h>
2010-05-28 00:45:18 +00:00
# include "driver.h"
2010-12-24 00:26:36 +00:00
# include "file.h"
# include "general.h"
2010-12-30 12:54:49 +00:00
# include "dynamic.h"
2011-12-02 00:34:02 +00:00
# include "audio/utils.h"
2011-01-03 19:46:50 +00:00
# include "record/ffemu.h"
2011-01-31 15:48:42 +00:00
# include "rewind.h"
2011-02-02 11:10:27 +00:00
# include "movie.h"
2012-03-16 22:26:57 +00:00
# include "compat/strl.h"
2011-05-15 15:16:29 +00:00
# include "screenshot.h"
2011-04-17 11:30:59 +00:00
# include "cheats.h"
2012-03-16 22:26:57 +00:00
# include "compat/getopt_ssnes.h"
2010-12-24 00:07:27 +00:00
2012-01-05 12:30:13 +00:00
# if defined(_WIN32) && !defined(_XBOX)
2011-02-05 19:46:58 +00:00
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
2012-01-05 12:30:13 +00:00
# elif defined(_XBOX)
# include <xtl.h>
2012-03-04 22:15:25 +00:00
# endif
# ifdef _WIN32
# include "msvc/msvc_compat.h"
2011-02-05 19:46:58 +00:00
# endif
2011-02-04 21:47:37 +00:00
# ifdef __APPLE__
2011-02-04 22:47:36 +00:00
# include "SDL.h"
// OSX seems to really need -lSDLmain,
// so we include SDL.h here so it can hack our main.
2011-08-26 15:32:04 +00:00
// We want to use -mconsole in Win32, so we need main().
2011-02-04 21:47:37 +00:00
# endif
2010-08-16 16:40:17 +00:00
// 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.
2011-05-24 00:31:21 +00:00
static void set_fast_forward_button ( bool new_button_state , bool new_hold_button_state )
2010-08-16 16:40:17 +00:00
{
2011-05-24 00:31:21 +00:00
bool update_sync = false ;
2010-08-16 16:40:17 +00:00
static bool old_button_state = false ;
2011-05-24 00:31:21 +00:00
static bool old_hold_button_state = false ;
2010-08-16 16:40:17 +00:00
static bool syncing_state = false ;
2011-05-24 00:31:21 +00:00
2010-08-16 16:40:17 +00:00
if ( new_button_state & & ! old_button_state )
{
syncing_state = ! syncing_state ;
2011-05-24 00:31:21 +00:00
update_sync = true ;
}
else if ( old_hold_button_state ! = new_hold_button_state )
{
syncing_state = new_hold_button_state ;
update_sync = true ;
}
if ( update_sync )
{
2010-12-29 18:18:37 +00:00
if ( g_extern . video_active )
2012-03-28 22:30:50 +00:00
video_set_nonblock_state_func ( syncing_state ) ;
2010-12-29 18:18:37 +00:00
if ( g_extern . audio_active )
2012-03-28 22:30:50 +00:00
audio_set_nonblock_state_func ( g_settings . audio . sync ? syncing_state : true ) ;
2011-01-14 14:34:38 +00:00
2010-08-16 18:17:01 +00:00
if ( syncing_state )
2012-03-28 22:30:50 +00:00
g_extern . audio_data . chunk_size =
syncing_state ? g_extern . audio_data . nonblock_chunk_size : g_extern . audio_data . block_chunk_size ;
2010-08-16 16:40:17 +00:00
}
2011-05-24 00:31:21 +00:00
2010-08-16 16:40:17 +00:00
old_button_state = new_button_state ;
2011-05-24 00:31:21 +00:00
old_hold_button_state = new_hold_button_state ;
2010-08-16 16:40:17 +00:00
}
2011-10-27 21:40:34 +00:00
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 ;
}
2012-03-25 21:41:20 +00:00
# ifdef HAVE_SCREENSHOTS
2011-10-23 10:38:11 +00:00
static void take_screenshot ( void )
2011-05-15 15:16:29 +00:00
{
2011-08-16 10:11:44 +00:00
if ( ! ( * g_settings . screenshot_directory ) )
2011-05-15 15:16:29 +00:00
return ;
2011-10-23 10:38:11 +00:00
bool ret = false ;
if ( g_extern . frame_cache . data )
{
const uint16_t * data = g_extern . frame_cache . data ;
unsigned width = g_extern . frame_cache . width ;
unsigned height = g_extern . frame_cache . height ;
ret = screenshot_dump ( g_settings . screenshot_directory ,
data ,
width , height ,
2011-10-27 21:40:34 +00:00
lines_to_pitch ( height ) ) ;
2011-10-23 10:38:11 +00:00
}
2011-05-15 15:16:29 +00:00
const char * msg = NULL ;
if ( ret )
{
2012-02-11 20:11:36 +00:00
SSNES_LOG ( " Taking screenshot. \n " ) ;
2012-02-11 19:59:41 +00:00
msg = " Taking screenshot. " ;
2011-05-15 15:16:29 +00:00
}
else
{
SSNES_WARN ( " Failed to take screenshot ... \n " ) ;
2012-02-11 19:59:41 +00:00
msg = " Failed to take screenshot. " ;
2011-05-15 15:16:29 +00:00
}
msg_queue_clear ( g_extern . msg_queue ) ;
2011-10-23 10:38:11 +00:00
if ( g_extern . is_paused )
{
msg_queue_push ( g_extern . msg_queue , msg , 1 , 1 ) ;
2012-01-21 11:12:44 +00:00
ssnes_render_cached_frame ( ) ;
2011-10-23 10:38:11 +00:00
}
else
msg_queue_push ( g_extern . msg_queue , msg , 1 , 180 ) ;
2011-05-15 15:16:29 +00:00
}
2012-01-26 02:00:56 +00:00
# endif
2011-05-15 15:16:29 +00:00
2012-03-21 21:47:03 +00:00
# ifndef SSNES_CONSOLE
2011-10-27 21:40:34 +00:00
static inline void adjust_crop ( const uint16_t * * data , unsigned * height )
{
// Rather SNES specific.
2011-11-09 22:58:14 +00:00
unsigned pixel_pitch = lines_to_pitch ( * height ) > > 1 ;
2011-05-05 12:13:12 +00:00
if ( g_settings . video . crop_overscan )
{
2011-10-27 21:40:34 +00:00
if ( * height = = 239 )
2011-05-05 12:13:12 +00:00
{
2011-11-09 22:58:14 +00:00
* data + = 7 * pixel_pitch ; // Skip 7 top scanlines.
2011-10-27 21:40:34 +00:00
* height = 224 ;
2011-05-05 12:13:12 +00:00
}
2011-10-27 21:40:34 +00:00
else if ( * height = = 478 )
2011-05-05 12:13:12 +00:00
{
2011-11-09 22:58:14 +00:00
* data + = 15 * pixel_pitch ; // Skip 15 top scanlines.
2011-10-27 21:40:34 +00:00
* height = 448 ;
2011-05-05 12:13:12 +00:00
}
}
2011-10-27 21:40:34 +00:00
}
2012-03-21 21:47:03 +00:00
static void readjust_audio_input_rate ( void )
{
2012-03-28 22:30:50 +00:00
int avail = audio_write_avail_func ( ) ;
2012-03-21 21:47:03 +00:00
//fprintf(stderr, "Audio buffer is %u%% full\n",
// (unsigned)(100 - (avail * 100) / g_extern.audio_data.driver_buffer_size));
int half_size = g_extern . audio_data . driver_buffer_size / 2 ;
int delta_mid = avail - half_size ;
double direction = ( double ) delta_mid / half_size ;
double adjust = 1.0 + g_settings . audio . rate_control_delta * direction ;
g_extern . audio_data . src_ratio = g_extern . audio_data . orig_src_ratio * adjust ;
//fprintf(stderr, "New rate: %lf, Orig rate: %lf\n",
// g_extern.audio_data.src_ratio, g_extern.audio_data.orig_src_ratio);
}
# endif
2011-10-27 21:40:34 +00:00
// libsnes: 0.065
// Format received is 16-bit 0RRRRRGGGGGBBBBB
static void video_frame ( const uint16_t * data , unsigned width , unsigned height )
{
2012-03-21 22:58:31 +00:00
# ifndef SSNES_CONSOLE
2011-10-27 21:40:34 +00:00
if ( ! g_extern . video_active )
return ;
adjust_crop ( & data , & height ) ;
2012-03-21 21:47:03 +00:00
# endif
2011-05-05 12:13:12 +00:00
2011-08-11 03:25:31 +00:00
// Slightly messy code,
// but we really need to do processing before blocking on VSync for best possible scheduling.
2011-01-05 19:07:55 +00:00
# ifdef HAVE_FFMPEG
2012-03-21 21:36:11 +00:00
bool is_dupe = ! data ;
2011-11-22 16:27:02 +00:00
if ( g_extern . recording & & ( ! g_extern . filter . active | | ! g_settings . video . post_filter_record | | is_dupe ) )
2011-01-05 19:07:55 +00:00
{
2011-12-24 12:46:12 +00:00
struct ffemu_video_data ffemu_data = { 0 } ;
ffemu_data . data = data ;
ffemu_data . pitch = lines_to_pitch ( height ) ;
ffemu_data . width = width ;
ffemu_data . height = height ;
ffemu_data . is_dupe = is_dupe ;
2011-01-05 19:07:55 +00:00
ffemu_push_video ( g_extern . rec , & ffemu_data ) ;
}
2011-01-03 19:46:50 +00:00
2011-11-22 16:32:38 +00:00
if ( is_dupe )
2011-11-22 16:27:02 +00:00
return ;
2012-03-21 21:36:11 +00:00
# endif
2011-11-22 16:27:02 +00:00
2011-01-23 12:34:41 +00:00
const char * msg = msg_queue_pull ( g_extern . msg_queue ) ;
2011-05-11 15:52:16 +00:00
# ifdef HAVE_DYLIB
2011-03-07 16:22:03 +00:00
if ( g_extern . filter . active )
2010-12-30 00:33:40 +00:00
{
2011-03-07 18:12:14 +00:00
unsigned owidth = width ;
unsigned oheight = height ;
g_extern . filter . psize ( & owidth , & oheight ) ;
g_extern . filter . prender ( g_extern . filter . colormap , g_extern . filter . buffer ,
2011-10-27 21:40:34 +00:00
g_extern . filter . pitch , data , lines_to_pitch ( height ) , width , height ) ;
2011-08-11 03:25:31 +00:00
# ifdef HAVE_FFMPEG
if ( g_extern . recording & & g_settings . video . post_filter_record )
{
2011-12-24 12:46:12 +00:00
struct ffemu_video_data ffemu_data = { 0 } ;
ffemu_data . data = g_extern . filter . buffer ;
ffemu_data . pitch = g_extern . filter . pitch ;
ffemu_data . width = owidth ;
ffemu_data . height = oheight ;
2011-08-11 03:25:31 +00:00
ffemu_push_video ( g_extern . rec , & ffemu_data ) ;
}
# endif
2012-03-28 22:30:50 +00:00
if ( ! video_frame_func ( g_extern . filter . buffer , owidth , oheight , g_extern . filter . pitch , msg ) )
2011-03-07 16:22:03 +00:00
g_extern . video_active = false ;
2010-12-30 00:33:40 +00:00
}
2012-03-28 22:30:50 +00:00
else if ( ! video_frame_func ( data , width , height , lines_to_pitch ( height ) , msg ) )
2011-03-07 16:22:03 +00:00
g_extern . video_active = false ;
2010-05-29 14:59:57 +00:00
# else
2012-03-28 22:30:50 +00:00
if ( ! video_frame_func ( data , width , height , lines_to_pitch ( height ) , msg ) )
2010-12-29 18:18:37 +00:00
g_extern . video_active = false ;
2010-12-29 18:43:17 +00:00
# endif
2011-10-18 15:26:15 +00:00
g_extern . frame_cache . data = data ;
g_extern . frame_cache . width = width ;
g_extern . frame_cache . height = height ;
}
2012-01-21 11:12:44 +00:00
void ssnes_render_cached_frame ( void )
2011-10-18 15:26:15 +00:00
{
2011-10-18 17:16:38 +00:00
# ifdef HAVE_FFMPEG
// Cannot allow FFmpeg recording when pushing duped frames.
2011-10-18 15:26:15 +00:00
bool recording = g_extern . recording ;
g_extern . recording = false ;
2011-10-18 17:16:38 +00:00
# endif
2011-10-18 15:26:15 +00:00
// Not 100% safe, since the library might have
// freed the memory, but no known implementations do this :D
// It would be really stupid at any rate ...
if ( g_extern . frame_cache . data )
{
2011-10-20 21:33:15 +00:00
video_frame ( g_extern . frame_cache . data ,
g_extern . frame_cache . width ,
g_extern . frame_cache . height ) ;
2011-10-18 15:26:15 +00:00
}
2011-10-18 17:16:38 +00:00
# ifdef HAVE_FFMPEG
2011-10-18 15:26:15 +00:00
g_extern . recording = recording ;
2011-10-18 17:16:38 +00:00
# endif
2010-05-26 19:27:37 +00:00
}
2011-11-26 14:54:58 +00:00
static bool audio_flush ( const int16_t * data , size_t samples )
2010-05-26 19:27:37 +00:00
{
2011-06-14 19:35:31 +00:00
# ifdef HAVE_FFMPEG
if ( g_extern . recording )
{
2011-12-24 12:46:12 +00:00
struct ffemu_audio_data ffemu_data = { 0 } ;
ffemu_data . data = data ;
ffemu_data . frames = samples / 2 ;
2011-06-14 19:35:31 +00:00
ffemu_push_audio ( g_extern . rec , & ffemu_data ) ;
}
# endif
2012-03-25 00:19:13 +00:00
# ifndef SSNES_CONSOLE
2011-10-17 19:30:58 +00:00
if ( g_extern . is_paused )
return true ;
2011-11-20 01:06:25 +00:00
if ( ! g_extern . audio_active )
return false ;
2012-03-25 00:19:13 +00:00
# endif
2011-10-17 19:30:58 +00:00
const float * output_data = NULL ;
unsigned output_frames = 0 ;
audio_convert_s16_to_float ( g_extern . audio_data . data , data , samples ) ;
2012-03-21 22:58:31 +00:00
ssnes_dsp_output_t dsp_output = { 0 } ;
dsp_output . should_resample = SSNES_TRUE ;
# ifdef HAVE_DYLIB
2011-12-24 12:46:12 +00:00
ssnes_dsp_input_t dsp_input = { 0 } ;
dsp_input . samples = g_extern . audio_data . data ;
dsp_input . frames = samples / 2 ;
2011-05-13 19:05:28 +00:00
if ( g_extern . audio_data . dsp_plugin )
g_extern . audio_data . dsp_plugin - > process ( g_extern . audio_data . dsp_handle , & dsp_output , & dsp_input ) ;
2012-03-21 22:58:31 +00:00
# endif
2011-01-31 16:24:31 +00:00
2011-05-13 19:05:28 +00:00
if ( dsp_output . should_resample )
{
2012-02-23 22:19:23 +00:00
struct resampler_data src_data = { 0 } ;
2011-12-24 12:46:12 +00:00
src_data . data_in = dsp_output . samples ? dsp_output . samples : g_extern . audio_data . data ;
src_data . data_out = g_extern . audio_data . outsamples ;
src_data . input_frames = dsp_output . samples ? dsp_output . frames : ( samples / 2 ) ;
2012-02-14 00:16:37 +00:00
2012-03-21 21:47:03 +00:00
# ifndef SSNES_CONSOLE
2012-02-14 00:16:37 +00:00
if ( g_extern . audio_data . rate_control )
readjust_audio_input_rate ( ) ;
2012-03-21 21:47:03 +00:00
# endif
2012-02-14 00:16:37 +00:00
2011-12-24 12:46:12 +00:00
src_data . ratio = g_extern . audio_data . src_ratio ;
2012-03-04 11:01:07 +00:00
if ( g_extern . is_slowmotion )
src_data . ratio * = g_settings . slowmotion_ratio ;
2011-11-03 22:48:36 +00:00
2012-02-23 22:19:23 +00:00
resampler_process ( g_extern . audio_data . source , & src_data ) ;
2011-05-13 19:05:28 +00:00
output_data = g_extern . audio_data . outsamples ;
2011-11-03 22:48:36 +00:00
output_frames = src_data . output_frames ;
2011-05-13 19:05:28 +00:00
}
2012-03-22 21:55:30 +00:00
# ifdef HAVE_DYLIB
2011-05-13 19:05:28 +00:00
else
{
output_data = dsp_output . samples ;
output_frames = dsp_output . frames ;
}
2012-03-21 22:58:31 +00:00
# endif
2011-05-13 19:05:28 +00:00
2011-11-26 14:54:58 +00:00
union
{
float f [ 0x10000 ] ;
int16_t i [ 0x10000 * sizeof ( float ) / sizeof ( int16_t ) ] ;
2011-12-25 18:31:05 +00:00
} static empty_buf ; // Const here would require us to statically initialize it, bloating the binary.
2011-11-26 14:54:58 +00:00
2011-05-13 19:05:28 +00:00
if ( g_extern . audio_data . use_float )
{
2012-03-28 22:30:50 +00:00
if ( audio_write_func ( g_extern . audio_data . mute ? empty_buf . f : output_data ,
output_frames * sizeof ( float ) * 2 ) < 0 )
2010-08-16 17:16:03 +00:00
{
2011-05-13 19:05:28 +00:00
fprintf ( stderr , " SSNES [ERROR]: Audio backend failed to write. Will continue without sound. \n " ) ;
2011-10-15 11:16:32 +00:00
return false ;
2011-01-14 14:34:38 +00:00
}
2011-05-13 19:05:28 +00:00
}
else
{
2011-11-26 14:54:58 +00:00
if ( ! g_extern . audio_data . mute )
{
audio_convert_float_to_s16 ( g_extern . audio_data . conv_outsamples ,
output_data , output_frames * 2 ) ;
}
2010-05-26 20:42:58 +00:00
2012-03-28 22:30:50 +00:00
if ( audio_write_func ( g_extern . audio_data . mute ? empty_buf . i : g_extern . audio_data . conv_outsamples ,
2011-11-20 01:06:25 +00:00
output_frames * sizeof ( int16_t ) * 2 ) < 0 )
2011-05-13 19:05:28 +00:00
{
fprintf ( stderr , " SSNES [ERROR]: Audio backend failed to write. Will continue without sound. \n " ) ;
2011-10-15 11:16:32 +00:00
return false ;
2011-05-13 19:05:28 +00:00
}
2010-05-26 19:27:37 +00:00
}
2011-05-13 19:05:28 +00:00
2011-10-15 11:16:32 +00:00
return true ;
}
2011-10-15 12:33:41 +00:00
static void audio_sample_rewind ( uint16_t left , uint16_t right )
{
g_extern . audio_data . rewind_buf [ - - g_extern . audio_data . rewind_ptr ] = right ;
g_extern . audio_data . rewind_buf [ - - g_extern . audio_data . rewind_ptr ] = left ;
}
2011-10-15 11:16:32 +00:00
static void audio_sample ( uint16_t left , uint16_t right )
{
g_extern . audio_data . conv_outsamples [ g_extern . audio_data . data_ptr + + ] = left ;
g_extern . audio_data . conv_outsamples [ g_extern . audio_data . data_ptr + + ] = right ;
if ( g_extern . audio_data . data_ptr < g_extern . audio_data . chunk_size )
return ;
2011-10-15 12:33:41 +00:00
g_extern . audio_active = audio_flush ( g_extern . audio_data . conv_outsamples ,
2011-11-20 01:06:25 +00:00
g_extern . audio_data . data_ptr ) & & g_extern . audio_active ;
2011-10-15 12:33:41 +00:00
2011-05-13 19:05:28 +00:00
g_extern . audio_data . data_ptr = 0 ;
2010-05-26 19:27:37 +00:00
}
2012-03-27 20:24:46 +00:00
// Non-standard, alternative callback better suited for systems that process audio in batch.
// Avoids tons of calls to audio_sample() ...
unsigned audio_sample_batch ( const int16_t * data , unsigned frames )
{
if ( frames > ( AUDIO_CHUNK_SIZE_NONBLOCKING > > 1 ) )
frames = AUDIO_CHUNK_SIZE_NONBLOCKING > > 1 ;
memcpy ( g_extern . audio_data . conv_outsamples ,
data , ( frames < < 1 ) * sizeof ( int16_t ) ) ;
g_extern . audio_data . data_ptr = frames < < 1 ;
if ( g_extern . audio_data . data_ptr > = g_extern . audio_data . chunk_size )
{
g_extern . audio_active = audio_flush ( g_extern . audio_data . conv_outsamples ,
g_extern . audio_data . data_ptr ) & & g_extern . audio_active ;
g_extern . audio_data . data_ptr = 0 ;
}
return frames ;
}
2010-05-28 00:45:18 +00:00
static void input_poll ( void )
2010-05-26 19:27:37 +00:00
{
2012-03-28 22:30:50 +00:00
input_poll_func ( ) ;
2010-05-26 19:27:37 +00:00
}
static int16_t input_state ( bool port , unsigned device , unsigned index , unsigned id )
{
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
2011-11-20 19:19:05 +00:00
if ( g_extern . bsv . movie & & g_extern . bsv . movie_playback )
2011-02-02 11:10:27 +00:00
{
int16_t ret ;
2011-11-20 19:19:05 +00:00
if ( bsv_movie_get_input ( g_extern . bsv . movie , & ret ) )
2011-02-02 11:10:27 +00:00
return ret ;
else
2011-11-20 19:19:05 +00:00
g_extern . bsv . movie_end = true ;
2011-02-02 11:10:27 +00:00
}
2012-03-25 22:04:12 +00:00
# endif
2011-02-02 11:10:27 +00:00
2011-06-11 18:02:17 +00:00
static const struct snes_keybind * binds [ MAX_PLAYERS ] = {
g_settings . input . binds [ 0 ] ,
g_settings . input . binds [ 1 ] ,
g_settings . input . binds [ 2 ] ,
g_settings . input . binds [ 3 ] ,
2012-02-04 00:54:19 +00:00
g_settings . input . binds [ 4 ] ,
g_settings . input . binds [ 5 ] ,
g_settings . input . binds [ 6 ] ,
g_settings . input . binds [ 7 ] ,
2011-06-11 18:02:17 +00:00
} ;
2011-01-10 15:53:37 +00:00
2011-10-16 13:45:04 +00:00
int16_t res = 0 ;
2011-10-25 21:02:17 +00:00
if ( id < SSNES_FIRST_META_KEY )
2012-03-28 22:30:50 +00:00
res = input_input_state_func ( binds , port , device , index , id ) ;
2011-10-16 13:45:04 +00:00
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
2011-11-20 19:19:05 +00:00
if ( g_extern . bsv . movie & & ! g_extern . bsv . movie_playback )
bsv_movie_set_input ( g_extern . bsv . movie , res ) ;
2012-03-25 22:04:12 +00:00
# endif
2011-02-02 11:10:27 +00:00
return res ;
2010-05-26 19:27:37 +00:00
}
2011-01-07 16:59:53 +00:00
# ifdef _WIN32
2011-08-20 01:28:24 +00:00
# define SSNES_DEFAULT_CONF_PATH_STR "\n\t\tDefaults to ssnes.cfg in same directory as ssnes.exe."
2011-02-22 10:28:28 +00:00
# elif defined(__APPLE__)
2011-06-21 20:37:48 +00:00
# define SSNES_DEFAULT_CONF_PATH_STR " Defaults to $HOME / .ssnes.cfg."
2011-01-07 16:59:53 +00:00
# else
2011-06-21 20:37:48 +00:00
# define SSNES_DEFAULT_CONF_PATH_STR " Defaults to $XDG_CONFIG_HOME / ssnes / ssnes.cfg,\n\t\tor $HOME / .ssnes.cfg, if $XDG_CONFIG_HOME is not defined."
2011-01-07 16:59:53 +00:00
# endif
2011-01-25 12:03:53 +00:00
# include "config.features.h"
# define _PSUPP(var, name, desc) printf("\t%s:\n\t\t%s: %s\n", name, desc, _##var##_supp ? "yes" : "no")
static void print_features ( void )
{
puts ( " " ) ;
puts ( " Features: " ) ;
_PSUPP ( sdl , " SDL " , " SDL drivers " ) ;
2011-11-30 16:46:58 +00:00
_PSUPP ( thread , " Threads " , " Threading support " ) ;
2011-11-01 17:45:50 +00:00
_PSUPP ( opengl , " OpenGL " , " OpenGL driver " ) ;
2011-06-25 16:11:04 +00:00
_PSUPP ( xvideo , " XVideo " , " XVideo output " ) ;
2011-01-25 12:03:53 +00:00
_PSUPP ( alsa , " ALSA " , " audio driver " ) ;
_PSUPP ( oss , " OSS " , " audio driver " ) ;
_PSUPP ( jack , " Jack " , " audio driver " ) ;
_PSUPP ( rsound , " RSound " , " audio driver " ) ;
_PSUPP ( roar , " RoarAudio " , " audio driver " ) ;
2011-01-29 23:30:54 +00:00
_PSUPP ( pulse , " PulseAudio " , " audio driver " ) ;
2011-12-25 00:59:30 +00:00
_PSUPP ( dsound , " DirectSound " , " audio driver " ) ;
2011-01-29 23:30:54 +00:00
_PSUPP ( xaudio , " XAudio2 " , " audio driver " ) ;
2011-01-25 12:03:53 +00:00
_PSUPP ( al , " OpenAL " , " audio driver " ) ;
2011-05-11 15:52:16 +00:00
_PSUPP ( dylib , " External " , " External filter and driver support " ) ;
2011-01-25 12:03:53 +00:00
_PSUPP ( cg , " Cg " , " Cg pixel shaders " ) ;
_PSUPP ( xml , " XML " , " bSNES XML pixel shaders " ) ;
2011-06-11 14:55:53 +00:00
_PSUPP ( sdl_image , " SDL_image " , " SDL_image image loading " ) ;
2011-03-23 22:48:13 +00:00
_PSUPP ( fbo , " FBO " , " OpenGL render-to-texture (multi-pass shaders) " ) ;
2011-01-25 12:03:53 +00:00
_PSUPP ( dynamic , " Dynamic " , " Dynamic run-time loading of libsnes library " ) ;
_PSUPP ( ffmpeg , " FFmpeg " , " On-the-fly recording of gameplay with libavcodec " ) ;
2011-10-11 18:02:46 +00:00
_PSUPP ( x264rgb , " x264 RGB " , " x264 lossless RGB recording for FFmpeg " ) ;
2011-01-25 12:03:53 +00:00
_PSUPP ( configfile , " Config file " , " Configuration file support " ) ;
_PSUPP ( freetype , " FreeType " , " TTF font rendering with FreeType " ) ;
2011-03-19 19:41:07 +00:00
_PSUPP ( netplay , " Netplay " , " Peer-to-peer netplay " ) ;
2011-06-06 18:21:26 +00:00
_PSUPP ( python , " Python " , " Script support in shaders " ) ;
2011-01-25 12:03:53 +00:00
}
# undef _PSUPP
2011-12-25 20:39:58 +00:00
static void print_compiler ( FILE * file )
2011-12-25 20:16:48 +00:00
{
2011-12-25 20:39:58 +00:00
fprintf ( file , " \n Compiler: " ) ;
2011-12-25 20:16:48 +00:00
# if defined(_MSC_VER)
2011-12-25 20:39:58 +00:00
fprintf ( file , " MSVC (%d) %u-bit \n " , _MSC_VER , ( unsigned ) ( CHAR_BIT * sizeof ( size_t ) ) ) ;
2012-01-11 21:55:07 +00:00
# elif defined(__SNC__)
2012-01-16 15:38:27 +00:00
fprintf ( file , " SNC (%d) %u-bit \n " ,
__SN_VER__ , ( unsigned ) ( CHAR_BIT * sizeof ( size_t ) ) ) ;
2011-12-25 20:16:48 +00:00
# elif defined(_WIN32) && defined(__GNUC__)
2011-12-25 20:39:58 +00:00
fprintf ( file , " MinGW (%d.%d.%d) %u-bit \n " ,
2011-12-25 20:16:48 +00:00
__GNUC__ , __GNUC_MINOR__ , __GNUC_PATCHLEVEL__ , ( unsigned ) ( CHAR_BIT * sizeof ( size_t ) ) ) ;
# elif defined(__clang__)
2011-12-25 20:39:58 +00:00
fprintf ( file , " Clang/LLVM (%s) %u-bit \n " ,
2011-12-25 20:16:48 +00:00
__VERSION__ , ( unsigned ) ( CHAR_BIT * sizeof ( size_t ) ) ) ;
# elif defined(__GNUC__)
2011-12-25 20:39:58 +00:00
fprintf ( file , " GCC (%d.%d.%d) %u-bit \n " ,
2011-12-25 20:16:48 +00:00
__GNUC__ , __GNUC_MINOR__ , __GNUC_PATCHLEVEL__ , ( unsigned ) ( CHAR_BIT * sizeof ( size_t ) ) ) ;
# else
2011-12-25 20:39:58 +00:00
fprintf ( file , " Unknown compiler %u-bit \n " ,
2011-12-25 20:16:48 +00:00
( unsigned ) ( CHAR_BIT * sizeof ( size_t ) ) ) ;
# endif
2011-12-25 20:39:58 +00:00
fprintf ( file , " Built: %s \n " , __DATE__ ) ;
2011-12-25 20:16:48 +00:00
}
2010-10-01 19:39:15 +00:00
static void print_help ( void )
{
2011-02-15 15:42:55 +00:00
puts ( " =================================================================== " ) ;
2011-01-24 21:15:49 +00:00
puts ( " ssnes: Simple Super Nintendo Emulator (libsnes) -- v " PACKAGE_VERSION " -- " ) ;
2011-12-25 20:39:58 +00:00
print_compiler ( stdout ) ;
2011-02-15 15:42:55 +00:00
puts ( " =================================================================== " ) ;
2011-02-18 13:49:15 +00:00
puts ( " Usage: ssnes [rom file] [options...] " ) ;
2011-06-21 20:37:48 +00:00
puts ( " \t -h/--help: Show this help message. " ) ;
2011-10-11 18:02:46 +00:00
puts ( " \t --features: Prints available features compiled into SSNES. " ) ;
2011-06-21 20:37:48 +00:00
puts ( " \t -s/--save: Path for save file (*.srm). Required when rom is input from stdin. " ) ;
2011-07-09 06:37:08 +00:00
puts ( " \t -f/--fullscreen: Start SSNES in fullscreen regardless of config settings. " ) ;
2011-01-12 17:07:31 +00:00
puts ( " \t -S/--savestate: Path to use for save states. If not selected, *.state will be assumed. " ) ;
2011-01-19 11:54:19 +00:00
# ifdef HAVE_CONFIGFILE
2011-01-07 16:59:53 +00:00
puts ( " \t -c/--config: Path for config file. " SSNES_DEFAULT_CONF_PATH_STR ) ;
2011-11-15 20:15:12 +00:00
# endif
# ifdef HAVE_DYNAMIC
puts ( " \t -L/--libsnes: Path to libsnes implementation. Overrides any config setting. " ) ;
2011-01-19 11:54:19 +00:00
# endif
2011-01-12 17:05:57 +00:00
puts ( " \t -g/--gameboy: Path to Gameboy ROM. Load SuperGameBoy as the regular rom. " ) ;
2011-01-12 20:57:55 +00:00
puts ( " \t -b/--bsx: Path to BSX rom. Load BSX BIOS as the regular rom. " ) ;
puts ( " \t -B/--bsxslot: Path to BSX slotted rom. Load BSX BIOS as the regular rom. " ) ;
puts ( " \t --sufamiA: Path to A slot of Sufami Turbo. Load Sufami base cart as regular rom. " ) ;
puts ( " \t --sufamiB: Path to B slot of Sufami Turbo. " ) ;
2011-01-10 13:29:00 +00:00
puts ( " \t -m/--mouse: Connect a virtual mouse into designated port of the SNES (1 or 2). " ) ;
2011-01-10 16:34:26 +00:00
puts ( " \t \t This argument can be specified several times to connect more mice. " ) ;
2011-11-13 00:12:32 +00:00
puts ( " \t -N/--nodevice: Disconnects the controller device connected to the emulated SNES (1 or 2). " ) ;
2011-01-10 13:39:14 +00:00
puts ( " \t -p/--scope: Connect a virtual SuperScope into port 2 of the SNES. " ) ;
2011-01-10 15:53:37 +00:00
puts ( " \t -j/--justifier: Connect a virtual Konami Justifier into port 2 of the SNES. " ) ;
2011-01-12 17:07:31 +00:00
puts ( " \t -J/--justifiers: Daisy chain two virtual Konami Justifiers into port 2 of the SNES. " ) ;
2011-01-10 15:53:37 +00:00
puts ( " \t -4/--multitap: Connect a multitap to port 2 of the SNES. " ) ;
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
2011-02-02 11:10:27 +00:00
puts ( " \t -P/--bsvplay: Playback a BSV movie file. " ) ;
2011-11-18 17:03:24 +00:00
puts ( " \t -R/--bsvrecord: Start recording a BSV movie file from the beginning. " ) ;
2011-11-20 19:19:05 +00:00
puts ( " \t -M/--sram-mode: Takes an argument telling how SRAM should be handled in the session. " ) ;
2012-03-25 22:04:12 +00:00
# endif
2011-11-18 17:03:24 +00:00
puts ( " \t \t {no,}load-{no,}save describes if SRAM should be loaded, and if SRAM should be saved. " ) ;
2011-11-20 19:19:05 +00:00
puts ( " \t \t Do note that noload-save implies that save files will be deleted and overwritten. " ) ;
2011-03-19 19:41:07 +00:00
# ifdef HAVE_NETPLAY
2011-02-13 15:40:24 +00:00
puts ( " \t -H/--host: Host netplay as player 1. " ) ;
puts ( " \t -C/--connect: Connect to netplay as player 2. " ) ;
2011-02-18 13:49:15 +00:00
puts ( " \t --port: Port used to netplay. Default is 55435. " ) ;
2011-02-15 14:32:26 +00:00
puts ( " \t -F/--frames: Sync frames when using netplay. " ) ;
2012-03-05 16:37:18 +00:00
puts ( " \t --spectate: Netplay will become spectating mode. " ) ;
2012-01-11 18:22:18 +00:00
puts ( " \t \t Host can live stream the game content to players that connect. " ) ;
puts ( " \t \t However, the client will not be able to play. Multiple clients can connect to the host. " ) ;
2012-01-21 17:12:42 +00:00
puts ( " \t --nick: Picks a nickname for use with netplay. Not mandatory. " ) ;
2011-03-19 19:41:07 +00:00
# endif
2011-01-07 16:59:53 +00:00
2011-01-05 19:07:55 +00:00
# ifdef HAVE_FFMPEG
2011-10-30 10:24:16 +00:00
puts ( " \t -r/--record: Path to record video file. \n \t \t Using .mkv extension is recommended. " ) ;
puts ( " \t --size: Overrides output video size when recording with FFmpeg (format: WIDTHxHEIGHT). " ) ;
2011-01-05 19:07:55 +00:00
# endif
2011-06-21 20:37:48 +00:00
puts ( " \t -v/--verbose: Verbose logging. " ) ;
2011-04-23 12:47:50 +00:00
puts ( " \t -U/--ups: Specifies path for UPS patch that will be applied to ROM. " ) ;
2011-08-17 22:24:57 +00:00
puts ( " \t --bps: Specifies path for BPS patch that will be applied to ROM. " ) ;
2012-03-20 22:08:34 +00:00
puts ( " \t --ips: Specifies path for IPS patch that will be applied to ROM. " ) ;
2012-03-20 22:45:58 +00:00
puts ( " \t --no-patch: Disables all forms of rom patching. " ) ;
2011-06-24 11:29:30 +00:00
puts ( " \t -X/--xml: Specifies path to XML memory map. " ) ;
2011-04-23 12:47:50 +00:00
puts ( " \t -D/--detach: Detach SSNES from the running console. Not relevant for all platforms. \n " ) ;
2010-10-01 19:39:15 +00:00
}
2011-02-11 13:27:19 +00:00
static void set_basename ( const char * path )
{
2011-10-27 21:40:34 +00:00
strlcpy ( g_extern . system . fullpath , path , sizeof ( g_extern . system . fullpath ) ) ;
2011-08-22 15:05:27 +00:00
strlcpy ( g_extern . basename , path , sizeof ( g_extern . basename ) ) ;
char * dst = strrchr ( g_extern . basename , ' . ' ) ;
2011-02-11 13:27:19 +00:00
if ( dst )
* dst = ' \0 ' ;
}
2011-08-24 13:47:39 +00:00
static void set_paths ( const char * path )
{
set_basename ( path ) ;
SSNES_LOG ( " Opening file: \" %s \" \n " , path ) ;
g_extern . rom_file = fopen ( path , " rb " ) ;
if ( g_extern . rom_file = = NULL )
{
SSNES_ERR ( " Could not open file: \" %s \" \n " , path ) ;
2012-01-14 15:08:54 +00:00
ssnes_fail ( 1 , " set_paths() " ) ;
2011-08-24 13:47:39 +00:00
}
if ( ! g_extern . has_set_save_path )
fill_pathname_noext ( g_extern . savefile_name_srm , g_extern . basename , " .srm " , sizeof ( g_extern . savefile_name_srm ) ) ;
if ( ! g_extern . has_set_state_path )
fill_pathname_noext ( g_extern . savestate_name , g_extern . basename , " .state " , sizeof ( g_extern . savestate_name ) ) ;
if ( path_is_directory ( g_extern . savefile_name_srm ) )
{
fill_pathname_dir ( g_extern . savefile_name_srm , g_extern . basename , " .srm " , sizeof ( g_extern . savefile_name_srm ) ) ;
SSNES_LOG ( " Redirecting save file to \" %s \" . \n " , g_extern . savefile_name_srm ) ;
}
if ( path_is_directory ( g_extern . savestate_name ) )
{
fill_pathname_dir ( g_extern . savestate_name , g_extern . basename , " .state " , sizeof ( g_extern . savestate_name ) ) ;
SSNES_LOG ( " Redirecting save state to \" %s \" . \n " , g_extern . savestate_name ) ;
}
2011-10-28 15:10:58 +00:00
# ifdef HAVE_CONFIGFILE
2011-08-24 13:47:39 +00:00
if ( * g_extern . config_path & & path_is_directory ( g_extern . config_path ) )
{
fill_pathname_dir ( g_extern . config_path , g_extern . basename , " .cfg " , sizeof ( g_extern . config_path ) ) ;
SSNES_LOG ( " Redirecting config file to \" %s \" . \n " , g_extern . config_path ) ;
if ( ! path_file_exists ( g_extern . config_path ) )
{
* g_extern . config_path = ' \0 ' ;
SSNES_LOG ( " Did not find config file. Using system default. \n " ) ;
}
}
2011-10-28 15:10:58 +00:00
# endif
2011-08-24 13:47:39 +00:00
}
static void verify_stdin_paths ( void )
{
if ( strlen ( g_extern . savefile_name_srm ) = = 0 )
{
SSNES_ERR ( " Need savefile path argument (--save) when reading rom from stdin. \n " ) ;
print_help ( ) ;
2012-01-14 15:08:54 +00:00
ssnes_fail ( 1 , " verify_stdin_paths() " ) ;
2011-08-24 13:47:39 +00:00
}
else if ( strlen ( g_extern . savestate_name ) = = 0 )
{
SSNES_ERR ( " Need savestate path argument (--savestate) when reading rom from stdin. \n " ) ;
print_help ( ) ;
2012-01-14 15:08:54 +00:00
ssnes_fail ( 1 , " verify_stdin_paths() " ) ;
2011-08-24 13:47:39 +00:00
}
if ( path_is_directory ( g_extern . savefile_name_srm ) )
{
SSNES_ERR ( " Cannot specify directory for path argument (--save) when reading from stdin. \n " ) ;
print_help ( ) ;
2012-01-14 15:08:54 +00:00
ssnes_fail ( 1 , " verify_stdin_paths() " ) ;
2011-08-24 13:47:39 +00:00
}
else if ( path_is_directory ( g_extern . savestate_name ) )
{
SSNES_ERR ( " Cannot specify directory for path argument (--savestate) when reading from stdin. \n " ) ;
print_help ( ) ;
2012-01-14 15:08:54 +00:00
ssnes_fail ( 1 , " verify_stdin_paths() " ) ;
2011-08-24 13:47:39 +00:00
}
2011-10-28 15:10:58 +00:00
# ifdef HAVE_CONFIGFILE
2011-08-24 13:47:39 +00:00
else if ( path_is_directory ( g_extern . config_path ) )
{
SSNES_ERR ( " Cannot specify directory for config file (--config) when reading from stdin. \n " ) ;
print_help ( ) ;
2012-01-14 15:08:54 +00:00
ssnes_fail ( 1 , " verify_stdin_paths() " ) ;
2011-08-24 13:47:39 +00:00
}
2011-10-28 15:10:58 +00:00
# endif
2011-08-24 13:47:39 +00:00
}
2010-10-01 19:39:15 +00:00
static void parse_input ( int argc , char * argv [ ] )
2010-05-26 19:27:37 +00:00
{
2010-10-01 19:39:15 +00:00
if ( argc < 2 )
2010-05-26 19:27:37 +00:00
{
2010-10-01 19:39:15 +00:00
print_help ( ) ;
2012-01-14 15:08:54 +00:00
ssnes_fail ( 1 , " parse_input() " ) ;
2010-05-26 19:27:37 +00:00
}
2010-05-29 13:21:30 +00:00
2012-02-01 20:32:27 +00:00
// Make sure we can call parse_input several times ...
optind = 1 ;
2011-02-18 13:49:15 +00:00
int val = 0 ;
2012-01-05 20:43:55 +00:00
const struct option opts [ ] = {
2011-11-15 20:15:12 +00:00
# ifdef HAVE_DYNAMIC
{ " libsnes " , 1 , NULL , ' L ' } ,
# endif
2010-10-01 19:39:15 +00:00
{ " help " , 0 , NULL , ' h ' } ,
{ " save " , 1 , NULL , ' s ' } ,
2011-07-09 06:37:08 +00:00
{ " fullscreen " , 0 , NULL , ' f ' } ,
2011-01-05 19:07:55 +00:00
# ifdef HAVE_FFMPEG
{ " record " , 1 , NULL , ' r ' } ,
2011-10-06 21:43:48 +00:00
{ " size " , 1 , & val , ' s ' } ,
2011-01-05 19:07:55 +00:00
# endif
2010-10-01 20:10:28 +00:00
{ " verbose " , 0 , NULL , ' v ' } ,
2011-01-12 17:05:57 +00:00
{ " gameboy " , 1 , NULL , ' g ' } ,
2011-01-19 11:54:19 +00:00
# ifdef HAVE_CONFIGFILE
2010-12-29 20:12:56 +00:00
{ " config " , 0 , NULL , ' c ' } ,
2011-01-19 11:54:19 +00:00
# endif
2011-01-10 13:29:00 +00:00
{ " mouse " , 1 , NULL , ' m ' } ,
2011-06-19 09:11:04 +00:00
{ " nodevice " , 1 , NULL , ' N ' } ,
2011-01-10 13:39:14 +00:00
{ " scope " , 0 , NULL , ' p ' } ,
2011-01-12 17:07:31 +00:00
{ " savestate " , 1 , NULL , ' S ' } ,
2011-01-12 20:57:55 +00:00
{ " bsx " , 1 , NULL , ' b ' } ,
{ " bsxslot " , 1 , NULL , ' B ' } ,
2011-01-10 15:53:37 +00:00
{ " justifier " , 0 , NULL , ' j ' } ,
2011-01-12 17:07:31 +00:00
{ " justifiers " , 0 , NULL , ' J ' } ,
2011-01-10 15:53:37 +00:00
{ " multitap " , 0 , NULL , ' 4 ' } ,
2011-01-12 20:57:55 +00:00
{ " sufamiA " , 1 , NULL , ' Y ' } ,
{ " sufamiB " , 1 , NULL , ' Z ' } ,
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
2011-02-02 11:10:27 +00:00
{ " bsvplay " , 1 , NULL , ' P ' } ,
2011-11-18 17:03:24 +00:00
{ " bsvrecord " , 1 , NULL , ' R ' } ,
2011-11-20 19:19:05 +00:00
{ " sram-mode " , 1 , NULL , ' M ' } ,
2012-03-25 22:04:12 +00:00
# endif
2011-11-30 15:43:09 +00:00
# ifdef HAVE_NETPLAY
2011-02-13 15:40:24 +00:00
{ " host " , 0 , NULL , ' H ' } ,
{ " connect " , 1 , NULL , ' C ' } ,
2011-02-15 14:32:26 +00:00
{ " frames " , 1 , NULL , ' F ' } ,
2011-02-18 13:49:15 +00:00
{ " port " , 1 , & val , ' p ' } ,
2012-01-11 18:22:18 +00:00
{ " spectate " , 0 , & val , ' S ' } ,
2012-01-21 17:12:42 +00:00
{ " nick " , 1 , & val , ' N ' } ,
2011-11-30 15:43:09 +00:00
# endif
2011-03-23 22:31:33 +00:00
{ " ups " , 1 , NULL , ' U ' } ,
2011-08-17 22:24:57 +00:00
{ " bps " , 1 , & val , ' B ' } ,
2012-03-20 22:08:34 +00:00
{ " ips " , 1 , & val , ' I ' } ,
2012-03-20 22:45:58 +00:00
{ " no-patch " , 0 , & val , ' n ' } ,
2011-06-24 11:29:30 +00:00
{ " xml " , 1 , NULL , ' X ' } ,
2011-04-23 12:47:50 +00:00
{ " detach " , 0 , NULL , ' D ' } ,
2011-10-11 18:02:46 +00:00
{ " features " , 0 , & val , ' f ' } ,
2010-10-01 19:39:15 +00:00
{ NULL , 0 , NULL , 0 }
} ;
2011-01-05 19:07:55 +00:00
# ifdef HAVE_FFMPEG
# define FFMPEG_RECORD_ARG "r:"
# else
# define FFMPEG_RECORD_ARG
# endif
2011-01-19 11:54:19 +00:00
# ifdef HAVE_CONFIGFILE
# define CONFIG_FILE_ARG "c:"
# else
# define CONFIG_FILE_ARG
# endif
2011-11-15 20:15:12 +00:00
# ifdef HAVE_DYNAMIC
# define DYNAMIC_ARG "L:"
# else
# define DYNAMIC_ARG
# endif
2011-11-30 15:43:09 +00:00
# ifdef HAVE_NETPLAY
# define NETPLAY_ARG "HC:F:"
# else
# define NETPLAY_ARG
# endif
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
# define BSV_MOVIE_ARG "P:R:M:"
# else
# define BSV_MOVIE_ARG
# endif
const char * optstring = " hs:fvS:m:p4jJg:b:B:Y:Z:U:DN:X: " BSV_MOVIE_ARG NETPLAY_ARG DYNAMIC_ARG FFMPEG_RECORD_ARG CONFIG_FILE_ARG ;
2011-10-15 10:56:48 +00:00
for ( ; ; )
2010-05-29 13:21:30 +00:00
{
2011-02-18 13:49:15 +00:00
val = 0 ;
2011-11-30 10:48:53 +00:00
int c = getopt_long ( argc , argv , optstring , opts , NULL ) ;
2011-01-10 13:29:00 +00:00
int port ;
2010-10-01 19:39:15 +00:00
if ( c = = - 1 )
break ;
switch ( c )
{
case ' h ' :
print_help ( ) ;
exit ( 0 ) ;
2011-01-10 15:53:37 +00:00
case ' 4 ' :
g_extern . has_multitap = true ;
break ;
case ' j ' :
g_extern . has_justifier = true ;
break ;
2011-01-12 17:07:31 +00:00
case ' J ' :
2011-01-10 15:53:37 +00:00
g_extern . has_justifiers = true ;
break ;
2010-10-01 19:39:15 +00:00
case ' s ' :
2011-04-03 20:29:36 +00:00
strlcpy ( g_extern . savefile_name_srm , optarg , sizeof ( g_extern . savefile_name_srm ) ) ;
2011-02-11 13:27:19 +00:00
g_extern . has_set_save_path = true ;
2011-01-08 18:15:18 +00:00
break ;
2011-07-09 06:37:08 +00:00
case ' f ' :
g_extern . force_fullscreen = true ;
break ;
2011-01-12 17:05:57 +00:00
case ' g ' :
2011-04-03 20:29:36 +00:00
strlcpy ( g_extern . gb_rom_path , optarg , sizeof ( g_extern . gb_rom_path ) ) ;
2011-01-12 17:05:57 +00:00
g_extern . game_type = SSNES_CART_SGB ;
break ;
2011-01-12 20:57:55 +00:00
case ' b ' :
2011-04-03 20:29:36 +00:00
strlcpy ( g_extern . bsx_rom_path , optarg , sizeof ( g_extern . bsx_rom_path ) ) ;
2011-01-12 20:57:55 +00:00
g_extern . game_type = SSNES_CART_BSX ;
break ;
case ' B ' :
2011-04-03 20:29:36 +00:00
strlcpy ( g_extern . bsx_rom_path , optarg , sizeof ( g_extern . bsx_rom_path ) ) ;
2011-01-12 20:57:55 +00:00
g_extern . game_type = SSNES_CART_BSX_SLOTTED ;
break ;
case ' Y ' :
2011-04-03 20:29:36 +00:00
strlcpy ( g_extern . sufami_rom_path [ 0 ] , optarg , sizeof ( g_extern . sufami_rom_path [ 0 ] ) ) ;
2011-01-12 20:57:55 +00:00
g_extern . game_type = SSNES_CART_SUFAMI ;
break ;
case ' Z ' :
2011-04-03 20:29:36 +00:00
strlcpy ( g_extern . sufami_rom_path [ 1 ] , optarg , sizeof ( g_extern . sufami_rom_path [ 1 ] ) ) ;
2011-01-12 20:57:55 +00:00
g_extern . game_type = SSNES_CART_SUFAMI ;
break ;
2011-01-12 17:07:31 +00:00
case ' S ' :
2011-04-03 20:29:36 +00:00
strlcpy ( g_extern . savestate_name , optarg , sizeof ( g_extern . savestate_name ) ) ;
2011-02-11 13:27:19 +00:00
g_extern . has_set_state_path = true ;
2010-10-01 19:39:15 +00:00
break ;
2010-10-01 20:10:28 +00:00
case ' v ' :
2010-12-29 18:43:17 +00:00
g_extern . verbose = true ;
2010-10-01 20:10:28 +00:00
break ;
2011-01-10 06:58:11 +00:00
case ' m ' :
2011-01-10 13:29:00 +00:00
port = strtol ( optarg , NULL , 0 ) ;
if ( port < 1 | | port > 2 )
{
SSNES_ERR ( " Connect mouse to port 1 or 2. \n " ) ;
print_help ( ) ;
2012-01-14 15:08:54 +00:00
ssnes_fail ( 1 , " parse_input() " ) ;
2011-01-10 13:29:00 +00:00
}
g_extern . has_mouse [ port - 1 ] = true ;
2011-01-10 06:58:11 +00:00
break ;
2011-06-19 09:11:04 +00:00
case ' N ' :
port = strtol ( optarg , NULL , 0 ) ;
if ( port < 1 | | port > 2 )
{
SSNES_ERR ( " Disconnected device from port 1 or 2. \n " ) ;
print_help ( ) ;
2012-01-14 15:08:54 +00:00
ssnes_fail ( 1 , " parse_input() " ) ;
2011-06-19 09:11:04 +00:00
}
g_extern . disconnect_device [ port - 1 ] = true ;
break ;
2011-01-10 06:58:11 +00:00
case ' p ' :
2011-01-10 13:39:14 +00:00
g_extern . has_scope [ 1 ] = true ;
2011-01-10 06:58:11 +00:00
break ;
2011-01-19 11:54:19 +00:00
# ifdef HAVE_CONFIGFILE
2010-12-29 20:12:56 +00:00
case ' c ' :
2011-04-03 20:29:36 +00:00
strlcpy ( g_extern . config_path , optarg , sizeof ( g_extern . config_path ) ) ;
2010-12-29 20:12:56 +00:00
break ;
2011-01-19 11:54:19 +00:00
# endif
2010-12-29 20:12:56 +00:00
2011-01-05 19:07:55 +00:00
# ifdef HAVE_FFMPEG
case ' r ' :
2011-04-03 20:29:36 +00:00
strlcpy ( g_extern . record_path , optarg , sizeof ( g_extern . record_path ) ) ;
2011-01-05 19:07:55 +00:00
g_extern . recording = true ;
break ;
# endif
2011-11-15 20:15:12 +00:00
# ifdef HAVE_DYNAMIC
case ' L ' :
strlcpy ( g_settings . libsnes , optarg , sizeof ( g_settings . libsnes ) ) ;
break ;
# endif
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
2011-02-02 11:10:27 +00:00
case ' P ' :
2011-11-20 19:19:05 +00:00
case ' R ' :
strlcpy ( g_extern . bsv . movie_start_path , optarg ,
sizeof ( g_extern . bsv . movie_start_path ) ) ;
g_extern . bsv . movie_start_playback = c = = ' P ' ;
g_extern . bsv . movie_start_recording = c = = ' R ' ;
2011-11-18 17:03:24 +00:00
break ;
2011-11-20 19:19:05 +00:00
case ' M ' :
2011-11-18 17:03:24 +00:00
if ( strcmp ( optarg , " noload-nosave " ) = = 0 )
{
2011-11-20 19:19:05 +00:00
g_extern . sram_load_disable = true ;
g_extern . sram_save_disable = true ;
2011-11-18 17:03:24 +00:00
}
else if ( strcmp ( optarg , " noload-save " ) = = 0 )
2011-11-20 19:19:05 +00:00
g_extern . sram_load_disable = true ;
2011-11-18 17:03:24 +00:00
else if ( strcmp ( optarg , " load-nosave " ) = = 0 )
2011-11-20 19:19:05 +00:00
g_extern . sram_save_disable = true ;
else if ( strcmp ( optarg , " load-save " ) ! = 0 )
2011-11-18 17:03:24 +00:00
{
2012-02-11 20:11:36 +00:00
SSNES_ERR ( " Invalid argument in --sram-mode. \n " ) ;
2011-11-18 17:03:24 +00:00
print_help ( ) ;
2012-01-14 15:08:54 +00:00
ssnes_fail ( 1 , " parse_input() " ) ;
2011-11-18 17:03:24 +00:00
}
2011-02-02 11:10:27 +00:00
break ;
2012-03-25 22:04:12 +00:00
# endif
2011-02-02 11:10:27 +00:00
2011-11-30 15:41:00 +00:00
# ifdef HAVE_NETPLAY
2011-02-13 15:40:24 +00:00
case ' H ' :
g_extern . netplay_enable = true ;
break ;
case ' C ' :
g_extern . netplay_enable = true ;
2011-04-03 20:29:36 +00:00
strlcpy ( g_extern . netplay_server , optarg , sizeof ( g_extern . netplay_server ) ) ;
2011-02-13 15:40:24 +00:00
break ;
2011-02-15 14:32:26 +00:00
case ' F ' :
g_extern . netplay_sync_frames = strtol ( optarg , NULL , 0 ) ;
2011-11-20 19:19:05 +00:00
if ( g_extern . netplay_sync_frames > 16 )
g_extern . netplay_sync_frames = 16 ;
2011-02-15 14:32:26 +00:00
break ;
2011-11-30 15:41:00 +00:00
# endif
2011-02-15 14:32:26 +00:00
2011-03-23 22:31:33 +00:00
case ' U ' :
2011-04-03 20:29:36 +00:00
strlcpy ( g_extern . ups_name , optarg , sizeof ( g_extern . ups_name ) ) ;
2011-08-17 22:24:57 +00:00
g_extern . ups_pref = true ;
2011-03-23 22:31:33 +00:00
break ;
2011-04-23 12:47:50 +00:00
2011-06-24 11:29:30 +00:00
case ' X ' :
strlcpy ( g_extern . xml_name , optarg , sizeof ( g_extern . xml_name ) ) ;
break ;
2011-04-23 12:47:50 +00:00
case ' D ' :
2012-01-05 12:30:13 +00:00
# if defined(_WIN32) && !defined(_XBOX)
2011-04-23 12:47:50 +00:00
FreeConsole ( ) ;
# endif
break ;
2011-03-23 22:31:33 +00:00
2011-02-18 13:49:15 +00:00
case 0 :
switch ( val )
{
2011-11-30 15:41:00 +00:00
# ifdef HAVE_NETPLAY
2011-02-18 13:49:15 +00:00
case ' p ' :
2011-08-24 13:47:39 +00:00
g_extern . netplay_port = strtoul ( optarg , NULL , 0 ) ;
2011-02-18 13:49:15 +00:00
break ;
2012-01-11 18:22:18 +00:00
case ' S ' :
g_extern . netplay_is_spectate = true ;
break ;
2012-01-21 17:12:42 +00:00
case ' N ' :
strlcpy ( g_extern . netplay_nick , optarg , sizeof ( g_extern . netplay_nick ) ) ;
break ;
2011-11-30 15:41:00 +00:00
# endif
2011-10-06 21:43:48 +00:00
2011-08-17 22:24:57 +00:00
case ' B ' :
strlcpy ( g_extern . bps_name , optarg , sizeof ( g_extern . bps_name ) ) ;
g_extern . bps_pref = true ;
break ;
2011-10-06 21:43:48 +00:00
2012-03-20 22:08:34 +00:00
case ' I ' :
strlcpy ( g_extern . ips_name , optarg , sizeof ( g_extern . ips_name ) ) ;
g_extern . ips_pref = true ;
break ;
2012-03-20 22:45:58 +00:00
case ' n ' :
g_extern . block_patch = true ;
break ;
2011-10-06 21:43:48 +00:00
# ifdef HAVE_FFMPEG
case ' s ' :
{
errno = 0 ;
char * ptr ;
g_extern . record_width = strtoul ( optarg , & ptr , 0 ) ;
2011-10-06 22:04:52 +00:00
if ( ( * ptr ! = ' x ' ) | | errno )
2011-10-06 21:43:48 +00:00
{
SSNES_ERR ( " Wrong format for --size. \n " ) ;
print_help ( ) ;
2012-01-14 15:08:54 +00:00
ssnes_fail ( 1 , " parse_input() " ) ;
2011-10-06 21:43:48 +00:00
}
ptr + + ;
g_extern . record_height = strtoul ( ptr , & ptr , 0 ) ;
if ( ( * ptr ! = ' \0 ' ) | | errno )
{
SSNES_ERR ( " Wrong format for --size. \n " ) ;
print_help ( ) ;
2012-01-14 15:08:54 +00:00
ssnes_fail ( 1 , " parse_input() " ) ;
2011-10-06 21:43:48 +00:00
}
break ;
}
# endif
2011-10-11 18:02:46 +00:00
case ' f ' :
print_features ( ) ;
exit ( 0 ) ;
2011-10-06 21:43:48 +00:00
2011-02-18 13:49:15 +00:00
default :
break ;
}
break ;
2010-10-01 19:39:15 +00:00
case ' ? ' :
print_help ( ) ;
2012-01-14 15:08:54 +00:00
ssnes_fail ( 1 , " parse_input() " ) ;
2010-10-01 19:39:15 +00:00
default :
2010-10-01 20:10:28 +00:00
SSNES_ERR ( " Error parsing arguments. \n " ) ;
2012-01-14 15:08:54 +00:00
ssnes_fail ( 1 , " parse_input() " ) ;
2010-10-01 19:39:15 +00:00
}
2010-05-29 13:21:30 +00:00
}
2010-10-01 19:39:15 +00:00
if ( optind < argc )
2011-08-24 13:47:39 +00:00
set_paths ( argv [ optind ] ) ;
else
verify_stdin_paths ( ) ;
2010-10-01 19:39:15 +00:00
}
2010-08-15 08:02:04 +00:00
2011-01-10 13:29:00 +00:00
// TODO: Add rest of the controllers.
static void init_controllers ( void )
{
2011-01-10 15:53:37 +00:00
if ( g_extern . has_justifier )
2011-01-10 13:29:00 +00:00
{
2011-01-10 15:53:37 +00:00
SSNES_LOG ( " Connecting Justifier to port 2. \n " ) ;
psnes_set_controller_port_device ( SNES_PORT_2 , SNES_DEVICE_JUSTIFIER ) ;
}
else if ( g_extern . has_justifiers )
{
SSNES_LOG ( " Connecting Justifiers to port 2. \n " ) ;
psnes_set_controller_port_device ( SNES_PORT_2 , SNES_DEVICE_JUSTIFIERS ) ;
}
else if ( g_extern . has_multitap )
{
2011-06-19 09:11:04 +00:00
SSNES_LOG ( " Connecting Multitap to port 2. \n " ) ;
2011-01-10 15:53:37 +00:00
psnes_set_controller_port_device ( SNES_PORT_2 , SNES_DEVICE_MULTITAP ) ;
}
else
{
2011-06-19 09:11:04 +00:00
for ( unsigned i = 0 ; i < 2 ; i + + )
2011-01-10 13:29:00 +00:00
{
2011-06-19 09:11:04 +00:00
if ( g_extern . disconnect_device [ i ] )
{
SSNES_LOG ( " Disconnecting device from port %u. \n " , i + 1 ) ;
psnes_set_controller_port_device ( i , SNES_DEVICE_NONE ) ;
}
else if ( g_extern . has_mouse [ i ] )
2011-01-10 15:53:37 +00:00
{
2011-06-19 09:11:04 +00:00
SSNES_LOG ( " Connecting mouse to port %u. \n " , i + 1 ) ;
2011-01-10 15:53:37 +00:00
psnes_set_controller_port_device ( i , SNES_DEVICE_MOUSE ) ;
}
else if ( g_extern . has_scope [ i ] )
{
2011-06-19 09:11:04 +00:00
SSNES_LOG ( " Connecting scope to port %u. \n " , i + 1 ) ;
2011-01-10 15:53:37 +00:00
psnes_set_controller_port_device ( i , SNES_DEVICE_SUPER_SCOPE ) ;
}
2011-01-10 13:29:00 +00:00
}
}
}
2011-01-12 20:57:55 +00:00
static inline void load_save_files ( void )
2011-01-12 18:09:24 +00:00
{
2011-01-12 20:57:55 +00:00
switch ( g_extern . game_type )
2011-01-12 18:09:24 +00:00
{
case SSNES_CART_NORMAL :
2011-01-18 14:34:37 +00:00
load_ram_file ( g_extern . savefile_name_srm , SNES_MEMORY_CARTRIDGE_RAM ) ;
load_ram_file ( g_extern . savefile_name_rtc , SNES_MEMORY_CARTRIDGE_RTC ) ;
2011-01-12 20:57:55 +00:00
break ;
2011-02-11 12:44:31 +00:00
case SSNES_CART_SGB :
save_ram_file ( g_extern . savefile_name_srm , SNES_MEMORY_GAME_BOY_RAM ) ;
save_ram_file ( g_extern . savefile_name_rtc , SNES_MEMORY_GAME_BOY_RTC ) ;
break ;
2011-01-12 20:57:55 +00:00
case SSNES_CART_BSX :
case SSNES_CART_BSX_SLOTTED :
2011-01-18 14:34:37 +00:00
load_ram_file ( g_extern . savefile_name_srm , SNES_MEMORY_BSX_RAM ) ;
load_ram_file ( g_extern . savefile_name_psrm , SNES_MEMORY_BSX_PRAM ) ;
2011-01-12 20:57:55 +00:00
break ;
case SSNES_CART_SUFAMI :
2011-01-18 14:34:37 +00:00
load_ram_file ( g_extern . savefile_name_asrm , SNES_MEMORY_SUFAMI_TURBO_A_RAM ) ;
load_ram_file ( g_extern . savefile_name_bsrm , SNES_MEMORY_SUFAMI_TURBO_B_RAM ) ;
2011-01-12 20:57:55 +00:00
break ;
default :
break ;
2011-01-12 18:09:24 +00:00
}
}
2011-01-12 20:57:55 +00:00
static inline void save_files ( void )
2011-01-12 18:09:24 +00:00
{
2011-01-12 20:57:55 +00:00
switch ( g_extern . game_type )
2011-01-12 18:09:24 +00:00
{
case SSNES_CART_NORMAL :
2011-04-07 19:20:11 +00:00
SSNES_LOG ( " Saving regular SRAM. \n " ) ;
2011-01-18 14:34:37 +00:00
save_ram_file ( g_extern . savefile_name_srm , SNES_MEMORY_CARTRIDGE_RAM ) ;
save_ram_file ( g_extern . savefile_name_rtc , SNES_MEMORY_CARTRIDGE_RTC ) ;
2011-01-12 20:57:55 +00:00
break ;
2011-01-12 18:09:24 +00:00
2011-02-11 12:44:31 +00:00
case SSNES_CART_SGB :
2011-04-07 19:20:11 +00:00
SSNES_LOG ( " Saving Gameboy SRAM. \n " ) ;
2011-02-11 12:44:31 +00:00
save_ram_file ( g_extern . savefile_name_srm , SNES_MEMORY_GAME_BOY_RAM ) ;
save_ram_file ( g_extern . savefile_name_rtc , SNES_MEMORY_GAME_BOY_RTC ) ;
break ;
2011-01-12 20:57:55 +00:00
case SSNES_CART_BSX :
case SSNES_CART_BSX_SLOTTED :
2011-04-07 19:20:11 +00:00
SSNES_LOG ( " Saving BSX (P)RAM. \n " ) ;
2011-01-18 14:34:37 +00:00
save_ram_file ( g_extern . savefile_name_srm , SNES_MEMORY_BSX_RAM ) ;
save_ram_file ( g_extern . savefile_name_psrm , SNES_MEMORY_BSX_PRAM ) ;
2011-01-12 20:57:55 +00:00
break ;
2011-01-12 17:05:57 +00:00
2011-01-12 20:57:55 +00:00
case SSNES_CART_SUFAMI :
2011-04-07 19:20:11 +00:00
SSNES_LOG ( " Saving Sufami turbo A/B RAM. \n " ) ;
2011-01-18 14:34:37 +00:00
save_ram_file ( g_extern . savefile_name_asrm , SNES_MEMORY_SUFAMI_TURBO_A_RAM ) ;
save_ram_file ( g_extern . savefile_name_bsrm , SNES_MEMORY_SUFAMI_TURBO_B_RAM ) ;
2011-01-12 20:57:55 +00:00
break ;
2011-01-12 18:09:24 +00:00
2011-01-12 20:57:55 +00:00
default :
break ;
}
2011-01-12 17:05:57 +00:00
}
2011-01-12 20:57:55 +00:00
2011-01-12 17:05:57 +00:00
# ifdef HAVE_FFMPEG
static void init_recording ( void )
{
2011-11-05 12:34:41 +00:00
if ( ! g_extern . recording )
return ;
2011-08-11 03:25:31 +00:00
2011-11-16 17:56:42 +00:00
// Canonical values.
double fps = psnes_get_region ( ) = = SNES_REGION_NTSC ? 60.00 : 50.00 ;
double samplerate = 32000.0 ;
if ( g_extern . system . timing_set )
{
fps = g_extern . system . timing . fps ;
samplerate = g_extern . system . timing . sample_rate ;
2011-12-24 12:46:12 +00:00
SSNES_LOG ( " Custom timing given: FPS: %.4f, Sample rate: %.4f \n " , ( float ) fps , ( float ) samplerate ) ;
2011-11-16 17:56:42 +00:00
}
2011-12-24 12:46:12 +00:00
struct ffemu_params params = { 0 } ;
params . out_width = g_extern . system . geom . base_width ;
params . out_height = g_extern . system . geom . base_height ;
params . fb_width = g_extern . system . geom . max_width ;
params . fb_height = g_extern . system . geom . max_height ;
params . channels = 2 ;
params . filename = g_extern . record_path ;
params . fps = fps ;
params . samplerate = samplerate ;
params . rgb32 = false ;
2011-08-11 03:25:31 +00:00
2011-11-05 12:34:41 +00:00
if ( g_extern . record_width | | g_extern . record_height )
{
params . out_width = g_extern . record_width ;
params . out_height = g_extern . record_height ;
}
else if ( g_settings . video . hires_record )
{
params . out_width * = 2 ;
params . out_height * = 2 ;
}
2011-10-14 09:00:31 +00:00
2011-11-05 12:34:41 +00:00
if ( g_settings . video . force_aspect & & ( g_settings . video . aspect_ratio > 0.0f ) )
params . aspect_ratio = g_settings . video . aspect_ratio ;
else
params . aspect_ratio = ( float ) params . out_width / params . out_height ;
2011-08-11 03:25:31 +00:00
2011-11-05 12:34:41 +00:00
if ( g_settings . video . post_filter_record & & g_extern . filter . active )
{
g_extern . filter . psize ( & params . out_width , & params . out_height ) ;
params . rgb32 = true ;
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 ) ;
}
SSNES_LOG ( " Recording with FFmpeg to %s @ %ux%u. (FB size: %ux%u 32-bit: %s) \n " , g_extern . record_path , params . out_width , params . out_height , params . fb_width , params . fb_height , params . rgb32 ? " yes " : " no " ) ;
g_extern . rec = ffemu_new ( & params ) ;
if ( ! g_extern . rec )
{
SSNES_ERR ( " Failed to start FFmpeg recording. \n " ) ;
g_extern . recording = false ;
2011-01-12 17:05:57 +00:00
}
}
static void deinit_recording ( void )
{
if ( g_extern . recording )
{
ffemu_finalize ( g_extern . rec ) ;
ffemu_free ( g_extern . rec ) ;
}
}
# endif
2011-01-23 01:23:20 +00:00
static void init_msg_queue ( void )
{
2011-12-25 11:47:47 +00:00
ssnes_assert ( g_extern . msg_queue = msg_queue_new ( 8 ) ) ;
2011-01-23 01:23:20 +00:00
}
static void deinit_msg_queue ( void )
{
if ( g_extern . msg_queue )
msg_queue_free ( g_extern . msg_queue ) ;
}
2011-04-17 11:30:59 +00:00
# ifdef HAVE_XML
static void init_cheats ( void )
{
if ( * g_settings . cheat_database )
g_extern . cheat = cheat_manager_new ( g_settings . cheat_database ) ;
}
static void deinit_cheats ( void )
{
if ( g_extern . cheat )
cheat_manager_free ( g_extern . cheat ) ;
}
# endif
2011-01-31 15:48:42 +00:00
static void init_rewind ( void )
{
2011-12-27 17:19:45 +00:00
if ( ! g_settings . rewind_enable )
return ;
g_extern . state_size = psnes_serialize_size ( ) ;
// Make sure we allocate at least 4-byte multiple.
size_t aligned_state_size = ( g_extern . state_size + 3 ) & ~ 3 ;
g_extern . state_buf = calloc ( 1 , aligned_state_size ) ;
if ( ! g_extern . state_buf )
2011-01-31 17:06:57 +00:00
{
2012-02-11 20:11:36 +00:00
SSNES_ERR ( " Failed to allocate memory for rewind buffer. \n " ) ;
2011-12-27 17:19:45 +00:00
return ;
2011-01-31 17:06:57 +00:00
}
2011-12-27 17:19:45 +00:00
if ( ! psnes_serialize ( ( uint8_t * ) g_extern . state_buf , g_extern . state_size ) )
{
2012-02-11 20:11:36 +00:00
SSNES_ERR ( " Failed to perform initial serialization for rewind. \n " ) ;
2011-12-27 17:19:45 +00:00
free ( g_extern . state_buf ) ;
g_extern . state_buf = NULL ;
return ;
}
SSNES_LOG ( " Initing rewind buffer with size: %u MB \n " , ( unsigned ) ( g_settings . rewind_buffer_size / 1000000 ) ) ;
g_extern . state_manager = state_manager_new ( aligned_state_size , g_settings . rewind_buffer_size , g_extern . state_buf ) ;
if ( ! g_extern . state_manager )
2012-02-11 20:11:36 +00:00
SSNES_WARN ( " Failed to init rewind buffer. Rewinding will be disabled. \n " ) ;
2011-01-31 15:48:42 +00:00
}
static void deinit_rewind ( void )
{
if ( g_extern . state_manager )
state_manager_free ( g_extern . state_manager ) ;
if ( g_extern . state_buf )
free ( g_extern . state_buf ) ;
}
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
2011-02-02 11:10:27 +00:00
static void init_movie ( void )
{
2011-11-20 19:19:05 +00:00
if ( g_extern . bsv . movie_start_playback )
2011-02-02 11:45:56 +00:00
{
2011-11-20 19:19:05 +00:00
g_extern . bsv . movie = bsv_movie_init ( g_extern . bsv . movie_start_path , SSNES_MOVIE_PLAYBACK ) ;
if ( ! g_extern . bsv . movie )
2011-02-02 11:45:56 +00:00
{
2012-02-11 20:11:36 +00:00
SSNES_ERR ( " Failed to load movie file: \" %s \" . \n " , g_extern . bsv . movie_start_path ) ;
2012-01-14 15:08:54 +00:00
ssnes_fail ( 1 , " init_movie() " ) ;
2011-02-02 11:45:56 +00:00
}
2011-11-20 19:19:05 +00:00
g_extern . bsv . movie_playback = true ;
2012-02-11 19:59:41 +00:00
msg_queue_push ( g_extern . msg_queue , " Starting movie playback. " , 2 , 180 ) ;
2012-02-11 20:11:36 +00:00
SSNES_LOG ( " Starting movie playback. \n " ) ;
2011-12-16 09:19:45 +00:00
g_settings . rewind_granularity = 1 ;
2011-02-02 11:45:56 +00:00
}
2011-11-20 19:19:05 +00:00
else if ( g_extern . bsv . movie_start_recording )
2011-11-18 17:03:24 +00:00
{
2012-01-02 12:32:25 +00:00
char msg [ PATH_MAX ] ;
2012-02-11 19:59:41 +00:00
snprintf ( msg , sizeof ( msg ) , " Starting movie record to \" %s \" . " ,
2011-11-20 19:19:05 +00:00
g_extern . bsv . movie_start_path ) ;
2011-11-19 00:33:21 +00:00
2011-11-20 19:19:05 +00:00
g_extern . bsv . movie = bsv_movie_init ( g_extern . bsv . movie_start_path , SSNES_MOVIE_RECORD ) ;
2011-11-18 17:03:24 +00:00
msg_queue_clear ( g_extern . msg_queue ) ;
2011-11-20 19:19:05 +00:00
msg_queue_push ( g_extern . msg_queue ,
2012-02-11 19:59:41 +00:00
g_extern . bsv . movie ? msg : " Failed to start movie record. " , 1 , 180 ) ;
2011-11-18 17:03:24 +00:00
2011-11-20 19:19:05 +00:00
if ( g_extern . bsv . movie )
2011-12-16 09:19:45 +00:00
{
2012-02-11 20:11:36 +00:00
SSNES_LOG ( " Starting movie record to \" %s \" . \n " , g_extern . bsv . movie_start_path ) ;
2011-12-16 09:19:45 +00:00
g_settings . rewind_granularity = 1 ;
}
2011-11-18 17:03:24 +00:00
else
2012-02-11 20:11:36 +00:00
SSNES_ERR ( " Failed to start movie record. \n " ) ;
2011-11-18 17:03:24 +00:00
}
2011-02-02 11:10:27 +00:00
}
static void deinit_movie ( void )
{
2011-11-20 19:19:05 +00:00
if ( g_extern . bsv . movie )
bsv_movie_free ( g_extern . bsv . movie ) ;
2011-02-02 11:10:27 +00:00
}
2012-03-25 22:04:12 +00:00
# endif
2011-02-02 11:10:27 +00:00
2011-02-18 13:49:15 +00:00
# define SSNES_DEFAULT_PORT 55435
2011-03-19 19:41:07 +00:00
# ifdef HAVE_NETPLAY
2011-02-13 15:40:24 +00:00
static void init_netplay ( void )
{
2011-11-03 20:05:12 +00:00
if ( ! g_extern . netplay_enable )
return ;
2011-12-24 12:46:12 +00:00
struct snes_callbacks cbs = { 0 } ;
cbs . frame_cb = video_frame ;
cbs . sample_cb = audio_sample ;
cbs . state_cb = input_state ;
2011-11-03 20:05:12 +00:00
if ( * g_extern . netplay_server )
2011-02-13 15:40:24 +00:00
{
2011-11-03 20:05:12 +00:00
SSNES_LOG ( " Connecting to netplay host... \n " ) ;
g_extern . netplay_is_client = true ;
}
else
SSNES_LOG ( " Waiting for client... \n " ) ;
2011-02-13 16:45:14 +00:00
2011-11-03 20:05:12 +00:00
g_extern . netplay = netplay_new ( g_extern . netplay_is_client ? g_extern . netplay_server : NULL ,
g_extern . netplay_port ? g_extern . netplay_port : SSNES_DEFAULT_PORT ,
2012-01-21 17:12:42 +00:00
g_extern . netplay_sync_frames , & cbs , g_extern . netplay_is_spectate ,
g_extern . netplay_nick ) ;
2011-11-03 20:05:12 +00:00
if ( ! g_extern . netplay )
{
g_extern . netplay_is_client = false ;
SSNES_WARN ( " Failed to init netplay ... \n " ) ;
2011-02-13 16:45:14 +00:00
2011-11-03 20:05:12 +00:00
if ( g_extern . msg_queue )
2011-02-13 16:45:14 +00:00
{
2011-11-03 20:05:12 +00:00
msg_queue_push ( g_extern . msg_queue ,
" Failed to init netplay ... " ,
0 , 180 ) ;
2011-02-13 16:45:14 +00:00
}
2011-02-13 15:40:24 +00:00
}
}
static void deinit_netplay ( void )
{
if ( g_extern . netplay )
netplay_free ( g_extern . netplay ) ;
}
2011-03-19 19:41:07 +00:00
# endif
2011-02-13 15:40:24 +00:00
2012-01-11 18:22:18 +00:00
static void init_libsnes_cbs ( void )
{
# ifdef HAVE_NETPLAY
if ( g_extern . netplay )
{
psnes_set_video_refresh ( g_extern . netplay_is_spectate ?
video_frame : video_frame_net ) ;
psnes_set_audio_sample ( g_extern . netplay_is_spectate ?
audio_sample : audio_sample_net ) ;
psnes_set_input_state ( g_extern . netplay_is_spectate ?
( g_extern . netplay_is_client ? input_state_spectate_client : input_state_spectate )
: input_state_net ) ;
}
else
{
psnes_set_video_refresh ( video_frame ) ;
psnes_set_audio_sample ( audio_sample ) ;
psnes_set_input_state ( input_state ) ;
}
# else
psnes_set_video_refresh ( video_frame ) ;
psnes_set_audio_sample ( audio_sample ) ;
psnes_set_input_state ( input_state ) ;
# endif
psnes_set_input_poll ( input_poll ) ;
}
2012-03-26 01:24:28 +00:00
# ifdef HAVE_THREADS
2011-02-10 20:16:59 +00:00
static void init_autosave ( void )
{
2011-02-11 12:44:31 +00:00
int ram_types [ 2 ] = { - 1 , - 1 } ;
const char * ram_paths [ 2 ] = { NULL , NULL } ;
switch ( g_extern . game_type )
{
case SSNES_CART_BSX :
case SSNES_CART_BSX_SLOTTED :
ram_types [ 0 ] = SNES_MEMORY_BSX_RAM ;
ram_types [ 1 ] = SNES_MEMORY_BSX_PRAM ;
ram_paths [ 0 ] = g_extern . savefile_name_srm ;
ram_paths [ 1 ] = g_extern . savefile_name_psrm ;
break ;
case SSNES_CART_SUFAMI :
ram_types [ 0 ] = SNES_MEMORY_SUFAMI_TURBO_A_RAM ;
ram_types [ 1 ] = SNES_MEMORY_SUFAMI_TURBO_B_RAM ;
ram_paths [ 0 ] = g_extern . savefile_name_asrm ;
ram_paths [ 1 ] = g_extern . savefile_name_bsrm ;
break ;
case SSNES_CART_SGB :
ram_types [ 0 ] = SNES_MEMORY_GAME_BOY_RAM ;
ram_types [ 1 ] = SNES_MEMORY_GAME_BOY_RTC ;
ram_paths [ 0 ] = g_extern . savefile_name_srm ;
ram_paths [ 1 ] = g_extern . savefile_name_rtc ;
break ;
default :
ram_types [ 0 ] = SNES_MEMORY_CARTRIDGE_RAM ;
ram_types [ 1 ] = SNES_MEMORY_CARTRIDGE_RTC ;
ram_paths [ 0 ] = g_extern . savefile_name_srm ;
ram_paths [ 1 ] = g_extern . savefile_name_rtc ;
}
2011-02-10 20:16:59 +00:00
if ( g_settings . autosave_interval > 0 )
{
2011-02-11 12:44:31 +00:00
for ( unsigned i = 0 ; i < sizeof ( g_extern . autosave ) / sizeof ( g_extern . autosave [ 0 ] ) ; i + + )
{
if ( ram_paths [ i ] & & strlen ( ram_paths [ i ] ) > 0 & & psnes_get_memory_size ( ram_types [ i ] ) > 0 )
{
g_extern . autosave [ i ] = autosave_new ( ram_paths [ i ] ,
psnes_get_memory_data ( ram_types [ i ] ) ,
psnes_get_memory_size ( ram_types [ i ] ) ,
g_settings . autosave_interval ) ;
if ( ! g_extern . autosave [ i ] )
SSNES_WARN ( " Could not initialize autosave. \n " ) ;
}
}
2011-02-10 20:16:59 +00:00
}
}
static void deinit_autosave ( void )
{
2011-02-11 12:44:31 +00:00
for ( unsigned i = 0 ; i < sizeof ( g_extern . autosave ) / sizeof ( g_extern . autosave [ 0 ] ) ; i + + )
{
if ( g_extern . autosave [ i ] )
autosave_free ( g_extern . autosave [ i ] ) ;
}
}
2012-03-26 01:24:28 +00:00
# endif
2011-02-11 12:44:31 +00:00
2011-09-27 13:31:25 +00:00
static void set_savestate_auto_index ( void )
{
if ( ! g_settings . savestate_auto_index )
return ;
2012-01-02 12:32:25 +00:00
char state_path [ PATH_MAX ] ;
2011-09-27 13:31:25 +00:00
strlcpy ( state_path , g_extern . savestate_name , sizeof ( state_path ) ) ;
char * split = strrchr ( state_path , ' / ' ) ;
if ( ! split )
split = strrchr ( state_path , ' \\ ' ) ;
const char * base = state_path ;
const char * dir = state_path ;
if ( split )
{
* split = ' \0 ' ;
base = split + 1 ;
}
unsigned max_index = 0 ;
char * * dir_list = dir_list_new ( dir , NULL ) ;
if ( ! dir_list )
return ;
unsigned index = 0 ;
const char * dir_elem ;
while ( ( dir_elem = dir_list [ index + + ] ) )
{
if ( ! strstr ( dir_elem , base ) )
continue ;
const char * end = dir_elem + strlen ( dir_elem ) ;
while ( ( end ! = dir_elem ) & & isdigit ( end [ - 1 ] ) ) end - - ;
unsigned index = strtoul ( end , NULL , 0 ) ;
if ( index > max_index )
max_index = index ;
}
dir_list_free ( dir_list ) ;
g_extern . state_slot = max_index ;
SSNES_LOG ( " Found last state slot: #%u \n " , g_extern . state_slot ) ;
}
2011-01-12 20:57:55 +00:00
static void fill_pathnames ( void )
{
switch ( g_extern . game_type )
{
case SSNES_CART_BSX :
case SSNES_CART_BSX_SLOTTED :
// BSX PSRM
2011-02-11 13:27:19 +00:00
if ( ! g_extern . has_set_save_path )
2012-01-06 19:32:30 +00:00
{
fill_pathname ( g_extern . savefile_name_srm ,
g_extern . bsx_rom_path , " .srm " , sizeof ( g_extern . savefile_name_srm ) ) ;
}
fill_pathname ( g_extern . savefile_name_psrm ,
g_extern . savefile_name_srm , " .psrm " , sizeof ( g_extern . savefile_name_psrm ) ) ;
2011-02-11 13:27:19 +00:00
if ( ! g_extern . has_set_state_path )
2012-01-06 19:32:30 +00:00
{
fill_pathname ( g_extern . savestate_name ,
g_extern . bsx_rom_path , " .state " , sizeof ( g_extern . savestate_name ) ) ;
}
2011-01-12 20:57:55 +00:00
break ;
case SSNES_CART_SUFAMI :
2012-01-06 19:32:30 +00:00
if ( g_extern . has_set_save_path & & * g_extern . sufami_rom_path [ 0 ] & & * g_extern . sufami_rom_path [ 1 ] )
SSNES_WARN ( " Sufami Turbo SRAM paths will be inferred from their respective paths to avoid conflicts. \n " ) ;
2011-01-12 20:57:55 +00:00
// SUFAMI ARAM
2012-01-06 19:32:30 +00:00
fill_pathname ( g_extern . savefile_name_asrm ,
g_extern . sufami_rom_path [ 0 ] , " .srm " , sizeof ( g_extern . savefile_name_asrm ) ) ;
2011-01-12 20:57:55 +00:00
// SUFAMI BRAM
2012-01-06 19:32:30 +00:00
fill_pathname ( g_extern . savefile_name_bsrm ,
g_extern . sufami_rom_path [ 1 ] , " .srm " , sizeof ( g_extern . savefile_name_bsrm ) ) ;
if ( ! g_extern . has_set_state_path )
{
fill_pathname ( g_extern . savestate_name ,
* g_extern . sufami_rom_path [ 0 ] ?
g_extern . sufami_rom_path [ 0 ] : g_extern . sufami_rom_path [ 1 ] ,
" .state " , sizeof ( g_extern . savestate_name ) ) ;
}
2011-01-12 20:57:55 +00:00
break ;
2011-02-11 13:27:19 +00:00
case SSNES_CART_SGB :
if ( ! g_extern . has_set_save_path )
2012-01-06 19:32:30 +00:00
{
fill_pathname ( g_extern . savefile_name_srm ,
g_extern . gb_rom_path , " .srm " , sizeof ( g_extern . savefile_name_srm ) ) ;
}
2011-02-11 13:27:19 +00:00
if ( ! g_extern . has_set_state_path )
2012-01-06 19:32:30 +00:00
{
fill_pathname ( g_extern . savestate_name ,
g_extern . gb_rom_path , " .state " , sizeof ( g_extern . savestate_name ) ) ;
}
fill_pathname ( g_extern . savefile_name_rtc ,
g_extern . savefile_name_srm , " .rtc " , sizeof ( g_extern . savefile_name_rtc ) ) ;
2011-02-11 13:27:19 +00:00
break ;
2011-01-12 20:57:55 +00:00
default :
// Infer .rtc save path from save ram path.
2012-01-06 19:32:30 +00:00
fill_pathname ( g_extern . savefile_name_rtc ,
g_extern . savefile_name_srm , " .rtc " , sizeof ( g_extern . savefile_name_rtc ) ) ;
2011-01-12 20:57:55 +00:00
}
2011-02-11 13:27:19 +00:00
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
2011-11-20 19:19:05 +00:00
fill_pathname ( g_extern . bsv . movie_path , g_extern . savefile_name_srm , " " , sizeof ( g_extern . bsv . movie_path ) ) ;
2012-03-25 22:04:12 +00:00
# endif
2011-03-23 22:31:33 +00:00
2011-08-17 22:05:56 +00:00
if ( * g_extern . basename )
{
if ( ! ( * g_extern . ups_name ) )
fill_pathname_noext ( g_extern . ups_name , g_extern . basename , " .ups " , sizeof ( g_extern . ups_name ) ) ;
if ( ! ( * g_extern . bps_name ) )
fill_pathname_noext ( g_extern . bps_name , g_extern . basename , " .bps " , sizeof ( g_extern . bps_name ) ) ;
2011-06-24 11:29:30 +00:00
2012-03-20 22:08:34 +00:00
if ( ! ( * g_extern . ips_name ) )
fill_pathname_noext ( g_extern . ips_name , g_extern . basename , " .ips " , sizeof ( g_extern . ips_name ) ) ;
2011-08-17 22:05:56 +00:00
if ( ! ( * g_extern . xml_name ) )
fill_pathname_noext ( g_extern . xml_name , g_extern . basename , " .xml " , sizeof ( g_extern . xml_name ) ) ;
2011-10-24 19:34:30 +00:00
2012-03-26 01:24:28 +00:00
# ifdef HAVE_SCREENSHOTS
2011-10-24 19:34:30 +00:00
if ( ! ( * g_settings . screenshot_directory ) )
{
strlcpy ( g_settings . screenshot_directory , g_extern . basename , sizeof ( g_settings . screenshot_directory ) ) ;
char * dir = strrchr ( g_settings . screenshot_directory , ' / ' ) ;
if ( ! dir )
dir = strrchr ( g_settings . screenshot_directory , ' \\ ' ) ;
if ( dir )
* dir = ' \0 ' ;
}
2012-03-26 01:24:28 +00:00
# endif
2011-08-17 22:05:56 +00:00
}
2011-01-12 20:57:55 +00:00
}
2012-02-13 19:57:32 +00:00
void ssnes_load_state ( void )
{
char load_path [ PATH_MAX ] ;
if ( g_extern . state_slot > 0 )
snprintf ( load_path , sizeof ( load_path ) , " %s%u " , g_extern . savestate_name , g_extern . state_slot ) ;
else
snprintf ( load_path , sizeof ( load_path ) , " %s " , g_extern . savestate_name ) ;
char msg [ 512 ] ;
if ( load_state ( load_path ) )
snprintf ( msg , sizeof ( msg ) , " Loaded state from slot #%u. " , g_extern . state_slot ) ;
else
snprintf ( msg , sizeof ( msg ) , " Failed to load state from \" %s \" . " , load_path ) ;
msg_queue_clear ( g_extern . msg_queue ) ;
msg_queue_push ( g_extern . msg_queue , msg , 2 , 180 ) ;
}
void ssnes_save_state ( void )
{
if ( g_settings . savestate_auto_index )
g_extern . state_slot + + ;
char save_path [ PATH_MAX ] ;
if ( g_extern . state_slot > 0 )
snprintf ( save_path , sizeof ( save_path ) , " %s%u " , g_extern . savestate_name , g_extern . state_slot ) ;
else
snprintf ( save_path , sizeof ( save_path ) , " %s " , g_extern . savestate_name ) ;
char msg [ 512 ] ;
if ( save_state ( save_path ) )
snprintf ( msg , sizeof ( msg ) , " Saved state to slot #%u. " , g_extern . state_slot ) ;
else
snprintf ( msg , sizeof ( msg ) , " Failed to save state to \" %s \" . " , save_path ) ;
msg_queue_clear ( g_extern . msg_queue ) ;
msg_queue_push ( g_extern . msg_queue , msg , 2 , 180 ) ;
}
2011-01-23 22:09:54 +00:00
// Save or load state here.
2012-03-10 13:08:04 +00:00
static void check_savestates ( bool immutable )
2011-01-23 22:09:54 +00:00
{
static bool old_should_savestate = false ;
2012-03-28 22:30:50 +00:00
bool should_savestate = input_key_pressed_func ( SSNES_SAVE_STATE_KEY ) ;
2012-03-10 13:08:04 +00:00
2011-01-23 22:09:54 +00:00
if ( should_savestate & & ! old_should_savestate )
2012-02-13 19:57:32 +00:00
ssnes_save_state ( ) ;
2011-01-23 22:09:54 +00:00
old_should_savestate = should_savestate ;
2012-03-10 13:08:04 +00:00
if ( ! immutable )
{
static bool old_should_loadstate = false ;
2012-03-28 22:30:50 +00:00
bool should_loadstate = input_key_pressed_func ( SSNES_LOAD_STATE_KEY ) ;
2012-03-10 13:08:04 +00:00
if ( ! should_savestate & & should_loadstate & & ! old_should_loadstate )
ssnes_load_state ( ) ;
old_should_loadstate = should_loadstate ;
}
2011-01-23 22:09:54 +00:00
}
2012-03-21 22:58:31 +00:00
# ifndef SSNES_CONSOLE
2011-10-18 12:39:54 +00:00
static bool check_fullscreen ( void )
2011-01-23 22:09:54 +00:00
{
// If we go fullscreen we drop all drivers and reinit to be safe.
2011-10-18 12:39:54 +00:00
static bool was_pressed = false ;
2012-03-28 22:30:50 +00:00
bool pressed = input_key_pressed_func ( SSNES_FULLSCREEN_TOGGLE_KEY ) ;
2011-10-18 12:39:54 +00:00
bool toggle = pressed & & ! was_pressed ;
if ( toggle )
2011-01-23 22:09:54 +00:00
{
g_settings . video . fullscreen = ! g_settings . video . fullscreen ;
uninit_drivers ( ) ;
init_drivers ( ) ;
2011-10-18 15:26:15 +00:00
// Poll input to avoid possibly stale data to corrupt things.
if ( driver . input )
2012-03-28 22:30:50 +00:00
input_poll_func ( ) ;
2011-01-23 22:09:54 +00:00
}
2011-04-09 11:15:14 +00:00
was_pressed = pressed ;
2011-10-18 12:39:54 +00:00
return toggle ;
2011-01-23 22:09:54 +00:00
}
2012-03-21 22:58:31 +00:00
# endif
2011-01-23 22:09:54 +00:00
2012-02-15 17:49:23 +00:00
void ssnes_state_slot_increase ( void )
{
g_extern . state_slot + + ;
if ( g_extern . msg_queue )
msg_queue_clear ( g_extern . msg_queue ) ;
char msg [ 256 ] ;
snprintf ( msg , sizeof ( msg ) , " Save state/movie slot: %u " , g_extern . state_slot ) ;
if ( g_extern . msg_queue )
msg_queue_push ( g_extern . msg_queue , msg , 1 , 180 ) ;
SSNES_LOG ( " %s \n " , msg ) ;
}
void ssnes_state_slot_decrease ( void )
{
if ( g_extern . state_slot > 0 )
g_extern . state_slot - - ;
if ( g_extern . msg_queue )
msg_queue_clear ( g_extern . msg_queue ) ;
char msg [ 256 ] ;
snprintf ( msg , sizeof ( msg ) , " Save state/movie slot: %u " , g_extern . state_slot ) ;
if ( g_extern . msg_queue )
msg_queue_push ( g_extern . msg_queue , msg , 1 , 180 ) ;
SSNES_LOG ( " %s \n " , msg ) ;
}
2011-01-23 22:09:54 +00:00
static void check_stateslots ( void )
{
// Save state slots
static bool old_should_slot_increase = false ;
2012-03-28 22:30:50 +00:00
bool should_slot_increase = input_key_pressed_func ( SSNES_STATE_SLOT_PLUS ) ;
2011-01-23 22:09:54 +00:00
if ( should_slot_increase & & ! old_should_slot_increase )
2012-02-15 17:49:23 +00:00
ssnes_state_slot_increase ( ) ;
2011-01-23 22:09:54 +00:00
old_should_slot_increase = should_slot_increase ;
static bool old_should_slot_decrease = false ;
2012-03-28 22:30:50 +00:00
bool should_slot_decrease = input_key_pressed_func ( SSNES_STATE_SLOT_MINUS ) ;
2011-01-23 22:09:54 +00:00
if ( should_slot_decrease & & ! old_should_slot_decrease )
2012-02-15 17:49:23 +00:00
ssnes_state_slot_decrease ( ) ;
2011-01-23 22:09:54 +00:00
old_should_slot_decrease = should_slot_decrease ;
}
2011-01-29 17:42:21 +00:00
static void check_input_rate ( void )
{
bool display = false ;
2012-03-28 22:30:50 +00:00
if ( input_key_pressed_func ( SSNES_AUDIO_INPUT_RATE_PLUS ) )
2011-01-29 17:42:21 +00:00
{
g_settings . audio . in_rate + = g_settings . audio . rate_step ;
display = true ;
}
2012-03-28 22:30:50 +00:00
else if ( input_key_pressed_func ( SSNES_AUDIO_INPUT_RATE_MINUS ) )
2011-01-29 17:42:21 +00:00
{
g_settings . audio . in_rate - = g_settings . audio . rate_step ;
display = true ;
}
if ( display )
{
char msg [ 256 ] ;
snprintf ( msg , sizeof ( msg ) , " Audio input rate: %.2f Hz " , g_settings . audio . in_rate ) ;
msg_queue_clear ( g_extern . msg_queue ) ;
2012-01-25 22:03:02 +00:00
msg_queue_push ( g_extern . msg_queue , msg , 1 , 180 ) ;
2011-01-29 17:42:21 +00:00
SSNES_LOG ( " %s \n " , msg ) ;
2011-10-15 14:16:13 +00:00
g_extern . audio_data . src_ratio =
( double ) g_settings . audio . out_rate / g_settings . audio . in_rate ;
2011-01-29 17:42:21 +00:00
}
}
2011-10-15 12:33:41 +00:00
static inline void flush_rewind_audio ( void )
{
2011-11-20 01:06:25 +00:00
if ( g_extern . frame_is_reverse ) // We just rewound. Flush rewind audio buffer.
2011-10-15 12:33:41 +00:00
{
g_extern . audio_active = audio_flush ( g_extern . audio_data . rewind_buf + g_extern . audio_data . rewind_ptr ,
2011-11-20 01:06:25 +00:00
g_extern . audio_data . rewind_size - g_extern . audio_data . rewind_ptr ) & & g_extern . audio_active ;
2011-10-15 12:33:41 +00:00
}
}
static inline void setup_rewind_audio ( void )
{
// Push audio ready to be played.
g_extern . audio_data . rewind_ptr = g_extern . audio_data . rewind_size ;
for ( unsigned i = 0 ; i < g_extern . audio_data . data_ptr ; i + = 2 )
{
g_extern . audio_data . rewind_buf [ - - g_extern . audio_data . rewind_ptr ] =
g_extern . audio_data . conv_outsamples [ i + 1 ] ;
g_extern . audio_data . rewind_buf [ - - g_extern . audio_data . rewind_ptr ] =
g_extern . audio_data . conv_outsamples [ i + 0 ] ;
}
g_extern . audio_data . data_ptr = 0 ;
}
2011-01-31 15:48:42 +00:00
static void check_rewind ( void )
{
2011-10-15 12:33:41 +00:00
flush_rewind_audio ( ) ;
2011-01-31 16:24:31 +00:00
g_extern . frame_is_reverse = false ;
2011-10-15 12:33:41 +00:00
2011-02-25 23:31:13 +00:00
static bool first = true ;
if ( first )
{
first = false ;
return ;
}
2011-01-31 15:48:42 +00:00
if ( ! g_extern . state_manager )
return ;
2012-03-28 22:30:50 +00:00
if ( input_key_pressed_func ( SSNES_REWIND ) )
2011-01-31 15:48:42 +00:00
{
2011-01-31 17:49:50 +00:00
msg_queue_clear ( g_extern . msg_queue ) ;
2011-01-31 15:48:42 +00:00
void * buf ;
if ( state_manager_pop ( g_extern . state_manager , & buf ) )
2011-01-31 16:24:31 +00:00
{
2011-02-25 23:31:13 +00:00
g_extern . frame_is_reverse = true ;
2011-10-15 12:33:41 +00:00
setup_rewind_audio ( ) ;
2012-02-11 19:59:41 +00:00
msg_queue_push ( g_extern . msg_queue , " Rewinding. " , 0 , g_extern . is_paused ? 1 : 30 ) ;
2011-12-27 17:19:45 +00:00
psnes_unserialize ( ( uint8_t * ) buf , g_extern . state_size ) ;
2011-02-25 23:31:13 +00:00
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
2011-11-20 19:19:05 +00:00
if ( g_extern . bsv . movie )
bsv_movie_frame_rewind ( g_extern . bsv . movie ) ;
2012-03-25 22:04:12 +00:00
# endif
2011-01-31 16:24:31 +00:00
}
2011-01-31 17:49:50 +00:00
else
2012-02-11 19:59:41 +00:00
msg_queue_push ( g_extern . msg_queue , " Reached end of rewind buffer. " , 0 , 30 ) ;
2011-01-31 15:48:42 +00:00
}
else
{
2011-02-01 16:30:18 +00:00
static unsigned cnt = 0 ;
cnt = ( cnt + 1 ) % ( g_settings . rewind_granularity ? g_settings . rewind_granularity : 1 ) ; // Avoid possible SIGFPE.
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
2011-11-20 19:19:05 +00:00
if ( cnt = = 0 | | g_extern . bsv . movie )
2012-03-25 22:04:12 +00:00
# else
if ( cnt = = 0 )
# endif
2011-02-01 16:30:18 +00:00
{
2011-12-27 17:19:45 +00:00
psnes_serialize ( ( uint8_t * ) g_extern . state_buf , g_extern . state_size ) ;
2011-02-01 16:30:18 +00:00
state_manager_push ( g_extern . state_manager , g_extern . state_buf ) ;
}
2011-01-31 15:48:42 +00:00
}
2011-10-15 12:33:41 +00:00
psnes_set_audio_sample ( g_extern . frame_is_reverse ? audio_sample_rewind : audio_sample ) ;
2011-01-31 15:48:42 +00:00
}
2012-03-04 11:01:07 +00:00
static void check_slowmotion ( void )
{
2012-03-28 22:30:50 +00:00
g_extern . is_slowmotion = input_key_pressed_func ( SSNES_SLOWMOTION ) ;
2012-03-04 11:01:07 +00:00
if ( g_extern . is_slowmotion )
{
msg_queue_clear ( g_extern . msg_queue ) ;
msg_queue_push ( g_extern . msg_queue , g_extern . frame_is_reverse ? " Slow motion rewind. " : " Slow motion. " , 0 , 30 ) ;
}
}
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
2012-03-10 17:31:25 +00:00
static void movie_record_toggle ( void )
2011-02-02 11:45:56 +00:00
{
2012-03-10 17:31:25 +00:00
if ( g_extern . bsv . movie )
2011-02-02 11:45:56 +00:00
{
2012-03-10 17:31:25 +00:00
msg_queue_clear ( g_extern . msg_queue ) ;
msg_queue_push ( g_extern . msg_queue , " Stopping movie record. " , 2 , 180 ) ;
SSNES_LOG ( " Stopping movie record. \n " ) ;
bsv_movie_free ( g_extern . bsv . movie ) ;
g_extern . bsv . movie = NULL ;
}
else
{
g_settings . rewind_granularity = 1 ;
char path [ PATH_MAX ] ;
if ( g_extern . state_slot > 0 )
2011-02-02 11:45:56 +00:00
{
2012-03-10 17:31:25 +00:00
snprintf ( path , sizeof ( path ) , " %s%u.bsv " ,
g_extern . bsv . movie_path , g_extern . state_slot ) ;
2011-02-02 11:45:56 +00:00
}
else
{
2012-03-10 17:31:25 +00:00
snprintf ( path , sizeof ( path ) , " %s.bsv " ,
g_extern . bsv . movie_path ) ;
}
2011-02-02 12:37:01 +00:00
2012-03-10 17:31:25 +00:00
char msg [ PATH_MAX ] ;
snprintf ( msg , sizeof ( msg ) , " Starting movie record to \" %s \" . " , path ) ;
2011-11-19 00:33:21 +00:00
2012-03-10 17:31:25 +00:00
g_extern . bsv . movie = bsv_movie_init ( path , SSNES_MOVIE_RECORD ) ;
msg_queue_clear ( g_extern . msg_queue ) ;
msg_queue_push ( g_extern . msg_queue , g_extern . bsv . movie ? msg : " Failed to start movie record. " , 1 , 180 ) ;
2011-02-02 11:45:56 +00:00
2012-03-10 17:31:25 +00:00
if ( g_extern . bsv . movie )
SSNES_LOG ( " Starting movie record to \" %s \" . \n " , path ) ;
else
SSNES_ERR ( " Failed to start movie record. \n " ) ;
2011-02-02 11:45:56 +00:00
}
2012-03-10 17:31:25 +00:00
}
2011-02-02 11:45:56 +00:00
2012-03-10 17:31:25 +00:00
static void check_movie_record ( bool pressed )
{
if ( pressed )
movie_record_toggle ( ) ;
2011-02-02 11:45:56 +00:00
}
2012-03-10 17:31:25 +00:00
static void check_movie_playback ( bool pressed )
2011-11-20 02:00:21 +00:00
{
2012-03-10 17:31:25 +00:00
if ( g_extern . bsv . movie_end | | pressed )
2011-11-20 02:00:21 +00:00
{
2012-02-11 19:59:41 +00:00
msg_queue_push ( g_extern . msg_queue , " Movie playback ended. " , 1 , 180 ) ;
2012-02-11 20:11:36 +00:00
SSNES_LOG ( " Movie playback ended. \n " ) ;
2011-11-20 19:19:05 +00:00
bsv_movie_free ( g_extern . bsv . movie ) ;
g_extern . bsv . movie = NULL ;
g_extern . bsv . movie_end = false ;
g_extern . bsv . movie_playback = false ;
2011-11-20 02:00:21 +00:00
}
}
2012-03-10 17:31:25 +00:00
static void check_movie ( void )
{
static bool old_button = false ;
2012-03-28 22:30:50 +00:00
bool new_button = input_key_pressed_func ( SSNES_MOVIE_RECORD_TOGGLE ) ;
2012-03-10 17:31:25 +00:00
bool pressed = new_button & & ! old_button ;
if ( g_extern . bsv . movie_playback )
check_movie_playback ( pressed ) ;
else
check_movie_record ( pressed ) ;
old_button = new_button ;
}
2012-03-25 22:04:12 +00:00
# endif
2012-03-10 17:31:25 +00:00
2012-03-25 00:19:13 +00:00
# ifndef SSNES_CONSOLE
2011-02-05 19:46:58 +00:00
static void check_pause ( void )
{
static bool old_state = false ;
2012-03-28 22:30:50 +00:00
bool new_state = input_key_pressed_func ( SSNES_PAUSE_TOGGLE ) ;
2011-02-05 20:45:44 +00:00
2011-10-18 19:57:28 +00:00
// FRAMEADVANCE will set us into pause mode.
2012-03-28 22:30:50 +00:00
new_state | = ! g_extern . is_paused & & input_key_pressed_func ( SSNES_FRAMEADVANCE ) ;
2011-10-18 19:57:28 +00:00
2011-02-05 20:45:44 +00:00
static bool old_focus = true ;
bool focus = true ;
if ( g_settings . pause_nonactive )
2012-03-28 22:30:50 +00:00
focus = video_focus_func ( ) ;
2011-02-05 20:45:44 +00:00
if ( focus & & new_state & & ! old_state )
2011-02-05 19:46:58 +00:00
{
g_extern . is_paused = ! g_extern . is_paused ;
if ( g_extern . is_paused )
{
2012-02-11 20:11:36 +00:00
SSNES_LOG ( " Paused. \n " ) ;
2011-02-05 19:46:58 +00:00
if ( driver . audio_data )
2012-03-28 22:30:50 +00:00
audio_stop_func ( ) ;
2011-02-05 19:46:58 +00:00
}
else
{
2012-02-11 20:11:36 +00:00
SSNES_LOG ( " Unpaused. \n " ) ;
2011-02-05 19:46:58 +00:00
if ( driver . audio_data )
2011-02-06 22:55:17 +00:00
{
2012-03-28 22:30:50 +00:00
if ( ! audio_start_func ( ) )
2011-02-06 22:55:17 +00:00
{
2012-02-11 20:11:36 +00:00
SSNES_ERR ( " Failed to resume audio driver. Will continue without audio. \n " ) ;
2011-02-06 22:55:17 +00:00
g_extern . audio_active = false ;
}
}
2011-02-05 19:46:58 +00:00
}
}
2011-02-05 20:45:44 +00:00
else if ( focus & & ! old_focus )
{
2012-02-11 20:11:36 +00:00
SSNES_LOG ( " Unpaused. \n " ) ;
2011-02-05 20:45:44 +00:00
g_extern . is_paused = false ;
2012-03-28 22:30:50 +00:00
if ( driver . audio_data & & ! audio_start_func ( ) )
2011-02-06 22:55:17 +00:00
{
2012-03-28 22:30:50 +00:00
SSNES_ERR ( " Failed to resume audio driver. Will continue without audio. \n " ) ;
g_extern . audio_active = false ;
2011-02-06 22:55:17 +00:00
}
2011-02-05 20:45:44 +00:00
}
else if ( ! focus & & old_focus )
{
2012-02-11 20:11:36 +00:00
SSNES_LOG ( " Paused. \n " ) ;
2011-02-05 20:45:44 +00:00
g_extern . is_paused = true ;
if ( driver . audio_data )
2012-03-28 22:30:50 +00:00
audio_stop_func ( ) ;
2011-02-05 20:45:44 +00:00
}
2011-02-05 19:46:58 +00:00
2011-02-05 20:45:44 +00:00
old_focus = focus ;
2011-02-05 19:46:58 +00:00
old_state = new_state ;
}
2012-03-25 00:19:13 +00:00
# endif
2011-02-05 19:46:58 +00:00
2011-10-17 19:30:58 +00:00
static void check_oneshot ( void )
{
static bool old_state = false ;
2012-03-28 22:30:50 +00:00
bool new_state = input_key_pressed_func ( SSNES_FRAMEADVANCE ) ;
2011-10-18 19:57:28 +00:00
g_extern . is_oneshot = ( new_state & & ! old_state ) ;
2011-10-17 19:30:58 +00:00
old_state = new_state ;
2011-10-18 19:57:28 +00:00
// Rewind buttons works like FRAMEREWIND when paused. We will one-shot in that case.
static bool old_rewind_state = false ;
2012-03-28 22:30:50 +00:00
bool new_rewind_state = input_key_pressed_func ( SSNES_REWIND ) ;
2011-10-18 19:57:28 +00:00
g_extern . is_oneshot | = new_rewind_state & & ! old_rewind_state ;
old_rewind_state = new_rewind_state ;
2011-10-17 19:30:58 +00:00
}
2012-02-02 19:39:28 +00:00
void ssnes_game_reset ( void )
2012-02-02 13:22:43 +00:00
{
2012-02-11 20:11:36 +00:00
SSNES_LOG ( " Resetting game. \n " ) ;
2012-02-02 13:22:43 +00:00
msg_queue_clear ( g_extern . msg_queue ) ;
2012-02-11 19:59:41 +00:00
msg_queue_push ( g_extern . msg_queue , " Reset. " , 1 , 120 ) ;
2012-02-02 13:22:43 +00:00
psnes_reset ( ) ;
init_controllers ( ) ; // bSNES since v073r01 resets controllers to JOYPAD after a reset, so just enforce it here.
}
2011-03-24 19:41:28 +00:00
static void check_reset ( void )
{
2011-10-29 11:35:01 +00:00
static bool old_state = false ;
2012-03-28 22:30:50 +00:00
bool new_state = input_key_pressed_func ( SSNES_RESET ) ;
2011-10-29 11:35:01 +00:00
if ( new_state & & ! old_state )
2012-02-02 19:39:28 +00:00
ssnes_game_reset ( ) ;
2011-10-29 11:35:01 +00:00
old_state = new_state ;
2011-03-24 19:41:28 +00:00
}
2011-03-29 16:28:31 +00:00
# ifdef HAVE_XML
static void check_shader_dir ( void )
{
static bool old_pressed_next = false ;
2011-04-17 14:53:19 +00:00
static bool old_pressed_prev = false ;
2011-03-29 16:28:31 +00:00
if ( ! g_extern . shader_dir . elems | | ! driver . video - > xml_shader )
return ;
bool should_apply = false ;
2012-03-28 22:30:50 +00:00
bool pressed_next = input_key_pressed_func ( SSNES_SHADER_NEXT ) ;
bool pressed_prev = input_key_pressed_func ( SSNES_SHADER_PREV ) ;
2011-03-29 16:28:31 +00:00
if ( pressed_next & & ! old_pressed_next )
{
should_apply = true ;
g_extern . shader_dir . ptr = ( g_extern . shader_dir . ptr + 1 ) % g_extern . shader_dir . size ;
}
else if ( pressed_prev & & ! old_pressed_prev )
{
should_apply = true ;
if ( g_extern . shader_dir . ptr = = 0 )
g_extern . shader_dir . ptr = g_extern . shader_dir . size - 1 ;
else
g_extern . shader_dir . ptr - - ;
}
if ( should_apply )
{
const char * shader = g_extern . shader_dir . elems [ g_extern . shader_dir . ptr ] ;
2011-03-29 17:09:10 +00:00
2011-04-03 20:29:36 +00:00
strlcpy ( g_settings . video . bsnes_shader_path , shader , sizeof ( g_settings . video . bsnes_shader_path ) ) ;
2011-03-29 17:09:10 +00:00
g_settings . video . shader_type = SSNES_SHADER_BSNES ;
2011-03-29 16:28:31 +00:00
msg_queue_clear ( g_extern . msg_queue ) ;
char msg [ 512 ] ;
snprintf ( msg , sizeof ( msg ) , " XML shader #%u: \" %s \" " , ( unsigned ) g_extern . shader_dir . ptr , shader ) ;
msg_queue_push ( g_extern . msg_queue , msg , 1 , 120 ) ;
SSNES_LOG ( " Applying shader \" %s \" \n " , shader ) ;
2012-03-28 22:30:50 +00:00
if ( ! video_xml_shader_func ( shader ) )
2012-02-11 20:11:36 +00:00
SSNES_WARN ( " Failed to apply shader. \n " ) ;
2011-03-29 16:28:31 +00:00
}
old_pressed_next = pressed_next ;
old_pressed_prev = pressed_prev ;
}
2011-04-17 14:53:19 +00:00
static void check_cheats ( void )
{
if ( ! g_extern . cheat )
return ;
static bool old_pressed_prev = false ;
static bool old_pressed_next = false ;
static bool old_pressed_toggle = false ;
2012-03-28 22:30:50 +00:00
bool pressed_next = input_key_pressed_func ( SSNES_CHEAT_INDEX_PLUS ) ;
bool pressed_prev = input_key_pressed_func ( SSNES_CHEAT_INDEX_MINUS ) ;
bool pressed_toggle = input_key_pressed_func ( SSNES_CHEAT_TOGGLE ) ;
2011-04-17 14:53:19 +00:00
if ( pressed_next & & ! old_pressed_next )
cheat_manager_index_next ( g_extern . cheat ) ;
else if ( pressed_prev & & ! old_pressed_prev )
cheat_manager_index_prev ( g_extern . cheat ) ;
else if ( pressed_toggle & & ! old_pressed_toggle )
cheat_manager_toggle ( g_extern . cheat ) ;
old_pressed_prev = pressed_prev ;
old_pressed_next = pressed_next ;
old_pressed_toggle = pressed_toggle ;
}
2011-03-29 16:28:31 +00:00
# endif
2012-03-25 21:41:20 +00:00
# ifdef HAVE_SCREENSHOTS
2011-05-15 15:16:29 +00:00
static void check_screenshot ( void )
{
static bool old_pressed = false ;
2012-03-28 22:30:50 +00:00
bool pressed = input_key_pressed_func ( SSNES_SCREENSHOT ) ;
2011-10-23 10:38:11 +00:00
if ( pressed & & ! old_pressed )
take_screenshot ( ) ;
2011-05-15 15:16:29 +00:00
old_pressed = pressed ;
}
2012-01-26 02:00:56 +00:00
# endif
2011-05-15 15:16:29 +00:00
2011-05-17 17:20:41 +00:00
# ifdef HAVE_DYLIB
static void check_dsp_config ( void )
{
if ( ! g_extern . audio_data . dsp_plugin | | ! g_extern . audio_data . dsp_plugin - > config )
return ;
static bool old_pressed = false ;
2012-03-28 22:30:50 +00:00
bool pressed = input_key_pressed_func ( SSNES_DSP_CONFIG ) ;
2011-05-17 17:20:41 +00:00
if ( pressed & & ! old_pressed )
g_extern . audio_data . dsp_plugin - > config ( g_extern . audio_data . dsp_handle ) ;
old_pressed = pressed ;
}
# endif
2012-03-26 01:24:28 +00:00
# ifndef SSNES_CONSOLE
2011-11-26 14:54:58 +00:00
static void check_mute ( void )
{
if ( ! g_extern . audio_active )
return ;
static bool old_pressed = false ;
2012-03-28 22:30:50 +00:00
bool pressed = input_key_pressed_func ( SSNES_MUTE ) ;
2011-11-26 14:54:58 +00:00
if ( pressed & & ! old_pressed )
{
g_extern . audio_data . mute = ! g_extern . audio_data . mute ;
2012-02-11 19:59:41 +00:00
const char * msg = g_extern . audio_data . mute ? " Audio muted. " : " Audio unmuted. " ;
2011-11-26 14:54:58 +00:00
msg_queue_clear ( g_extern . msg_queue ) ;
msg_queue_push ( g_extern . msg_queue , msg , 1 , 180 ) ;
SSNES_LOG ( " %s \n " , msg ) ;
}
old_pressed = pressed ;
}
2012-03-26 01:24:28 +00:00
# endif
2011-11-26 14:54:58 +00:00
2012-01-21 13:24:38 +00:00
# ifdef HAVE_NETPLAY
static void check_netplay_flip ( void )
{
static bool old_pressed = false ;
2012-03-28 22:30:50 +00:00
bool pressed = input_key_pressed_func ( SSNES_NETPLAY_FLIP ) ;
2012-01-21 13:24:38 +00:00
if ( pressed & & ! old_pressed )
netplay_flip_players ( g_extern . netplay ) ;
old_pressed = pressed ;
}
# endif
2011-01-23 22:09:54 +00:00
static void do_state_checks ( void )
{
2012-03-25 21:41:20 +00:00
# ifdef HAVE_SCREENSHOTS
2011-10-23 10:38:11 +00:00
check_screenshot ( ) ;
2012-01-26 02:00:56 +00:00
# endif
2012-03-26 01:24:28 +00:00
# ifndef SSNES_CONSOLE
2011-11-26 14:54:58 +00:00
check_mute ( ) ;
2012-03-26 01:24:28 +00:00
# endif
2011-11-26 14:54:58 +00:00
2011-11-30 15:41:00 +00:00
# ifdef HAVE_NETPLAY
2011-02-13 15:40:24 +00:00
if ( ! g_extern . netplay )
{
2011-11-30 15:41:00 +00:00
# endif
2012-03-25 00:19:13 +00:00
# ifndef SSNES_CONSOLE
2011-02-13 15:40:24 +00:00
check_pause ( ) ;
2012-03-25 00:19:13 +00:00
# endif
2011-10-17 19:30:58 +00:00
check_oneshot ( ) ;
2011-10-18 12:39:54 +00:00
2012-03-21 22:58:31 +00:00
# ifdef SSNES_CONSOLE
if ( g_extern . is_paused )
# else
2011-10-18 15:26:15 +00:00
if ( check_fullscreen ( ) & & g_extern . is_paused )
2012-03-21 22:58:31 +00:00
# endif
2012-01-21 11:12:44 +00:00
ssnes_render_cached_frame ( ) ;
2011-02-05 19:46:58 +00:00
2012-03-25 00:19:13 +00:00
# ifndef SSNES_CONSOLE
2011-10-18 12:39:54 +00:00
if ( g_extern . is_paused & & ! g_extern . is_oneshot )
return ;
2012-03-25 00:19:13 +00:00
# endif
2011-10-18 12:39:54 +00:00
2011-05-24 00:31:21 +00:00
set_fast_forward_button (
2012-03-28 22:30:50 +00:00
input_key_pressed_func ( SSNES_FAST_FORWARD_KEY ) ,
input_key_pressed_func ( SSNES_FAST_FORWARD_HOLD_KEY ) ) ;
2011-01-23 22:09:54 +00:00
2012-03-10 13:08:04 +00:00
check_stateslots ( ) ;
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
2012-03-10 13:08:04 +00:00
check_savestates ( g_extern . bsv . movie ) ;
2012-03-25 22:04:12 +00:00
# else
check_savestates ( false ) ;
# endif
2011-02-13 15:40:24 +00:00
2011-10-18 19:57:28 +00:00
check_rewind ( ) ;
2012-03-04 11:01:07 +00:00
check_slowmotion ( ) ;
2011-11-20 19:19:05 +00:00
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
2012-03-10 17:31:25 +00:00
check_movie ( ) ;
2012-03-25 22:04:12 +00:00
# endif
2011-10-18 19:57:28 +00:00
2011-03-29 16:28:31 +00:00
# ifdef HAVE_XML
check_shader_dir ( ) ;
2011-10-18 19:59:37 +00:00
check_cheats ( ) ;
2011-03-29 16:28:31 +00:00
# endif
2011-05-17 17:20:41 +00:00
# ifdef HAVE_DYLIB
check_dsp_config ( ) ;
# endif
2011-10-18 19:59:37 +00:00
check_reset ( ) ;
2011-11-30 15:41:00 +00:00
# ifdef HAVE_NETPLAY
2011-02-02 11:10:27 +00:00
}
2011-10-18 12:39:54 +00:00
else
2012-01-21 13:24:38 +00:00
{
check_netplay_flip ( ) ;
2012-03-21 22:58:31 +00:00
# ifndef SSNES_CONSOLE
2011-10-18 12:39:54 +00:00
check_fullscreen ( ) ;
2012-03-21 22:58:31 +00:00
# endif
2012-01-21 13:24:38 +00:00
}
2011-11-30 15:41:00 +00:00
# endif
2011-05-17 17:20:41 +00:00
# ifdef HAVE_DYLIB
// DSP plugin doesn't use variable input rate.
if ( ! g_extern . audio_data . dsp_plugin )
# endif
check_input_rate ( ) ;
2011-01-23 22:09:54 +00:00
}
2011-03-17 00:25:44 +00:00
static void fill_title_buf ( void )
{
2011-10-05 19:44:17 +00:00
snprintf ( g_extern . title_buf , sizeof ( g_extern . title_buf ) , " SSNES : %s " , psnes_library_id ( ) ) ;
2011-03-17 00:25:44 +00:00
}
2011-08-26 15:32:04 +00:00
static void init_state ( void )
{
g_extern . video_active = true ;
g_extern . audio_active = true ;
g_extern . game_type = SSNES_CART_NORMAL ;
}
2011-01-12 17:05:57 +00:00
2012-01-14 15:08:54 +00:00
void ssnes_main_clear_state ( void )
2010-10-01 19:39:15 +00:00
{
2012-01-14 15:08:54 +00:00
memset ( & g_settings , 0 , sizeof ( g_settings ) ) ;
2012-02-06 14:51:35 +00:00
free ( g_extern . system . environment ) ;
free ( g_extern . system . environment_split ) ;
2012-01-14 15:08:54 +00:00
memset ( & g_extern , 0 , sizeof ( g_extern ) ) ;
2012-02-06 14:51:35 +00:00
2012-01-21 17:00:07 +00:00
# ifdef SSNES_CONSOLE
memset ( & g_console , 0 , sizeof ( g_console ) ) ;
# endif
2012-02-06 14:51:35 +00:00
2011-08-26 15:32:04 +00:00
init_state ( ) ;
2012-01-14 15:08:54 +00:00
}
2011-08-26 15:32:04 +00:00
2012-01-14 15:08:54 +00:00
int ssnes_main_init ( int argc , char * argv [ ] )
{
init_state ( ) ;
2010-12-29 20:12:56 +00:00
parse_input ( argc , argv ) ;
2011-12-25 20:39:58 +00:00
2012-01-14 15:08:54 +00:00
int sjlj_ret ;
if ( ( sjlj_ret = setjmp ( g_extern . error_sjlj_context ) ) > 0 )
{
SSNES_ERR ( " Fatal error received in: \" %s \" \n " , g_extern . error_string ) ;
return sjlj_ret ;
}
g_extern . error_in_init = true ;
2011-12-25 20:39:58 +00:00
if ( g_extern . verbose )
{
2011-12-25 20:43:00 +00:00
fprintf ( stderr , " === Build ======================================= " ) ;
2011-12-25 20:39:58 +00:00
print_compiler ( stderr ) ;
2011-12-25 20:43:00 +00:00
fprintf ( stderr , " ================================================= \n " ) ;
2011-12-25 20:39:58 +00:00
}
2012-01-28 14:47:02 +00:00
config_load ( ) ;
2011-12-01 21:36:26 +00:00
init_libsnes_sym ( ) ;
2011-03-17 00:25:44 +00:00
fill_title_buf ( ) ;
2010-12-30 12:54:49 +00:00
psnes_init ( ) ;
2011-09-06 09:55:21 +00:00
if ( * g_extern . basename )
2010-12-30 13:11:56 +00:00
psnes_set_cartridge_basename ( g_extern . basename ) ;
2012-01-14 13:08:43 +00:00
SSNES_LOG ( " Version of libsnes API: %u.%u \n " ,
psnes_library_revision_major ( ) , psnes_library_revision_minor ( ) ) ;
2010-10-01 19:39:15 +00:00
2011-01-12 20:57:55 +00:00
fill_pathnames ( ) ;
2011-09-27 13:31:25 +00:00
set_savestate_auto_index ( ) ;
2011-01-12 17:05:57 +00:00
2012-01-14 13:08:43 +00:00
g_extern . use_sram = true ;
2012-03-25 22:10:39 +00:00
# ifdef HAVE_XML
2012-03-25 22:04:12 +00:00
bool allow_cheats = true ;
2012-03-25 22:10:39 +00:00
# endif
2011-12-24 12:46:12 +00:00
2011-01-12 20:57:55 +00:00
if ( ! init_rom_file ( g_extern . game_type ) )
2011-01-12 17:05:57 +00:00
goto error ;
2010-10-01 19:39:15 +00:00
2011-02-13 17:19:37 +00:00
init_msg_queue ( ) ;
2011-02-13 16:45:14 +00:00
2011-11-20 19:19:05 +00:00
if ( ! g_extern . sram_load_disable )
load_save_files ( ) ;
2011-11-18 17:03:24 +00:00
else
2012-02-11 20:11:36 +00:00
SSNES_LOG ( " Skipping SRAM load. \n " ) ;
2011-11-20 19:19:05 +00:00
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
2011-11-20 19:19:05 +00:00
init_movie ( ) ;
2012-03-25 22:04:12 +00:00
# endif
2011-02-25 10:47:27 +00:00
2011-03-19 19:41:07 +00:00
# ifdef HAVE_NETPLAY
2011-02-13 15:40:24 +00:00
init_netplay ( ) ;
2011-03-19 19:41:07 +00:00
# endif
2011-02-13 17:19:37 +00:00
init_drivers ( ) ;
2010-10-01 19:39:15 +00:00
2011-11-30 15:41:00 +00:00
# ifdef HAVE_NETPLAY
2011-02-25 10:47:27 +00:00
if ( ! g_extern . netplay )
2011-11-30 15:41:00 +00:00
# endif
2011-02-25 10:47:27 +00:00
init_rewind ( ) ;
2011-09-06 09:55:21 +00:00
2012-01-11 18:22:18 +00:00
init_libsnes_cbs ( ) ;
2011-01-10 13:29:00 +00:00
init_controllers ( ) ;
2011-02-13 16:45:14 +00:00
2011-01-05 19:07:55 +00:00
# ifdef HAVE_FFMPEG
2011-01-12 17:05:57 +00:00
init_recording ( ) ;
2011-01-05 19:07:55 +00:00
# endif
2011-01-03 19:46:50 +00:00
2011-11-30 15:41:00 +00:00
# ifdef HAVE_NETPLAY
2012-01-14 13:08:43 +00:00
g_extern . use_sram = ! g_extern . sram_save_disable & & ! g_extern . netplay_is_client ;
2011-11-30 15:41:00 +00:00
# else
2012-01-14 13:08:43 +00:00
g_extern . use_sram = ! g_extern . sram_save_disable ;
2011-11-30 15:41:00 +00:00
# endif
2011-11-20 19:19:05 +00:00
2012-01-14 13:08:43 +00:00
if ( ! g_extern . use_sram )
2012-02-11 20:11:36 +00:00
SSNES_LOG ( " SRAM will not be saved. \n " ) ;
2011-11-18 17:03:24 +00:00
2012-03-26 01:24:28 +00:00
# ifdef HAVE_THREADS
2012-01-14 13:08:43 +00:00
if ( g_extern . use_sram )
2011-02-13 16:45:14 +00:00
init_autosave ( ) ;
2012-03-26 01:24:28 +00:00
# endif
2011-09-06 09:55:21 +00:00
# ifdef HAVE_XML
2011-11-30 15:41:00 +00:00
# ifdef HAVE_NETPLAY
2012-03-25 22:04:12 +00:00
allow_cheats & = ! g_extern . netplay ;
2011-11-30 15:41:00 +00:00
# endif
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
allow_cheats & = ! g_extern . bsv . movie ;
# endif
if ( allow_cheats )
2011-09-06 09:55:21 +00:00
init_cheats ( ) ;
# endif
2011-01-23 01:23:20 +00:00
2012-01-14 15:08:54 +00:00
g_extern . error_in_init = false ;
2012-01-14 13:08:43 +00:00
return 0 ;
2011-08-13 02:09:08 +00:00
2012-01-14 13:08:43 +00:00
error :
psnes_unload_cartridge ( ) ;
psnes_term ( ) ;
uninit_drivers ( ) ;
uninit_libsnes_sym ( ) ;
2010-05-26 19:27:37 +00:00
2012-01-14 13:08:43 +00:00
return 1 ;
}
bool ssnes_main_iterate ( void )
{
2012-03-21 21:12:18 +00:00
# ifdef HAVE_DYLIB
2012-01-14 13:08:43 +00:00
// DSP plugin GUI events.
if ( g_extern . audio_data . dsp_handle & & g_extern . audio_data . dsp_plugin - > events )
g_extern . audio_data . dsp_plugin - > events ( g_extern . audio_data . dsp_handle ) ;
2012-03-21 21:12:18 +00:00
# endif
2012-01-14 13:08:43 +00:00
// Time to drop?
2012-03-28 22:30:50 +00:00
if ( input_key_pressed_func ( SSNES_QUIT_KEY ) | |
! video_alive_func ( ) )
2012-01-14 13:08:43 +00:00
return false ;
// Checks for stuff like fullscreen, save states, etc.
do_state_checks ( ) ;
// Run libsnes for one frame.
2012-03-21 21:12:18 +00:00
# ifndef SSNES_CONSOLE // On consoles pausing is handled better elsewhere.
2012-01-14 13:08:43 +00:00
if ( ! g_extern . is_paused | | g_extern . is_oneshot )
2012-03-21 21:12:18 +00:00
# endif
2012-01-14 13:08:43 +00:00
{
2011-11-30 16:46:58 +00:00
# ifdef HAVE_THREADS
2012-01-14 13:08:43 +00:00
lock_autosave ( ) ;
2011-11-30 16:46:58 +00:00
# endif
2011-02-14 15:10:53 +00:00
2011-03-19 19:41:07 +00:00
# ifdef HAVE_NETPLAY
2012-01-14 13:08:43 +00:00
if ( g_extern . netplay )
netplay_pre_frame ( g_extern . netplay ) ;
2011-03-19 19:41:07 +00:00
# endif
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
2012-01-14 13:08:43 +00:00
if ( g_extern . bsv . movie )
bsv_movie_set_frame_start ( g_extern . bsv . movie ) ;
2012-03-25 22:04:12 +00:00
# endif
2011-02-14 15:10:53 +00:00
2012-01-14 13:08:43 +00:00
psnes_run ( ) ;
2011-02-14 15:10:53 +00:00
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
2012-01-14 13:08:43 +00:00
if ( g_extern . bsv . movie )
bsv_movie_set_frame_end ( g_extern . bsv . movie ) ;
2012-03-25 22:04:12 +00:00
# endif
2011-03-19 19:41:07 +00:00
# ifdef HAVE_NETPLAY
2012-01-14 13:08:43 +00:00
if ( g_extern . netplay )
netplay_post_frame ( g_extern . netplay ) ;
2011-03-19 19:41:07 +00:00
# endif
2011-02-14 15:10:53 +00:00
2011-11-30 16:46:58 +00:00
# ifdef HAVE_THREADS
2012-01-14 13:08:43 +00:00
unlock_autosave ( ) ;
2011-11-30 16:46:58 +00:00
# endif
2010-05-26 19:27:37 +00:00
}
2012-03-21 21:12:18 +00:00
# ifndef SSNES_CONSOLE
2012-01-14 13:08:43 +00:00
else
{
input_poll ( ) ;
ssnes_sleep ( 10 ) ;
}
2012-03-21 21:12:18 +00:00
# endif
2012-01-14 13:08:43 +00:00
return true ;
}
2010-05-26 19:27:37 +00:00
2012-01-14 13:08:43 +00:00
void ssnes_main_deinit ( void )
{
2011-03-19 19:41:07 +00:00
# ifdef HAVE_NETPLAY
2011-02-13 15:40:24 +00:00
deinit_netplay ( ) ;
2011-03-19 19:41:07 +00:00
# endif
2011-02-13 16:45:14 +00:00
2012-03-26 01:24:28 +00:00
# ifdef HAVE_THREADS
2012-01-14 13:08:43 +00:00
if ( g_extern . use_sram )
2011-02-13 16:45:14 +00:00
deinit_autosave ( ) ;
2012-03-26 01:24:28 +00:00
# endif
2011-02-10 20:16:59 +00:00
2011-01-05 19:07:55 +00:00
# ifdef HAVE_FFMPEG
2011-01-12 17:05:57 +00:00
deinit_recording ( ) ;
2011-01-05 19:07:55 +00:00
# endif
2011-01-03 19:46:50 +00:00
2012-01-14 13:08:43 +00:00
if ( g_extern . use_sram )
2011-02-02 11:10:27 +00:00
save_files ( ) ;
2011-02-25 10:47:27 +00:00
2011-11-30 15:41:00 +00:00
# ifdef HAVE_NETPLAY
2011-02-25 10:47:27 +00:00
if ( ! g_extern . netplay )
2011-11-30 15:41:00 +00:00
# endif
2011-02-25 10:47:27 +00:00
deinit_rewind ( ) ;
2010-05-26 19:27:37 +00:00
2011-04-17 11:30:59 +00:00
# ifdef HAVE_XML
deinit_cheats ( ) ;
# endif
2012-03-25 22:04:12 +00:00
# ifdef HAVE_BSV_MOVIE
2011-02-02 11:10:27 +00:00
deinit_movie ( ) ;
2012-03-25 22:04:12 +00:00
# endif
2011-02-02 11:45:56 +00:00
deinit_msg_queue ( ) ;
2010-12-30 12:54:49 +00:00
psnes_unload_cartridge ( ) ;
psnes_term ( ) ;
2010-05-28 00:45:18 +00:00
uninit_drivers ( ) ;
2011-12-01 21:36:26 +00:00
uninit_libsnes_sym ( ) ;
2012-01-14 13:08:43 +00:00
}
2010-05-26 19:27:37 +00:00
2012-03-28 21:08:57 +00:00
# ifndef SSNES_CONSOLE
// Consoles use the higher level API.
2012-01-14 13:08:43 +00:00
int main ( int argc , char * argv [ ] )
{
int init_ret ;
if ( ( init_ret = ssnes_main_init ( argc , argv ) ) ) return init_ret ;
while ( ssnes_main_iterate ( ) ) ;
ssnes_main_deinit ( ) ;
2012-02-06 14:51:35 +00:00
ssnes_main_clear_state ( ) ;
2012-01-14 19:45:05 +00:00
return 0 ;
2010-05-26 19:27:37 +00:00
}
2012-03-28 21:08:57 +00:00
# endif
2010-05-26 22:39:56 +00:00