* Add MelonDS.cs, support opening (but not really) .nds files.

* init MelonDS

* MelonDS: Load selected ROM.

* MelonDS: FrameAdvance and frame counter.

* MelonDS: IVideoProvider

* MelonDS: Add DLL files.

* MelonDS: IInputPollable

* MelonDS: IStatable (and add forgotten file MelonDS_InputPollable.cs)

* update libmelonDS.dll

* MelonDS: ISoundProvider

* Add NDS to Global.SystemInfo, and convert screen coords when running NDS.

* set up default NDS controller

* MelonDS: ISaveRam

* MelonDS: remove romlist.bin

* MelonDS: ISettable

* Create firmware folder if it doesn't exist on Windows; otherwise, an exception is thrown.

* Add database entries for NDS bios/firmware files.

* MelonDS: Use the bios/firmware files selected in BizHawk's "Firmwares" dialog.

* MelonDS: Re-work sync settings a bit.

* NDS's firmware file contains user settings; these are over-written by sync settings, so we shouldn't allow them to impact the hash

* MelonDS: Add (currently unused) bootToFirmware sync setting, and NDSSettings dialog.

* Update NDS firmware hash; it seems I had somehow corrupted mine.

* MelonDS: Use boot to firmware sync setting.

* MelonDS: Allow user to set some firmware user settings via the NDS settings dialog.

* MelonDS: Add singleInstance attribute to core.

* MelonDS: IMemoryDomains

* update libmelonDS.dll

* MelonDS: Set up default sync settings if none are provided.

* MelonDS: Allow user to reset settings to default.

* MelonDS: bios+firmware files are recommended

* libmelonDS.dll

* MelonDS: Don't use real time.

* MelonDS: Update to reflect new way of handling RTC in MelonDS.

* MelonDS: Notify if savestate load failed.

* update MelonDS.dll

* MelonDS: Allow user to set startup date/time in settings dialog.

* MelonDS: Create melon directory if it doesn't already exist.

* Don't include Designer's "fixes" in PR (partially reverts 56b474c00)

* Don't show a broken console window; alert user of need to restart instead.
This fixes an error related to MelonDS trying to use the broken stdout stream.

* update default NDS controls to match other updated controls

* Implement a system bus, using ARM9 read/writes.

* MelonDS: Allow BizHawk to change the contents of the frame buffer.

* update libmelonDS.dll

* fix stuff that was merged incorrectly, or was broken by merge

* update libmelonDS.dll
(includes memory leak fix)

* update libmelonDS.dll
(fixes memory leak and an occasional savestate crash)

* fix stuff that broke with the merge

* cleanups, remove stuff that is no longer needed by service interaces

* simplify DS MemoryDomains

* DS - fix order of controller buttons to be consistent with other consoles.  This probably breaks any existing movies made on this core, but those would have been experiments, right?

* NDSSettings - make min value for day and month 0, whiel those aren't "valid" values they are the default values in the core for whatever reason, better to not crash on load and not show a value that isn't actually the setting.  This can easily be reverted if the core changes to default to 1

Co-authored-by: YoshiRulz <OSSYoshiRulz@gmail.com>
Co-authored-by: adelikat <adelikat@tasvideos.org>
This commit is contained in:
SuuperW 2020-03-21 10:53:30 -05:00 committed by GitHub
parent 04a33600da
commit c59d8130bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1265 additions and 26 deletions

View File

