2010-05-28 18:21:33 +02: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 16:46:22 +02:00
# include <stdbool.h>
2010-05-26 21:27:37 +02:00
# include <GL/glfw.h>
2010-05-26 22:42:58 +02:00
# include <samplerate.h>
2010-05-28 18:07:04 +02:00
# include <libsnes.hpp>
2010-05-26 21:27:37 +02:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include "config.h"
2010-05-28 02:45:18 +02:00
# include "driver.h"
2010-05-29 16:59:57 +02:00
# include "hqflt/pastlib.h"
2010-08-19 15:21:30 +02:00
# include "hqflt/grayscale.h"
2010-08-19 23:44:12 +02:00
# include "hqflt/bleed.h"
2010-05-26 21:27:37 +02:00
2010-05-28 02:45:18 +02:00
static bool video_active = true ;
static bool audio_active = true ;
2010-05-26 22:42:58 +02:00
static SRC_STATE * source = NULL ;
2010-05-26 21:27:37 +02:00
2010-05-28 02:45:18 +02:00
//////////////////////////////////////////////// Backends
extern const audio_driver_t audio_rsound ;
2010-05-28 14:33:18 +02:00
extern const audio_driver_t audio_oss ;
2010-05-28 15:41:38 +02:00
extern const audio_driver_t audio_alsa ;
2010-08-16 21:20:07 +02:00
extern const audio_driver_t audio_roar ;
2010-08-25 22:42:09 +02:00
extern const audio_driver_t audio_openal ;
2010-05-28 02:45:18 +02:00
extern const video_driver_t video_gl ;
////////////////////////////////////////////////
2010-05-27 00:39:56 +02:00
2010-05-28 02:45:18 +02:00
static driver_t driver = {
2010-05-28 13:53:54 +02:00
# if VIDEO_DRIVER == VIDEO_GL
. video = & video_gl ,
2010-05-28 18:21:33 +02:00
# else
2010-08-15 10:02:04 +02:00
# error "Define a valid video driver in config.h"
2010-05-28 13:53:54 +02:00
# endif
2010-05-28 18:21:33 +02:00
2010-05-28 13:53:54 +02:00
# if AUDIO_DRIVER == AUDIO_RSOUND
2010-05-28 14:33:18 +02:00
. audio = & audio_rsound ,
# elif AUDIO_DRIVER == AUDIO_OSS
. audio = & audio_oss ,
2010-05-28 15:41:38 +02:00
# elif AUDIO_DRIVER == AUDIO_ALSA
. audio = & audio_alsa ,
2010-08-16 21:20:07 +02:00
# elif AUDIO_DRIVER == AUDIO_ROAR
. audio = & audio_roar ,
2010-08-25 22:40:43 +02:00
# elif AUDIO_DRIVER == AUDIO_AL
. audio = & audio_openal ,
2010-05-28 18:21:33 +02:00
# else
2010-08-15 10:02:04 +02:00
# error "Define a valid audio driver in config.h"
2010-05-28 13:53:54 +02:00
# endif
2010-05-28 02:45:18 +02: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-27 00:39:56 +02:00
static void load_state ( const char * path , uint8_t * data , size_t size ) ;
2010-08-15 11:08:40 +02: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 21:27:37 +02:00
2010-08-16 18:40:17 +02: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 20:17:01 +02:00
# define AUDIO_CHUNK_SIZE_BLOCKING 64
2010-08-17 23:06:44 +02:00
# define AUDIO_CHUNK_SIZE_NONBLOCKING 2048 // So we don't get complete line-noise when fast-forwarding audio.
2010-08-16 20:17:01 +02:00
static size_t audio_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING ;
2010-08-16 18:40:17 +02: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 15:26:58 +02:00
if ( video_active )
2010-08-19 15:56:00 +02:00
driver . video - > set_nonblock_state ( driver . video_data , syncing_state ) ;
2010-08-19 15:26:58 +02:00
if ( audio_active )
2010-08-19 15:52:43 +02:00
driver . audio - > set_nonblock_state ( driver . audio_data , ( audio_sync ) ? syncing_state : true ) ;
2010-08-16 20:17:01 +02:00
if ( syncing_state )
audio_chunk_size = AUDIO_CHUNK_SIZE_NONBLOCKING ;
else
audio_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING ;
2010-08-16 18:40:17 +02:00
}
old_button_state = new_button_state ;
}
2010-05-28 02:45:18 +02:00
static void init_drivers ( void )
2010-05-26 21:27:37 +02:00
{
2010-05-28 02:45:18 +02:00
init_video_input ( ) ;
init_audio ( ) ;
2010-05-26 21:27:37 +02:00
}
2010-05-28 02:45:18 +02:00
static void uninit_drivers ( void )
2010-05-26 21:27:37 +02:00
{
2010-05-28 02:45:18 +02:00
uninit_video_input ( ) ;
uninit_audio ( ) ;
2010-05-26 21:27:37 +02:00
}
2010-05-28 02:45:18 +02:00
static void init_audio ( void )
2010-05-26 21:27:37 +02:00
{
2010-08-19 16:00:50 +02:00
if ( ! audio_enable )
{
audio_active = false ;
return ;
}
2010-05-28 02:45:18 +02:00
driver . audio_data = driver . audio - > init ( audio_device , out_rate , out_latency ) ;
if ( driver . audio_data = = NULL )
audio_active = false ;
2010-05-26 21:27:37 +02:00
2010-08-19 15:52:43 +02:00
if ( ! audio_sync & & audio_active )
driver . audio - > set_nonblock_state ( driver . audio_data , true ) ;
2010-05-28 02:45:18 +02:00
int err ;
2010-05-29 15:25:49 +02:00
source = src_new ( SAMPLERATE_QUALITY , 2 , & err ) ;
2010-08-19 15:52:43 +02:00
if ( ! source )
audio_active = false ;
2010-05-28 02:45:18 +02:00
}
2010-05-26 21:27:37 +02:00
2010-05-28 02:45:18 +02:00
static void uninit_audio ( void )
{
2010-08-19 16:00:50 +02:00
if ( ! audio_enable )
{
audio_active = false ;
return ;
}
2010-05-28 02:45:18 +02:00
if ( driver . audio_data & & driver . audio )
driver . audio - > free ( driver . audio_data ) ;
2010-05-26 21:27:37 +02:00
2010-05-28 02:45:18 +02:00
if ( source )
src_delete ( source ) ;
2010-05-26 21:27:37 +02:00
}
2010-05-28 02:45:18 +02:00
static void init_video_input ( void )
2010-05-26 21:27:37 +02:00
{
2010-05-29 16:59:57 +02:00
int scale ;
2010-08-22 00:51:25 +02:00
// We multiply scales with 2 to allow for hi-res games.
2010-05-29 16:59:57 +02:00
# if VIDEO_FILTER == FILTER_NONE
scale = 2 ;
2010-08-22 00:51:25 +02:00
# elif VIDEO_FILTER == FILTER_HQ2X
2010-05-29 16:59:57 +02:00
scale = 4 ;
2010-08-22 00:51:25 +02:00
# elif VIDEO_FILTER == FILTER_HQ4X
scale = 8 ;
2010-08-19 15:21:30 +02:00
# elif VIDEO_FILTER == FILTER_GRAYSCALE
2010-08-22 00:51:25 +02:00
scale = 2 ;
2010-08-19 23:44:12 +02:00
# elif VIDEO_FILTER == FILTER_BLEED
2010-08-22 00:56:16 +02:00
scale = 2 ;
2010-05-29 16:59:57 +02:00
# else
2010-08-22 00:51:25 +02:00
scale = 2 ;
2010-05-29 16:59:57 +02:00
# endif
2010-05-29 14:45:40 +02:00
video_info_t video = {
2010-05-30 20:36:58 +02:00
. width = ( fullscreen ) ? fullscreen_x : ( 296 * xscale ) ,
2010-05-29 14:45:40 +02:00
. height = ( fullscreen ) ? fullscreen_y : ( 224 * yscale ) ,
. fullscreen = fullscreen ,
. vsync = vsync ,
. force_aspect = force_aspect ,
2010-05-29 16:59:57 +02:00
. smooth = video_smooth ,
. input_scale = scale ,
2010-05-29 14:45:40 +02:00
} ;
2010-06-27 14:57:37 +02:00
driver . video_data = driver . video - > init ( & video , & ( driver . input ) ) ;
2010-05-26 22:42:58 +02:00
2010-05-28 02:45:18 +02:00
if ( driver . video_data = = NULL )
2010-05-26 22:42:58 +02:00
{
exit ( 1 ) ;
}
2010-05-28 02:45:18 +02:00
if ( driver . input ! = NULL )
2010-05-26 22:42:58 +02:00
{
2010-05-28 02:45:18 +02:00
driver . input_data = driver . video_data ;
2010-05-26 22:42:58 +02:00
}
2010-05-28 02:45:18 +02:00
else
2010-05-26 22:42:58 +02:00
{
2010-05-28 02:45:18 +02:00
driver . input_data = driver . input - > init ( ) ;
if ( driver . input_data = = NULL )
exit ( 1 ) ;
2010-05-26 22:42:58 +02:00
}
2010-05-26 21:41:57 +02:00
}
2010-05-28 02:45:18 +02:00
static void uninit_video_input ( void )
2010-05-26 21:27:37 +02:00
{
2010-05-28 02:45:18 +02:00
if ( driver . video_data & & driver . video )
driver . video - > free ( driver . video_data ) ;
2010-05-26 21:27:37 +02:00
2010-05-28 02:45:18 +02:00
if ( driver . input_data ! = driver . video_data & & driver . input )
driver . input - > free ( driver . input_data ) ;
}
2010-05-26 21:27:37 +02:00
2010-06-27 15:46:23 +02:00
static inline void process_frame ( uint16_t * restrict out , const uint16_t * restrict in , unsigned width , unsigned height )
{
for ( int y = 0 ; y < height ; y + + )
{
const uint16_t * src = in + y * 1024 ;
uint16_t * dst = out + y * width ;
memcpy ( dst , src , width * sizeof ( uint16_t ) ) ;
}
}
2010-06-27 18:24:26 +02:00
// libsnes: 0.065
// Format received is 16-bit 0RRRRRGGGGGBBBBB
2010-05-28 02:45:18 +02:00
static void video_frame ( const uint16_t * data , unsigned width , unsigned height )
{
if ( ! video_active )
return ;
2010-05-26 21:27:37 +02:00
2010-05-29 16:59:57 +02:00
# if VIDEO_FILTER == FILTER_HQ2X
uint16_t outputHQ2x [ width * height * 2 * 2 ] ;
2010-06-27 14:57:37 +02:00
# elif VIDEO_FILTER == FILTER_HQ4X
2010-05-29 16:59:57 +02:00
uint16_t outputHQ4x [ width * height * 4 * 4 ] ;
# endif
2010-06-27 15:46:23 +02:00
uint16_t output [ width * height ] ;
2010-05-26 21:27:37 +02:00
2010-06-27 15:46:23 +02:00
process_frame ( output , data , width , height ) ;
2010-05-26 21:27:37 +02:00
2010-05-29 16:59:57 +02:00
# if VIDEO_FILTER == FILTER_NONE
if ( ! driver . video - > frame ( driver . video_data , output , width , height ) )
video_active = false ;
# elif VIDEO_FILTER == FILTER_HQ2X
ProcessHQ2x ( output , outputHQ2x ) ;
if ( ! driver . video - > frame ( driver . video_data , outputHQ2x , width * 2 , height * 2 ) )
video_active = false ;
# elif VIDEO_FILTER == FILTER_HQ4X
ProcessHQ4x ( output , outputHQ4x ) ;
if ( ! driver . video - > frame ( driver . video_data , outputHQ4x , width * 4 , height * 4 ) )
video_active = false ;
2010-08-19 15:21:30 +02:00
# elif VIDEO_FILTER == FILTER_GRAYSCALE
grayscale_filter ( output , width , height ) ;
if ( ! driver . video - > frame ( driver . video_data , output , width , height ) )
2010-08-19 23:44:12 +02:00
video_active = false ;
# elif VIDEO_FILTER == FILTER_BLEED
bleed_filter ( output , width , height ) ;
if ( ! driver . video - > frame ( driver . video_data , output , width , height ) )
2010-08-19 15:21:30 +02:00
video_active = false ;
2010-05-29 16:59:57 +02:00
# else
2010-05-28 02:45:18 +02:00
if ( ! driver . video - > frame ( driver . video_data , output , width , height ) )
video_active = false ;
2010-05-29 16:59:57 +02:00
# endif
2010-05-26 21:27:37 +02:00
}
2010-05-28 02:45:18 +02:00
static void audio_sample ( uint16_t left , uint16_t right )
2010-05-26 21:27:37 +02:00
{
2010-05-28 02:45:18 +02:00
if ( ! audio_active )
return ;
2010-08-16 20:17:01 +02:00
static float data [ AUDIO_CHUNK_SIZE_NONBLOCKING ] ;
2010-05-27 00:26:11 +02:00
static int data_ptr = 0 ;
2010-05-26 22:42:58 +02:00
2010-05-27 00:26:11 +02:00
data [ data_ptr + + ] = ( float ) ( * ( int16_t * ) & left ) / 0x7FFF ;
data [ data_ptr + + ] = ( float ) ( * ( int16_t * ) & right ) / 0x7FFF ;
2010-05-26 21:27:37 +02:00
2010-08-16 20:17:01 +02:00
if ( data_ptr > = audio_chunk_size )
2010-05-26 22:42:58 +02:00
{
2010-08-16 20:17:01 +02:00
float outsamples [ audio_chunk_size * 16 ] ;
int16_t temp_outsamples [ audio_chunk_size * 16 ] ;
2010-05-26 22:42:58 +02:00
2010-05-27 00:26:11 +02:00
SRC_DATA src_data ;
2010-05-26 22:42:58 +02:00
2010-05-27 00:26:11 +02:00
src_data . data_in = data ;
src_data . data_out = outsamples ;
2010-08-16 20:17:01 +02:00
src_data . input_frames = audio_chunk_size / 2 ;
src_data . output_frames = audio_chunk_size * 8 ;
2010-05-27 00:26:11 +02:00
src_data . end_of_input = 0 ;
src_data . src_ratio = ( double ) out_rate / ( double ) in_rate ;
2010-05-26 22:42:58 +02:00
2010-05-27 00:26:11 +02:00
src_process ( source , & src_data ) ;
2010-05-26 22:42:58 +02:00
2010-08-15 10:02:04 +02:00
src_float_to_short_array ( outsamples , temp_outsamples , src_data . output_frames_gen * 2 ) ;
2010-05-26 22:42:58 +02:00
2010-05-28 02:45:18 +02:00
if ( driver . audio - > write ( driver . audio_data , temp_outsamples , src_data . output_frames_gen * 4 ) < 0 )
2010-08-16 19:16:03 +02:00
{
fprintf ( stderr , " SSNES [ERROR]: Audio backend failed to write. Will continue without sound. \n " ) ;
2010-05-28 02:45:18 +02:00
audio_active = false ;
2010-08-16 19:16:03 +02:00
}
2010-05-26 22:42:58 +02:00
2010-05-28 02:45:18 +02:00
data_ptr = 0 ;
2010-05-26 21:27:37 +02:00
}
}
2010-05-28 02:45:18 +02:00
static void input_poll ( void )
2010-05-26 21:27:37 +02:00
{
2010-05-28 02:45:18 +02:00
driver . input - > poll ( driver . input_data ) ;
2010-05-26 21:27:37 +02:00
}
static int16_t input_state ( bool port , unsigned device , unsigned index , unsigned id )
{
2010-05-28 02:54:20 +02:00
return driver . input - > input_state ( driver . input_data , snes_keybinds , port , device , index , id ) ;
2010-05-26 21:27:37 +02:00
}
int main ( int argc , char * argv [ ] )
{
if ( argc ! = 2 )
{
fprintf ( stderr , " Usage: %s file \n " , argv [ 0 ] ) ;
exit ( 1 ) ;
}
2010-05-29 15:21:30 +02:00
2010-08-15 10:02:04 +02:00
fprintf ( stderr , " SSNES: Opening file: \" %s \" \n " , argv [ 1 ] ) ;
2010-05-29 15:21:30 +02:00
FILE * file = fopen ( argv [ 1 ] , " rb " ) ;
if ( file = = NULL )
{
2010-08-15 10:02:04 +02:00
fprintf ( stderr , " SSNES [ERROR]: Could not open file: \" %s \" \n " , argv [ 1 ] ) ;
2010-05-29 15:21:30 +02:00
exit ( 1 ) ;
}
2010-08-15 10:02:04 +02:00
const char * statefile_tok = NULL ;
char statefile_name [ strlen ( argv [ 1 ] ) + strlen ( " state " ) + 1 ] ;
2010-08-15 11:08:40 +02:00
char savefile_name_rtc [ strlen ( argv [ 1 ] ) + strlen ( " rtc " ) + 1 ] ;
char savefile_name_srm [ strlen ( argv [ 1 ] ) + strlen ( " srm " ) + 1 ] ;
2010-08-15 10:02:04 +02:00
statefile_tok = strtok ( argv [ 1 ] , " . " ) ;
strcpy ( statefile_name , statefile_tok ) ;
strcat ( statefile_name , " .state " ) ;
2010-08-15 11:08:40 +02:00
strcpy ( savefile_name_rtc , statefile_tok ) ;
strcat ( savefile_name_rtc , " .rtc " ) ;
strcpy ( savefile_name_srm , statefile_tok ) ;
strcat ( savefile_name_srm , " .srm " ) ;
2010-05-26 21:27:37 +02:00
2010-05-28 02:45:18 +02:00
init_drivers ( ) ;
2010-05-26 21:27:37 +02:00
snes_init ( ) ;
2010-05-28 02:45:18 +02:00
snes_set_video_refresh ( video_frame ) ;
snes_set_audio_sample ( audio_sample ) ;
snes_set_input_poll ( input_poll ) ;
2010-05-26 21:27:37 +02:00
snes_set_input_state ( input_state ) ;
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-05-28 14:33:18 +02:00
fprintf ( stderr , " SSNES [ERROR] :: Couldn't allocate memory! \n " ) ;
2010-06-27 15:46:23 +02:00
goto error ;
2010-05-26 21:27:37 +02:00
}
if ( fread ( rom_buf , 1 , length , file ) < length )
{
2010-05-28 14:33:18 +02:00
fprintf ( stderr , " SSNES [ERROR] :: Didn't read whole file. \n " ) ;
2010-06-27 15:46:23 +02:00
goto error ;
2010-05-26 21:27:37 +02:00
}
fclose ( file ) ;
2010-08-15 11:24:27 +02:00
if ( ! snes_load_cartridge_normal ( NULL , rom_buf , length ) )
{
fprintf ( stderr , " SSNES [ERROR] :: ROM file \" %s \" is not valid! \n " , argv [ 1 ] ) ; ;
goto error ;
}
2010-05-26 21:27:37 +02:00
free ( rom_buf ) ;
unsigned serial_size = snes_serialize_size ( ) ;
uint8_t * serial_data = malloc ( serial_size ) ;
2010-06-27 15:46:23 +02:00
2010-08-15 11:08:40 +02:00
load_save_file ( savefile_name_srm , SNES_MEMORY_CARTRIDGE_RAM ) ;
load_save_file ( savefile_name_rtc , SNES_MEMORY_CARTRIDGE_RTC ) ;
2010-05-26 21:27:37 +02:00
2010-05-28 02:45:18 +02:00
///// TODO: Modular friendly!!!
2010-05-26 21:27:37 +02:00
for ( ; ; )
{
int quitting = glfwGetKey ( GLFW_KEY_ESC ) | | ! glfwGetWindowParam ( GLFW_OPENED ) ;
if ( quitting )
break ;
2010-05-26 21:41:57 +02:00
if ( glfwGetKey ( SAVE_STATE_KEY ) )
2010-05-27 00:39:56 +02:00
{
2010-08-15 11:08:40 +02:00
write_file ( statefile_name , serial_data , serial_size ) ;
2010-05-27 00:39:56 +02:00
}
2010-05-26 21:41:57 +02:00
else if ( glfwGetKey ( LOAD_STATE_KEY ) )
2010-08-15 10:02:04 +02:00
load_state ( statefile_name , serial_data , serial_size ) ;
2010-05-27 00:39:56 +02:00
2010-05-26 21:41:57 +02:00
else if ( glfwGetKey ( TOGGLE_FULLSCREEN ) )
{
fullscreen = ! fullscreen ;
2010-05-28 02:45:18 +02:00
uninit_drivers ( ) ;
init_drivers ( ) ;
2010-05-26 21:41:57 +02:00
}
2010-05-26 21:27:37 +02:00
snes_run ( ) ;
}
2010-08-15 11:08:40 +02:00
save_file ( savefile_name_srm , SNES_MEMORY_CARTRIDGE_RAM ) ;
save_file ( savefile_name_rtc , SNES_MEMORY_CARTRIDGE_RTC ) ;
2010-05-26 21:27:37 +02:00
2010-06-27 14:40:06 +02:00
snes_unload_cartridge ( ) ;
2010-05-26 21:27:37 +02:00
snes_term ( ) ;
2010-05-28 02:45:18 +02:00
uninit_drivers ( ) ;
2010-05-26 21:27:37 +02:00
return 0 ;
2010-06-27 15:46:23 +02:00
error :
snes_unload_cartridge ( ) ;
snes_term ( ) ;
uninit_drivers ( ) ;
return 1 ;
2010-05-26 21:27:37 +02:00
}
2010-05-27 00:39:56 +02:00
2010-08-15 11:08:40 +02:00
static void write_file ( const char * path , uint8_t * data , size_t size )
2010-05-27 00:39:56 +02:00
{
FILE * file = fopen ( path , " wb " ) ;
if ( file ! = NULL )
{
2010-08-15 10:02:04 +02:00
fprintf ( stderr , " SSNES: Saving state \" %s \" . Size: %d bytes. \n " , path , ( int ) size ) ;
2010-05-27 00:39:56 +02:00
snes_serialize ( data , size ) ;
2010-05-27 16:46:22 +02:00
if ( fwrite ( data , 1 , size , file ) ! = size )
fprintf ( stderr , " SSNES [WARN]: Did not save state properly. " ) ;
2010-05-27 00:39:56 +02:00
fclose ( file ) ;
}
}
static void load_state ( const char * path , uint8_t * data , size_t size )
{
2010-08-15 10:02:04 +02:00
fprintf ( stderr , " SSNES: Loading state: \" %s \" . \n " , path ) ;
2010-05-27 00:39:56 +02:00
FILE * file = fopen ( path , " rb " ) ;
if ( file ! = NULL )
{
2010-05-28 14:33:18 +02:00
//fprintf(stderr, "SSNES: Loading state. Size: %d bytes.\n", (int)size);
2010-05-27 16:46:22 +02:00
if ( fread ( data , 1 , size , file ) ! = size )
fprintf ( stderr , " SSNES [WARN]: Did not load state properly. " ) ;
2010-05-27 00:39:56 +02:00
fclose ( file ) ;
snes_unserialize ( data , size ) ;
}
2010-08-15 10:02:04 +02:00
else
{
fprintf ( stderr , " SSNES: No state file found. Will create new. \n " ) ;
}
2010-05-27 00:39:56 +02:00
}
2010-08-15 10:02:04 +02:00
2010-08-15 11:08:40 +02:00
static void load_save_file ( const char * path , int type )
2010-08-15 10:02:04 +02:00
{
2010-08-15 11:08:40 +02:00
FILE * file ;
2010-08-15 10:02:04 +02:00
2010-08-15 11:08:40 +02:00
file = fopen ( path , " rb " ) ;
if ( ! file )
2010-08-15 10:02:04 +02:00
{
2010-08-15 11:08:40 +02:00
return ;
2010-08-15 10:02:04 +02:00
}
2010-08-15 11:08:40 +02:00
size_t size = snes_get_memory_size ( type ) ;
uint8_t * data = snes_get_memory_data ( type ) ;
2010-08-15 10:02:04 +02:00
2010-08-15 11:08:40 +02:00
if ( size = = 0 | | ! data )
2010-08-15 10:02:04 +02:00
{
2010-08-15 11:08:40 +02:00
fclose ( file ) ;
return ;
2010-08-15 10:02:04 +02:00
}
2010-08-15 11:08:40 +02:00
int rc = fread ( data , 1 , size , file ) ;
if ( rc ! = size )
2010-08-15 10:02:04 +02:00
{
2010-08-15 11:08:40 +02:00
fprintf ( stderr , " SSNES [ERROR]: Couldn't load save file. \n " ) ;
2010-08-15 10:02:04 +02:00
}
2010-08-15 11:08:40 +02:00
fprintf ( stderr , " SSNES: Loaded save file: \" %s \" \n " , path ) ;
2010-08-15 10:02:04 +02:00
2010-08-15 11:08:40 +02:00
fclose ( file ) ;
2010-08-15 10:02:04 +02:00
}
2010-08-15 11:08:40 +02: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 ) ;
}