BSNESv115+: add option for disabling ppu sprite limit
This commit is contained in:
parent
066297d5e7
commit
c21fedc76a
Assets/dll
src
BizHawk.Client.EmuHawk/config/SNES
BizHawk.Emulation.Cores/Consoles/Nintendo/BSNES
waterbox/bsnescore/bsnes/target-bsnescore
Binary file not shown.
|
@ -1,32 +1,32 @@
|
|||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
partial class BSNESOptions
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
partial class BSNESOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
/// <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);
|
||||
/// <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
|
||||
#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()
|
||||
/// <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();
|
||||
|
@ -57,6 +57,7 @@
|
|||
this.cbUseSGB2 = new System.Windows.Forms.CheckBox();
|
||||
this.cbFastDSP = new System.Windows.Forms.CheckBox();
|
||||
this.cbFastCoprocessor = new System.Windows.Forms.CheckBox();
|
||||
this.cbNoPPUSpriteLimit = new System.Windows.Forms.CheckBox();
|
||||
this.groupBox1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
|
@ -86,7 +87,7 @@
|
|||
// cbDoubleSize
|
||||
//
|
||||
this.cbDoubleSize.AutoSize = true;
|
||||
this.cbDoubleSize.Location = new System.Drawing.Point(18, 16);
|
||||
this.cbDoubleSize.Location = new System.Drawing.Point(16, 16);
|
||||
this.cbDoubleSize.Name = "cbDoubleSize";
|
||||
this.cbDoubleSize.Size = new System.Drawing.Size(178, 17);
|
||||
this.cbDoubleSize.TabIndex = 6;
|
||||
|
@ -95,7 +96,7 @@
|
|||
//
|
||||
// lblDoubleSize
|
||||
//
|
||||
this.lblDoubleSize.Location = new System.Drawing.Point(33, 34);
|
||||
this.lblDoubleSize.Location = new System.Drawing.Point(31, 33);
|
||||
this.lblDoubleSize.MaximumSize = new System.Drawing.Size(260, 0);
|
||||
this.lblDoubleSize.Name = "lblDoubleSize";
|
||||
this.lblDoubleSize.Text = "Some games are changing the resolution constantly (e.g. SD3) so this option can f" +
|
||||
|
@ -269,14 +270,14 @@
|
|||
"None",
|
||||
"Low",
|
||||
"High"});
|
||||
this.EntropyBox.Location = new System.Drawing.Point(18, 173);
|
||||
this.EntropyBox.Location = new System.Drawing.Point(16, 175);
|
||||
this.EntropyBox.Name = "EntropyBox";
|
||||
this.EntropyBox.Size = new System.Drawing.Size(128, 21);
|
||||
this.EntropyBox.TabIndex = 14;
|
||||
//
|
||||
// lblEntropy
|
||||
//
|
||||
this.lblEntropy.Location = new System.Drawing.Point(15, 157);
|
||||
this.lblEntropy.Location = new System.Drawing.Point(13, 159);
|
||||
this.lblEntropy.Name = "lblEntropy";
|
||||
this.lblEntropy.Text = "Entropy";
|
||||
//
|
||||
|
@ -288,21 +289,21 @@
|
|||
"Auto",
|
||||
"NTSC",
|
||||
"PAL"});
|
||||
this.RegionBox.Location = new System.Drawing.Point(159, 173);
|
||||
this.RegionBox.Location = new System.Drawing.Point(161, 175);
|
||||
this.RegionBox.Name = "RegionBox";
|
||||
this.RegionBox.Size = new System.Drawing.Size(128, 21);
|
||||
this.RegionBox.TabIndex = 15;
|
||||
//
|
||||
// lblRegion
|
||||
//
|
||||
this.lblRegion.Location = new System.Drawing.Point(156, 157);
|
||||
this.lblRegion.Location = new System.Drawing.Point(158, 159);
|
||||
this.lblRegion.Name = "lblRegion";
|
||||
this.lblRegion.Text = "Region";
|
||||
//
|
||||
// cbGameHotfixes
|
||||
//
|
||||
this.cbGameHotfixes.AutoSize = true;
|
||||
this.cbGameHotfixes.Location = new System.Drawing.Point(18, 107);
|
||||
this.cbGameHotfixes.Location = new System.Drawing.Point(16, 112);
|
||||
this.cbGameHotfixes.Name = "cbGameHotfixes";
|
||||
this.cbGameHotfixes.Size = new System.Drawing.Size(93, 17);
|
||||
this.cbGameHotfixes.TabIndex = 22;
|
||||
|
@ -312,7 +313,7 @@
|
|||
// cbFastPPU
|
||||
//
|
||||
this.cbFastPPU.AutoSize = true;
|
||||
this.cbFastPPU.Location = new System.Drawing.Point(159, 107);
|
||||
this.cbFastPPU.Location = new System.Drawing.Point(161, 112);
|
||||
this.cbFastPPU.Name = "cbFastPPU";
|
||||
this.cbFastPPU.Size = new System.Drawing.Size(90, 17);
|
||||
this.cbFastPPU.TabIndex = 23;
|
||||
|
@ -323,7 +324,7 @@
|
|||
// cbCropSGBFrame
|
||||
//
|
||||
this.cbCropSGBFrame.AutoSize = true;
|
||||
this.cbCropSGBFrame.Location = new System.Drawing.Point(18, 84);
|
||||
this.cbCropSGBFrame.Location = new System.Drawing.Point(16, 89);
|
||||
this.cbCropSGBFrame.Name = "cbCropSGBFrame";
|
||||
this.cbCropSGBFrame.Size = new System.Drawing.Size(105, 17);
|
||||
this.cbCropSGBFrame.TabIndex = 27;
|
||||
|
@ -333,7 +334,7 @@
|
|||
// cbUseSGB2
|
||||
//
|
||||
this.cbUseSGB2.AutoSize = true;
|
||||
this.cbUseSGB2.Location = new System.Drawing.Point(159, 84);
|
||||
this.cbUseSGB2.Location = new System.Drawing.Point(161, 89);
|
||||
this.cbUseSGB2.Name = "cbUseSGB2";
|
||||
this.cbUseSGB2.Size = new System.Drawing.Size(76, 17);
|
||||
this.cbUseSGB2.TabIndex = 30;
|
||||
|
@ -343,7 +344,7 @@
|
|||
// cbFastDSP
|
||||
//
|
||||
this.cbFastDSP.AutoSize = true;
|
||||
this.cbFastDSP.Location = new System.Drawing.Point(18, 130);
|
||||
this.cbFastDSP.Location = new System.Drawing.Point(16, 135);
|
||||
this.cbFastDSP.Name = "cbFastDSP";
|
||||
this.cbFastDSP.Size = new System.Drawing.Size(101, 17);
|
||||
this.cbFastDSP.TabIndex = 34;
|
||||
|
@ -353,13 +354,23 @@
|
|||
// cbFastCoprocessor
|
||||
//
|
||||
this.cbFastCoprocessor.AutoSize = true;
|
||||
this.cbFastCoprocessor.Location = new System.Drawing.Point(159, 130);
|
||||
this.cbFastCoprocessor.Location = new System.Drawing.Point(161, 135);
|
||||
this.cbFastCoprocessor.Name = "cbFastCoprocessor";
|
||||
this.cbFastCoprocessor.Size = new System.Drawing.Size(138, 17);
|
||||
this.cbFastCoprocessor.TabIndex = 35;
|
||||
this.cbFastCoprocessor.Text = "Coprocessor Fast Mode";
|
||||
this.cbFastCoprocessor.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// cbPPUNoSpriteLimit
|
||||
//
|
||||
this.cbNoPPUSpriteLimit.AutoSize = true;
|
||||
this.cbNoPPUSpriteLimit.Location = new System.Drawing.Point(161, 66);
|
||||
this.cbNoPPUSpriteLimit.Name = "cbNoPPUSpriteLimit";
|
||||
this.cbNoPPUSpriteLimit.Size = new System.Drawing.Size(113, 17);
|
||||
this.cbNoPPUSpriteLimit.TabIndex = 39;
|
||||
this.cbNoPPUSpriteLimit.Text = "No PPU sprite limit";
|
||||
this.cbNoPPUSpriteLimit.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// BSNESOptions
|
||||
//
|
||||
this.AcceptButton = this.btnOk;
|
||||
|
@ -367,6 +378,7 @@
|
|||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.CancelButton = this.btnCancel;
|
||||
this.ClientSize = new System.Drawing.Size(304, 379);
|
||||
this.Controls.Add(this.cbNoPPUSpriteLimit);
|
||||
this.Controls.Add(this.cbFastCoprocessor);
|
||||
this.Controls.Add(this.cbFastDSP);
|
||||
this.Controls.Add(this.cbUseSGB2);
|
||||
|
@ -396,36 +408,37 @@
|
|||
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Button btnOk;
|
||||
private System.Windows.Forms.Button btnCancel;
|
||||
private System.Windows.Forms.CheckBox cbDoubleSize;
|
||||
private BizHawk.WinForms.Controls.LocLabelEx lblDoubleSize;
|
||||
private System.Windows.Forms.GroupBox groupBox1;
|
||||
private System.Windows.Forms.CheckBox Bg4_1Checkbox;
|
||||
private System.Windows.Forms.CheckBox Bg3_1Checkbox;
|
||||
private System.Windows.Forms.CheckBox Bg2_1Checkbox;
|
||||
private System.Windows.Forms.CheckBox Bg1_1Checkbox;
|
||||
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.ComboBox EntropyBox;
|
||||
private System.Windows.Forms.Button btnOk;
|
||||
private System.Windows.Forms.Button btnCancel;
|
||||
private System.Windows.Forms.CheckBox cbDoubleSize;
|
||||
private BizHawk.WinForms.Controls.LocLabelEx lblDoubleSize;
|
||||
private System.Windows.Forms.GroupBox groupBox1;
|
||||
private System.Windows.Forms.CheckBox Bg4_1Checkbox;
|
||||
private System.Windows.Forms.CheckBox Bg3_1Checkbox;
|
||||
private System.Windows.Forms.CheckBox Bg2_1Checkbox;
|
||||
private System.Windows.Forms.CheckBox Bg1_1Checkbox;
|
||||
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.ComboBox EntropyBox;
|
||||
private WinForms.Controls.LocLabelEx lblEntropy;
|
||||
private System.Windows.Forms.ComboBox RegionBox;
|
||||
private WinForms.Controls.LocLabelEx lblRegion;
|
||||
private System.Windows.Forms.CheckBox cbGameHotfixes;
|
||||
private System.Windows.Forms.CheckBox cbFastPPU;
|
||||
private System.Windows.Forms.CheckBox Bg1_0Checkbox;
|
||||
private System.Windows.Forms.CheckBox Bg4_0Checkbox;
|
||||
private System.Windows.Forms.CheckBox Bg3_0Checkbox;
|
||||
private System.Windows.Forms.CheckBox Bg2_0Checkbox;
|
||||
private WinForms.Controls.LocLabelEx lblPriority1;
|
||||
private WinForms.Controls.LocLabelEx lblPriority0;
|
||||
private System.Windows.Forms.CheckBox cbCropSGBFrame;
|
||||
private WinForms.Controls.LocLabelEx lblRegion;
|
||||
private System.Windows.Forms.CheckBox cbGameHotfixes;
|
||||
private System.Windows.Forms.CheckBox cbFastPPU;
|
||||
private System.Windows.Forms.CheckBox Bg1_0Checkbox;
|
||||
private System.Windows.Forms.CheckBox Bg4_0Checkbox;
|
||||
private System.Windows.Forms.CheckBox Bg3_0Checkbox;
|
||||
private System.Windows.Forms.CheckBox Bg2_0Checkbox;
|
||||
private WinForms.Controls.LocLabelEx lblPriority1;
|
||||
private WinForms.Controls.LocLabelEx lblPriority0;
|
||||
private System.Windows.Forms.CheckBox cbCropSGBFrame;
|
||||
private System.Windows.Forms.CheckBox cbUseSGB2;
|
||||
private System.Windows.Forms.CheckBox cbFastDSP;
|
||||
private System.Windows.Forms.CheckBox cbFastCoprocessor;
|
||||
}
|
||||
}
|
||||
private System.Windows.Forms.CheckBox cbNoPPUSpriteLimit;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
AlwaysDoubleSize = s.AlwaysDoubleSize,
|
||||
CropSGBFrame = s.CropSGBFrame,
|
||||
NoPPUSpriteLimit = s.NoPPUSpriteLimit,
|
||||
Entropy = ss.Entropy,
|
||||
RegionOverride = ss.RegionOverride,
|
||||
Hotfixes = ss.Hotfixes,
|
||||
|
@ -48,6 +49,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
s.AlwaysDoubleSize = dlg.AlwaysDoubleSize;
|
||||
s.CropSGBFrame = dlg.CropSGBFrame;
|
||||
s.NoPPUSpriteLimit = dlg.NoPPUSpriteLimit;
|
||||
ss.Entropy = dlg.Entropy;
|
||||
ss.RegionOverride = dlg.RegionOverride;
|
||||
ss.Hotfixes = dlg.Hotfixes;
|
||||
|
@ -84,6 +86,12 @@ namespace BizHawk.Client.EmuHawk
|
|||
init => cbCropSGBFrame.Checked = value;
|
||||
}
|
||||
|
||||
private bool NoPPUSpriteLimit
|
||||
{
|
||||
get => cbNoPPUSpriteLimit.Checked;
|
||||
init => cbNoPPUSpriteLimit.Checked = value;
|
||||
}
|
||||
|
||||
private bool Hotfixes
|
||||
{
|
||||
get => cbGameHotfixes.Checked;
|
||||
|
@ -93,7 +101,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
private bool FastPPU
|
||||
{
|
||||
get => cbFastPPU.Checked;
|
||||
init => cbDoubleSize.Enabled = cbFastPPU.Checked = value;
|
||||
init => cbDoubleSize.Enabled = cbNoPPUSpriteLimit.Enabled = cbFastPPU.Checked = value;
|
||||
}
|
||||
|
||||
private bool FastDSP
|
||||
|
@ -154,7 +162,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
private void FastPPU_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
cbDoubleSize.Enabled = cbFastPPU.Checked;
|
||||
cbDoubleSize.Enabled = cbNoPPUSpriteLimit.Enabled = cbFastPPU.Checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
|
|||
public abstract void snes_set_trace_enabled(bool enabled);
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void snes_set_hooks_enabled(bool readHookEnabled, bool writeHookEnabled, bool executeHookEnabled);
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void snes_set_ppu_sprite_limit_enabled(bool enabled);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract IntPtr snes_get_audiobuffer_and_size(out int size);
|
||||
|
@ -178,7 +180,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
|
|||
exe.Dispose();
|
||||
exe = null;
|
||||
core = null;
|
||||
// serializedSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,27 +284,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
|
|||
_readonlyFiles.RemoveAll(s => !s.StartsWith("msu1/"));
|
||||
}
|
||||
|
||||
// TODO: confirm that the serializedSize is CONSTANT for any given game,
|
||||
// else this might be problematic
|
||||
// private int serializedSize;// = 284275;
|
||||
// private int serializedSize;
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
// if (serializedSize == 0)
|
||||
// serializedSize = _core.snes_serialized_size();
|
||||
// TODO: do some profiling and testing to check whether this is actually better than _exe.SaveStateBinary(writer);
|
||||
// re-adding bsnes's own serialization will need to be done once it's confirmed to be deterministic, aka after libco update
|
||||
// commented code left for debug purposes; created savestates are native bsnes savestates
|
||||
// and therefor compatible across minor core updates
|
||||
|
||||
// if (serializedSize == 0) serializedSize = core.snes_serialized_size();
|
||||
// byte[] serializedData = new byte[serializedSize];
|
||||
// _core.snes_serialize(serializedData, serializedSize);
|
||||
// core.snes_serialize(serializedData, serializedSize);
|
||||
// writer.Write(serializedData);
|
||||
exe.SaveStateBinary(writer);
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
// if (serializedSize == 0) serializedSize = core.snes_serialized_size();
|
||||
// byte[] serializedData = reader.ReadBytes(serializedSize);
|
||||
// _core.snes_unserialize(serializedData, serializedSize);
|
||||
// core.snes_unserialize(serializedData, serializedSize);
|
||||
exe.LoadStateBinary(reader);
|
||||
core.snes_msu_sync();
|
||||
}
|
||||
|
|
|
@ -16,27 +16,27 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
|
|||
public bool FrameAdvance(IController controller, bool render, bool renderSound)
|
||||
{
|
||||
using (Api.EnterExit())
|
||||
{
|
||||
FrameAdvancePre(controller, render, renderSound);
|
||||
|
||||
bool resetSignal = controller.IsPressed("Reset");
|
||||
if (resetSignal)
|
||||
{
|
||||
Api.core.snes_reset();
|
||||
}
|
||||
|
||||
bool powerSignal = controller.IsPressed("Power");
|
||||
if (powerSignal)
|
||||
{
|
||||
Api.core.snes_power();
|
||||
}
|
||||
|
||||
IsLagFrame = true;
|
||||
// run the core for one frame
|
||||
Api.core.snes_run(false);
|
||||
FrameAdvancePost();
|
||||
|
||||
return true;
|
||||
{
|
||||
FrameAdvancePre(controller, render, renderSound);
|
||||
|
||||
bool resetSignal = controller.IsPressed("Reset");
|
||||
if (resetSignal)
|
||||
{
|
||||
Api.core.snes_reset();
|
||||
}
|
||||
|
||||
bool powerSignal = controller.IsPressed("Power");
|
||||
if (powerSignal)
|
||||
{
|
||||
Api.core.snes_power();
|
||||
}
|
||||
|
||||
IsLagFrame = true;
|
||||
// run the core for one frame
|
||||
Api.core.snes_run(false);
|
||||
FrameAdvancePost();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
|
|||
Api.core.snes_set_trace_enabled(_tracer.IsEnabled());
|
||||
Api.core.snes_set_video_enabled(render);
|
||||
Api.core.snes_set_audio_enabled(renderSound);
|
||||
Api.core.snes_set_ppu_sprite_limit_enabled(!_settings.NoPPUSpriteLimit);
|
||||
}
|
||||
|
||||
internal void FrameAdvancePost()
|
||||
|
@ -80,13 +81,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
|
|||
}
|
||||
|
||||
private int UpdateAudioBuffer()
|
||||
{
|
||||
var rawAudioBuffer = Api.core.snes_get_audiobuffer_and_size(out var size);
|
||||
if (size == 0) return 0;
|
||||
if (size > _audioBuffer.Length)
|
||||
_audioBuffer = new short[size];
|
||||
Marshal.Copy(rawAudioBuffer, _audioBuffer, 0, size);
|
||||
|
||||
{
|
||||
var rawAudioBuffer = Api.core.snes_get_audiobuffer_and_size(out var size);
|
||||
if (size == 0) return 0;
|
||||
if (size > _audioBuffer.Length)
|
||||
_audioBuffer = new short[size];
|
||||
Marshal.Copy(rawAudioBuffer, _audioBuffer, 0, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.BSNES
|
|||
|
||||
public bool AlwaysDoubleSize { get; set; }
|
||||
public bool CropSGBFrame { get; set; }
|
||||
public bool NoPPUSpriteLimit { get; set; }
|
||||
|
||||
public SnesSettings Clone()
|
||||
{
|
||||
|
|
|
@ -264,6 +264,15 @@ EXPORT void snes_set_hooks_enabled(bool read_hook_enabled, bool write_hook_enabl
|
|||
platform->executeHookEnabled = execute_hook_enabled;
|
||||
}
|
||||
|
||||
EXPORT void snes_set_ppu_sprite_limit_enabled(bool enabled)
|
||||
{
|
||||
if (!SuperFamicom::system.fastPPU()) return;
|
||||
|
||||
// see ppu-fast/ppu.cpp in PPU::power(...)
|
||||
ppufast.ItemLimit = enabled ? 32 : 128;
|
||||
ppufast.TileLimit = enabled ? 34 : 128;
|
||||
}
|
||||
|
||||
|
||||
uint8_t* snes_get_effective_saveram(int* ram_size) {
|
||||
if (cartridge.has.SA1) {
|
||||
|
|
Loading…
Reference in New Issue