2017-04-14 19:59:01 +00:00
using System.Collections.Generic ;
2013-11-01 20:53:47 +00:00
using System.IO ;
2015-06-30 22:54:42 +00:00
using System.Linq ;
2013-11-01 20:53:47 +00:00
2014-11-30 16:42:58 +00:00
using BizHawk.Common ;
2014-07-03 18:54:53 +00:00
using BizHawk.Common.BufferExtensions ;
2014-12-05 00:52:16 +00:00
using BizHawk.Emulation.Common.IEmulatorExtensions ;
2013-11-01 20:53:47 +00:00
namespace BizHawk.Client.Common
{
public static class SavestateManager
{
public static void SaveStateFile ( string filename , string name )
{
2014-12-05 00:52:16 +00:00
var core = Global . Emulator . AsStatable ( ) ;
2017-04-14 19:59:01 +00:00
2014-06-16 21:19:48 +00:00
// the old method of text savestate save is now gone.
// a text savestate is just like a binary savestate, but with a different core lump
2014-10-12 04:24:31 +00:00
using ( var bs = new BinaryStateSaver ( filename ) )
2013-11-01 20:53:47 +00:00
{
2014-06-16 21:19:48 +00:00
if ( Global . Config . SaveStateType = = Config . SaveStateTypeE . Text | |
2014-11-30 16:42:58 +00:00
( Global . Config . SaveStateType = = Config . SaveStateTypeE . Default & & ! core . BinarySaveStatesPreferred ) )
2014-06-16 21:19:48 +00:00
{
// text savestate format
2014-10-09 01:49:44 +00:00
using ( new SimpleTime ( "Save Core" ) )
2017-04-14 19:59:01 +00:00
{
2014-11-30 16:42:58 +00:00
bs . PutLump ( BinaryStateLump . CorestateText , ( tw ) = > core . SaveStateText ( tw ) ) ;
2017-04-14 19:59:01 +00:00
}
2014-06-16 21:19:48 +00:00
}
else
{
// binary core lump format
2014-10-09 01:49:44 +00:00
using ( new SimpleTime ( "Save Core" ) )
2017-04-14 19:59:01 +00:00
{
2014-11-30 16:42:58 +00:00
bs . PutLump ( BinaryStateLump . Corestate , bw = > core . SaveStateBinary ( bw ) ) ;
2017-04-14 19:59:01 +00:00
}
2014-06-16 21:19:48 +00:00
}
2016-12-04 16:30:51 +00:00
if ( Global . Config . SaveScreenshotWithStates & & Global . Emulator . HasVideoProvider ( ) )
2013-11-01 20:53:47 +00:00
{
2016-12-04 16:30:51 +00:00
var vp = Global . Emulator . AsVideoProvider ( ) ;
2015-02-17 02:08:08 +00:00
var buff = vp . GetVideoBuffer ( ) ;
2016-02-22 06:23:20 +00:00
if ( buff . Length = = 1 )
{
2017-04-14 19:59:01 +00:00
// is a hacky opengl texture ID. can't handle this now!
// need to discuss options
// 1. cores must be able to provide a pixels videoprovider in addition to a texture ID, on command (not very hard overall but interface changing and work per core)
// 2. SavestateManager must be setup with a mechanism for resolving texture IDs (even less work, but sloppy)
// There are additional problems with AVWriting. They depend on VideoProvider providing pixels.
2016-02-22 06:23:20 +00:00
}
else
2014-05-07 01:36:19 +00:00
{
2016-02-22 06:23:20 +00:00
int out_w = vp . BufferWidth ;
int out_h = vp . BufferHeight ;
// if buffer is too big, scale down screenshot
if ( ! Global . Config . NoLowResLargeScreenshotWithStates & & buff . Length > = Global . Config . BigScreenshotSize )
{
out_w / = 2 ;
out_h / = 2 ;
}
2017-04-14 19:59:01 +00:00
2016-02-22 06:23:20 +00:00
using ( new SimpleTime ( "Save Framebuffer" ) )
2017-04-14 19:59:01 +00:00
{
2017-05-19 18:17:07 +00:00
bs . PutLump ( BinaryStateLump . Framebuffer , s = > QuickBmpFile . Save ( Global . Emulator . AsVideoProvider ( ) , s , out_w , out_h ) ) ;
2017-04-14 19:59:01 +00:00
}
2014-05-07 01:36:19 +00:00
}
2013-11-01 20:53:47 +00:00
}
2014-01-08 03:53:53 +00:00
2014-06-16 21:19:48 +00:00
if ( Global . MovieSession . Movie . IsActive )
2013-11-01 20:53:47 +00:00
{
2014-06-16 21:19:48 +00:00
bs . PutLump ( BinaryStateLump . Input ,
delegate ( TextWriter tw )
2014-05-07 01:36:19 +00:00
{
2014-06-16 21:19:48 +00:00
// this never should have been a core's responsibility
tw . WriteLine ( "Frame {0}" , Global . Emulator . Frame ) ;
Global . MovieSession . HandleMovieSaveState ( tw ) ;
} ) ;
2013-11-01 20:53:47 +00:00
}
2015-06-30 22:54:42 +00:00
if ( Global . UserBag . Any ( ) )
{
bs . PutLump ( BinaryStateLump . UserData ,
delegate ( TextWriter tw )
{
var data = ConfigService . SaveWithType ( Global . UserBag ) ;
tw . WriteLine ( data ) ;
} ) ;
}
2015-07-18 13:48:14 +00:00
if ( Global . MovieSession . Movie . IsActive & & Global . MovieSession . Movie is TasMovie )
{
bs . PutLump ( BinaryStateLump . LagLog ,
delegate ( BinaryWriter bw )
{
( Global . MovieSession . Movie as TasMovie ) . TasLagLog . Save ( bw ) ;
} ) ;
}
2013-11-01 20:53:47 +00:00
}
}
2014-10-27 01:14:47 +00:00
public static void PopulateFramebuffer ( BinaryReader br )
{
2016-12-04 16:30:51 +00:00
if ( ! Global . Emulator . HasVideoProvider ( ) )
{
return ;
}
2014-10-27 01:14:47 +00:00
try
{
2015-02-17 02:31:56 +00:00
using ( new SimpleTime ( "Load Framebuffer" ) )
2017-04-14 19:59:01 +00:00
{
2016-12-04 16:30:51 +00:00
QuickBmpFile . Load ( Global . Emulator . AsVideoProvider ( ) , br . BaseStream ) ;
2017-04-14 19:59:01 +00:00
}
2015-02-02 00:25:50 +00:00
}
catch
{
2016-12-04 16:30:51 +00:00
var buff = Global . Emulator . AsVideoProvider ( ) . GetVideoBuffer ( ) ;
2015-02-02 00:25:50 +00:00
try
2014-10-27 01:14:47 +00:00
{
2015-02-02 00:25:50 +00:00
for ( int i = 0 ; i < buff . Length ; i + + )
{
int j = br . ReadInt32 ( ) ;
buff [ i ] = j ;
}
2014-10-27 01:14:47 +00:00
}
2017-05-17 16:16:55 +00:00
catch ( EndOfStreamException )
{
}
2014-10-27 01:14:47 +00:00
}
}
2015-07-19 14:37:53 +00:00
public static void PopulateFramebuffer ( byte [ ] bytes )
{
using ( var ms = new MemoryStream ( bytes ) )
{
using ( var br = new BinaryReader ( ms ) )
{
PopulateFramebuffer ( br ) ;
}
}
}
2013-11-01 20:53:47 +00:00
public static bool LoadStateFile ( string path , string name )
{
2014-12-05 00:52:16 +00:00
var core = Global . Emulator . AsStatable ( ) ;
2014-11-30 16:42:58 +00:00
2013-11-01 20:53:47 +00:00
// try to detect binary first
2014-01-08 03:53:53 +00:00
var bl = BinaryStateLoader . LoadAndDetect ( path ) ;
2013-12-17 21:26:15 +00:00
if ( bl ! = null )
2013-11-01 20:53:47 +00:00
{
try
{
2014-01-08 03:53:53 +00:00
var succeed = false ;
2013-11-01 20:53:47 +00:00
if ( Global . MovieSession . Movie . IsActive )
{
2014-04-18 17:41:14 +00:00
bl . GetLump ( BinaryStateLump . Input , true , tr = > succeed = Global . MovieSession . HandleMovieLoadState_HackyStep1 ( tr ) ) ;
2014-11-09 15:48:06 +00:00
if ( ! succeed )
{
return false ;
}
2014-04-18 17:41:14 +00:00
bl . GetLump ( BinaryStateLump . Input , true , tr = > succeed = Global . MovieSession . HandleMovieLoadState_HackyStep2 ( tr ) ) ;
2013-11-01 20:53:47 +00:00
if ( ! succeed )
2014-01-08 03:53:53 +00:00
{
2013-11-01 20:53:47 +00:00
return false ;
2014-01-08 03:53:53 +00:00
}
2013-11-01 20:53:47 +00:00
}
2014-10-09 01:49:44 +00:00
using ( new SimpleTime ( "Load Core" ) )
2017-04-14 19:59:01 +00:00
{
2014-11-30 16:42:58 +00:00
bl . GetCoreState ( br = > core . LoadStateBinary ( br ) , tr = > core . LoadStateText ( tr ) ) ;
2017-04-14 19:59:01 +00:00
}
2013-11-01 20:53:47 +00:00
2014-10-27 01:14:47 +00:00
bl . GetLump ( BinaryStateLump . Framebuffer , false , PopulateFramebuffer ) ;
2015-06-30 22:54:42 +00:00
2017-05-10 11:45:23 +00:00
string userData = "" ;
2017-04-24 18:18:45 +00:00
bl . GetLump ( BinaryStateLump . UserData , false , delegate ( TextReader tr )
2015-06-30 22:54:42 +00:00
{
2017-04-24 18:18:45 +00:00
string line ;
while ( ( line = tr . ReadLine ( ) ) ! = null )
2015-06-30 22:54:42 +00:00
{
2017-04-24 18:18:45 +00:00
if ( ! string . IsNullOrWhiteSpace ( line ) )
2015-06-30 22:54:42 +00:00
{
2017-04-24 18:18:45 +00:00
userData = line ;
2015-06-30 22:54:42 +00:00
}
2017-04-24 18:18:45 +00:00
}
} ) ;
2015-06-30 22:54:42 +00:00
2017-04-24 18:18:45 +00:00
if ( ! string . IsNullOrWhiteSpace ( userData ) )
{
2015-06-30 22:54:42 +00:00
Global . UserBag = ( Dictionary < string , object > ) ConfigService . LoadWithType ( userData ) ;
}
2015-07-18 13:48:14 +00:00
2017-04-24 18:18:45 +00:00
if ( Global . MovieSession . Movie . IsActive & & Global . MovieSession . Movie is TasMovie )
2015-07-18 13:48:14 +00:00
{
bl . GetLump ( BinaryStateLump . LagLog , false , delegate ( BinaryReader br , long length )
{
2017-04-24 18:18:45 +00:00
( ( TasMovie ) Global . MovieSession . Movie ) . TasLagLog . Load ( br ) ;
2015-07-18 13:48:14 +00:00
} ) ;
}
2013-11-01 20:53:47 +00:00
}
2014-09-07 15:17:47 +00:00
catch
{
return false ;
}
2013-11-01 20:53:47 +00:00
finally
{
2013-12-17 21:26:15 +00:00
bl . Dispose ( ) ;
2013-11-01 20:53:47 +00:00
}
return true ;
}
else // text mode
{
if ( Global . MovieSession . HandleMovieLoadState ( path ) )
{
using ( var reader = new StreamReader ( path ) )
{
2014-11-30 16:42:58 +00:00
core . LoadStateText ( reader ) ;
2013-11-01 20:53:47 +00:00
while ( true )
{
2014-01-08 03:53:53 +00:00
var str = reader . ReadLine ( ) ;
if ( str = = null )
{
break ;
}
2017-05-10 11:45:23 +00:00
if ( str . Trim ( ) = = "" )
2014-01-08 03:53:53 +00:00
{
continue ;
}
2013-11-01 20:53:47 +00:00
2014-01-08 03:53:53 +00:00
var args = str . Split ( ' ' ) ;
2016-12-04 16:30:51 +00:00
if ( args [ 0 ] = = "Framebuffer" & & Global . Emulator . HasVideoProvider ( ) )
2013-11-01 20:53:47 +00:00
{
2016-12-04 16:30:51 +00:00
Global . Emulator . AsVideoProvider ( ) . GetVideoBuffer ( ) . ReadFromHex ( args [ 1 ] ) ;
2013-11-01 20:53:47 +00:00
}
}
}
2014-01-08 03:53:53 +00:00
2013-11-01 20:53:47 +00:00
return true ;
}
else
{
return false ;
}
}
}
}
}