Remove most of (old) BSNES' managed source and callsites

This commit is contained in:
YoshiRulz 2025-01-11 01:14:42 +10:00
parent fe85bfa41b
commit 3cc3440f22
No known key found for this signature in database
GPG Key ID: C4DE31C245353FB7
37 changed files with 9 additions and 4726 deletions

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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 ]),
};

View File

@ -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;

View File

@ -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

View File

@ -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,

View File

@ -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", },

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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>

View File

@ -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
}

View File

@ -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" />

View File

@ -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();
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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();
}
}
}
}

View File

@ -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
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}

View File

@ -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();
}
}
}
}

View File

@ -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.");
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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";

View File

@ -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),