BizHawk/BizHawk.Emulation.Cores/Consoles/Nintendo/VB/VirtualBoyee.cs

260 lines
6.6 KiB
C#

using BizHawk.Common;
using BizHawk.Common.BizInvoke;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Waterbox;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.VB
{
[Core("Virtual Boyee", "Mednafen Team", true, true, "0.9.44.1",
"https://mednafen.github.io/releases/", false)]
public class VirtualBoyee : WaterboxCore, ISettable<VirtualBoyee.Settings, VirtualBoyee.SyncSettings>
{
private LibVirtualBoyee _boyee;
[CoreConstructor("VB")]
public VirtualBoyee(CoreComm comm, byte[] rom, Settings settings, SyncSettings syncSettings)
: base(comm, new Configuration
{
DefaultFpsNumerator = 20000000,
DefaultFpsDenominator = 397824,
DefaultWidth = 384,
DefaultHeight = 224,
MaxWidth = 1024,
MaxHeight = 1024,
MaxSamples = 8192,
SystemId = "VB"
})
{
_settings = settings ?? new Settings();
_syncSettings = syncSettings ?? new SyncSettings();
_boyee = PreInit<LibVirtualBoyee>(new PeRunnerOptions
{
Filename = "vb.wbx",
SbrkHeapSizeKB = 256,
SealedHeapSizeKB = 4 * 1024,
InvisibleHeapSizeKB = 256,
PlainHeapSizeKB = 256
});
if (!_boyee.Load(rom, rom.Length, LibVirtualBoyee.NativeSyncSettings.FromFrontendSettings(_syncSettings)))
throw new InvalidOperationException("Core rejected the rom");
// do a quick hack up for frame zero size
var tmp = new LibVirtualBoyee.FrameInfo();
_boyee.PredictFrameSize(tmp);
BufferWidth = tmp.Width;
BufferHeight = tmp.Height;
PostInit();
_boyee.SetSettings(LibVirtualBoyee.NativeSettings.FromFrontendSettings(_settings));
}
protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound)
{
if (controller.IsPressed("Power"))
_boyee.HardReset();
return new LibVirtualBoyee.FrameInfo { Buttons = GetButtons(controller) };
}
#region Controller
private LibVirtualBoyee.Buttons GetButtons(IController c)
{
var ret = 0;
var val = 1;
foreach (var s in CoreButtons)
{
if (c.IsPressed(s))
ret |= val;
val <<= 1;
}
return (LibVirtualBoyee.Buttons)ret;
}
private static readonly string[] CoreButtons =
{
"A", "B", "R", "L",
"R_Up", "R_Right",
"L_Right", "L_Left", "L_Down", "L_Up",
"Start", "Select", "R_Left", "R_Down"
};
private static readonly Dictionary<string, int> _buttonOrdinals = new Dictionary<string, int>
{
["L_Up"] = 1,
["L_Down"] = 2,
["L_Left"] = 3,
["L_Right"] = 4,
["R_Up"] = 5,
["R_Down"] = 6,
["R_Left"] = 7,
["R_Right"] = 8,
["B"] = 9,
["A"] = 10,
["L"] = 11,
["R"] = 12,
["Select"] = 13,
["Start"] = 14
};
private static readonly ControllerDefinition VirtualBoyController = new ControllerDefinition
{
Name = "VirtualBoy Controller",
BoolButtons = CoreButtons
.OrderBy(b => _buttonOrdinals[b])
.Concat(new[] { "Power" })
.ToList()
};
public override ControllerDefinition ControllerDefinition => VirtualBoyController;
#endregion
#region ISettable
public class SyncSettings
{
[DefaultValue(false)]
[Description("Reduce input latency. Works with all known commercial games, may have homebrew issues.")]
public bool InstantReadHack { get; set; }
[DefaultValue(false)]
[Description("Disable parallax for rendering.")]
public bool DisableParallax { get; set; }
public SyncSettings Clone()
{
return (SyncSettings)MemberwiseClone();
}
public static bool NeedsReboot(SyncSettings x, SyncSettings y)
{
return !DeepEquality.DeepEquals(x, y);
}
public SyncSettings()
{
SettingsUtil.SetDefaultValues(this);
}
}
public class Settings
{
public enum ThreeDeeModes : int
{
Anaglyph = 0,
CyberScope = 1,
SideBySide = 2,
//OverUnder,
VerticalInterlaced = 4,
HorizontalInterlaced = 5
}
[DefaultValue(ThreeDeeModes.Anaglyph)]
[Description("How to display the 3d image. Use whichever method works with your VR hardware.")]
public ThreeDeeModes ThreeDeeMode { get; set; }
[DefaultValue(false)]
[Description("Swap the left and right views.")]
public bool SwapViews { get; set; }
public enum AnaglyphPresets : int
{
Custom,
RedBlue,
RedCyan,
RedElectricCyan,
RedGreen,
GreenMagneto,
YellowBlue
}
[DefaultValue(AnaglyphPresets.RedBlue)]
[Description("Color preset for Anaglyph mode.")]
public AnaglyphPresets AnaglyphPreset { get; set; }
[DefaultValue(typeof(Color), "Green")]
[Description("Left anaglyph color. Ignored unless Preset is Custom.")]
public Color AnaglyphCustomLeftColor { get; set; }
[DefaultValue(typeof(Color), "Purple")]
[Description("Right anaglyph color. Ignored unless Preset is Custom.")]
public Color AnaglyphCustomRightColor { get; set; }
[DefaultValue(typeof(Color), "White")]
[Description("Display color for all of the non-anaglyph modes. Real hardware was red, but other colors may be easier on your eyes.")]
public Color NonAnaglyphColor { get; set; }
[DefaultValue(1750)]
[Range(1000, 2000)]
[Description("LED gamma ramp. Range of 1000 to 2000")]
public int LedOnScale { get; set; }
[DefaultValue(2)]
[Range(1, 10)]
public int InterlacePrescale { get; set; }
[DefaultValue(0)]
[Range(0, 1024)]
[Description("How many pixels to put between views in Side By Side mode")]
public int SideBySideSeparation { get; set; }
public Settings Clone()
{
return (Settings)MemberwiseClone();
}
public static bool NeedsReboot(Settings x, Settings y)
{
return !DeepEquality.DeepEquals(x, y);
}
public Settings()
{
SettingsUtil.SetDefaultValues(this);
}
}
private Settings _settings;
private SyncSettings _syncSettings;
public Settings GetSettings()
{
return _settings.Clone();
}
public SyncSettings GetSyncSettings()
{
return _syncSettings.Clone();
}
public bool PutSettings(Settings o)
{
var ret = Settings.NeedsReboot(_settings, o);
_settings = o;
return ret;
}
public bool PutSyncSettings(SyncSettings o)
{
var ret = SyncSettings.NeedsReboot(_syncSettings, o);
_syncSettings = o;
return ret;
}
#endregion
}
}