@ -340,6 +340,22 @@
"R": "E, J1 B6, X1 RightShoulder",
"Power": ""
},
"NDS Controller": {
"A": "X, J1 B3, X1 B",
"B": "Z, J1 B2, X1 A",
"X": "S, J1 B4, X1 Y",
"Y": "A, J1 B1, X1 X",
"Up": "Up, J1 POV1U, X1 DpadUp, X1 LStickUp",
"Down": "Down, J1 POV1D, X1 DpadDown, X1 LStickDown",
"Left": "Left, J1 POV1L, X1 DpadLeft, X1 LStickLeft",
"Right": "Right, J1 POV1R, X1 DpadRight, X1 LStickRight",
"L": "W, J1 B5, X1 LeftShoulder",
"R": "E, J1 B6, X1 RightShoulder",
"Select": "Space, J1 B9, X1 Back",
"Start": "Return, J1 B10, X1 Start",
"Lid": "",
"Touch": "WMouse L"
},
"Atari 2600 Basic Controller": {
"Reset": "D, J1 B9, X1 Back",
"Select": "S, J1 B10, X1 Start",
@ -1616,7 +1632,19 @@
"Deadzone": 0.0
}
},
"Nintendo 64 Controller": {
"NDS Controller": {
"TouchX": {
"Value": "WMouse X",
"Mult": 1.0,
"Deadzone": 0.0
},
"TouchY": {
"Value": "WMouse Y",
"Mult": 1.0,
"Deadzone": 0.0
}
},
"Nintento 64 Controller": {
"P1 X Axis": {
"Value": "X1 LeftThumbX Axis",
"Mult": 1.0,

View File

@ -40,6 +40,7 @@ namespace BizHawk.Client.ApiHawk
"Libretro" => CoreSystem.Libretro,
"Lynx" => CoreSystem.Lynx,
"SMS" => CoreSystem.MasterSystem,
"NDS" => CoreSystem.NintendoDS,
"NES" => CoreSystem.NES,
"N64" => CoreSystem.Nintendo64,
"NULL" => CoreSystem.Null,

View File

@ -17,6 +17,7 @@
GameBoy,
DualGameBoy,
GameBoyAdvance,
NintendoDS,
Nintendo64,
NES,
SNES,

View File

@ -240,10 +240,15 @@ namespace BizHawk.Client.Common
continue;
}
// compute its hash
var rff = reader.Read(fi);
ri.Size = fi.Length;
ri.Hash = rff.Hash;
// compute its hash
RealFirmwareFile rff;
// NDS's firmware file contains user settings; these are over-written by sync settings, so we shouldn't allow them to impact the hash
if (fr.SystemId == "NDS" && fr.FirmwareId == "firmware")
rff = reader.Read(new FileInfo(Emulation.Cores.Consoles.Nintendo.NDS.MelonDS.CreateModifiedFirmware(userSpec)));
else
rff = reader.Read(fi);
ri.Size = fi.Length;
ri.Hash = rff.Hash;
// check whether it was a known file anyway, and go ahead and bind to the known file, as a perk (the firmwares config doesn't really use this information right now)
if (FirmwareDatabase.FirmwareFilesByHash.TryGetValue(rff.Hash, out var ff))

View File

@ -102,6 +102,11 @@ namespace BizHawk.Client.Common
/// </summary>
public static SystemInfo GBC { get; } = new SystemInfo("Gameboy Color", CoreSystem.GameBoy, 1, StandardButtons);
/// <summary>
/// Gets the <see cref="SystemInfo"/> instance for Nintendo DS
/// </summary>
public static SystemInfo NDS { get; } = new SystemInfo("NDS", CoreSystem.NintendoDS, 1, StandardButtons | JoypadButton.L | JoypadButton.R | JoypadButton.X | JoypadButton.Y);
/// <summary>
/// Gets the <see cref="SystemInfo"/> instance for Genesis
/// </summary>

View File

@ -342,7 +342,8 @@
<Compile Update="config/N64/N64VideoPluginconfig.cs" SubType="Form" />
<Compile Update="config/N64/N64VideoPluginconfig.Designer.cs" DependentUpon="N64VideoPluginconfig.cs" />
<EmbeddedResource Update="config/N64/N64VideoPluginconfig.resx" DependentUpon="N64VideoPluginconfig.cs" />
<Compile Update="config/NES/NesControllerSettings.cs" SubType="Form" />
<Compile Update="config/NDS/NDSSettings.cs" SubType="Form" />
<Compile Update="config/NDS/NDSSettings.Designer.cs" DependentUpon="NDSSettings.cs" /> <Compile Update="config/NES/NesControllerSettings.cs" SubType="Form" />
<Compile Update="config/NES/NesControllerSettings.Designer.cs" DependentUpon="NesControllerSettings.cs" />
<EmbeddedResource Update="config/NES/NesControllerSettings.resx" DependentUpon="NesControllerSettings.cs" />
<Compile Update="config/NES/NESGraphicsConfig.cs" SubType="Form" />

View File

@ -394,6 +394,8 @@ namespace BizHawk.Client.EmuHawk
// otherwise, have the filter program untransform it
Vector2 v = new Vector2(p.X, p.Y);
v = _currentFilterProgram.UntransformPoint("default", v);
if (Global.Emulator.SystemId == "NDS")
v.Y = 2 * v.Y - (Global.Emulator as IVideoProvider).BufferHeight;
return new Point((int)v.X, (int)v.Y);
}

View File

@ -102,6 +102,8 @@ namespace BizHawk.Client.EmuHawk.CoreExtensions
return SystemInfo.Coleco;
case "GBA":
return SystemInfo.GBA;
case "NDS":
return SystemInfo.NDS;
case "N64":
return SystemInfo.N64;
case "SAT":

View File

@ -459,6 +459,8 @@
this.ShowMenuContextMenuSeparator = new System.Windows.Forms.ToolStripSeparator();
this.ShowMenuContextMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.timerMouseIdle = new System.Windows.Forms.Timer(this.components);
this.NDSSubMenu = new System.Windows.Forms.ToolStripMenuItem();
this.NDSSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.MainformMenu.SuspendLayout();
this.MainStatusBar.SuspendLayout();
this.MainFormContextMenu.SuspendLayout();
@ -468,26 +470,27 @@
//
this.MainformMenu.ClickThrough = true;
this.MainformMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.FileSubMenu,
this.EmulationSubMenu,
this.ViewSubMenu,
this.ConfigSubMenu,
this.ToolsSubMenu,
this.NESSubMenu,
this.PCESubMenu,
this.SMSSubMenu,
this.TI83SubMenu,
this.AtariSubMenu,
this.A7800SubMenu,
this.GBSubMenu,
this.GBASubMenu,
this.PSXSubMenu,
this.SNESSubMenu,
this.ColecoSubMenu,
this.N64SubMenu,
this.SaturnSubMenu,
this.DGBSubMenu,
this.DGBHawkSubMenu,
this.FileSubMenu,
this.EmulationSubMenu,
this.ViewSubMenu,
this.ConfigSubMenu,
this.ToolsSubMenu,
this.NESSubMenu,
this.PCESubMenu,
this.SMSSubMenu,
this.TI83SubMenu,
this.AtariSubMenu,
this.A7800SubMenu,
this.GBSubMenu,
this.GBASubMenu,
this.NDSSubMenu,
this.PSXSubMenu,
this.SNESSubMenu,
this.ColecoSubMenu,
this.N64SubMenu,
this.SaturnSubMenu,
this.DGBSubMenu,
this.DGBHawkSubMenu,
this.GB3xSubMenu,
this.GB4xSubMenu,
this.GGLSubMenu,
@ -3984,6 +3987,21 @@
this.timerMouseIdle.Interval = 2000;
this.timerMouseIdle.Tick += new System.EventHandler(this.TimerMouseIdle_Tick);
//
// NDSSubMenu
//
this.NDSSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.NDSSettingsMenuItem});
this.NDSSubMenu.Name = "NDSSubMenu";
this.NDSSubMenu.Size = new System.Drawing.Size(42, 19);
this.NDSSubMenu.Text = "NDS";
//
// NDSSettingsMenuItem
//
this.NDSSettingsMenuItem.Name = "NDSSettingsMenuItem";
this.NDSSettingsMenuItem.Size = new System.Drawing.Size(180, 22);
this.NDSSettingsMenuItem.Text = "Settings...";
this.NDSSettingsMenuItem.Click += new System.EventHandler(this.NDSSettingsMenuItem_Click);
//
// MainForm
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
@ -4453,6 +4471,8 @@
private System.Windows.Forms.ToolStripMenuItem SubNesHawkMenuItem;
private System.Windows.Forms.ToolStripMenuItem SubGBHawkMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator38;
private System.Windows.Forms.ToolStripMenuItem NDSSubMenu;
private System.Windows.Forms.ToolStripMenuItem NDSSettingsMenuItem;
private System.Windows.Forms.ToolStripMenuItem arcadeToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem settingsToolStripMenuItem2;
}

