2010-05-28 16:21:33 +00:00
/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright ( C ) 2010 - Hans - Kristian Arntzen
*
* 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-26 19:27:37 +00:00
# include <GL/glfw.h>
2010-05-26 20:42:58 +00:00
# include <samplerate.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>
2010-05-26 19:27:37 +00:00
# include "config.h"
2010-05-28 00:45:18 +00:00
# include "driver.h"
2010-05-29 14:59:57 +00:00
# include "hqflt/pastlib.h"
2010-08-19 13:21:30 +00:00
# include "hqflt/grayscale.h"
2010-08-19 21:44:12 +00:00
# include "hqflt/bleed.h"
2010-08-28 14:36:15 +00:00
# include "hqflt/ntsc.h"
2010-05-26 19:27:37 +00:00
2010-05-28 00:45:18 +00:00
static bool video_active = true ;
static bool audio_active = true ;
2010-05-26 20:42:58 +00:00
static SRC_STATE * source = NULL ;
2010-05-26 19:27:37 +00:00
2010-05-28 00:45:18 +00:00
//////////////////////////////////////////////// Backends
extern const audio_driver_t audio_rsound ;
2010-05-28 12:33:18 +00:00
extern const audio_driver_t audio_oss ;
2010-05-28 13:41:38 +00:00
extern const audio_driver_t audio_alsa ;
2010-08-16 19:20:07 +00:00
extern const audio_driver_t audio_roar ;
2010-08-25 20:42:09 +00:00
extern const audio_driver_t audio_openal ;
2010-05-28 00:45:18 +00:00
extern const video_driver_t video_gl ;
////////////////////////////////////////////////
2010-05-26 22:39:56 +00:00
2010-05-28 00:45:18 +00:00
static driver_t driver = {
2010-05-28 11:53:54 +00:00
# if VIDEO_DRIVER == VIDEO_GL
. video = & video_gl ,
2010-05-28 16:21:33 +00:00
# else
2010-08-15 08:02:04 +00:00
# error "Define a valid video driver in config.h"
2010-05-28 11:53:54 +00:00
# endif
2010-05-28 16:21:33 +00:00
2010-05-28 11:53:54 +00:00
# if AUDIO_DRIVER == AUDIO_RSOUND
2010-05-28 12:33:18 +00:00
. audio = & audio_rsound ,
# elif AUDIO_DRIVER == AUDIO_OSS
. audio = & audio_oss ,
2010-05-28 13:41:38 +00:00
# elif AUDIO_DRIVER == AUDIO_ALSA
. audio = & audio_alsa ,
2010-08-16 19:20:07 +00:00
# elif AUDIO_DRIVER == AUDIO_ROAR
. audio = & audio_roar ,
2010-08-25 20:40:43 +00:00
# elif AUDIO_DRIVER == AUDIO_AL
. audio = & audio_openal ,
2010-05-28 16:21:33 +00:00
# else
2010-08-15 08:02:04 +00:00
# error "Define a valid audio driver in config.h"
2010-05-28 11:53:54 +00:00
# endif
2010-05-28 00:45:18 +00:00
} ;
static void init_drivers ( void ) ;
static void uninit_drivers ( void ) ;
static void init_video_input ( void ) ;
static void uninit_video_input ( void ) ;
static void init_audio ( void ) ;
static void uninit_audio ( void ) ;
2010-05-26 22:39:56 +00:00
static void load_state ( const char * path , uint8_t * data , size_t size ) ;
2010-08-15 09:08:40 +00:00
static void write_file ( const char * path , uint8_t * data , size_t size ) ;
static void load_save_file ( const char * path , int type ) ;
static void save_file ( const char * path , int type ) ;
2010-05-26 19:27: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.
2010-08-16 18:17:01 +00:00
# define AUDIO_CHUNK_SIZE_BLOCKING 64
2010-08-17 21:06:44 +00:00
# define AUDIO_CHUNK_SIZE_NONBLOCKING 2048 // So we don't get complete line-noise when fast-forwarding audio.
2010-08-16 18:17:01 +00:00
static size_t audio_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING ;
2010-08-16 16:40:17 +00:00
void set_fast_forward_button ( bool new_button_state )
{
static bool old_button_state = false ;
static bool syncing_state = false ;
if ( new_button_state & & ! old_button_state )
{
syncing_state = ! syncing_state ;
2010-08-19 13:26:58 +00:00
if ( video_active )
2010-08-19 13:56:00 +00:00
driver . video - > set_nonblock_state ( driver . video_data , syncing_state ) ;
2010-08-19 13:26:58 +00:00
if ( audio_active )
2010-08-19 13:52:43 +00:00
driver . audio - > set_nonblock_state ( driver . audio_data , ( audio_sync ) ? syncing_state : true ) ;
2010-08-16 18:17:01 +00:00
if ( syncing_state )
audio_chunk_size = AUDIO_CHUNK_SIZE_NONBLOCKING ;
else
audio_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING ;
2010-08-16 16:40:17 +00:00
}
old_button_state = new_button_state ;
}
2010-05-28 00:45:18 +00:00
static void init_drivers ( void )
2010-05-26 19:27:37 +00:00
{
2010-05-28 00:45:18 +00:00
init_video_input ( ) ;
init_audio ( ) ;
2010-05-26 19:27:37 +00:00
}
2010-05-28 00:45:18 +00:00
static void uninit_drivers ( void )
2010-05-26 19:27:37 +00:00
{
2010-05-28 00:45:18 +00:00
uninit_video_input ( ) ;
uninit_audio ( ) ;
2010-05-26 19:27:37 +00:00
}
2010-05-28 00:45:18 +00:00
static void init_audio ( void )
2010-05-26 19:27:37 +00:00
{
2010-08-19 14:00:50 +00:00
if ( ! audio_enable )
{
audio_active = false ;
return ;
}
2010-05-28 00:45:18 +00:00
driver . audio_data = driver . audio - > init ( audio_device , out_rate , out_latency ) ;
if ( driver . audio_data = = NULL )
audio_active = false ;
2010-05-26 19:27:37 +00:00
2010-08-19 13:52:43 +00:00
if ( ! audio_sync & & audio_active )
driver . audio - > set_nonblock_state ( driver . audio_data , true ) ;
2010-05-28 00:45:18 +00:00
int err ;
2010-05-29 13:25:49 +00:00
source = src_new ( SAMPLERATE_QUALITY , 2 , & err ) ;
2010-08-19 13:52:43 +00:00
if ( ! source )
audio_active = false ;
2010-05-28 00:45:18 +00:00
}
2010-05-26 19:27:37 +00:00
2010-05-28 00:45:18 +00:00
static void uninit_audio ( void )
{
2010-08-19 14:00:50 +00:00
if ( ! audio_enable )
{
audio_active = false ;
return ;
}
2010-05-28 00:45:18 +00:00
if ( driver . audio_data & & driver . audio )
driver . audio - > free ( driver . audio_data ) ;
2010-05-26 19:27:37 +00:00
2010-05-28 00:45:18 +00:00
if ( source )
src_delete ( source ) ;
2010-05-26 19:27:37 +00:00
}
2010-05-28 00:45:18 +00:00
static void init_video_input ( void )
2010-05-26 19:27:37 +00:00
{
2010-05-29 14:59:57 +00:00
int scale ;
2010-08-21 22:51:25 +00:00
// We multiply scales with 2 to allow for hi-res games.
2010-05-29 14:59:57 +00:00
# if VIDEO_FILTER == FILTER_NONE
scale = 2 ;
2010-08-21 22:51:25 +00:00
# elif VIDEO_FILTER == FILTER_HQ2X
2010-05-29 14:59:57 +00:00
scale = 4 ;
2010-08-21 22:51:25 +00:00
# elif VIDEO_FILTER == FILTER_HQ4X
scale = 8 ;
2010-08-28 14:36:15 +00:00
# elif VIDEO_FILTER == FILTER_NTSC
scale = 8 ;
2010-08-19 13:21:30 +00:00
# elif VIDEO_FILTER == FILTER_GRAYSCALE
2010-08-21 22:51:25 +00:00
scale = 2 ;
2010-08-19 21:44:12 +00:00
# elif VIDEO_FILTER == FILTER_BLEED
2010-08-21 22:56:16 +00:00
scale = 2 ;
2010-05-29 14:59:57 +00:00
# else
2010-08-21 22:51:25 +00:00
scale = 2 ;
2010-05-29 14:59:57 +00:00
# endif
2010-05-29 12:45:40 +00:00
video_info_t video = {
2010-05-30 18:36:58 +00:00
. width = ( fullscreen ) ? fullscreen_x : ( 296 * xscale ) ,
2010-05-29 12:45:40 +00:00
. height = ( fullscreen ) ? fullscreen_y : ( 224 * yscale ) ,
. fullscreen = fullscreen ,
. vsync = vsync ,
. force_aspect = force_aspect ,
2010-05-29 14:59:57 +00:00
. smooth = video_smooth ,
. input_scale = scale ,
2010-05-29 12:45:40 +00:00
} ;
2010-06-27 12:57:37 +00:00
driver . video_data = driver . video - > init ( & video , & ( driver . input ) ) ;
2010-05-26 20:42:58 +00:00
2010-05-28 00:45:18 +00:00
if ( driver . video_data = = NULL )
2010-05-26 20:42:58 +00:00
{
exit ( 1 ) ;
}
2010-05-28 00:45:18 +00:00
if ( driver . input ! = NULL )
2010-05-26 20:42:58 +00:00
{
2010-05-28 00:45:18 +00:00
driver . input_data = driver . video_data ;
2010-05-26 20:42:58 +00:00
}
2010-05-28 00:45:18 +00:00
else
2010-05-26 20:42:58 +00:00
{
2010-05-28 00:45:18 +00:00
driver . input_data = driver . input - > init ( ) ;
if ( driver . input_data = = NULL )
exit ( 1 ) ;
2010-05-26 20:42:58 +00:00
}
2010-05-26 19:41:57 +00:00
}
2010-05-28 00:45:18 +00:00
static void uninit_video_input ( void )
2010-05-26 19:27:37 +00:00
{
2010-05-28 00:45:18 +00:00
if ( driver . video_data & & driver . video )
driver . video - > free ( driver . video_data ) ;
2010-05-26 19:27:37 +00:00
2010-05-28 00:45:18 +00:00
if ( driver . input_data ! = driver . video_data & & driver . input )
driver . input - > free ( driver . input_data ) ;
}
2010-05-26 19:27:37 +00:00
2010-11-08 22:38:32 +00:00
# if VIDEO_FILTER != FILTER_NONE
2010-06-27 13:46:23 +00:00
static inline void process_frame ( uint16_t * restrict out , const uint16_t * restrict in , unsigned width , unsigned height )
{
2010-08-28 21:55:09 +00:00
int pitch = 1024 ;
if ( height = = 448 | | height = = 478 )
pitch = 512 ;
2010-06-27 13:46:23 +00:00
for ( int y = 0 ; y < height ; y + + )
{
2010-08-28 21:55:09 +00:00
const uint16_t * src = in + y * pitch ;
2010-06-27 13:46:23 +00:00
uint16_t * dst = out + y * width ;
memcpy ( dst , src , width * sizeof ( uint16_t ) ) ;
}
}
2010-11-08 22:38:32 +00:00
# endif
2010-06-27 13:46:23 +00:00
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 )
{
if ( ! video_active )
return ;
2010-05-26 19:27:37 +00:00
2010-05-29 14:59:57 +00:00
# if VIDEO_FILTER == FILTER_HQ2X
uint16_t outputHQ2x [ width * height * 2 * 2 ] ;
2010-06-27 12:57:37 +00:00
# elif VIDEO_FILTER == FILTER_HQ4X
2010-05-29 14:59:57 +00:00
uint16_t outputHQ4x [ width * height * 4 * 4 ] ;
2010-08-28 14:36:15 +00:00
# elif VIDEO_FILTER == FILTER_NTSC
uint16_t output_ntsc [ SNES_NTSC_OUT_WIDTH ( width ) * height ] ;
2010-05-29 14:59:57 +00:00
# endif
2010-05-26 19:27:37 +00:00
2010-11-08 22:38:32 +00:00
# if VIDEO_FILTER != FILTER_NONE
uint16_t output [ width * height ] ;
2010-06-27 13:46:23 +00:00
process_frame ( output , data , width , height ) ;
2010-11-08 22:38:32 +00:00
# endif
2010-05-26 19:27:37 +00:00
2010-11-08 22:38:32 +00:00
# if VIDEO_FILTER == FILTER_HQ2X
2010-05-29 14:59:57 +00:00
ProcessHQ2x ( output , outputHQ2x ) ;
2010-11-08 22:46:56 +00:00
if ( ! driver . video - > frame ( driver . video_data , outputHQ2x , width < < 1 , height < < 1 , width < < 2 ) )
2010-05-29 14:59:57 +00:00
video_active = false ;
# elif VIDEO_FILTER == FILTER_HQ4X
ProcessHQ4x ( output , outputHQ4x ) ;
2010-11-08 22:46:56 +00:00
if ( ! driver . video - > frame ( driver . video_data , outputHQ4x , width < < 2 , height < < 2 , width < < 3 ) )
2010-05-29 14:59:57 +00:00
video_active = false ;
2010-08-19 13:21:30 +00:00
# elif VIDEO_FILTER == FILTER_GRAYSCALE
grayscale_filter ( output , width , height ) ;
2010-11-08 22:46:56 +00:00
if ( ! driver . video - > frame ( driver . video_data , output , width , height , width < < 1 ) )
2010-08-19 21:44:12 +00:00
video_active = false ;
# elif VIDEO_FILTER == FILTER_BLEED
bleed_filter ( output , width , height ) ;
2010-11-08 22:46:56 +00:00
if ( ! driver . video - > frame ( driver . video_data , output , width , height , width < < 1 ) )
2010-08-19 13:21:30 +00:00
video_active = false ;
2010-08-28 14:36:15 +00:00
# elif VIDEO_FILTER == FILTER_NTSC
ntsc_filter ( output_ntsc , output , width , height ) ;
2010-11-08 22:46:56 +00:00
if ( ! driver . video - > frame ( driver . video_data , output_ntsc , SNES_NTSC_OUT_WIDTH ( width ) , height , SNES_NTSC_OUT_WIDTH ( width ) < < 1 ) )
2010-08-28 14:36:15 +00:00
video_active = false ;
2010-05-29 14:59:57 +00:00
# else
2010-11-08 22:38:32 +00:00
if ( ! driver . video - > frame ( driver . video_data , data , width , height , ( height = = 448 | | height = = 478 ) ? 1024 : 2048 ) )
2010-05-28 00:45:18 +00:00
video_active = false ;
2010-05-29 14:59:57 +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-05-28 00:45:18 +00:00
if ( ! audio_active )
return ;
2010-08-16 18:17:01 +00:00
static float data [ AUDIO_CHUNK_SIZE_NONBLOCKING ] ;
2010-05-26 22:26:11 +00:00
static int data_ptr = 0 ;
2010-05-26 20:42:58 +00:00
2010-05-26 22:26:11 +00:00
data [ data_ptr + + ] = ( float ) ( * ( int16_t * ) & left ) / 0x7FFF ;
data [ data_ptr + + ] = ( float ) ( * ( int16_t * ) & right ) / 0x7FFF ;
2010-05-26 19:27:37 +00:00
2010-08-16 18:17:01 +00:00
if ( data_ptr > = audio_chunk_size )
2010-05-26 20:42:58 +00:00
{
2010-08-16 18:17:01 +00:00
float outsamples [ audio_chunk_size * 16 ] ;
int16_t temp_outsamples [ audio_chunk_size * 16 ] ;
2010-05-26 20:42:58 +00:00
2010-05-26 22:26:11 +00:00
SRC_DATA src_data ;
2010-05-26 20:42:58 +00:00
2010-05-26 22:26:11 +00:00
src_data . data_in = data ;
src_data . data_out = outsamples ;
2010-08-16 18:17:01 +00:00
src_data . input_frames = audio_chunk_size / 2 ;
src_data . output_frames = audio_chunk_size * 8 ;
2010-05-26 22:26:11 +00:00
src_data . end_of_input = 0 ;
src_data . src_ratio = ( double ) out_rate / ( double ) in_rate ;
2010-05-26 20:42:58 +00:00
2010-05-26 22:26:11 +00:00
src_process ( source , & src_data ) ;
2010-05-26 20:42:58 +00:00
2010-08-15 08:02:04 +00:00
src_float_to_short_array ( outsamples , temp_outsamples , src_data . output_frames_gen * 2 ) ;
2010-05-26 20:42:58 +00:00
2010-05-28 00:45:18 +00:00
if ( driver . audio - > write ( driver . audio_data , temp_outsamples , src_data . output_frames_gen * 4 ) < 0 )
2010-08-16 17:16:03 +00:00
{
fprintf ( stderr , " SSNES [ERROR]: Audio backend failed to write. Will continue without sound. \n " ) ;
2010-05-28 00:45:18 +00:00
audio_active = false ;
2010-08-16 17:16:03 +00:00
}
2010-05-26 20:42:58 +00:00
2010-05-28 00:45:18 +00:00
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 )
{
2010-10-01 18:15:45 +00:00
const struct snes_keybind * binds [ ] = { snes_keybinds_1 , snes_keybinds_2 } ;
return driver . input - > input_state ( driver . input_data , binds , port , device , index , id ) ;
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
2010-10-01 19:39:15 +00:00
static void print_help ( void )
{
2010-10-02 11:54:32 +00:00
puts ( " ================================================= " ) ;
2010-10-01 19:39:15 +00:00
puts ( " ssnes: Simple Super Nintendo Emulator (libsnes) " ) ;
2010-10-02 11:54:32 +00:00
puts ( " ================================================= " ) ;
2010-10-01 19:39:15 +00:00
puts ( " Usage: ssnes [rom file] [-h/--help | -s/--save] " ) ;
puts ( " \t -h/--help: Show this help message " ) ;
puts ( " \t -s/--save: Path for save file (*.srm). Required when rom is input from stdin " ) ;
2010-11-13 14:14:30 +00:00
# ifdef HAVE_CG
puts ( " \t -f/--shader: Path to Cg shader. Will be compiled at runtime. \n " ) ;
# endif
2010-10-01 20:10:28 +00:00
puts ( " \t -v/--verbose: Verbose logging " ) ;
2010-10-01 19:39:15 +00:00
}
static FILE * rom_file = NULL ;
static char savefile_name_srm [ 256 ] = { 0 } ;
2010-10-01 20:10:28 +00:00
static bool verbose = false ;
2010-11-13 14:14:30 +00:00
# ifdef HAVE_CG
char cg_shader_path [ 256 ] = DEFAULT_CG_SHADER ;
# endif
2010-10-01 20:10:28 +00:00
# define SSNES_LOG(msg, args...) do { \
if ( verbose ) \
fprintf ( stderr , " SSNES: " msg , # # args ) ; \
} while ( 0 )
# define SSNES_ERR(msg, args...) do { \
fprintf ( stderr , " SSNES [ERROR] :: " msg , # # args ) ; \
} while ( 0 )
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
2010-10-01 19:39:15 +00:00
struct option opts [ ] = {
{ " help " , 0 , NULL , ' h ' } ,
{ " save " , 1 , NULL , ' s ' } ,
2010-10-01 20:10:28 +00:00
{ " verbose " , 0 , NULL , ' v ' } ,
2010-11-13 14:14:30 +00:00
# ifdef HAVE_CG
{ " shader " , 1 , NULL , ' f ' } ,
# endif
2010-10-01 19:39:15 +00:00
{ NULL , 0 , NULL , 0 }
} ;
int option_index = 0 ;
2010-11-13 14:14:30 +00:00
# ifdef HAVE_CG
char optstring [ ] = " hs:vf: " ;
# else
2010-10-01 20:10:28 +00:00
char optstring [ ] = " hs:v " ;
2010-11-13 14:14:30 +00:00
# endif
2010-10-01 19:39:15 +00:00
for ( ; ; )
2010-05-29 13:21:30 +00:00
{
2010-10-01 19:39:15 +00:00
int c = getopt_long ( argc , argv , optstring , opts , & option_index ) ;
if ( c = = - 1 )
break ;
switch ( c )
{
case ' h ' :
print_help ( ) ;
exit ( 0 ) ;
case ' s ' :
strncpy ( savefile_name_srm , optarg , sizeof ( savefile_name_srm ) ) ;
savefile_name_srm [ sizeof ( savefile_name_srm ) - 1 ] = ' \0 ' ;
break ;
2010-10-01 20:10:28 +00:00
2010-11-13 14:14:30 +00:00
# ifdef HAVE_CG
case ' f ' :
strncpy ( cg_shader_path , optarg , sizeof ( cg_shader_path ) - 1 ) ;
break ;
# endif
2010-10-01 20:10:28 +00:00
case ' v ' :
verbose = true ;
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 )
{
2010-10-01 20:10:28 +00:00
SSNES_LOG ( " Opening file: \" %s \" \n " , argv [ optind ] ) ;
2010-10-01 19:39:15 +00:00
rom_file = fopen ( argv [ optind ] , " rb " ) ;
if ( rom_file = = NULL )
{
2010-10-01 20:10:28 +00:00
SSNES_ERR ( " Could not open file: \" %s \" \n " , optarg ) ;
2010-10-01 19:39:15 +00:00
exit ( 1 ) ;
}
if ( strlen ( savefile_name_srm ) = = 0 )
fill_pathname ( savefile_name_srm , argv [ optind ] , " .srm " ) ;
}
2010-10-01 20:10:28 +00:00
else if ( strlen ( savefile_name_srm ) = = 0 )
{
SSNES_ERR ( " Need savefile argument when reading rom from stdin. \n " ) ;
print_help ( ) ;
exit ( 1 ) ;
}
2010-10-01 19:39:15 +00:00
}
2010-08-15 08:02:04 +00:00
2010-10-01 19:39:15 +00:00
static ssize_t read_file ( FILE * file , void * * buf )
{
ssize_t ret ;
if ( file = = NULL ) // stdin
{
2010-10-01 20:10:28 +00:00
SSNES_LOG ( " Reading ROM from stdin ... \n " ) ;
2010-10-01 19:39:15 +00:00
size_t buf_size = 0xFFFFF ; // Some initial guesstimate.
size_t buf_ptr = 0 ;
char * rom_buf = malloc ( buf_size ) ;
if ( rom_buf = = NULL )
{
2010-10-01 20:10:28 +00:00
SSNES_ERR ( " Couldn't allocate memory! \n " ) ;
2010-10-01 19:39:15 +00:00
return - 1 ;
}
2010-05-26 19:27:37 +00:00
2010-10-01 19:39:15 +00:00
for ( ; ; )
{
size_t ret = fread ( rom_buf + buf_ptr , 1 , buf_size - buf_ptr , stdin ) ;
buf_ptr + = ret ;
2010-05-26 19:27:37 +00:00
2010-10-01 19:39:15 +00:00
// We've reached the end
if ( buf_ptr < buf_size )
break ;
2010-05-26 19:27:37 +00:00
2010-10-01 19:39:15 +00:00
rom_buf = realloc ( rom_buf , buf_size * 2 ) ;
if ( rom_buf = = NULL )
{
2010-10-01 20:10:28 +00:00
SSNES_ERR ( " Couldn't allocate memory! \n " ) ;
2010-10-01 19:39:15 +00:00
return - 1 ;
}
2010-05-26 19:27:37 +00:00
2010-10-01 19:39:15 +00:00
buf_size * = 2 ;
}
2010-05-26 19:27:37 +00:00
2010-10-01 19:52:30 +00:00
if ( ( buf_ptr & 0x7fff ) = = 512 )
2010-10-01 19:39:15 +00:00
{
2010-10-01 19:52:30 +00:00
memmove ( rom_buf , rom_buf + 512 , buf_ptr - 512 ) ;
buf_ptr - = 512 ;
2010-10-01 19:39:15 +00:00
}
2010-05-26 19:27:37 +00:00
2010-10-01 19:39:15 +00:00
* buf = rom_buf ;
2010-10-01 19:52:30 +00:00
ret = buf_ptr ;
2010-10-01 19:39:15 +00:00
}
else
2010-05-26 19:27:37 +00:00
{
2010-10-01 19:39:15 +00:00
fseek ( file , 0 , SEEK_END ) ;
long length = ftell ( file ) ;
rewind ( file ) ;
if ( ( length & 0x7fff ) = = 512 )
{
length - = 512 ;
fseek ( file , 512 , SEEK_SET ) ;
}
void * rom_buf = malloc ( length ) ;
if ( rom_buf = = NULL )
{
2010-10-01 20:10:28 +00:00
SSNES_ERR ( " Couldn't allocate memory! \n " ) ;
2010-10-01 19:39:15 +00:00
return - 1 ;
}
if ( fread ( rom_buf , 1 , length , file ) < length )
{
2010-10-01 20:10:28 +00:00
SSNES_ERR ( " Didn't read whole file. \n " ) ;
2010-10-01 19:39:15 +00:00
free ( rom_buf ) ;
return - 1 ;
}
* buf = rom_buf ;
ret = length ;
2010-05-26 19:27:37 +00:00
}
2010-10-01 19:39:15 +00:00
return ret ;
}
int main ( int argc , char * argv [ ] )
{
parse_input ( argc , argv ) ;
2010-05-26 19:27:37 +00:00
2010-10-01 19:39:15 +00:00
void * rom_buf ;
ssize_t rom_len = 0 ;
if ( ( rom_len = read_file ( rom_file , & rom_buf ) ) = = - 1 )
2010-05-26 19:27:37 +00:00
{
2010-10-01 20:10:28 +00:00
SSNES_ERR ( " Could not read ROM file. \n " ) ;
2010-10-01 19:39:15 +00:00
exit ( 1 ) ;
2010-05-26 19:27:37 +00:00
}
2010-10-01 20:10:28 +00:00
SSNES_LOG ( " ROM size: %zi bytes \n " , rom_len ) ;
2010-05-26 19:27:37 +00:00
2010-10-01 19:39:15 +00:00
if ( rom_file ! = NULL )
fclose ( rom_file ) ;
char statefile_name [ strlen ( savefile_name_srm ) + strlen ( " .state " ) + 1 ] ;
char savefile_name_rtc [ strlen ( savefile_name_srm ) + strlen ( " .rtc " ) + 1 ] ;
2010-05-26 19:27:37 +00:00
2010-10-01 19:39:15 +00:00
fill_pathname ( statefile_name , argv [ 1 ] , " .state " ) ;
fill_pathname ( savefile_name_rtc , argv [ 1 ] , " .rtc " ) ;
init_drivers ( ) ;
snes_init ( ) ;
snes_set_video_refresh ( video_frame ) ;
snes_set_audio_sample ( audio_sample ) ;
snes_set_input_poll ( input_poll ) ;
snes_set_input_state ( input_state ) ;
if ( ! snes_load_cartridge_normal ( NULL , rom_buf , rom_len ) )
2010-08-15 09:24:27 +00:00
{
2010-10-01 20:10:28 +00:00
SSNES_ERR ( " ROM file is not valid! \n " ) ;
2010-08-15 09:24:27 +00:00
goto error ;
}
2010-05-26 19:27:37 +00:00
free ( rom_buf ) ;
unsigned serial_size = snes_serialize_size ( ) ;
uint8_t * serial_data = malloc ( serial_size ) ;
2010-10-01 19:39:15 +00:00
if ( serial_data = = NULL )
{
2010-10-01 20:10:28 +00:00
SSNES_ERR ( " Failed to allocate memory for states! \n " ) ;
2010-10-01 19:39:15 +00:00
goto error ;
}
2010-06-27 13:46:23 +00:00
2010-08-15 09:08:40 +00:00
load_save_file ( savefile_name_srm , SNES_MEMORY_CARTRIDGE_RAM ) ;
load_save_file ( savefile_name_rtc , SNES_MEMORY_CARTRIDGE_RTC ) ;
2010-05-26 19:27:37 +00:00
2010-05-28 00:45:18 +00:00
///// TODO: Modular friendly!!!
2010-05-26 19:27:37 +00:00
for ( ; ; )
{
2010-10-01 19:39:15 +00:00
bool quitting = glfwGetKey ( GLFW_KEY_ESC ) | | ! glfwGetWindowParam ( GLFW_OPENED ) ;
2010-05-26 19:27:37 +00:00
if ( quitting )
break ;
2010-05-26 19:41:57 +00:00
if ( glfwGetKey ( SAVE_STATE_KEY ) )
2010-05-26 22:39:56 +00:00
{
2010-08-15 09:08:40 +00:00
write_file ( statefile_name , serial_data , serial_size ) ;
2010-05-26 22:39:56 +00:00
}
2010-05-26 19:41:57 +00:00
else if ( glfwGetKey ( LOAD_STATE_KEY ) )
2010-08-15 08:02:04 +00:00
load_state ( statefile_name , serial_data , serial_size ) ;
2010-05-26 22:39:56 +00:00
2010-05-26 19:41:57 +00:00
else if ( glfwGetKey ( TOGGLE_FULLSCREEN ) )
{
fullscreen = ! fullscreen ;
2010-05-28 00:45:18 +00:00
uninit_drivers ( ) ;
init_drivers ( ) ;
2010-05-26 19:41:57 +00:00
}
2010-05-26 19:27:37 +00:00
snes_run ( ) ;
}
2010-08-15 09:08:40 +00:00
save_file ( savefile_name_srm , SNES_MEMORY_CARTRIDGE_RAM ) ;
save_file ( savefile_name_rtc , SNES_MEMORY_CARTRIDGE_RTC ) ;
2010-05-26 19:27:37 +00:00
2010-06-27 12:40:06 +00:00
snes_unload_cartridge ( ) ;
2010-05-26 19:27:37 +00:00
snes_term ( ) ;
2010-05-28 00:45:18 +00:00
uninit_drivers ( ) ;
2010-10-01 19:39:15 +00:00
free ( serial_data ) ;
2010-05-26 19:27:37 +00:00
return 0 ;
2010-06-27 13:46:23 +00:00
error :
snes_unload_cartridge ( ) ;
snes_term ( ) ;
uninit_drivers ( ) ;
return 1 ;
2010-05-26 19:27:37 +00:00
}
2010-05-26 22:39:56 +00:00
2010-08-15 09:08:40 +00:00
static void write_file ( const char * path , uint8_t * data , size_t size )
2010-05-26 22:39:56 +00:00
{
FILE * file = fopen ( path , " wb " ) ;
if ( file ! = NULL )
{
2010-10-01 20:10:28 +00:00
SSNES_LOG ( " Saving state \" %s \" . Size: %d bytes. \n " , path , ( int ) size ) ;
2010-05-26 22:39:56 +00:00
snes_serialize ( data , size ) ;
2010-05-27 14:46:22 +00:00
if ( fwrite ( data , 1 , size , file ) ! = size )
2010-10-01 20:10:28 +00:00
SSNES_ERR ( " Did not save state properly. \n " ) ;
2010-05-26 22:39:56 +00:00
fclose ( file ) ;
}
}
static void load_state ( const char * path , uint8_t * data , size_t size )
{
2010-10-01 20:10:28 +00:00
SSNES_LOG ( " Loading state: \" %s \" . \n " , path ) ;
2010-05-26 22:39:56 +00:00
FILE * file = fopen ( path , " rb " ) ;
if ( file ! = NULL )
{
2010-05-28 12:33:18 +00:00
//fprintf(stderr, "SSNES: Loading state. Size: %d bytes.\n", (int)size);
2010-05-27 14:46:22 +00:00
if ( fread ( data , 1 , size , file ) ! = size )
2010-10-01 20:10:28 +00:00
SSNES_ERR ( " Did not load state properly. \n " ) ;
2010-05-26 22:39:56 +00:00
fclose ( file ) ;
snes_unserialize ( data , size ) ;
}
2010-08-15 08:02:04 +00:00
else
{
2010-10-01 20:10:28 +00:00
SSNES_LOG ( " No state file found. Will create new. \n " ) ;
2010-08-15 08:02:04 +00:00
}
2010-05-26 22:39:56 +00:00
}
2010-08-15 08:02:04 +00:00
2010-08-15 09:08:40 +00:00
static void load_save_file ( const char * path , int type )
2010-08-15 08:02:04 +00:00
{
2010-08-15 09:08:40 +00:00
FILE * file ;
2010-08-15 08:02:04 +00:00
2010-08-15 09:08:40 +00:00
file = fopen ( path , " rb " ) ;
if ( ! file )
2010-08-15 08:02:04 +00:00
{
2010-08-15 09:08:40 +00:00
return ;
2010-08-15 08:02:04 +00:00
}
2010-08-15 09:08:40 +00:00
size_t size = snes_get_memory_size ( type ) ;
uint8_t * data = snes_get_memory_data ( type ) ;
2010-08-15 08:02:04 +00:00
2010-08-15 09:08:40 +00:00
if ( size = = 0 | | ! data )
2010-08-15 08:02:04 +00:00
{
2010-08-15 09:08:40 +00:00
fclose ( file ) ;
return ;
2010-08-15 08:02:04 +00:00
}
2010-08-15 09:08:40 +00:00
int rc = fread ( data , 1 , size , file ) ;
if ( rc ! = size )
2010-08-15 08:02:04 +00:00
{
2010-10-01 20:10:28 +00:00
SSNES_ERR ( " Couldn't load save file. \n " ) ;
2010-08-15 08:02:04 +00:00
}
2010-10-01 20:10:28 +00:00
SSNES_LOG ( " Loaded save file: \" %s \" \n " , path ) ;
2010-08-15 08:02:04 +00:00
2010-08-15 09:08:40 +00:00
fclose ( file ) ;
2010-08-15 08:02:04 +00:00
}
2010-08-15 09:08:40 +00:00
static void save_file ( const char * path , int type )
{
size_t size = snes_get_memory_size ( type ) ;
uint8_t * data = snes_get_memory_data ( type ) ;
if ( data & & size > 0 )
write_file ( path , data , size ) ;
}