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>
2010-10-01 19:39:15 +00:00
# include <getopt.h>
2011-02-05 19:46:58 +00:00
# include <time.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-01-03 19:46:50 +00:00
# include <assert.h>
2011-01-07 10:09:56 +00:00
# ifdef HAVE_SRC
# include <samplerate.h>
# endif
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.
// I had issues including this in Win32 for some reason. :)
2011-02-04 21:47:37 +00:00
# endif
2010-12-29 18:18:37 +00:00
struct global g_extern = {
. video_active = true ,
2010-12-29 19:05:57 +00:00
. audio_active = true ,
2011-01-12 17:05:57 +00:00
. game_type = SSNES_CART_NORMAL ,
2010-12-29 18:18:37 +00:00
} ;
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-01-08 17:37:45 +00:00
static void set_fast_forward_button ( bool new_button_state )
2010-08-16 16:40:17 +00:00
{
static bool old_button_state = false ;
static bool syncing_state = false ;
if ( new_button_state & & ! old_button_state )
{
syncing_state = ! syncing_state ;
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
}
old_button_state = new_button_state ;
}
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 )
{
2010-12-29 18:18:37 +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-01-05 19:07:55 +00:00
# ifdef HAVE_FFMPEG
if ( g_extern . recording )
{
struct ffemu_video_data ffemu_data = {
. data = data ,
. pitch = height = = 448 | | height = = 478 ? 1024 : 2048 ,
. width = width ,
. height = height
} ;
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 ) ;
2010-12-30 00:33:40 +00:00
# ifdef HAVE_FILTER
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-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
2010-05-26 19:27:37 +00:00
}
2010-05-28 00:45:18 +00:00
static void audio_sample ( uint16_t left , uint16_t right )
2010-05-26 19:27:37 +00:00
{
2010-12-29 18:18:37 +00:00
if ( ! g_extern . audio_active )
2010-05-28 00:45:18 +00:00
return ;
2011-01-05 19:07:55 +00:00
# ifdef HAVE_FFMPEG
if ( g_extern . recording )
{
static int16_t static_data [ 2 ] ;
static_data [ 0 ] = left ;
static_data [ 1 ] = right ;
struct ffemu_audio_data ffemu_data = {
. data = static_data ,
. frames = 1
} ;
ffemu_push_audio ( g_extern . rec , & ffemu_data ) ;
}
# endif
2011-01-03 19:46:50 +00:00
2011-02-06 12:29:48 +00:00
g_extern . audio_data . data [ g_extern . audio_data . data_ptr + + ] = ( float ) ( int16_t ) left / 0x8000 ;
g_extern . audio_data . data [ g_extern . audio_data . data_ptr + + ] = ( float ) ( int16_t ) right / 0x8000 ;
2010-05-26 19:27:37 +00:00
2011-01-14 14:34:38 +00:00
if ( g_extern . audio_data . data_ptr > = g_extern . audio_data . chunk_size )
2010-05-26 20:42:58 +00:00
{
2011-01-31 16:24:31 +00:00
if ( g_extern . frame_is_reverse ) // Disable fucked up audio when rewinding...
memset ( g_extern . audio_data . data , 0 , g_extern . audio_data . chunk_size * sizeof ( float ) ) ;
2011-02-06 12:29:48 +00:00
# ifdef HAVE_SRC
SRC_DATA src_data = {
2011-02-06 17:38:04 +00:00
# else
struct hermite_data src_data = {
# endif
2011-02-06 12:29:48 +00:00
. data_in = g_extern . audio_data . data ,
. data_out = g_extern . audio_data . outsamples ,
. input_frames = g_extern . audio_data . chunk_size / 2 ,
. output_frames = g_extern . audio_data . chunk_size * 8 ,
. end_of_input = 0 ,
. src_ratio = ( double ) g_settings . audio . out_rate / ( double ) g_settings . audio . in_rate ,
} ;
2010-05-26 20:42:58 +00:00
2011-02-06 12:29:48 +00:00
# ifdef HAVE_SRC
src_process ( g_extern . audio_data . source , & src_data ) ;
# else
2011-02-06 17:38:04 +00:00
hermite_process ( g_extern . audio_data . source , & src_data ) ;
2011-02-06 12:29:48 +00:00
# endif
2011-01-15 19:37:42 +00:00
if ( g_extern . audio_data . use_float )
2010-08-16 17:16:03 +00:00
{
2011-01-14 14:34:38 +00:00
if ( driver . audio - > write ( driver . audio_data , g_extern . audio_data . outsamples , src_data . output_frames_gen * sizeof ( float ) * 2 ) < 0 )
{
fprintf ( stderr , " SSNES [ERROR]: Audio backend failed to write. Will continue without sound. \n " ) ;
g_extern . audio_active = false ;
}
}
else
{
2011-02-06 17:38:04 +00:00
for ( unsigned i = 0 ; i < src_data . output_frames_gen * 2 ; i + + )
2011-02-06 12:29:48 +00:00
{
int32_t val = g_extern . audio_data . outsamples [ i ] * 0x8000 ;
g_extern . audio_data . conv_outsamples [ i ] = ( val > 0x7FFF ) ? 0x7FFF : ( val < - 0x8000 ? - 0x8000 : ( int16_t ) val ) ;
}
2011-01-14 14:34:38 +00:00
if ( driver . audio - > write ( driver . audio_data , g_extern . audio_data . conv_outsamples , src_data . output_frames_gen * sizeof ( int16_t ) * 2 ) < 0 )
{
fprintf ( stderr , " SSNES [ERROR]: Audio backend failed to write. Will continue without sound. \n " ) ;
g_extern . audio_active = false ;
}
2010-08-16 17:16:03 +00:00
}
2010-05-26 20:42:58 +00:00
2011-01-14 14:34:38 +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-01-10 15:53:37 +00:00
const struct snes_keybind * binds [ MAX_PLAYERS ] ;
for ( int i = 0 ; i < MAX_PLAYERS ; i + + )
binds [ i ] = g_settings . input . binds [ i ] ;
2011-02-02 11:10:27 +00:00
int16_t 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
}
2010-08-28 15:20:29 +00:00
static void fill_pathname ( char * out_path , char * in_path , const char * replace )
{
char tmp_path [ strlen ( in_path ) + 1 ] ;
strcpy ( tmp_path , in_path ) ;
char * tok = NULL ;
tok = strrchr ( tmp_path , ' . ' ) ;
if ( tok ! = NULL )
* tok = ' \0 ' ;
strcpy ( out_path , tmp_path ) ;
strcat ( out_path , replace ) ;
}
2010-05-26 19:27:37 +00:00
2011-01-07 16:59:53 +00:00
# ifdef _WIN32
# define SSNES_DEFAULT_CONF_PATH_STR "\n\tDefaults to ssnes.cfg in same directory as ssnes.exe"
2011-02-22 10:28:28 +00:00
# elif defined(__APPLE__)
# define SSNES_DEFAULT_CONF_PATH_STR " Defaults to $HOME / .ssnes.cfg"
2011-01-07 16:59:53 +00:00
# else
# define SSNES_DEFAULT_CONF_PATH_STR " Defaults to $XDG_CONFIG_HOME / ssnes / ssnes.cfg"
# endif
2011-01-24 21:15:49 +00:00
# ifdef _WIN32
2011-03-30 09:05:56 +00:00
# define PACKAGE_VERSION "0.4-beta"
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 " ) ;
_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 " ) ;
_PSUPP ( filter , " Filter " , " CPU based video filters " ) ;
_PSUPP ( cg , " Cg " , " Cg pixel shaders " ) ;
_PSUPP ( xml , " XML " , " bSNES XML pixel shaders " ) ;
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 " ) ;
_PSUPP ( src , " SRC " , " libsamplerate audio resampling " ) ;
_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-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...] " ) ;
2010-10-01 19:39:15 +00:00
puts ( " \t -h/--help: Show this help message " ) ;
puts ( " \t -s/--save: Path for save file (*.srm). Required when rom is input from stdin " ) ;
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-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
puts ( " \t -r/--record: Path to record video file. Settings for video/audio codecs are found in config file. " ) ;
# endif
2010-10-01 20:10:28 +00:00
puts ( " \t -v/--verbose: Verbose logging " ) ;
2011-03-23 22:31:33 +00:00
puts ( " \t -U/--ups: Specifies path for UPS patch that will be applied to ROM. \n " ) ;
2011-01-25 12:03:53 +00:00
print_features ( ) ;
2010-10-01 19:39:15 +00:00
}
2011-02-11 13:27:19 +00:00
static void set_basename ( const char * path )
{
char tmp [ strlen ( path ) + 1 ] ;
strcpy ( tmp , path ) ;
char * dst = strrchr ( tmp , ' . ' ) ;
if ( dst )
* dst = ' \0 ' ;
strncpy ( g_extern . basename , tmp , sizeof ( g_extern . basename ) - 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-01-05 19:07:55 +00:00
# ifdef HAVE_FFMPEG
{ " record " , 1 , NULL , ' r ' } ,
# 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-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 ' } ,
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-03-23 22:31:33 +00:00
char optstring [ ] = " hs:vS:m:p4jJg:b:B:Y:Z:P:HC:F:U: " FFMPEG_RECORD_ARG CONFIG_FILE_ARG ;
2010-10-01 19:39:15 +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-01-08 18:15:18 +00:00
strncpy ( g_extern . savefile_name_srm , optarg , sizeof ( g_extern . savefile_name_srm ) - 1 ) ;
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-01-12 17:05:57 +00:00
case ' g ' :
strncpy ( g_extern . gb_rom_path , optarg , sizeof ( g_extern . gb_rom_path ) - 1 ) ;
g_extern . game_type = SSNES_CART_SGB ;
break ;
2011-01-12 20:57:55 +00:00
case ' b ' :
strncpy ( g_extern . bsx_rom_path , optarg , sizeof ( g_extern . bsx_rom_path ) - 1 ) ;
g_extern . game_type = SSNES_CART_BSX ;
break ;
case ' B ' :
strncpy ( g_extern . bsx_rom_path , optarg , sizeof ( g_extern . bsx_rom_path ) - 1 ) ;
g_extern . game_type = SSNES_CART_BSX_SLOTTED ;
break ;
case ' Y ' :
strncpy ( g_extern . sufami_rom_path [ 0 ] , optarg , sizeof ( g_extern . sufami_rom_path [ 0 ] ) - 1 ) ;
g_extern . game_type = SSNES_CART_SUFAMI ;
break ;
case ' Z ' :
strncpy ( g_extern . sufami_rom_path [ 1 ] , optarg , sizeof ( g_extern . sufami_rom_path [ 1 ] ) - 1 ) ;
g_extern . game_type = SSNES_CART_SUFAMI ;
break ;
2011-01-12 17:07:31 +00:00
case ' S ' :
2011-01-08 18:15:18 +00:00
strncpy ( g_extern . savestate_name , optarg , sizeof ( g_extern . savestate_name ) - 1 ) ;
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 ;
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 ' :
strncpy ( g_extern . config_path , optarg , sizeof ( g_extern . config_path ) - 1 ) ;
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 ' :
strncpy ( g_extern . record_path , optarg , sizeof ( g_extern . record_path ) - 1 ) ;
g_extern . recording = true ;
break ;
# endif
2011-02-02 11:10:27 +00:00
case ' P ' :
strncpy ( g_extern . bsv_movie_path , optarg , sizeof ( g_extern . bsv_movie_path ) - 1 ) ;
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 ;
strncpy ( g_extern . netplay_server , optarg , sizeof ( g_extern . netplay_server ) - 1 ) ;
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 ' :
strncpy ( g_extern . ups_name , optarg , sizeof ( g_extern . ups_name ) - 1 ) ;
break ;
2011-02-18 13:49:15 +00:00
case 0 :
switch ( val )
{
case ' p ' :
g_extern . netplay_port = strtol ( optarg , NULL , 0 ) ;
break ;
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-02-11 13:27:19 +00:00
set_basename ( argv [ optind ] ) ;
2010-10-01 20:10:28 +00:00
SSNES_LOG ( " Opening file: \" %s \" \n " , argv [ optind ] ) ;
2010-12-29 18:43:17 +00:00
g_extern . rom_file = fopen ( argv [ optind ] , " rb " ) ;
if ( g_extern . rom_file = = NULL )
2010-10-01 19:39:15 +00:00
{
2011-01-08 13:40:54 +00:00
SSNES_ERR ( " Could not open file: \" %s \" \n " , argv [ optind ] ) ;
2010-10-01 19:39:15 +00:00
exit ( 1 ) ;
}
2011-01-08 18:15:18 +00:00
// strl* would be nice :D
2011-02-11 13:27:19 +00:00
if ( ! g_extern . has_set_save_path )
fill_pathname ( g_extern . savefile_name_srm , g_extern . basename , " .srm " ) ;
if ( ! g_extern . has_set_state_path )
fill_pathname ( g_extern . savestate_name , g_extern . basename , " .state " ) ;
2010-10-01 19:39:15 +00:00
}
2010-12-29 18:43:17 +00:00
else if ( strlen ( g_extern . savefile_name_srm ) = = 0 )
2010-10-01 20:10:28 +00:00
{
2011-01-08 18:15:18 +00:00
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 (--savefile) when reading rom from stdin. \n " ) ;
2010-10-01 20:10:28 +00:00
print_help ( ) ;
exit ( 1 ) ;
}
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 )
{
SSNES_LOG ( " Connecting multitap to port 2. \n " ) ;
psnes_set_controller_port_device ( SNES_PORT_2 , SNES_DEVICE_MULTITAP ) ;
}
else
{
for ( int i = 0 ; i < 2 ; i + + )
2011-01-10 13:29:00 +00:00
{
2011-01-10 15:53:37 +00:00
if ( g_extern . has_mouse [ i ] )
{
SSNES_LOG ( " Connecting mouse to port %d \n " , i + 1 ) ;
psnes_set_controller_port_device ( i , SNES_DEVICE_MOUSE ) ;
}
else if ( g_extern . has_scope [ i ] )
{
SSNES_LOG ( " Connecting scope to port %d \n " , i + 1 ) ;
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-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 :
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
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-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 )
{
// Hardcode these options at the moment. Should be specificed in the config file later on.
if ( g_extern . recording )
{
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 = {
. vcodec = FFEMU_VIDEO_H264 ,
. acodec = FFEMU_AUDIO_VORBIS ,
. rescaler = FFEMU_RESCALER_POINT ,
. out_width = 512 ,
. out_height = 448 ,
. 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 ,
. aspect_ratio = 4.0 / 3
} ;
SSNES_LOG ( " Recording with FFmpeg to %s. \n " , g_extern . record_path ) ;
g_extern . rec = ffemu_new ( & params ) ;
if ( ! g_extern . rec )
{
SSNES_ERR ( " Failed to start FFmpeg recording. \n " ) ;
g_extern . recording = false ;
}
}
}
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-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 ) ;
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 ,
. poll_cb = input_poll ,
. 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-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 )
fill_pathname ( g_extern . savefile_name_srm , g_extern . bsx_rom_path , " .srm " ) ;
if ( ! g_extern . has_set_state_path )
fill_pathname ( g_extern . savestate_name , g_extern . bsx_rom_path , " .state " ) ;
2011-01-12 20:57:55 +00:00
fill_pathname ( g_extern . savefile_name_psrm , g_extern . savefile_name_srm , " .psrm " ) ;
break ;
case SSNES_CART_SUFAMI :
// SUFAMI ARAM
fill_pathname ( g_extern . savefile_name_asrm , g_extern . savefile_name_srm , " .asrm " ) ;
// SUFAMI BRAM
fill_pathname ( g_extern . savefile_name_bsrm , g_extern . savefile_name_srm , " .bsrm " ) ;
break ;
2011-02-11 13:27:19 +00:00
case SSNES_CART_SGB :
if ( ! g_extern . has_set_save_path )
fill_pathname ( g_extern . savefile_name_srm , g_extern . gb_rom_path , " .srm " ) ;
if ( ! g_extern . has_set_state_path )
fill_pathname ( g_extern . savestate_name , g_extern . gb_rom_path , " .state " ) ;
fill_pathname ( g_extern . savefile_name_rtc , g_extern . savefile_name_srm , " .rtc " ) ;
break ;
2011-01-12 20:57:55 +00:00
default :
// Infer .rtc save path from save ram path.
fill_pathname ( g_extern . savefile_name_rtc , g_extern . savefile_name_srm , " .rtc " ) ;
}
2011-02-11 13:27:19 +00:00
if ( ! g_extern . bsv_movie_playback )
fill_pathname ( g_extern . bsv_movie_path , g_extern . savefile_name_srm , " " ) ;
2011-03-23 22:31:33 +00:00
if ( ! ( * g_extern . ups_name ) & & * g_extern . basename )
fill_pathname ( g_extern . ups_name , g_extern . basename , " .ups " ) ;
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 )
{
char save_path [ strlen ( g_extern . savestate_name ) * 2 ] ;
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-01-23 22:09:54 +00:00
if ( ! save_state ( save_path ) )
{
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 )
{
char load_path [ strlen ( g_extern . savestate_name ) * 2 ] ;
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
if ( ! load_state ( load_path ) )
{
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 ;
}
static void check_fullscreen ( void )
{
// If we go fullscreen we drop all drivers and reinit to be safe.
if ( driver . input - > key_pressed ( driver . input_data , SSNES_FULLSCREEN_TOGGLE_KEY ) )
{
g_settings . video . fullscreen = ! g_settings . video . fullscreen ;
uninit_drivers ( ) ;
init_drivers ( ) ;
}
}
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-01-31 15:48:42 +00:00
static void check_rewind ( void )
{
2011-01-31 16:24:31 +00:00
g_extern . frame_is_reverse = false ;
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-01-31 17:27:21 +00:00
msg_queue_push ( g_extern . msg_queue , " Rewinding! " , 0 , 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 )
{
for ( unsigned i = 0 ; i < ( g_settings . rewind_granularity ? g_settings . rewind_granularity : 1 ) ; i + + )
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-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-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
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-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 ;
static bool old_pressed_prev = true ;
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
strncpy ( g_settings . video . bsnes_shader_path , shader , sizeof ( g_settings . video . bsnes_shader_path ) - 1 ) ;
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 ;
}
# 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 )
{
2011-03-24 19:41:28 +00:00
check_reset ( ) ;
2011-02-13 15:40:24 +00:00
check_pause ( ) ;
if ( g_extern . is_paused )
return ;
2011-02-05 19:46:58 +00:00
2011-02-13 15:40:24 +00:00
set_fast_forward_button ( driver . input - > key_pressed ( driver . input_data , SSNES_FAST_FORWARD_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-02-25 10:47:27 +00:00
check_rewind ( ) ;
2011-02-13 15:40:24 +00:00
if ( ! g_extern . bsv_movie_playback )
check_movie_record ( ) ;
2011-03-29 16:28:31 +00:00
# ifdef HAVE_XML
check_shader_dir ( ) ;
# endif
2011-02-02 11:10:27 +00:00
}
2011-02-13 15:40:24 +00:00
2011-01-23 22:09:54 +00:00
check_fullscreen ( ) ;
2011-01-29 17:42:21 +00:00
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 )
{
if ( psnes_library_id )
snprintf ( g_extern . title_buf , sizeof ( g_extern . title_buf ) , " SSNES : %s " , psnes_library_id ( ) ) ;
else
snprintf ( g_extern . title_buf , sizeof ( g_extern . title_buf ) , " SSNES " ) ;
}
2011-01-12 17:05:57 +00:00
2010-10-01 19:39:15 +00:00
int main ( int argc , char * argv [ ] )
{
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 ( ) ;
2010-12-30 13:11:56 +00:00
if ( strlen ( g_extern . basename ) > 0 )
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-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-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_poll ( g_extern . netplay ? input_poll_net : input_poll ) ;
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_poll ( input_poll ) ;
psnes_set_input_state ( input_state ) ;
# endif
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-01-23 01:23:20 +00:00
2011-01-08 17:37:45 +00:00
// Main loop
2010-05-26 19:27:37 +00:00
for ( ; ; )
{
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-02-05 19:46:58 +00:00
if ( ! g_extern . is_paused )
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 ( ) ;
# ifdef _WIN32
Sleep ( 10 ) ;
# else
struct timespec tv = {
. tv_sec = 0 ,
. tv_nsec = 10000000
} ;
nanosleep ( & tv , NULL ) ;
# endif
}
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-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