412 lines
10 KiB
C#
412 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
|
|
using BizHawk.Common;
|
|
using BizHawk.Common.BufferExtensions;
|
|
using BizHawk.Client.EmuHawk;
|
|
using BizHawk.Bizware.BizwareGL;
|
|
using BizHawk.Client.Common;
|
|
using BizHawk.Emulation.Common;
|
|
using BizHawk.Emulation.Common.IEmulatorExtensions;
|
|
using System.IO;
|
|
|
|
|
|
namespace BizHawk.Client.MultiHawk
|
|
{
|
|
// TODO: we can safely assume videoprovider cores are a requirement of multihawk,
|
|
// but fail sooner and with a clear message instead of making misc calls to AsVideoProvider that will fail
|
|
public partial class EmulatorWindow : Form
|
|
{
|
|
public EmulatorWindow(Mainform parent)
|
|
{
|
|
InitializeComponent();
|
|
Closing += (o, e) =>
|
|
{
|
|
ShutDown();
|
|
};
|
|
|
|
MainForm = parent;
|
|
}
|
|
|
|
private void EmulatorWindow_Load(object sender, EventArgs e)
|
|
{
|
|
if (Game != null)
|
|
{
|
|
Text = Game.Name;
|
|
}
|
|
}
|
|
|
|
public Mainform MainForm { get; private set; }
|
|
public IEmulator Emulator { get; set; }
|
|
public CoreComm CoreComm { get; set; }
|
|
public GameInfo Game { get; set; }
|
|
public string CurrentRomPath { get; set; }
|
|
|
|
public IGL GL { get; set; }
|
|
public GLManager.ContextRef CR_GL { get; set; }
|
|
public GLManager GLManager { get; set; }
|
|
|
|
public PresentationPanel PresentationPanel { get; set; }
|
|
|
|
//public Sound Sound; // TODO
|
|
public DisplayManager DisplayManager;
|
|
|
|
|
|
public void Init()
|
|
{
|
|
PresentationPanel = new PresentationPanel(this, GL);
|
|
CR_GL = GLManager.GetContextForIGL(GL);
|
|
DisplayManager = new DisplayManager(PresentationPanel, GL ,GLManager);
|
|
|
|
Controls.Add(PresentationPanel);
|
|
Controls.SetChildIndex(PresentationPanel, 0);
|
|
}
|
|
|
|
public void ShutDown()
|
|
{
|
|
SaveRam();
|
|
MainForm.EmulatorWindowClosed(this);
|
|
Emulator.Dispose();
|
|
GL.Dispose();
|
|
}
|
|
|
|
public void LoadQuickSave(string quickSlotName)
|
|
{
|
|
if (!Emulator.HasSavestates())
|
|
{
|
|
return;
|
|
}
|
|
|
|
var path = PathManager.SaveStatePrefix(Game) + "." + quickSlotName + ".State";
|
|
|
|
if (LoadStateFile(path, quickSlotName))
|
|
{
|
|
// SetMainformMovieInfo(); // TODO
|
|
MainForm.AddMessage("Loaded state: " + quickSlotName);
|
|
}
|
|
else
|
|
{
|
|
MainForm.AddMessage("Loadstate error!");
|
|
}
|
|
}
|
|
|
|
public bool LoadStateFile(string path, string name)
|
|
{
|
|
var core = Emulator.AsStatable();
|
|
|
|
// try to detect binary first
|
|
var bl = BinaryStateLoader.LoadAndDetect(path);
|
|
if (bl != null)
|
|
{
|
|
try
|
|
{
|
|
var succeed = false;
|
|
|
|
// TODO
|
|
if (IAmMaster)
|
|
{
|
|
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);
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
finally
|
|
{
|
|
bl.Dispose();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else // text mode
|
|
{
|
|
if (Global.MovieSession.HandleMovieLoadState(path))
|
|
{
|
|
using (var reader = new StreamReader(path))
|
|
{
|
|
core.LoadStateText(reader);
|
|
|
|
while (true)
|
|
{
|
|
var str = reader.ReadLine();
|
|
if (str == null)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (str.Trim() == "")
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var args = str.Split(' ');
|
|
if (args[0] == "Framebuffer")
|
|
{
|
|
Emulator.AsVideoProvider().GetVideoBuffer().ReadFromHex(args[1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void PopulateFramebuffer(BinaryReader br)
|
|
{
|
|
try
|
|
{
|
|
using (new SimpleTime("Load Framebuffer"))
|
|
QuickBmpFile.Load(Emulator.AsVideoProvider(), br.BaseStream);
|
|
}
|
|
catch
|
|
{
|
|
var buff = Emulator.AsVideoProvider().GetVideoBuffer();
|
|
try
|
|
{
|
|
for (int i = 0; i < buff.Length; i++)
|
|
{
|
|
int j = br.ReadInt32();
|
|
buff[i] = j;
|
|
}
|
|
}
|
|
catch (EndOfStreamException) { }
|
|
}
|
|
}
|
|
|
|
public void SaveQuickSave(string quickSlotName)
|
|
{
|
|
if (!Emulator.HasSavestates())
|
|
{
|
|
return;
|
|
}
|
|
|
|
var path = PathManager.SaveStatePrefix(Game) + "." + quickSlotName + ".State";
|
|
|
|
var file = new FileInfo(path);
|
|
if (file.Directory != null && file.Directory.Exists == false)
|
|
{
|
|
file.Directory.Create();
|
|
}
|
|
|
|
// TODO
|
|
// Make backup first
|
|
//if (Global.Config.BackupSavestates && file.Exists)
|
|
//{
|
|
// var backup = path + ".bak";
|
|
// var backupFile = new FileInfo(backup);
|
|
// if (backupFile.Exists)
|
|
// {
|
|
// backupFile.Delete();
|
|
// }
|
|
|
|
// File.Move(path, backup);
|
|
//}
|
|
|
|
try
|
|
{
|
|
SaveStateFile(path, quickSlotName);
|
|
|
|
MainForm.AddMessage("Saved state: " + quickSlotName);
|
|
}
|
|
catch (IOException)
|
|
{
|
|
MainForm.AddMessage("Unable to save state " + path);
|
|
}
|
|
|
|
// TODO
|
|
}
|
|
|
|
private void SaveStateFile(string filename, string name)
|
|
{
|
|
var core = Emulator.AsStatable();
|
|
|
|
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 (true) //TODO: Global.Config.SaveScreenshotWithStates)
|
|
{
|
|
var vp = Emulator.AsVideoProvider();
|
|
var buff = vp.GetVideoBuffer();
|
|
|
|
int out_w = vp.BufferWidth;
|
|
int out_h = vp.BufferHeight;
|
|
|
|
// if buffer is too big, scale down screenshot
|
|
if (true /* !Global.Config.NoLowResLargeScreenshotWithStates*/ && buff.Length >= Global.Config.BigScreenshotSize)
|
|
{
|
|
out_w /= 2;
|
|
out_h /= 2;
|
|
}
|
|
using (new SimpleTime("Save Framebuffer"))
|
|
bs.PutLump(BinaryStateLump.Framebuffer, (s) => QuickBmpFile.Save(Emulator.AsVideoProvider(), s, out_w, out_h));
|
|
}
|
|
|
|
if (IAmMaster)
|
|
{
|
|
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}", Emulator.Frame);
|
|
Global.MovieSession.HandleMovieSaveState(tw);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IAmMaster
|
|
{
|
|
get
|
|
{
|
|
return MainForm.EmulatorWindows.First() == this;
|
|
}
|
|
}
|
|
|
|
public void FrameBufferResized()
|
|
{
|
|
// run this entire thing exactly twice, since the first resize may adjust the menu stacking
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
var video = Emulator.AsVideoProvider();
|
|
int zoom = Global.Config.TargetZoomFactors[Global.Emulator.SystemId];
|
|
var area = Screen.FromControl(this).WorkingArea;
|
|
|
|
int borderWidth = Size.Width - PresentationPanel.Control.Size.Width;
|
|
int borderHeight = Size.Height - PresentationPanel.Control.Size.Height;
|
|
|
|
// start at target zoom and work way down until we find acceptable zoom
|
|
Size lastComputedSize = new Size(1, 1);
|
|
for (; zoom >= 1; zoom--)
|
|
{
|
|
lastComputedSize = DisplayManager.CalculateClientSize(video, zoom);
|
|
if ((((lastComputedSize.Width) + borderWidth) < area.Width)
|
|
&& (((lastComputedSize.Height) + borderHeight) < area.Height))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
Console.WriteLine("Selecting display size " + lastComputedSize.ToString());
|
|
|
|
// Change size
|
|
Size = new Size((lastComputedSize.Width) + borderWidth, ((lastComputedSize.Height) + borderHeight));
|
|
PerformLayout();
|
|
PresentationPanel.Resized = true;
|
|
|
|
// Is window off the screen at this size?
|
|
if (area.Contains(Bounds) == false)
|
|
{
|
|
if (Bounds.Right > area.Right) // Window is off the right edge
|
|
{
|
|
Location = new Point(area.Right - Size.Width, Location.Y);
|
|
}
|
|
|
|
if (Bounds.Bottom > area.Bottom) // Window is off the bottom edge
|
|
{
|
|
Location = new Point(Location.X, area.Bottom - Size.Height);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private Size _lastVideoSize = new Size(-1, -1), _lastVirtualSize = new Size(-1, -1);
|
|
public void Render()
|
|
{
|
|
var video = Emulator.AsVideoProvider();
|
|
|
|
Size currVideoSize = new Size(video.BufferWidth, video.BufferHeight);
|
|
Size currVirtualSize = new Size(video.VirtualWidth, video.VirtualWidth);
|
|
if (currVideoSize != _lastVideoSize || currVirtualSize != _lastVirtualSize)
|
|
{
|
|
_lastVideoSize = currVideoSize;
|
|
_lastVirtualSize = currVirtualSize;
|
|
FrameBufferResized();
|
|
}
|
|
|
|
DisplayManager.UpdateSource(video);
|
|
}
|
|
|
|
public void FrameAdvance()
|
|
{
|
|
Emulator.FrameAdvance(Global.ControllerOutput, true);
|
|
}
|
|
|
|
public void SaveRam()
|
|
{
|
|
if (Emulator.HasSaveRam() && Emulator.AsSaveRam().SaveRamModified)
|
|
{
|
|
var path = PathManager.SaveRamPath(Global.Game);
|
|
var f = new FileInfo(path);
|
|
if (f.Directory != null && f.Directory.Exists == false)
|
|
{
|
|
f.Directory.Create();
|
|
}
|
|
|
|
// Make backup first
|
|
if (Global.Config.BackupSaveram && f.Exists)
|
|
{
|
|
var backup = path + ".bak";
|
|
var backupFile = new FileInfo(backup);
|
|
if (backupFile.Exists)
|
|
{
|
|
backupFile.Delete();
|
|
}
|
|
|
|
f.CopyTo(backup);
|
|
}
|
|
|
|
var writer = new BinaryWriter(new FileStream(path, FileMode.Create, FileAccess.Write));
|
|
var saveram = Emulator.AsSaveRam().CloneSaveRam();
|
|
|
|
writer.Write(saveram, 0, saveram.Length);
|
|
writer.Close();
|
|
}
|
|
}
|
|
}
|
|
}
|