2011-01-17 19:54:58 +00:00
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
2011-01-23 19:29:28 +00:00
* Copyright ( C ) 2010 - 2011 - 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/>.
*/
2010-05-27 14:46:22 +00:00
# include <stdbool.h>
2010-05-28 16:07:04 +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>
2010-10-01 19:39:15 +00:00
# include <getopt.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-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"
2011-02-13 15:40:24 +00:00
# include "netplay.h"
2011-04-03 20:29:36 +00:00
# include "strl.h"
2011-05-15 15:16:29 +00:00
# include "screenshot.h"
2011-04-17 11:30:59 +00:00
# include "cheats.h"
2011-10-15 15:47:29 +00:00
# include "audio/utils.h"
2011-01-03 19:46:50 +00:00
# include <assert.h>
2010-12-24 00:07:27 +00:00
2011-02-05 19:46:58 +00:00
# ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# 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 )
2010-08-19 13:56:00 +00:00
driver . video - > set_nonblock_state ( driver . video_data , syncing_state ) ;
2010-12-29 18:18:37 +00:00
if ( g_extern . audio_active )
2010-12-29 18:43:17 +00:00
driver . audio - > set_nonblock_state ( driver . audio_data , ( 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 )
2011-01-14 14:34:38 +00:00
g_extern . audio_data . chunk_size = g_extern . audio_data . nonblock_chunk_size ;
2010-08-16 18:17:01 +00:00
else
2011-01-14 14:34:38 +00:00
g_extern . audio_data . 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-05-15 15:16:29 +00:00
static void take_screenshot ( const uint16_t * frame , unsigned width , unsigned height )
{
2011-08-16 10:11:44 +00:00
if ( ! ( * g_settings . screenshot_directory ) )
2011-05-15 15:16:29 +00:00
return ;
bool ret = screenshot_dump ( g_settings . screenshot_directory , frame ,
width , height , ( height = = 448 | | height = = 478 ) ? 1024 : 2048 ) ;
const char * msg = NULL ;
if ( ret )
{
SSNES_LOG ( " Taking screenshot! \n " ) ;
msg = " Taking screenshot! " ;
}
else
{
SSNES_WARN ( " Failed to take screenshot ... \n " ) ;
msg = " Failed to take screenshot! " ;
}
msg_queue_clear ( g_extern . msg_queue ) ;
msg_queue_push ( g_extern . msg_queue , msg , 1 , 180 ) ;
}
2010-06-27 16:24:26 +00:00
// libsnes: 0.065
// Format received is 16-bit 0RRRRRGGGGGBBBBB
2010-05-28 00:45:18 +00:00
static void video_frame ( const uint16_t * data , unsigned width , unsigned height )
{
2011-05-05 12:13:12 +00:00
if ( ! g_extern . video_active )
2010-05-28 00:45:18 +00:00
return ;
2010-05-26 19:27:37 +00:00
2011-05-05 12:13:12 +00:00
if ( g_settings . video . crop_overscan )
{
if ( height = = 239 )
{
data + = 7 * 1024 ; // Skip 7 top scanlines.
height = 224 ;
}
else if ( height = = 478 )
{
data + = 15 * 512 ; // Skip 15 top scanlines.
height = 448 ;
}
}
2011-05-15 15:16:29 +00:00
if ( g_extern . do_screenshot )
take_screenshot ( data , width , height ) ;
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
2011-08-11 03:25:31 +00:00
if ( g_extern . recording & & ( ! g_extern . filter . active | | ! g_settings . video . post_filter_record ) )
2011-01-05 19:07:55 +00:00
{
struct ffemu_video_data ffemu_data = {
. data = data ,
. pitch = height = = 448 | | height = = 478 ? 1024 : 2048 ,
. width = width ,
2011-08-11 03:25:31 +00:00
. height = height ,
2011-01-05 19:07:55 +00:00
} ;
ffemu_push_video ( g_extern . rec , & ffemu_data ) ;
}
# endif
2011-01-03 19:46:50 +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-03-07 18:56:40 +00:00
g_extern . filter . pitch , data , ( height = = 448 | | height = = 478 ) ? 1024 : 2048 , width , height ) ;
2011-08-11 03:25:31 +00:00
# ifdef HAVE_FFMPEG
if ( g_extern . recording & & g_settings . video . post_filter_record )
{
struct ffemu_video_data ffemu_data = {
. data = g_extern . filter . buffer ,
. pitch = g_extern . filter . pitch ,
. width = owidth ,
. height = oheight ,
} ;
ffemu_push_video ( g_extern . rec , & ffemu_data ) ;
}
# endif
2011-03-07 16:22:03 +00:00
if ( ! driver . video - > frame ( driver . video_data , g_extern . filter . buffer , owidth , oheight , g_extern . filter . pitch , msg ) )
g_extern . video_active = false ;
2010-12-30 00:33:40 +00:00
}
2011-03-07 16:22:03 +00:00
else if ( ! driver . video - > frame ( driver . video_data , data , width , height , ( height = = 448 | | height = = 478 ) ? 1024 : 2048 , msg ) )
g_extern . video_active = false ;
2010-05-29 14:59:57 +00:00
# else
2011-01-23 02:16:14 +00:00
if ( ! driver . video - > frame ( driver . video_data , data , width , height , ( height = = 448 | | height = = 478 ) ? 1024 : 2048 , 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 ;
}
static void video_cached_frame ( void )
{
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:28:46 +00:00
// Push the pipeline through. A hack sort of.
2011-10-20 21:33:15 +00:00
SSNES_LOG ( " Pushing cached frame. \n " ) ;
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-10-15 11:16:32 +00:00
static bool audio_flush ( const int16_t * data , unsigned samples )
2010-05-26 19:27:37 +00:00
{
2011-06-14 19:35:31 +00:00
# ifdef HAVE_FFMPEG
if ( g_extern . recording )
{
struct ffemu_audio_data ffemu_data = {
2011-10-15 11:16:32 +00:00
. data = data ,
. frames = samples / 2
2011-06-14 19:35:31 +00:00
} ;
ffemu_push_audio ( g_extern . rec , & ffemu_data ) ;
}
# endif
2011-10-17 19:30:58 +00:00
if ( g_extern . is_paused )
return true ;
const float * output_data = NULL ;
unsigned output_frames = 0 ;
audio_convert_s16_to_float ( g_extern . audio_data . data , data , samples ) ;
2011-05-13 19:05:28 +00:00
ssnes_dsp_input_t dsp_input = {
. samples = g_extern . audio_data . data ,
2011-10-15 11:16:32 +00:00
. frames = samples / 2
2011-05-13 19:05:28 +00:00
} ;
ssnes_dsp_output_t dsp_output = {
. should_resample = SSNES_TRUE
} ;
if ( g_extern . audio_data . dsp_plugin )
g_extern . audio_data . dsp_plugin - > process ( g_extern . audio_data . dsp_handle , & dsp_output , & dsp_input ) ;
2011-01-31 16:24:31 +00:00
2011-05-13 19:05:28 +00:00
struct hermite_data src_data = {
. data_in = dsp_output . samples ? dsp_output . samples : g_extern . audio_data . data ,
. data_out = g_extern . audio_data . outsamples ,
2011-10-15 11:16:32 +00:00
. input_frames = dsp_output . samples ? dsp_output . frames : ( samples / 2 ) ,
2011-10-15 14:16:13 +00:00
. ratio = g_extern . audio_data . src_ratio ,
. output_frames = samples * g_extern . audio_data . src_ratio ,
2011-05-13 19:05:28 +00:00
} ;
2010-05-26 20:42:58 +00:00
2011-05-13 19:05:28 +00:00
if ( dsp_output . should_resample )
{
2011-02-06 17:38:04 +00:00
hermite_process ( g_extern . audio_data . source , & src_data ) ;
2011-05-13 19:05:28 +00:00
output_data = g_extern . audio_data . outsamples ;
output_frames = src_data . output_frames_gen ;
}
else
{
output_data = dsp_output . samples ;
output_frames = dsp_output . frames ;
}
if ( g_extern . audio_data . use_float )
{
2011-05-13 19:43:32 +00:00
if ( driver . audio - > write ( driver . audio_data , 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-10-15 15:47:29 +00:00
audio_convert_float_to_s16 ( g_extern . audio_data . conv_outsamples ,
output_data , output_frames * 2 ) ;
2010-05-26 20:42:58 +00:00
2011-05-13 19:05:28 +00:00
if ( driver . audio - > write ( driver . audio_data , g_extern . audio_data . conv_outsamples , output_frames * sizeof ( int16_t ) * 2 ) < 0 )
{
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 )
{
if ( ! g_extern . audio_active )
return ;
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 )
{
if ( ! g_extern . audio_active )
return ;
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 ,
g_extern . audio_data . data_ptr ) ;
2011-05-13 19:05:28 +00:00
g_extern . audio_data . data_ptr = 0 ;
2010-05-26 19:27:37 +00:00
}
2010-05-28 00:45:18 +00:00
static void input_poll ( void )
2010-05-26 19:27:37 +00:00
{
2010-05-28 00:45:18 +00:00
driver . input - > poll ( driver . input_data ) ;
2010-05-26 19:27:37 +00:00
}
static int16_t input_state ( bool port , unsigned device , unsigned index , unsigned id )
{
2011-02-25 23:31:13 +00:00
if ( g_extern . bsv_movie & & g_extern . bsv_movie_playback )
2011-02-02 11:10:27 +00:00
{
int16_t ret ;
if ( bsv_movie_get_input ( g_extern . bsv_movie , & ret ) )
return ret ;
else
{
g_extern . bsv_movie_end = true ;
return 0 ;
}
}
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 ] ,
g_settings . input . binds [ 4 ]
} ;
2011-01-10 15:53:37 +00:00
2011-10-16 13:45:04 +00:00
int16_t res = 0 ;
if ( id < 12 )
res = driver . input - > input_state ( driver . input_data , binds , port , device , index , id ) ;
2011-02-25 23:31:13 +00:00
if ( g_extern . bsv_movie & & ! g_extern . bsv_movie_playback )
2011-02-02 11:10:27 +00:00
bsv_movie_set_input ( g_extern . bsv_movie , res ) ;
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-24 21:15:49 +00:00
# ifdef _WIN32
2011-10-18 17:12:28 +00:00
# define PACKAGE_VERSION "0.9-rc4"
2011-01-24 21:15:49 +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-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 " ) ;
_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
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-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-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-06-19 09:11:04 +00:00
puts ( " \t -N/--nodevice: Disconnects the controller device connected to the emulated 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-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. " ) ;
2011-02-02 11:10:27 +00:00
puts ( " \t -P/--bsvplay: Playback a BSV movie file. " ) ;
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. " ) ;
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-09-01 08:06:18 +00:00
puts ( " \t -r/--record: Path to record video file. \n \t \t Using .mkv extension is recommended, and codecs used are FFV1/FLAC. " ) ;
2011-10-06 22:04:52 +00:00
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. " ) ;
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-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 ) ;
exit ( 1 ) ;
}
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 ) ;
}
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 " ) ;
}
}
}
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 ( ) ;
exit ( 1 ) ;
}
else if ( strlen ( g_extern . savestate_name ) = = 0 )
{
SSNES_ERR ( " Need savestate path argument (--savestate) when reading rom from stdin. \n " ) ;
print_help ( ) ;
exit ( 1 ) ;
}
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 ( ) ;
exit ( 1 ) ;
}
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 ( ) ;
exit ( 1 ) ;
}
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 ( ) ;
exit ( 1 ) ;
}
}
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 ( ) ;
2010-05-26 19:27:37 +00:00
exit ( 1 ) ;
}
2010-05-29 13:21:30 +00:00
2011-02-18 13:49:15 +00:00
int val = 0 ;
2010-10-01 19:39:15 +00:00
struct option opts [ ] = {
{ " 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 ' } ,
2011-02-02 11:10:27 +00:00
{ " bsvplay " , 1 , NULL , ' P ' } ,
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 ' } ,
2011-03-23 22:31:33 +00:00
{ " ups " , 1 , NULL , ' U ' } ,
2011-08-17 22:24:57 +00:00
{ " bps " , 1 , & val , ' B ' } ,
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 }
} ;
int option_index = 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-07-09 06:37:08 +00:00
char optstring [ ] = " hs:fvS:m:p4jJg:b:B:Y:Z:P:HC:F:U:DN:X: " 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 ;
2010-10-01 19:39:15 +00:00
int c = getopt_long ( argc , argv , optstring , opts , & option_index ) ;
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 ( ) ;
exit ( 1 ) ;
}
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 ( ) ;
exit ( 1 ) ;
}
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-02-02 11:10:27 +00:00
case ' P ' :
2011-04-03 20:29:36 +00:00
strlcpy ( g_extern . bsv_movie_path , optarg , sizeof ( g_extern . bsv_movie_path ) ) ;
2011-02-02 11:10:27 +00:00
g_extern . bsv_movie_playback = true ;
break ;
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-02-18 02:14:32 +00:00
if ( g_extern . netplay_sync_frames > 32 )
2011-02-15 14:32:26 +00:00
g_extern . netplay_sync_frames = 32 ;
break ;
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 ' :
# ifdef _WIN32
FreeConsole ( ) ;
# endif
break ;
2011-03-23 22:31:33 +00:00
2011-02-18 13:49:15 +00:00
case 0 :
switch ( val )
{
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 ;
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
# 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 ( ) ;
exit ( 1 ) ;
}
ptr + + ;
g_extern . record_height = strtoul ( ptr , & ptr , 0 ) ;
if ( ( * ptr ! = ' \0 ' ) | | errno )
{
SSNES_ERR ( " Wrong format for --size. \n " ) ;
print_help ( ) ;
exit ( 1 ) ;
}
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 ( ) ;
exit ( 1 ) ;
default :
2010-10-01 20:10:28 +00:00
SSNES_ERR ( " Error parsing arguments. \n " ) ;
2010-10-01 19:39:15 +00:00
exit ( 1 ) ;
}
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 )
{
if ( g_extern . recording )
{
2011-08-11 03:25:31 +00:00
// Not perfectly SNES accurate, but this can be adjusted later during processing
// and playback losslessly, and we please the containers more by
// using "sane" values.
2011-02-02 16:19:13 +00:00
struct ffemu_rational ntsc_fps = { 60000 , 1000 } ;
2011-02-02 19:15:00 +00:00
struct ffemu_rational pal_fps = { 50000 , 1000 } ;
2011-01-12 17:05:57 +00:00
struct ffemu_params params = {
2011-06-14 16:45:30 +00:00
. out_width = 256 ,
. out_height = 224 ,
2011-10-06 22:04:52 +00:00
. fb_width = 512 ,
2011-06-15 02:16:53 +00:00
. fb_height = 512 ,
2011-01-12 17:05:57 +00:00
. channels = 2 ,
2011-02-02 16:19:13 +00:00
. samplerate = 32000 ,
2011-01-12 17:05:57 +00:00
. filename = g_extern . record_path ,
. fps = psnes_get_region ( ) = = SNES_REGION_NTSC ? ntsc_fps : pal_fps ,
2011-08-11 03:25:31 +00:00
. rgb32 = false ,
2011-01-12 17:05:57 +00:00
} ;
2011-08-11 03:25:31 +00:00
2011-10-06 21:43:48 +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 )
2011-08-11 03:25:31 +00:00
{
params . out_width = 512 ;
params . out_height = 448 ;
}
2011-10-14 09:00:31 +00:00
if ( g_settings . video . force_aspect )
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
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 = 512 ;
unsigned max_height = 512 ;
g_extern . filter . psize ( & max_width , & max_height ) ;
2011-10-06 22:04:52 +00:00
params . fb_width = next_pow2 ( max_width ) ;
2011-08-11 03:25:31 +00:00
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 " ) ;
2011-01-12 17:05:57 +00:00
g_extern . rec = ffemu_new ( & params ) ;
if ( ! g_extern . rec )
{
SSNES_ERR ( " Failed to start FFmpeg recording. \n " ) ;
g_extern . recording = false ;
}
2011-06-14 16:45:30 +00:00
else
g_settings . video . crop_overscan = true ;
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 )
{
g_extern . msg_queue = msg_queue_new ( 8 ) ;
assert ( g_extern . msg_queue ) ;
}
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-01-31 17:06:57 +00:00
if ( g_settings . rewind_enable )
{
2011-02-02 10:47:05 +00:00
size_t serial_size = psnes_serialize_size ( ) ;
2011-01-31 17:27:21 +00:00
g_extern . state_buf = malloc ( ( serial_size + 3 ) & ~ 3 ) ; // Make sure we allocate at least 4-byte multiple.
2011-02-02 10:47:05 +00:00
psnes_serialize ( g_extern . state_buf , serial_size ) ;
2011-01-31 17:06:57 +00:00
SSNES_LOG ( " Initing rewind buffer with size: %u MB \n " , ( unsigned ) g_settings . rewind_buffer_size / 1000000 ) ;
2011-01-31 17:27:21 +00:00
g_extern . state_manager = state_manager_new ( ( serial_size + 3 ) & ~ 3 , g_settings . rewind_buffer_size , g_extern . state_buf ) ;
2011-01-31 17:06:57 +00:00
if ( ! g_extern . state_manager )
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 ) ;
}
2011-02-02 11:10:27 +00:00
static void init_movie ( void )
{
2011-02-02 11:45:56 +00:00
if ( g_extern . bsv_movie_playback )
{
g_extern . bsv_movie = bsv_movie_init ( g_extern . bsv_movie_path , SSNES_MOVIE_PLAYBACK ) ;
if ( ! g_extern . bsv_movie )
{
SSNES_ERR ( " Failed to load movie file: \" %s \" ! \n " , g_extern . bsv_movie_path ) ;
exit ( 1 ) ;
}
msg_queue_push ( g_extern . msg_queue , " Starting movie playback! " , 2 , 180 ) ;
2011-06-14 20:09:16 +00:00
g_settings . rewind_granularity = 1 ;
2011-02-02 11:45:56 +00:00
SSNES_LOG ( " Starting movie playback! \n " ) ;
}
2011-02-02 11:10:27 +00:00
}
static void deinit_movie ( void )
{
if ( g_extern . bsv_movie )
bsv_movie_free ( g_extern . bsv_movie ) ;
}
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 )
{
if ( g_extern . netplay_enable )
{
struct snes_callbacks cbs = {
. frame_cb = video_frame ,
. sample_cb = audio_sample ,
. state_cb = input_state
} ;
2011-02-13 16:45:14 +00:00
if ( strlen ( g_extern . netplay_server ) > 0 )
2011-02-13 18:50:45 +00:00
{
SSNES_LOG ( " Connecting to netplay host... \n " ) ;
2011-02-13 16:45:14 +00:00
g_extern . netplay_is_client = true ;
2011-02-13 18:50:45 +00:00
}
else
SSNES_LOG ( " Waiting for client... \n " ) ;
2011-02-13 16:45:14 +00:00
2011-02-18 13:49:15 +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 , g_extern . netplay_sync_frames , & cbs ) ;
2011-02-13 15:40:24 +00:00
if ( ! g_extern . netplay )
2011-02-13 16:45:14 +00:00
{
g_extern . netplay_is_client = false ;
2011-02-13 15:40:24 +00:00
SSNES_WARN ( " Failed to init netplay... \n " ) ;
2011-02-13 16:45:14 +00:00
}
2011-02-13 15:40:24 +00:00
}
}
2011-03-19 19:41:07 +00:00
# endif
2011-02-13 15:40:24 +00:00
2011-03-19 19:41:07 +00:00
# ifdef HAVE_NETPLAY
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
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 ] ) ;
}
}
2011-09-27 13:31:25 +00:00
static void set_savestate_auto_index ( void )
{
if ( ! g_settings . savestate_auto_index )
return ;
char state_path [ MAXPATHLEN ] ;
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 )
2011-04-03 20:29:36 +00:00
fill_pathname ( g_extern . savefile_name_srm , g_extern . bsx_rom_path , " .srm " , sizeof ( g_extern . savefile_name_srm ) ) ;
2011-02-11 13:27:19 +00:00
if ( ! g_extern . has_set_state_path )
2011-04-03 20:29:36 +00:00
fill_pathname ( g_extern . savestate_name , g_extern . bsx_rom_path , " .state " , sizeof ( g_extern . savestate_name ) ) ;
fill_pathname ( g_extern . savefile_name_psrm , g_extern . savefile_name_srm , " .psrm " , sizeof ( g_extern . savefile_name_psrm ) ) ;
2011-01-12 20:57:55 +00:00
break ;
case SSNES_CART_SUFAMI :
// SUFAMI ARAM
2011-04-03 20:29:36 +00:00
fill_pathname ( g_extern . savefile_name_asrm , g_extern . savefile_name_srm , " .asrm " , sizeof ( g_extern . savefile_name_asrm ) ) ;
2011-01-12 20:57:55 +00:00
// SUFAMI BRAM
2011-04-03 20:29:36 +00:00
fill_pathname ( g_extern . savefile_name_bsrm , g_extern . savefile_name_srm , " .bsrm " , sizeof ( g_extern . savefile_name_bsrm ) ) ;
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 )
2011-04-03 20:29:36 +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 )
2011-04-03 20:29:36 +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.
2011-04-03 20:29:36 +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
if ( ! g_extern . bsv_movie_playback )
2011-04-03 20:29:36 +00:00
fill_pathname ( g_extern . bsv_movie_path , g_extern . savefile_name_srm , " " , sizeof ( g_extern . bsv_movie_path ) ) ;
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
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-01-12 20:57:55 +00:00
}
2011-01-23 22:09:54 +00:00
// Save or load state here.
static void check_savestates ( void )
{
static bool old_should_savestate = false ;
bool should_savestate = driver . input - > key_pressed ( driver . input_data , SSNES_SAVE_STATE_KEY ) ;
if ( should_savestate & & ! old_should_savestate )
{
2011-09-27 13:31:25 +00:00
if ( g_settings . savestate_auto_index )
g_extern . state_slot + + ;
2011-08-24 13:47:39 +00:00
char save_path [ MAXPATHLEN ] ;
2011-01-31 20:23:07 +00:00
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 ) ;
2011-09-27 13:31:25 +00:00
if ( ! save_state ( save_path ) )
2011-01-23 22:09:54 +00:00
{
msg_queue_clear ( g_extern . msg_queue ) ;
char msg [ 512 ] ;
snprintf ( msg , sizeof ( msg ) , " Failed to save state to \" %s \" " , save_path ) ;
msg_queue_push ( g_extern . msg_queue , msg , 2 , 180 ) ;
}
else
{
msg_queue_clear ( g_extern . msg_queue ) ;
msg_queue_push ( g_extern . msg_queue , " Saved state! " , 1 , 180 ) ;
}
}
old_should_savestate = should_savestate ;
static bool old_should_loadstate = false ;
bool should_loadstate = driver . input - > key_pressed ( driver . input_data , SSNES_LOAD_STATE_KEY ) ;
if ( ! should_savestate & & should_loadstate & & ! old_should_loadstate )
{
2011-08-24 13:47:39 +00:00
char load_path [ MAXPATHLEN ] ;
2011-01-31 20:23:07 +00:00
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 ) ;
2011-01-23 22:09:54 +00:00
2011-09-27 13:31:25 +00:00
if ( ! load_state ( load_path ) )
2011-01-23 22:09:54 +00:00
{
msg_queue_clear ( g_extern . msg_queue ) ;
char msg [ 512 ] ;
snprintf ( msg , sizeof ( msg ) , " Failed to load state from \" %s \" " , load_path ) ;
msg_queue_push ( g_extern . msg_queue , msg , 2 , 180 ) ;
}
else
{
msg_queue_clear ( g_extern . msg_queue ) ;
msg_queue_push ( g_extern . msg_queue , " Loaded state! " , 1 , 180 ) ;
}
}
old_should_loadstate = should_loadstate ;
}
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 ;
bool pressed = driver . input - > key_pressed ( driver . input_data , SSNES_FULLSCREEN_TOGGLE_KEY ) ;
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 )
driver . input - > poll ( driver . input_data ) ;
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
}
static void check_stateslots ( void )
{
// Save state slots
static bool old_should_slot_increase = false ;
bool should_slot_increase = driver . input - > key_pressed ( driver . input_data , SSNES_STATE_SLOT_PLUS ) ;
if ( should_slot_increase & & ! old_should_slot_increase )
{
g_extern . state_slot + + ;
msg_queue_clear ( g_extern . msg_queue ) ;
char msg [ 256 ] ;
2011-02-02 12:37:01 +00:00
snprintf ( msg , sizeof ( msg ) , " Save state/movie slot: %u " , g_extern . state_slot ) ;
2011-01-23 22:09:54 +00:00
msg_queue_push ( g_extern . msg_queue , msg , 1 , 180 ) ;
2011-01-23 22:30:55 +00:00
SSNES_LOG ( " %s \n " , msg ) ;
2011-01-23 22:09:54 +00:00
}
old_should_slot_increase = should_slot_increase ;
static bool old_should_slot_decrease = false ;
bool should_slot_decrease = driver . input - > key_pressed ( driver . input_data , SSNES_STATE_SLOT_MINUS ) ;
if ( should_slot_decrease & & ! old_should_slot_decrease )
{
if ( g_extern . state_slot > 0 )
g_extern . state_slot - - ;
msg_queue_clear ( g_extern . msg_queue ) ;
char msg [ 256 ] ;
2011-02-02 12:37:01 +00:00
snprintf ( msg , sizeof ( msg ) , " Save state/movie slot: %u " , g_extern . state_slot ) ;
2011-01-23 22:09:54 +00:00
msg_queue_push ( g_extern . msg_queue , msg , 1 , 180 ) ;
2011-01-23 22:30:55 +00:00
SSNES_LOG ( " %s \n " , msg ) ;
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 ;
if ( driver . input - > key_pressed ( driver . input_data , SSNES_AUDIO_INPUT_RATE_PLUS ) )
{
g_settings . audio . in_rate + = g_settings . audio . rate_step ;
display = true ;
}
else if ( driver . input - > key_pressed ( driver . input_data , SSNES_AUDIO_INPUT_RATE_MINUS ) )
{
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 ) ;
msg_queue_push ( g_extern . msg_queue , msg , 0 , 180 ) ;
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-10-16 02:01:01 +00:00
if ( g_extern . frame_is_reverse & & g_extern . audio_active ) // 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 ,
g_extern . audio_data . rewind_size - g_extern . audio_data . rewind_ptr ) ;
}
}
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 ;
if ( driver . input - > key_pressed ( driver . input_data , SSNES_REWIND ) )
{
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 ( ) ;
2011-10-26 01:24:39 +00:00
msg_queue_push ( g_extern . msg_queue , " Rewinding! " , 0 , g_extern . is_paused ? 1 : 30 ) ;
2011-02-02 10:47:05 +00:00
psnes_unserialize ( buf , psnes_serialize_size ( ) ) ;
2011-02-25 23:31:13 +00:00
2011-02-25 11:37:05 +00:00
if ( g_extern . bsv_movie )
2011-06-14 20:09:16 +00:00
bsv_movie_frame_rewind ( g_extern . bsv_movie ) ;
2011-01-31 16:24:31 +00:00
}
2011-01-31 17:49:50 +00:00
else
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.
2011-02-26 14:09:04 +00:00
if ( cnt = = 0 | | g_extern . bsv_movie )
2011-02-01 16:30:18 +00:00
{
2011-02-02 10:47:05 +00:00
psnes_serialize ( g_extern . state_buf , psnes_serialize_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
}
2011-02-02 11:45:56 +00:00
static void check_movie_record ( void )
{
static bool old_button = false ;
bool new_button ;
if ( ( new_button = driver . input - > key_pressed ( driver . input_data , SSNES_MOVIE_RECORD_TOGGLE ) ) & & ! old_button )
{
if ( g_extern . bsv_movie )
{
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
{
2011-06-14 20:09:16 +00:00
g_settings . rewind_granularity = 1 ;
2011-02-02 12:37:01 +00:00
char path [ 512 ] ;
if ( g_extern . state_slot > 0 )
snprintf ( path , sizeof ( path ) , " %s%d.bsv " , g_extern . bsv_movie_path , g_extern . state_slot ) ;
else
snprintf ( path , sizeof ( path ) , " %s.bsv " , g_extern . bsv_movie_path ) ;
g_extern . bsv_movie = bsv_movie_init ( path , SSNES_MOVIE_RECORD ) ;
2011-02-02 11:45:56 +00:00
msg_queue_clear ( g_extern . msg_queue ) ;
2011-02-14 19:11:46 +00:00
msg_queue_push ( g_extern . msg_queue , g_extern . bsv_movie ? " Starting movie record! " : " Failed to start movie record! " , 1 , 180 ) ;
2011-02-02 11:45:56 +00:00
if ( g_extern . bsv_movie )
SSNES_LOG ( " Starting movie record! \n " ) ;
else
SSNES_ERR ( " Failed to start movie record! \n " ) ;
}
}
old_button = new_button ;
}
2011-02-05 19:46:58 +00:00
static void check_pause ( void )
{
static bool old_state = false ;
bool new_state = driver . input - > key_pressed ( driver . input_data , 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.
new_state | = ! g_extern . is_paused & & driver . input - > key_pressed ( driver . input_data , SSNES_FRAMEADVANCE ) ;
2011-02-05 20:45:44 +00:00
static bool old_focus = true ;
bool focus = true ;
if ( g_settings . pause_nonactive )
focus = driver . video - > focus ( driver . video_data ) ;
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 )
{
SSNES_LOG ( " Paused! \n " ) ;
if ( driver . audio_data )
driver . audio - > stop ( driver . audio_data ) ;
}
else
{
SSNES_LOG ( " Unpaused! \n " ) ;
if ( driver . audio_data )
2011-02-06 22:55:17 +00:00
{
if ( ! driver . audio - > start ( driver . audio_data ) )
{
SSNES_ERR ( " Failed to resume audio driver! Will continue without audio! \n " ) ;
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 )
{
SSNES_LOG ( " Unpaused! \n " ) ;
g_extern . is_paused = false ;
if ( driver . audio_data )
2011-02-06 22:55:17 +00:00
{
if ( ! driver . audio - > start ( driver . audio_data ) )
{
SSNES_ERR ( " Failed to resume audio driver! Will continue without audio! \n " ) ;
g_extern . audio_active = false ;
}
}
2011-02-05 20:45:44 +00:00
}
else if ( ! focus & & old_focus )
{
SSNES_LOG ( " Paused! \n " ) ;
g_extern . is_paused = true ;
if ( driver . audio_data )
driver . audio - > stop ( driver . audio_data ) ;
}
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 ;
}
2011-10-17 19:30:58 +00:00
static void check_oneshot ( void )
{
static bool old_state = false ;
bool new_state = driver . input - > key_pressed ( driver . input_data , 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 ;
bool new_rewind_state = driver . input - > key_pressed ( driver . input_data , SSNES_REWIND ) ;
g_extern . is_oneshot | = new_rewind_state & & ! old_rewind_state ;
old_rewind_state = new_rewind_state ;
2011-10-17 19:30:58 +00:00
}
2011-03-24 19:41:28 +00:00
static void check_reset ( void )
{
if ( driver . input - > key_pressed ( driver . input_data , SSNES_RESET ) )
{
SSNES_LOG ( " Resetting game! \n " ) ;
msg_queue_clear ( g_extern . msg_queue ) ;
msg_queue_push ( g_extern . msg_queue , " Reset! " , 1 , 120 ) ;
psnes_reset ( ) ;
init_controllers ( ) ; // bSNES since v073r01 resets controllers to JOYPAD after a reset, so just enforce it here.
}
}
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 ;
bool pressed_next = driver . input - > key_pressed ( driver . input_data , SSNES_SHADER_NEXT ) ;
bool pressed_prev = driver . input - > key_pressed ( driver . input_data , SSNES_SHADER_PREV ) ;
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 ) ;
if ( ! driver . video - > xml_shader ( driver . video_data , shader ) )
SSNES_WARN ( " Failed to apply shader! \n " ) ;
}
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 ;
bool pressed_next = driver . input - > key_pressed ( driver . input_data , SSNES_CHEAT_INDEX_PLUS ) ;
bool pressed_prev = driver . input - > key_pressed ( driver . input_data , SSNES_CHEAT_INDEX_MINUS ) ;
bool pressed_toggle = driver . input - > key_pressed ( driver . input_data , SSNES_CHEAT_TOGGLE ) ;
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
2011-05-15 15:16:29 +00:00
static void check_screenshot ( void )
{
static bool old_pressed = false ;
bool pressed = driver . input - > key_pressed ( driver . input_data , SSNES_SCREENSHOT ) ;
g_extern . do_screenshot = pressed & & ! old_pressed ;
old_pressed = pressed ;
}
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 ;
bool pressed = driver . input - > key_pressed ( driver . input_data , SSNES_DSP_CONFIG ) ;
if ( pressed & & ! old_pressed )
g_extern . audio_data . dsp_plugin - > config ( g_extern . audio_data . dsp_handle ) ;
old_pressed = pressed ;
}
# endif
2011-01-23 22:09:54 +00:00
static void do_state_checks ( void )
{
2011-02-13 15:40:24 +00:00
if ( ! g_extern . netplay )
{
check_pause ( ) ;
2011-10-17 19:30:58 +00:00
check_oneshot ( ) ;
2011-10-18 12:39:54 +00:00
2011-10-18 15:26:15 +00:00
if ( check_fullscreen ( ) & & g_extern . is_paused )
video_cached_frame ( ) ;
2011-02-05 19:46:58 +00:00
2011-10-18 12:39:54 +00:00
if ( g_extern . is_paused & & ! g_extern . is_oneshot )
return ;
2011-05-24 00:31:21 +00:00
set_fast_forward_button (
driver . input - > key_pressed ( driver . input_data , SSNES_FAST_FORWARD_KEY ) ,
driver . input - > key_pressed ( driver . input_data , SSNES_FAST_FORWARD_HOLD_KEY ) ) ;
2011-01-23 22:09:54 +00:00
2011-02-13 15:40:24 +00:00
if ( ! g_extern . bsv_movie )
{
check_stateslots ( ) ;
check_savestates ( ) ;
}
2011-10-18 19:57:28 +00:00
check_rewind ( ) ;
2011-02-13 15:40:24 +00:00
if ( ! g_extern . bsv_movie_playback )
check_movie_record ( ) ;
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-02-02 11:10:27 +00:00
}
2011-10-18 12:39:54 +00:00
else
check_fullscreen ( ) ;
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-05-15 15:16:29 +00:00
check_screenshot ( ) ;
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 )
{
// Make absolutely sure our big global structs are in-fact zeroed out.
memset ( & g_extern , 0 , sizeof ( g_extern ) ) ;
memset ( & g_settings , 0 , sizeof ( g_settings ) ) ;
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
2010-10-01 19:39:15 +00:00
int main ( int argc , char * argv [ ] )
{
2011-08-26 15:32:04 +00:00
init_state ( ) ;
2010-12-29 20:12:56 +00:00
parse_input ( argc , argv ) ;
2010-12-30 00:33:40 +00:00
parse_config ( ) ;
2010-12-30 12:54:49 +00:00
init_dlsym ( ) ;
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 ) ;
2010-12-30 12:54:49 +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
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
init_movie ( ) ;
if ( ! g_extern . bsv_movie )
load_save_files ( ) ;
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-02-25 10:47:27 +00:00
if ( ! g_extern . netplay )
init_rewind ( ) ;
2011-09-06 09:55:21 +00:00
2011-03-19 19:41:07 +00:00
# ifdef HAVE_NETPLAY
2011-02-13 15:40:24 +00:00
psnes_set_video_refresh ( g_extern . netplay ? video_frame_net : video_frame ) ;
psnes_set_audio_sample ( g_extern . netplay ? audio_sample_net : audio_sample ) ;
psnes_set_input_state ( g_extern . netplay ? input_state_net : input_state ) ;
2011-03-19 19:41:07 +00:00
# else
psnes_set_video_refresh ( video_frame ) ;
psnes_set_audio_sample ( audio_sample ) ;
psnes_set_input_state ( input_state ) ;
# endif
2011-06-26 13:32:24 +00:00
psnes_set_input_poll ( input_poll ) ;
2011-01-12 17:05:57 +00:00
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-02-13 16:45:14 +00:00
if ( ! g_extern . bsv_movie_playback & & ! g_extern . netplay_is_client )
init_autosave ( ) ;
2011-09-06 09:55:21 +00:00
# ifdef HAVE_XML
if ( ! g_extern . netplay & & ! g_extern . bsv_movie )
init_cheats ( ) ;
# endif
2011-01-23 01:23:20 +00:00
2011-01-08 17:37:45 +00:00
// Main loop
2011-09-17 08:57:55 +00:00
for ( ; ; )
2010-05-26 19:27:37 +00:00
{
2011-08-13 02:09:08 +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 ) ;
2011-01-08 18:15:18 +00:00
// Time to drop?
2011-01-08 17:37:45 +00:00
if ( driver . input - > key_pressed ( driver . input_data , SSNES_QUIT_KEY ) | |
2011-02-02 11:10:27 +00:00
! driver . video - > alive ( driver . video_data ) | | g_extern . bsv_movie_end )
2010-05-26 19:27:37 +00:00
break ;
2011-01-23 22:09:54 +00:00
// Checks for stuff like fullscreen, save states, etc.
do_state_checks ( ) ;
2011-01-23 01:23:20 +00:00
2011-01-08 18:15:18 +00:00
// Run libsnes for one frame.
2011-10-17 19:30:58 +00:00
if ( ! g_extern . is_paused | | g_extern . is_oneshot )
2011-02-10 20:16:59 +00:00
{
2011-02-11 12:44:31 +00:00
lock_autosave ( ) ;
2011-02-14 15:10:53 +00:00
2011-03-19 19:41:07 +00:00
# ifdef HAVE_NETPLAY
2011-02-14 15:10:53 +00:00
if ( g_extern . netplay )
netplay_pre_frame ( g_extern . netplay ) ;
2011-03-19 19:41:07 +00:00
# endif
2011-02-25 23:31:13 +00:00
if ( g_extern . bsv_movie )
bsv_movie_set_frame_start ( g_extern . bsv_movie ) ;
2011-02-14 15:10:53 +00:00
2011-02-05 19:46:58 +00:00
psnes_run ( ) ;
2011-02-14 15:10:53 +00:00
2011-02-25 23:59:17 +00:00
if ( g_extern . bsv_movie )
2011-02-25 10:47:27 +00:00
bsv_movie_set_frame_end ( g_extern . bsv_movie ) ;
2011-03-19 19:41:07 +00:00
# ifdef HAVE_NETPLAY
2011-02-14 15:10:53 +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-02-11 12:44:31 +00:00
unlock_autosave ( ) ;
2011-02-10 20:16:59 +00:00
}
2011-02-05 19:46:58 +00:00
else
{
input_poll ( ) ;
2011-10-17 18:46:38 +00:00
ssnes_sleep ( 10 ) ;
2011-02-05 19:46:58 +00:00
}
2010-05-26 19:27:37 +00:00
}
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
if ( ! g_extern . bsv_movie_playback & & ! g_extern . netplay_is_client )
deinit_autosave ( ) ;
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
2011-02-13 16:45:14 +00:00
if ( ! g_extern . bsv_movie_playback & & ! g_extern . netplay_is_client )
2011-02-02 11:10:27 +00:00
save_files ( ) ;
2011-02-25 10:47:27 +00:00
if ( ! g_extern . netplay )
deinit_rewind ( ) ;
2010-05-26 19:27:37 +00:00
2011-04-17 11:30:59 +00:00
# ifdef HAVE_XML
deinit_cheats ( ) ;
# endif
2011-02-02 11:10:27 +00:00
deinit_movie ( ) ;
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 ( ) ;
2010-12-30 12:54:49 +00:00
uninit_dlsym ( ) ;
2010-05-26 19:27:37 +00:00
return 0 ;
2010-06-27 13:46:23 +00:00
error :
2010-12-30 12:54:49 +00:00
psnes_unload_cartridge ( ) ;
psnes_term ( ) ;
2010-06-27 13:46:23 +00:00
uninit_drivers ( ) ;
2010-12-30 12:54:49 +00:00
uninit_dlsym ( ) ;
2010-06-27 13:46:23 +00:00
return 1 ;
2010-05-26 19:27:37 +00:00
}
2010-05-26 22:39:56 +00:00
2011-08-26 15:32:04 +00:00