View File

@ -2023,6 +2023,21 @@ namespace BizHawk.Client.EmuHawk
#endregion
#region NDS
private void NDSSettingsMenuItem_Click(object sender, EventArgs e)
{
DialogResult result = new NDSSettings().ShowDialog();
if (result == DialogResult.OK)
GlobalWin.OSD.AddMessage("Settings saved.");
else if (result == DialogResult.Yes)
FlagNeedsReboot();
else
GlobalWin.OSD.AddMessage("Settings aborted.");
}
#endregion
#region PSX
private void PsxSubMenu_DropDownOpened(object sender, EventArgs e)

View File

@ -1796,6 +1796,7 @@ namespace BizHawk.Client.EmuHawk
SMSSubMenu.Visible = false;
GBSubMenu.Visible = false;
GBASubMenu.Visible = false;
NDSSubMenu.Visible = false;
AtariSubMenu.Visible = false;
A7800SubMenu.Visible = false;
SNESSubMenu.Visible = false;
@ -1859,6 +1860,9 @@ namespace BizHawk.Client.EmuHawk
case "GBA":
GBASubMenu.Visible = true;
break;
case "NDS":
NDSSubMenu.Visible = true;
break;
case "A26":
AtariSubMenu.Visible = true;
break;
@ -2208,6 +2212,7 @@ namespace BizHawk.Client.EmuHawk
new FilesystemFilter("Nintendo 64", new[] { "z64", "v64", "n64" }),
new FilesystemFilter("Gameboy", new[] { "gb", "gbc", "sgb" }, addArchiveExts: true),
new FilesystemFilter("Gameboy Advance", new[] { "gba" }, addArchiveExts: true),
new FilesystemFilter("Nintendo DS", new[] { "nds" }),
new FilesystemFilter("Master System", new[] { "sms", "gg", "sg" }, addArchiveExts: true),
new FilesystemFilter("PC Engine", new[] { "pce", "sgx", "cue", "ccd", "mds" }, addArchiveExts: true),
new FilesystemFilter("Atari 2600", new[] { "a26" }, devBuildExtraExts: new[] { "bin" }, addArchiveExts: true),

View File

@ -41,6 +41,7 @@ namespace BizHawk.Client.EmuHawk
["A78"] = "Atari 7800",
["Coleco"] = "Colecovision",
["GBA"] = "GBA",
["NDS"] = "Nintendo DS",
["TI83"] = "TI-83",
["INTV"] = "Intellivision",
["C64"] = "C64",

View File

