diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
index b236773592..127d3adb1a 100644
--- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
+++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
@@ -901,9 +901,24 @@
-
+
+ LibsnesCore.cs
+
+
LibsnesCore.cs
+
+ LibsnesCore.cs
+
+
+ LibsnesCore.cs
+
+
+ LibsnesCore.cs
+
+
+ LibsnesCore.cs
+
@@ -911,6 +926,12 @@
+
+ LibsnesCore.cs
+
+
+ LibsnesCore.cs
+
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ICodeDataLogger.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ICodeDataLogger.cs
new file mode 100644
index 0000000000..64d72acc24
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ICodeDataLogger.cs
@@ -0,0 +1,42 @@
+using System.IO;
+using BizHawk.Emulation.Common;
+
+namespace BizHawk.Emulation.Cores.Nintendo.SNES
+{
+ public partial class LibsnesCore : ICodeDataLogger
+ {
+ public void SetCDL(ICodeDataLog cdl)
+ {
+ _currCdl?.Unpin();
+ _currCdl = cdl;
+ _currCdl?.Pin();
+
+ // set it no matter what. if its null, the cdl will be unhooked from libsnes internally
+ api.QUERY_set_cdl(_currCdl);
+ }
+
+ public void NewCDL(ICodeDataLog cdl)
+ {
+ cdl["CARTROM"] = new byte[MemoryDomains["CARTROM"].Size];
+
+ if (MemoryDomains.Has("CARTRAM"))
+ {
+ cdl["CARTRAM"] = new byte[MemoryDomains["CARTRAM"].Size];
+ }
+
+ cdl["WRAM"] = new byte[MemoryDomains["WRAM"].Size];
+ cdl["APURAM"] = new byte[MemoryDomains["APURAM"].Size];
+
+ cdl.SubType = "SNES";
+ cdl.SubVer = 0;
+ }
+
+ public void DisassembleCDL(Stream s, ICodeDataLog cdl)
+ {
+ // TODO: should this throw a NotImplementedException?
+ // not supported yet
+ }
+
+ private ICodeDataLog _currCdl;
+ }
+}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IDebuggable.cs
new file mode 100644
index 0000000000..d076b971b8
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IDebuggable.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+
+using BizHawk.Emulation.Common;
+
+namespace BizHawk.Emulation.Cores.Nintendo.SNES
+{
+ public partial class LibsnesCore : IDebuggable
+ {
+ public IDictionary GetCpuFlagsAndRegisters()
+ {
+ LibsnesApi.CPURegs regs;
+ api.QUERY_peek_cpu_regs(out regs);
+
+ bool fn = (regs.p & 0x80) != 0;
+ bool fv = (regs.p & 0x40) != 0;
+ bool fm = (regs.p & 0x20) != 0;
+ bool fx = (regs.p & 0x10) != 0;
+ bool fd = (regs.p & 0x08) != 0;
+ bool fi = (regs.p & 0x04) != 0;
+ bool fz = (regs.p & 0x02) != 0;
+ bool fc = (regs.p & 0x01) != 0;
+
+ return new Dictionary
+ {
+ { "PC", regs.pc },
+ { "A", regs.a },
+ { "X", regs.x },
+ { "Y", regs.y },
+ { "Z", regs.z },
+ { "S", regs.s },
+ { "D", regs.d },
+ { "Vector", regs.vector },
+ { "P", regs.p },
+ { "AA", regs.aa },
+ { "RD", regs.rd },
+ { "SP", regs.sp },
+ { "DP", regs.dp },
+ { "DB", regs.db },
+ { "MDR", regs.mdr },
+ { "Flag N", fn },
+ { "Flag V", fv },
+ { "Flag M", fm },
+ { "Flag X", fx },
+ { "Flag D", fd },
+ { "Flag I", fi },
+ { "Flag Z", fz },
+ { "Flag C", fc },
+ };
+ }
+
+ [FeatureNotImplemented]
+ public void SetCpuRegister(string register, int value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IMemoryCallbackSystem MemoryCallbacks { get; } = new MemoryCallbackSystem();
+
+ public bool CanStep(StepType type)
+ {
+ return false;
+ }
+
+ [FeatureNotImplemented]
+ public void Step(StepType type)
+ {
+ throw new NotImplementedException();
+ }
+
+ [FeatureNotImplemented]
+ public int TotalExecutedCycles
+ {
+ get { throw new NotImplementedException(); }
+ }
+ }
+}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IInputPollable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IInputPollable.cs
new file mode 100644
index 0000000000..c22064569a
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IInputPollable.cs
@@ -0,0 +1,14 @@
+using BizHawk.Emulation.Common;
+
+namespace BizHawk.Emulation.Cores.Nintendo.SNES
+{
+ public partial class LibsnesCore : IInputPollable
+ {
+ public int LagCount { get; set; }
+
+ public bool IsLagFrame { get; set; }
+
+ // TODO: optimize managed to unmanaged using the ActiveChanged event
+ public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem();
+ }
+}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IRegionable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IRegionable.cs
new file mode 100644
index 0000000000..c2ae1f8fa7
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IRegionable.cs
@@ -0,0 +1,20 @@
+using BizHawk.Emulation.Common;
+
+namespace BizHawk.Emulation.Cores.Nintendo.SNES
+{
+ public partial class LibsnesCore : IRegionable
+ {
+ public DisplayType Region
+ {
+ get
+ {
+ if (api.Region == LibsnesApi.SNES_REGION.NTSC)
+ {
+ return DisplayType.NTSC;
+ }
+
+ return DisplayType.PAL;
+ }
+ }
+ }
+}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs
new file mode 100644
index 0000000000..102effe33d
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Runtime.InteropServices;
+
+using BizHawk.Emulation.Common;
+
+namespace BizHawk.Emulation.Cores.Nintendo.SNES
+{
+ public unsafe partial class LibsnesCore : ISaveRam
+ {
+ public bool SaveRamModified =>
+ api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM) != 0
+ || api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM) != 0;
+
+ public byte[] CloneSaveRam()
+ {
+ byte* buf = api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
+ var size = api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
+ if (buf == null)
+ {
+ buf = api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.SGB_CARTRAM);
+ size = api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM);
+ }
+
+ var ret = new byte[size];
+ Marshal.Copy((IntPtr)buf, ret, 0, size);
+ return ret;
+ }
+
+ public void StoreSaveRam(byte[] data)
+ {
+ byte* buf = api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
+ var size = api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
+ if (buf == null)
+ {
+ buf = api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.SGB_CARTRAM);
+ size = api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM);
+ }
+
+ if (size == 0)
+ {
+ return;
+ }
+
+ if (size != data.Length)
+ {
+ throw new InvalidOperationException("Somehow, we got a mismatch between saveram size and what bsnes says the saveram size is");
+ }
+
+ Marshal.Copy(data, 0, (IntPtr)buf, size);
+ }
+ }
+}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IStatable.cs
new file mode 100644
index 0000000000..62983983bf
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IStatable.cs
@@ -0,0 +1,136 @@
+using System;
+using System.IO;
+
+using BizHawk.Common.BufferExtensions;
+using BizHawk.Emulation.Common;
+
+namespace BizHawk.Emulation.Cores.Nintendo.SNES
+{
+ public unsafe partial class LibsnesCore : IStatable
+ {
+ public bool BinarySaveStatesPreferred => true;
+
+ public void SaveStateText(TextWriter writer)
+ {
+ var temp = SaveStateBinary();
+ temp.SaveAsHexFast(writer);
+ writer.WriteLine("Frame {0}", Frame); // we don't parse this, it's only for the client to use
+ writer.WriteLine("Profile {0}", CurrentProfile);
+ }
+
+ public void LoadStateText(TextReader reader)
+ {
+ string hex = reader.ReadLine();
+ byte[] state = new byte[hex.Length / 2];
+ state.ReadFromHexFast(hex);
+ LoadStateBinary(new BinaryReader(new MemoryStream(state)));
+ reader.ReadLine(); // Frame #
+ var profile = reader.ReadLine().Split(' ')[1];
+ ValidateLoadstateProfile(profile);
+ }
+
+ public void SaveStateBinary(BinaryWriter writer)
+ {
+ writer.Write(DeterministicEmulation ? _savestatebuff : CoreSaveState());
+
+ // other variables
+ writer.Write(IsLagFrame);
+ writer.Write(LagCount);
+ writer.Write(Frame);
+ writer.Write(CurrentProfile);
+
+ writer.Flush();
+ }
+
+ public void LoadStateBinary(BinaryReader reader)
+ {
+ int size = api.QUERY_serialize_size();
+ byte[] buf = reader.ReadBytes(size);
+ CoreLoadState(buf);
+
+ if (DeterministicEmulation) // deserialize controller and fast-foward now
+ {
+ // reconstruct savestatebuff at the same time to avoid a costly core serialize
+ var ms = new MemoryStream();
+ var bw = new BinaryWriter(ms);
+ bw.Write(buf);
+ bool framezero = reader.ReadBoolean();
+ bw.Write(framezero);
+ if (!framezero)
+ {
+ var ssc = new SnesSaveController(ControllerDefinition);
+ ssc.DeSerialize(reader);
+ IController tmp = Controller;
+ Controller = ssc;
+ nocallbacks = true;
+ FrameAdvance(false, false);
+ nocallbacks = false;
+ Controller = tmp;
+ ssc.Serialize(bw);
+ }
+ else // hack: dummy controller info
+ {
+ bw.Write(reader.ReadBytes(536));
+ }
+
+ bw.Close();
+ _savestatebuff = ms.ToArray();
+ }
+
+ // other variables
+ IsLagFrame = reader.ReadBoolean();
+ LagCount = reader.ReadInt32();
+ Frame = reader.ReadInt32();
+ var profile = reader.ReadString();
+ ValidateLoadstateProfile(profile);
+ }
+
+ public byte[] SaveStateBinary()
+ {
+ var ms = new MemoryStream();
+ var bw = new BinaryWriter(ms);
+ SaveStateBinary(bw);
+ bw.Flush();
+ return ms.ToArray();
+ }
+
+ // handle the unmanaged part of loadstating
+ private void CoreLoadState(byte[] data)
+ {
+ int size = api.QUERY_serialize_size();
+ if (data.Length != size)
+ {
+ throw new Exception("Libsnes internal savestate size mismatch!");
+ }
+
+ api.CMD_init();
+
+ // zero 01-sep-2014 - this approach isn't being used anymore, it's too slow!
+ // LoadCurrent(); //need to make sure chip roms are reloaded
+ fixed (byte* pbuf = &data[0])
+ api.CMD_unserialize(new IntPtr(pbuf), size);
+ }
+
+
+ // handle the unmanaged part of savestating
+ private byte[] CoreSaveState()
+ {
+ int size = api.QUERY_serialize_size();
+ byte[] buf = new byte[size];
+ fixed (byte* pbuf = &buf[0])
+ api.CMD_serialize(new IntPtr(pbuf), size);
+ return buf;
+ }
+
+ private void ValidateLoadstateProfile(string profile)
+ {
+ if (profile != CurrentProfile)
+ {
+ throw new InvalidOperationException($"You've attempted to load a savestate made using a different SNES profile ({profile}) than your current configuration ({CurrentProfile}). We COULD automatically switch for you, but we havent done that yet. This error is to make sure you know that this isnt going to work right now.");
+ }
+ }
+
+ // most recent internal savestate, for deterministic mode ONLY
+ private byte[] _savestatebuff;
+ }
+}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IVideoProvider.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IVideoProvider.cs
new file mode 100644
index 0000000000..453485a52a
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IVideoProvider.cs
@@ -0,0 +1,26 @@
+using BizHawk.Emulation.Common;
+
+namespace BizHawk.Emulation.Cores.Nintendo.SNES
+{
+ public partial class LibsnesCore : IVideoProvider
+ {
+ public int VirtualWidth => (int)(_videoWidth * 1.146);
+
+ public int VirtualHeight => _videoHeight;
+
+ public int BufferWidth => _videoWidth;
+
+ public int BufferHeight => _videoHeight;
+
+ public int BackgroundColor => 0;
+
+ public int[] GetVideoBuffer()
+ {
+ return _videoBuffer;
+ }
+
+ private int[] _videoBuffer = new int[256 * 224];
+ private int _videoWidth = 256;
+ private int _videoHeight = 224;
+ }
+}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs
index bcf18ae93f..9433b0bd0f 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.cs
@@ -1,19 +1,17 @@
-//TODO - add serializer (?)
+// TODO - add serializer (?)
-//http://wiki.superfamicom.org/snes/show/Backgrounds
+// http://wiki.superfamicom.org/snes/show/Backgrounds
-//TODO
-//libsnes needs to be modified to support multiple instances - THIS IS NECESSARY - or else loading one game and then another breaks things
+// TODO
+// libsnes needs to be modified to support multiple instances - THIS IS NECESSARY - or else loading one game and then another breaks things
// edit - this is a lot of work
-//wrap dll code around some kind of library-accessing interface so that it doesnt malfunction if the dll is unavailablecd
+// wrap dll code around some kind of library-accessing interface so that it doesnt malfunction if the dll is unavailablecd
using System;
using System.Linq;
using System.Xml;
-using System.Xml.Linq;
using System.IO;
using System.Collections.Generic;
-using System.Runtime.InteropServices;
using BizHawk.Common;
using BizHawk.Common.BufferExtensions;
@@ -36,7 +34,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
public LibsnesCore(GameInfo game, byte[] romData, bool deterministicEmulation, byte[] xmlData, CoreComm comm, object Settings, object SyncSettings)
{
ServiceProvider = new BasicServiceProvider(this);
- MemoryCallbacks = new MemoryCallbackSystem();
Tracer = new TraceBuffer
{
Header = "65816: PC, mnemonic, operands, registers (A, X, Y, S, D, DB, flags (NVMXDIZC), V, H)"
@@ -173,41 +170,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
// hack: write fake dummy controller info
bw.Write(new byte[536]);
bw.Close();
- savestatebuff = ms.ToArray();
+ _savestatebuff = ms.ToArray();
}
}
- ICodeDataLog currCdl;
-
- public void SetCDL(ICodeDataLog cdl)
- {
- if(currCdl != null) currCdl.Unpin();
- currCdl = cdl;
- if(currCdl != null) currCdl.Pin();
-
- //set it no matter what. if its null, the cdl will be unhooked from libsnes internally
- api.QUERY_set_cdl(currCdl);
- }
-
- public void NewCDL(ICodeDataLog cdl)
- {
- cdl["CARTROM"] = new byte[MemoryDomains["CARTROM"].Size];
-
- if (MemoryDomains.Has("CARTRAM"))
- cdl["CARTRAM"] = new byte[MemoryDomains["CARTRAM"].Size];
-
- cdl["WRAM"] = new byte[MemoryDomains["WRAM"].Size];
- cdl["APURAM"] = new byte[MemoryDomains["APURAM"].Size];
-
- cdl.SubType = "SNES";
- cdl.SubVer = 0;
- }
-
- public void DisassembleCDL(Stream s, ICodeDataLog cdl)
- {
- //not supported yet
- }
-
public IEmulatorServiceProvider ServiceProvider { get; private set; }
private GameInfo _game;
@@ -245,75 +211,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
resampler.Dispose();
api.Dispose();
- if (currCdl != null) currCdl.Unpin();
+ if (_currCdl != null) _currCdl.Unpin();
}
- public IDictionary GetCpuFlagsAndRegisters()
- {
- LibsnesApi.CPURegs regs;
- api.QUERY_peek_cpu_regs(out regs);
-
- bool fn = (regs.p & 0x80)!=0;
- bool fv = (regs.p & 0x40)!=0;
- bool fm = (regs.p & 0x20)!=0;
- bool fx = (regs.p & 0x10)!=0;
- bool fd = (regs.p & 0x08)!=0;
- bool fi = (regs.p & 0x04)!=0;
- bool fz = (regs.p & 0x02)!=0;
- bool fc = (regs.p & 0x01)!=0;
-
- return new Dictionary
- {
- { "PC", regs.pc },
- { "A", regs.a },
- { "X", regs.x },
- { "Y", regs.y },
- { "Z", regs.z },
- { "S", regs.s },
- { "D", regs.d },
- { "Vector", regs.vector },
- { "P", regs.p },
- { "AA", regs.aa },
- { "RD", regs.rd },
- { "SP", regs.sp },
- { "DP", regs.dp },
- { "DB", regs.db },
- { "MDR", regs.mdr },
- { "Flag N", fn },
- { "Flag V", fv },
- { "Flag M", fm },
- { "Flag X", fx },
- { "Flag D", fd },
- { "Flag I", fi },
- { "Flag Z", fz },
- { "Flag C", fc },
- };
- }
-
- private readonly InputCallbackSystem _inputCallbacks = new InputCallbackSystem();
-
- // TODO: optimize managed to unmanaged using the ActiveChanged event
- public IInputCallbackSystem InputCallbacks { get { return _inputCallbacks; } }
-
public ITraceable Tracer { get; private set; }
- public IMemoryCallbackSystem MemoryCallbacks { get; private set; }
-
- public bool CanStep(StepType type) { return false; }
-
- [FeatureNotImplemented]
- public void Step(StepType type) { throw new NotImplementedException(); }
-
- [FeatureNotImplemented]
- public void SetCpuRegister(string register, int value)
- {
- throw new NotImplementedException();
- }
-
- [FeatureNotImplemented]
- public int TotalExecutedCycles
- {
- get { throw new NotImplementedException(); }
- }
public class MyScanlineHookManager : ScanlineHookManager
{
@@ -328,6 +229,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
core.OnScanlineHooksChanged();
}
}
+
public MyScanlineHookManager ScanlineHookManager;
void OnScanlineHooksChanged()
{
@@ -534,15 +436,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
bool doubleSize = _settings.AlwaysDoubleSize;
bool lineDouble = doubleSize, dotDouble = doubleSize;
- vidWidth = width;
- vidHeight = height;
+ _videoWidth = width;
+ _videoHeight = height;
int yskip = 1, xskip = 1;
//if we are in high-res mode, we get double width. so, lets double the height here to keep it square.
if (width == 512)
{
- vidHeight *= 2;
+ _videoHeight *= 2;
yskip = 2;
lineDouble = true;
@@ -551,7 +453,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
}
else if (lineDouble)
{
- vidHeight *= 2;
+ _videoHeight *= 2;
yskip = 2;
}
@@ -570,18 +472,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
lineDouble = false;
srcPitch = 512;
yskip = 1;
- vidHeight = height;
+ _videoHeight = height;
}
if (dotDouble)
{
- vidWidth *= 2;
+ _videoWidth *= 2;
xskip = 2;
}
- int size = vidWidth * vidHeight;
- if (vidBuffer.Length != size)
- vidBuffer = new int[size];
+ int size = _videoWidth * _videoHeight;
+ if (_videoBuffer.Length != size)
+ _videoBuffer = new int[size];
for (int j = 0; j < 2; j++)
{
@@ -592,14 +494,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
//potentially do this twice, if we need to line double
if (i == 1 && !lineDouble) break;
- int bonus = i * vidWidth + xbonus;
+ int bonus = i * _videoWidth + xbonus;
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
int si = y * srcPitch + x + srcStart;
- int di = y * vidWidth * yskip + x * xskip + bonus;
+ int di = y * _videoWidth * yskip + x * xskip + bonus;
int rgb = data[si];
- vidBuffer[di] = rgb;
+ _videoBuffer[di] = rgb;
}
}
}
@@ -632,7 +534,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
ssc.CopyFrom(Controller);
ssc.Serialize(bw);
bw.Close();
- savestatebuff = ms.ToArray();
+ _savestatebuff = ms.ToArray();
}
// speedup when sound rendering is not needed
@@ -682,28 +584,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
api.QUERY_set_state_hook_write(!suppress && mcs.HasWrites);
}
- public DisplayType Region
- {
- get
- {
- if (api.Region == LibsnesApi.SNES_REGION.NTSC)
- return DisplayType.NTSC;
- else
- return DisplayType.PAL;
- }
- }
-
- //video provider
- int IVideoProvider.BackgroundColor { get { return 0; } }
- int[] IVideoProvider.GetVideoBuffer() { return vidBuffer; }
- int IVideoProvider.VirtualWidth { get { return (int)(vidWidth * 1.146); } }
- public int VirtualHeight { get { return vidHeight; } }
- int IVideoProvider.BufferWidth { get { return vidWidth; } }
- int IVideoProvider.BufferHeight { get { return vidHeight; } }
-
- int[] vidBuffer = new int[256 * 224];
- int vidWidth = 256, vidHeight = 224;
-
public ControllerDefinition ControllerDefinition { get { return _controllerDeck.Definition; } }
IController controller;
public IController Controller
@@ -714,8 +594,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
int timeFrameCounter;
public int Frame { get { return timeFrameCounter; } set { timeFrameCounter = value; } }
- public int LagCount { get; set; }
- public bool IsLagFrame { get; set; }
+
public string SystemId { get; private set; }
public string BoardName { get; private set; }
@@ -734,28 +613,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
private set { /* Do nothing */ }
}
- public bool SaveRamModified
- {
- get
- {
- return api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM) != 0 || api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM) != 0;
- }
- }
-
- public byte[] CloneSaveRam()
- {
- byte* buf = api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
- var size = api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
- if (buf == null)
- {
- buf = api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.SGB_CARTRAM);
- size = api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM);
- }
- var ret = new byte[size];
- Marshal.Copy((IntPtr)buf, ret, 0, size);
- return ret;
- }
-
//public byte[] snes_get_memory_data_read(LibsnesApi.SNES_MEMORY id)
//{
// var size = (int)api.snes_get_memory_size(id);
@@ -764,20 +621,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
// return ret;
//}
- public void StoreSaveRam(byte[] data)
- {
- byte* buf = api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
- var size = api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM);
- if (buf == null)
- {
- buf = api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.SGB_CARTRAM);
- size = api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM);
- }
- if (size == 0) return;
- if (size != data.Length) throw new InvalidOperationException("Somehow, we got a mismatch between saveram size and what bsnes says the saveram size is");
- Marshal.Copy(data, 0, (IntPtr)buf, size);
- }
-
public void ResetCounters()
{
timeFrameCounter = 0;
@@ -886,133 +729,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
}
}
-
- public void SaveStateText(TextWriter writer)
- {
- var temp = SaveStateBinary();
- temp.SaveAsHexFast(writer);
- writer.WriteLine("Frame {0}", Frame); // we don't parse this, it's only for the client to use
- writer.WriteLine("Profile {0}", CurrentProfile);
- }
- public void LoadStateText(TextReader reader)
- {
- string hex = reader.ReadLine();
- byte[] state = new byte[hex.Length / 2];
- state.ReadFromHexFast(hex);
- LoadStateBinary(new BinaryReader(new MemoryStream(state)));
- reader.ReadLine(); // Frame #
- var profile = reader.ReadLine().Split(' ')[1];
- ValidateLoadstateProfile(profile);
- }
-
- public void SaveStateBinary(BinaryWriter writer)
- {
- if (!DeterministicEmulation)
- writer.Write(CoreSaveState());
- else
- writer.Write(savestatebuff);
-
- // other variables
- writer.Write(IsLagFrame);
- writer.Write(LagCount);
- writer.Write(Frame);
- writer.Write(CurrentProfile);
-
- writer.Flush();
- }
- public void LoadStateBinary(BinaryReader reader)
- {
- int size = api.QUERY_serialize_size();
- byte[] buf = reader.ReadBytes(size);
- CoreLoadState(buf);
-
- if (DeterministicEmulation) // deserialize controller and fast-foward now
- {
- // reconstruct savestatebuff at the same time to avoid a costly core serialize
- MemoryStream ms = new MemoryStream();
- BinaryWriter bw = new BinaryWriter(ms);
- bw.Write(buf);
- bool framezero = reader.ReadBoolean();
- bw.Write(framezero);
- if (!framezero)
- {
- SnesSaveController ssc = new SnesSaveController(ControllerDefinition);
- ssc.DeSerialize(reader);
- IController tmp = this.Controller;
- this.Controller = ssc;
- nocallbacks = true;
- FrameAdvance(false, false);
- nocallbacks = false;
- this.Controller = tmp;
- ssc.Serialize(bw);
- }
- else // hack: dummy controller info
- {
- bw.Write(reader.ReadBytes(536));
- }
- bw.Close();
- savestatebuff = ms.ToArray();
- }
-
- // other variables
- IsLagFrame = reader.ReadBoolean();
- LagCount = reader.ReadInt32();
- Frame = reader.ReadInt32();
- var profile = reader.ReadString();
- ValidateLoadstateProfile(profile);
- }
-
- void ValidateLoadstateProfile(string profile)
- {
- if (profile != CurrentProfile)
- {
- throw new InvalidOperationException(string.Format("You've attempted to load a savestate made using a different SNES profile ({0}) than your current configuration ({1}). We COULD automatically switch for you, but we havent done that yet. This error is to make sure you know that this isnt going to work right now.", profile, CurrentProfile));
- }
- }
-
- public byte[] SaveStateBinary()
- {
- MemoryStream ms = new MemoryStream();
- BinaryWriter bw = new BinaryWriter(ms);
- SaveStateBinary(bw);
- bw.Flush();
- return ms.ToArray();
- }
-
- public bool BinarySaveStatesPreferred { get { return true; } }
-
- ///
- /// handle the unmanaged part of loadstating
- ///
- void CoreLoadState(byte[] data)
- {
- int size = api.QUERY_serialize_size();
- if (data.Length != size)
- throw new Exception("Libsnes internal savestate size mismatch!");
- api.CMD_init();
- //zero 01-sep-2014 - this approach isn't being used anymore, it's too slow!
- //LoadCurrent(); //need to make sure chip roms are reloaded
- fixed (byte* pbuf = &data[0])
- api.CMD_unserialize(new IntPtr(pbuf), size);
- }
+
- ///
- /// handle the unmanaged part of savestating
- ///
- byte[] CoreSaveState()
- {
- int size = api.QUERY_serialize_size();
- byte[] buf = new byte[size];
- fixed (byte* pbuf = &buf[0])
- api.CMD_serialize(new IntPtr(pbuf), size);
- return buf;
- }
-
- ///
- /// most recent internal savestate, for deterministic mode ONLY
- ///
- byte[] savestatebuff;
#endregion