BizHawk/BizHawk.Client.Common/SavestateManager.cs

202 lines
5.3 KiB
C#

using System.Collections.Generic;
using System.IO;
using System.Linq;
using BizHawk.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
namespace BizHawk.Client.Common
{
public static class SavestateManager
{
public static void SaveStateFile(string filename, string name)
{
var core = Global.Emulator.AsStatable();
// 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
using var bs = new BinaryStateSaver(filename);
if (Global.Config.SaveStateType == Config.SaveStateTypeE.Text
|| (Global.Config.SaveStateType == Config.SaveStateTypeE.Default && !core.BinarySaveStatesPreferred))
{
// text savestate format
using (new SimpleTime("Save Core"))
{
bs.PutLump(BinaryStateLump.CorestateText, tw => core.SaveStateText(tw));
}
}
else
{
// binary core lump format
using (new SimpleTime("Save Core"))
{
bs.PutLump(BinaryStateLump.Corestate, bw => core.SaveStateBinary(bw));
}
}
if (Global.Config.SaveScreenshotWithStates && Global.Emulator.HasVideoProvider())
{
var vp = Global.Emulator.AsVideoProvider();
var buff = vp.GetVideoBuffer();
if (buff.Length == 1)
{
// 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.
}
else
{
int outWidth = vp.BufferWidth;
int outHeight = vp.BufferHeight;
// if buffer is too big, scale down screenshot
if (!Global.Config.NoLowResLargeScreenshotWithStates && buff.Length >= Global.Config.BigScreenshotSize)
{
outWidth /= 2;
outHeight /= 2;
}
using (new SimpleTime("Save Framebuffer"))
{
bs.PutLump(BinaryStateLump.Framebuffer, s => QuickBmpFile.Save(Global.Emulator.AsVideoProvider(), s, outWidth, outHeight));
}
}
}
if (Global.MovieSession.Movie.IsActive())
{
bs.PutLump(BinaryStateLump.Input,
delegate(TextWriter tw)
{
// this never should have been a core's responsibility
tw.WriteLine("Frame {0}", Global.Emulator.Frame);
Global.MovieSession.HandleMovieSaveState(tw);
});
}
if (Global.UserBag.Any())
{
bs.PutLump(BinaryStateLump.UserData,
delegate(TextWriter tw)
{
var data = ConfigService.SaveWithType(Global.UserBag);
tw.WriteLine(data);
});
}
if (Global.MovieSession.Movie.IsActive() && Global.MovieSession.Movie is TasMovie)
{
bs.PutLump(BinaryStateLump.LagLog,
delegate(TextWriter tw)
{
((TasMovie)Global.MovieSession.Movie).TasLagLog.Save(tw);
});
}
}
public static void PopulateFramebuffer(BinaryReader br)
{
if (!Global.Emulator.HasVideoProvider())
{
return;
}
try
{
using (new SimpleTime("Load Framebuffer"))
{
QuickBmpFile.Load(Global.Emulator.AsVideoProvider(), br.BaseStream);
}
}
catch
{
var buff = Global.Emulator.AsVideoProvider().GetVideoBuffer();
try
{
for (int i = 0; i < buff.Length; i++)
{
int j = br.ReadInt32();
buff[i] = j;
}
}
catch (EndOfStreamException)
{
}
}
}
public static bool LoadStateFile(string path, string name)
{
var core = Global.Emulator.AsStatable();
// try to detect binary first
var bl = BinaryStateLoader.LoadAndDetect(path);
if (bl != null)
{
try
{
var succeed = false;
if (Global.MovieSession.Movie.IsActive())
{
bl.GetLump(BinaryStateLump.Input, true, tr => succeed = Global.MovieSession.HandleMovieLoadState_HackyStep1(tr));
if (!succeed)
{
return false;
}
bl.GetLump(BinaryStateLump.Input, true, tr => succeed = Global.MovieSession.HandleMovieLoadState_HackyStep2(tr));
if (!succeed)
{
return false;
}
}
using (new SimpleTime("Load Core"))
{
bl.GetCoreState(br => core.LoadStateBinary(br), tr => core.LoadStateText(tr));
}
bl.GetLump(BinaryStateLump.Framebuffer, false, PopulateFramebuffer);
string userData = "";
bl.GetLump(BinaryStateLump.UserData, false, delegate(TextReader tr)
{
string line;
while ((line = tr.ReadLine()) != null)
{
if (!string.IsNullOrWhiteSpace(line))
{
userData = line;
}
}
});
if (!string.IsNullOrWhiteSpace(userData))
{
Global.UserBag = (Dictionary<string, object>)ConfigService.LoadWithType(userData);
}
if (Global.MovieSession.Movie.IsActive() && Global.MovieSession.Movie is TasMovie)
{
bl.GetLump(BinaryStateLump.LagLog, false, delegate(TextReader tr)
{
((TasMovie)Global.MovieSession.Movie).TasLagLog.Load(tr);
});
}
}
finally
{
bl.Dispose();
}
return true;
}
return false;
}
}
}