@ -0,0 +1,281 @@
namespace BizHawk.Client.EmuHawk
{
partial class NDSSettings
{
/// <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.components = new System.ComponentModel.Container();
this.chkBootToFirmware = new System.Windows.Forms.CheckBox();
this.btnCancel = new System.Windows.Forms.Button();
this.btnSave = new System.Windows.Forms.Button();
this.ttipFirmware = new System.Windows.Forms.ToolTip(this.components);
this.txtName = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.numBirthDay = new System.Windows.Forms.NumericUpDown();
this.numBirthMonth = new System.Windows.Forms.NumericUpDown();
this.label3 = new System.Windows.Forms.Label();
this.cbxFavColor = new System.Windows.Forms.ComboBox();
this.label2 = new System.Windows.Forms.Label();
this.btnDefault = new System.Windows.Forms.Button();
this.dtpStartupTime = new System.Windows.Forms.DateTimePicker();
this.label4 = new System.Windows.Forms.Label();
this.groupBox1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.numBirthDay)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.numBirthMonth)).BeginInit();
this.SuspendLayout();
//
// chkBootToFirmware
//
this.chkBootToFirmware.AutoSize = true;
this.chkBootToFirmware.Location = new System.Drawing.Point(12, 12);
this.chkBootToFirmware.Name = "chkBootToFirmware";
this.chkBootToFirmware.Size = new System.Drawing.Size(102, 17);
this.chkBootToFirmware.TabIndex = 0;
this.chkBootToFirmware.Text = "Boot to firmware";
this.ttipFirmware.SetToolTip(this.chkBootToFirmware, "This option requires that a firmware file be in use, as well as both bios files. " +
"See Config -> Firmwares.");
this.chkBootToFirmware.UseVisualStyleBackColor = true;
//
// btnCancel
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.Location = new System.Drawing.Point(144, 175);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(60, 23);
this.btnCancel.TabIndex = 1;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
//
// btnSave
//
this.btnSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnSave.Location = new System.Drawing.Point(78, 175);
this.btnSave.Name = "btnSave";
this.btnSave.Size = new System.Drawing.Size(60, 23);
this.btnSave.TabIndex = 1;
this.btnSave.Text = "Save";
this.btnSave.UseVisualStyleBackColor = true;
this.btnSave.Click += new System.EventHandler(this.btnSave_Click);
//
// txtName
//
this.txtName.Location = new System.Drawing.Point(50, 16);
this.txtName.MaxLength = 10;
this.txtName.Name = "txtName";
this.txtName.Size = new System.Drawing.Size(94, 20);
this.txtName.TabIndex = 2;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(6, 19);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(38, 13);
this.label1.TabIndex = 3;
this.label1.Text = "Name:";
//
// groupBox1
//
this.groupBox1.Controls.Add(this.numBirthDay);
this.groupBox1.Controls.Add(this.numBirthMonth);
this.groupBox1.Controls.Add(this.label3);
this.groupBox1.Controls.Add(this.cbxFavColor);
this.groupBox1.Controls.Add(this.label2);
this.groupBox1.Controls.Add(this.txtName);
this.groupBox1.Controls.Add(this.label1);
this.groupBox1.Location = new System.Drawing.Point(12, 35);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(192, 106);
this.groupBox1.TabIndex = 4;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Firmware settings";
//
// numBirthDay
//
this.numBirthDay.Location = new System.Drawing.Point(133, 69);
this.numBirthDay.Maximum = new decimal(new int[] {
31,
0,
0,
0});
this.numBirthDay.Minimum = new decimal(new int[] {
0,
0,
0,
0});
this.numBirthDay.Name = "numBirthDay";
this.numBirthDay.Size = new System.Drawing.Size(36, 20);
this.numBirthDay.TabIndex = 7;
this.numBirthDay.Value = new decimal(new int[] {
1,
0,
0,
0});
//
// numBirthMonth
//
this.numBirthMonth.Location = new System.Drawing.Point(91, 69);
this.numBirthMonth.Maximum = new decimal(new int[] {
12,
0,
0,
0});
this.numBirthMonth.Minimum = new decimal(new int[] {
0,
0,
0,
0});
this.numBirthMonth.Name = "numBirthMonth";
this.numBirthMonth.Size = new System.Drawing.Size(36, 20);
this.numBirthMonth.TabIndex = 6;
this.numBirthMonth.Value = new decimal(new int[] {
1,
0,
0,
0});
this.numBirthMonth.ValueChanged += new System.EventHandler(this.numBirthMonth_ValueChanged);
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(6, 71);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(79, 13);
this.label3.TabIndex = 6;
this.label3.Text = "Birthday (M/D):";
//
// cbxFavColor
//
this.cbxFavColor.FormattingEnabled = true;
this.cbxFavColor.Items.AddRange(new object[] {
"Gray",
"Brown",
"Red",
"Pink",
"Orange",
"Yellow",
"Lime Green",
"Green",
"Dark Green",
"Sea Green",
"Turquoise",
"Blue",
"Dark Blue",
"Dark Purple",
"Violet",
"Magenta"});
this.cbxFavColor.Location = new System.Drawing.Point(50, 42);
this.cbxFavColor.Name = "cbxFavColor";
this.cbxFavColor.Size = new System.Drawing.Size(94, 21);
this.cbxFavColor.TabIndex = 5;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(6, 45);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(34, 13);
this.label2.TabIndex = 4;
this.label2.Text = "Color:";
//
// btnDefault
//
this.btnDefault.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnDefault.Location = new System.Drawing.Point(12, 175);
this.btnDefault.Name = "btnDefault";
this.btnDefault.Size = new System.Drawing.Size(60, 23);
this.btnDefault.TabIndex = 1;
this.btnDefault.Text = "Default";
this.btnDefault.UseVisualStyleBackColor = true;
this.btnDefault.Click += new System.EventHandler(this.btnDefault_Click);
//
// dtpStartupTime
//
this.dtpStartupTime.CustomFormat = "yyyy-MM-dd HH:mm:ss";
this.dtpStartupTime.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
this.dtpStartupTime.Location = new System.Drawing.Point(68, 147);
this.dtpStartupTime.MaxDate = new System.DateTime(2099, 12, 31, 23, 59, 59, 0);
this.dtpStartupTime.MinDate = new System.DateTime(2000, 1, 1, 0, 0, 0, 0);
this.dtpStartupTime.Name = "dtpStartupTime";
this.dtpStartupTime.Size = new System.Drawing.Size(136, 20);
this.dtpStartupTime.TabIndex = 5;
this.dtpStartupTime.Value = new System.DateTime(2000, 1, 1, 0, 0, 0, 0);
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(12, 150);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(57, 13);
this.label4.TabIndex = 6;
this.label4.Text = "Date/time:";
//
// NDSSettings
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(216, 210);
this.Controls.Add(this.label4);
this.Controls.Add(this.dtpStartupTime);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.btnDefault);
this.Controls.Add(this.btnSave);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.chkBootToFirmware);
this.Name = "NDSSettings";
this.Text = "NDSSettings";
this.Load += new System.EventHandler(this.NDSSettings_Load);
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.numBirthDay)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.numBirthMonth)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.CheckBox chkBootToFirmware;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.Button btnSave;
private System.Windows.Forms.ToolTip ttipFirmware;
private System.Windows.Forms.TextBox txtName;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.ComboBox cbxFavColor;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.NumericUpDown numBirthDay;
private System.Windows.Forms.NumericUpDown numBirthMonth;
private System.Windows.Forms.Button btnDefault;
private System.Windows.Forms.DateTimePicker dtpStartupTime;
private System.Windows.Forms.Label label4;
}
}

