From 1ca7b0295dc98e41d3818c1a12e06eae7b8111a8 Mon Sep 17 00:00:00 2001 From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com> Date: Thu, 2 Nov 2023 10:12:38 -0700 Subject: [PATCH] c# changes for melonDS update --- .../Consoles/Nintendo/NDS/LibMelonDS.cs | 56 ++++---- .../Consoles/Nintendo/NDS/MelonDS.ISaveRam.cs | 38 ++---- .../Nintendo/NDS/MelonDS.ISettable.cs | 69 ++++++---- .../Consoles/Nintendo/NDS/MelonDS.cs | 102 +++++++------- .../Consoles/Nintendo/NDS/NDSFirmware.cs | 124 ++++-------------- 5 files changed, 167 insertions(+), 222 deletions(-) diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/LibMelonDS.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/LibMelonDS.cs index 7027a400bd..31d45fb9b2 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/LibMelonDS.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/LibMelonDS.cs @@ -50,6 +50,31 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS public bool GLBetterPolygons; } + [StructLayout(LayoutKind.Sequential)] + public struct NDSTime + { + public int Year; + public int Month; + public int Day; + public int Hour; + public int Minute; + public int Second; + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct FirmwareSettings + { + public bool OverrideSettings; + public int UsernameLength; + public fixed char Username[10]; + public NDS.NDSSyncSettings.Language Language; + public NDS.NDSSyncSettings.Month BirthdayMonth; + public int BirthdayDay; + public NDS.NDSSyncSettings.Color Color; + public int MessageLength; + public fixed char Message[26]; + } + [StructLayout(LayoutKind.Sequential)] public struct InitConfig { @@ -61,21 +86,17 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS public bool IsWinApi; public NDS.NDSSyncSettings.ThreeDeeRendererType ThreeDeeRenderer; public RenderSettings RenderSettings; + public NDSTime StartTime; + public FirmwareSettings FirmwareSettings; } public enum ConfigEntry { + // JIT_ENABLED define would add 5 entries here + // it is currently not (and unlikely ever to be) defined + ExternalBIOSEnable, - BIOS9Path, - BIOS7Path, - FirmwarePath, - - DSi_BIOS9Path, - DSi_BIOS7Path, - DSi_FirmwarePath, - DSi_NANDPath, - DLDI_Enable, DLDI_ImagePath, DLDI_ImageSize, @@ -90,13 +111,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS DSiSD_FolderSync, DSiSD_FolderPath, - Firm_OverrideSettings, - Firm_Username, - Firm_Language, - Firm_BirthdayMonth, - Firm_BirthdayDay, - Firm_Color, - Firm_Message, Firm_MAC, WifiSettingsPath, @@ -105,10 +119,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS DSi_FullBIOSBoot, - // BizHawk-melonDS specific - UseRealTime, - FixedBootTime, - TimeAtBoot, + // GDBSTUB_ENABLED define would add 5 entries here + // it will not be defined for our purposes } [UnmanagedFunctionPointer(CC)] @@ -192,10 +204,10 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS public abstract bool SaveRamIsDirty(); [BizImport(CC)] - public abstract void ImportDSiWareSavs(uint titleId); + public abstract void ImportDSiWareSavs(uint titleId, byte[] data); [BizImport(CC)] - public abstract void ExportDSiWareSavs(uint titleId); + public abstract void ExportDSiWareSavs(uint titleId, byte[] data); [BizImport(CC)] public abstract void DSiWareSavsLength(uint titleId, out int publicSavSize, out int privateSavSize, out int bannerSavSize); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISaveRam.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISaveRam.cs index 0a10664c5d..621a9c5734 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISaveRam.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISaveRam.cs @@ -1,39 +1,26 @@ using System; + using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS { public partial class NDS : ISaveRam { + private readonly int DSiWareSaveLength; + public new bool SaveRamModified => IsDSiWare || _core.SaveRamIsDirty(); public new byte[] CloneSaveRam() { if (IsDSiWare) { - _core.DSiWareSavsLength(DSiTitleId.Lower, out var publicSavSize, out var privateSavSize, out var bannerSavSize); - if (publicSavSize + privateSavSize + bannerSavSize == 0) + if (DSiWareSaveLength == 0) { return null; } - _exe.AddTransientFile(Array.Empty(), "public.sav"); - _exe.AddTransientFile(Array.Empty(), "private.sav"); - _exe.AddTransientFile(Array.Empty(), "banner.sav"); - _core.ExportDSiWareSavs(DSiTitleId.Lower); - - var publicSav = _exe.RemoveTransientFile("public.sav"); - var privateSav = _exe.RemoveTransientFile("private.sav"); - var bannerSav = _exe.RemoveTransientFile("banner.sav"); - if (publicSav.Length != publicSavSize || privateSav.Length != privateSavSize || bannerSav.Length != bannerSavSize) - { - throw new InvalidOperationException("Unexpected size difference in DSiWare sav files!"); - } - - var ret = new byte[publicSavSize + privateSavSize + bannerSavSize]; - publicSav.AsSpan().CopyTo(ret.AsSpan().Slice(0, publicSavSize)); - privateSav.AsSpan().CopyTo(ret.AsSpan().Slice(publicSavSize, privateSavSize)); - bannerSav.AsSpan().CopyTo(ret.AsSpan().Slice(publicSavSize + privateSavSize, bannerSavSize)); + var ret = new byte[DSiWareSaveLength]; + _core.ExportDSiWareSavs(DSiTitleId.Lower, ret); return ret; } @@ -53,16 +40,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS { if (IsDSiWare) { - _core.DSiWareSavsLength(DSiTitleId.Lower, out var publicSavSize, out var privateSavSize, out var bannerSavSize); - if (data.Length == publicSavSize + privateSavSize + bannerSavSize) + if (data.Length == DSiWareSaveLength) { - if (publicSavSize > 0) _exe.AddReadonlyFile(data.AsSpan().Slice(0, publicSavSize).ToArray(), "public.sav"); - if (privateSavSize > 0) _exe.AddReadonlyFile(data.AsSpan().Slice(publicSavSize, privateSavSize).ToArray(), "private.sav"); - if (bannerSavSize > 0) _exe.AddReadonlyFile(data.AsSpan().Slice(publicSavSize + privateSavSize, bannerSavSize).ToArray(), "banner.sav"); - _core.ImportDSiWareSavs(DSiTitleId.Lower); - if (publicSavSize > 0) _exe.RemoveReadonlyFile("public.sav"); - if (privateSavSize > 0) _exe.RemoveReadonlyFile("private.sav"); - if (bannerSavSize > 0) _exe.RemoveReadonlyFile("banner.sav"); + _core.ImportDSiWareSavs(DSiTitleId.Lower, data); } } else if (data.Length > 0) @@ -71,4 +51,4 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS } } } -} +} \ No newline at end of file diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISettable.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISettable.cs index 26b3dca570..a67c587f76 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISettable.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.ISettable.cs @@ -1,14 +1,15 @@ -using BizHawk.Common; -using BizHawk.Emulation.Common; - using System; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Runtime.InteropServices; using System.Text; -using BizHawk.Common.CollectionExtensions; + using Newtonsoft.Json; +using BizHawk.Common; +using BizHawk.Common.CollectionExtensions; +using BizHawk.Emulation.Common; + // ReSharper disable SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS @@ -30,10 +31,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS LibMelonDS.ConfigEntry.DSiSD_Enable => false, // TODO LibMelonDS.ConfigEntry.DSiSD_ReadOnly => false, // TODO LibMelonDS.ConfigEntry.DSiSD_FolderSync => false, // TODO - LibMelonDS.ConfigEntry.Firm_OverrideSettings => _activeSyncSettings.FirmwareOverride, LibMelonDS.ConfigEntry.DSi_FullBIOSBoot => false, // TODO - LibMelonDS.ConfigEntry.UseRealTime => false, // RTC callback overrides this anyways, really this is so gmtime_r is used over localtime_r - LibMelonDS.ConfigEntry.FixedBootTime => true, // this just means use TimeAtBoot (which we always want at Unix epoch) _ => throw new InvalidOperationException() }; @@ -41,32 +39,19 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS { LibMelonDS.ConfigEntry.DLDI_ImageSize => 0, // TODO LibMelonDS.ConfigEntry.DSiSD_ImageSize => 0, // TODO - LibMelonDS.ConfigEntry.Firm_Language => (int)_activeSyncSettings.FirmwareLanguage, - LibMelonDS.ConfigEntry.Firm_BirthdayMonth => (int)_activeSyncSettings.FirmwareBirthdayMonth, - LibMelonDS.ConfigEntry.Firm_BirthdayDay => _activeSyncSettings.FirmwareBirthdayDay, - LibMelonDS.ConfigEntry.Firm_Color => (int)_activeSyncSettings.FirmwareFavouriteColour, LibMelonDS.ConfigEntry.AudioBitDepth => (int)_settings.AudioBitDepth, - LibMelonDS.ConfigEntry.TimeAtBoot => 0, _ => throw new InvalidOperationException() }; - private void GetStringSettingCallback(LibMelonDS.ConfigEntry configEntry, IntPtr buffer, int bufferSize) + private static void GetStringSettingCallback(LibMelonDS.ConfigEntry configEntry, IntPtr buffer, int bufferSize) { + // none of these are actually implemented yet var ret = configEntry switch { - LibMelonDS.ConfigEntry.BIOS9Path => _configEntryToPath.GetValueOrDefault(configEntry), - LibMelonDS.ConfigEntry.BIOS7Path => _configEntryToPath.GetValueOrDefault(configEntry), - LibMelonDS.ConfigEntry.FirmwarePath => _configEntryToPath.GetValueOrDefault(configEntry), - LibMelonDS.ConfigEntry.DSi_BIOS9Path => _configEntryToPath.GetValueOrDefault(configEntry), - LibMelonDS.ConfigEntry.DSi_BIOS7Path => _configEntryToPath.GetValueOrDefault(configEntry), - LibMelonDS.ConfigEntry.DSi_FirmwarePath => _configEntryToPath.GetValueOrDefault(configEntry), - LibMelonDS.ConfigEntry.DSi_NANDPath => _configEntryToPath.GetValueOrDefault(configEntry), LibMelonDS.ConfigEntry.DLDI_ImagePath => "dldi.bin", LibMelonDS.ConfigEntry.DLDI_FolderPath => "dldi", LibMelonDS.ConfigEntry.DSiSD_ImagePath => "sd.bin", LibMelonDS.ConfigEntry.DSiSD_FolderPath => "sd", - LibMelonDS.ConfigEntry.Firm_Username => _activeSyncSettings.FirmwareUsername, - LibMelonDS.ConfigEntry.Firm_Message => _activeSyncSettings.FirmwareMessage, LibMelonDS.ConfigEntry.WifiSettingsPath => "wfcsettings.bin", _ => throw new InvalidOperationException() }; @@ -272,7 +257,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS private DateTime _initaltime; [DisplayName("Initial Time")] - [Description("Initial time of emulation.")] + [Description("Initial time of emulation. Not used if Use Real Time is true")] [DefaultValue(typeof(DateTime), "2010-01-01")] [TypeConverter(typeof(BizDateTimeConverter))] public DateTime InitialTime @@ -282,8 +267,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS } [DisplayName("Use Real Time")] - [Description("If true, RTC clock will be based off of real time instead of emulated time. Ignored (set to false) when recording a movie.")] - [DefaultValue(false)] + [Description("If true, the initial RTC clock will be based off of real time instead of the Initial Time setting. Ignored (set to false) when recording a movie.")] + [DefaultValue(true)] public bool UseRealTime { get; set; } [DisplayName("DSi Mode")] @@ -443,6 +428,40 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS set => _firmwaremessage = value.Substring(0, Math.Min(26, value.Length)); } + public unsafe void GetFirmwareSettings(out LibMelonDS.FirmwareSettings fwSettings) + { + fwSettings.OverrideSettings = FirmwareOverride; + fwSettings.UsernameLength = Math.Min(FirmwareUsername.Length, 10); + + fixed (char* p = fwSettings.Username) + { + var username = new Span(p, 10); + username.Clear(); + + FirmwareUsername + .AsSpan() + .Slice(0, fwSettings.UsernameLength) + .CopyTo(username); + } + + fwSettings.Language = FirmwareLanguage; + fwSettings.BirthdayMonth = FirmwareBirthdayMonth; + fwSettings.BirthdayDay = FirmwareBirthdayDay; + fwSettings.Color = FirmwareFavouriteColour; + fwSettings.MessageLength = Math.Min(FirmwareMessage.Length, 26); + + fixed (char* p = fwSettings.Message) + { + var message = new Span(p, 26); + message.Clear(); + + FirmwareMessage + .AsSpan() + .Slice(0, fwSettings.MessageLength) + .CopyTo(message); + } + } + public NDSSyncSettings Clone() => (NDSSyncSettings)MemberwiseClone(); diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.cs index 69bab0e8fc..01eff0026e 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/MelonDS.cs @@ -25,7 +25,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS private readonly NDSDisassembler _disassembler; private readonly Dictionary _coreFiles = new(); - private readonly Dictionary _configEntryToPath = new(); private readonly LibMelonDS.FileCallbackInterface _fileCallbackInterface; private int GetFileLengthCallback(string path) @@ -37,17 +36,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS Marshal.Copy(file, 0, buffer, file.Length); } - private void AddCoreFile(LibMelonDS.ConfigEntry configEntry, string path, byte[] file) - { - if (file.Length == 0) - { - throw new InvalidOperationException($"Tried to add 0-sized core file to {path}"); - } - - _configEntryToPath.Add(configEntry, path); - _coreFiles.Add(path, file); - } - private void AddCoreFile(string path, byte[] file) { if (file.Length == 0) @@ -213,27 +201,16 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS if (_activeSyncSettings.UseRealBIOS) { - AddCoreFile(LibMelonDS.ConfigEntry.BIOS7Path, "bios7.bin", - CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios7"))); - AddCoreFile(LibMelonDS.ConfigEntry.BIOS9Path, "bios9.bin", - CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios9"))); + AddCoreFile("bios7.bin", CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios7"))); + AddCoreFile("bios9.bin", CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios9"))); + AddCoreFile("firmware.bin", CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", IsDSi ? "firmwarei" : "firmware"))); } if (IsDSi) { - AddCoreFile(LibMelonDS.ConfigEntry.DSi_BIOS7Path, "bios7i.bin", - CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios7i"))); - AddCoreFile(LibMelonDS.ConfigEntry.DSi_BIOS9Path, "bios9i.bin", - CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios9i"))); - AddCoreFile(LibMelonDS.ConfigEntry.DSi_FirmwarePath, "firmwarei.bin", - CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "firmwarei"))); - AddCoreFile(LibMelonDS.ConfigEntry.DSi_NANDPath, "nand.bin", - DecideNAND(CoreComm.CoreFileProvider, (DSiTitleId.Upper & ~0xFF) == 0x00030000, roms[0][0x1B0])); - } - else if (_activeSyncSettings.UseRealBIOS) - { - AddCoreFile(LibMelonDS.ConfigEntry.FirmwarePath, "firmware.bin", - CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "firmware"))); + AddCoreFile("bios7i.bin", CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios7i"))); + AddCoreFile("bios9i.bin", CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios9i"))); + AddCoreFile("nand.bin", DecideNAND(CoreComm.CoreFileProvider, (DSiTitleId.Upper & ~0xFF) == 0x00030000, roms[0][0x1B0])); } if (IsDSiWare) @@ -250,6 +227,17 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS } } + _activeSyncSettings.FirmwareOverride |= !_activeSyncSettings.UseRealBIOS || lp.DeterministicEmulationRequested; + + // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags + if (!IsDSi && _activeSyncSettings.FirmwareStartUp == NDSSyncSettings.StartUp.AutoBoot) + { + _activeSyncSettings.FirmwareLanguage |= (NDSSyncSettings.Language)0x40; + } + + _activeSyncSettings.UseRealTime &= !lp.DeterministicEmulationRequested; + var startTime = _activeSyncSettings.UseRealTime ? DateTime.Now : _activeSyncSettings.InitialTime; + LibMelonDS.InitConfig initConfig; initConfig.SkipFW = _activeSyncSettings.SkipFirmware && !IsDSi; initConfig.HasGBACart = roms.Count == 2; @@ -261,24 +249,26 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS initConfig.RenderSettings.SoftThreaded = _activeSyncSettings.ThreadedRendering; initConfig.RenderSettings.GLScaleFactor = _activeSyncSettings.GLScaleFactor; initConfig.RenderSettings.GLBetterPolygons = _activeSyncSettings.GLBetterPolygons; - - _activeSyncSettings.FirmwareOverride |= !_activeSyncSettings.UseRealBIOS || lp.DeterministicEmulationRequested; - - // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags - if (!IsDSi && _syncSettings.FirmwareStartUp == NDSSyncSettings.StartUp.AutoBoot) - { - _activeSyncSettings.FirmwareLanguage |= (NDSSyncSettings.Language)0x40; - } + initConfig.StartTime.Year = startTime.Year % 100; + initConfig.StartTime.Month = startTime.Month; + initConfig.StartTime.Day = startTime.Day; + initConfig.StartTime.Hour = startTime.Hour; + initConfig.StartTime.Minute = startTime.Minute; + initConfig.StartTime.Second = startTime.Second; + _activeSyncSettings.GetFirmwareSettings(out initConfig.FirmwareSettings); if (_activeSyncSettings.UseRealBIOS) { - var fw = _coreFiles[_configEntryToPath[IsDSi ? LibMelonDS.ConfigEntry.DSi_FirmwarePath : LibMelonDS.ConfigEntry.FirmwarePath]]; - if (IsDSi || NDSFirmware.MaybeWarnIfBadFw(fw, CoreComm)) // fw checks dont work on dsi firmware, don't bother + var fw = _coreFiles["firmware.bin"]; + + if (fw.Length is not (0x20000 or 0x40000 or 0x80000)) { - if (_activeSyncSettings.FirmwareOverride) - { - NDSFirmware.SanitizeFw(fw); - } + throw new InvalidOperationException("Invalid firmware length"); + } + + if (fw.Length == 0x20000) // fw checks dont work on dsi firmware for some reason, need to check what's going wrong + { + NDSFirmware.MaybeWarnIfBadFw(fw, CoreComm.ShowMessage); } } @@ -288,6 +278,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS _fileCallbackInterface.AllCallbacksInArray(_adapter), _logCallback, _glContext != null ? _getGLProcAddressCallback : null); + if (error != IntPtr.Zero) { using (_exe.EnterExit()) @@ -296,15 +287,34 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS } } - // the semantics of firmware override mean that sync settings will override firmware on a hard reset, which we don't want here - _activeSyncSettings.FirmwareOverride = false; + // add DSiWare sav files to the core files, so we can import/export for SaveRAM + if (IsDSiWare) + { + _core.DSiWareSavsLength(DSiTitleId.Lower, out var publicSavSize, out var privateSavSize, out var bannerSavSize); + + if (publicSavSize != 0) + { + AddCoreFile("public.sav", new byte[publicSavSize]); + } + + if (privateSavSize != 0) + { + AddCoreFile("private.sav", new byte[privateSavSize]); + } + + if (bannerSavSize != 0) + { + AddCoreFile("banner.sav", new byte[bannerSavSize]); + } + + DSiWareSaveLength = publicSavSize + privateSavSize + bannerSavSize; + } PostInit(); ((MemoryDomainList)this.AsMemoryDomains()).SystemBus = new NDSSystemBus(this.AsMemoryDomains()["ARM9 System Bus"], this.AsMemoryDomains()["ARM7 System Bus"]); DeterministicEmulation = lp.DeterministicEmulationRequested || !_activeSyncSettings.UseRealTime; - InitializeRtc(_activeSyncSettings.InitialTime); _frameThreadPtr = _core.GetFrameThreadProc(); if (_frameThreadPtr != IntPtr.Zero) diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/NDSFirmware.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/NDSFirmware.cs index c3fe952c57..ab205b957e 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/NDSFirmware.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/NDS/NDSFirmware.cs @@ -7,134 +7,61 @@ using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS { // mostly a c++ -> c# port of melon's firmware verification code - public class NDSFirmware + internal static class NDSFirmware { - public static bool MaybeWarnIfBadFw(byte[] fw, CoreComm comm) + public static void MaybeWarnIfBadFw(byte[] fw, Action warningCallback) { - if (fw.Length != 0x20000 && fw.Length != 0x40000 && fw.Length != 0x80000) - { - comm.ShowMessage("Bad firmware length detected! Firmware might not work!"); - return false; - } if (fw[0x17C] != 0xFF) { - comm.ShowMessage("Hacked firmware detected! Firmware might not work!"); - return false; + warningCallback("Hacked firmware detected! Firmware might not work!"); + return; } + var fwMask = fw.Length - 1; var badCrc16s = string.Empty; + if (!VerifyCrc16(fw, 0x2C, (fw[0x2C + 1] << 8) | fw[0x2C], 0x0000, 0x2A)) { badCrc16s += " Wifi "; } + if (!VerifyCrc16(fw, 0x7FA00 & fwMask, 0xFE, 0x0000, 0x7FAFE & fwMask)) { badCrc16s += " AP1 "; } + if (!VerifyCrc16(fw, 0x7FB00 & fwMask, 0xFE, 0x0000, 0x7FBFE & fwMask)) { badCrc16s += " AP2 "; } + if (!VerifyCrc16(fw, 0x7FC00 & fwMask, 0xFE, 0x0000, 0x7FCFE & fwMask)) { badCrc16s += " AP3 "; } + if (!VerifyCrc16(fw, 0x7FE00 & fwMask, 0x70, 0xFFFF, 0x7FE72 & fwMask)) { badCrc16s += " USER0 "; } + if (!VerifyCrc16(fw, 0x7FF00 & fwMask, 0x70, 0xFFFF, 0x7FF72 & fwMask)) { badCrc16s += " USER1 "; } - if (badCrc16s != "") + + if (badCrc16s != string.Empty) { - comm.ShowMessage("Bad Firmware CRC16(s) detected! Firmware might not work! Bad CRC16(s): " + badCrc16s); - return false; + warningCallback("Bad Firmware CRC16(s) detected! Firmware might not work! Bad CRC16(s): " + badCrc16s); + return; } - return CheckDecryptedCodeChecksum(fw, comm); - } - - public static void SanitizeFw(byte[] fw) - { - var fwMask = fw.Length - 1; - var apstart = new int[3] { 0x07FA00 & fwMask, 0x07FB00 & fwMask, 0x07FC00 & fwMask }; - - for (var i = 0; i < 3; i++) - { - for (var j = 0; j < 0x100; j++) - { - fw[apstart[i] + j] = 0; - } - } - - // gbatek marks these as unknown, they seem to depend on the mac address??? - // bytes 4 (upper nibble only) and 5 also seem to be just random? - // various combinations noted (noting last 2 bytes are crc16) - // F8 98 C1 E6 CC DD A9 E1 85 D4 9B - // F8 98 C1 E6 CC 1D 66 E1 85 D8 A4 - // F8 98 C1 E6 CC 9D 6B E1 85 60 A7 - // F8 98 C1 E6 CC 5D 92 E1 85 8C 96 - // different mac address - // 18 90 15 E9 7C 1D F1 E1 85 74 02 - var macdependentbytes = new byte[11] { 0xF8, 0x98, 0xC1, 0xE6, 0xCC, 0x9D, 0xBE, 0xE1, 0x85, 0x71, 0x5F }; - - var apoffset = 0xF5; - - for (var i = 0; i < 2; i++) - { - for (var j = 0; j < 11; j++) - { - fw[apstart[i] + apoffset + j] = macdependentbytes[j]; - } - } - - var ffoffset = 0xE7; - - for (var i = 0; i < 3; i++) - { - fw[apstart[i] + ffoffset] = 0xFF; - } - - // slot 3 doesn't have those mac dependent bytes??? - fw[apstart[2] + 0xFE] = 0x0A; - fw[apstart[2] + 0xFF] = 0xF0; - - var usersettings = new int[2] { 0x7FE00 & fwMask, 0x7FF00 & fwMask }; - - for (var i = 0; i < 2; i++) - { - unsafe - { - fixed (byte* us = &fw[usersettings[i]]) - { - // alarm settings - us[0x52] = 0; - us[0x53] = 0; - us[0x56] = 0; - // year of first boot - us[0x66] = 0; - // rtc offset - us[0x68] = 0; - us[0x69] = 0; - us[0x6A] = 0; - us[0x6B] = 0; - // update counter - us[0x70] = 0; - us[0x71] = 0; - // fix crc16 (probably redundant) - ushort crc16 = Crc16(us, 0x70, 0xFFFF); - us[0x72] = (byte)(crc16 & 0xFF); - us[0x73] = (byte)(crc16 >> 8); - } - } - } + CheckDecryptedCodeChecksum(fw, warningCallback); } private static unsafe ushort Crc16(byte* data, int len, int seed) { - var poly = new ushort[8] { 0xC0C1, 0xC181, 0xC301, 0xC601, 0xCC01, 0xD801, 0xF001, 0xA001 }; + Span poly = stackalloc ushort[8] { 0xC0C1, 0xC181, 0xC301, 0xC601, 0xCC01, 0xD801, 0xF001, 0xA001 }; for (var i = 0; i < len; i++) { @@ -180,25 +107,22 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS "674639373F16539F718C728D6CA0C83A2DB66770", // MACh nds-lite (v6) }; - private static bool CheckDecryptedCodeChecksum(byte[] fw, CoreComm comm) + private static void CheckDecryptedCodeChecksum(byte[] fw, Action warningCallback) { if (!GetDecryptedFirmware(fw, fw.Length, out var decryptedfw, out var decrypedfwlen)) { - comm.ShowMessage("Firmware could not be decryped for verification! This firmware might be not work!"); - return false; + warningCallback("Firmware could not be decryped for verification! This firmware might be not work!"); + return; } - var DecryptedFirmware = new byte[decrypedfwlen]; - Marshal.Copy(decryptedfw, DecryptedFirmware, 0, decrypedfwlen); + var decryptedFirmware = new byte[decrypedfwlen]; + Marshal.Copy(decryptedfw, decryptedFirmware, 0, decrypedfwlen); FreeDecryptedFirmware(decryptedfw); - var hash = SHA1Checksum.ComputeDigestHex(DecryptedFirmware); + var hash = SHA1Checksum.ComputeDigestHex(decryptedFirmware); if (hash != goodhashes[0] && hash != goodhashes[1] && hash != goodhashes[2]) { - comm.ShowMessage("Potentially bad firmware dump! Decrypted hash " + hash + " does not match known good dumps."); - return false; + warningCallback("Potentially bad firmware dump! Decrypted hash " + hash + " does not match known good dumps."); } - - return true; } } }