Remove most of (old) BSNES' managed source and callsites
This commit is contained in:
parent
fe85bfa41b
commit
3cc3440f22
|
@ -11,7 +11,6 @@ using BizHawk.Emulation.Cores.Consoles.Sega.gpgx;
|
|||
using BizHawk.Emulation.Cores.Nintendo.BSNES;
|
||||
using BizHawk.Emulation.Cores.Nintendo.NES;
|
||||
using BizHawk.Emulation.Cores.Consoles.Nintendo.NDS;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SNES;
|
||||
using BizHawk.Emulation.Cores.PCEngine;
|
||||
using BizHawk.Emulation.Cores.Sega.MasterSystem;
|
||||
using BizHawk.Emulation.Cores.WonderSwan;
|
||||
|
@ -195,7 +194,6 @@ namespace BizHawk.Client.Common
|
|||
public object? GetSettings() => Emulator switch
|
||||
{
|
||||
GPGX gpgx => gpgx.GetSettings(),
|
||||
LibsnesCore snes => snes.GetSettings(),
|
||||
NES nes => nes.GetSettings(),
|
||||
NDS nds => nds.GetSettings(),
|
||||
PCEngine pce => pce.GetSettings(),
|
||||
|
@ -208,7 +206,6 @@ namespace BizHawk.Client.Common
|
|||
public PutSettingsDirtyBits PutSettings(object settings) => Emulator switch
|
||||
{
|
||||
GPGX gpgx => gpgx.PutSettings((GPGX.GPGXSettings) settings),
|
||||
LibsnesCore snes => snes.PutSettings((LibsnesCore.SnesSettings) settings),
|
||||
NES nes => nes.PutSettings((NES.NESSettings) settings),
|
||||
NDS nds => nds.PutSettings((NDS.NDSSettings) settings),
|
||||
PCEngine pce => pce.PutSettings((PCEngine.PCESettings) settings),
|
||||
|
@ -221,19 +218,6 @@ namespace BizHawk.Client.Common
|
|||
public void SetRenderPlanes(params bool[] args)
|
||||
{
|
||||
static bool GetSetting(bool[] settings, int index) => index >= settings.Length || settings[index];
|
||||
void SetLibsnes(LibsnesCore core)
|
||||
{
|
||||
var s = core.GetSettings();
|
||||
s.ShowBG1_0 = s.ShowBG1_1 = GetSetting(args, 0);
|
||||
s.ShowBG2_0 = s.ShowBG2_1 = GetSetting(args, 1);
|
||||
s.ShowBG3_0 = s.ShowBG3_1 = GetSetting(args, 2);
|
||||
s.ShowBG4_0 = s.ShowBG4_1 = GetSetting(args, 3);
|
||||
s.ShowOBJ_0 = GetSetting(args, 4);
|
||||
s.ShowOBJ_1 = GetSetting(args, 5);
|
||||
s.ShowOBJ_2 = GetSetting(args, 6);
|
||||
s.ShowOBJ_3 = GetSetting(args, 7);
|
||||
core.PutSettings(s);
|
||||
}
|
||||
void SetBsnes(ISettable<BsnesCore.SnesSettings, BsnesCore.SnesSyncSettings> settingsProvider)
|
||||
{
|
||||
var s = settingsProvider.GetSettings();
|
||||
|
@ -306,9 +290,6 @@ namespace BizHawk.Client.Common
|
|||
case GPGX gpgx:
|
||||
SetGPGX(gpgx);
|
||||
break;
|
||||
case LibsnesCore snes:
|
||||
SetLibsnes(snes);
|
||||
break;
|
||||
case BsnesCore bsnes:
|
||||
SetBsnes(bsnes);
|
||||
break;
|
||||
|
|
|
@ -10,7 +10,6 @@ using BizHawk.Emulation.Common;
|
|||
using BizHawk.Emulation.Cores;
|
||||
using BizHawk.Emulation.Cores.Libretro;
|
||||
using BizHawk.Emulation.Cores.Nintendo.Sameboy;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SNES;
|
||||
using BizHawk.Emulation.Cores.Sony.PSX;
|
||||
using BizHawk.Emulation.Cores.Arcades.MAME;
|
||||
using BizHawk.Emulation.DiscSystem;
|
||||
|
@ -696,28 +695,8 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
// need to get rid of this hack at some point
|
||||
rom = new RomGame(file);
|
||||
game = rom.GameInfo;
|
||||
game.System = VSystemID.Raw.SNES;
|
||||
nextEmulator = new LibsnesCore(
|
||||
game,
|
||||
null,
|
||||
rom.FileData,
|
||||
Path.GetDirectoryName(path.SubstringBefore('|')),
|
||||
nextComm,
|
||||
GetCoreSettings<LibsnesCore, LibsnesCore.SnesSettings>(),
|
||||
GetCoreSyncSettings<LibsnesCore, LibsnesCore.SnesSyncSettings>()
|
||||
);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
DoLoadErrorCallback(ex.ToString(), VSystemID.Raw.GBL, LoadErrorType.Xml);
|
||||
return false;
|
||||
}
|
||||
DoLoadErrorCallback(ex.ToString(), VSystemID.Raw.GBL, LoadErrorType.Xml);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace BizHawk.Client.Common
|
|||
([ VSystemID.Raw.GBL ],
|
||||
[ CoreNames.GambatteLink, CoreNames.GBHawkLink, CoreNames.GBHawkLink3x, CoreNames.GBHawkLink4x ]),
|
||||
([ VSystemID.Raw.SGB ],
|
||||
[ CoreNames.Gambatte, CoreNames.Bsnes115, CoreNames.SubBsnes115, CoreNames.Bsnes ]),
|
||||
[ CoreNames.Gambatte, CoreNames.Bsnes115, CoreNames.SubBsnes115 ]),
|
||||
([ VSystemID.Raw.GEN ],
|
||||
[ CoreNames.Gpgx, CoreNames.PicoDrive ]),
|
||||
([ VSystemID.Raw.N64 ],
|
||||
|
@ -49,7 +49,7 @@ namespace BizHawk.Client.Common
|
|||
([ VSystemID.Raw.SMS, VSystemID.Raw.GG, VSystemID.Raw.SG ],
|
||||
[ CoreNames.Gpgx, CoreNames.SMSHawk ]),
|
||||
([ VSystemID.Raw.SNES ],
|
||||
[ CoreNames.Snes9X, CoreNames.Bsnes115, CoreNames.SubBsnes115, CoreNames.Faust, CoreNames.Bsnes ]),
|
||||
[ CoreNames.Snes9X, CoreNames.Bsnes115, CoreNames.SubBsnes115, CoreNames.Faust ]),
|
||||
([ VSystemID.Raw.TI83 ],
|
||||
[ CoreNames.Emu83, CoreNames.TI83Hawk ]),
|
||||
};
|
||||
|
|
|
@ -50,7 +50,6 @@ using BizHawk.Emulation.Cores.Nintendo.Gameboy;
|
|||
using BizHawk.Emulation.Cores.Nintendo.N64;
|
||||
using BizHawk.Emulation.Cores.Nintendo.NES;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SNES9X;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SNES;
|
||||
using BizHawk.Emulation.Cores.Nintendo.Sameboy;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SubGBHawk;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SubNESHawk;
|
||||
|
@ -734,12 +733,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
|
||||
|
||||
private DialogResult OpenOldBSNESGamepadSettingsDialog(ISettingsAdapter settable)
|
||||
{
|
||||
using SNESControllerSettings form = new(settable);
|
||||
return this.ShowDialogWithTempMute(form);
|
||||
}
|
||||
|
||||
private DialogResult OpenBSNESGamepadSettingsDialog(ISettingsAdapter settable)
|
||||
{
|
||||
using BSNESControllerSettings form = new(settable);
|
||||
|
@ -749,7 +742,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
private void SNESControllerConfigurationMenuItem_Click(object sender, EventArgs e)
|
||||
=> _ = Emulator switch
|
||||
{
|
||||
LibsnesCore => OpenOldBSNESGamepadSettingsDialog(GetSettingsAdapterForLoadedCore<LibsnesCore>()),
|
||||
BsnesCore => OpenBSNESGamepadSettingsDialog(GetSettingsAdapterForLoadedCore<BsnesCore>()),
|
||||
SubBsnesCore => OpenBSNESGamepadSettingsDialog(GetSettingsAdapterForLoadedCore<SubBsnesCore>()),
|
||||
_ => DialogResult.None
|
||||
|
@ -758,16 +750,12 @@ namespace BizHawk.Client.EmuHawk
|
|||
private void SnesGfxDebuggerMenuItem_Click(object sender, EventArgs e)
|
||||
=> Tools.Load<SNESGraphicsDebugger>();
|
||||
|
||||
private DialogResult OpenOldBSNESSettingsDialog(ISettingsAdapter settable)
|
||||
=> SNESOptions.DoSettingsDialog(this, settable);
|
||||
|
||||
private DialogResult OpenBSNESSettingsDialog(ISettingsAdapter settable)
|
||||
=> BSNESOptions.DoSettingsDialog(this, settable);
|
||||
|
||||
private void SnesOptionsMenuItem_Click(object sender, EventArgs e)
|
||||
=> _ = Emulator switch
|
||||
{
|
||||
LibsnesCore => OpenOldBSNESSettingsDialog(GetSettingsAdapterForLoadedCore<LibsnesCore>()),
|
||||
BsnesCore => OpenBSNESSettingsDialog(GetSettingsAdapterForLoadedCore<BsnesCore>()),
|
||||
SubBsnesCore => OpenBSNESSettingsDialog(GetSettingsAdapterForLoadedCore<SubBsnesCore>()),
|
||||
_ => DialogResult.None
|
||||
|
@ -805,27 +793,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
settingsProvider.PutSettings(s);
|
||||
break;
|
||||
}
|
||||
case LibsnesCore libsnes:
|
||||
{
|
||||
var s = libsnes.GetSettings();
|
||||
switch (layer)
|
||||
{
|
||||
case 1:
|
||||
result = s.ShowBG1_0 = s.ShowBG1_1 = !s.ShowBG1_1;
|
||||
break;
|
||||
case 2:
|
||||
result = s.ShowBG2_0 = s.ShowBG2_1 = !s.ShowBG2_1;
|
||||
break;
|
||||
case 3:
|
||||
result = s.ShowBG3_0 = s.ShowBG3_1 = !s.ShowBG3_1;
|
||||
break;
|
||||
case 4:
|
||||
result = s.ShowBG4_0 = s.ShowBG4_1 = !s.ShowBG4_1;
|
||||
break;
|
||||
}
|
||||
libsnes.PutSettings(s);
|
||||
break;
|
||||
}
|
||||
case Snes9x snes9X:
|
||||
{
|
||||
var s = snes9X.GetSettings();
|
||||
|
@ -1130,13 +1097,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
// Atari2600Hawk
|
||||
items.Add(CreateCoreSubmenu(VSystemCategory.Consoles, CoreNames.Atari2600Hawk, CreateGenericCoreConfigItem<Atari2600>(CoreNames.Atari2600Hawk)));
|
||||
|
||||
// BSNES
|
||||
var oldBSNESGamepadSettingsItem = CreateSettingsItem("Controller Configuration...", (_, _) => OpenOldBSNESGamepadSettingsDialog(GetSettingsAdapterFor<LibsnesCore>()));
|
||||
var oldBSNESSettingsItem = CreateSettingsItem("Options...", (_, _) => OpenOldBSNESSettingsDialog(GetSettingsAdapterFor<LibsnesCore>()));
|
||||
var oldBSNESSubmenu = CreateCoreSubmenu(VSystemCategory.Consoles, CoreNames.Bsnes, oldBSNESGamepadSettingsItem, oldBSNESSettingsItem);
|
||||
oldBSNESSubmenu.DropDownOpened += (_, _) => oldBSNESGamepadSettingsItem.Enabled = MovieSession.Movie.NotActive() || Emulator is not LibsnesCore;
|
||||
items.Add(oldBSNESSubmenu);
|
||||
|
||||
// BSNESv115+
|
||||
var bsnesGamepadSettingsItem = CreateSettingsItem("Controller Configuration...", (_, _) => OpenBSNESGamepadSettingsDialog(GetSettingsAdapterFor<BsnesCore>()));
|
||||
var bsnesSettingsItem = CreateSettingsItem("Options...", (_, _) => OpenBSNESSettingsDialog(GetSettingsAdapterFor<BsnesCore>()));
|
||||
|
@ -1500,10 +1460,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
GBSubMenu.Visible = true;
|
||||
SameBoyColorChooserMenuItem.Visible = Emulator is Sameboy { IsCGBMode: false }; // palette config only works in DMG mode
|
||||
break;
|
||||
case VSystemID.Raw.SNES when Emulator is LibsnesCore oldBSNES: // doesn't use "SGB" sysID, always "SNES"
|
||||
SNESSubMenu.Text = oldBSNES.IsSGB ? "&SGB" : "&SNES";
|
||||
SNESSubMenu.Visible = true;
|
||||
break;
|
||||
case var _ when Emulator is BsnesCore or SubBsnesCore:
|
||||
SNESSubMenu.Text = $"&{sysID}";
|
||||
SNESSubMenu.Visible = true;
|
||||
|
|
|
@ -33,7 +33,6 @@ using BizHawk.Emulation.Cores.Consoles.SNK;
|
|||
using BizHawk.Emulation.Cores.Nintendo.GBA;
|
||||
using BizHawk.Emulation.Cores.Nintendo.N64;
|
||||
using BizHawk.Emulation.Cores.Nintendo.NES;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SNES;
|
||||
|
||||
using BizHawk.Emulation.DiscSystem;
|
||||
|
||||
|
@ -3532,12 +3531,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
var name = Game.FilesystemSafeName();
|
||||
name += $".{Emulator.Attributes().CoreName}";
|
||||
|
||||
// Bsnes profiles have incompatible savestates so save the profile name
|
||||
if (Emulator is LibsnesCore bsnes)
|
||||
{
|
||||
name += $".{bsnes.CurrentProfile}";
|
||||
}
|
||||
|
||||
if (MovieSession.Movie.IsActive())
|
||||
{
|
||||
name += $".{Path.GetFileNameWithoutExtension(MovieSession.Movie.Filename)}";
|
||||
|
@ -3731,7 +3724,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
InputManager.SyncControls(Emulator, MovieSession, Config);
|
||||
_multiDiskMode = false;
|
||||
|
||||
if (oaOpenrom != null && Path.GetExtension(oaOpenrom.Path.Replace("|", "")).ToLowerInvariant() == ".xml" && Emulator is not LibsnesCore)
|
||||
if (oaOpenrom != null && Path.GetExtension(oaOpenrom.Path.Replace("|", "")).ToLowerInvariant() == ".xml")
|
||||
{
|
||||
// this is a multi-disk bundler file
|
||||
// determine the xml assets and create RomStatusDetails for all of them
|
||||
|
|
|
@ -4,7 +4,6 @@ using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy;
|
|||
using BizHawk.Emulation.Cores.Consoles.Nintendo.NDS;
|
||||
using BizHawk.Emulation.Cores.Consoles.Sega.gpgx;
|
||||
using BizHawk.Emulation.Cores.Consoles.Sega.PicoDrive;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SNES;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
|
@ -155,7 +154,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
VSystemID.Raw.SGX => ConsoleID.PCEngine, // ???
|
||||
VSystemID.Raw.SGXCD => ConsoleID.PCEngineCD, // ???
|
||||
VSystemID.Raw.SMS => ConsoleID.MasterSystem,
|
||||
VSystemID.Raw.SNES when Emu is LibsnesCore { IsSGB: true } => ConsoleID.GB,
|
||||
VSystemID.Raw.SNES => ConsoleID.SNES,
|
||||
VSystemID.Raw.TI83 => ConsoleID.TI83,
|
||||
VSystemID.Raw.TIC80 => ConsoleID.Tic80,
|
||||
|
|
|
@ -13,7 +13,6 @@ using BizHawk.Emulation.Cores.Nintendo.Gameboy;
|
|||
using BizHawk.Emulation.Cores.Nintendo.GBA;
|
||||
using BizHawk.Emulation.Cores.Nintendo.NES;
|
||||
using BizHawk.Emulation.Cores.Nintendo.Sameboy;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SNES;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SNES9X;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SubGBHawk;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SubNESHawk;
|
||||
|
@ -51,7 +50,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
[typeof(MGBAHawk)] = new[] { "DisplayBG0", "DisplayBG1", "DisplayBG2", "DisplayBG3", "DisplayOBJ" },
|
||||
[typeof(NES)] = new[] { "DispBackground", "DispSprites" },
|
||||
[typeof(Sameboy)] = new[] { "EnableBGWIN", "EnableOBJ" },
|
||||
[typeof(LibsnesCore)] = new[] { "ShowBG1_0", "ShowBG2_0", "ShowBG3_0", "ShowBG4_0", "ShowBG1_1", "ShowBG2_1", "ShowBG3_1", "ShowBG4_1", "ShowOBJ_0", "ShowOBJ_1", "ShowOBJ_2", "ShowOBJ_3" },
|
||||
[typeof(Snes9x)] = new[] { "ShowBg0", "ShowBg1", "ShowBg2", "ShowBg3", "ShowSprites0", "ShowSprites1", "ShowSprites2", "ShowSprites3", "ShowWindow", "ShowTransparency" },
|
||||
[typeof(PCEngine)] = new[] { "ShowBG1", "ShowOBJ1", "ShowBG2", "ShowOBJ2", },
|
||||
[typeof(GPGX)] = new[] { "DrawBGA", "DrawBGB", "DrawBGW", "DrawObj", },
|
||||
|
|
|
@ -1,196 +0,0 @@
|
|||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
partial class SNESControllerSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.OkBtn = new System.Windows.Forms.Button();
|
||||
this.CancelBtn = new System.Windows.Forms.Button();
|
||||
this.label1 = new BizHawk.WinForms.Controls.LocLabelEx();
|
||||
this.label5 = new BizHawk.WinForms.Controls.LocLabelEx();
|
||||
this.label4 = new BizHawk.WinForms.Controls.LocLabelEx();
|
||||
this.Port2ComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.Port1ComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.MouseSpeedLabel1 = new BizHawk.WinForms.Controls.LocLabelEx();
|
||||
this.LimitAnalogChangeCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.MouseSpeedLabel2 = new BizHawk.WinForms.Controls.LocLabelEx();
|
||||
this.MouseSpeedLabel3 = new BizHawk.WinForms.Controls.LocLabelEx();
|
||||
this.MouseNagLabel1 = new BizHawk.WinForms.Controls.LocLabelEx();
|
||||
this.MouseNagLabel2 = new BizHawk.WinForms.Controls.LocLabelEx();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// OkBtn
|
||||
//
|
||||
this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.OkBtn.Location = new System.Drawing.Point(170, 264);
|
||||
this.OkBtn.Name = "OkBtn";
|
||||
this.OkBtn.Size = new System.Drawing.Size(60, 23);
|
||||
this.OkBtn.TabIndex = 4;
|
||||
this.OkBtn.Text = "&OK";
|
||||
this.OkBtn.UseVisualStyleBackColor = true;
|
||||
this.OkBtn.Click += new System.EventHandler(this.OkBtn_Click);
|
||||
//
|
||||
// CancelBtn
|
||||
//
|
||||
this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.CancelBtn.Location = new System.Drawing.Point(236, 264);
|
||||
this.CancelBtn.Name = "CancelBtn";
|
||||
this.CancelBtn.Size = new System.Drawing.Size(60, 23);
|
||||
this.CancelBtn.TabIndex = 5;
|
||||
this.CancelBtn.Text = "&Cancel";
|
||||
this.CancelBtn.UseVisualStyleBackColor = true;
|
||||
this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click);
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.Location = new System.Drawing.Point(12, 9);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Text = "SNES Controller Settings";
|
||||
//
|
||||
// label5
|
||||
//
|
||||
this.label5.Location = new System.Drawing.Point(9, 88);
|
||||
this.label5.Name = "label5";
|
||||
this.label5.Text = "Port 2:";
|
||||
//
|
||||
// label4
|
||||
//
|
||||
this.label4.Location = new System.Drawing.Point(12, 38);
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Text = "Port 1:";
|
||||
//
|
||||
// Port2ComboBox
|
||||
//
|
||||
this.Port2ComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.Port2ComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.Port2ComboBox.FormattingEnabled = true;
|
||||
this.Port2ComboBox.Location = new System.Drawing.Point(12, 104);
|
||||
this.Port2ComboBox.Name = "Port2ComboBox";
|
||||
this.Port2ComboBox.Size = new System.Drawing.Size(284, 21);
|
||||
this.Port2ComboBox.TabIndex = 20;
|
||||
this.Port2ComboBox.SelectedIndexChanged += new System.EventHandler(this.PortComboBox_SelectedIndexChanged);
|
||||
//
|
||||
// Port1ComboBox
|
||||
//
|
||||
this.Port1ComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.Port1ComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.Port1ComboBox.FormattingEnabled = true;
|
||||
this.Port1ComboBox.Location = new System.Drawing.Point(12, 54);
|
||||
this.Port1ComboBox.Name = "Port1ComboBox";
|
||||
this.Port1ComboBox.Size = new System.Drawing.Size(284, 21);
|
||||
this.Port1ComboBox.TabIndex = 19;
|
||||
this.Port1ComboBox.SelectedIndexChanged += new System.EventHandler(this.PortComboBox_SelectedIndexChanged);
|
||||
//
|
||||
// MouseSpeedLabel1
|
||||
//
|
||||
this.MouseSpeedLabel1.Location = new System.Drawing.Point(12, 195);
|
||||
this.MouseSpeedLabel1.Name = "MouseSpeedLabel1";
|
||||
this.MouseSpeedLabel1.Text = "For casual play this should be checked";
|
||||
//
|
||||
// LimitAnalogChangeCheckBox
|
||||
//
|
||||
this.LimitAnalogChangeCheckBox.AutoSize = true;
|
||||
this.LimitAnalogChangeCheckBox.Location = new System.Drawing.Point(15, 175);
|
||||
this.LimitAnalogChangeCheckBox.Name = "LimitAnalogChangeCheckBox";
|
||||
this.LimitAnalogChangeCheckBox.Size = new System.Drawing.Size(173, 17);
|
||||
this.LimitAnalogChangeCheckBox.TabIndex = 24;
|
||||
this.LimitAnalogChangeCheckBox.Text = "Limit Analog Change Sensitivity";
|
||||
this.LimitAnalogChangeCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// MouseSpeedLabel2
|
||||
//
|
||||
this.MouseSpeedLabel2.Location = new System.Drawing.Point(12, 208);
|
||||
this.MouseSpeedLabel2.Name = "MouseSpeedLabel2";
|
||||
this.MouseSpeedLabel2.Text = "The full range of values are rather unusuable in";
|
||||
//
|
||||
// MouseSpeedLabel3
|
||||
//
|
||||
this.MouseSpeedLabel3.Location = new System.Drawing.Point(12, 221);
|
||||
this.MouseSpeedLabel3.Name = "MouseSpeedLabel3";
|
||||
this.MouseSpeedLabel3.Text = "normal situations, but good if you need total control";
|
||||
//
|
||||
// MouseNagLabel1
|
||||
//
|
||||
this.MouseNagLabel1.Location = new System.Drawing.Point(12, 135);
|
||||
this.MouseNagLabel1.Name = "MouseNagLabel1";
|
||||
this.MouseNagLabel1.Text = "*Note: mouse and scope controls should be bound to an";
|
||||
//
|
||||
// MouseNagLabel2
|
||||
//
|
||||
this.MouseNagLabel2.Location = new System.Drawing.Point(45, 148);
|
||||
this.MouseNagLabel2.Name = "MouseNagLabel2";
|
||||
this.MouseNagLabel2.Text = "analog stick not the mouse";
|
||||
//
|
||||
// SNESControllerSettings
|
||||
//
|
||||
this.AcceptButton = this.OkBtn;
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.CancelButton = this.CancelBtn;
|
||||
this.ClientSize = new System.Drawing.Size(308, 299);
|
||||
this.Controls.Add(this.MouseNagLabel2);
|
||||
this.Controls.Add(this.MouseNagLabel1);
|
||||
this.Controls.Add(this.MouseSpeedLabel3);
|
||||
this.Controls.Add(this.MouseSpeedLabel2);
|
||||
this.Controls.Add(this.LimitAnalogChangeCheckBox);
|
||||
this.Controls.Add(this.MouseSpeedLabel1);
|
||||
this.Controls.Add(this.label5);
|
||||
this.Controls.Add(this.label4);
|
||||
this.Controls.Add(this.Port2ComboBox);
|
||||
this.Controls.Add(this.Port1ComboBox);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.CancelBtn);
|
||||
this.Controls.Add(this.OkBtn);
|
||||
this.Name = "SNESControllerSettings";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "Controller Settings";
|
||||
this.Load += new System.EventHandler(this.SNESControllerSettings_Load);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Button OkBtn;
|
||||
private System.Windows.Forms.Button CancelBtn;
|
||||
private BizHawk.WinForms.Controls.LocLabelEx label1;
|
||||
private BizHawk.WinForms.Controls.LocLabelEx label5;
|
||||
private BizHawk.WinForms.Controls.LocLabelEx label4;
|
||||
private System.Windows.Forms.ComboBox Port2ComboBox;
|
||||
private System.Windows.Forms.ComboBox Port1ComboBox;
|
||||
private BizHawk.WinForms.Controls.LocLabelEx MouseSpeedLabel1;
|
||||
private System.Windows.Forms.CheckBox LimitAnalogChangeCheckBox;
|
||||
private BizHawk.WinForms.Controls.LocLabelEx MouseSpeedLabel2;
|
||||
private BizHawk.WinForms.Controls.LocLabelEx MouseSpeedLabel3;
|
||||
private BizHawk.WinForms.Controls.LocLabelEx MouseNagLabel1;
|
||||
private BizHawk.WinForms.Controls.LocLabelEx MouseNagLabel2;
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
using System.Windows.Forms;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SNES;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
public partial class SNESControllerSettings : Form
|
||||
{
|
||||
private readonly ISettingsAdapter _settable;
|
||||
|
||||
private readonly LibsnesCore.SnesSyncSettings _syncSettings;
|
||||
private bool _suppressDropdownChangeEvents;
|
||||
|
||||
public SNESControllerSettings(ISettingsAdapter settable)
|
||||
{
|
||||
_settable = settable;
|
||||
_syncSettings = (LibsnesCore.SnesSyncSettings) _settable.GetSyncSettings();
|
||||
InitializeComponent();
|
||||
Icon = Properties.Resources.GameControllerIcon;
|
||||
}
|
||||
|
||||
private void SNESControllerSettings_Load(object sender, EventArgs e)
|
||||
{
|
||||
LimitAnalogChangeCheckBox.Checked = _syncSettings.LimitAnalogChangeSensitivity;
|
||||
|
||||
_suppressDropdownChangeEvents = true;
|
||||
Port1ComboBox.PopulateFromEnum(_syncSettings.LeftPort);
|
||||
Port2ComboBox.PopulateFromEnum(_syncSettings.RightPort);
|
||||
_suppressDropdownChangeEvents = false;
|
||||
}
|
||||
|
||||
private void OkBtn_Click(object sender, EventArgs e)
|
||||
{
|
||||
bool changed =
|
||||
_syncSettings.LeftPort.ToString() != Port1ComboBox.SelectedItem.ToString()
|
||||
|| _syncSettings.RightPort.ToString() != Port2ComboBox.SelectedItem.ToString()
|
||||
|| _syncSettings.LimitAnalogChangeSensitivity != LimitAnalogChangeCheckBox.Checked;
|
||||
|
||||
if (changed)
|
||||
{
|
||||
_syncSettings.LeftPort = (LibsnesControllerDeck.ControllerType)Enum.Parse(typeof(LibsnesControllerDeck.ControllerType), Port1ComboBox.SelectedItem.ToString());
|
||||
_syncSettings.RightPort = (LibsnesControllerDeck.ControllerType)Enum.Parse(typeof(LibsnesControllerDeck.ControllerType), Port2ComboBox.SelectedItem.ToString());
|
||||
_syncSettings.LimitAnalogChangeSensitivity = LimitAnalogChangeCheckBox.Checked;
|
||||
|
||||
_settable.PutCoreSyncSettings(_syncSettings);
|
||||
}
|
||||
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void CancelBtn_Click(object sender, EventArgs e)
|
||||
{
|
||||
DialogResult = DialogResult.Cancel;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void PortComboBox_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (!_suppressDropdownChangeEvents)
|
||||
{
|
||||
var leftPort = (LibsnesControllerDeck.ControllerType)Enum.Parse(typeof(LibsnesControllerDeck.ControllerType), Port1ComboBox.SelectedItem.ToString());
|
||||
var rightPort = (LibsnesControllerDeck.ControllerType)Enum.Parse(typeof(LibsnesControllerDeck.ControllerType), Port2ComboBox.SelectedItem.ToString());
|
||||
ToggleMouseSection(leftPort == LibsnesControllerDeck.ControllerType.Mouse
|
||||
|| rightPort == LibsnesControllerDeck.ControllerType.Mouse);
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleMouseSection(bool show)
|
||||
{
|
||||
LimitAnalogChangeCheckBox.Visible =
|
||||
MouseSpeedLabel1.Visible =
|
||||
MouseSpeedLabel2.Visible =
|
||||
MouseSpeedLabel3.Visible =
|
||||
MouseNagLabel1.Visible =
|
||||
MouseNagLabel2.Visible =
|
||||
show;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
|
@ -1,270 +0,0 @@
|
|||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
partial class SNESOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.btnOk = new System.Windows.Forms.Button();
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.cbDoubleSize = new System.Windows.Forms.CheckBox();
|
||||
this.lblDoubleSize = new BizHawk.WinForms.Controls.LocSzLabelEx();
|
||||
this.radioButton1 = new System.Windows.Forms.RadioButton();
|
||||
this.cbCropSGBFrame = new System.Windows.Forms.CheckBox();
|
||||
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
||||
this.Bg4Checkbox = new System.Windows.Forms.CheckBox();
|
||||
this.Bg3Checkbox = new System.Windows.Forms.CheckBox();
|
||||
this.Bg2Checkbox = new System.Windows.Forms.CheckBox();
|
||||
this.Bg1Checkbox = new System.Windows.Forms.CheckBox();
|
||||
this.Obj4Checkbox = new System.Windows.Forms.CheckBox();
|
||||
this.Obj3Checkbox = new System.Windows.Forms.CheckBox();
|
||||
this.Obj2Checkbox = new System.Windows.Forms.CheckBox();
|
||||
this.Obj1Checkbox = new System.Windows.Forms.CheckBox();
|
||||
this.cbRandomizedInitialState = new System.Windows.Forms.CheckBox();
|
||||
this.groupBox1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// btnOk
|
||||
//
|
||||
this.btnOk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnOk.Location = new System.Drawing.Point(136, 303);
|
||||
this.btnOk.Name = "btnOk";
|
||||
this.btnOk.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnOk.TabIndex = 0;
|
||||
this.btnOk.Text = "OK";
|
||||
this.btnOk.UseVisualStyleBackColor = true;
|
||||
this.btnOk.Click += new System.EventHandler(this.BtnOk_Click);
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.btnCancel.Location = new System.Drawing.Point(217, 303);
|
||||
this.btnCancel.Name = "btnCancel";
|
||||
this.btnCancel.Size = new System.Drawing.Size(75, 23);
|
||||
this.btnCancel.TabIndex = 1;
|
||||
this.btnCancel.Text = "Cancel";
|
||||
this.btnCancel.UseVisualStyleBackColor = true;
|
||||
this.btnCancel.Click += new System.EventHandler(this.BtnCancel_Click);
|
||||
//
|
||||
// cbDoubleSize
|
||||
//
|
||||
this.cbDoubleSize.AutoSize = true;
|
||||
this.cbDoubleSize.Location = new System.Drawing.Point(18, 20);
|
||||
this.cbDoubleSize.Name = "cbDoubleSize";
|
||||
this.cbDoubleSize.Size = new System.Drawing.Size(178, 17);
|
||||
this.cbDoubleSize.TabIndex = 6;
|
||||
this.cbDoubleSize.Text = "Always Double-Size Framebuffer";
|
||||
this.cbDoubleSize.UseVisualStyleBackColor = true;
|
||||
this.cbDoubleSize.CheckedChanged += new System.EventHandler(this.CbDoubleSize_CheckedChanged);
|
||||
//
|
||||
// lblDoubleSize
|
||||
//
|
||||
this.lblDoubleSize.Location = new System.Drawing.Point(36, 41);
|
||||
this.lblDoubleSize.Name = "lblDoubleSize";
|
||||
this.lblDoubleSize.Size = new System.Drawing.Size(254, 57);
|
||||
this.lblDoubleSize.Text = "Some games are changing the resolution constantly (e.g. SD3) so this option can f" +
|
||||
"orce the SNES output to stay double-size always. NOTE: The Accuracy core runs as" +
|
||||
" if this is selected.\r\n";
|
||||
//
|
||||
// radioButton1
|
||||
//
|
||||
this.radioButton1.AutoSize = true;
|
||||
this.radioButton1.Location = new System.Drawing.Point(37, 46);
|
||||
this.radioButton1.Name = "radioButton1";
|
||||
this.radioButton1.Size = new System.Drawing.Size(202, 17);
|
||||
this.radioButton1.TabIndex = 9;
|
||||
this.radioButton1.TabStop = true;
|
||||
this.radioButton1.Text = "Performance (only for casual gaming!)";
|
||||
this.radioButton1.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// cbCropSGBFrame
|
||||
//
|
||||
this.cbCropSGBFrame.AutoSize = true;
|
||||
this.cbCropSGBFrame.Location = new System.Drawing.Point(15, 110);
|
||||
this.cbCropSGBFrame.Name = "cbCropSGBFrame";
|
||||
this.cbCropSGBFrame.Size = new System.Drawing.Size(105, 17);
|
||||
this.cbCropSGBFrame.TabIndex = 10;
|
||||
this.cbCropSGBFrame.Text = "Crop SGB Frame";
|
||||
this.cbCropSGBFrame.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// groupBox1
|
||||
//
|
||||
this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.groupBox1.Controls.Add(this.Bg4Checkbox);
|
||||
this.groupBox1.Controls.Add(this.Bg3Checkbox);
|
||||
this.groupBox1.Controls.Add(this.Bg2Checkbox);
|
||||
this.groupBox1.Controls.Add(this.Bg1Checkbox);
|
||||
this.groupBox1.Controls.Add(this.Obj4Checkbox);
|
||||
this.groupBox1.Controls.Add(this.Obj3Checkbox);
|
||||
this.groupBox1.Controls.Add(this.Obj2Checkbox);
|
||||
this.groupBox1.Controls.Add(this.Obj1Checkbox);
|
||||
this.groupBox1.Location = new System.Drawing.Point(18, 165);
|
||||
this.groupBox1.Name = "groupBox1";
|
||||
this.groupBox1.Size = new System.Drawing.Size(274, 132);
|
||||
this.groupBox1.TabIndex = 11;
|
||||
this.groupBox1.TabStop = false;
|
||||
this.groupBox1.Text = "Display";
|
||||
//
|
||||
// Bg4Checkbox
|
||||
//
|
||||
this.Bg4Checkbox.AutoSize = true;
|
||||
this.Bg4Checkbox.Location = new System.Drawing.Point(150, 98);
|
||||
this.Bg4Checkbox.Name = "Bg4Checkbox";
|
||||
this.Bg4Checkbox.Size = new System.Drawing.Size(50, 17);
|
||||
this.Bg4Checkbox.TabIndex = 7;
|
||||
this.Bg4Checkbox.Text = "BG 4";
|
||||
this.Bg4Checkbox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// Bg3Checkbox
|
||||
//
|
||||
this.Bg3Checkbox.AutoSize = true;
|
||||
this.Bg3Checkbox.Location = new System.Drawing.Point(150, 75);
|
||||
this.Bg3Checkbox.Name = "Bg3Checkbox";
|
||||
this.Bg3Checkbox.Size = new System.Drawing.Size(50, 17);
|
||||
this.Bg3Checkbox.TabIndex = 6;
|
||||
this.Bg3Checkbox.Text = "BG 3";
|
||||
this.Bg3Checkbox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// Bg2Checkbox
|
||||
//
|
||||
this.Bg2Checkbox.AutoSize = true;
|
||||
this.Bg2Checkbox.Location = new System.Drawing.Point(150, 52);
|
||||
this.Bg2Checkbox.Name = "Bg2Checkbox";
|
||||
this.Bg2Checkbox.Size = new System.Drawing.Size(50, 17);
|
||||
this.Bg2Checkbox.TabIndex = 5;
|
||||
this.Bg2Checkbox.Text = "BG 2";
|
||||
this.Bg2Checkbox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// Bg1Checkbox
|
||||
//
|
||||
this.Bg1Checkbox.AutoSize = true;
|
||||
this.Bg1Checkbox.Location = new System.Drawing.Point(150, 29);
|
||||
this.Bg1Checkbox.Name = "Bg1Checkbox";
|
||||
this.Bg1Checkbox.Size = new System.Drawing.Size(50, 17);
|
||||
this.Bg1Checkbox.TabIndex = 4;
|
||||
this.Bg1Checkbox.Text = "BG 1";
|
||||
this.Bg1Checkbox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// Obj4Checkbox
|
||||
//
|
||||
this.Obj4Checkbox.AutoSize = true;
|
||||
this.Obj4Checkbox.Location = new System.Drawing.Point(21, 98);
|
||||
this.Obj4Checkbox.Name = "Obj4Checkbox";
|
||||
this.Obj4Checkbox.Size = new System.Drawing.Size(55, 17);
|
||||
this.Obj4Checkbox.TabIndex = 3;
|
||||
this.Obj4Checkbox.Text = "OBJ 4";
|
||||
this.Obj4Checkbox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// Obj3Checkbox
|
||||
//
|
||||
this.Obj3Checkbox.AutoSize = true;
|
||||
this.Obj3Checkbox.Location = new System.Drawing.Point(21, 75);
|
||||
this.Obj3Checkbox.Name = "Obj3Checkbox";
|
||||
this.Obj3Checkbox.Size = new System.Drawing.Size(55, 17);
|
||||
this.Obj3Checkbox.TabIndex = 2;
|
||||
this.Obj3Checkbox.Text = "OBJ 3";
|
||||
this.Obj3Checkbox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// Obj2Checkbox
|
||||
//
|
||||
this.Obj2Checkbox.AutoSize = true;
|
||||
this.Obj2Checkbox.Location = new System.Drawing.Point(22, 52);
|
||||
this.Obj2Checkbox.Name = "Obj2Checkbox";
|
||||
this.Obj2Checkbox.Size = new System.Drawing.Size(55, 17);
|
||||
this.Obj2Checkbox.TabIndex = 1;
|
||||
this.Obj2Checkbox.Text = "OBJ 2";
|
||||
this.Obj2Checkbox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// Obj1Checkbox
|
||||
//
|
||||
this.Obj1Checkbox.AutoSize = true;
|
||||
this.Obj1Checkbox.Location = new System.Drawing.Point(21, 29);
|
||||
this.Obj1Checkbox.Name = "Obj1Checkbox";
|
||||
this.Obj1Checkbox.Size = new System.Drawing.Size(55, 17);
|
||||
this.Obj1Checkbox.TabIndex = 0;
|
||||
this.Obj1Checkbox.Text = "OBJ 1";
|
||||
this.Obj1Checkbox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// cbRandomizedInitialState
|
||||
//
|
||||
this.cbRandomizedInitialState.AutoSize = true;
|
||||
this.cbRandomizedInitialState.Location = new System.Drawing.Point(15, 133);
|
||||
this.cbRandomizedInitialState.Name = "cbRandomizedInitialState";
|
||||
this.cbRandomizedInitialState.Size = new System.Drawing.Size(140, 17);
|
||||
this.cbRandomizedInitialState.TabIndex = 12;
|
||||
this.cbRandomizedInitialState.Text = "Randomized Initial State";
|
||||
this.cbRandomizedInitialState.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// SNESOptions
|
||||
//
|
||||
this.AcceptButton = this.btnOk;
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.CancelButton = this.btnCancel;
|
||||
this.ClientSize = new System.Drawing.Size(304, 338);
|
||||
this.Controls.Add(this.cbRandomizedInitialState);
|
||||
this.Controls.Add(this.groupBox1);
|
||||
this.Controls.Add(this.cbCropSGBFrame);
|
||||
this.Controls.Add(this.lblDoubleSize);
|
||||
this.Controls.Add(this.cbDoubleSize);
|
||||
this.Controls.Add(this.btnCancel);
|
||||
this.Controls.Add(this.btnOk);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "SNESOptions";
|
||||
this.ShowIcon = false;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "BSNES Options";
|
||||
this.groupBox1.ResumeLayout(false);
|
||||
this.groupBox1.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Button btnOk;
|
||||
private System.Windows.Forms.Button btnCancel;
|
||||
private System.Windows.Forms.CheckBox cbDoubleSize;
|
||||
private BizHawk.WinForms.Controls.LocSzLabelEx lblDoubleSize;
|
||||
private System.Windows.Forms.RadioButton radioButton1;
|
||||
private System.Windows.Forms.CheckBox cbCropSGBFrame;
|
||||
private System.Windows.Forms.GroupBox groupBox1;
|
||||
private System.Windows.Forms.CheckBox Bg4Checkbox;
|
||||
private System.Windows.Forms.CheckBox Bg3Checkbox;
|
||||
private System.Windows.Forms.CheckBox Bg2Checkbox;
|
||||
private System.Windows.Forms.CheckBox Bg1Checkbox;
|
||||
private System.Windows.Forms.CheckBox Obj4Checkbox;
|
||||
private System.Windows.Forms.CheckBox Obj3Checkbox;
|
||||
private System.Windows.Forms.CheckBox Obj2Checkbox;
|
||||
private System.Windows.Forms.CheckBox Obj1Checkbox;
|
||||
private System.Windows.Forms.CheckBox cbRandomizedInitialState;
|
||||
}
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
using System.Windows.Forms;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SNES;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
public partial class SNESOptions : Form
|
||||
{
|
||||
private SNESOptions()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private bool _suppressDoubleSize;
|
||||
private bool _userDoubleSizeOption;
|
||||
|
||||
public static DialogResult DoSettingsDialog(IDialogParent dialogParent, ISettingsAdapter settable)
|
||||
{
|
||||
var s = (LibsnesCore.SnesSettings) settable.GetSettings();
|
||||
var ss = (LibsnesCore.SnesSyncSettings) settable.GetSyncSettings();
|
||||
using var dlg = new SNESOptions
|
||||
{
|
||||
RandomizedInitialState = ss.RandomizedInitialState,
|
||||
AlwaysDoubleSize = s.AlwaysDoubleSize,
|
||||
CropSGBFrame = s.CropSGBFrame,
|
||||
ShowObj1 = s.ShowOBJ_0,
|
||||
ShowObj2 = s.ShowOBJ_1,
|
||||
ShowObj3 = s.ShowOBJ_2,
|
||||
ShowObj4 = s.ShowOBJ_3,
|
||||
ShowBg1 = s.ShowBG1_0,
|
||||
ShowBg2 = s.ShowBG2_0,
|
||||
ShowBg3 = s.ShowBG3_0,
|
||||
ShowBg4 = s.ShowBG4_0
|
||||
};
|
||||
|
||||
var result = dialogParent.ShowDialogAsChild(dlg);
|
||||
if (!result.IsOk()) return result;
|
||||
|
||||
s.AlwaysDoubleSize = dlg.AlwaysDoubleSize;
|
||||
s.CropSGBFrame = dlg.CropSGBFrame;
|
||||
ss.RandomizedInitialState = dlg.RandomizedInitialState;
|
||||
s.ShowOBJ_0 = dlg.ShowObj1;
|
||||
s.ShowOBJ_1 = dlg.ShowObj2;
|
||||
s.ShowOBJ_2 = dlg.ShowObj3;
|
||||
s.ShowOBJ_3 = dlg.ShowObj4;
|
||||
s.ShowBG1_0 = s.ShowBG1_1 = dlg.ShowBg1;
|
||||
s.ShowBG2_0 = s.ShowBG2_1 = dlg.ShowBg2;
|
||||
s.ShowBG3_0 = s.ShowBG3_1 = dlg.ShowBg3;
|
||||
s.ShowBG4_0 = s.ShowBG4_1 = dlg.ShowBg4;
|
||||
settable.PutCoreSettings(s);
|
||||
settable.PutCoreSyncSettings(ss);
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool AlwaysDoubleSize
|
||||
{
|
||||
get => _userDoubleSizeOption;
|
||||
set
|
||||
{
|
||||
_userDoubleSizeOption = value;
|
||||
RefreshDoubleSizeOption();
|
||||
}
|
||||
}
|
||||
|
||||
private bool CropSGBFrame
|
||||
{
|
||||
get => cbCropSGBFrame.Checked;
|
||||
set => cbCropSGBFrame.Checked = value;
|
||||
}
|
||||
|
||||
private bool RandomizedInitialState
|
||||
{
|
||||
get => cbRandomizedInitialState.Checked;
|
||||
set => cbRandomizedInitialState.Checked = value;
|
||||
}
|
||||
|
||||
private bool ShowObj1 { get => Obj1Checkbox.Checked; set => Obj1Checkbox.Checked = value;
|
||||
}
|
||||
private bool ShowObj2 { get => Obj2Checkbox.Checked; set => Obj2Checkbox.Checked = value; }
|
||||
private bool ShowObj3 { get => Obj3Checkbox.Checked; set => Obj3Checkbox.Checked = value; }
|
||||
private bool ShowObj4 { get => Obj4Checkbox.Checked; set => Obj4Checkbox.Checked = value; }
|
||||
|
||||
private bool ShowBg1 { get => Bg1Checkbox.Checked; set => Bg1Checkbox.Checked = value; }
|
||||
private bool ShowBg2 { get => Bg2Checkbox.Checked; set => Bg2Checkbox.Checked = value; }
|
||||
private bool ShowBg3 { get => Bg3Checkbox.Checked; set => Bg3Checkbox.Checked = value; }
|
||||
private bool ShowBg4 { get => Bg4Checkbox.Checked; set => Bg4Checkbox.Checked = value; }
|
||||
|
||||
private void RefreshDoubleSizeOption()
|
||||
{
|
||||
_suppressDoubleSize = true;
|
||||
cbDoubleSize.Checked = !cbDoubleSize.Enabled || _userDoubleSizeOption;
|
||||
_suppressDoubleSize = false;
|
||||
}
|
||||
|
||||
private void CbDoubleSize_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (_suppressDoubleSize)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_userDoubleSizeOption = cbDoubleSize.Checked;
|
||||
}
|
||||
|
||||
private void BtnOk_Click(object sender, EventArgs e)
|
||||
{
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void BtnCancel_Click(object sender, EventArgs e)
|
||||
{
|
||||
DialogResult = DialogResult.Cancel;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
|
@ -1207,7 +1207,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
#if false
|
||||
//TODO
|
||||
LibsnesApi dll = TODO;
|
||||
dll.QUERY_set_backdropColor(DecodeWinformsColorToSNES(checkBackdropColor.Checked ? pnBackdropColor.BackColor : Color.FromArgb(-1)));
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -67,7 +67,6 @@
|
|||
<Compile Update="Consoles/Nintendo/SubNESHawk/SubNESHawk.*.cs" DependentUpon="SubNESHawk.cs" />
|
||||
<Compile Update="Consoles/Nintendo/QuickNES/QuickNES.*.cs" DependentUpon="QuickNES.cs" />
|
||||
<Compile Update="Consoles/Nintendo/Sameboy/SameBoy.*.cs" DependentUpon="SameBoy.cs" />
|
||||
<Compile Update="Consoles/Nintendo/SNES/LibsnesCore.*.cs" DependentUpon="LibsnesCore.cs" />
|
||||
<Compile Update="Consoles/PC Engine/PCEngine.*.cs" DependentUpon="PCEngine.cs" />
|
||||
<Compile Update="Consoles/Sega/GGHawkLink/GGHawkLink.*.cs" DependentUpon="GGHawkLink.cs" />
|
||||
<Compile Update="Consoles/Sega/gpgx64/GPGX.*.cs" DependentUpon="GPGX.cs" />
|
||||
|
|
|
@ -1,448 +0,0 @@
|
|||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Cores.Waterbox;
|
||||
using BizHawk.BizInvoke;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||
{
|
||||
public abstract unsafe class CoreImpl
|
||||
{
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract IntPtr DllInit();
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract void Message(LibsnesApi.eMessage msg);
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract void CopyBuffer(int id, void* ptr, int size);
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract void SetBuffer(int id, void* ptr, int size);
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void PostLoadState();
|
||||
}
|
||||
|
||||
public unsafe partial class LibsnesApi : IDisposable, IMonitor, IStatable
|
||||
{
|
||||
static LibsnesApi()
|
||||
{
|
||||
if (sizeof(CommStruct) != 368)
|
||||
{
|
||||
throw new InvalidOperationException("sizeof(comm)");
|
||||
}
|
||||
}
|
||||
|
||||
private WaterboxHost _exe;
|
||||
private CoreImpl _core;
|
||||
private bool _disposed;
|
||||
private CommStruct* _comm;
|
||||
private readonly Dictionary<string, IntPtr> _sharedMemoryBlocks = new Dictionary<string, IntPtr>();
|
||||
private bool _sealed = false;
|
||||
|
||||
public void Enter()
|
||||
{
|
||||
_exe.Enter();
|
||||
}
|
||||
|
||||
public void Exit()
|
||||
{
|
||||
_exe.Exit();
|
||||
}
|
||||
|
||||
private readonly List<string> _readonlyFiles = new List<string>();
|
||||
|
||||
public void AddReadonlyFile(byte[] data, string name)
|
||||
{
|
||||
_exe.AddReadonlyFile(data, name);
|
||||
_readonlyFiles.Add(name);
|
||||
}
|
||||
|
||||
public LibsnesApi(string dllPath, CoreComm comm, IEnumerable<Delegate> allCallbacks)
|
||||
{
|
||||
_exe = new WaterboxHost(new WaterboxOptions
|
||||
{
|
||||
Filename = "libsnes.wbx",
|
||||
Path = dllPath,
|
||||
SbrkHeapSizeKB = 4 * 1024,
|
||||
InvisibleHeapSizeKB = 8 * 1024,
|
||||
MmapHeapSizeKB = 32 * 1024, // TODO: see if we can safely make libco stacks smaller
|
||||
PlainHeapSizeKB = 32 * 1024, // TODO: This can be smaller, probably; needs to be as big as largest ROM + 2MB, or less
|
||||
SealedHeapSizeKB = 80 * 1024,
|
||||
SkipCoreConsistencyCheck = comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck),
|
||||
SkipMemoryConsistencyCheck = comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck),
|
||||
});
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
// Marshal checks that function pointers passed to GetDelegateForFunctionPointer are
|
||||
// _currently_ valid when created, even though they don't need to be valid until
|
||||
// the delegate is later invoked. so GetInvoker needs to be acquired within a lock.
|
||||
_core = BizInvoker.GetInvoker<CoreImpl>(_exe, _exe, CallingConventionAdapters.MakeWaterbox(allCallbacks, _exe));
|
||||
_comm = (CommStruct*)_core.DllInit().ToPointer();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
_disposed = true;
|
||||
_exe.Dispose();
|
||||
_exe = null;
|
||||
_core = null;
|
||||
_comm = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy an ascii string into libretro. It keeps the copy.
|
||||
/// </summary>
|
||||
public void CopyAscii(int id, string str)
|
||||
{
|
||||
fixed (byte* cp = System.Text.Encoding.ASCII.GetBytes(str + "\0"))
|
||||
{
|
||||
_core.CopyBuffer(id, cp, str.Length + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy a buffer into libretro. It keeps the copy.
|
||||
/// </summary>
|
||||
public void CopyBytes(int id, byte[] bytes)
|
||||
{
|
||||
fixed (byte* bp = bytes)
|
||||
{
|
||||
_core.CopyBuffer(id, bp, bytes.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Locks a buffer and sets it into libretro. You must pass a delegate to be executed while that buffer is locked.
|
||||
/// This is meant to be used for avoiding a memcpy for large roms (which the core is then just going to memcpy again on its own)
|
||||
/// The memcpy has to happen at some point (libretro semantics specify [not literally, the docs don't say] that the core should finish using the buffer before its init returns)
|
||||
/// but this limits it to once.
|
||||
/// Moreover, this keeps the c++ side from having to free strings when they're no longer used (and memory management is trickier there, so we try to avoid it)
|
||||
/// </summary>
|
||||
public void SetBytes(int id, byte[] bytes, Action andThen)
|
||||
{
|
||||
if (_sealed)
|
||||
throw new InvalidOperationException("Init period is over");
|
||||
fixed (byte* bp = bytes)
|
||||
{
|
||||
_core.SetBuffer(id, bp, bytes.Length);
|
||||
andThen();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// see SetBytes
|
||||
/// </summary>
|
||||
public void SetAscii(int id, string str, Action andThen)
|
||||
{
|
||||
if (_sealed)
|
||||
throw new InvalidOperationException("Init period is over");
|
||||
fixed (byte* cp = System.Text.Encoding.ASCII.GetBytes(str + "\0"))
|
||||
{
|
||||
_core.SetBuffer(id, cp, str.Length + 1);
|
||||
andThen();
|
||||
}
|
||||
}
|
||||
|
||||
public Action<uint> ReadHook, ExecHook;
|
||||
public Action<uint, byte> WriteHook;
|
||||
|
||||
public Action<uint> ReadHook_SMP, ExecHook_SMP;
|
||||
public Action<uint, byte> WriteHook_SMP;
|
||||
|
||||
public enum eCDLog_AddrType
|
||||
{
|
||||
CARTROM, CARTRAM, WRAM, APURAM,
|
||||
SGB_CARTROM, SGB_CARTRAM, SGB_WRAM, SGB_HRAM,
|
||||
NUM
|
||||
}
|
||||
|
||||
public enum eTRACE : uint
|
||||
{
|
||||
CPU = 0,
|
||||
SMP = 1,
|
||||
GB = 2
|
||||
}
|
||||
|
||||
public enum eCDLog_Flags
|
||||
{
|
||||
ExecFirst = 0x01,
|
||||
ExecOperand = 0x02,
|
||||
CPUData = 0x04,
|
||||
DMAData = 0x08, //not supported yet
|
||||
BRR = 0x80,
|
||||
}
|
||||
|
||||
private snes_video_refresh_t video_refresh;
|
||||
private snes_input_poll_t input_poll;
|
||||
private snes_input_state_t input_state;
|
||||
private snes_input_notify_t input_notify;
|
||||
private snes_audio_sample_t audio_sample;
|
||||
private snes_scanlineStart_t scanlineStart;
|
||||
private snes_path_request_t pathRequest;
|
||||
private snes_trace_t traceCallback;
|
||||
|
||||
public void QUERY_set_video_refresh(snes_video_refresh_t video_refresh) { this.video_refresh = video_refresh; }
|
||||
// not used??
|
||||
public void QUERY_set_input_poll(snes_input_poll_t input_poll) { this.input_poll = input_poll; }
|
||||
public void QUERY_set_input_state(snes_input_state_t input_state) { this.input_state = input_state; }
|
||||
public void QUERY_set_input_notify(snes_input_notify_t input_notify) { this.input_notify = input_notify; }
|
||||
public void QUERY_set_path_request(snes_path_request_t pathRequest) { this.pathRequest = pathRequest; }
|
||||
|
||||
public delegate void snes_video_refresh_t(int* data, int width, int height);
|
||||
public delegate void snes_input_poll_t();
|
||||
public delegate short snes_input_state_t(int port, int device, int index, int id);
|
||||
public delegate void snes_input_notify_t(int index);
|
||||
public delegate void snes_audio_sample_t(short left, short right);
|
||||
public delegate void snes_scanlineStart_t(int line);
|
||||
public delegate string snes_path_request_t(int slot, string hint);
|
||||
public delegate void snes_trace_t(uint which, string msg);
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct CPURegs
|
||||
{
|
||||
public uint pc;
|
||||
public ushort a, x, y, s, d, vector; //7x
|
||||
public byte p, db, nothing, nothing2;
|
||||
public ushort v, h;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct LayerEnables
|
||||
{
|
||||
private byte _BG1_Prio0, _BG1_Prio1;
|
||||
private byte _BG2_Prio0, _BG2_Prio1;
|
||||
private byte _BG3_Prio0, _BG3_Prio1;
|
||||
private byte _BG4_Prio0, _BG4_Prio1;
|
||||
private byte _Obj_Prio0, _Obj_Prio1, _Obj_Prio2, _Obj_Prio3;
|
||||
|
||||
public bool BG1_Prio0
|
||||
{
|
||||
get => _BG1_Prio0 != 0;
|
||||
set => _BG1_Prio0 = (byte)(value ? 1 : 0);
|
||||
}
|
||||
public bool BG1_Prio1
|
||||
{
|
||||
get => _BG1_Prio1 != 0;
|
||||
set => _BG1_Prio1 = (byte)(value ? 1 : 0);
|
||||
}
|
||||
public bool BG2_Prio0
|
||||
{
|
||||
get => _BG2_Prio0 != 0;
|
||||
set => _BG2_Prio0 = (byte)(value ? 1 : 0);
|
||||
}
|
||||
public bool BG2_Prio1
|
||||
{
|
||||
get => _BG2_Prio1 != 0;
|
||||
set => _BG2_Prio1 = (byte)(value ? 1 : 0);
|
||||
}
|
||||
public bool BG3_Prio0
|
||||
{
|
||||
get => _BG3_Prio0 != 0;
|
||||
set => _BG3_Prio0 = (byte)(value ? 1 : 0);
|
||||
}
|
||||
public bool BG3_Prio1
|
||||
{
|
||||
get => _BG3_Prio1 != 0;
|
||||
set => _BG3_Prio1 = (byte)(value ? 1 : 0);
|
||||
}
|
||||
public bool BG4_Prio0
|
||||
{
|
||||
get => _BG4_Prio0 != 0;
|
||||
set => _BG4_Prio0 = (byte)(value ? 1 : 0);
|
||||
}
|
||||
public bool BG4_Prio1
|
||||
{
|
||||
get => _BG4_Prio1 != 0;
|
||||
set => _BG4_Prio1 = (byte)(value ? 1 : 0);
|
||||
}
|
||||
|
||||
public bool Obj_Prio0
|
||||
{
|
||||
get => _Obj_Prio0 != 0;
|
||||
set => _Obj_Prio0 = (byte)(value ? 1 : 0);
|
||||
}
|
||||
public bool Obj_Prio1
|
||||
{
|
||||
get => _Obj_Prio1 != 0;
|
||||
set => _Obj_Prio1 = (byte)(value ? 1 : 0);
|
||||
}
|
||||
public bool Obj_Prio2
|
||||
{
|
||||
get => _Obj_Prio2 != 0;
|
||||
set => _Obj_Prio2 = (byte)(value ? 1 : 0);
|
||||
}
|
||||
public bool Obj_Prio3
|
||||
{
|
||||
get => _Obj_Prio3 != 0;
|
||||
set => _Obj_Prio3 = (byte)(value ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
private struct CommStruct
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
//the cmd being executed
|
||||
public readonly eMessage cmd;
|
||||
[FieldOffset(4)]
|
||||
//the status of the core
|
||||
public readonly eStatus status;
|
||||
[FieldOffset(8)]
|
||||
//the SIG or BRK that the core is halted in
|
||||
public readonly eMessage reason;
|
||||
|
||||
//flexible in/out parameters
|
||||
//these are all "overloaded" a little so it isn't clear what's used for what in for any particular message..
|
||||
//but I think it will beat having to have some kind of extremely verbose custom layouts for every message
|
||||
[FieldOffset(16)]
|
||||
public sbyte* str;
|
||||
[FieldOffset(24)]
|
||||
public void* ptr;
|
||||
[FieldOffset(32)]
|
||||
public uint id;
|
||||
[FieldOffset(36)]
|
||||
public uint addr;
|
||||
[FieldOffset(40)]
|
||||
public uint value;
|
||||
[FieldOffset(44)]
|
||||
public uint size;
|
||||
[FieldOffset(48)]
|
||||
public int port;
|
||||
[FieldOffset(52)]
|
||||
public int device;
|
||||
[FieldOffset(56)]
|
||||
public int index;
|
||||
[FieldOffset(60)]
|
||||
public int slot;
|
||||
[FieldOffset(64)]
|
||||
public int width;
|
||||
[FieldOffset(68)]
|
||||
public int height;
|
||||
[FieldOffset(72)]
|
||||
public int scanline;
|
||||
[FieldOffset(76)]
|
||||
public fixed int inports[2];
|
||||
|
||||
[FieldOffset(88)]
|
||||
//this should always be used in pairs
|
||||
public fixed long buf[3]; //ACTUALLY A POINTER but can't marshal it :(
|
||||
[FieldOffset(112)]
|
||||
public fixed int buf_size[3];
|
||||
|
||||
[FieldOffset(128)]
|
||||
//bleck. this is a long so that it can be a 32/64bit pointer
|
||||
public fixed long cdl_ptr[16];
|
||||
[FieldOffset(256)]
|
||||
public fixed int cdl_size[16];
|
||||
|
||||
[FieldOffset(320)]
|
||||
public CPURegs cpuregs;
|
||||
[FieldOffset(344)]
|
||||
public LayerEnables layerEnables;
|
||||
|
||||
[FieldOffset(356)]
|
||||
//static configuration-type information which can be grabbed off the core at any time without even needing a QUERY command
|
||||
public SNES_REGION region;
|
||||
[FieldOffset(360)]
|
||||
public SNES_MAPPER mapper;
|
||||
|
||||
[FieldOffset(364)] private uint BLANK0;
|
||||
|
||||
|
||||
//utilities
|
||||
//TODO: make internal, wrap on the API instead of the comm
|
||||
public string GetAscii() => _getAscii(str);
|
||||
public bool GetBool() { return value != 0; }
|
||||
|
||||
private string _getAscii(sbyte* ptr)
|
||||
{
|
||||
int len = 0;
|
||||
sbyte* junko = ptr;
|
||||
while (junko[len] != 0) len++;
|
||||
|
||||
return new string(str, 0, len, System.Text.Encoding.ASCII);
|
||||
}
|
||||
}
|
||||
|
||||
public SNES_REGION Region
|
||||
{
|
||||
get
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
return _comm->region;
|
||||
}
|
||||
}
|
||||
}
|
||||
public SNES_MAPPER Mapper
|
||||
{
|
||||
get
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
return _comm->mapper;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetLayerEnables(ref LayerEnables enables)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
_comm->layerEnables = enables;
|
||||
QUERY_set_layer_enable();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetInputPortBeforeInit(int port, SNES_INPUT_PORT type)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
_comm->inports[port] = (int)type;
|
||||
}
|
||||
}
|
||||
|
||||
public void Seal()
|
||||
{
|
||||
/* Cothreads can very easily acquire "pointer poison"; because their stack and even registers
|
||||
* are part of state, any poisoned pointer that's used even temporarily might be persisted longer
|
||||
* than needed. Most of the libsnes core cothreads handle internal matters only and aren't very
|
||||
* vulnerable to pointer poison, but the main boss cothread is used heavily during init, when
|
||||
* many syscalls happen and many kinds of poison can end up on the stack. so here, we call
|
||||
* _core.DllInit() again, which recreates that cothread, zeroing out all of the memory first,
|
||||
* as well as zeroing out the comm struct. */
|
||||
_core.DllInit();
|
||||
_exe.Seal();
|
||||
_sealed = true;
|
||||
foreach (var s in _readonlyFiles)
|
||||
{
|
||||
_exe.RemoveReadonlyFile(s);
|
||||
}
|
||||
_readonlyFiles.Clear();
|
||||
}
|
||||
|
||||
public bool AvoidRewind => false;
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
_exe.SaveStateBinary(writer);
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
_exe.LoadStateBinary(reader);
|
||||
_core.PostLoadState();
|
||||
}
|
||||
|
||||
public MemoryDomain GetPagesDomain()
|
||||
{
|
||||
return _exe.GetPagesDomain();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
using BizHawk.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||
{
|
||||
unsafe partial class LibsnesApi
|
||||
{
|
||||
private bool Handle_BRK(eMessage msg)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
|
||||
case eMessage.eMessage_BRK_hook_exec:
|
||||
ExecHook(_comm->addr);
|
||||
break;
|
||||
case eMessage.eMessage_BRK_hook_read:
|
||||
ReadHook(_comm->addr);
|
||||
break;
|
||||
case eMessage.eMessage_BRK_hook_write:
|
||||
WriteHook(_comm->addr, (byte)_comm->value);
|
||||
break;
|
||||
|
||||
case eMessage.eMessage_BRK_hook_exec_smp:
|
||||
ExecHook_SMP(_comm->addr);
|
||||
break;
|
||||
case eMessage.eMessage_BRK_hook_read_smp:
|
||||
ReadHook_SMP(_comm->addr);
|
||||
break;
|
||||
case eMessage.eMessage_BRK_hook_write_smp:
|
||||
WriteHook_SMP(_comm->addr, (byte)_comm->value);
|
||||
break;
|
||||
|
||||
//not supported yet
|
||||
case eMessage.eMessage_BRK_hook_nmi:
|
||||
break;
|
||||
case eMessage.eMessage_BRK_hook_irq:
|
||||
break;
|
||||
|
||||
case eMessage.eMessage_BRK_scanlineStart:
|
||||
scanlineStart?.Invoke(_comm->scanline);
|
||||
break;
|
||||
|
||||
} //switch(msg)
|
||||
|
||||
_core.Message(eMessage.eMessage_Resume);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
using BizHawk.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||
{
|
||||
unsafe partial class LibsnesApi
|
||||
{
|
||||
private void WaitForCMD()
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (_comm->status == eStatus.eStatus_Idle)
|
||||
break;
|
||||
if (Handle_SIG(_comm->reason)) continue;
|
||||
if (Handle_BRK(_comm->reason)) continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CMD_init(bool randomize)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
_comm->value = randomize ? 1u : 0u;
|
||||
_core.Message(eMessage.eMessage_CMD_init);
|
||||
WaitForCMD();
|
||||
}
|
||||
}
|
||||
public void CMD_power()
|
||||
{
|
||||
_core.Message(eMessage.eMessage_CMD_power);
|
||||
WaitForCMD();
|
||||
}
|
||||
public void CMD_reset()
|
||||
{
|
||||
_core.Message(eMessage.eMessage_CMD_reset);
|
||||
WaitForCMD();
|
||||
}
|
||||
|
||||
public void CMD_run()
|
||||
{
|
||||
_core.Message(eMessage.eMessage_CMD_run);
|
||||
WaitForCMD();
|
||||
}
|
||||
|
||||
public bool CMD_load_cartridge_super_game_boy(string rom_xml, byte[] rom_data, uint rom_size, byte[] dmg_data)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
SetAscii(0, rom_xml ?? "", () =>
|
||||
SetBytes(1, rom_data, () =>
|
||||
SetBytes(2, dmg_data, () =>
|
||||
{
|
||||
_core.Message(eMessage.eMessage_CMD_load_cartridge_sgb);
|
||||
WaitForCMD();
|
||||
})
|
||||
)
|
||||
);
|
||||
return _comm->GetBool();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CMD_load_cartridge_normal(byte[] rom_xml, byte[] rom_data)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
//why don't we need this for the other loads? I don't know, our XML handling is really confusing
|
||||
string xml = rom_xml == null ? null : System.Text.Encoding.ASCII.GetString(rom_xml);
|
||||
|
||||
SetAscii(0, xml ?? "", () =>
|
||||
SetBytes(1, rom_data, () =>
|
||||
{
|
||||
_core.Message(eMessage.eMessage_CMD_load_cartridge_normal);
|
||||
WaitForCMD();
|
||||
})
|
||||
);
|
||||
return _comm->GetBool();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,251 +0,0 @@
|
|||
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||
{
|
||||
public partial class LibsnesApi
|
||||
{
|
||||
public enum eMessage : int
|
||||
{
|
||||
eMessage_NotSet,
|
||||
|
||||
eMessage_Resume,
|
||||
|
||||
eMessage_QUERY_FIRST,
|
||||
eMessage_QUERY_get_memory_size,
|
||||
eMessage_QUERY_peek,
|
||||
eMessage_QUERY_poke,
|
||||
eMessage_QUERY_serialize_size,
|
||||
eMessage_QUERY_set_color_lut,
|
||||
eMessage_QUERY_GetMemoryIdName,
|
||||
eMessage_QUERY_state_hook_exec,
|
||||
eMessage_QUERY_state_hook_read,
|
||||
eMessage_QUERY_state_hook_write,
|
||||
eMessage_QUERY_state_hook_nmi,
|
||||
eMessage_QUERY_state_hook_irq,
|
||||
eMessage_QUERY_state_hook_exec_smp,
|
||||
eMessage_QUERY_state_hook_read_smp,
|
||||
eMessage_QUERY_state_hook_write_smp,
|
||||
eMessage_QUERY_enable_trace,
|
||||
eMessage_QUERY_enable_scanline,
|
||||
eMessage_QUERY_enable_audio,
|
||||
eMessage_QUERY_set_layer_enable,
|
||||
eMessage_QUERY_set_backdropColor,
|
||||
eMessage_QUERY_peek_logical_register,
|
||||
eMessage_QUERY_peek_cpu_regs,
|
||||
eMessage_QUERY_set_cdl,
|
||||
eMessage_QUERY_LAST,
|
||||
|
||||
eMessage_CMD_FIRST,
|
||||
eMessage_CMD_init,
|
||||
eMessage_CMD_power,
|
||||
eMessage_CMD_reset,
|
||||
eMessage_CMD_run,
|
||||
eMessage_CMD_serialize,
|
||||
eMessage_CMD_unserialize,
|
||||
eMessage_CMD_load_cartridge_normal,
|
||||
eMessage_CMD_load_cartridge_sgb,
|
||||
eMessage_CMD_term,
|
||||
eMessage_CMD_unload_cartridge,
|
||||
eMessage_CMD_LAST,
|
||||
|
||||
eMessage_SIG_video_refresh,
|
||||
eMessage_SIG_input_poll,
|
||||
eMessage_SIG_input_state,
|
||||
eMessage_SIG_input_notify,
|
||||
eMessage_SIG_audio_flush,
|
||||
eMessage_SIG_path_request,
|
||||
eMessage_SIG_trace_callback,
|
||||
eMessage_SIG_allocSharedMemory, //?
|
||||
eMessage_SIG_freeSharedMemory, //?
|
||||
|
||||
eMessage_BRK_Complete,
|
||||
eMessage_BRK_hook_exec,
|
||||
eMessage_BRK_hook_read,
|
||||
eMessage_BRK_hook_write,
|
||||
eMessage_BRK_hook_nmi,
|
||||
eMessage_BRK_hook_irq,
|
||||
eMessage_BRK_hook_exec_smp,
|
||||
eMessage_BRK_hook_read_smp,
|
||||
eMessage_BRK_hook_write_smp,
|
||||
eMessage_BRK_scanlineStart,
|
||||
}
|
||||
|
||||
private enum eStatus : int
|
||||
{
|
||||
eStatus_Idle,
|
||||
eStatus_CMD,
|
||||
eStatus_BRK
|
||||
}
|
||||
|
||||
public enum SNES_INPUT_PORT : int
|
||||
{
|
||||
None,
|
||||
Joypad,
|
||||
Multitap,
|
||||
Mouse,
|
||||
SuperScope,
|
||||
Justifier,
|
||||
Justifiers,
|
||||
USART
|
||||
}
|
||||
|
||||
public enum SNES_REG : int
|
||||
{
|
||||
//$2105
|
||||
BG_MODE = 0,
|
||||
BG3_PRIORITY = 1,
|
||||
BG1_TILESIZE = 2,
|
||||
BG2_TILESIZE = 3,
|
||||
BG3_TILESIZE = 4,
|
||||
BG4_TILESIZE = 5,
|
||||
//$2107
|
||||
BG1_SCADDR = 10,
|
||||
BG1_SCSIZE = 11,
|
||||
//$2108
|
||||
BG2_SCADDR = 12,
|
||||
BG2_SCSIZE = 13,
|
||||
//$2109
|
||||
BG3_SCADDR = 14,
|
||||
BG3_SCSIZE = 15,
|
||||
//$210A
|
||||
BG4_SCADDR = 16,
|
||||
BG4_SCSIZE = 17,
|
||||
//$210B
|
||||
BG1_TDADDR = 20,
|
||||
BG2_TDADDR = 21,
|
||||
//$210C
|
||||
BG3_TDADDR = 22,
|
||||
BG4_TDADDR = 23,
|
||||
//$2133 SETINI
|
||||
SETINI_MODE7_EXTBG = 30,
|
||||
SETINI_HIRES = 31,
|
||||
SETINI_OVERSCAN = 32,
|
||||
SETINI_OBJ_INTERLACE = 33,
|
||||
SETINI_SCREEN_INTERLACE = 34,
|
||||
//$2130 CGWSEL
|
||||
CGWSEL_COLORMASK = 40,
|
||||
CGWSEL_COLORSUBMASK = 41,
|
||||
CGWSEL_ADDSUBMODE = 42,
|
||||
CGWSEL_DIRECTCOLOR = 43,
|
||||
//$2101 OBSEL
|
||||
OBSEL_NAMEBASE = 50,
|
||||
OBSEL_NAMESEL = 51,
|
||||
OBSEL_SIZE = 52,
|
||||
//$2131 CGADSUB
|
||||
CGADSUB_MODE = 60,
|
||||
CGADSUB_HALF = 61,
|
||||
CGADSUB_BG4 = 62,
|
||||
CGADSUB_BG3 = 63,
|
||||
CGADSUB_BG2 = 64,
|
||||
CGADSUB_BG1 = 65,
|
||||
CGADSUB_OBJ = 66,
|
||||
CGADSUB_BACKDROP = 67,
|
||||
//$212C TM
|
||||
TM_BG1 = 70,
|
||||
TM_BG2 = 71,
|
||||
TM_BG3 = 72,
|
||||
TM_BG4 = 73,
|
||||
TM_OBJ = 74,
|
||||
//$212D TM
|
||||
TS_BG1 = 80,
|
||||
TS_BG2 = 81,
|
||||
TS_BG3 = 82,
|
||||
TS_BG4 = 83,
|
||||
TS_OBJ = 84,
|
||||
//Mode7 regs
|
||||
M7SEL_REPEAT = 90,
|
||||
M7SEL_HFLIP = 91,
|
||||
M7SEL_VFLIP = 92,
|
||||
M7A = 93,
|
||||
M7B = 94,
|
||||
M7C = 95,
|
||||
M7D = 96,
|
||||
M7X = 97,
|
||||
M7Y = 98,
|
||||
//BG scroll regs
|
||||
BG1HOFS = 100,
|
||||
BG1VOFS = 101,
|
||||
BG2HOFS = 102,
|
||||
BG2VOFS = 103,
|
||||
BG3HOFS = 104,
|
||||
BG3VOFS = 105,
|
||||
BG4HOFS = 106,
|
||||
BG4VOFS = 107,
|
||||
M7HOFS = 108,
|
||||
M7VOFS = 109,
|
||||
}
|
||||
|
||||
public enum SNES_MEMORY : uint
|
||||
{
|
||||
CARTRIDGE_RAM = 0,
|
||||
CARTRIDGE_RTC = 1,
|
||||
BSX_RAM = 2,
|
||||
BSX_PRAM = 3,
|
||||
SUFAMI_TURBO_A_RAM = 4,
|
||||
SUFAMI_TURBO_B_RAM = 5,
|
||||
SGB_CARTRAM = 6,
|
||||
SGB_RTC = 7,
|
||||
SGB_WRAM = 8,
|
||||
SGB_HRAM = 9,
|
||||
SA1_IRAM = 10,
|
||||
|
||||
WRAM = 100,
|
||||
APURAM = 101,
|
||||
VRAM = 102,
|
||||
OAM = 103,
|
||||
CGRAM = 104,
|
||||
|
||||
CARTRIDGE_ROM = 105,
|
||||
|
||||
SYSBUS = 200,
|
||||
LOGICAL_REGS = 201
|
||||
}
|
||||
|
||||
public enum SNES_MAPPER : byte
|
||||
{
|
||||
LOROM = 0,
|
||||
HIROM = 1,
|
||||
EXLOROM = 2,
|
||||
EXHIROM = 3,
|
||||
SUPERFXROM = 4,
|
||||
SA1ROM = 5,
|
||||
SPC7110ROM = 6,
|
||||
BSCLOROM = 7,
|
||||
BSCHIROM = 8,
|
||||
BSXROM = 9,
|
||||
STROM = 10
|
||||
}
|
||||
|
||||
public enum SNES_REGION : uint
|
||||
{
|
||||
NTSC = 0,
|
||||
PAL = 1,
|
||||
}
|
||||
|
||||
public enum SNES_DEVICE : uint
|
||||
{
|
||||
NONE = 0,
|
||||
JOYPAD = 1,
|
||||
MULTITAP = 2,
|
||||
MOUSE = 3,
|
||||
SUPER_SCOPE = 4,
|
||||
JUSTIFIER = 5,
|
||||
JUSTIFIERS = 6,
|
||||
SERIAL_CABLE = 7
|
||||
}
|
||||
|
||||
public enum SNES_DEVICE_ID : uint
|
||||
{
|
||||
JOYPAD_B = 0,
|
||||
JOYPAD_Y = 1,
|
||||
JOYPAD_SELECT = 2,
|
||||
JOYPAD_START = 3,
|
||||
JOYPAD_UP = 4,
|
||||
JOYPAD_DOWN = 5,
|
||||
JOYPAD_LEFT = 6,
|
||||
JOYPAD_RIGHT = 7,
|
||||
JOYPAD_A = 8,
|
||||
JOYPAD_X = 9,
|
||||
JOYPAD_L = 10,
|
||||
JOYPAD_R = 11
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,225 +0,0 @@
|
|||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||
{
|
||||
unsafe partial class LibsnesApi
|
||||
{
|
||||
public int QUERY_get_memory_size(SNES_MEMORY id)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
_comm->value = (uint)id;
|
||||
_core.Message(eMessage.eMessage_QUERY_get_memory_size);
|
||||
return (int)_comm->value;
|
||||
}
|
||||
}
|
||||
|
||||
private string QUERY_MemoryNameForId(SNES_MEMORY id)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
_comm->id = (uint)id;
|
||||
_core.Message(eMessage.eMessage_QUERY_GetMemoryIdName);
|
||||
return _comm->GetAscii();
|
||||
}
|
||||
}
|
||||
|
||||
public byte* QUERY_get_memory_data(SNES_MEMORY id)
|
||||
{
|
||||
string name = QUERY_MemoryNameForId(id);
|
||||
_ = _sharedMemoryBlocks.TryGetValue(name, out var ret);
|
||||
return (byte*)ret;
|
||||
}
|
||||
|
||||
public byte QUERY_peek(SNES_MEMORY id, uint addr)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
_comm->id = (uint)id;
|
||||
_comm->addr = addr;
|
||||
_core.Message(eMessage.eMessage_QUERY_peek);
|
||||
return (byte)_comm->value;
|
||||
}
|
||||
}
|
||||
public void QUERY_poke(SNES_MEMORY id, uint addr, byte val)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
_comm->id = (uint)id;
|
||||
_comm->addr = addr;
|
||||
_comm->value = val;
|
||||
_core.Message(eMessage.eMessage_QUERY_poke);
|
||||
}
|
||||
}
|
||||
|
||||
public void QUERY_set_color_lut(IntPtr colors)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
_comm->ptr = colors.ToPointer();
|
||||
_core.Message(eMessage.eMessage_QUERY_set_color_lut);
|
||||
}
|
||||
}
|
||||
|
||||
public void QUERY_set_state_hook_exec(bool state)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
_comm->value = state ? 1u : 0u;
|
||||
_core.Message(eMessage.eMessage_QUERY_state_hook_exec);
|
||||
}
|
||||
}
|
||||
|
||||
public void QUERY_set_state_hook_read(bool state)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
_comm->value = state ? 1u : 0u;
|
||||
_core.Message(eMessage.eMessage_QUERY_state_hook_read);
|
||||
}
|
||||
}
|
||||
|
||||
public void QUERY_set_state_hook_write(bool state)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
_comm->value = state ? 1u : 0u;
|
||||
_core.Message(eMessage.eMessage_QUERY_state_hook_write);
|
||||
}
|
||||
}
|
||||
|
||||
public void QUERY_set_trace_callback(int mask, snes_trace_t callback)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
this.traceCallback = callback;
|
||||
_comm->value = (uint)mask;
|
||||
_core.Message(eMessage.eMessage_QUERY_enable_trace);
|
||||
}
|
||||
}
|
||||
public void QUERY_set_scanlineStart(snes_scanlineStart_t scanlineStart)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
this.scanlineStart = scanlineStart;
|
||||
_comm->value = (scanlineStart != null) ? 1u : 0u;
|
||||
_core.Message(eMessage.eMessage_QUERY_enable_scanline);
|
||||
}
|
||||
}
|
||||
public void QUERY_set_audio_sample(snes_audio_sample_t audio_sample)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
this.audio_sample = audio_sample;
|
||||
_comm->value = (audio_sample != null) ? 1u : 0u;
|
||||
_core.Message(eMessage.eMessage_QUERY_enable_audio);
|
||||
}
|
||||
}
|
||||
|
||||
public void QUERY_set_layer_enable()
|
||||
{
|
||||
_core.Message(eMessage.eMessage_QUERY_set_layer_enable);
|
||||
}
|
||||
|
||||
public void QUERY_set_backdropColor(int backdropColor)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
_comm->value = (uint)backdropColor;
|
||||
_core.Message(eMessage.eMessage_QUERY_set_backdropColor);
|
||||
}
|
||||
}
|
||||
|
||||
public int QUERY_peek_logical_register(SNES_REG reg)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
_comm->id = (uint)reg;
|
||||
_core.Message(eMessage.eMessage_QUERY_peek_logical_register);
|
||||
return (int)_comm->value;
|
||||
}
|
||||
}
|
||||
|
||||
public void QUERY_peek_cpu_regs(out CPURegs ret)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
_core.Message(eMessage.eMessage_QUERY_peek_cpu_regs);
|
||||
ret = _comm->cpuregs;
|
||||
}
|
||||
}
|
||||
|
||||
public void QUERY_set_cdl(ICodeDataLog cdl)
|
||||
{
|
||||
if (_exe == null)
|
||||
return;
|
||||
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
_comm->cdl_ptr[i] = 0;
|
||||
_comm->cdl_size[i] = 0;
|
||||
}
|
||||
|
||||
if (cdl != null)
|
||||
{
|
||||
int zz = 0;
|
||||
|
||||
_comm->cdl_ptr[zz] = cdl.GetPin("CARTROM").ToInt64();
|
||||
_comm->cdl_size[zz] = cdl["CARTROM"].Length;
|
||||
zz++;
|
||||
|
||||
_comm->cdl_ptr[zz] = cdl.GetPin("CARTROM-DB").ToInt64();
|
||||
_comm->cdl_size[zz] = cdl["CARTROM"].Length;
|
||||
zz++;
|
||||
|
||||
_comm->cdl_ptr[zz] = cdl.GetPin("CARTROM-D").ToInt64();
|
||||
_comm->cdl_size[zz] = cdl["CARTROM"].Length * 2;
|
||||
zz++;
|
||||
|
||||
if (cdl.Has("CARTRAM"))
|
||||
{
|
||||
_comm->cdl_ptr[zz] = cdl.GetPin("CARTRAM").ToInt64();
|
||||
_comm->cdl_size[zz] = cdl["CARTRAM"].Length;
|
||||
}
|
||||
zz++;
|
||||
|
||||
_comm->cdl_ptr[zz] = cdl.GetPin("WRAM").ToInt64();
|
||||
_comm->cdl_size[zz] = cdl["WRAM"].Length;
|
||||
zz++;
|
||||
|
||||
_comm->cdl_ptr[zz] = cdl.GetPin("APURAM").ToInt64();
|
||||
_comm->cdl_size[zz] = cdl["APURAM"].Length;
|
||||
zz++;
|
||||
|
||||
if (cdl.Has("SGB_CARTROM"))
|
||||
{
|
||||
_comm->cdl_ptr[zz] = cdl.GetPin("SGB_CARTROM").ToInt64();
|
||||
_comm->cdl_size[zz] = cdl["SGB_CARTROM"].Length;
|
||||
zz++;
|
||||
|
||||
if (cdl.Has("SGB_CARTRAM"))
|
||||
{
|
||||
_comm->cdl_ptr[zz] = cdl.GetPin("SGB_CARTRAM").ToInt64();
|
||||
_comm->cdl_size[zz] = cdl["SGB_CARTRAM"].Length;
|
||||
}
|
||||
zz++;
|
||||
|
||||
_comm->cdl_ptr[zz] = cdl.GetPin("SGB_WRAM").ToInt64();
|
||||
_comm->cdl_size[zz] = cdl["SGB_WRAM"].Length;
|
||||
zz++;
|
||||
|
||||
_comm->cdl_ptr[zz] = cdl.GetPin("SGB_HRAM").ToInt64();
|
||||
_comm->cdl_size[zz] = cdl["SGB_HRAM"].Length;
|
||||
zz++;
|
||||
}
|
||||
else zz += 4;
|
||||
}
|
||||
|
||||
_core.Message(eMessage.eMessage_QUERY_set_cdl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
using BizHawk.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||
{
|
||||
unsafe partial class LibsnesApi
|
||||
{
|
||||
private bool Handle_SIG(eMessage msg)
|
||||
{
|
||||
using (_exe.EnterExit())
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
|
||||
case eMessage.eMessage_SIG_video_refresh:
|
||||
{
|
||||
int width = _comm->width;
|
||||
int height = _comm->height;
|
||||
video_refresh?.Invoke((int*)_comm->ptr, width, height);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_input_poll:
|
||||
break;
|
||||
case eMessage.eMessage_SIG_input_state:
|
||||
{
|
||||
int port = _comm->port;
|
||||
int device = _comm->device;
|
||||
int index = _comm->index;
|
||||
int id = (int)_comm->id;
|
||||
if (input_state != null)
|
||||
_comm->value = (uint)input_state(port, device, index, id);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_input_notify:
|
||||
{
|
||||
input_notify?.Invoke(_comm->index);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_audio_flush:
|
||||
{
|
||||
uint nsamples = _comm->size;
|
||||
|
||||
if (audio_sample != null)
|
||||
{
|
||||
var audiobuffer = (short*)_comm->ptr;
|
||||
for (uint i = 0; i < nsamples;)
|
||||
{
|
||||
var left = audiobuffer[i++];
|
||||
var right = audiobuffer[i++];
|
||||
audio_sample(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_path_request:
|
||||
{
|
||||
int slot = _comm->slot;
|
||||
string hint = _comm->GetAscii();
|
||||
string ret = hint;
|
||||
if (pathRequest != null)
|
||||
hint = pathRequest(slot, hint);
|
||||
CopyAscii(0, hint);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_trace_callback:
|
||||
{
|
||||
traceCallback?.Invoke(_comm->value, _comm->GetAscii());
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_allocSharedMemory:
|
||||
{
|
||||
// NB: shared memory blocks are allocated on the unmanaged side
|
||||
var name = _comm->GetAscii();
|
||||
var size = _comm->size;
|
||||
var ptr = _comm->ptr;
|
||||
|
||||
if (_sharedMemoryBlocks.ContainsKey(name))
|
||||
throw new InvalidOperationException("Re-defined a shared memory block. Check bsnes init/shutdown code. Block name: " + name);
|
||||
|
||||
_sharedMemoryBlocks.Add(name, (IntPtr)ptr);
|
||||
break;
|
||||
}
|
||||
case eMessage.eMessage_SIG_freeSharedMemory:
|
||||
throw new InvalidOperationException("Unexpected call: SIG_freeSharedMemory");
|
||||
} //switch(msg)
|
||||
|
||||
_core.Message(eMessage.eMessage_Resume);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,91 +1,8 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Common.NumberExtensions;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||
{
|
||||
public class LibsnesControllerDeck
|
||||
{
|
||||
public enum ControllerType
|
||||
{
|
||||
Unplugged,
|
||||
Gamepad,
|
||||
Multitap,
|
||||
Mouse,
|
||||
SuperScope,
|
||||
Justifier,
|
||||
Payload
|
||||
}
|
||||
|
||||
private static ILibsnesController Factory(ControllerType t, LibsnesCore.SnesSyncSettings ss)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case ControllerType.Unplugged:
|
||||
return new SnesUnpluggedController();
|
||||
case ControllerType.Gamepad:
|
||||
return new SnesController();
|
||||
case ControllerType.Multitap:
|
||||
return new SnesMultitapController();
|
||||
case ControllerType.Payload:
|
||||
return new SnesPayloadController();
|
||||
case ControllerType.Mouse:
|
||||
return new SnesMouseController
|
||||
{
|
||||
LimitAnalogChangeSensitivity = ss.LimitAnalogChangeSensitivity
|
||||
};
|
||||
case ControllerType.SuperScope:
|
||||
return new SnesSuperScopeController();
|
||||
case ControllerType.Justifier:
|
||||
return new SnesJustifierController();
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly ILibsnesController[] _ports;
|
||||
private readonly ControlDefUnMerger[] _mergers;
|
||||
|
||||
public ControllerDefinition Definition { get; }
|
||||
|
||||
public LibsnesControllerDeck(LibsnesCore.SnesSyncSettings ss)
|
||||
{
|
||||
_ports = new[]
|
||||
{
|
||||
Factory(ss.LeftPort, ss),
|
||||
Factory(ss.RightPort, ss)
|
||||
};
|
||||
|
||||
Definition = ControllerDefinitionMerger.GetMerged(
|
||||
"SNES Controller",
|
||||
_ports.Select(p => p.Definition),
|
||||
out var tmp);
|
||||
_mergers = tmp.ToArray();
|
||||
|
||||
// add buttons that the core itself will handle
|
||||
Definition.BoolButtons.Add("Reset");
|
||||
Definition.BoolButtons.Add("Power");
|
||||
|
||||
Definition.MakeImmutable();
|
||||
}
|
||||
|
||||
public void NativeInit(LibsnesApi api)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
api.SetInputPortBeforeInit(i, _ports[i].PortType);
|
||||
}
|
||||
}
|
||||
|
||||
public short CoreInputState(IController controller, int port, int device, int index, int id)
|
||||
{
|
||||
return _ports[port].GetState(_mergers[port].UnMerge(controller), index, id);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class SNESControllerDefExtensions
|
||||
{
|
||||
/// <remarks>
|
||||
|
@ -97,296 +14,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
=> def.AddXYPair(nameFormat, AxisPairOrientation.RightAndDown, 0.RangeTo(255), 128, 0.RangeTo(239), 120); //TODO verify direction against hardware
|
||||
}
|
||||
|
||||
public interface ILibsnesController
|
||||
public static class SnesMouseController
|
||||
{
|
||||
/// <summary>
|
||||
/// the type to pass back to the native init
|
||||
/// </summary>
|
||||
LibsnesApi.SNES_INPUT_PORT PortType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// respond to a native core poll
|
||||
/// </summary>
|
||||
/// <param name="controller">controller input from user, remapped</param>
|
||||
/// <param name="index">libsnes specific value, sometimes multitap number</param>
|
||||
/// <param name="id">libsnes specific value, sometimes button number</param>
|
||||
short GetState(IController controller, int index, int id);
|
||||
|
||||
ControllerDefinition Definition { get; }
|
||||
|
||||
// due to the way things are implemented, right now, all of the ILibsnesControllers are stateless
|
||||
// but if one needed state, that would be doable
|
||||
// void SyncState(Serializer ser);
|
||||
}
|
||||
|
||||
public class SnesController : ILibsnesController
|
||||
{
|
||||
public LibsnesApi.SNES_INPUT_PORT PortType { get; } = LibsnesApi.SNES_INPUT_PORT.Joypad;
|
||||
|
||||
private static readonly string[] Buttons =
|
||||
{
|
||||
"0B",
|
||||
"0Y",
|
||||
"0Select",
|
||||
"0Start",
|
||||
"0Up",
|
||||
"0Down",
|
||||
"0Left",
|
||||
"0Right",
|
||||
"0A",
|
||||
"0X",
|
||||
"0L",
|
||||
"0R"
|
||||
};
|
||||
|
||||
private static int ButtonOrder(string btn)
|
||||
{
|
||||
var order = new Dictionary<string, int>
|
||||
{
|
||||
["0Up"] = 0,
|
||||
["0Down"] = 1,
|
||||
["0Left"] = 2,
|
||||
["0Right"] = 3,
|
||||
|
||||
["0Select"] = 4,
|
||||
["0Start"] = 5,
|
||||
|
||||
["0Y"] = 6,
|
||||
["0B"] = 7,
|
||||
|
||||
["0X"] = 8,
|
||||
["0A"] = 9,
|
||||
|
||||
["0L"] = 10,
|
||||
["0R"] = 11
|
||||
};
|
||||
|
||||
return order[btn];
|
||||
}
|
||||
|
||||
private static readonly ControllerDefinition _definition = new("(SNES Controller fragment)")
|
||||
{
|
||||
BoolButtons = Buttons.OrderBy(ButtonOrder).ToList()
|
||||
};
|
||||
|
||||
public ControllerDefinition Definition { get; } = _definition;
|
||||
|
||||
public short GetState(IController controller, int index, int id)
|
||||
{
|
||||
if (id >= 12)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return (short)(controller.IsPressed(Buttons[id]) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
public class SnesMultitapController : ILibsnesController
|
||||
{
|
||||
public LibsnesApi.SNES_INPUT_PORT PortType { get; } = LibsnesApi.SNES_INPUT_PORT.Multitap;
|
||||
|
||||
private static readonly string[] Buttons =
|
||||
{
|
||||
"B",
|
||||
"Y",
|
||||
"Select",
|
||||
"Start",
|
||||
"Up",
|
||||
"Down",
|
||||
"Left",
|
||||
"Right",
|
||||
"A",
|
||||
"X",
|
||||
"L",
|
||||
"R"
|
||||
};
|
||||
|
||||
private static int ButtonOrder(string btn)
|
||||
{
|
||||
var order = new Dictionary<string, int>
|
||||
{
|
||||
["Up"] = 0,
|
||||
["Down"] = 1,
|
||||
["Left"] = 2,
|
||||
["Right"] = 3,
|
||||
|
||||
["Select"] = 4,
|
||||
["Start"] = 5,
|
||||
|
||||
["Y"] = 6,
|
||||
["B"] = 7,
|
||||
|
||||
["X"] = 8,
|
||||
["A"] = 9,
|
||||
|
||||
["L"] = 10,
|
||||
["R"] = 11
|
||||
};
|
||||
|
||||
return order[btn];
|
||||
}
|
||||
|
||||
private static readonly ControllerDefinition _definition = new("(SNES Controller fragment)")
|
||||
{
|
||||
BoolButtons = Enumerable.Range(0, 4)
|
||||
.SelectMany(i => Buttons
|
||||
.OrderBy(ButtonOrder)
|
||||
.Select(b => i + b))
|
||||
.Concat(new[] { "0Toggle Multitap" })
|
||||
.ToList()
|
||||
};
|
||||
|
||||
public ControllerDefinition Definition { get; } = _definition;
|
||||
|
||||
public short GetState(IController controller, int index, int id)
|
||||
{
|
||||
if (id == 16)
|
||||
{
|
||||
return (short)(controller.IsPressed("0Toggle Multitap") ? 1 : 0);
|
||||
}
|
||||
if (id >= 12)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return (short)(controller.IsPressed(index + Buttons[id]) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
public class SnesPayloadController : ILibsnesController
|
||||
{
|
||||
public LibsnesApi.SNES_INPUT_PORT PortType { get; } = LibsnesApi.SNES_INPUT_PORT.Multitap;
|
||||
|
||||
private static readonly ControllerDefinition _definition = new("(SNES Controller fragment)")
|
||||
{
|
||||
BoolButtons = Enumerable.Range(0, 32).Select(i => "0B" + i).ToList()
|
||||
};
|
||||
|
||||
public ControllerDefinition Definition { get; } = _definition;
|
||||
|
||||
public short GetState(IController controller, int index, int id)
|
||||
{
|
||||
return (short)(controller.IsPressed("0B" + (index << 4 & 16 | id)) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
public class SnesUnpluggedController : ILibsnesController
|
||||
{
|
||||
public LibsnesApi.SNES_INPUT_PORT PortType { get; } = LibsnesApi.SNES_INPUT_PORT.None;
|
||||
|
||||
private static readonly ControllerDefinition _definition = new("(SNES Controller fragment)");
|
||||
|
||||
public ControllerDefinition Definition { get; } = _definition;
|
||||
|
||||
public short GetState(IController controller, int index, int id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public class SnesMouseController : ILibsnesController
|
||||
{
|
||||
public LibsnesApi.SNES_INPUT_PORT PortType => LibsnesApi.SNES_INPUT_PORT.Mouse;
|
||||
|
||||
private static readonly ControllerDefinition _definition
|
||||
public static readonly ControllerDefinition Definition
|
||||
= new ControllerDefinition("(SNES Controller fragment)") { BoolButtons = { "0Mouse Left", "0Mouse Right" } }
|
||||
.AddXYPair("0Mouse {0}", AxisPairOrientation.RightAndDown, (-127).RangeTo(127), 0); //TODO verify direction against hardware, R+D inferred from behaviour in Mario Paint
|
||||
|
||||
public ControllerDefinition Definition => _definition;
|
||||
|
||||
public bool LimitAnalogChangeSensitivity { get; set; } = true;
|
||||
|
||||
public short GetState(IController controller, int index, int id)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case 0:
|
||||
var x = controller.AxisValue("0Mouse X");
|
||||
if (LimitAnalogChangeSensitivity)
|
||||
{
|
||||
x = x.Clamp(-10, 10);
|
||||
}
|
||||
|
||||
return (short)x;
|
||||
case 1:
|
||||
var y = controller.AxisValue("0Mouse Y");
|
||||
if (LimitAnalogChangeSensitivity)
|
||||
{
|
||||
y = y.Clamp(-10, 10);
|
||||
}
|
||||
|
||||
return (short)y;
|
||||
case 2:
|
||||
return (short)(controller.IsPressed("0Mouse Left") ? 1 : 0);
|
||||
case 3:
|
||||
return (short)(controller.IsPressed("0Mouse Right") ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SnesSuperScopeController : ILibsnesController
|
||||
{
|
||||
public LibsnesApi.SNES_INPUT_PORT PortType => LibsnesApi.SNES_INPUT_PORT.SuperScope;
|
||||
|
||||
private static readonly ControllerDefinition _definition
|
||||
= new ControllerDefinition("(SNES Controller fragment)") { BoolButtons = { "0Trigger", "0Cursor", "0Turbo", "0Pause", "0Offscreen" } }
|
||||
.AddLightGun("0Scope {0}");
|
||||
|
||||
public ControllerDefinition Definition => _definition;
|
||||
|
||||
public short GetState(IController controller, int index, int id)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case 0:
|
||||
if (controller.IsPressed("0Offscreen")) return -1;
|
||||
return (short)controller.AxisValue("0Scope X");
|
||||
case 1:
|
||||
if (controller.IsPressed("0Offscreen")) return -1;
|
||||
return (short)controller.AxisValue("0Scope Y");
|
||||
case 2:
|
||||
return (short)(controller.IsPressed("0Trigger") ? 1 : 0);
|
||||
case 3:
|
||||
return (short)(controller.IsPressed("0Cursor") ? 1 : 0);
|
||||
case 4:
|
||||
return (short)(controller.IsPressed("0Turbo") ? 1 : 0);
|
||||
case 5:
|
||||
return (short)(controller.IsPressed("0Pause") ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SnesJustifierController : ILibsnesController
|
||||
{
|
||||
public LibsnesApi.SNES_INPUT_PORT PortType => LibsnesApi.SNES_INPUT_PORT.Justifier;
|
||||
|
||||
private static readonly ControllerDefinition _definition
|
||||
= new ControllerDefinition("(SNES Controller fragment)") { BoolButtons = { "0Trigger", "0Start", "0Offscreen", "1Trigger", "1Start", "1Offscreen" } }
|
||||
.AddLightGun("0Justifier {0}")
|
||||
.AddLightGun("1Justifier {0}");
|
||||
|
||||
public ControllerDefinition Definition => _definition;
|
||||
|
||||
public short GetState(IController controller, int index, int id)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case 0:
|
||||
if (controller.IsPressed(index + "Offscreen")) return -1;
|
||||
return (short)controller.AxisValue(index + "Justifier X");
|
||||
case 1:
|
||||
if (controller.IsPressed(index + "Offscreen")) return -1;
|
||||
return (short)controller.AxisValue(index + "Justifier Y");
|
||||
case 2:
|
||||
return (short)(controller.IsPressed(index + "Trigger") ? 1 : 0);
|
||||
case 3:
|
||||
return (short)(controller.IsPressed(index + "Start") ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
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)
|
||||
{
|
||||
void AddIfExists(string name, string addAs = null)
|
||||
{
|
||||
var found = _memoryDomains[name];
|
||||
if (found is not null) cdl[addAs ?? name] = new byte[found.Size];
|
||||
}
|
||||
|
||||
cdl["CARTROM"] = new byte[_memoryDomains["CARTROM"]!.Size];
|
||||
cdl["CARTROM-DB"] = new byte[_memoryDomains["CARTROM"]!.Size];
|
||||
cdl["CARTROM-D"] = new byte[_memoryDomains["CARTROM"]!.Size*2];
|
||||
cdl["WRAM"] = new byte[_memoryDomains["WRAM"]!.Size];
|
||||
cdl["APURAM"] = new byte[_memoryDomains["APURAM"]!.Size];
|
||||
AddIfExists("CARTRAM");
|
||||
|
||||
if (IsSGB)
|
||||
{
|
||||
cdl["SGB_CARTROM"] = new byte[_memoryDomains["SGB CARTROM"]!.Size];
|
||||
cdl["SGB_HRAM"] = new byte[_memoryDomains["SGB HRAM"]!.Size];
|
||||
cdl["SGB_WRAM"] = new byte[_memoryDomains["SGB WRAM"]!.Size];
|
||||
AddIfExists("SGB CARTRAM", addAs: "SGB_CARTRAM");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||
{
|
||||
public partial class LibsnesCore : IDebuggable
|
||||
{
|
||||
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
|
||||
{
|
||||
Api.QUERY_peek_cpu_regs(out var 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<string, RegisterValue>
|
||||
{
|
||||
["PC"] = regs.pc,
|
||||
["A"] = regs.a,
|
||||
["X"] = regs.x,
|
||||
["Y"] = regs.y,
|
||||
["S"] = regs.s,
|
||||
["D"] = regs.d,
|
||||
["Vector"] = regs.vector,
|
||||
["P"] = regs.p,
|
||||
["Flag N"] = fn,
|
||||
["Flag V"] = fv,
|
||||
["Flag M"] = fm,
|
||||
["Flag X"] = fx,
|
||||
["Flag D"] = fd,
|
||||
["Flag I"] = fi,
|
||||
["Flag Z"] = fz,
|
||||
["Flag C"] = fc,
|
||||
["V"] = regs.v,
|
||||
["H"] = regs.h
|
||||
};
|
||||
}
|
||||
|
||||
[FeatureNotImplemented]
|
||||
public void SetCpuRegister(string register, int value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IMemoryCallbackSystem MemoryCallbacks { get; } = new MemoryCallbackSystem(new[] { "System Bus" });
|
||||
|
||||
public bool CanStep(StepType type)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
[FeatureNotImplemented]
|
||||
public void Step(StepType type)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[FeatureNotImplemented]
|
||||
public long TotalExecutedCycles => throw new NotImplementedException();
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||
{
|
||||
public partial class LibsnesCore : IEmulator
|
||||
{
|
||||
public IEmulatorServiceProvider ServiceProvider { get; }
|
||||
|
||||
public ControllerDefinition ControllerDefinition => _controllerDeck.Definition;
|
||||
|
||||
public bool FrameAdvance(IController controller, bool render, bool renderSound)
|
||||
{
|
||||
_controller = controller;
|
||||
|
||||
/* if the input poll callback is called, it will set this to false
|
||||
* this has to be done before we save the per-frame state in deterministic
|
||||
* mode, because in there, the core actually advances, and might advance
|
||||
* through the point in time where IsLagFrame gets set to false. makes sense?
|
||||
*/
|
||||
|
||||
IsLagFrame = true;
|
||||
|
||||
if (_tracer.IsEnabled())
|
||||
{
|
||||
//Api.QUERY_set_trace_callback(1<<(int)LibsnesApi.eTRACE.SMP, _tracecb); //TEST -- it works but theres no way to control it from the frontend now
|
||||
|
||||
if(IsSGB)
|
||||
Api.QUERY_set_trace_callback(1<<(int)LibsnesApi.eTRACE.GB, _tracecb);
|
||||
else
|
||||
Api.QUERY_set_trace_callback(1<<(int)LibsnesApi.eTRACE.CPU, _tracecb);
|
||||
}
|
||||
else
|
||||
{
|
||||
Api.QUERY_set_trace_callback(0,null);
|
||||
}
|
||||
|
||||
// speedup when sound rendering is not needed
|
||||
Api.QUERY_set_audio_sample(renderSound ? _soundcb : null);
|
||||
|
||||
bool resetSignal = controller.IsPressed("Reset");
|
||||
if (resetSignal)
|
||||
{
|
||||
Api.CMD_reset();
|
||||
}
|
||||
|
||||
bool powerSignal = controller.IsPressed("Power");
|
||||
if (powerSignal)
|
||||
{
|
||||
Api.CMD_power();
|
||||
}
|
||||
|
||||
var enables = new LibsnesApi.LayerEnables
|
||||
{
|
||||
BG1_Prio0 = _settings.ShowBG1_0,
|
||||
BG1_Prio1 = _settings.ShowBG1_1,
|
||||
BG2_Prio0 = _settings.ShowBG2_0,
|
||||
BG2_Prio1 = _settings.ShowBG2_1,
|
||||
BG3_Prio0 = _settings.ShowBG3_0,
|
||||
BG3_Prio1 = _settings.ShowBG3_1,
|
||||
BG4_Prio0 = _settings.ShowBG4_0,
|
||||
BG4_Prio1 = _settings.ShowBG4_1,
|
||||
Obj_Prio0 = _settings.ShowOBJ_0,
|
||||
Obj_Prio1 = _settings.ShowOBJ_1,
|
||||
Obj_Prio2 = _settings.ShowOBJ_2,
|
||||
Obj_Prio3 = _settings.ShowOBJ_3
|
||||
};
|
||||
|
||||
Api.SetLayerEnables(ref enables);
|
||||
|
||||
RefreshMemoryCallbacks(false);
|
||||
|
||||
// apparently this is one frame?
|
||||
Api.CMD_run();
|
||||
_timeFrameCounter++;
|
||||
|
||||
// once upon a time we forwarded messages from bsnes here, by checking for queued text messages, but I don't think it's needed any longer
|
||||
if (IsLagFrame)
|
||||
{
|
||||
LagCount++;
|
||||
}
|
||||
|
||||
if (renderSound)
|
||||
{
|
||||
ProcessSoundEnd();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int Frame
|
||||
{
|
||||
get => _timeFrameCounter;
|
||||
private set => _timeFrameCounter = value;
|
||||
}
|
||||
|
||||
public string SystemId { get; }
|
||||
public bool DeterministicEmulation => true;
|
||||
|
||||
public void ResetCounters()
|
||||
{
|
||||
_timeFrameCounter = 0;
|
||||
LagCount = 0;
|
||||
IsLagFrame = false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Api.Dispose();
|
||||
_blipL.Dispose();
|
||||
_blipR.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
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();
|
||||
}
|
||||
}
|
|
@ -1,227 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||
{
|
||||
public partial class LibsnesCore
|
||||
{
|
||||
private readonly List<MemoryDomain> _memoryDomainList = new List<MemoryDomain>();
|
||||
private IMemoryDomains _memoryDomains;
|
||||
private LibsnesApi.SNES_MAPPER? _mapper;
|
||||
private LibsnesApi.SNES_REGION? _region;
|
||||
|
||||
// works for WRAM, garbage for anything else
|
||||
private static int? FakeBusMap(int addr)
|
||||
{
|
||||
addr &= 0xffffff;
|
||||
int bank = addr >> 16;
|
||||
if (bank == 0x7e || bank == 0x7f)
|
||||
{
|
||||
return addr & 0x1ffff;
|
||||
}
|
||||
|
||||
bank &= 0x7f;
|
||||
int low = addr & 0xffff;
|
||||
if (bank < 0x40 && low < 0x2000)
|
||||
{
|
||||
return low;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void SetupMemoryDomains(byte[] romData, byte[] sgbRomData)
|
||||
{
|
||||
MakeMemoryDomain("WRAM", LibsnesApi.SNES_MEMORY.WRAM, MemoryDomain.Endian.Little);
|
||||
MakeMemoryDomain("CARTROM", LibsnesApi.SNES_MEMORY.CARTRIDGE_ROM, MemoryDomain.Endian.Little, byteSize: 2); //there are signs this doesnt work on SGB?
|
||||
MakeMemoryDomain("CARTRAM", LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM, MemoryDomain.Endian.Little, byteSize: 2);
|
||||
MakeMemoryDomain("SA1 IRAM", LibsnesApi.SNES_MEMORY.SA1_IRAM, MemoryDomain.Endian.Little, byteSize: 2);
|
||||
MakeMemoryDomain("VRAM", LibsnesApi.SNES_MEMORY.VRAM, MemoryDomain.Endian.Little, byteSize: 2);
|
||||
MakeMemoryDomain("OAM", LibsnesApi.SNES_MEMORY.OAM, MemoryDomain.Endian.Little, byteSize: 2);
|
||||
MakeMemoryDomain("CGRAM", LibsnesApi.SNES_MEMORY.CGRAM, MemoryDomain.Endian.Little, byteSize: 2);
|
||||
MakeMemoryDomain("APURAM", LibsnesApi.SNES_MEMORY.APURAM, MemoryDomain.Endian.Little, byteSize: 2);
|
||||
|
||||
if (!DeterministicEmulation)
|
||||
{
|
||||
_memoryDomainList.Add(new MemoryDomainDelegate(
|
||||
"System Bus",
|
||||
0x1000000,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => Api.QUERY_peek(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr),
|
||||
(addr, val) => Api.QUERY_poke(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr, val), wordSize: 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
// limited function bus
|
||||
MakeFakeBus();
|
||||
}
|
||||
|
||||
if (IsSGB)
|
||||
{
|
||||
// NOTE: CGB has 32K of wram, and DMG has 8KB of wram. Not sure how to control this right now.. bsnes might not have any ready way of doign that? I couldnt spot it.
|
||||
// You wouldnt expect a DMG game to access excess wram, but what if it tried to? maybe an oversight in bsnes?
|
||||
MakeMemoryDomain("SGB WRAM", LibsnesApi.SNES_MEMORY.SGB_WRAM, MemoryDomain.Endian.Little);
|
||||
|
||||
//uhhh why can't this be done with MakeMemoryDomain? improve that.
|
||||
var romDomain = new MemoryDomainByteArray("SGB CARTROM", MemoryDomain.Endian.Little, romData, true, 1);
|
||||
_memoryDomainList.Add(romDomain);
|
||||
|
||||
// the last 1 byte of this is special.. its an interrupt enable register, instead of ram. weird. maybe its actually ram and just getting specially used?
|
||||
MakeMemoryDomain("SGB HRAM", LibsnesApi.SNES_MEMORY.SGB_HRAM, MemoryDomain.Endian.Little);
|
||||
|
||||
MakeMemoryDomain("SGB CARTRAM", LibsnesApi.SNES_MEMORY.SGB_CARTRAM, MemoryDomain.Endian.Little);
|
||||
}
|
||||
|
||||
_memoryDomainList.Add(Api.GetPagesDomain());
|
||||
|
||||
_memoryDomains = new MemoryDomainList(_memoryDomainList);
|
||||
((BasicServiceProvider) ServiceProvider).Register(_memoryDomains);
|
||||
}
|
||||
|
||||
private unsafe void MakeMemoryDomain(string name, LibsnesApi.SNES_MEMORY id, MemoryDomain.Endian endian, int byteSize = 1)
|
||||
{
|
||||
int size = Api.QUERY_get_memory_size(id);
|
||||
|
||||
// if this type of memory isn't available, don't make the memory domain (most commonly save ram)
|
||||
if (size == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
byte* blockPtr = Api.QUERY_get_memory_data(id);
|
||||
|
||||
var md = new MemoryDomainIntPtrMonitor(name, MemoryDomain.Endian.Little, (IntPtr)blockPtr, size,
|
||||
true,
|
||||
byteSize, Api);
|
||||
|
||||
_memoryDomainList.Add(md);
|
||||
}
|
||||
|
||||
private unsafe void MakeFakeBus()
|
||||
{
|
||||
int size = Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.WRAM);
|
||||
if (size != 0x20000)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
byte* blockPtr = Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.WRAM);
|
||||
|
||||
var md = new MemoryDomainDelegate("System Bus", 0x1000000, MemoryDomain.Endian.Little,
|
||||
addr =>
|
||||
{
|
||||
using (Api.EnterExit())
|
||||
{
|
||||
var a = FakeBusMap((int)addr);
|
||||
if (a.HasValue)
|
||||
{
|
||||
return blockPtr[a.Value];
|
||||
}
|
||||
|
||||
return FakeBusRead((int)addr);
|
||||
}
|
||||
},
|
||||
(addr, val) =>
|
||||
{
|
||||
using (Api.EnterExit())
|
||||
{
|
||||
var a = FakeBusMap((int)addr);
|
||||
if (a.HasValue)
|
||||
blockPtr[a.Value] = val;
|
||||
}
|
||||
}, wordSize: 2);
|
||||
_memoryDomainList.Add(md);
|
||||
}
|
||||
|
||||
// works for ROM, garbage for anything else
|
||||
private byte FakeBusRead(int addr)
|
||||
{
|
||||
addr &= 0xffffff;
|
||||
int bank = addr >> 16;
|
||||
int low = addr & 0xffff;
|
||||
|
||||
if (!_mapper.HasValue)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (_mapper)
|
||||
{
|
||||
case LibsnesApi.SNES_MAPPER.LOROM:
|
||||
if (low >= 0x8000)
|
||||
{
|
||||
return Api.QUERY_peek(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr);
|
||||
}
|
||||
|
||||
break;
|
||||
case LibsnesApi.SNES_MAPPER.EXLOROM:
|
||||
if ((bank >= 0x40 && bank <= 0x7f) || low >= 0x8000)
|
||||
{
|
||||
return Api.QUERY_peek(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr);
|
||||
}
|
||||
|
||||
break;
|
||||
case LibsnesApi.SNES_MAPPER.HIROM:
|
||||
case LibsnesApi.SNES_MAPPER.EXHIROM:
|
||||
if ((bank >= 0x40 && bank <= 0x7f) || bank >= 0xc0 || low >= 0x8000)
|
||||
{
|
||||
return Api.QUERY_peek(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr);
|
||||
}
|
||||
|
||||
break;
|
||||
case LibsnesApi.SNES_MAPPER.SUPERFXROM:
|
||||
if (bank is (>= 0x40 and <= 0x5F) or (>= 0xC0 and <= 0xDF)
|
||||
|| (low >= 0x8000 && bank is (>= 0x00 and <= 0x3F) or (>= 0x80 and <= 0xBF)))
|
||||
{
|
||||
return Api.QUERY_peek(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr);
|
||||
}
|
||||
|
||||
break;
|
||||
case LibsnesApi.SNES_MAPPER.SA1ROM:
|
||||
if (bank >= 0xc0 || (low >= 0x8000 && ((bank >= 0x00 && bank <= 0x3f) || (bank >= 0x80 && bank <= 0xbf))))
|
||||
{
|
||||
return Api.QUERY_peek(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr);
|
||||
}
|
||||
|
||||
break;
|
||||
case LibsnesApi.SNES_MAPPER.BSCLOROM:
|
||||
if (low >= 0x8000 && ((bank >= 0x00 && bank <= 0x3f) || (bank >= 0x80 && bank <= 0xbf)))
|
||||
{
|
||||
return Api.QUERY_peek(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr);
|
||||
}
|
||||
|
||||
break;
|
||||
case LibsnesApi.SNES_MAPPER.BSCHIROM:
|
||||
if (bank is (>= 0x40 and <= 0x5F) or (>= 0xC0 and <= 0xDF)
|
||||
|| (low >= 0x8000 && bank is (>= 0x00 and <= 0x1F) or (>= 0x80 and <= 0x9F)))
|
||||
{
|
||||
return Api.QUERY_peek(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr);
|
||||
}
|
||||
|
||||
break;
|
||||
case LibsnesApi.SNES_MAPPER.BSXROM:
|
||||
if (bank is (>= 0x40 and <= 0x7F) or >= 0xC0
|
||||
|| (low >= 0x8000 && bank is (>= 0x00 and <= 0x3F) or (>= 0x80 and <= 0xBF))
|
||||
|| (low is >= 0x6000 and <= 0x7FFF && bank is >= 0x20 and <= 0x3F))
|
||||
{
|
||||
return Api.QUERY_peek(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr);
|
||||
}
|
||||
|
||||
break;
|
||||
case LibsnesApi.SNES_MAPPER.STROM:
|
||||
if (low >= 0x8000 && ((bank >= 0x00 && bank <= 0x5f) || (bank >= 0x80 && bank <= 0xdf)))
|
||||
{
|
||||
return Api.QUERY_peek(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Unknown mapper: {_mapper}");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||
{
|
||||
public partial class LibsnesCore : IRegionable
|
||||
{
|
||||
public DisplayType Region => _region == LibsnesApi.SNES_REGION.NTSC
|
||||
? DisplayType.NTSC
|
||||
: DisplayType.PAL;
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.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()
|
||||
{
|
||||
using (Api.EnterExit())
|
||||
{
|
||||
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 && Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM) > 0)
|
||||
{
|
||||
buf = Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.SGB_CARTRAM);
|
||||
size = Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM);
|
||||
}
|
||||
|
||||
if (buf == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var ret = new byte[size];
|
||||
Marshal.Copy((IntPtr)buf, ret, 0, size);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public void StoreSaveRam(byte[] data)
|
||||
{
|
||||
using (Api.EnterExit())
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||
{
|
||||
public partial class LibsnesCore : ISettable<LibsnesCore.SnesSettings, LibsnesCore.SnesSyncSettings>
|
||||
{
|
||||
public SnesSettings GetSettings()
|
||||
{
|
||||
return _settings.Clone();
|
||||
}
|
||||
|
||||
IBSNESForGfxDebugger.SettingsObj IBSNESForGfxDebugger.GetSettings()
|
||||
=> GetSettings();
|
||||
|
||||
public SnesSyncSettings GetSyncSettings()
|
||||
{
|
||||
return _syncSettings.Clone();
|
||||
}
|
||||
|
||||
public PutSettingsDirtyBits PutSettings(SnesSettings o)
|
||||
{
|
||||
bool refreshNeeded = o.Palette != _settings.Palette;
|
||||
_settings = o;
|
||||
if (refreshNeeded)
|
||||
{
|
||||
RefreshPalette();
|
||||
}
|
||||
|
||||
return PutSettingsDirtyBits.None;
|
||||
}
|
||||
|
||||
void IBSNESForGfxDebugger.PutSettings(IBSNESForGfxDebugger.SettingsObj s)
|
||||
=> PutSettings((SnesSettings) s);
|
||||
|
||||
public PutSettingsDirtyBits PutSyncSettings(SnesSyncSettings o)
|
||||
{
|
||||
bool ret = o.LeftPort != _syncSettings.LeftPort
|
||||
|| o.RightPort != _syncSettings.RightPort
|
||||
|| o.LimitAnalogChangeSensitivity != _syncSettings.LimitAnalogChangeSensitivity
|
||||
|| o.RandomizedInitialState != _syncSettings.RandomizedInitialState;
|
||||
|
||||
_syncSettings = o;
|
||||
return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None;
|
||||
}
|
||||
|
||||
private SnesSettings _settings;
|
||||
private SnesSyncSettings _syncSettings;
|
||||
|
||||
public class SnesSettings : IBSNESForGfxDebugger.SettingsObj
|
||||
{
|
||||
public bool ShowBG1_0 { get; set; } = true;
|
||||
public bool ShowBG2_0 { get; set; } = true;
|
||||
public bool ShowBG3_0 { get; set; } = true;
|
||||
public bool ShowBG4_0 { get; set; } = true;
|
||||
public bool ShowBG1_1 { get; set; } = true;
|
||||
public bool ShowBG2_1 { get; set; } = true;
|
||||
public bool ShowBG3_1 { get; set; } = true;
|
||||
public bool ShowBG4_1 { get; set; } = true;
|
||||
public bool ShowOBJ_0 { get; set; } = true;
|
||||
public bool ShowOBJ_1 { get; set; } = true;
|
||||
public bool ShowOBJ_2 { get; set; } = true;
|
||||
public bool ShowOBJ_3 { get; set; } = true;
|
||||
|
||||
public bool CropSGBFrame { get; set; } = false;
|
||||
public bool AlwaysDoubleSize { get; set; } = false;
|
||||
public string Palette { get; set; } = "BizHawk";
|
||||
|
||||
public SnesSettings Clone()
|
||||
{
|
||||
return (SnesSettings)MemberwiseClone();
|
||||
}
|
||||
}
|
||||
|
||||
public class SnesSyncSettings
|
||||
{
|
||||
public LibsnesControllerDeck.ControllerType LeftPort { get; set; } = LibsnesControllerDeck.ControllerType.Gamepad;
|
||||
public LibsnesControllerDeck.ControllerType RightPort { get; set; } = LibsnesControllerDeck.ControllerType.Unplugged;
|
||||
|
||||
public bool LimitAnalogChangeSensitivity { get; set; } = true;
|
||||
|
||||
public bool RandomizedInitialState { get; set; } = true;
|
||||
|
||||
public SnesSyncSettings Clone()
|
||||
{
|
||||
return (SnesSyncSettings)MemberwiseClone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||
{
|
||||
public partial class LibsnesCore : ISoundProvider
|
||||
{
|
||||
private readonly BlipBuffer _blipL = new(4096);
|
||||
private readonly BlipBuffer _blipR = new(4096);
|
||||
private short _latchL, _latchR;
|
||||
private readonly short[] _sampleBuffer = new short[4096];
|
||||
private uint _inSamps;
|
||||
private int _outSamps;
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
_outSamps = 0;
|
||||
}
|
||||
|
||||
public void GetSamplesSync(out short[] samples, out int nsamp)
|
||||
{
|
||||
samples = _sampleBuffer;
|
||||
nsamp = _outSamps;
|
||||
DiscardSamples();
|
||||
}
|
||||
|
||||
private void InitAudio()
|
||||
{
|
||||
_blipL.SetRates(32040.5, 44100);
|
||||
_blipR.SetRates(32040.5, 44100);
|
||||
}
|
||||
|
||||
private void snes_audio_sample(short left, short right)
|
||||
{
|
||||
if (_latchL != left)
|
||||
{
|
||||
_blipL.AddDelta(_inSamps, _latchL - left);
|
||||
_latchL = left;
|
||||
}
|
||||
|
||||
if (_latchR != right)
|
||||
{
|
||||
_blipR.AddDelta(_inSamps, _latchR - right);
|
||||
_latchR = right;
|
||||
}
|
||||
|
||||
_inSamps++;
|
||||
}
|
||||
|
||||
private void ProcessSoundEnd()
|
||||
{
|
||||
_blipL.EndFrame(_inSamps);
|
||||
_blipR.EndFrame(_inSamps);
|
||||
_inSamps = 0;
|
||||
|
||||
_outSamps = _blipL.SamplesAvailable();
|
||||
if (_outSamps != _blipR.SamplesAvailable())
|
||||
{
|
||||
throw new InvalidOperationException("Audio processing error");
|
||||
}
|
||||
|
||||
_blipL.ReadSamplesLeft(_sampleBuffer, _outSamps);
|
||||
_blipR.ReadSamplesRight(_sampleBuffer, _outSamps);
|
||||
}
|
||||
|
||||
public bool CanProvideAsync => false;
|
||||
|
||||
public void GetSamplesAsync(short[] samples)
|
||||
{
|
||||
throw new InvalidOperationException("Async mode is not supported.");
|
||||
}
|
||||
|
||||
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
|
||||
|
||||
public void SetSyncMode(SyncSoundMode mode)
|
||||
{
|
||||
if (mode == SyncSoundMode.Async)
|
||||
{
|
||||
throw new NotSupportedException("Async mode is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
using System.IO;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||
{
|
||||
public partial class LibsnesCore : IStatable
|
||||
{
|
||||
public bool AvoidRewind => false;
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
Api.SaveStateBinary(writer);
|
||||
writer.Write(IsLagFrame);
|
||||
writer.Write(LagCount);
|
||||
writer.Write(Frame);
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
Api.LoadStateBinary(reader);
|
||||
IsLagFrame = reader.ReadBoolean();
|
||||
LagCount = reader.ReadInt32();
|
||||
Frame = reader.ReadInt32();
|
||||
Api.QUERY_set_cdl(_currCdl);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||
{
|
||||
public partial class LibsnesCore : IVideoProvider
|
||||
{
|
||||
public int VirtualWidth { get; private set; } = 293;
|
||||
|
||||
public int VirtualHeight { get; private set; } = 224;
|
||||
|
||||
public int BufferWidth => _videoWidth;
|
||||
|
||||
public int BufferHeight => _videoHeight;
|
||||
|
||||
public int BackgroundColor => 0;
|
||||
|
||||
public int[] GetVideoBuffer() => _videoBuffer;
|
||||
|
||||
public int VsyncNumerator { get; }
|
||||
public int VsyncDenominator { get; }
|
||||
|
||||
private int[] _videoBuffer = new int[256 * 224];
|
||||
private int _videoWidth = 256;
|
||||
private int _videoHeight = 224;
|
||||
}
|
||||
}
|
|
@ -1,631 +0,0 @@
|
|||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.IO;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Common.PathExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Components.W65816;
|
||||
|
||||
// TODO - add serializer (?)
|
||||
|
||||
// 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
|
||||
// edit - this is a lot of work
|
||||
// wrap dll code around some kind of library-accessing interface so that it doesn't malfunction if the dll is unavailable
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||
{
|
||||
[PortedCore(CoreNames.Bsnes, "byuu", "v87", "https://github.com/bsnes-emu/bsnes/tree/v087")]
|
||||
public unsafe partial class LibsnesCore : IEmulator, IVideoProvider, ISaveRam, IStatable, IInputPollable, IRegionable, ICodeDataLogger,
|
||||
IDebuggable, ISettable<LibsnesCore.SnesSettings, LibsnesCore.SnesSyncSettings>, IBSNESForGfxDebugger
|
||||
{
|
||||
[CoreConstructor(VSystemID.Raw.SGB)]
|
||||
[CoreConstructor(VSystemID.Raw.SNES)]
|
||||
public LibsnesCore(GameInfo game, byte[] rom, CoreComm comm,
|
||||
LibsnesCore.SnesSettings settings, LibsnesCore.SnesSyncSettings syncSettings)
|
||||
:this(game, rom, null, null, comm, settings, syncSettings)
|
||||
{}
|
||||
|
||||
public LibsnesCore(GameInfo game, byte[] romData, byte[] xmlData, string baseRomPath, CoreComm comm,
|
||||
LibsnesCore.SnesSettings settings, LibsnesCore.SnesSyncSettings syncSettings)
|
||||
{
|
||||
_baseRomPath = baseRomPath;
|
||||
var ser = new BasicServiceProvider(this);
|
||||
ServiceProvider = ser;
|
||||
|
||||
const string TRACE_HEADER = "65816: PC, mnemonic, operands, registers (A, X, Y, S, D, DB, flags (NVMXDIZC), V, H)";
|
||||
_tracer = new TraceBuffer(TRACE_HEADER);
|
||||
|
||||
ser.Register<IDisassemblable>(new W65816_DisassemblerService());
|
||||
|
||||
_game = game;
|
||||
CoreComm = comm;
|
||||
byte[] sgbRomData = null;
|
||||
|
||||
if (game.System == VSystemID.Raw.SGB)
|
||||
{
|
||||
if ((romData[0x143] & 0xc0) == 0xc0)
|
||||
{
|
||||
throw new CGBNotSupportedException();
|
||||
}
|
||||
|
||||
sgbRomData = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("SNES", "Rom_SGB"), "SGB Rom is required for SGB emulation.");
|
||||
game.FirmwareHash = SHA1Checksum.ComputeDigestHex(sgbRomData);
|
||||
}
|
||||
|
||||
_settings = settings ?? new SnesSettings();
|
||||
_syncSettings = syncSettings ?? new SnesSyncSettings();
|
||||
|
||||
_videocb = snes_video_refresh;
|
||||
_inputpollcb = snes_input_poll;
|
||||
_inputstatecb = snes_input_state;
|
||||
_inputnotifycb = snes_input_notify;
|
||||
_scanlineStartCb = snes_scanlineStart;
|
||||
_tracecb = snes_trace;
|
||||
_soundcb = snes_audio_sample;
|
||||
_pathrequestcb = snes_path_request;
|
||||
|
||||
// TODO: pass profile here
|
||||
Api = new(PathUtils.DllDirectoryPath, CoreComm, new Delegate[]
|
||||
{
|
||||
_videocb,
|
||||
_inputpollcb,
|
||||
_inputstatecb,
|
||||
_inputnotifycb,
|
||||
_scanlineStartCb,
|
||||
_tracecb,
|
||||
_soundcb,
|
||||
_pathrequestcb
|
||||
})
|
||||
{
|
||||
ReadHook = ReadHook,
|
||||
ExecHook = ExecHook,
|
||||
WriteHook = WriteHook,
|
||||
ReadHook_SMP = ReadHook_SMP,
|
||||
ExecHook_SMP = ExecHook_SMP,
|
||||
WriteHook_SMP = WriteHook_SMP,
|
||||
};
|
||||
|
||||
ScanlineHookManager = new MyScanlineHookManager(this);
|
||||
|
||||
_controllerDeck = new LibsnesControllerDeck(_syncSettings);
|
||||
_controllerDeck.NativeInit(Api);
|
||||
|
||||
Api.CMD_init(_syncSettings.RandomizedInitialState);
|
||||
|
||||
Api.QUERY_set_path_request(_pathrequestcb);
|
||||
|
||||
// start up audio resampler
|
||||
InitAudio();
|
||||
|
||||
// strip header
|
||||
if ((romData?.Length & 0x7FFF) == 512)
|
||||
{
|
||||
var newData = new byte[romData.Length - 512];
|
||||
Array.Copy(romData, 512, newData, 0, newData.Length);
|
||||
romData = newData;
|
||||
}
|
||||
|
||||
if (game.System == VSystemID.Raw.SGB)
|
||||
{
|
||||
IsSGB = true;
|
||||
SystemId = VSystemID.Raw.SNES;
|
||||
ser.Register<IBoardInfo>(new SGBBoardInfo());
|
||||
|
||||
_currLoadParams = new LoadParams
|
||||
{
|
||||
type = LoadParamType.SuperGameBoy,
|
||||
rom_xml = null,
|
||||
rom_data = sgbRomData,
|
||||
rom_size = (uint)sgbRomData.Length,
|
||||
dmg_data = romData,
|
||||
};
|
||||
|
||||
if (!LoadCurrent())
|
||||
{
|
||||
throw new Exception("snes_load_cartridge_normal() failed");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we may need to get some information out of the cart, even during the following bootup/load process
|
||||
if (xmlData != null)
|
||||
{
|
||||
_romxml = new XmlDocument();
|
||||
_romxml.Load(new MemoryStream(xmlData));
|
||||
|
||||
// bsnes wont inspect the xml to load the necessary sfc file.
|
||||
// so, we have to do that here and pass it in as the romData :/
|
||||
if (_romxml["cartridge"]?["rom"] != null)
|
||||
{
|
||||
romData = File.ReadAllBytes(PathSubfile(_romxml["cartridge"]["rom"].Attributes["name"].Value));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Could not find rom file specification in xml file. Please check the integrity of your xml file");
|
||||
}
|
||||
}
|
||||
|
||||
SystemId = VSystemID.Raw.SNES;
|
||||
_currLoadParams = new LoadParams
|
||||
{
|
||||
type = LoadParamType.Normal,
|
||||
xml_data = xmlData,
|
||||
rom_data = romData
|
||||
};
|
||||
|
||||
if (!LoadCurrent())
|
||||
{
|
||||
throw new Exception("snes_load_cartridge_normal() failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (_region == LibsnesApi.SNES_REGION.NTSC)
|
||||
{
|
||||
// similar to what aviout reports from snes9x and seems logical from bsnes first principles. bsnes uses that numerator (ntsc master clockrate) for sure.
|
||||
VsyncNumerator = 21477272;
|
||||
VsyncDenominator = 4 * 341 * 262;
|
||||
}
|
||||
else
|
||||
{
|
||||
// http://forums.nesdev.com/viewtopic.php?t=5367&start=19
|
||||
VsyncNumerator = 21281370;
|
||||
VsyncDenominator = 4 * 341 * 312;
|
||||
}
|
||||
|
||||
Api.CMD_power();
|
||||
|
||||
SetupMemoryDomains(romData, sgbRomData);
|
||||
|
||||
if (CurrentProfile == "Compatibility")
|
||||
{
|
||||
ser.Register<ITraceable>(_tracer);
|
||||
}
|
||||
|
||||
Api.QUERY_set_path_request(null);
|
||||
Api.QUERY_set_video_refresh(_videocb);
|
||||
Api.QUERY_set_input_poll(_inputpollcb);
|
||||
Api.QUERY_set_input_state(_inputstatecb);
|
||||
Api.QUERY_set_input_notify(_inputnotifycb);
|
||||
Api.QUERY_set_audio_sample(_soundcb);
|
||||
Api.Seal();
|
||||
RefreshPalette();
|
||||
}
|
||||
|
||||
private readonly LibsnesApi.snes_video_refresh_t _videocb;
|
||||
private readonly LibsnesApi.snes_input_poll_t _inputpollcb;
|
||||
private readonly LibsnesApi.snes_input_state_t _inputstatecb;
|
||||
private readonly LibsnesApi.snes_input_notify_t _inputnotifycb;
|
||||
private readonly LibsnesApi.snes_path_request_t _pathrequestcb;
|
||||
|
||||
internal CoreComm CoreComm { get; }
|
||||
|
||||
private readonly string _baseRomPath = "";
|
||||
|
||||
private string PathSubfile(string fname) => Path.Combine(_baseRomPath, fname);
|
||||
|
||||
private readonly GameInfo _game;
|
||||
private readonly LibsnesControllerDeck _controllerDeck;
|
||||
private readonly ITraceable _tracer;
|
||||
private readonly XmlDocument _romxml;
|
||||
private readonly LibsnesApi.snes_scanlineStart_t _scanlineStartCb;
|
||||
private readonly LibsnesApi.snes_trace_t _tracecb;
|
||||
private readonly LibsnesApi.snes_audio_sample_t _soundcb;
|
||||
|
||||
private IController _controller;
|
||||
private readonly LoadParams _currLoadParams;
|
||||
private int _timeFrameCounter;
|
||||
private bool _disposed;
|
||||
|
||||
public bool IsSGB { get; }
|
||||
|
||||
private class SGBBoardInfo : IBoardInfo
|
||||
{
|
||||
public string BoardName => "SGB";
|
||||
}
|
||||
|
||||
public string CurrentProfile => "Compatibility"; // We no longer support performance, and accuracy isn't worth the effort so we shall just hardcode this one
|
||||
|
||||
public LibsnesApi Api { get; }
|
||||
|
||||
public SnesColors.ColorType CurrPalette { get; private set; }
|
||||
|
||||
public void SetPalette(SnesColors.ColorType palette)
|
||||
{
|
||||
var s = GetSettings();
|
||||
s.Palette = Enum.GetName(typeof(SnesColors.ColorType), palette);
|
||||
PutSettings(s);
|
||||
}
|
||||
|
||||
public ISNESGraphicsDecoder CreateGraphicsDecoder()
|
||||
=> new SNESGraphicsDecoder(Api, CurrPalette);
|
||||
|
||||
public ScanlineHookManager ScanlineHookManager { get; }
|
||||
|
||||
public class MyScanlineHookManager : ScanlineHookManager
|
||||
{
|
||||
private readonly LibsnesCore _core;
|
||||
|
||||
public MyScanlineHookManager(LibsnesCore core)
|
||||
{
|
||||
_core = core;
|
||||
}
|
||||
|
||||
protected override void OnHooksChanged()
|
||||
{
|
||||
_core.OnScanlineHooksChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnScanlineHooksChanged()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Api.QUERY_set_scanlineStart(ScanlineHookManager.HookCount == 0 ? null : _scanlineStartCb);
|
||||
}
|
||||
|
||||
private void snes_scanlineStart(int line)
|
||||
{
|
||||
ScanlineHookManager.HandleScanline(line);
|
||||
}
|
||||
|
||||
private string snes_path_request(int slot, string hint)
|
||||
{
|
||||
// every rom requests msu1.rom... why? who knows.
|
||||
// also handle msu-1 pcm files here
|
||||
bool isMsu1Rom = hint == "msu1.rom";
|
||||
bool isMsu1Pcm = Path.GetExtension(hint).ToLowerInvariant() == ".pcm";
|
||||
if (isMsu1Rom || isMsu1Pcm)
|
||||
{
|
||||
// well, check if we have an msu-1 xml
|
||||
if (_romxml?["cartridge"]?["msu1"] != null)
|
||||
{
|
||||
var msu1 = _romxml["cartridge"]["msu1"];
|
||||
if (isMsu1Rom && msu1["rom"]?.Attributes["name"] != null)
|
||||
{
|
||||
return PathSubfile(msu1["rom"].Attributes["name"].Value);
|
||||
}
|
||||
|
||||
if (isMsu1Pcm)
|
||||
{
|
||||
// return @"D:\roms\snes\SuperRoadBlaster\SuperRoadBlaster-1.pcm";
|
||||
// return "";
|
||||
int wantsTrackNumber = int.Parse(hint.Replace("track-", "").Replace(".pcm", ""));
|
||||
wantsTrackNumber++;
|
||||
string wantsTrackString = wantsTrackNumber.ToString();
|
||||
foreach (var child in msu1.ChildNodes.Cast<XmlNode>())
|
||||
{
|
||||
if (child.Name == "track" && child.Attributes["number"].Value == wantsTrackString)
|
||||
{
|
||||
return PathSubfile(child.Attributes["name"].Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not found.. what to do? (every rom will get here when msu1.rom is requested)
|
||||
return "";
|
||||
}
|
||||
|
||||
// not MSU-1. ok.
|
||||
string firmwareId;
|
||||
|
||||
switch (hint)
|
||||
{
|
||||
case "cx4.rom": firmwareId = "CX4"; break;
|
||||
case "dsp1.rom": firmwareId = "DSP1"; break;
|
||||
case "dsp1b.rom": firmwareId = "DSP1b"; break;
|
||||
case "dsp2.rom": firmwareId = "DSP2"; break;
|
||||
case "dsp3.rom": firmwareId = "DSP3"; break;
|
||||
case "dsp4.rom": firmwareId = "DSP4"; break;
|
||||
case "st010.rom": firmwareId = "ST010"; break;
|
||||
case "st011.rom": firmwareId = "ST011"; break;
|
||||
case "st018.rom": firmwareId = "ST018"; break;
|
||||
default:
|
||||
CoreComm.ShowMessage($"Unrecognized SNES firmware request \"{hint}\".");
|
||||
return "";
|
||||
}
|
||||
|
||||
string ret;
|
||||
var data = CoreComm.CoreFileProvider.GetFirmware(new("SNES", firmwareId), "Game may function incorrectly without the requested firmware.");
|
||||
if (data != null)
|
||||
{
|
||||
ret = hint;
|
||||
Api.AddReadonlyFile(data, hint);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = "";
|
||||
}
|
||||
|
||||
Console.WriteLine("Served libsnes request for firmware \"{0}\"", hint);
|
||||
|
||||
// return the path we built
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void snes_trace(uint which, string msg)
|
||||
{
|
||||
// TODO: get them out of the core split up and remove this hackery
|
||||
const string splitStr = "A:";
|
||||
|
||||
if (which == (uint)LibsnesApi.eTRACE.CPU)
|
||||
{
|
||||
var split = msg.Split(new[] { splitStr }, 2, StringSplitOptions.None);
|
||||
_tracer.Put(new(disassembly: split[0].PadRight(34), registerInfo: splitStr + split[1]));
|
||||
}
|
||||
else if (which == (uint)LibsnesApi.eTRACE.SMP)
|
||||
{
|
||||
int idx = msg.IndexOf("YA:", StringComparison.Ordinal);
|
||||
_tracer.Put(new(disassembly: msg.Substring(0, idx).TrimEnd(), registerInfo: msg.Substring(idx)));
|
||||
}
|
||||
else if (which == (uint)LibsnesApi.eTRACE.GB)
|
||||
{
|
||||
int idx = msg.IndexOf("AF:", StringComparison.Ordinal);
|
||||
_tracer.Put(new(disassembly: msg.Substring(0, idx).TrimEnd(), registerInfo: msg.Substring(idx)));
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadHook(uint addr)
|
||||
{
|
||||
if (MemoryCallbacks.HasReads)
|
||||
{
|
||||
uint flags = (uint)MemoryCallbackFlags.AccessRead;
|
||||
MemoryCallbacks.CallMemoryCallbacks(addr, 0, flags, "System Bus");
|
||||
// we RefreshMemoryCallbacks() after the trigger in case the trigger turns itself off at that point
|
||||
// EDIT: for now, theres some IPC re-entrancy problem
|
||||
// RefreshMemoryCallbacks();
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecHook(uint addr)
|
||||
{
|
||||
if (MemoryCallbacks.HasExecutes)
|
||||
{
|
||||
uint flags = (uint)MemoryCallbackFlags.AccessExecute;
|
||||
MemoryCallbacks.CallMemoryCallbacks(addr, 0, flags, "System Bus");
|
||||
// we RefreshMemoryCallbacks() after the trigger in case the trigger turns itself off at that point
|
||||
// EDIT: for now, theres some IPC re-entrancy problem
|
||||
// RefreshMemoryCallbacks();
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteHook(uint addr, byte val)
|
||||
{
|
||||
if (MemoryCallbacks.HasWrites)
|
||||
{
|
||||
uint flags = (uint)MemoryCallbackFlags.AccessWrite;
|
||||
MemoryCallbacks.CallMemoryCallbacks(addr, val, flags, "System Bus");
|
||||
// we RefreshMemoryCallbacks() after the trigger in case the trigger turns itself off at that point
|
||||
// EDIT: for now, theres some IPC re-entrancy problem
|
||||
// RefreshMemoryCallbacks();
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadHook_SMP(uint addr)
|
||||
{
|
||||
if (MemoryCallbacks.HasReads)
|
||||
{
|
||||
uint flags = (uint)MemoryCallbackFlags.AccessRead;
|
||||
MemoryCallbacks.CallMemoryCallbacks(addr, 0, flags, "SMP");
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecHook_SMP(uint addr)
|
||||
{
|
||||
if (MemoryCallbacks.HasExecutes)
|
||||
{
|
||||
uint flags = (uint)MemoryCallbackFlags.AccessExecute;
|
||||
MemoryCallbacks.CallMemoryCallbacks(addr, 0, flags, "SMP");
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteHook_SMP(uint addr, byte val)
|
||||
{
|
||||
if (MemoryCallbacks.HasWrites)
|
||||
{
|
||||
uint flags = (uint)MemoryCallbackFlags.AccessWrite;
|
||||
MemoryCallbacks.CallMemoryCallbacks(addr, val, flags, "SMP");
|
||||
}
|
||||
}
|
||||
|
||||
private enum LoadParamType
|
||||
{
|
||||
Normal, SuperGameBoy
|
||||
}
|
||||
|
||||
private struct LoadParams
|
||||
{
|
||||
public LoadParamType type;
|
||||
public byte[] xml_data;
|
||||
|
||||
public string rom_xml;
|
||||
public byte[] rom_data;
|
||||
public uint rom_size;
|
||||
public byte[] dmg_data;
|
||||
}
|
||||
|
||||
private bool LoadCurrent()
|
||||
{
|
||||
bool result = _currLoadParams.type == LoadParamType.Normal
|
||||
? Api.CMD_load_cartridge_normal(_currLoadParams.xml_data, _currLoadParams.rom_data)
|
||||
: Api.CMD_load_cartridge_super_game_boy(_currLoadParams.rom_xml, _currLoadParams.rom_data, _currLoadParams.rom_size, _currLoadParams.dmg_data);
|
||||
|
||||
_mapper = Api.Mapper;
|
||||
_region = Api.Region;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <param name="port">0 or 1, corresponding to L and R physical ports on the snes</param>
|
||||
/// <param name="device">LibsnesApi.SNES_DEVICE enum index specifying type of device</param>
|
||||
/// <param name="index">meaningless for most controllers. for multitap, 0-3 for which multitap controller</param>
|
||||
/// <param name="id">button ID enum; in the case of a regular controller, this corresponds to shift register position</param>
|
||||
/// <returns>for regular controllers, one bit D0 of button status. for other controls, varying ranges depending on id</returns>
|
||||
private short snes_input_state(int port, int device, int index, int id)
|
||||
{
|
||||
return _controllerDeck.CoreInputState(_controller, port, device, index, id);
|
||||
}
|
||||
|
||||
private void snes_input_poll()
|
||||
{
|
||||
// this doesn't actually correspond to anything in the underlying bsnes;
|
||||
// it gets called once per frame with video_refresh() and has nothing to do with anything
|
||||
}
|
||||
|
||||
private void snes_input_notify(int index)
|
||||
{
|
||||
// gets called with the following numbers:
|
||||
// 4xxx : lag frame related
|
||||
// 0: signifies latch bit going to 0. should be reported as oninputpoll
|
||||
// 1: signifies latch bit going to 1. should be reported as oninputpoll
|
||||
if (index >= 0x4000)
|
||||
{
|
||||
IsLagFrame = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void snes_video_refresh(int* data, int width, int height)
|
||||
{
|
||||
bool doubleSize = _settings.AlwaysDoubleSize;
|
||||
bool lineDouble = doubleSize, dotDouble = doubleSize;
|
||||
|
||||
_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)
|
||||
{
|
||||
_videoHeight *= 2;
|
||||
yskip = 2;
|
||||
|
||||
lineDouble = true;
|
||||
|
||||
// we don't dot double here because the user wanted double res and the game provided double res
|
||||
dotDouble = false;
|
||||
}
|
||||
else if (lineDouble)
|
||||
{
|
||||
_videoHeight *= 2;
|
||||
yskip = 2;
|
||||
}
|
||||
|
||||
int srcPitch = 1024;
|
||||
int srcStart = 0;
|
||||
|
||||
bool interlaced = height == 478 || height == 448;
|
||||
if (interlaced)
|
||||
{
|
||||
// from bsnes in interlaced mode we have each field side by side
|
||||
// so we will come in with a dimension of 512x448, say
|
||||
// but the fields are side by side, so it's actually 1024x224.
|
||||
// copy the first scanline from row 0, then the 2nd scanline from row 0 (offset 512)
|
||||
// EXAMPLE: yu yu hakushu legal screens
|
||||
// EXAMPLE: World Class Service Super Nintendo Tester (double resolution vertically but not horizontally, in character test the stars should shrink)
|
||||
lineDouble = false;
|
||||
srcPitch = 512;
|
||||
yskip = 1;
|
||||
_videoHeight = height;
|
||||
}
|
||||
|
||||
if (dotDouble)
|
||||
{
|
||||
_videoWidth *= 2;
|
||||
xskip = 2;
|
||||
}
|
||||
|
||||
if (_settings.CropSGBFrame && IsSGB)
|
||||
{
|
||||
_videoWidth = 160;
|
||||
_videoHeight = 144;
|
||||
}
|
||||
|
||||
int size = _videoWidth * _videoHeight;
|
||||
if (_videoBuffer.Length != size)
|
||||
{
|
||||
_videoBuffer = new int[size];
|
||||
}
|
||||
|
||||
if (_settings.CropSGBFrame && IsSGB)
|
||||
{
|
||||
int di = 0;
|
||||
for (int y = 0; y < 144; y++)
|
||||
{
|
||||
int si = ((y+39) * srcPitch) + 48;
|
||||
for(int x=0;x<160;x++)
|
||||
_videoBuffer[di++] = data[si++];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
if (j == 1 && !dotDouble)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int xbonus = j;
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
// potentially do this twice, if we need to line double
|
||||
if (i == 1 && !lineDouble)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
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 * _videoWidth * yskip + x * xskip + bonus;
|
||||
int rgb = data[si];
|
||||
_videoBuffer[di] = rgb;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VirtualHeight = BufferHeight;
|
||||
VirtualWidth = BufferWidth;
|
||||
if (VirtualHeight * 2 < VirtualWidth)
|
||||
VirtualHeight *= 2;
|
||||
if (VirtualHeight > 240)
|
||||
VirtualWidth = 512;
|
||||
VirtualWidth = (int)Math.Round(VirtualWidth * 1.146);
|
||||
}
|
||||
|
||||
private void RefreshMemoryCallbacks(bool suppress)
|
||||
{
|
||||
var mcs = MemoryCallbacks;
|
||||
Api.QUERY_set_state_hook_exec(!suppress && mcs.HasExecutesForScope("System Bus"));
|
||||
Api.QUERY_set_state_hook_read(!suppress && mcs.HasReadsForScope("System Bus"));
|
||||
Api.QUERY_set_state_hook_write(!suppress && mcs.HasWritesForScope("System Bus"));
|
||||
}
|
||||
|
||||
//public byte[] snes_get_memory_data_read(LibsnesApi.SNES_MEMORY id)
|
||||
//{
|
||||
// var size = (int)api.snes_get_memory_size(id);
|
||||
// if (size == 0) return new byte[0];
|
||||
// var ret = api.snes_get_memory_data(id);
|
||||
// return ret;
|
||||
//}
|
||||
|
||||
private void RefreshPalette()
|
||||
{
|
||||
CurrPalette = (SnesColors.ColorType)Enum.Parse(typeof(SnesColors.ColorType), _settings.Palette, false);
|
||||
int[] tmp = SnesColors.GetLUT(CurrPalette);
|
||||
fixed (int* p = &tmp[0])
|
||||
Api.QUERY_set_color_lut((IntPtr)p);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -103,7 +103,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
void SetBackColor(int snescol = -1);
|
||||
}
|
||||
|
||||
public unsafe class SNESGraphicsDecoder : ISNESGraphicsDecoder
|
||||
public static class SNESGraphicsDecoder
|
||||
{
|
||||
public class PaletteSelection
|
||||
{
|
||||
|
@ -289,59 +289,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
public BGInfo this[int index] => bgs[index - 1];
|
||||
}
|
||||
|
||||
public class OAMInfo : ISNESGraphicsDecoder.OAMInfo
|
||||
{
|
||||
public int Index { get; }
|
||||
public ushort X { get; }
|
||||
public byte Y { get; }
|
||||
public int Tile { get; }
|
||||
public bool Table { get; }
|
||||
public int Palette { get; }
|
||||
public byte Priority { get; }
|
||||
public bool VFlip { get; }
|
||||
public bool HFlip { get; }
|
||||
public bool Size { get; }
|
||||
|
||||
/// <summary>
|
||||
/// tiledata address
|
||||
/// </summary>
|
||||
public int Address { get; }
|
||||
|
||||
public OAMInfo(SNESGraphicsDecoder dec, ScreenInfo si, int num)
|
||||
{
|
||||
Index = num;
|
||||
|
||||
int lowaddr = num*4;
|
||||
X = dec.oam[lowaddr++];
|
||||
Y = dec.oam[lowaddr++];
|
||||
byte name = dec.oam[lowaddr++];
|
||||
Table = (dec.oam[lowaddr] & 1) == 1;
|
||||
Palette = (dec.oam[lowaddr]>>1) & 7;
|
||||
Priority = (byte)((dec.oam[lowaddr] >> 4) & 3);
|
||||
HFlip = ((dec.oam[lowaddr] >> 6) & 1) == 1;
|
||||
VFlip = ((dec.oam[lowaddr] >> 7) & 1) == 1;
|
||||
|
||||
int highaddr = num / 4;
|
||||
int shift = (num % 4) * 2;
|
||||
int high = dec.oam[512+highaddr];
|
||||
high >>= shift;
|
||||
int x = high & 1;
|
||||
high >>= 1;
|
||||
Size = (high & 1) != 0;
|
||||
X = (ushort)(X | (x << 8));
|
||||
|
||||
Tile = name + (Table ? 256 : 0);
|
||||
Address = 32 * Tile;
|
||||
|
||||
if (Tile < 256)
|
||||
Address += si.OBJTable0Addr;
|
||||
else
|
||||
Address += si.OBJTable1Addr - (256 * 32);
|
||||
|
||||
Address &= 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
public class ScreenInfo
|
||||
{
|
||||
public Size ObjSizeBounds;
|
||||
|
@ -402,187 +349,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
{8,7,0,0}
|
||||
};
|
||||
|
||||
|
||||
public ScreenInfo ScanScreenInfo()
|
||||
{
|
||||
int OBSEL_NameSel = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_NAMESEL);
|
||||
int OBSEL_NameBase = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_NAMEBASE);
|
||||
|
||||
var si = new ScreenInfo
|
||||
{
|
||||
Mode = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG_MODE),
|
||||
Mode1_BG3_Priority = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_PRIORITY) == 1,
|
||||
OBSEL_Size = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_SIZE),
|
||||
OBSEL_NameSel = OBSEL_NameSel,
|
||||
OBSEL_NameBase = OBSEL_NameBase,
|
||||
OBJTable0Addr = OBSEL_NameBase << 14,
|
||||
OBJTable1Addr = ((OBSEL_NameBase << 14) + ((OBSEL_NameSel + 1) << 13)) & 0xFFFF,
|
||||
SETINI_Mode7ExtBG = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_MODE7_EXTBG) == 1,
|
||||
SETINI_HiRes = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_HIRES) == 1,
|
||||
SETINI_Overscan = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_OVERSCAN) == 1,
|
||||
SETINI_ObjInterlace = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_OBJ_INTERLACE) == 1,
|
||||
SETINI_ScreenInterlace = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_SCREEN_INTERLACE) == 1,
|
||||
CGWSEL_ColorMask = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_COLORMASK),
|
||||
CGWSEL_ColorSubMask = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_COLORSUBMASK),
|
||||
CGWSEL_AddSubMode = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_ADDSUBMODE),
|
||||
CGWSEL_DirectColor = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_DIRECTCOLOR) == 1,
|
||||
CGADSUB_AddSub = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_MODE),
|
||||
CGADSUB_Half = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_HALF) == 1,
|
||||
OBJ_MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_OBJ) == 1,
|
||||
OBJ_SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_OBJ) == 1,
|
||||
OBJ_MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_OBJ) == 1,
|
||||
BK_MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BACKDROP) == 1,
|
||||
M7HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7HOFS),
|
||||
M7VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7VOFS),
|
||||
M7A = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7A),
|
||||
M7B = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7B),
|
||||
M7C = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7C),
|
||||
M7D = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7D),
|
||||
M7X = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7X),
|
||||
M7Y = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7Y),
|
||||
M7SEL_REPEAT = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_REPEAT),
|
||||
M7SEL_HFLIP = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_HFLIP)!=0,
|
||||
M7SEL_VFLIP = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_VFLIP)!=0,
|
||||
};
|
||||
|
||||
si.ObjSizeBounds = ObjSizes[si.OBSEL_Size,1];
|
||||
int square = Math.Max(si.ObjSizeBounds.Width, si.ObjSizeBounds.Height);
|
||||
si.ObjSizeBoundsSquare = new Size(square, square);
|
||||
|
||||
si.BG.BG1.Bpp = ModeBpps[si.Mode, 0];
|
||||
si.BG.BG2.Bpp = ModeBpps[si.Mode, 1];
|
||||
si.BG.BG3.Bpp = ModeBpps[si.Mode, 2];
|
||||
si.BG.BG4.Bpp = ModeBpps[si.Mode, 3];
|
||||
|
||||
//initial setting of mode type (derived from bpp table.. mode7 bg types will be fixed up later)
|
||||
for(int i=1;i<=4;i++)
|
||||
si.BG[i].BGMode = si.BG[i].Bpp == 0 ? BGMode.Unavailable : BGMode.Text;
|
||||
|
||||
si.BG.BG1.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_TILESIZE);
|
||||
si.BG.BG2.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_TILESIZE);
|
||||
si.BG.BG3.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_TILESIZE);
|
||||
si.BG.BG4.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_TILESIZE);
|
||||
|
||||
si.BG.BG1.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_SCSIZE);
|
||||
si.BG.BG2.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_SCSIZE);
|
||||
si.BG.BG3.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_SCSIZE);
|
||||
si.BG.BG4.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_SCSIZE);
|
||||
si.BG.BG1.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_SCADDR);
|
||||
si.BG.BG2.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_SCADDR);
|
||||
si.BG.BG3.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_SCADDR);
|
||||
si.BG.BG4.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_SCADDR);
|
||||
si.BG.BG1.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_TDADDR);
|
||||
si.BG.BG2.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_TDADDR);
|
||||
si.BG.BG3.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_TDADDR);
|
||||
si.BG.BG4.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_TDADDR);
|
||||
|
||||
si.BG.BG1.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG1) == 1;
|
||||
si.BG.BG2.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG2) == 1;
|
||||
si.BG.BG3.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG3) == 1;
|
||||
si.BG.BG4.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG4) == 1;
|
||||
si.BG.BG1.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG1) == 1;
|
||||
si.BG.BG2.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG2) == 1;
|
||||
si.BG.BG3.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG3) == 1;
|
||||
si.BG.BG4.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG4) == 1;
|
||||
si.BG.BG1.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG1) == 1;
|
||||
si.BG.BG2.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG2) == 1;
|
||||
si.BG.BG3.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG3) == 1;
|
||||
si.BG.BG4.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG4) == 1;
|
||||
|
||||
si.BG.BG1.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1HOFS);
|
||||
si.BG.BG1.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1VOFS);
|
||||
si.BG.BG2.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2HOFS);
|
||||
si.BG.BG2.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2VOFS);
|
||||
si.BG.BG3.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3HOFS);
|
||||
si.BG.BG3.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3VOFS);
|
||||
si.BG.BG4.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4HOFS);
|
||||
si.BG.BG4.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4VOFS);
|
||||
|
||||
for (int i = 1; i <= 4; i++)
|
||||
{
|
||||
si.BG[i].Mode = si.Mode;
|
||||
si.BG[i].TiledataAddr = si.BG[i].TDADDR << 13;
|
||||
si.BG[i].ScreenAddr = si.BG[i].SCADDR << 9;
|
||||
}
|
||||
|
||||
//fixup irregular things for mode 7
|
||||
if (si.Mode == 7)
|
||||
{
|
||||
si.BG.BG1.TiledataAddr = 0;
|
||||
si.BG.BG1.ScreenAddr = 0;
|
||||
|
||||
if (si.CGWSEL_DirectColor)
|
||||
{
|
||||
si.BG.BG1.BGMode = BGMode.Mode7DC;
|
||||
}
|
||||
else
|
||||
si.BG.BG1.BGMode = BGMode.Mode7;
|
||||
|
||||
if (si.SETINI_Mode7ExtBG)
|
||||
{
|
||||
si.BG.BG2.BGMode = BGMode.Mode7Ext;
|
||||
si.BG.BG2.Bpp = 7;
|
||||
si.BG.BG2.TiledataAddr = 0;
|
||||
si.BG.BG2.ScreenAddr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//determine which colors each BG could use
|
||||
switch (si.Mode)
|
||||
{
|
||||
case 0:
|
||||
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 32);
|
||||
si.BG.BG2.PaletteSelection = new PaletteSelection(32, 32);
|
||||
si.BG.BG3.PaletteSelection = new PaletteSelection(64, 32);
|
||||
si.BG.BG4.PaletteSelection = new PaletteSelection(96, 32);
|
||||
break;
|
||||
case 1:
|
||||
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128);
|
||||
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128);
|
||||
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 32);
|
||||
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
|
||||
break;
|
||||
case 2:
|
||||
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128);
|
||||
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128);
|
||||
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
|
||||
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
|
||||
break;
|
||||
case 3:
|
||||
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 256);
|
||||
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128);
|
||||
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
|
||||
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
|
||||
break;
|
||||
case 4:
|
||||
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 256);
|
||||
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 32);
|
||||
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
|
||||
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
|
||||
break;
|
||||
case 5:
|
||||
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128);
|
||||
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 32);
|
||||
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
|
||||
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
|
||||
break;
|
||||
case 6:
|
||||
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128);
|
||||
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 32);
|
||||
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
|
||||
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
|
||||
break;
|
||||
case 7:
|
||||
si.BG.BG1.PaletteSelection = new PaletteSelection(0, 256);
|
||||
si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128);
|
||||
si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0);
|
||||
si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
return si;
|
||||
}
|
||||
|
||||
//the same basic color table that libsnes uses to convert from snes 555 to rgba32
|
||||
private static readonly int[] directColorTable = new int[256]; //8bpp gfx -> rgb555
|
||||
static SNESGraphicsDecoder()
|
||||
|
@ -601,30 +367,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
}
|
||||
}
|
||||
|
||||
private readonly int[] colortable;
|
||||
public byte* vram, oam;
|
||||
public ushort* cgram, vram16;
|
||||
|
||||
private readonly LibsnesApi api;
|
||||
|
||||
public SNESGraphicsDecoder(LibsnesApi api, SnesColors.ColorType pal)
|
||||
{
|
||||
this.api = api;
|
||||
colortable = SnesColors.GetLUT(pal);
|
||||
IntPtr block = (IntPtr)api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.VRAM);
|
||||
vram = (byte*)block;
|
||||
vram16 = (ushort*)block;
|
||||
block = (IntPtr)api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CGRAM);
|
||||
cgram = (ushort*)block;
|
||||
block = (IntPtr)api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.OAM);
|
||||
oam = (byte*)block;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
//todo - unhook from api?
|
||||
}
|
||||
|
||||
public struct TileEntry
|
||||
{
|
||||
public ushort tilenum;
|
||||
|
@ -638,473 +380,5 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
|||
{
|
||||
None = 0, Priority = 1, Horz = 2, Vert = 4,
|
||||
}
|
||||
|
||||
public ISNESGraphicsDecoder.OAMInfo CreateOAMInfo(ScreenInfo si, int num)
|
||||
=> new OAMInfo(this, si, num);
|
||||
|
||||
/// <summary>
|
||||
/// decodes a mode7 BG. youll still need to paletteize and colorize it.
|
||||
/// </summary>
|
||||
public void DecodeMode7BG(int* screen, int stride, bool extBg)
|
||||
{
|
||||
int[] tileCache = _tileCache[extBg?17:7];
|
||||
for (int ty = 0, tidx = 0; ty < 128; ty++)
|
||||
{
|
||||
for (int tx = 0; tx < 128; tx++, tidx++)
|
||||
{
|
||||
int tileEntry = vram[tidx * 2];
|
||||
int src = tileEntry * 64;
|
||||
for (int py = 0, pix=src; py < 8; py++)
|
||||
{
|
||||
for (int px = 0; px < 8; px++, pix++)
|
||||
{
|
||||
int dst = (ty * 8 + py) * stride + (tx * 8 + px);
|
||||
int srcData = tileCache[pix];
|
||||
screen[dst] = srcData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// returns a tilemap which might be resized into 8x8 physical tiles if the 16x16 logical tilesize is specified
|
||||
// /// </summary>
|
||||
// TileEntry[] AdaptTilemap(TileEntry[] map8x8, int tilesWide, int tilesTall, int tilesize)
|
||||
// {
|
||||
// if (tilesize == 8) return map8x8;
|
||||
// int numTiles = tilesWide * tilesTall;
|
||||
// var ret = new TileEntry[numTiles * 4];
|
||||
// for(int y=0;y<tilesTall;y++)
|
||||
// {
|
||||
// for (int x = 0; x < tilesWide; x++)
|
||||
// {
|
||||
// int si = tilesWide * y + x;
|
||||
// int di = tilesHigh
|
||||
// for (int tx = 0; tx < 2; tx++)
|
||||
// {
|
||||
// for (int ty = 0; ty < 2; ty++)
|
||||
// {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// decodes a BG. youll still need to paletteize and colorize it.
|
||||
/// someone else has to take care of calculating the starting color from the mode and layer number.
|
||||
/// </summary>
|
||||
public void DecodeBG(int* screen, int stride, TileEntry[] map, int tiledataBaseAddr, ScreenSize size, int bpp, int tilesize, int paletteStart)
|
||||
{
|
||||
//emergency backstop. this can only happen if we're displaying an unavailable BG or other similar such value
|
||||
if (bpp == 0) return;
|
||||
|
||||
int ncolors = 1 << bpp;
|
||||
|
||||
int[] tileBuf = new int[16*16];
|
||||
var dims = SizeInTilesForBGSize(size);
|
||||
int count8x8 = tilesize / 8;
|
||||
int tileSizeBytes = 8 * bpp;
|
||||
int baseTileNum = tiledataBaseAddr / tileSizeBytes;
|
||||
int[] tileCache = _tileCache[bpp];
|
||||
int tileCacheMask = tileCache.Length - 1;
|
||||
|
||||
int screenWidth = dims.Width * count8x8 * 8;
|
||||
|
||||
for (int mty = 0; mty < dims.Height; mty++)
|
||||
{
|
||||
for (int mtx = 0; mtx < dims.Width; mtx++)
|
||||
{
|
||||
for (int tx = 0; tx < count8x8; tx++)
|
||||
{
|
||||
for (int ty = 0; ty < count8x8; ty++)
|
||||
{
|
||||
int mapIndex = mty * dims.Width + mtx;
|
||||
var te = map[mapIndex];
|
||||
|
||||
//apply metatile flipping
|
||||
int tnx = tx, tny = ty;
|
||||
if (tilesize == 16)
|
||||
{
|
||||
if ((te.flags & TileEntryFlags.Horz) != 0) tnx = 1 - tnx;
|
||||
if ((te.flags & TileEntryFlags.Vert) != 0) tny = 1 - tny;
|
||||
}
|
||||
|
||||
int tileNum = te.tilenum + tnx + tny * 16 + baseTileNum;
|
||||
int srcOfs = tileNum * 64;
|
||||
for (int i = 0, y = 0; y < 8; y++)
|
||||
{
|
||||
for (int x = 0; x < 8; x++, i++)
|
||||
{
|
||||
int px = x;
|
||||
int py = y;
|
||||
if ((te.flags & TileEntryFlags.Horz) != 0) px = 7 - x;
|
||||
if ((te.flags & TileEntryFlags.Vert) != 0) py = 7 - y;
|
||||
int dstX = (mtx * count8x8 + tx) * 8 + px;
|
||||
int dstY = (mty * count8x8 + ty) * 8 + py;
|
||||
int dstOfs = dstY * stride + dstX;
|
||||
int color = tileCache[srcOfs & tileCacheMask];
|
||||
srcOfs++;
|
||||
if (color == 0 && usingUserBackColor)
|
||||
{ }
|
||||
else
|
||||
{
|
||||
color += te.palette * ncolors;
|
||||
color += paletteStart;
|
||||
}
|
||||
screen[dstOfs] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TileEntry[] FetchMode7Tilemap()
|
||||
{
|
||||
TileEntry[] buf = new TileEntry[128*128];
|
||||
for (int ty = 0, tidx = 0; ty < 128; ty++)
|
||||
{
|
||||
for (int tx = 0; tx < 128; tx++, tidx++)
|
||||
{
|
||||
int tileEntry = vram[tidx * 2];
|
||||
buf[tidx].address = tidx * 2;
|
||||
buf[tidx].tilenum = (ushort)tileEntry;
|
||||
//palette and flags are ok defaulting to 0
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// fetches a tilemap. this is simple; apparently only the screen size (shape) is a factor (not the tile size)
|
||||
/// </summary>
|
||||
public TileEntry[] FetchTilemap(int addr, ScreenSize size)
|
||||
{
|
||||
var blockDims = SizeInBlocksForBGSize(size);
|
||||
int blocksw = blockDims.Width;
|
||||
int blocksh = blockDims.Height;
|
||||
int width = blockDims.Width * 32;
|
||||
int height = blockDims.Height * 32;
|
||||
TileEntry[] buf = new TileEntry[width*height];
|
||||
|
||||
for (int by = 0; by < blocksh; by++)
|
||||
{
|
||||
for (int bx = 0; bx < blocksw; bx++)
|
||||
{
|
||||
for (int y = 0; y < 32; y++)
|
||||
{
|
||||
for (int x = 0; x < 32; x++)
|
||||
{
|
||||
int idx = (by * 32 + y) * width + bx * 32 + x;
|
||||
ushort entry = *(ushort*)(vram + addr);
|
||||
buf[idx].tilenum = (ushort)(entry & 0x3FF);
|
||||
buf[idx].palette = (byte)((entry >> 10) & 7);
|
||||
buf[idx].flags = (TileEntryFlags)((entry >> 13) & 7);
|
||||
buf[idx].address = addr;
|
||||
addr += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
//TODO - paletteize and colorize could be in one step, for more speed
|
||||
public void Paletteize(int* buf, int offset, int startcolor, int numpixels)
|
||||
{
|
||||
for (int i = 0; i < numpixels; i++)
|
||||
{
|
||||
int entry = buf[offset + i];
|
||||
int color;
|
||||
if (entry == 0 && usingUserBackColor)
|
||||
color = userBackColor;
|
||||
else color = cgram[startcolor + entry] & 0x7FFF; //unfortunate that we have to mask this here.. maybe do it in a more optimal spot when we port it to c++
|
||||
|
||||
buf[offset + i] = color;
|
||||
}
|
||||
}
|
||||
public void Colorize(int* buf, int offset, int numpixels)
|
||||
{
|
||||
for (int i = 0; i < numpixels; i++)
|
||||
{
|
||||
buf[offset + i] = colortable[491520 + buf[offset + i]];
|
||||
}
|
||||
}
|
||||
|
||||
private readonly int[][] _tileCache = new int[18][];
|
||||
|
||||
private bool usingUserBackColor;
|
||||
private int userBackColor;
|
||||
|
||||
public void SetBackColor(int snescol)
|
||||
{
|
||||
if (snescol == -1)
|
||||
{
|
||||
usingUserBackColor = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
usingUserBackColor = true;
|
||||
userBackColor = snescol;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Caches all tiles at the 2bpp, 4bpp, and 8bpp decoded states.
|
||||
/// we COULD defer this til we need it, you know. sort of a cool idea, not too hard
|
||||
/// </summary>
|
||||
public void CacheTiles()
|
||||
{
|
||||
//generate 2bpp tiles
|
||||
int numtiles = 65536/8/2;
|
||||
int[] tiles = new int[8 * 8 * numtiles];
|
||||
_tileCache[2] = tiles;
|
||||
for (int i = 0; i < numtiles; i++)
|
||||
{
|
||||
Decode8x8x2bpp(tiles, i * 64, 16 * i, 8);
|
||||
}
|
||||
|
||||
//merge 2bpp tiles into 4bpp and 8bpp
|
||||
CacheTiles_Merge(2);
|
||||
CacheTiles_Merge(4);
|
||||
CacheTilesMode7();
|
||||
CacheTilesMode7ExtBg();
|
||||
}
|
||||
|
||||
public void CacheTilesMode7()
|
||||
{
|
||||
int numtiles = 256;
|
||||
int[] tiles = new int[8 * 8 * numtiles];
|
||||
_tileCache[7] = tiles;
|
||||
for (int i = 0, j=0; i < numtiles; i++)
|
||||
{
|
||||
for (int y = 0; y < 8; y++)
|
||||
for (int x = 0; x < 8; x++, j++)
|
||||
tiles[j] = vram[j * 2 + 1];
|
||||
}
|
||||
}
|
||||
|
||||
//not being used.. do we need it?
|
||||
public int[] GetCachedTile(int bpp, int tilenum)
|
||||
{
|
||||
int[] ret = new int[8 * 8];
|
||||
int idx = tilenum * 64;
|
||||
for (int i = 0; i < 64; i++)
|
||||
ret[i] = _tileCache[bpp][idx + i];
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void CacheTilesMode7ExtBg()
|
||||
{
|
||||
int numtiles = 256;
|
||||
int[] tiles = new int[8 * 8 * numtiles];
|
||||
_tileCache[17] = tiles;
|
||||
int[] mode7tiles = _tileCache[7];
|
||||
int numPixels = numtiles*8*8;
|
||||
for (int i = 0; i < numPixels; i++)
|
||||
tiles[i] = mode7tiles[i] & 0x7F;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// merges one type of tiles with another to create the higher-order bitdepth.
|
||||
/// TODO - templateize this when we change it to c++
|
||||
/// </summary>
|
||||
private void CacheTiles_Merge(int fromBpp)
|
||||
{
|
||||
int toBpp = fromBpp * 2;
|
||||
int shift = fromBpp;
|
||||
int numtiles = 8192 / toBpp;
|
||||
int[] tilesDst = new int[8 * 8 * numtiles];
|
||||
_tileCache[toBpp] = tilesDst;
|
||||
int[] tilesSrc = _tileCache[fromBpp];
|
||||
|
||||
for (int i = 0; i < numtiles; i++)
|
||||
{
|
||||
int srcAddr = i * 128;
|
||||
int dstAddr = i * 64;
|
||||
for (int p = 0; p < 64; p++)
|
||||
{
|
||||
int tileA = tilesSrc[srcAddr + p];
|
||||
int tileB = tilesSrc[srcAddr + p + 64];
|
||||
tilesDst[dstAddr + p] = tileA | (tileB << shift);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// decodes an 8x8 tile to a linear framebuffer type thing. fundamental unit of tile decoding.
|
||||
/// </summary>
|
||||
public void Decode8x8x2bpp(int[] buf, int offset, int addr, int stride=8)
|
||||
{
|
||||
for (int y = 0; y < 8; y++)
|
||||
{
|
||||
byte val = vram[addr + 1];
|
||||
for (int x = 0; x < 8; x++) buf[offset + y * stride + x] = val >> (7 - x) & 1;
|
||||
val = vram[addr + 0];
|
||||
for (int x = 0; x < 8; x++) buf[offset + y * stride + x] = (buf[offset + y * stride + x] << 1) | (val >> (7 - x) & 1);
|
||||
addr += 2;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// renders the mode7 tiles to a screen with the predefined size.
|
||||
/// </summary>
|
||||
public void RenderMode7TilesToScreen(int* screen, int stride, bool ext, bool directColor, int tilesWide = 16, int startTile = 0, int numTiles = 256)
|
||||
{
|
||||
int[] tilebuf = _tileCache[ext?17:7];
|
||||
for (int i = 0; i < numTiles; i++)
|
||||
{
|
||||
int tnum = startTile + i;
|
||||
//TODO - mask by possible number of tiles? only in OBJ rendering mode?
|
||||
|
||||
int ty = i / tilesWide;
|
||||
int tx = i % tilesWide;
|
||||
int dstOfs = (ty * 8) * stride + tx * 8;
|
||||
int srcOfs = tnum * 64;
|
||||
for (int y = 0, p = 0; y < 8; y++)
|
||||
{
|
||||
for (int x = 0; x < 8; x++, p++)
|
||||
{
|
||||
screen[dstOfs + y * stride + x] = tilebuf[srcOfs + p];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int numPixels = numTiles * 8 * 8;
|
||||
if (directColor) DirectColorify(screen, numPixels);
|
||||
else Paletteize(screen, 0, 0, numPixels);
|
||||
Colorize(screen, 0, numPixels);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// renders the tiles to a screen of the crudely specified size.
|
||||
/// we might need 16x16 unscrambling and some other perks here eventually.
|
||||
/// provide a start color to use as the basis for the palette
|
||||
/// </summary>
|
||||
public void RenderTilesToScreen(int* screen, int stride, int bpp, int startcolor, int startTile = 0, int numTiles = -1)
|
||||
{
|
||||
if (numTiles == -1)
|
||||
numTiles = 8192 / bpp;
|
||||
int[] tilebuf = _tileCache[bpp];
|
||||
int tilesWide = stride / 8;
|
||||
for (int i = 0; i < numTiles; i++)
|
||||
{
|
||||
int tnum = startTile + i;
|
||||
//TODO - mask by possible number of tiles? only in OBJ rendering mode?
|
||||
int ty = i / tilesWide;
|
||||
int tx = i % tilesWide;
|
||||
int dstOfs = (ty * 8) * stride + tx * 8;
|
||||
int srcOfs = tnum * 64;
|
||||
for (int y = 0, p = 0; y < 8; y++)
|
||||
for (int x = 0; x < 8; x++, p++)
|
||||
{
|
||||
screen[dstOfs + y * stride + x] = tilebuf[srcOfs + p];
|
||||
}
|
||||
}
|
||||
|
||||
int numPixels = numTiles * 8 * 8;
|
||||
Paletteize(screen, 0, startcolor, numPixels);
|
||||
Colorize(screen, 0, numPixels);
|
||||
}
|
||||
|
||||
|
||||
public void RenderSpriteToScreen(int* screen, int stride, int destx, int desty, ScreenInfo si, int spritenum, ISNESGraphicsDecoder.OAMInfo oam = null, int xlimit = 1024, int ylimit = 1024, byte[,] spriteMap = null)
|
||||
{
|
||||
oam ??= new OAMInfo(this, si, spritenum);
|
||||
var dim = ObjSizes[si.OBSEL_Size, oam.Size ? 1 : 0];
|
||||
|
||||
int[] tilebuf = _tileCache[4];
|
||||
|
||||
int baseaddr = oam.Table ? si.OBJTable1Addr : si.OBJTable0Addr;
|
||||
|
||||
//TODO - flips of 'undocumented' rectangular oam settings are wrong. probably easy to do right, but we need a test
|
||||
|
||||
int bcol = oam.Tile & 0xF;
|
||||
int brow = (oam.Tile >> 4) & 0xF;
|
||||
for(int oy=0;oy<dim.Height;oy++)
|
||||
for (int ox = 0; ox < dim.Width; ox++)
|
||||
{
|
||||
int x = ox;
|
||||
int y = oy;
|
||||
|
||||
int dy, dx;
|
||||
|
||||
if (oam.HFlip)
|
||||
dx = dim.Width - 1 - x;
|
||||
else dx = x;
|
||||
if (oam.VFlip)
|
||||
dy = dim.Height - 1 - y;
|
||||
else dy = y;
|
||||
|
||||
dx += destx;
|
||||
dy += desty;
|
||||
|
||||
if(dx>=xlimit || dy>=ylimit || dx<0 || dy<0)
|
||||
continue;
|
||||
|
||||
int col = (bcol + (x >> 3)) & 0xF;
|
||||
int row = (brow + (y >> 3)) & 0xF;
|
||||
int sx = x & 0x7;
|
||||
int sy = y & 0x7;
|
||||
|
||||
int addr = baseaddr*2 + (row * 16 + col) * 64;
|
||||
addr += sy * 8 + sx;
|
||||
|
||||
int dofs = stride*dy+dx;
|
||||
int color = tilebuf[addr];
|
||||
if (spriteMap != null && color == 0)
|
||||
{
|
||||
//skip transparent pixels
|
||||
}
|
||||
else
|
||||
{
|
||||
screen[dofs] = color;
|
||||
Paletteize(screen, dofs, oam.Palette * 16 + 128, 1);
|
||||
Colorize(screen, dofs, 1);
|
||||
if (spriteMap != null) spriteMap[dx, dy] = (byte)spritenum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Colorize(int rgb555)
|
||||
{
|
||||
//skip to max luminance in the palette table
|
||||
return colortable[491520 + rgb555];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns the current palette, transformed into an int array, for more convenience
|
||||
/// </summary>
|
||||
public int[] GetPalette()
|
||||
{
|
||||
var ret = new int[256];
|
||||
for (int i = 0; i < 256; i++)
|
||||
ret[i] = cgram[i] & 0x7FFF;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void DirectColorify(int* screen, int numPixels)
|
||||
{
|
||||
for (int i = 0; i < numPixels; i++)
|
||||
screen[i] = directColorTable[screen[i]];
|
||||
|
||||
}
|
||||
|
||||
public void Enter()
|
||||
{
|
||||
((IMonitor)api).Enter();
|
||||
}
|
||||
|
||||
public void Exit()
|
||||
{
|
||||
((IMonitor)api).Exit();
|
||||
}
|
||||
} //class SNESGraphicsDecoder
|
||||
} //namespace
|
||||
|
|
|
@ -12,7 +12,6 @@ namespace BizHawk.Emulation.Cores
|
|||
public const string A7800Hawk = "A7800Hawk";
|
||||
public const string Ares64 = "Ares64";
|
||||
public const string Atari2600Hawk = "Atari2600Hawk";
|
||||
public const string Bsnes = "BSNES";
|
||||
public const string Bsnes115 = "BSNESv115+";
|
||||
public const string C64Hawk = "C64Hawk";
|
||||
public const string ChannelFHawk = "ChannelFHawk";
|
||||
|
|
|
@ -16,7 +16,6 @@ namespace BizHawk.Emulation.Cores
|
|||
{
|
||||
return core switch
|
||||
{
|
||||
LibsnesCore libsnes => GetLibsnesPadSchemas(libsnes),
|
||||
BsnesCore bsnes => GetBsnesPadSchemas(bsnes),
|
||||
SubBsnesCore subBsnes => GetBsnesPadSchemas(subBsnes.ServiceProvider.GetService<ISettable<BsnesCore.SnesSettings, BsnesCore.SnesSyncSettings>>()),
|
||||
NymaCore nyma => GetFaustSchemas(nyma, showMessageBox),
|
||||
|
@ -57,60 +56,6 @@ namespace BizHawk.Emulation.Cores
|
|||
yield return ConsoleButtons();
|
||||
}
|
||||
|
||||
private IEnumerable<PadSchema> GetLibsnesPadSchemas(LibsnesCore core)
|
||||
{
|
||||
var syncSettings = core.GetSyncSettings();
|
||||
|
||||
var ports = new[]
|
||||
{
|
||||
syncSettings.LeftPort,
|
||||
syncSettings.RightPort
|
||||
};
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
int playerNum = i + offset + 1;
|
||||
switch (ports[i])
|
||||
{
|
||||
default:
|
||||
case LibsnesControllerDeck.ControllerType.Unplugged:
|
||||
offset -= 1;
|
||||
break;
|
||||
case LibsnesControllerDeck.ControllerType.Gamepad:
|
||||
yield return StandardController(playerNum);
|
||||
break;
|
||||
case LibsnesControllerDeck.ControllerType.Multitap:
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
yield return StandardController(playerNum + j);
|
||||
}
|
||||
|
||||
offset += 3;
|
||||
break;
|
||||
case LibsnesControllerDeck.ControllerType.Mouse:
|
||||
yield return Mouse(playerNum);
|
||||
break;
|
||||
case LibsnesControllerDeck.ControllerType.SuperScope:
|
||||
yield return SuperScope(playerNum);
|
||||
break;
|
||||
case LibsnesControllerDeck.ControllerType.Justifier:
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
yield return Justifier(playerNum);
|
||||
offset += j;
|
||||
}
|
||||
|
||||
break;
|
||||
case LibsnesControllerDeck.ControllerType.Payload:
|
||||
yield return Payload(playerNum);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
yield return ConsoleButtons();
|
||||
}
|
||||
|
||||
private IEnumerable<PadSchema> GetBsnesPadSchemas(ISettable<BsnesCore.SnesSettings, BsnesCore.SnesSyncSettings> settingsProvider)
|
||||
{
|
||||
var syncSettings = settingsProvider.GetSyncSettings();
|
||||
|
@ -231,7 +176,7 @@ namespace BizHawk.Emulation.Cores
|
|||
|
||||
private static PadSchema Mouse(int controller)
|
||||
{
|
||||
var defAxes = new SnesMouseController().Definition.Axes;
|
||||
var defAxes = SnesMouseController.Definition.Axes;
|
||||
return new PadSchema
|
||||
{
|
||||
Size = new Size(345, 225),
|
||||
|
|
Loading…
Reference in New Issue