View File

@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using BizHawk.Client.Common;
using BizHawk.Emulation.Cores.Consoles.Nintendo.NDS;
namespace BizHawk.Client.EmuHawk
{
public partial class NDSSettings : Form
{
public NDSSettings()
{
InitializeComponent();
}
MelonDS.MelonSyncSettings syncSettings;
private void NDSSettings_Load(object sender, EventArgs e)
{
syncSettings = Global.Config.GetCoreSyncSettings<MelonDS>() as MelonDS.MelonSyncSettings;
chkBootToFirmware.Checked = syncSettings.bootToFirmware;
txtName.Text = syncSettings.nickname;
cbxFavColor.SelectedIndex = syncSettings.favoriteColor;
numBirthDay.Value = syncSettings.birthdayDay;
numBirthMonth.Value = syncSettings.birthdayMonth;
dtpStartupTime.Value = DateTimeOffset.FromUnixTimeSeconds(syncSettings.timeAtBoot).UtcDateTime;
}
private void btnCancel_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
Close();
}
private void btnSave_Click(object sender, EventArgs e)
{
syncSettings.bootToFirmware = chkBootToFirmware.Checked;
syncSettings.nickname = txtName.Text;
syncSettings.favoriteColor = (byte)cbxFavColor.SelectedIndex;
syncSettings.birthdayDay = (byte)numBirthDay.Value;
syncSettings.birthdayMonth = (byte)numBirthMonth.Value;
// Converting to local time is necessary, because user-set values are "unspecified" which ToUnixTimeSeconds assumes are local.
// But ToLocalTime assumes these are UTC. So here we are adding and then subtracting the UTC-to-local offset.
syncSettings.timeAtBoot = (uint)new DateTimeOffset(dtpStartupTime.Value.ToLocalTime()).ToUnixTimeSeconds();
Global.Config.PutCoreSyncSettings<MelonDS>(syncSettings);
bool reboot = (Global.Emulator as MelonDS).PutSyncSettings(syncSettings);
DialogResult = reboot ? DialogResult.Yes : DialogResult.OK;
Close();
}
private void numBirthMonth_ValueChanged(object sender, EventArgs e)
{
switch (numBirthMonth.Value)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
numBirthDay.Maximum = 31;
break;
case 4:
case 6:
case 9:
case 11:
numBirthDay.Maximum = 30;
break;
case 2:
numBirthDay.Maximum = 29;
break;
}
}
private void btnDefault_Click(object sender, EventArgs e)
{
if (MessageBox.Show("Revert to and save default settings?", "default settings", MessageBoxButtons.OKCancel) == DialogResult.OK)
{
bool reboot = (Global.Emulator as MelonDS).PutSyncSettings(null);
syncSettings = (Global.Emulator as MelonDS).GetSyncSettings();
Global.Config.PutCoreSyncSettings<MelonDS>(syncSettings);
DialogResult = reboot ? DialogResult.Yes : DialogResult.OK;
Close();
}
}
}
}

View File

@ -0,0 +1,123 @@
<?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>
<metadata name="ttipFirmware.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View File

@ -229,6 +229,9 @@ namespace BizHawk.Emulation.Common
case ".GBA":
game.System = "GBA";
break;
case ".NDS":
game.System = "NDS";
break;
case ".SMS":
game.System = "SMS";

View File

@ -45,6 +45,10 @@ namespace BizHawk.Emulation.Common
Option("GBA", "Bios", gbaNormal);
Option("GBA", "Bios", gbaJDebug);
FirmwareAndOption("24F67BDEA115A2C847C8813A262502EE1607B7DF", 16384, "NDS", "bios7", "bios7.bin", "ARM7 BIOS");
FirmwareAndOption("BFAAC75F101C135E32E2AAF541DE6B1BE4C8C62D", 4096, "NDS", "bios9", "bios9.bin", "ARM9 BIOS");
FirmwareAndOption("22A7547DBC302BCBFB4005CFB5A2D426D3F85AC6", 262144, "NDS", "firmware", "firmware.bin", "NDS Firmware (note: given hash is with blank user data)");
FirmwareAndOption("E4ED47FAE31693E016B081C6BDA48DA5B70D7CCB", 512, "Lynx", "Boot", "lynxboot.img", "Boot Rom");
////FirmwareAndOption("24F67BDEA115A2C847C8813A262502EE1607B7DF", "NDS", "Bios_Arm7", "biosnds7.rom", "ARM7 Bios");

View File

