2012-04-21 21:13:50 +00:00
/* RetroArch - A frontend for libretro.
2014-01-01 00:50:59 +00:00
* Copyright ( C ) 2010 - 2014 - Hans - Kristian Arntzen
2011-02-02 10:47:05 +00:00
*
2012-04-21 21:13:50 +00:00
* RetroArch is free software : you can redistribute it and / or modify it under the terms
2011-02-02 10:47:05 +00:00
* 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 .
*
2012-04-21 21:13:50 +00:00
* RetroArch is distributed in the hope that it will be useful , but WITHOUT ANY WARRANTY ;
2011-02-02 10:47:05 +00:00
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE . See the GNU General Public License for more details .
*
2012-04-21 21:31:57 +00:00
* You should have received a copy of the GNU General Public License along with RetroArch .
2011-02-02 10:47:05 +00:00
* If not , see < http : //www.gnu.org/licenses/>.
*/
# include "movie.h"
2012-03-25 21:29:39 +00:00
# include "hash.h"
2011-02-02 10:47:05 +00:00
# include <stdio.h>
# include <stdlib.h>
2012-01-11 18:22:18 +00:00
# include <string.h>
2011-02-02 10:47:05 +00:00
# include "general.h"
# include "dynamic.h"
struct bsv_movie
{
FILE * file ;
2011-02-25 10:47:27 +00:00
2014-09-02 14:13:42 +00:00
/* A ring buffer keeping track of positions
* in the file for each frame . */
size_t * frame_pos ;
2011-02-25 10:47:27 +00:00
size_t frame_mask ;
size_t frame_ptr ;
2011-11-10 23:55:35 +00:00
size_t min_file_pos ;
2011-02-26 00:13:52 +00:00
2013-11-04 04:57:41 +00:00
size_t state_size ;
uint8_t * state ;
bool playback ;
2011-02-25 23:59:17 +00:00
bool first_rewind ;
2011-02-26 00:13:52 +00:00
bool did_rewind ;
2011-02-02 10:47:05 +00:00
} ;
static bool init_playback ( bsv_movie_t * handle , const char * path )
{
2011-02-25 10:47:27 +00:00
handle - > playback = true ;
2011-02-02 10:47:05 +00:00
handle - > file = fopen ( path , " rb " ) ;
if ( ! handle - > file )
{
2012-04-21 21:25:32 +00:00
RARCH_ERR ( " Couldn't open BSV file \" %s \" for playback. \n " , path ) ;
2011-02-02 10:47:05 +00:00
return false ;
}
uint32_t header [ 4 ] = { 0 } ;
2011-02-02 11:45:56 +00:00
if ( fread ( header , sizeof ( uint32_t ) , 4 , handle - > file ) ! = 4 )
2011-02-02 10:47:05 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_ERR ( " Couldn't read movie header. \n " ) ;
2011-02-02 10:47:05 +00:00
return false ;
}
2014-09-02 14:13:42 +00:00
/* Compatibility with old implementation that
* used incorrect documentation . */
if ( swap_if_little32 ( header [ MAGIC_INDEX ] ) ! = BSV_MAGIC
& & swap_if_big32 ( header [ MAGIC_INDEX ] ) ! = BSV_MAGIC )
2011-02-02 10:47:05 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_ERR ( " Movie file is not a valid BSV1 file. \n " ) ;
2011-02-02 10:47:05 +00:00
return false ;
}
2014-08-12 01:19:02 +00:00
if ( swap_if_big32 ( header [ CRC_INDEX ] ) ! = g_extern . content_crc )
RARCH_WARN ( " CRC32 checksum mismatch between content file and saved content checksum in replay file header; replay highly likely to desync on playback. \n " ) ;
2011-02-02 10:47:05 +00:00
uint32_t state_size = swap_if_big32 ( header [ STATE_SIZE_INDEX ] ) ;
2011-11-10 23:55:35 +00:00
if ( state_size )
2011-02-02 10:47:05 +00:00
{
2011-12-24 12:46:12 +00:00
handle - > state = ( uint8_t * ) malloc ( state_size ) ;
2012-03-10 17:10:54 +00:00
handle - > state_size = state_size ;
2011-11-10 23:55:35 +00:00
if ( ! handle - > state )
return false ;
2011-02-02 10:47:05 +00:00
2011-11-10 23:55:35 +00:00
if ( fread ( handle - > state , 1 , state_size , handle - > file ) ! = state_size )
{
2012-04-21 21:25:32 +00:00
RARCH_ERR ( " Couldn't read state from movie. \n " ) ;
2011-11-10 23:55:35 +00:00
return false ;
}
2012-04-07 09:55:37 +00:00
if ( pretro_serialize_size ( ) = = state_size )
pretro_unserialize ( handle - > state , state_size ) ;
2012-03-10 17:10:54 +00:00
else
2012-04-21 21:25:32 +00:00
RARCH_WARN ( " Movie format seems to have a different serializer version. Will most likely fail. \n " ) ;
2011-02-02 19:15:00 +00:00
}
2011-11-10 23:55:35 +00:00
handle - > min_file_pos = sizeof ( header ) + state_size ;
2011-02-02 10:47:05 +00:00
return true ;
}
static bool init_record ( bsv_movie_t * handle , const char * path )
{
handle - > file = fopen ( path , " wb " ) ;
if ( ! handle - > file )
{
2012-04-21 21:25:32 +00:00
RARCH_ERR ( " Couldn't open BSV \" %s \" for recording. \n " , path ) ;
2011-02-02 10:47:05 +00:00
return false ;
}
uint32_t header [ 4 ] = { 0 } ;
2011-11-28 01:11:33 +00:00
2014-09-03 04:14:13 +00:00
/* This value is supposed to show up as
* BSV1 in a HEX editor , big - endian . */
2011-11-28 01:11:33 +00:00
header [ MAGIC_INDEX ] = swap_if_little32 ( BSV_MAGIC ) ;
2014-08-12 01:19:02 +00:00
header [ CRC_INDEX ] = swap_if_big32 ( g_extern . content_crc ) ;
2011-02-02 10:47:05 +00:00
2012-04-07 09:55:37 +00:00
uint32_t state_size = pretro_serialize_size ( ) ;
2011-11-18 17:03:24 +00:00
2011-02-02 10:47:05 +00:00
header [ STATE_SIZE_INDEX ] = swap_if_big32 ( state_size ) ;
fwrite ( header , 4 , sizeof ( uint32_t ) , handle - > file ) ;
2011-02-25 10:47:27 +00:00
handle - > min_file_pos = sizeof ( header ) + state_size ;
2012-03-10 17:10:54 +00:00
handle - > state_size = state_size ;
2011-02-25 10:47:27 +00:00
2012-03-10 17:10:54 +00:00
if ( state_size )
2011-11-18 17:03:24 +00:00
{
2012-03-10 17:10:54 +00:00
handle - > state = ( uint8_t * ) malloc ( state_size ) ;
if ( ! handle - > state )
return false ;
2012-04-07 09:55:37 +00:00
pretro_serialize ( handle - > state , state_size ) ;
2011-11-18 17:03:24 +00:00
fwrite ( handle - > state , 1 , state_size , handle - > file ) ;
}
2011-02-02 10:47:05 +00:00
return true ;
}
void bsv_movie_free ( bsv_movie_t * handle )
{
if ( handle )
{
if ( handle - > file )
fclose ( handle - > file ) ;
free ( handle - > state ) ;
2011-02-25 23:31:13 +00:00
free ( handle - > frame_pos ) ;
2011-02-02 10:47:05 +00:00
free ( handle ) ;
}
}
bool bsv_movie_get_input ( bsv_movie_t * handle , int16_t * input )
{
2011-02-25 23:59:17 +00:00
if ( fread ( input , sizeof ( int16_t ) , 1 , handle - > file ) ! = 1 )
return false ;
2011-02-02 10:47:05 +00:00
2011-02-25 23:59:17 +00:00
* input = swap_if_big16 ( * input ) ;
return true ;
2011-02-02 10:47:05 +00:00
}
void bsv_movie_set_input ( bsv_movie_t * handle , int16_t input )
{
2012-01-10 23:55:50 +00:00
input = swap_if_big16 ( input ) ;
2011-02-25 23:59:17 +00:00
fwrite ( & input , sizeof ( int16_t ) , 1 , handle - > file ) ;
2011-02-02 10:47:05 +00:00
}
2012-04-21 21:25:32 +00:00
bsv_movie_t * bsv_movie_init ( const char * path , enum rarch_movie_type type )
2011-02-02 10:47:05 +00:00
{
2011-12-24 12:46:12 +00:00
bsv_movie_t * handle = ( bsv_movie_t * ) calloc ( 1 , sizeof ( * handle ) ) ;
2011-02-02 10:47:05 +00:00
if ( ! handle )
return NULL ;
2012-04-21 21:25:32 +00:00
if ( type = = RARCH_MOVIE_PLAYBACK )
2011-02-02 10:47:05 +00:00
{
if ( ! init_playback ( handle , path ) )
goto error ;
}
else if ( ! init_record ( handle , path ) )
goto error ;
2014-09-02 14:13:42 +00:00
/* Just pick something really large
* ~ 1 million frames rewind should do the trick . */
2011-12-24 12:46:12 +00:00
if ( ! ( handle - > frame_pos = ( size_t * ) calloc ( ( 1 < < 20 ) , sizeof ( size_t ) ) ) )
2011-02-25 10:47:27 +00:00
goto error ;
2011-11-10 23:55:35 +00:00
handle - > frame_pos [ 0 ] = handle - > min_file_pos ;
2011-02-25 10:47:27 +00:00
handle - > frame_mask = ( 1 < < 20 ) - 1 ;
2011-02-02 10:47:05 +00:00
return handle ;
error :
bsv_movie_free ( handle ) ;
return NULL ;
}
2011-02-25 10:47:27 +00:00
2011-02-25 23:31:13 +00:00
void bsv_movie_set_frame_start ( bsv_movie_t * handle )
{
handle - > frame_pos [ handle - > frame_ptr ] = ftell ( handle - > file ) ;
}
2011-02-25 10:47:27 +00:00
void bsv_movie_set_frame_end ( bsv_movie_t * handle )
{
handle - > frame_ptr = ( handle - > frame_ptr + 1 ) & handle - > frame_mask ;
2011-02-26 00:13:52 +00:00
handle - > first_rewind = ! handle - > did_rewind ;
handle - > did_rewind = false ;
2011-02-25 10:47:27 +00:00
}
void bsv_movie_frame_rewind ( bsv_movie_t * handle )
{
2011-02-26 00:13:52 +00:00
handle - > did_rewind = true ;
2011-11-10 23:55:35 +00:00
if ( ( handle - > frame_ptr < = 1 ) & & ( handle - > frame_pos [ 0 ] = = handle - > min_file_pos ) )
2011-02-26 00:13:52 +00:00
{
2014-09-02 14:13:42 +00:00
/* If we're at the beginning... */
2011-02-25 23:59:17 +00:00
handle - > frame_ptr = 0 ;
2011-02-26 00:13:52 +00:00
fseek ( handle - > file , handle - > min_file_pos , SEEK_SET ) ;
}
2011-02-25 23:59:17 +00:00
else
{
2014-09-02 14:13:42 +00:00
/* First time rewind is performed, the old frame is simply replayed.
* However , playing back that frame caused us to read data , and push
* data to the ring buffer .
*
* Sucessively rewinding frames , we need to rewind past the read data ,
* plus another . */
handle - > frame_ptr = ( handle - > frame_ptr -
( handle - > first_rewind ? 1 : 2 ) ) & handle - > frame_mask ;
2011-02-25 23:59:17 +00:00
fseek ( handle - > file , handle - > frame_pos [ handle - > frame_ptr ] , SEEK_SET ) ;
}
2011-02-25 10:47:27 +00:00
if ( ftell ( handle - > file ) < = ( long ) handle - > min_file_pos )
{
2014-09-02 14:13:42 +00:00
/* We rewound past the beginning. */
2011-02-25 10:47:27 +00:00
if ( ! handle - > playback )
{
2014-09-02 14:13:42 +00:00
/* If recording, we simply reset
* the starting point . Nice and easy . */
2011-02-25 10:47:27 +00:00
fseek ( handle - > file , 4 * sizeof ( uint32_t ) , SEEK_SET ) ;
2012-04-07 09:55:37 +00:00
pretro_serialize ( handle - > state , handle - > state_size ) ;
2012-03-10 17:10:54 +00:00
fwrite ( handle - > state , 1 , handle - > state_size , handle - > file ) ;
2011-02-25 10:47:27 +00:00
}
2011-02-25 23:31:13 +00:00
else
fseek ( handle - > file , handle - > min_file_pos , SEEK_SET ) ;
2011-02-25 10:47:27 +00:00
}
}
2012-01-11 18:22:18 +00:00