200 lines
5.0 KiB
C#
200 lines
5.0 KiB
C#
using System;
|
|
using System.ComponentModel;
|
|
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<MelonDS.MelonSettings, MelonDS.MelonSyncSettings>
|
|
{
|
|
private MelonSettings _settings = new MelonSettings();
|
|
|
|
public MelonSettings GetSettings() => _settings.Clone();
|
|
|
|
public MelonSyncSettings GetSyncSettings()
|
|
{
|
|
var ret = new MelonSyncSettings();
|
|
fixed (byte* ptr = ret.UserSettings)
|
|
{
|
|
if (!GetUserSettings(ptr))
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
ret.BootToFirmware = !GetDirectBoot();
|
|
ret.TimeAtBoot = GetTimeAtBoot();
|
|
return ret;
|
|
}
|
|
|
|
public PutSettingsDirtyBits PutSettings(MelonSettings o)
|
|
{
|
|
bool screenChanged = false;
|
|
if (o != null)
|
|
{
|
|
screenChanged |= _settings.ScaleFactor != o.ScaleFactor;
|
|
screenChanged |= _settings.ScreenGap != o.ScreenGap;
|
|
screenChanged |= _settings.ScreenLayout != o.ScreenLayout;
|
|
screenChanged |= _settings.ScreenRotation != o.ScreenRotation;
|
|
}
|
|
_settings = o ?? new MelonSettings();
|
|
SetScaleFactor(_settings.ScaleFactor);
|
|
return screenChanged ? PutSettingsDirtyBits.None : PutSettingsDirtyBits.None;
|
|
}
|
|
|
|
public PutSettingsDirtyBits 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 PutSettingsDirtyBits.RebootCore;
|
|
}
|
|
|
|
[DllImport(dllPath)]
|
|
private static extern bool GetUserSettings(byte* dst);
|
|
|
|
[DllImport(dllPath)]
|
|
private static extern int GetUserSettingsLength();
|
|
|
|
private static readonly 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();
|
|
|
|
[DllImport(dllPath)]
|
|
private static extern uint GetScaleFactor();
|
|
[DllImport(dllPath)]
|
|
private static extern void SetScaleFactor(uint value);
|
|
|
|
public enum ScreenLayoutKind
|
|
{
|
|
Vertical, Horizontal, Top, Bottom
|
|
}
|
|
|
|
public enum ScreenRotationKind
|
|
{
|
|
Rotate0, Rotate90, Rotate180, Rotate270
|
|
}
|
|
|
|
public class MelonSettings
|
|
{
|
|
public MelonSettings Clone() => (MelonSettings)MemberwiseClone();
|
|
|
|
[DisplayName("Screen Layout")]
|
|
[Description("Adjusts the layout of the screens")]
|
|
public ScreenLayoutKind ScreenLayout { get; set; } = ScreenLayoutKind.Vertical;
|
|
|
|
[DisplayName("Rotation")]
|
|
[Description("Adjusts the orientation of the screens")]
|
|
public ScreenRotationKind ScreenRotation { get; set; } = ScreenRotationKind.Rotate0;
|
|
|
|
[DisplayName("Screen Gap")]
|
|
public int ScreenGap { get; set; }
|
|
|
|
[DisplayName("Scale Factor")]
|
|
public uint ScaleFactor { get; set; } = 1;
|
|
}
|
|
|
|
public class MelonSyncSettings
|
|
{
|
|
public MelonSyncSettings()
|
|
{
|
|
UserSettings = new byte[UserSettingsLength];
|
|
}
|
|
|
|
public MelonSyncSettings Clone() => (MelonSyncSettings)MemberwiseClone();
|
|
|
|
public bool BootToFirmware { get; set; }
|
|
public uint TimeAtBoot { get; set; } = 946684800; // 2000-01-01 00:00:00 (earliest date possible on a DS)
|
|
|
|
[JsonProperty]
|
|
internal 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;
|
|
}
|
|
|
|
private 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);
|
|
}
|
|
}
|
|
}
|
|
}
|