@ -0,0 +1,174 @@
using System;
using System.Runtime.InteropServices;
using System.IO;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
{
[Core("MelonDS", "Arisotura", false, false, null, null, true)]
public unsafe partial class MelonDS : IEmulator
{
private readonly BasicServiceProvider _serviceProvider;
public IEmulatorServiceProvider ServiceProvider => _serviceProvider;
public ControllerDefinition ControllerDefinition { get; }
public int Frame => GetFrameCount();
public string SystemId => "NDS";
public bool DeterministicEmulation => true;
internal CoreComm CoreComm { get; }
public void Dispose()
{
Deinit();
}
public bool FrameAdvance(IController controller, bool render, bool rendersound = true)
{
int buttons = (controller.IsPressed("A") ? 1 : 0) | (controller.IsPressed("B") ? 2 : 0)
| (controller.IsPressed("Select") ? 4 : 0) | (controller.IsPressed("Start") ? 8 : 0)
| (controller.IsPressed("Right") ? 0x10 : 0) | (controller.IsPressed("Left") ? 0x20 : 0)
| (controller.IsPressed("Up") ? 0x40 : 0) | (controller.IsPressed("Down") ? 0x80 : 0)
| (controller.IsPressed("R") ? 0x100 : 0) | (controller.IsPressed("L") ? 0x200 : 0)
| (controller.IsPressed("X") ? 0x400 : 0) | (controller.IsPressed("Y") ? 0x800 : 0)
| (controller.IsPressed("Touch") ? 0x2000 : 0)
| (controller.IsPressed("Lid") ? 0x4000 : 0);
FrameAdvance((short)buttons, (byte)controller.GetFloat("TouchX"), (byte)controller.GetFloat("TouchY"));
getNewBuffer = true;
return true;
}
public void ResetCounters()
{
_ResetCounters();
}
// debug path/build for easier testing
//const string dllPath = "../../MelonDS/build/libmelonDS.dll";
const string dllPath = "dll/libmelonDS.dll";
[DllImport(dllPath)]
private static extern bool Init();
[DllImport(dllPath)]
private static extern void Deinit();
[DllImport(dllPath)]
private static extern void LoadROM(byte* file, int fileSize);
[DllImport(dllPath, EntryPoint = "ResetCounters")]
private static extern void _ResetCounters();
[DllImport(dllPath)]
private static extern int GetFrameCount();
[DllImport(dllPath)]
private static extern void FrameAdvance(short buttons, byte touchX, byte touchY);
[CoreConstructor("NDS")]
public MelonDS(byte[] file, CoreComm comm, object settings, object syncSettings)
{
_serviceProvider = new BasicServiceProvider(this);
ControllerDefinition = new ControllerDefinition { Name = "NDS Controller" };
ControllerDefinition.BoolButtons.Add("Up");
ControllerDefinition.BoolButtons.Add("Down");
ControllerDefinition.BoolButtons.Add("Right");
ControllerDefinition.BoolButtons.Add("Left");
ControllerDefinition.BoolButtons.Add("Y");
ControllerDefinition.BoolButtons.Add("B");
ControllerDefinition.BoolButtons.Add("X");
ControllerDefinition.BoolButtons.Add("A");
ControllerDefinition.BoolButtons.Add("L");
ControllerDefinition.BoolButtons.Add("R");
ControllerDefinition.BoolButtons.Add("Start");
ControllerDefinition.BoolButtons.Add("Select");
ControllerDefinition.BoolButtons.Add("Lid");
ControllerDefinition.BoolButtons.Add("Touch");
ControllerDefinition.FloatControls.Add("TouchX");
ControllerDefinition.FloatRanges.Add(new ControllerDefinition.AxisRange(0, 128, 255));
ControllerDefinition.FloatControls.Add("TouchY");
ControllerDefinition.FloatRanges.Add(new ControllerDefinition.AxisRange(0, 96, 191));
CoreComm = comm;
if (!Init())
throw new Exception("Failed to init NDS.");
SetUpFiles();
PutSyncSettings(syncSettings as MelonSyncSettings);
InitMemoryDomains();
fixed (byte* f = file)
{
LoadROM(f, file.Length);
}
}
/// <summary>
/// MelonDS expects bios and firmware files at a specific location.
/// This should never be called without an accompanying call to PutSyncSettings.
/// </summary>
private void SetUpFiles()
{
Directory.CreateDirectory("melon");
byte[] fwBytes;
bool missingAny = false;
fwBytes = CoreComm.CoreFileProvider.GetFirmware("NDS", "bios7", false);
if (fwBytes != null)
File.WriteAllBytes("melon/bios7.bin", fwBytes);
else
{
File.Delete("melon/bios7.bin");
missingAny = true;
}
fwBytes = CoreComm.CoreFileProvider.GetFirmware("NDS", "bios9", false);
if (fwBytes != null)
File.WriteAllBytes("melon/bios9.bin", fwBytes);
else
{
File.Delete("melon/bios9.bin");
missingAny = true;
}
fwBytes = CoreComm.CoreFileProvider.GetFirmware("NDS", "firmware", false);
if (fwBytes != null)
File.WriteAllBytes("melon/firmware.bin", fwBytes);
else
{
File.Delete("melon/firmware.bin");
missingAny = true;
}
if (missingAny)
CoreComm.Notify("NDS bios and firmware files are recommended; at least one is missing.");
}
/// <summary>
/// Creates a modified copy of the given firmware file, with the user settings erased.
/// </summary>
/// <returns>Returns a path to the new file.</returns>
public static string CreateModifiedFirmware(string firmwarePath)
{
const string newPath = "melon/tohash.bin";
byte[] bytes = File.ReadAllBytes(firmwarePath);
// There are two regions for user settings
int settingsLength = getUserSettingsLength();
for (int i = bytes.Length - 0x200; i < bytes.Length - 0x200 + settingsLength; i++)
bytes[i] = 0xFF;
for (int i = bytes.Length - 0x100; i < bytes.Length - 0x100 + settingsLength; i++)
bytes[i] = 0xFF;
File.WriteAllBytes(newPath, bytes);
return newPath;
}
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
{
partial class MelonDS : IInputPollable
{
public int LagCount { get => GetLagFrameCount(); set => throw new NotImplementedException(); }
public bool IsLagFrame { get => _IsLagFrame(); set => throw new NotImplementedException(); }
public IInputCallbackSystem InputCallbacks => throw new NotImplementedException();
[DllImport(dllPath, EntryPoint = "IsLagFrame")]
private static extern bool _IsLagFrame();
[DllImport(dllPath)]
private static extern int GetLagFrameCount();
}
}

View File

@ -0,0 +1,121 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
{
unsafe partial class MelonDS : IMemoryDomains
{
SortedList<string, MemoryDomain> domains;
private void InitMemoryDomains()
{
MainMemory = new MemoryDomainIntPtr("RAM", MemoryDomain.Endian.Little, (IntPtr)GetMainMemory(), GetMainMemorySize(), true, 4);
SystemBus = new MelonSystemBus();
domains = new SortedList<string, MemoryDomain>();
domains.Add("RAM", MainMemory);
domains.Add("System Bus", SystemBus);
}
public MemoryDomain this[string name] => domains[name];
public bool Has(string name)
{
return domains.ContainsKey(name);
}
public MemoryDomain MainMemory { get; private set; }
[DllImport(dllPath)]
private static extern byte* GetMainMemory();
[DllImport(dllPath)]
private static extern int GetMainMemorySize();
// NDS has two CPUs, with different memory mappings.
public bool HasSystemBus => true;
public MemoryDomain SystemBus { get; private set; }
private class MelonSystemBus : MemoryDomain
{
[DllImport(dllPath)]
private static extern byte ARM9Read8(uint addr);
[DllImport(dllPath)]
private static extern void ARM9Write8(uint addr, byte value);
[DllImport(dllPath)]
private static extern ushort ARM9Read16(uint addr);
[DllImport(dllPath)]
private static extern void ARM9Write16(uint addr, ushort value);
[DllImport(dllPath)]
private static extern uint ARM9Read32(uint addr);
[DllImport(dllPath)]
private static extern void ARM9Write32(uint addr, uint value);
public MelonSystemBus()
{
Name = "System Bus";
Size = 0x0B00_0000;
WordSize = 4;
EndianType = Endian.Big;
Writable = true;
}
public override byte PeekByte(long addr)
{
return ARM9Read8((uint)addr);
}
public override void PokeByte(long addr, byte val)
{
ARM9Write8((uint)addr, val);
}
public override ushort PeekUshort(long addr, bool bigEndian)
{
ushort ret = ARM9Read16((uint)addr);
if (bigEndian)
ret = SwapEndianness(ret);
return ret;
}
public override void PokeUshort(long addr, ushort val, bool bigEndian)
{
if (bigEndian)
val = SwapEndianness(val);
ARM9Write16((uint)addr, val);
}
public override uint PeekUint(long addr, bool bigEndian)
{
uint ret = ARM9Read32((uint)addr);
if (!bigEndian)
ret = SwapEndianness(ret);
return ret;
}
public override void PokeUint(long addr, uint val, bool bigEndian)
{
if (!bigEndian)
val = SwapEndianness(val);
ARM9Write32((uint)addr, val);
}
}
public IEnumerator<MemoryDomain> GetEnumerator()
{
return domains.Values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public static ushort SwapEndianness(ushort value)
{
return (ushort)((value >> 8) | (value << 8));
}
public static uint SwapEndianness(uint value)
{
return (value >> 24) | ((value & 0x00ff0000) >> 8) | ((value & 0x0000ff00) << 8) | (value << 24);
}
}
}

View File

@ -0,0 +1,40 @@
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
{
unsafe partial class MelonDS : ISaveRam
{
public bool SaveRamModified => IsSRAMModified();
public byte[] CloneSaveRam()
{
int length = GetSRAMLength();
byte[] data = new byte[length];
fixed (byte* dst = data)
{
GetSRAM(dst, length);
}
return data;
}
public void StoreSaveRam(byte[] data)
{
fixed (byte* src = data)
{
SetSRAM(src, data.Length);
}
}
[DllImport(dllPath)]
private static extern int GetSRAMLength();
[DllImport(dllPath)]
private static extern bool IsSRAMModified();
[DllImport(dllPath)]
private static extern void GetSRAM(byte* dst, int length);
[DllImport(dllPath)]
private static extern void SetSRAM(byte* src, int length);
}
}

View File

@ -0,0 +1,139 @@
using System;
using System.Text;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common;
using Newtonsoft.Json;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
{
unsafe partial class MelonDS : ISettable<object, MelonDS.MelonSyncSettings>
{
public object GetSettings()
{
return new object();
}
public MelonSyncSettings GetSyncSettings()
{
MelonSyncSettings ret = new MelonSyncSettings();
fixed (byte* ptr = ret.userSettings)
{
if (!GetUserSettings(ptr))
return null;
}
ret.bootToFirmware = !GetDirectBoot();
ret.timeAtBoot = GetTimeAtBoot();
return ret;
}
public bool PutSettings(object o)
{
return false;
}
public bool PutSyncSettings(MelonSyncSettings o)
{
if (o == null)
{
o = new MelonSyncSettings();
SetUserSettings(null);
}
else
{
fixed (byte* ptr = o.userSettings)
SetUserSettings(ptr);
}
SetDirectBoot(!o.bootToFirmware);
SetTimeAtBoot(o.timeAtBoot);
// At present, no sync settings can be modified without requiring a reboot.
return true;
}
[DllImport(dllPath)]
private static extern bool GetUserSettings(byte* dst);
[DllImport(dllPath)]
private static extern int getUserSettingsLength();
static int userSettingsLength = getUserSettingsLength();
[DllImport(dllPath)]
private static extern void SetUserSettings(byte* src);
[DllImport(dllPath)]
private static extern bool GetDirectBoot();
[DllImport(dllPath)]
private static extern void SetDirectBoot(bool value);
[DllImport(dllPath)]
private static extern void SetTimeAtBoot(uint value);
[DllImport(dllPath)]
private static extern uint GetTimeAtBoot();
unsafe public class MelonSettings
{
}
public class MelonSyncSettings
{
public MelonSyncSettings()
{
userSettings = new byte[userSettingsLength];
}
public bool bootToFirmware = false;
public uint timeAtBoot = 946684800; // 2000-01-01 00:00:00 (earliest date possible on a DS)
public byte[] userSettings;
[JsonIgnore]
public byte favoriteColor
{
get => userSettings[2];
set { userSettings[2] = value; }
}
[JsonIgnore]
public byte birthdayMonth
{
get => userSettings[3];
set { userSettings[3] = value; }
}
[JsonIgnore]
public byte birthdayDay
{
get => userSettings[4];
set { userSettings[4] = value; }
}
const int maxNicknameLength = 10;
[JsonIgnore]
public string nickname
{
get
{
fixed (byte* ptr = userSettings)
return Encoding.Unicode.GetString(ptr + 6, nicknameLength * 2);
}
set
{
if (value.Length > maxNicknameLength) value = value.Substring(0, maxNicknameLength);
byte[] nick = new byte[maxNicknameLength * 2 + 2];
// I do not know how an actual NDS would handle characters that require more than 2 bytes to encode.
// They can't be input normally, so I will ignore attempts to set a nickname that uses them.
if (Encoding.Unicode.GetBytes(value, 0, value.Length, nick, 0) != value.Length * 2)
return;
// The extra 2 bytes on the end will overwrite nickname length, which is set immediately after
nick.CopyTo(userSettings, 6);
userSettings[0x1A] = (byte)value.Length;
}
}
[JsonIgnore]
public short nicknameLength => userSettings[0x1A];
[JsonIgnore]
public long rtcOffset
{
get => BitConverter.ToInt64(userSettings, 0x68);
set { BitConverter.GetBytes(value).CopyTo(userSettings, 0x68); }
}
}
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
{
unsafe partial class MelonDS : ISoundProvider
{
public bool CanProvideAsync => false;
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
public void DiscardSamples()
{
_DiscardSamples();
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException();
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
nsamp = GetSampleCount();
samples = new short[nsamp * 2]; //*2 for stereo sound
fixed (short* data = samples)
{
GetSamples(data, nsamp);
}
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
throw new InvalidOperationException();
}
[DllImport(dllPath)]
private static extern int GetSampleCount();
[DllImport(dllPath)]
private static extern void GetSamples(short* data, int count);
[DllImport(dllPath, EntryPoint = "DiscardSamples")]
private static extern void _DiscardSamples();
}
}

View File

@ -0,0 +1,52 @@
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common;
using System.IO;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
{
unsafe partial class MelonDS : IStatable
{
public void LoadStateBinary(BinaryReader reader)
{
MemoryStream mStream = new MemoryStream();
reader.BaseStream.CopyTo(mStream);
LoadStateByteArray(mStream.GetBuffer(), (int)mStream.Length);
}
private void LoadStateByteArray(byte[] data, int length = -1)
{
if (length == -1) length = data.Length;
fixed (byte* ptr = data)
{
if (!UseSavestate(ptr, length))
CoreComm.Notify("Savestate load failed! See log window for details.");
}
}
public byte[] SaveStateBinary()
{
int len = GetSavestateSize();
byte[] ret = new byte[len];
fixed (byte* ptr = ret)
{
GetSavestateData(ptr, len);
}
return ret;
}
public void SaveStateBinary(BinaryWriter writer)
{
byte[] data = SaveStateBinary();
writer.Write(data);
}
[DllImport(dllPath)]
private static extern bool UseSavestate(byte* data, int len);
[DllImport(dllPath)]
private static extern int GetSavestateSize();
[DllImport(dllPath)]
private static extern void GetSavestateData(byte* data, int size);
}
}

View File

@ -0,0 +1,47 @@
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
{
unsafe partial class MelonDS : IVideoProvider
{
private const int NATIVE_WIDTH = 256;
/// <summary>
/// for a single screen
/// </summary>
private const int NATIVE_HEIGHT = 192;
public int VirtualWidth => NATIVE_WIDTH;
public int VirtualHeight => NATIVE_HEIGHT * 2;
public int BufferWidth => NATIVE_WIDTH;
public int BufferHeight => NATIVE_HEIGHT * 2;
public int VsyncNumerator => 60;
public int VsyncDenominator => 1;
public int BackgroundColor => 0;
[DllImport(dllPath)]
private static extern void VideoBuffer32bit(int* dstBuffer);
// BizHawk needs to be able to modify the buffer when loading savestates.
private int[] buffer = new int[NATIVE_WIDTH * NATIVE_HEIGHT * 2];
private bool getNewBuffer = true;
public int[] GetVideoBuffer()
{
if (getNewBuffer)
{
fixed (int* v = buffer)
{
VideoBuffer32bit(v);
}
getNewBuffer = false;
}
return buffer;
}
}
}

BIN
output/dll/SDL2.dll Normal file

Binary file not shown.

BIN
output/dll/libmelonDS.dll Normal file

Binary file not shown.