Update melonDS

This commit is contained in:
CasualPokePlayer 2024-06-05 23:36:44 -07:00
parent 75502e7ffb
commit b7265e2f39
28 changed files with 1470 additions and 1584 deletions

Binary file not shown.

View File

@ -27,37 +27,74 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
TOUCH = 0x1000, TOUCH = 0x1000,
LIDOPEN = 0x2000, LIDOPEN = 0x2000,
LIDCLOSE = 0x4000, LIDCLOSE = 0x4000,
POWER = 0x8000,
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public new class FrameInfo : LibWaterboxCore.FrameInfo public new class FrameInfo : LibWaterboxCore.FrameInfo
{ {
public IntPtr Console;
public Buttons Keys; public Buttons Keys;
public byte TouchX; public byte TouchX;
public byte TouchY; public byte TouchY;
public byte MicVolume; public byte MicVolume;
public byte GBALightSensor; public byte GBALightSensor;
public bool ConsiderAltLag; public byte ConsiderAltLag;
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct RenderSettings public struct ConsoleCreationArgs
{ {
public bool SoftThreaded; public IntPtr NdsRomData;
public int GLScaleFactor; public int NdsRomLength;
public bool GLBetterPolygons;
}
[StructLayout(LayoutKind.Sequential)] public IntPtr GbaRomData;
public struct NDSTime public int GbaRomLength;
{
public int Year; public IntPtr Arm9BiosData;
public int Month; public int Arm9BiosLength;
public int Day;
public int Hour; public IntPtr Arm7BiosData;
public int Minute; public int Arm7BiosLength;
public int Second;
public IntPtr FirmwareData;
public int FirmwareLength;
public IntPtr Arm9iBiosData;
public int Arm9iBiosLength;
public IntPtr Arm7iBiosData;
public int Arm7iBiosLength;
public IntPtr NandData;
public int NandLength;
public IntPtr DsiWareData;
public int DsiWareLength;
public IntPtr TmdData;
public int TmdLength;
public bool DSi;
public bool ClearNAND;
public bool SkipFW;
public NDS.NDSSettings.AudioBitDepthType BitDepth;
public NDS.NDSSettings.AudioInterpolationType Interpolation;
public NDS.NDSSyncSettings.ThreeDeeRendererType ThreeDeeRenderer;
public bool Threaded3D;
public int ScaleFactor;
public bool BetterPolygons;
public bool HiResCoordinates;
public int StartYear;
public int StartMonth;
public int StartDay;
public int StartHour;
public int StartMinute;
public int StartSecond;
public FirmwareSettings FwSettings;
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
@ -72,100 +109,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
public NDS.NDSSyncSettings.Color Color; public NDS.NDSSyncSettings.Color Color;
public int MessageLength; public int MessageLength;
public fixed char Message[26]; public fixed char Message[26];
} public fixed byte MacAddress[6];
[StructLayout(LayoutKind.Sequential)]
public struct InitConfig
{
public bool SkipFW;
public bool HasGBACart;
public bool DSi;
public bool ClearNAND;
public bool LoadDSiWare;
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,
DLDI_Enable,
DLDI_ImagePath,
DLDI_ImageSize,
DLDI_ReadOnly,
DLDI_FolderSync,
DLDI_FolderPath,
DSiSD_Enable,
DSiSD_ImagePath,
DSiSD_ImageSize,
DSiSD_ReadOnly,
DSiSD_FolderSync,
DSiSD_FolderPath,
Firm_MAC,
WifiSettingsPath,
AudioBitDepth,
DSi_FullBIOSBoot,
// GDBSTUB_ENABLED define would add 5 entries here
// it will not be defined for our purposes
}
[UnmanagedFunctionPointer(CC)]
public delegate bool GetBooleanSettingCallback(ConfigEntry configEntry);
[UnmanagedFunctionPointer(CC)]
public delegate int GetIntegerSettingCallback(ConfigEntry configEntry);
[UnmanagedFunctionPointer(CC)]
public delegate void GetStringSettingCallback(ConfigEntry configEntry, IntPtr buffer, int bufferSize);
[UnmanagedFunctionPointer(CC)]
public delegate void GetArraySettingCallback(ConfigEntry configEntry, IntPtr buffer);
[StructLayout(LayoutKind.Sequential)]
public struct ConfigCallbackInterface
{
public GetBooleanSettingCallback GetBoolean;
public GetIntegerSettingCallback GetInteger;
public GetStringSettingCallback GetString;
public GetArraySettingCallback GetArray;
public IntPtr[] AllCallbacksInArray(ICallingConventionAdapter adapter)
{
return new Delegate[] { GetBoolean, GetInteger, GetString, GetArray }
.Select(adapter.GetFunctionPointerForDelegate).ToArray();
}
}
[UnmanagedFunctionPointer(CC)]
public delegate int GetFileLengthCallback(string path);
[UnmanagedFunctionPointer(CC)]
public delegate void GetFileDataCallback(string path, IntPtr buffer);
[StructLayout(LayoutKind.Sequential)]
public struct FileCallbackInterface
{
public GetFileLengthCallback GetLength;
public GetFileDataCallback GetData;
public IntPtr[] AllCallbacksInArray(ICallingConventionAdapter adapter)
{
return new Delegate[] { GetLength, GetData }
.Select(adapter.GetFunctionPointerForDelegate).ToArray();
}
} }
[UnmanagedFunctionPointer(CC)] [UnmanagedFunctionPointer(CC)]
@ -183,42 +127,47 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
public delegate void LogCallback(LogLevel level, string message); public delegate void LogCallback(LogLevel level, string message);
[BizImport(CC)] [BizImport(CC)]
public abstract IntPtr Init( public abstract void SetLogCallback(LogCallback logCallback);
ref InitConfig loadData,
IntPtr[] configCallbackInterface, /* ref ConfigCallbackInterface */
IntPtr[] fileCallbackInterface, /* ref FileCallbackInterface */
LogCallback logCallback,
GetGLProcAddressCallback getGLProcAddressCallback);
[BizImport(CC)] [BizImport(CC)]
public abstract void PutSaveRam(byte[] data, uint len); public abstract IntPtr InitGL(GetGLProcAddressCallback getGLProcAddressCallback,
NDS.NDSSyncSettings.ThreeDeeRendererType threeDeeRenderer, int scaleFactor, bool isWinApi);
[BizImport(CC)] [BizImport(CC)]
public abstract void GetSaveRam(byte[] data); public abstract IntPtr CreateConsole(ref ConsoleCreationArgs args, byte[] error);
[BizImport(CC)] [BizImport(CC)]
public abstract int GetSaveRamLength(); public abstract void ResetConsole(IntPtr console, bool skipFw, ulong dsiTitleId);
[BizImport(CC)]
public abstract void PutSaveRam(IntPtr console, byte[] data, uint len);
[BizImport(CC)]
public abstract void GetSaveRam(IntPtr console, byte[] data);
[BizImport(CC)]
public abstract int GetSaveRamLength(IntPtr console);
[BizImport(CC)] [BizImport(CC)]
public abstract bool SaveRamIsDirty(); public abstract bool SaveRamIsDirty();
[BizImport(CC)] [BizImport(CC)]
public abstract void ImportDSiWareSavs(uint titleId, byte[] data); public abstract void ImportDSiWareSavs(IntPtr console, uint titleId);
[BizImport(CC)] [BizImport(CC)]
public abstract void ExportDSiWareSavs(uint titleId, byte[] data); public abstract void ExportDSiWareSavs(IntPtr console, uint titleId);
[BizImport(CC)] [BizImport(CC)]
public abstract void DSiWareSavsLength(uint titleId, out int publicSavSize, out int privateSavSize, out int bannerSavSize); public abstract void DSiWareSavsLength(IntPtr console, uint titleId, out int publicSavSize, out int privateSavSize, out int bannerSavSize);
[BizImport(CC)] [BizImport(CC)]
public abstract void GetRegs(uint[] regs); public abstract void GetRegs(IntPtr console, uint[] regs);
[BizImport(CC)] [BizImport(CC)]
public abstract void SetReg(int ncpu, int index, int val); public abstract void SetReg(IntPtr console, int ncpu, int index, int val);
[BizImport(CC)] [BizImport(CC)]
public abstract int GetCallbackCycleOffset(); public abstract int GetCallbackCycleOffset(IntPtr console);
[UnmanagedFunctionPointer(CC)] [UnmanagedFunctionPointer(CC)]
public delegate void MemoryCallback(uint addr); public delegate void MemoryCallback(uint addr);
@ -255,10 +204,10 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
public abstract void SetThreadStartCallback(ThreadStartCallback callback); public abstract void SetThreadStartCallback(ThreadStartCallback callback);
[BizImport(CC)] [BizImport(CC)]
public abstract int GetNANDSize(); public abstract int GetNANDSize(IntPtr console);
[BizImport(CC)] [BizImport(CC)]
public abstract void GetNANDData(byte[] buf); public abstract void GetNANDData(IntPtr console, byte[] buf);
[BizImport(CC)] [BizImport(CC)]
public abstract int GetGLTexture(); public abstract int GetGLTexture();
@ -301,7 +250,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
} }
[BizImport(CC)] [BizImport(CC)]
public abstract void SetScreenSettings(ref ScreenSettings screenSettings, out int width, out int height, out int vwidth, out int vheight); public abstract void SetScreenSettings(IntPtr console, ref ScreenSettings screenSettings, out int width, out int height, out int vwidth, out int vheight);
[BizImport(CC)] [BizImport(CC)]
public abstract void GetTouchCoords(ref int x, ref int y); public abstract void GetTouchCoords(ref int x, ref int y);

View File

@ -10,7 +10,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters() public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
{ {
var regs = new uint[2 * 16]; var regs = new uint[2 * 16];
_core.GetRegs(regs); _core.GetRegs(_console, regs);
var ret = new Dictionary<string, RegisterValue>(); var ret = new Dictionary<string, RegisterValue>();
for (var i = 0; i < 2; i++) for (var i = 0; i < 2; i++)
@ -40,7 +40,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
{ {
throw new InvalidOperationException("Invalid Reg Index???"); throw new InvalidOperationException("Invalid Reg Index???");
} }
_core.SetReg(ncpu == 9 ? 0 : 1, index, value); _core.SetReg(_console, ncpu == 9 ? 0 : 1, index, value);
} }
public bool CanStep(StepType type) => false; public bool CanStep(StepType type) => false;
@ -48,7 +48,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
[FeatureNotImplemented] [FeatureNotImplemented]
public void Step(StepType type) => throw new NotImplementedException(); public void Step(StepType type) => throw new NotImplementedException();
public long TotalExecutedCycles => CycleCount + _core.GetCallbackCycleOffset(); public long TotalExecutedCycles => CycleCount + _core.GetCallbackCycleOffset(_console);
public IMemoryCallbackSystem MemoryCallbacks => _memoryCallbacks; public IMemoryCallbackSystem MemoryCallbacks => _memoryCallbacks;

View File

@ -1,12 +1,15 @@
using System;
using BizHawk.Emulation.Common; using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
{ {
public partial class NDS : ISaveRam public partial class NDS : ISaveRam
{ {
private readonly int PublicSavSize, PrivateSavSize, BannerSavSize;
private readonly int DSiWareSaveLength; private readonly int DSiWareSaveLength;
public new bool SaveRamModified => IsDSiWare || _core.SaveRamIsDirty(); public new bool SaveRamModified => IsDSiWare ? DSiWareSaveLength != 0 : _core.SaveRamIsDirty();
public new byte[] CloneSaveRam() public new byte[] CloneSaveRam()
{ {
@ -17,17 +20,32 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
return null; return null;
} }
_exe.AddTransientFile([ ], "public.sav");
_exe.AddTransientFile([ ], "private.sav");
_exe.AddTransientFile([ ], "banner.sav");
_core.ExportDSiWareSavs(_console, 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[DSiWareSaveLength]; var ret = new byte[DSiWareSaveLength];
_core.ExportDSiWareSavs(DSiTitleId.Lower, ret); 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));
return ret; return ret;
} }
var length = _core.GetSaveRamLength(); var length = _core.GetSaveRamLength(_console);
if (length > 0) if (length > 0)
{ {
var ret = new byte[length]; var ret = new byte[length];
_core.GetSaveRam(ret); _core.GetSaveRam(_console, ret);
return ret; return ret;
} }
@ -40,12 +58,20 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
{ {
if (data.Length == DSiWareSaveLength) if (data.Length == DSiWareSaveLength)
{ {
_core.ImportDSiWareSavs(DSiTitleId.Lower, data); 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(_console, DSiTitleId.Lower);
if (PublicSavSize > 0) _exe.RemoveReadonlyFile("public.sav");
if (PrivateSavSize > 0) _exe.RemoveReadonlyFile("private.sav");
if (BannerSavSize > 0) _exe.RemoveReadonlyFile("banner.sav");
} }
} }
else if (data.Length > 0) else if (data.Length > 0)
{ {
_core.PutSaveRam(data, (uint)data.Length); _core.PutSaveRam(_console, data, (uint)data.Length);
} }
} }
} }

View File

@ -19,69 +19,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
private NDSSyncSettings _syncSettings; private NDSSyncSettings _syncSettings;
private readonly NDSSyncSettings _activeSyncSettings; private readonly NDSSyncSettings _activeSyncSettings;
private readonly LibMelonDS.ConfigCallbackInterface _configCallbackInterface;
private bool GetBooleanSettingCallback(LibMelonDS.ConfigEntry configEntry) => configEntry switch
{
LibMelonDS.ConfigEntry.ExternalBIOSEnable => _activeSyncSettings.UseRealBIOS,
LibMelonDS.ConfigEntry.DLDI_Enable => false, // TODO
LibMelonDS.ConfigEntry.DLDI_ReadOnly => false, // TODO
LibMelonDS.ConfigEntry.DLDI_FolderSync => false, // TODO
LibMelonDS.ConfigEntry.DSiSD_Enable => false, // TODO
LibMelonDS.ConfigEntry.DSiSD_ReadOnly => false, // TODO
LibMelonDS.ConfigEntry.DSiSD_FolderSync => false, // TODO
LibMelonDS.ConfigEntry.DSi_FullBIOSBoot => false, // TODO
_ => throw new InvalidOperationException()
};
private int GetIntegerSettingCallback(LibMelonDS.ConfigEntry configEntry) => configEntry switch
{
LibMelonDS.ConfigEntry.DLDI_ImageSize => 0, // TODO
LibMelonDS.ConfigEntry.DSiSD_ImageSize => 0, // TODO
LibMelonDS.ConfigEntry.AudioBitDepth => (int)_settings.AudioBitDepth,
_ => throw new InvalidOperationException()
};
private static void GetStringSettingCallback(LibMelonDS.ConfigEntry configEntry, IntPtr buffer, int bufferSize)
{
// none of these are actually implemented yet
var ret = configEntry switch
{
LibMelonDS.ConfigEntry.DLDI_ImagePath => "dldi.bin",
LibMelonDS.ConfigEntry.DLDI_FolderPath => "dldi",
LibMelonDS.ConfigEntry.DSiSD_ImagePath => "sd.bin",
LibMelonDS.ConfigEntry.DSiSD_FolderPath => "sd",
LibMelonDS.ConfigEntry.WifiSettingsPath => "wfcsettings.bin",
_ => throw new InvalidOperationException()
};
if (string.IsNullOrEmpty(ret))
{
Marshal.WriteByte(buffer, 0, 0);
return;
}
var bytes = Encoding.UTF8.GetBytes(ret);
var numToCopy = Math.Min(bytes.Length, bufferSize - 1);
Marshal.Copy(bytes, 0, buffer, numToCopy);
Marshal.WriteByte(buffer, numToCopy, 0);
}
private static void GetArraySettingCallback(LibMelonDS.ConfigEntry configEntry, IntPtr buffer)
{
if (configEntry != LibMelonDS.ConfigEntry.Firm_MAC)
{
throw new InvalidOperationException();
}
// TODO make MAC configurable
Marshal.WriteByte(buffer, 0, 0x00);
Marshal.WriteByte(buffer, 1, 0x09);
Marshal.WriteByte(buffer, 2, 0xBF);
Marshal.WriteByte(buffer, 3, 0x0E);
Marshal.WriteByte(buffer, 4, 0x49);
Marshal.WriteByte(buffer, 5, 0x16);
}
public enum ScreenLayoutKind public enum ScreenLayoutKind
{ {
@ -153,6 +90,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
[TypeConverter(typeof(DescribableEnumConverter))] [TypeConverter(typeof(DescribableEnumConverter))]
public AudioBitDepthType AudioBitDepth { get; set; } public AudioBitDepthType AudioBitDepth { get; set; }
public enum AudioInterpolationType : int
{
None,
}
[DisplayName("Alt Lag")] [DisplayName("Alt Lag")]
[Description("If true, touch screen polling and ARM7 key polling will be considered for lag frames. Otherwise, only ARM9 key polling will be considered.")] [Description("If true, touch screen polling and ARM7 key polling will be considered for lag frames. Otherwise, only ARM9 key polling will be considered.")]
[DefaultValue(false)] [DefaultValue(false)]
@ -219,13 +161,12 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
Software, Software,
[Display(Name = "OpenGL Classic")] [Display(Name = "OpenGL Classic")]
OpenGL_Classic, OpenGL_Classic,
// [Display(Name = "OpenGL Compute")] [Display(Name = "OpenGL Compute")]
//OpenGL_Compute, OpenGL_Compute,
} }
[DisplayName("3D Renderer")] [DisplayName("3D Renderer")]
// [Description("Renderer used for 3D. OpenGL Classic requires at least OpenGL 3.2, OpenGL Compute requires at least OpenGL 4.3. Forced to Software when recording a movie.")] [Description("Renderer used for 3D. OpenGL Classic requires at least OpenGL 3.2, OpenGL Compute requires at least OpenGL 4.3. Forced to Software when recording a movie.")]
[Description("Renderer used for 3D. OpenGL Classic requires at least OpenGL 3.2. Forced to Software when recording a movie.")]
[DefaultValue(ThreeDeeRendererType.Software)] [DefaultValue(ThreeDeeRendererType.Software)]
[TypeConverter(typeof(DescribableEnumConverter))] [TypeConverter(typeof(DescribableEnumConverter))]
public ThreeDeeRendererType ThreeDeeRenderer { get; set; } public ThreeDeeRendererType ThreeDeeRenderer { get; set; }
@ -248,10 +189,15 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
} }
[DisplayName("OpenGL Better Polygons")] [DisplayName("OpenGL Better Polygons")]
[Description("Enhances polygon quality with OpenGL. Not used for the software renderer.")] [Description("Enhances polygon quality with OpenGL Classic. Not used for the software nor OpenGL Compute renderer.")]
[DefaultValue(false)] [DefaultValue(false)]
public bool GLBetterPolygons { get; set; } public bool GLBetterPolygons { get; set; }
[DisplayName("OpenGL Hi Res Coordinates")]
[Description("Uses high resolution coordinates with OpenGL Compute. Not used for the software nor OpenGL Classic renderer.")]
[DefaultValue(false)]
public bool GLHiResCoordinates { get; set; }
[JsonIgnore] [JsonIgnore]
private DateTime _initaltime; private DateTime _initaltime;
@ -462,6 +408,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
.Slice(0, fwSettings.MessageLength) .Slice(0, fwSettings.MessageLength)
.CopyTo(message); .CopyTo(message);
} }
// TODO make MAC configurable
fwSettings.MacAddress[0] = 0x00;
fwSettings.MacAddress[1] = 0x09;
fwSettings.MacAddress[2] = 0xBF;
fwSettings.MacAddress[3] = 0x0E;
fwSettings.MacAddress[4] = 0x49;
fwSettings.MacAddress[5] = 0x16;
} }
public NDSSyncSettings Clone() public NDSSyncSettings Clone()
@ -510,7 +464,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
}; };
_openGLProvider.ActivateGLContext(_glContext); // SetScreenSettings will re-present the frame, so needs OpenGL context active _openGLProvider.ActivateGLContext(_glContext); // SetScreenSettings will re-present the frame, so needs OpenGL context active
_core.SetScreenSettings(ref screenSettings, out var w , out var h, out var vw, out var vh); _core.SetScreenSettings(_console, ref screenSettings, out var w , out var h, out var vw, out var vh);
BufferWidth = w; BufferWidth = w;
BufferHeight = h; BufferHeight = h;

View File

@ -5,6 +5,7 @@ using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading; using System.Threading;
using BizHawk.BizInvoke; using BizHawk.BizInvoke;
@ -22,30 +23,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
public partial class NDS : WaterboxCore public partial class NDS : WaterboxCore
{ {
private readonly LibMelonDS _core; private readonly LibMelonDS _core;
private readonly IntPtr _console;
private readonly NDSDisassembler _disassembler; private readonly NDSDisassembler _disassembler;
private readonly Dictionary<string, byte[]> _coreFiles = new();
private readonly LibMelonDS.FileCallbackInterface _fileCallbackInterface;
private int GetFileLengthCallback(string path)
=> _coreFiles.TryGetValue(path, out var file) ? file.Length : 0;
private void GetFileDataCallback(string path, IntPtr buffer)
{
var file = _coreFiles[path];
Marshal.Copy(file, 0, buffer, file.Length);
}
private void AddCoreFile(string path, byte[] file)
{
if (file.Length == 0)
{
throw new InvalidOperationException($"Tried to add 0-sized core file to {path}");
}
_coreFiles.Add(path, file);
}
private readonly LibMelonDS.LogCallback _logCallback; private readonly LibMelonDS.LogCallback _logCallback;
private static void LogCallback(LibMelonDS.LogLevel level, string message) private static void LogCallback(LibMelonDS.LogLevel level, string message)
@ -105,238 +85,266 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
SystemId = VSystemID.Raw.NDS, SystemId = VSystemID.Raw.NDS,
}) })
{ {
_syncSettings = lp.SyncSettings ?? new(); try
_settings = lp.Settings ?? new();
_activeSyncSettings = _syncSettings.Clone();
IsDSi = _activeSyncSettings.UseDSi;
var roms = lp.Roms.Select(r => r.RomData).ToList();
DSiTitleId = GetDSiTitleId(roms[0]);
IsDSi |= IsDSiWare;
if (roms.Count > (IsDSi ? 1 : 2))
{ {
throw new InvalidOperationException("Wrong number of ROMs!"); _syncSettings = lp.SyncSettings ?? new();
} _settings = lp.Settings ?? new();
InitMemoryCallbacks(); _activeSyncSettings = _syncSettings.Clone();
_traceCallback = MakeTrace; IsDSi = _activeSyncSettings.UseDSi;
_threadStartCallback = ThreadStartCallback;
_configCallbackInterface.GetBoolean = GetBooleanSettingCallback; var roms = lp.Roms.Select(r => r.RomData).ToList();
_configCallbackInterface.GetInteger = GetIntegerSettingCallback;
_configCallbackInterface.GetString = GetStringSettingCallback;
_configCallbackInterface.GetArray = GetArraySettingCallback;
_fileCallbackInterface.GetLength = GetFileLengthCallback; DSiTitleId = GetDSiTitleId(roms[0]);
_fileCallbackInterface.GetData = GetFileDataCallback; IsDSi |= IsDSiWare;
_logCallback = LogCallback; if (roms.Count > (IsDSi ? 1 : 2))
_openGLProvider = CoreComm.OpenGLProvider;
_getGLProcAddressCallback = GetGLProcAddressCallback;
if (lp.DeterministicEmulationRequested)
{
_activeSyncSettings.ThreeDeeRenderer = NDSSyncSettings.ThreeDeeRendererType.Software;
}
if (_activeSyncSettings.ThreeDeeRenderer != NDSSyncSettings.ThreeDeeRendererType.Software)
{
// ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault
var (majorGlVersion, minorGlVersion) = _activeSyncSettings.ThreeDeeRenderer switch
{ {
NDSSyncSettings.ThreeDeeRendererType.OpenGL_Classic => (3, 2), throw new InvalidOperationException("Wrong number of ROMs!");
// NDSSyncSettings.ThreeDeeRendererType.OpenGL_Compute => (4, 3), }
_ => throw new InvalidOperationException($"Invalid {nameof(NDSSyncSettings.ThreeDeeRenderer)}")
};
if (!_openGLProvider.SupportsGLVersion(majorGlVersion, minorGlVersion)) InitMemoryCallbacks();
_traceCallback = MakeTrace;
_threadStartCallback = ThreadStartCallback;
_logCallback = LogCallback;
_openGLProvider = CoreComm.OpenGLProvider;
_getGLProcAddressCallback = GetGLProcAddressCallback;
if (lp.DeterministicEmulationRequested)
{ {
lp.Comm.Notify($"OpenGL {majorGlVersion}.{minorGlVersion} is not supported on this machine, falling back to software renderer", null);
_activeSyncSettings.ThreeDeeRenderer = NDSSyncSettings.ThreeDeeRendererType.Software; _activeSyncSettings.ThreeDeeRenderer = NDSSyncSettings.ThreeDeeRendererType.Software;
} }
else
{
_glContext = _openGLProvider.RequestGLContext(majorGlVersion, minorGlVersion, true);
}
}
if (_activeSyncSettings.ThreeDeeRenderer == NDSSyncSettings.ThreeDeeRendererType.Software) if (_activeSyncSettings.ThreeDeeRenderer != NDSSyncSettings.ThreeDeeRendererType.Software)
{
if (!_openGLProvider.SupportsGLVersion(3, 1))
{ {
lp.Comm.Notify("OpenGL 3.1 is not supported on this machine, screen control options will not work.", null); // ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault
var (majorGlVersion, minorGlVersion) = _activeSyncSettings.ThreeDeeRenderer switch
{
NDSSyncSettings.ThreeDeeRendererType.OpenGL_Classic => (3, 2),
NDSSyncSettings.ThreeDeeRendererType.OpenGL_Compute => (4, 3),
_ => throw new InvalidOperationException($"Invalid {nameof(NDSSyncSettings.ThreeDeeRenderer)}")
};
if (!_openGLProvider.SupportsGLVersion(majorGlVersion, minorGlVersion))
{
lp.Comm.Notify($"OpenGL {majorGlVersion}.{minorGlVersion} is not supported on this machine, falling back to software renderer", null);
_activeSyncSettings.ThreeDeeRenderer = NDSSyncSettings.ThreeDeeRendererType.Software;
}
else
{
_glContext = _openGLProvider.RequestGLContext(majorGlVersion, minorGlVersion, true);
}
}
if (_activeSyncSettings.ThreeDeeRenderer == NDSSyncSettings.ThreeDeeRendererType.Software)
{
if (!_openGLProvider.SupportsGLVersion(3, 1))
{
lp.Comm.Notify("OpenGL 3.1 is not supported on this machine, screen control options will not work.", null);
}
else
{
_glContext = _openGLProvider.RequestGLContext(3, 1, true);
}
}
_core = PreInit<LibMelonDS>(new()
{
Filename = "melonDS.wbx",
SbrkHeapSizeKB = 2 * 1024,
SealedHeapSizeKB = 4,
InvisibleHeapSizeKB = 16 * 1024,
PlainHeapSizeKB = 4,
MmapHeapSizeKB = 1024 * 1024,
SkipCoreConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck),
SkipMemoryConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck),
}, new Delegate[]
{
_readCallback, _writeCallback, _execCallback, _traceCallback,
_threadStartCallback, _logCallback, _getGLProcAddressCallback
});
_core.SetLogCallback(_logCallback);
if (_glContext != null)
{
var error = _core.InitGL(_getGLProcAddressCallback, _activeSyncSettings.ThreeDeeRenderer, _activeSyncSettings.GLScaleFactor, !OSTailoredCode.IsUnixHost);
if (error != IntPtr.Zero)
{
using (_exe.EnterExit())
{
throw new InvalidOperationException(Marshal.PtrToStringAnsi(error));
}
}
}
_activeSyncSettings.UseRealBIOS |= IsDSi;
byte[] bios9 = null, bios7 = null, firmware = null;
if (_activeSyncSettings.UseRealBIOS)
{
bios9 = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios9"));
bios7 = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios7"));
firmware = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", IsDSi ? "firmwarei" : "firmware"));
if (firmware.Length is not (0x20000 or 0x40000 or 0x80000))
{
throw new InvalidOperationException("Invalid firmware length");
}
NDSFirmware.MaybeWarnIfBadFw(firmware, CoreComm.ShowMessage);
}
byte[] bios9i = null, bios7i = null, nand = null;
if (IsDSi)
{
bios9i = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios9i"));
bios7i = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios7i"));
nand = DecideNAND(CoreComm.CoreFileProvider, (DSiTitleId.Upper & ~0xFF) == 0x00030000, roms[0][0x1B0]);
}
byte[] ndsRom = null, gbaRom = null, dsiWare = null, tmd = null;
if (IsDSiWare)
{
tmd = GetTMDData(DSiTitleId.Full);
dsiWare = roms[0];
} }
else else
{ {
_glContext = _openGLProvider.RequestGLContext(3, 1, true); ndsRom = roms[0];
if (roms.Count == 2)
{
gbaRom = roms[1];
}
} }
}
_core = PreInit<LibMelonDS>(new() _activeSyncSettings.FirmwareOverride |= !_activeSyncSettings.UseRealBIOS || lp.DeterministicEmulationRequested;
{
Filename = "melonDS.wbx",
SbrkHeapSizeKB = 2 * 1024,
SealedHeapSizeKB = 4,
InvisibleHeapSizeKB = 4 * 1024,
PlainHeapSizeKB = 4,
MmapHeapSizeKB = 1920 * 1024,
SkipCoreConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck),
SkipMemoryConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck),
}, new Delegate[]
{
_readCallback, _writeCallback, _execCallback, _traceCallback, _threadStartCallback,
_configCallbackInterface.GetBoolean, _configCallbackInterface.GetInteger,
_configCallbackInterface.GetString, _configCallbackInterface.GetArray,
_fileCallbackInterface.GetLength, _fileCallbackInterface.GetData,
_logCallback, _getGLProcAddressCallback
});
_activeSyncSettings.UseRealBIOS |= IsDSi; // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags
if (!IsDSi && _activeSyncSettings.FirmwareStartUp == NDSSyncSettings.StartUp.AutoBoot)
if (_activeSyncSettings.UseRealBIOS)
{
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("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)
{
AddCoreFile("tmd.rom", GetTMDData(DSiTitleId.Full));
AddCoreFile("dsiware.rom", roms[0]);
}
else
{
AddCoreFile("nds.rom", roms[0]);
if (roms.Count == 2)
{ {
AddCoreFile("gba.rom", roms[1]); _activeSyncSettings.FirmwareLanguage |= (NDSSyncSettings.Language)0x40;
} }
}
_activeSyncSettings.FirmwareOverride |= !_activeSyncSettings.UseRealBIOS || lp.DeterministicEmulationRequested; _activeSyncSettings.UseRealTime &= !lp.DeterministicEmulationRequested;
var startTime = _activeSyncSettings.UseRealTime ? DateTime.Now : _activeSyncSettings.InitialTime;
// ReSharper disable once BitwiseOperatorOnEnumWithoutFlags LibMelonDS.ConsoleCreationArgs consoleCreationArgs;
if (!IsDSi && _activeSyncSettings.FirmwareStartUp == NDSSyncSettings.StartUp.AutoBoot)
{
_activeSyncSettings.FirmwareLanguage |= (NDSSyncSettings.Language)0x40;
}
_activeSyncSettings.UseRealTime &= !lp.DeterministicEmulationRequested; consoleCreationArgs.NdsRomLength = ndsRom?.Length ?? 0;
var startTime = _activeSyncSettings.UseRealTime ? DateTime.Now : _activeSyncSettings.InitialTime; consoleCreationArgs.GbaRomLength = gbaRom?.Length ?? 0;
consoleCreationArgs.Arm9BiosLength = bios9?.Length ?? 0;
consoleCreationArgs.Arm7BiosLength = bios7?.Length ?? 0;
consoleCreationArgs.FirmwareLength = firmware?.Length ?? 0;
consoleCreationArgs.Arm9iBiosLength = bios9i?.Length ?? 0;
consoleCreationArgs.Arm7iBiosLength = bios7i?.Length ?? 0;
consoleCreationArgs.NandLength = nand?.Length ?? 0;
consoleCreationArgs.DsiWareLength = dsiWare?.Length ?? 0;
consoleCreationArgs.TmdLength = tmd?.Length ?? 0;
LibMelonDS.InitConfig initConfig; consoleCreationArgs.DSi = IsDSi;
initConfig.SkipFW = _activeSyncSettings.SkipFirmware; consoleCreationArgs.ClearNAND = _activeSyncSettings.ClearNAND || lp.DeterministicEmulationRequested;
initConfig.HasGBACart = roms.Count == 2; consoleCreationArgs.SkipFW = _activeSyncSettings.SkipFirmware;
initConfig.DSi = IsDSi;
initConfig.ClearNAND = _activeSyncSettings.ClearNAND || lp.DeterministicEmulationRequested;
initConfig.LoadDSiWare = IsDSiWare;
initConfig.IsWinApi = !OSTailoredCode.IsUnixHost;
initConfig.ThreeDeeRenderer = _activeSyncSettings.ThreeDeeRenderer;
initConfig.RenderSettings.SoftThreaded = _activeSyncSettings.ThreadedRendering;
initConfig.RenderSettings.GLScaleFactor = _activeSyncSettings.GLScaleFactor;
initConfig.RenderSettings.GLBetterPolygons = _activeSyncSettings.GLBetterPolygons;
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) consoleCreationArgs.BitDepth = _settings.AudioBitDepth;
{ consoleCreationArgs.Interpolation = NDSSettings.AudioInterpolationType.None;
var fw = _coreFiles["firmware.bin"];
if (fw.Length is not (0x20000 or 0x40000 or 0x80000)) consoleCreationArgs.ThreeDeeRenderer = _activeSyncSettings.ThreeDeeRenderer;
consoleCreationArgs.Threaded3D = _activeSyncSettings.ThreadedRendering;
consoleCreationArgs.ScaleFactor = _activeSyncSettings.GLScaleFactor;
consoleCreationArgs.BetterPolygons = _activeSyncSettings.GLBetterPolygons;
consoleCreationArgs.HiResCoordinates = _activeSyncSettings.GLHiResCoordinates;
consoleCreationArgs.StartYear = startTime.Year % 100;
consoleCreationArgs.StartMonth = startTime.Month;
consoleCreationArgs.StartDay = startTime.Day;
consoleCreationArgs.StartHour = startTime.Hour;
consoleCreationArgs.StartMinute = startTime.Minute;
consoleCreationArgs.StartSecond = startTime.Second;
_activeSyncSettings.GetFirmwareSettings(out consoleCreationArgs.FwSettings);
var errorBuffer = new byte[1024];
unsafe
{ {
throw new InvalidOperationException("Invalid firmware length"); fixed (byte*
ndsRomPtr = ndsRom,
gbaRomPtr = gbaRom,
bios9Ptr = bios9,
bios7Ptr = bios7,
firmwarePtr = firmware,
bios9iPtr = bios9i,
bios7iPtr = bios7i,
nandPtr = nand,
dsiWarePtr = dsiWare,
tmdPtr = tmd)
{
consoleCreationArgs.NdsRomData = (IntPtr)ndsRomPtr;
consoleCreationArgs.GbaRomData = (IntPtr)gbaRomPtr;
consoleCreationArgs.Arm9BiosData = (IntPtr)bios9Ptr;
consoleCreationArgs.Arm7BiosData = (IntPtr)bios7Ptr;
consoleCreationArgs.FirmwareData = (IntPtr)firmwarePtr;
consoleCreationArgs.Arm9iBiosData = (IntPtr)bios9iPtr;
consoleCreationArgs.Arm7iBiosData = (IntPtr)bios7iPtr;
consoleCreationArgs.NandData = (IntPtr)nandPtr;
consoleCreationArgs.DsiWareData = (IntPtr)dsiWarePtr;
consoleCreationArgs.TmdData = (IntPtr)tmdPtr;
_console = _core.CreateConsole(ref consoleCreationArgs, errorBuffer);
}
} }
NDSFirmware.MaybeWarnIfBadFw(fw, CoreComm.ShowMessage); if (_console == IntPtr.Zero)
}
var error = _core.Init(
ref initConfig,
_configCallbackInterface.AllCallbacksInArray(_adapter),
_fileCallbackInterface.AllCallbacksInArray(_adapter),
_logCallback,
_glContext != null ? _getGLProcAddressCallback : null);
if (error != IntPtr.Zero)
{
using (_exe.EnterExit())
{ {
throw new InvalidOperationException(Marshal.PtrToStringAnsi(error)); var errorStr = Encoding.ASCII.GetString(errorBuffer).TrimEnd('\0');
throw new InvalidOperationException(errorStr);
}
if (IsDSiWare)
{
_core.DSiWareSavsLength(_console, DSiTitleId.Lower, out PublicSavSize, out PrivateSavSize, out 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;
_frameThreadPtr = _core.GetFrameThreadProc();
if (_frameThreadPtr != IntPtr.Zero)
{
Console.WriteLine($"Setting up waterbox thread for 0x{(ulong)_frameThreadPtr:X16}");
_frameThread = new(FrameThreadProc) { IsBackground = true };
_frameThread.Start();
_frameThreadAction = CallingConventionAdapters
.GetWaterboxUnsafeUnwrapped()
.GetDelegateForFunctionPointer<Action>(_frameThreadPtr);
_core.SetThreadStartCallback(_threadStartCallback);
}
_disassembler = new(_core);
_serviceProvider.Register<IDisassemblable>(_disassembler);
const string TRACE_HEADER = "ARM9+ARM7: Opcode address, opcode, registers (r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, SP, LR, PC, Cy, CpuMode)";
Tracer = new TraceBuffer(TRACE_HEADER);
_serviceProvider.Register(Tracer);
if (_glContext != null)
{
_glTextureProvider = new(this, _core, () => _openGLProvider.ActivateGLContext(_glContext));
_serviceProvider.Register<IVideoProvider>(_glTextureProvider);
RefreshScreenSettings(_settings);
} }
} }
catch
// 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); Dispose();
throw;
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;
_frameThreadPtr = _core.GetFrameThreadProc();
if (_frameThreadPtr != IntPtr.Zero)
{
Console.WriteLine($"Setting up waterbox thread for 0x{(ulong)_frameThreadPtr:X16}");
_frameThread = new(FrameThreadProc) { IsBackground = true };
_frameThread.Start();
_frameThreadAction = CallingConventionAdapters
.GetWaterboxUnsafeUnwrapped()
.GetDelegateForFunctionPointer<Action>(_frameThreadPtr);
_core.SetThreadStartCallback(_threadStartCallback);
}
_disassembler = new(_core);
_serviceProvider.Register<IDisassemblable>(_disassembler);
const string TRACE_HEADER = "ARM9+ARM7: Opcode address, opcode, registers (r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, SP, LR, PC, Cy, CpuMode)";
Tracer = new TraceBuffer(TRACE_HEADER);
_serviceProvider.Register(Tracer);
if (_glContext != null)
{
_glTextureProvider = new(this, _core, () => _openGLProvider.ActivateGLContext(_glContext));
_serviceProvider.Register<IVideoProvider>(_glTextureProvider);
RefreshScreenSettings(_settings);
} }
} }
@ -385,12 +393,12 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
// todo: wire this up w/ frontend // todo: wire this up w/ frontend
public byte[] GetNAND() public byte[] GetNAND()
{ {
var length = _core.GetNANDSize(); var length = _core.GetNANDSize(_console);
if (length > 0) if (length > 0)
{ {
var ret = new byte[length]; var ret = new byte[length];
_core.GetNANDData(ret); _core.GetNANDData(_console, ret);
return ret; return ret;
} }
@ -449,8 +457,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
b |= LibMelonDS.Buttons.LIDCLOSE; b |= LibMelonDS.Buttons.LIDCLOSE;
if (c.IsPressed("Touch")) if (c.IsPressed("Touch"))
b |= LibMelonDS.Buttons.TOUCH; b |= LibMelonDS.Buttons.TOUCH;
if (c.IsPressed("Power"))
b |= LibMelonDS.Buttons.POWER;
return b; return b;
} }
@ -464,14 +470,20 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
_core.SetTraceCallback(Tracer.IsEnabled() ? _traceCallback : null, _settings.GetTraceMask()); _core.SetTraceCallback(Tracer.IsEnabled() ? _traceCallback : null, _settings.GetTraceMask());
if (controller.IsPressed("Power"))
{
_core.ResetConsole(_console, _activeSyncSettings.SkipFirmware, DSiTitleId.Full);
}
return new LibMelonDS.FrameInfo return new LibMelonDS.FrameInfo
{ {
Console = _console,
Keys = GetButtons(controller), Keys = GetButtons(controller),
TouchX = (byte)controller.AxisValue("Touch X"), TouchX = (byte)controller.AxisValue("Touch X"),
TouchY = (byte)controller.AxisValue("Touch Y"), TouchY = (byte)controller.AxisValue("Touch Y"),
MicVolume = (byte)controller.AxisValue("Mic Volume"), MicVolume = (byte)controller.AxisValue("Mic Volume"),
GBALightSensor = (byte)controller.AxisValue("GBA Light Sensor"), GBALightSensor = (byte)controller.AxisValue("GBA Light Sensor"),
ConsiderAltLag = _settings.ConsiderAltLag, ConsiderAltLag = (byte)(_settings.ConsiderAltLag ? 1 : 0),
}; };
} }

View File

@ -204,7 +204,7 @@ SECTIONS
*(.lbss .lbss.* .gnu.linkonce.lb.*) *(.lbss .lbss.* .gnu.linkonce.lb.*)
*(LARGE_COMMON) *(LARGE_COMMON)
} }
. = ALIGN(64 / 4); . = ALIGN(64);
. = SEGMENT_START("ldata-segment", .); . = SEGMENT_START("ldata-segment", .);
.lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : .lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
{ {

View File

@ -0,0 +1,740 @@
#include "NDS.h"
#include "NDSCart.h"
#include "DSi.h"
#include "DSi_NAND.h"
#include "DSi_TMD.h"
#include "GPU3D_OpenGL.h"
#include "GPU3D_Compute.h"
#include "CRC32.h"
#include "FreeBIOS.h"
#include "SPI.h"
#include "BizPlatform/BizFile.h"
#include "BizTypes.h"
extern melonDS::NDS* CurrentNDS;
namespace ConsoleCreator
{
struct FirmwareSettings
{
bool OverrideSettings;
int UsernameLength;
char16_t Username[10];
int Language;
int BirthdayMonth;
int BirthdayDay;
int Color;
int MessageLength;
char16_t Message[26];
u8 MacAddress[6];
};
template <typename T>
static std::unique_ptr<T> CreateBiosImage(u8* biosData, u32 biosLength, std::optional<T> biosFallback = std::nullopt)
{
auto bios = std::make_unique<T>();
if (biosData)
{
if (biosLength != bios->size())
{
throw std::runtime_error("Invalid BIOS size");
}
memcpy(bios->data(), biosData, bios->size());
}
else
{
if (!biosFallback)
{
throw std::runtime_error("Failed to load BIOS");
}
memcpy(bios->data(), biosFallback->data(), bios->size());
}
return std::move(bios);
}
static void SanitizeExternalFirmware(melonDS::Firmware& firmware)
{
auto& header = firmware.GetHeader();
const bool isDSiFw = header.ConsoleType == melonDS::Firmware::FirmwareConsoleType::DSi;
const auto defaultHeader = melonDS::Firmware::FirmwareHeader{isDSiFw};
// the user data offset won't necessarily be 0x7FE00 & Mask, DSi/iQue use 0x7FC00 & Mask instead
// but we don't want to crash due to an invalid offset
const auto maxUserDataOffset = 0x7FE00 & firmware.Mask();
if (firmware.GetUserDataOffset() > maxUserDataOffset)
{
header.UserSettingsOffset = maxUserDataOffset >> 3;
}
if (isDSiFw)
{
memset(&header.Bytes[0x22], 0x00, 6);
memset(&header.Bytes[0x28], 0xFF, 2);
}
memcpy(&header.Bytes[0x2C], &defaultHeader.Bytes[0x2C], 0x136);
memset(&header.Bytes[0x162], 0xFF, 0x9E);
if (isDSiFw)
{
header.WifiBoard = defaultHeader.WifiBoard;
header.WifiFlash = defaultHeader.WifiFlash;
}
header.UpdateChecksum();
auto& aps = firmware.GetAccessPoints();
aps[0] = melonDS::Firmware::WifiAccessPoint{isDSiFw};
aps[1] = melonDS::Firmware::WifiAccessPoint{};
aps[2] = melonDS::Firmware::WifiAccessPoint{};
if (isDSiFw)
{
auto& exAps = firmware.GetExtendedAccessPoints();
exAps[0] = melonDS::Firmware::ExtendedWifiAccessPoint{};
exAps[1] = melonDS::Firmware::ExtendedWifiAccessPoint{};
exAps[2] = melonDS::Firmware::ExtendedWifiAccessPoint{};
}
}
static void FixFirmwareTouchscreenCalibration(melonDS::Firmware::UserData& userData)
{
userData.TouchCalibrationADC1[0] = 0;
userData.TouchCalibrationADC1[1] = 0;
userData.TouchCalibrationPixel1[0] = 0;
userData.TouchCalibrationPixel1[1] = 0;
userData.TouchCalibrationADC2[0] = 255 << 4;
userData.TouchCalibrationADC2[1] = 191 << 4;
userData.TouchCalibrationPixel2[0] = 255;
userData.TouchCalibrationPixel2[1] = 191;
}
static void SetFirmwareSettings(melonDS::Firmware::UserData& userData, FirmwareSettings& fwSettings)
{
memset(userData.Bytes, 0, 0x74);
userData.Version = 5;
FixFirmwareTouchscreenCalibration(userData);
userData.NameLength = fwSettings.UsernameLength;
memcpy(userData.Nickname, fwSettings.Username, sizeof(fwSettings.Username));
userData.Settings = fwSettings.Language | melonDS::Firmware::BacklightLevel::Max | 0xEC00;
userData.BirthdayMonth = fwSettings.BirthdayMonth;
userData.BirthdayDay = fwSettings.BirthdayDay;
userData.FavoriteColor = fwSettings.Color;
userData.MessageLength = fwSettings.MessageLength;
memcpy(userData.Message, fwSettings.Message, sizeof(fwSettings.Message));
if (userData.ExtendedSettings.Unknown0 == 1)
{
userData.ExtendedSettings.ExtendedLanguage = static_cast<melonDS::Firmware::Language>(fwSettings.Language & melonDS::Firmware::Language::Reserved);
memset(userData.ExtendedSettings.Unused0, 0, sizeof(userData.ExtendedSettings.Unused0));
if (!((1 << static_cast<u8>(userData.ExtendedSettings.ExtendedLanguage)) & userData.ExtendedSettings.SupportedLanguageMask))
{
userData.ExtendedSettings.ExtendedLanguage = melonDS::Firmware::Language::English;
userData.Settings &= ~melonDS::Firmware::Language::Reserved;
userData.Settings |= melonDS::Firmware::Language::English;
}
}
else
{
memset(userData.Unused3, 0xFF, sizeof(userData.Unused3));
}
// only extended settings should have Chinese / Korean
// note that melonDS::Firmware::Language::Reserved is Korean, so it's valid to have language set to that
if ((userData.Settings & melonDS::Firmware::Language::Reserved) >= melonDS::Firmware::Language::Chinese)
{
userData.Settings &= ~melonDS::Firmware::Language::Reserved;
userData.Settings |= melonDS::Firmware::Language::English;
}
}
static melonDS::Firmware CreateFirmware(u8* fwData, u32 fwLength, bool dsi, FirmwareSettings& fwSettings)
{
melonDS::Firmware firmware{dsi};
if (fwData)
{
firmware = melonDS::Firmware{fwData, fwLength};
if (firmware.Buffer())
{
// sanitize header, wifi calibration, and AP points
SanitizeExternalFirmware(firmware);
}
}
else
{
fwSettings.OverrideSettings = true;
}
if (!firmware.Buffer())
{
throw std::runtime_error("Failed to load firmware!");
}
for (auto& userData : firmware.GetUserData())
{
FixFirmwareTouchscreenCalibration(userData);
userData.UpdateChecksum();
}
if (fwSettings.OverrideSettings)
{
for (auto& userData : firmware.GetUserData())
{
SetFirmwareSettings(userData, fwSettings);
userData.UpdateChecksum();
}
melonDS::MacAddress mac;
static_assert(mac.size() == sizeof(fwSettings.MacAddress));
memcpy(mac.data(), fwSettings.MacAddress, mac.size());
auto& header = firmware.GetHeader();
header.MacAddr = mac;
header.UpdateChecksum();
}
return firmware;
}
static u8 GetDefaultCountryCode(melonDS::DSi_NAND::ConsoleRegion region)
{
// TODO: CountryCode probably should be configurable
// these defaults are also completely arbitrary
switch (region)
{
case melonDS::DSi_NAND::ConsoleRegion::Japan: return 0x01; // Japan
case melonDS::DSi_NAND::ConsoleRegion::USA: return 0x31; // United States
case melonDS::DSi_NAND::ConsoleRegion::Europe: return 0x6E; // United Kingdom
case melonDS::DSi_NAND::ConsoleRegion::Australia: return 0x41; // Australia
case melonDS::DSi_NAND::ConsoleRegion::China: return 0xA0; // China
case melonDS::DSi_NAND::ConsoleRegion::Korea: return 0x88; // Korea
default: return 0x31; // ???
}
}
static void SanitizeNandSettings(melonDS::DSi_NAND::DSiFirmwareSystemSettings& settings, melonDS::DSi_NAND::ConsoleRegion region)
{
memset(settings.Zero00, 0, sizeof(settings.Zero00));
settings.Version = 1;
settings.UpdateCounter = 0;
memset(settings.Zero01, 0, sizeof(settings.Zero01));
settings.BelowRAMAreaSize = 0x128;
// bit 0-1 are unknown (but usually 1)
// bit 2 indicates language set (?)
// bit 3 is wifi enable (really wifi LED enable; usually set)
// bit 24 set will indicate EULA is "agreed" to
settings.ConfigFlags = 0x0100000F;
settings.Zero02 = 0;
settings.CountryCode = GetDefaultCountryCode(region);
settings.RTCYear = 0;
settings.RTCOffset = 0;
memset(settings.Zero3, 0, sizeof(settings.Zero3));
settings.EULAVersion = 1;
memset(settings.Zero04, 0, sizeof(settings.Zero04));
settings.AlarmHour = 0;
settings.AlarmMinute = 0;
memset(settings.Zero05, 0, sizeof(settings.Zero05));
settings.AlarmEnable = false;
memset(settings.Zero06, 0, sizeof(settings.Zero06));
settings.Unknown0 = 0;
settings.Unknown1 = 3; // apparently 2 or 3
memset(settings.Zero07, 0, sizeof(settings.Zero07));
settings.SystemMenuMostRecentTitleID.fill(0);
settings.Unknown2[0] = 0x9C;
settings.Unknown2[1] = 0x20;
settings.Unknown2[2] = 0x01;
settings.Unknown2[3] = 0x02;
memset(settings.Zero08, 0, sizeof(settings.Zero08));
settings.Zero09 = 0;
settings.ParentalControlsFlags = 0;
memset(settings.Zero10, 0, sizeof(settings.Zero10));
settings.ParentalControlsRegion = 0;
settings.ParentalControlsYearsOfAgeRating = 0;
settings.ParentalControlsSecretQuestion = 0;
settings.Unknown3 = 0; // apparently 0 or 6 or 7
memset(settings.Zero11, 0, sizeof(settings.Zero11));
memset(settings.ParentalControlsPIN, 0, sizeof(settings.ParentalControlsPIN));
memset(settings.ParentalControlsSecretAnswer, 0, sizeof(settings.ParentalControlsSecretAnswer));
}
static melonDS::DSi_NAND::NANDImage CreateNandImage(
u8* nandData, u32 nandLength, std::unique_ptr<melonDS::DSiBIOSImage>& arm7Bios,
FirmwareSettings& fwSettings, bool clearNand,
u8* dsiWareData, u32 dsiWareLength, u8* tmdData, u32 tmdLength)
{
auto nand = melonDS::DSi_NAND::NANDImage{melonDS::Platform::CreateMemoryFile(nandData, nandLength), &arm7Bios->data()[0x8308]};
if (!nand)
{
throw std::runtime_error("Failed to parse DSi NAND!");
}
{
auto mount = melonDS::DSi_NAND::NANDMount(nand);
if (!mount)
{
throw std::runtime_error("Failed to mount DSi NAND!");
}
melonDS::DSi_NAND::DSiFirmwareSystemSettings settings{};
if (!mount.ReadUserData(settings))
{
throw std::runtime_error("Failed to read DSi NAND user data");
}
// serial data will contain the NAND's region
melonDS::DSi_NAND::DSiSerialData serialData;
if (!mount.ReadSerialData(serialData))
{
throw std::runtime_error("Failed to obtain serial data!");
}
if (fwSettings.OverrideSettings)
{
SanitizeNandSettings(settings, serialData.Region);
memset(settings.Nickname, 0, sizeof(settings.Nickname));
memcpy(settings.Nickname, fwSettings.Username, fwSettings.UsernameLength * 2);
settings.Language = static_cast<melonDS::Firmware::Language>(fwSettings.Language & melonDS::Firmware::Language::Reserved);
settings.FavoriteColor = fwSettings.Color;
settings.BirthdayMonth = fwSettings.BirthdayMonth;
settings.BirthdayDay = fwSettings.BirthdayDay;
memset(settings.Message, 0, sizeof(settings.Message));
memcpy(settings.Message, fwSettings.Message, fwSettings.MessageLength * 2);
if (!((1 << static_cast<u8>(settings.Language)) & serialData.SupportedLanguages))
{
// English is valid among all NANDs
settings.Language = melonDS::Firmware::Language::English;
}
}
settings.TouchCalibrationADC1[0] = 0;
settings.TouchCalibrationADC1[1] = 0;
settings.TouchCalibrationPixel1[0] = 0;
settings.TouchCalibrationPixel1[1] = 0;
settings.TouchCalibrationADC2[0] = 255 << 4;
settings.TouchCalibrationADC2[1] = 191 << 4;
settings.TouchCalibrationPixel2[0] = 255;
settings.TouchCalibrationPixel2[1] = 191;
settings.UpdateHash();
if (!mount.ApplyUserData(settings))
{
throw std::runtime_error("Failed to write DSi NAND user data");
}
if (clearNand)
{
// clear out DSiWare
constexpr u32 DSIWARE_CATEGORY = 0x00030004;
std::vector<u32> titlelist;
mount.ListTitles(DSIWARE_CATEGORY, titlelist);
for (auto& title : titlelist)
{
mount.DeleteTitle(DSIWARE_CATEGORY, title);
}
}
if (dsiWareData)
{
if (tmdLength != sizeof(melonDS::DSi_TMD::TitleMetadata))
{
throw std::runtime_error("TMD is the wrong size!");
}
melonDS::DSi_TMD::TitleMetadata tmd;
memcpy(&tmd, tmdData, sizeof(melonDS::DSi_TMD::TitleMetadata));
if (!mount.ImportTitle(dsiWareData, dsiWareLength, tmd, false))
{
throw std::runtime_error("Loading DSiWare failed!");
}
// verify that the imported title is supported by this NAND
// it will not actually appear otherwise
auto regionFlags = dsiWareLength > 0x1B0 ? dsiWareData[0x1B0] : 0;
if (!(regionFlags & (1 << static_cast<u8>(serialData.Region))))
{
throw std::runtime_error("Loaded NAND region does not support this DSiWare title!");
}
}
}
return nand;
}
enum class GBASaveType
{
NONE,
SRAM,
EEPROM512,
EEPROM,
FLASH512,
FLASH1M,
};
#include "GBASaveOverrides.h"
static GBASaveType FindGbaSaveType(const u8* gbaRomData, size_t gbaRomSize)
{
u32 crc = melonDS::CRC32(gbaRomData, gbaRomSize);
if (auto saveOverride = GbaCrcSaveTypeOverrides.find(crc); saveOverride != GbaCrcSaveTypeOverrides.end())
{
return saveOverride->second;
}
if (gbaRomSize >= 0xB0)
{
char gameId[4];
std::memcpy(gameId, &gbaRomData[0xAC], 4);
if (auto saveOverride = GbaGameIdSaveTypeOverrides.find(std::string(gameId, 4)); saveOverride != GbaGameIdSaveTypeOverrides.end())
{
return saveOverride->second;
}
}
if (memmem(gbaRomData, gbaRomSize, "EEPROM_V", strlen("EEPROM_V")))
{
return GBASaveType::EEPROM512;
}
if (memmem(gbaRomData, gbaRomSize, "SRAM_V", strlen("SRAM_V")))
{
return GBASaveType::SRAM;
}
if (memmem(gbaRomData, gbaRomSize, "FLASH_V", strlen("FLASH_V"))
|| memmem(gbaRomData, gbaRomSize, "FLASH512_V", strlen("FLASH512_V")))
{
return GBASaveType::FLASH512;
}
if (memmem(gbaRomData, gbaRomSize, "FLASH1M_V", strlen("FLASH1M_V")))
{
return GBASaveType::FLASH1M;
}
return GBASaveType::NONE;
}
static std::pair<std::unique_ptr<u8[]>, size_t> CreateBlankGbaSram(const u8* gbaRomData, size_t gbaRomSize)
{
auto saveType = FindGbaSaveType(gbaRomData, gbaRomSize);
if (saveType == GBASaveType::NONE)
{
return std::make_pair(nullptr, 0);
}
size_t size;
switch (saveType)
{
case GBASaveType::SRAM:
size = 0x8000;
break;
case GBASaveType::EEPROM512:
size = 0x200;
break;
case GBASaveType::EEPROM:
size = 0x2000;
break;
case GBASaveType::FLASH512:
size = 0x10000;
break;
case GBASaveType::FLASH1M:
size = 0x20000;
break;
default:
__builtin_unreachable();
}
auto data = std::make_unique<u8[]>(size);
memset(data.get(), 0xFF, size);
return std::make_pair(std::move(data), size);
}
#pragma pack(push, 1)
struct DSiAutoLoad
{
u8 ID[4]; // "TLNC"
u8 Unknown1; // "usually 01h"
u8 Length; // starting from PrevTitleId
u16 CRC16; // covering Length bytes ("18h=norm")
u8 PrevTitleID[8]; // can be 0 ("anonymous")
u8 NewTitleID[8];
u32 Flags; // bit 0: is valid, bit 1-3: boot type ("01h=Cartridge, 02h=Landing, 03h=DSiware"), other bits unknown/unused
u32 Unused1; // this part is typically still checksummed
u8 Unused2[0xE0]; // this part isn't checksummed, but is 0 filled on erasing autoload data
};
#pragma pack(pop)
static_assert(sizeof(DSiAutoLoad) == 0x100, "DSiAutoLoad wrong size");
ECL_EXPORT void ResetConsole(melonDS::NDS* nds, bool skipFw, u64 dsiTitleId)
{
nds->Reset();
if (skipFw || nds->NeedsDirectBoot())
{
if (nds->GetNDSCart())
{
nds->SetupDirectBoot("nds.rom");
}
else
{
auto* dsi = static_cast<melonDS::DSi*>(nds);
// set warm boot flag
dsi->I2C.GetBPTWL()->SetBootFlag(true);
// setup "auto-load" feature
DSiAutoLoad dsiAutoLoad;
memset(&dsiAutoLoad, 0, sizeof(dsiAutoLoad));
memcpy(dsiAutoLoad.ID, "TLNC", sizeof(dsiAutoLoad.ID));
dsiAutoLoad.Unknown1 = 0x01;
dsiAutoLoad.Length = 0x18;
for (int i = 0; i < 8; i++)
{
dsiAutoLoad.NewTitleID[i] = dsiTitleId & 0xFF;
dsiTitleId >>= 8;
}
dsiAutoLoad.Flags |= (0x03 << 1) | 0x01;
dsiAutoLoad.Flags |= (1 << 4); // unknown bit, seems to be required to boot into games (errors otherwise?)
dsiAutoLoad.CRC16 = melonDS::CRC16((u8*)&dsiAutoLoad.PrevTitleID, dsiAutoLoad.Length, 0xFFFF);
memcpy(&nds->MainRAM[0x300], &dsiAutoLoad, sizeof(dsiAutoLoad));
}
}
nds->Start();
}
struct ConsoleCreationArgs
{
u8* NdsRomData;
u32 NdsRomLength;
u8* GbaRomData;
u32 GbaRomLength;
u8* Arm9BiosData;
u32 Arm9BiosLength;
u8* Arm7BiosData;
u32 Arm7BiosLength;
u8* FirmwareData;
u32 FirmwareLength;
u8* Arm9iBiosData;
u32 Arm9iBiosLength;
u8* Arm7iBiosData;
u32 Arm7iBiosLength;
u8* NandData;
u32 NandLength;
u8* DsiWareData;
u32 DsiWareLength;
u8* TmdData;
u32 TmdLength;
bool DSi;
bool ClearNAND;
bool SkipFW;
int BitDepth;
int Interpolation;
int ThreeDeeRenderer;
bool Threaded3D;
int ScaleFactor;
bool BetterPolygons;
bool HiResCoordinates;
int StartYear; // 0-99
int StartMonth; // 1-12
int StartDay; // 1-(28/29/30/31 depending on month/year)
int StartHour; // 0-23
int StartMinute; // 0-59
int StartSecond; // 0-59
FirmwareSettings FwSettings;
};
ECL_EXPORT melonDS::NDS* CreateConsole(ConsoleCreationArgs* args, char* error)
{
try
{
std::unique_ptr<melonDS::NDSCart::CartCommon> ndsRom = nullptr;
if (args->NdsRomData)
{
ndsRom = melonDS::NDSCart::ParseROM(args->NdsRomData, args->NdsRomLength, std::nullopt);
if (!ndsRom)
{
throw std::runtime_error("Failed to parse NDS ROM");
}
}
std::unique_ptr<melonDS::GBACart::CartCommon> gbaRom = nullptr;
if (args->GbaRomData)
{
auto gbaSram = CreateBlankGbaSram(args->GbaRomData, args->GbaRomLength);
gbaRom = melonDS::GBACart::ParseROM(args->GbaRomData, args->GbaRomLength, gbaSram.first.get(), gbaSram.second);
if (!gbaRom)
{
throw std::runtime_error("Failed to parse GBA ROM");
}
}
auto arm9Bios = CreateBiosImage<melonDS::ARM9BIOSImage>(args->Arm9BiosData, args->Arm9BiosLength, melonDS::bios_arm9_bin);
auto arm7Bios = CreateBiosImage<melonDS::ARM7BIOSImage>(args->Arm7BiosData, args->Arm7BiosLength, melonDS::bios_arm7_bin);
auto firmware = CreateFirmware(args->FirmwareData, args->FirmwareLength, args->DSi, args->FwSettings);
auto bitDepth = static_cast<melonDS::AudioBitDepth>(args->BitDepth);
auto interpolation = static_cast<melonDS::AudioInterpolation>(args->Interpolation);
std::unique_ptr<melonDS::Renderer3D> renderer3d;
switch (args->ThreeDeeRenderer)
{
case 0:
{
auto softRenderer = std::make_unique<melonDS::SoftRenderer>();
// SetThreaded needs the nds GPU field, so can't do this now
// softRenderer->SetThreaded(args->Threaded3D, nds->GPU);
renderer3d = std::move(softRenderer);
break;
}
case 1:
{
auto glRenderer = melonDS::GLRenderer::New();
glRenderer->SetRenderSettings(args->BetterPolygons, args->ScaleFactor);
renderer3d = std::move(glRenderer);
break;
}
case 2:
{
auto computeRenderer = melonDS::ComputeRenderer::New();
computeRenderer->SetRenderSettings(args->ScaleFactor, args->HiResCoordinates);
renderer3d = std::move(computeRenderer);
break;
}
default:
throw std::runtime_error("Unknown 3DRenderer!");
}
int currentShader, shadersCount;
while (renderer3d->NeedsShaderCompile())
{
renderer3d->ShaderCompileStep(currentShader, shadersCount);
}
std::unique_ptr<melonDS::NDS> nds;
if (args->DSi)
{
auto arm9iBios = CreateBiosImage<melonDS::DSiBIOSImage>(args->Arm9iBiosData, args->Arm9iBiosLength);
auto arm7iBios = CreateBiosImage<melonDS::DSiBIOSImage>(args->Arm7iBiosData, args->Arm7iBiosLength);
// upstream applies this patch to overwrite the reset vector for non-full boots
static const u8 dsiBiosPatch[] = { 0xFE, 0xFF, 0xFF, 0xEA };
memcpy(arm9iBios->data(), dsiBiosPatch, sizeof(dsiBiosPatch));
memcpy(arm7iBios->data(), dsiBiosPatch, sizeof(dsiBiosPatch));
auto nandImage = CreateNandImage(
args->NandData, args->NandLength, arm7iBios,
args->FwSettings, args->ClearNAND,
args->DsiWareData, args->DsiWareLength, args->TmdData, args->TmdLength);
melonDS::DSiArgs dsiArgs
{
std::move(ndsRom),
std::move(gbaRom),
std::move(arm9Bios),
std::move(arm7Bios),
std::move(firmware),
std::nullopt,
bitDepth,
interpolation,
std::nullopt,
std::move(renderer3d),
// dsi specific args
std::move(arm9iBios),
std::move(arm7iBios),
std::move(nandImage),
std::nullopt,
false,
};
nds = std::make_unique<melonDS::DSi>(std::move(dsiArgs));
}
else
{
melonDS::NDSArgs ndsArgs
{
std::move(ndsRom),
std::move(gbaRom),
std::move(arm9Bios),
std::move(arm7Bios),
std::move(firmware),
std::nullopt,
bitDepth,
interpolation,
std::nullopt,
std::move(renderer3d)
};
nds = std::make_unique<melonDS::NDS>(std::move(ndsArgs));
}
if (args->ThreeDeeRenderer == 0)
{
auto& softRenderer = static_cast<melonDS::SoftRenderer&>(nds->GetRenderer3D());
softRenderer.SetThreaded(args->Threaded3D, nds->GPU);
}
nds->RTC.SetDateTime(args->StartYear, args->StartMonth, args->StartDay,
args->StartHour, args->StartMinute, args->StartSecond);
u64 dsiWareId = 0;
if (args->DsiWareLength >= 0x238)
{
for (int i = 0; i < 8; i++)
{
dsiWareId <<= 8;
dsiWareId |= args->DsiWareData[0x237 - i];
}
}
ResetConsole(nds.get(), args->SkipFW, dsiWareId);
CurrentNDS = nds.release();
return CurrentNDS;
}
catch (const std::exception& e)
{
strncpy(error, e.what(), 1024);
return nullptr;
}
}
}

View File

@ -5,10 +5,13 @@
#include "ARM.h" #include "ARM.h"
#include "SPI.h" #include "SPI.h"
#include "BizTypes.h"
#include "dthumb.h" #include "dthumb.h"
#include <waterboxcore.h> #include <waterboxcore.h>
melonDS::NDS* CurrentNDS;
void (*InputCallback)() = nullptr; void (*InputCallback)() = nullptr;
ECL_EXPORT void SetInputCallback(void (*callback)()) ECL_EXPORT void SetInputCallback(void (*callback)())
@ -56,7 +59,7 @@ ECL_EXPORT void GetDisassembly(TraceMask_t type, u32 opcode, char* ret)
memcpy(ret, disasm, DTHUMB_STRING_LENGTH); memcpy(ret, disasm, DTHUMB_STRING_LENGTH);
} }
void TraceTrampoline(TraceMask_t type, u32* regs, u32 opcode) void TraceTrampoline(TraceMask_t type, u32* regs, u32 opcode, u32 cycleOffset)
{ {
static char disasm[DTHUMB_STRING_LENGTH]; static char disasm[DTHUMB_STRING_LENGTH];
@ -70,31 +73,31 @@ void TraceTrampoline(TraceMask_t type, u32* regs, u32 opcode)
default: __builtin_unreachable(); default: __builtin_unreachable();
} }
TraceCallback(type, opcode, regs, disasm, NDS::GetSysClockCycles(2)); TraceCallback(type, opcode, regs, disasm, cycleOffset);
} }
ECL_EXPORT void GetRegs(u32* regs) ECL_EXPORT void GetRegs(melonDS::NDS* nds, u32* regs)
{ {
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
{ {
*regs++ = NDS::ARM9->R[i]; *regs++ = nds->ARM9.R[i];
} }
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
{ {
*regs++ = NDS::ARM7->R[i]; *regs++ = nds->ARM7.R[i];
} }
} }
ECL_EXPORT void SetReg(s32 ncpu, s32 index, s32 val) ECL_EXPORT void SetReg(melonDS::NDS* nds, s32 ncpu, s32 index, s32 val)
{ {
if (ncpu) if (ncpu)
{ {
NDS::ARM7->R[index] = val; nds->ARM7.R[index] = val;
} }
else else
{ {
NDS::ARM9->R[index] = val; nds->ARM9.R[index] = val;
} }
} }
@ -176,42 +179,44 @@ static void ARM9Access(u8* buffer, s64 address, s64 count, bool write)
{ {
if (write) if (write)
{ {
void (*Write)(u32, u8) = NDS::ConsoleType == 1 ? DSi::ARM9Write8 : NDS::ARM9Write8;
while (count--) while (count--)
{ {
if (address < NDS::ARM9->ITCMSize) if (address < CurrentNDS->ARM9.ITCMSize)
{ {
NDS::ARM9->ITCM[address++ & (ITCMPhysicalSize - 1)] = *buffer++; CurrentNDS->ARM9.ITCM[address & (melonDS::ITCMPhysicalSize - 1)] = *buffer;
} }
else if ((address & NDS::ARM9->DTCMMask) == NDS::ARM9->DTCMBase) else if ((address & CurrentNDS->ARM9.DTCMMask) == CurrentNDS->ARM9.DTCMBase)
{ {
NDS::ARM9->DTCM[address++ & (DTCMPhysicalSize - 1)] = *buffer++; CurrentNDS->ARM9.DTCM[address & (melonDS::DTCMPhysicalSize - 1)] = *buffer;
} }
else else
{ {
Write(address++, *buffer++); CurrentNDS->ARM9Write8(address, *buffer);
} }
address++;
buffer++;
} }
} }
else else
{ {
u8 (*Read)(u32) = NDS::ConsoleType == 1 ? DSi::ARM9Read8 : NDS::ARM9Read8;
while (count--) while (count--)
{ {
if (address < NDS::ARM9->ITCMSize) if (address < CurrentNDS->ARM9.ITCMSize)
{ {
*buffer++ = NDS::ARM9->ITCM[address & (ITCMPhysicalSize - 1)]; *buffer = CurrentNDS->ARM9.ITCM[address & (melonDS::ITCMPhysicalSize - 1)];
} }
else if ((address & NDS::ARM9->DTCMMask) == NDS::ARM9->DTCMBase) else if ((address & CurrentNDS->ARM9.DTCMMask) == CurrentNDS->ARM9.DTCMBase)
{ {
*buffer++ = NDS::ARM9->DTCM[address & (DTCMPhysicalSize - 1)]; *buffer = CurrentNDS->ARM9.DTCM[address & (melonDS::DTCMPhysicalSize - 1)];
} }
else else
{ {
*buffer++ = SafeToPeek<true>(address) ? Read(address) : 0; *buffer = SafeToPeek<true>(address) ? CurrentNDS->ARM9Read8(address) : 0;
} }
address++; address++;
buffer++;
} }
} }
} }
@ -220,15 +225,23 @@ static void ARM7Access(u8* buffer, s64 address, s64 count, bool write)
{ {
if (write) if (write)
{ {
void (*Write)(u32, u8) = NDS::ConsoleType == 1 ? DSi::ARM7Write8 : NDS::ARM7Write8;
while (count--) while (count--)
Write(address++, *buffer++); {
CurrentNDS->ARM7Write8(address, *buffer);
address++;
buffer++;
}
} }
else else
{ {
u8 (*Read)(u32) = NDS::ConsoleType == 1 ? DSi::ARM7Read8 : NDS::ARM7Read8;
while (count--) while (count--)
*buffer++ = SafeToPeek<true>(address) ? Read(address) : 0, address++; {
*buffer = SafeToPeek<false>(address) ? CurrentNDS->ARM7Read8(address) : 0;
address++;
buffer++;
}
} }
} }
@ -237,41 +250,43 @@ ECL_EXPORT void GetMemoryAreas(MemoryArea *m)
int i = 0; int i = 0;
#define ADD_MEMORY_DOMAIN(name, data, size, flags) do \ #define ADD_MEMORY_DOMAIN(name, data, size, flags) do \
{ \ { \
m[i].Data = (void*)data; \ m[i].Data = reinterpret_cast<void*>(data); \
m[i].Name = name; \ m[i].Name = name; \
m[i].Size = size; \ m[i].Size = size; \
m[i].Flags = flags; \ m[i].Flags = flags; \
i++; \ i++; \
} while (0) } while (0)
ADD_MEMORY_DOMAIN("Main RAM", NDS::MainRAM, NDS::ConsoleType == 1 ? NDS::MainRAMMaxSize : NDS::MainRAMMaxSize / 4, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_PRIMARY); const auto mainRamSize = CurrentNDS->ConsoleType == 1 ? melonDS::MainRAMMaxSize : melonDS::MainRAMMaxSize / 4;
ADD_MEMORY_DOMAIN("Shared WRAM", NDS::SharedWRAM, NDS::SharedWRAMSize, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE); ADD_MEMORY_DOMAIN("Main RAM", CurrentNDS->MainRAM, mainRamSize, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_PRIMARY);
ADD_MEMORY_DOMAIN("ARM7 WRAM", NDS::ARM7WRAM, NDS::ARM7WRAMSize, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE); ADD_MEMORY_DOMAIN("Shared WRAM", CurrentNDS->SharedWRAM, melonDS::SharedWRAMSize, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE);
ADD_MEMORY_DOMAIN("ARM7 WRAM", CurrentNDS->ARM7WRAM, melonDS::ARM7WRAMSize, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE);
if (NDSCart::Cart) if (auto* ndsCart = CurrentNDS->GetNDSCart())
{ {
ADD_MEMORY_DOMAIN("SRAM", NDSCart::GetSaveMemory(), NDSCart::GetSaveMemoryLength(), MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE); ADD_MEMORY_DOMAIN("SRAM", CurrentNDS->GetNDSSave(), CurrentNDS->GetNDSSaveLength(), MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE);
ADD_MEMORY_DOMAIN("ROM", NDSCart::Cart->GetROM(), NDSCart::Cart->GetROMLength(), MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE); ADD_MEMORY_DOMAIN("ROM", const_cast<u8*>(ndsCart->GetROM()), ndsCart->GetROMLength(), MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE);
} }
if (GBACart::Cart) if (auto* gbaCart = CurrentNDS->GetGBACart())
{ {
ADD_MEMORY_DOMAIN("GBA SRAM", GBACart::GetSaveMemory(), GBACart::GetSaveMemoryLength(), MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE); ADD_MEMORY_DOMAIN("GBA SRAM", CurrentNDS->GetGBASave(), CurrentNDS->GetGBASaveLength(), MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE);
ADD_MEMORY_DOMAIN("GBA ROM", GBACart::Cart->GetROM(), GBACart::Cart->GetROMLength(), MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE); ADD_MEMORY_DOMAIN("GBA ROM", const_cast<u8*>(gbaCart->GetROM()), gbaCart->GetROMLength(), MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE);
} }
ADD_MEMORY_DOMAIN("Instruction TCM", NDS::ARM9->ITCM, ITCMPhysicalSize, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE); ADD_MEMORY_DOMAIN("Instruction TCM", CurrentNDS->ARM9.ITCM, melonDS::ITCMPhysicalSize, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE);
ADD_MEMORY_DOMAIN("Data TCM", NDS::ARM9->DTCM, DTCMPhysicalSize, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE); ADD_MEMORY_DOMAIN("Data TCM", CurrentNDS->ARM9.DTCM, melonDS::DTCMPhysicalSize, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE);
ADD_MEMORY_DOMAIN("ARM9 BIOS", NDS::ARM9BIOS, sizeof(NDS::ARM9BIOS), MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE); ADD_MEMORY_DOMAIN("ARM9 BIOS", const_cast<u8*>(CurrentNDS->GetARM9BIOS().data()), melonDS::ARM9BIOSSize, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE);
ADD_MEMORY_DOMAIN("ARM7 BIOS", NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS), MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE); ADD_MEMORY_DOMAIN("ARM7 BIOS", const_cast<u8*>(CurrentNDS->GetARM7BIOS().data()), melonDS::ARM7BIOSSize, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE);
ADD_MEMORY_DOMAIN("Firmware", SPI_Firmware::GetFirmware()->Buffer(), SPI_Firmware::GetFirmware()->Length(), MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE); ADD_MEMORY_DOMAIN("Firmware", CurrentNDS->GetFirmware().Buffer(), CurrentNDS->GetFirmware().Length(), MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE);
if (NDS::ConsoleType == 1) if (CurrentNDS->ConsoleType == 1)
{ {
ADD_MEMORY_DOMAIN("ARM9i BIOS", DSi::ARM9iBIOS, sizeof(DSi::ARM9iBIOS), MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE); auto* dsi = static_cast<melonDS::DSi*>(CurrentNDS);
ADD_MEMORY_DOMAIN("ARM7i BIOS", DSi::ARM7iBIOS, sizeof(DSi::ARM7iBIOS), MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE); ADD_MEMORY_DOMAIN("ARM9i BIOS", dsi->ARM9iBIOS.data(), melonDS::DSiBIOSSize, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE);
ADD_MEMORY_DOMAIN("ARM7i BIOS", dsi->ARM7iBIOS.data(), melonDS::DSiBIOSSize, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE);
} }
ADD_MEMORY_DOMAIN("ARM9 System Bus", ARM9Access, 1ull << 32, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_FUNCTIONHOOK); ADD_MEMORY_DOMAIN("ARM9 System Bus", ARM9Access, 1ull << 32, MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_FUNCTIONHOOK);

View File

@ -1,628 +0,0 @@
#include "NDS.h"
#include "NDSCart.h"
#include "DSi.h"
#include "DSi_NAND.h"
#include "DSi_TMD.h"
#include "CRC32.h"
#include "FreeBIOS.h"
#include "SPI.h"
#include "BizFileManager.h"
// need to peek at these internals
namespace DSi_BPTWL
{
extern u8 Registers[0x100];
}
namespace FileManager
{
constexpr u32 DSIWARE_CATEGORY = 0x00030004;
static u8 DSiWareID[8];
static std::optional<std::pair<std::unique_ptr<u8[]>, size_t>> GetFileData(std::string path)
{
auto file = Platform::OpenFile(path, Platform::FileMode::Read);
if (!file)
{
return std::nullopt;
}
size_t size = Platform::FileLength(file);
auto data = std::make_unique<u8[]>(size);
Platform::FileRewind(file);
Platform::FileRead(data.get(), size, 1, file);
Platform::CloseFile(file);
return std::make_pair(std::move(data), size);
}
static bool LoadBIOS(const char* path, u8* buffer, size_t len)
{
auto bios = Platform::OpenFile(path, Platform::FileMode::Read);
if (!bios)
{
return false;
}
if (Platform::FileLength(bios) != len)
{
Platform::CloseFile(bios);
return false;
}
Platform::FileRewind(bios);
auto read = Platform::FileRead(buffer, len, 1, bios);
Platform::CloseFile(bios);
return read == len;
}
// Inits NDS BIOS7 and BIOS9
const char* InitNDSBIOS()
{
if (NDS::ConsoleType == 1 || Platform::GetConfigBool(Platform::ExternalBIOSEnable))
{
if (!LoadBIOS("bios7.bin", NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS)))
{
return "Failed to load BIOS7!";
}
if (!LoadBIOS("bios9.bin", NDS::ARM9BIOS, sizeof(NDS::ARM9BIOS)))
{
return "Failed to load BIOS9!";
}
}
else
{
memcpy(NDS::ARM7BIOS, bios_arm7_bin, sizeof(bios_arm7_bin));
memcpy(NDS::ARM9BIOS, bios_arm9_bin, sizeof(bios_arm9_bin));
}
return nullptr;
}
static void SanitizeExternalFirmware(SPI_Firmware::Firmware& firmware)
{
auto& header = firmware.Header();
const bool isDSiFw = header.ConsoleType == SPI_Firmware::FirmwareConsoleType::DSi;
const auto defaultHeader = SPI_Firmware::FirmwareHeader(isDSiFw);
// the user data offset won't necessarily be 0x7FE00 & Mask, DSi/iQue use 0x7FC00 & Mask instead
// but we don't want to crash due to an invalid offset
const auto maxUserDataOffset = 0x7FE00 & firmware.Mask();
if (firmware.UserDataOffset() > maxUserDataOffset)
{
header.UserSettingsOffset = maxUserDataOffset >> 3;
}
if (isDSiFw)
{
memset(&header.Bytes[0x22], 0x00, 6);
memset(&header.Bytes[0x28], 0xFF, 2);
}
memcpy(&header.Bytes[0x2C], &defaultHeader.Bytes[0x2C], 0x136);
memset(&header.Bytes[0x162], 0xFF, 0x9E);
if (isDSiFw)
{
header.WifiBoard = defaultHeader.WifiBoard;
header.WifiFlash = defaultHeader.WifiFlash;
}
header.UpdateChecksum();
auto& aps = firmware.AccessPoints();
aps[0] = SPI_Firmware::WifiAccessPoint(isDSiFw);
aps[1] = SPI_Firmware::WifiAccessPoint();
aps[2] = SPI_Firmware::WifiAccessPoint();
if (isDSiFw)
{
auto& exAps = firmware.ExtendedAccessPoints();
exAps[0] = SPI_Firmware::ExtendedWifiAccessPoint();
exAps[1] = SPI_Firmware::ExtendedWifiAccessPoint();
exAps[2] = SPI_Firmware::ExtendedWifiAccessPoint();
}
}
static void FixFirmwareTouchscreenCalibration(SPI_Firmware::UserData& userData)
{
userData.TouchCalibrationADC1[0] = 0;
userData.TouchCalibrationADC1[1] = 0;
userData.TouchCalibrationPixel1[0] = 0;
userData.TouchCalibrationPixel1[1] = 0;
userData.TouchCalibrationADC2[0] = 255 << 4;
userData.TouchCalibrationADC2[1] = 191 << 4;
userData.TouchCalibrationPixel2[0] = 255;
userData.TouchCalibrationPixel2[1] = 191;
}
static void SetFirmwareSettings(SPI_Firmware::UserData& userData, FirmwareSettings& fwSettings)
{
memset(userData.Bytes, 0, 0x74);
userData.Version = 5;
FixFirmwareTouchscreenCalibration(userData);
userData.NameLength = fwSettings.UsernameLength;
memcpy(userData.Nickname, fwSettings.Username, sizeof(fwSettings.Username));
userData.Settings = fwSettings.Language | SPI_Firmware::BacklightLevel::Max | 0xEC00;
userData.BirthdayMonth = fwSettings.BirthdayMonth;
userData.BirthdayDay = fwSettings.BirthdayDay;
userData.FavoriteColor = fwSettings.Color;
userData.MessageLength = fwSettings.MessageLength;
memcpy(userData.Message, fwSettings.Message, sizeof(fwSettings.Message));
if (userData.ExtendedSettings.Unknown0 == 1)
{
userData.ExtendedSettings.ExtendedLanguage = static_cast<SPI_Firmware::Language>(fwSettings.Language & SPI_Firmware::Language::Reserved);
memset(userData.ExtendedSettings.Unused0, 0, sizeof(userData.ExtendedSettings.Unused0));
if (!((1 << static_cast<u8>(userData.ExtendedSettings.ExtendedLanguage)) & userData.ExtendedSettings.SupportedLanguageMask))
{
userData.ExtendedSettings.ExtendedLanguage = SPI_Firmware::Language::English;
userData.Settings &= ~SPI_Firmware::Language::Reserved;
userData.Settings |= SPI_Firmware::Language::English;
}
}
else
{
memset(userData.Unused3, 0xFF, sizeof(userData.Unused3));
}
// only extended settings should have Chinese / Korean
// note that SPI_Firmware::Language::Reserved is Korean, so it's valid to have language set to that
if ((userData.Settings & SPI_Firmware::Language::Reserved) >= SPI_Firmware::Language::Chinese)
{
userData.Settings &= ~SPI_Firmware::Language::Reserved;
userData.Settings |= SPI_Firmware::Language::English;
}
}
// Inits NDS firmware
const char* InitFirmware(FirmwareSettings& fwSettings)
{
auto firmware = SPI_Firmware::Firmware(NDS::ConsoleType);
if (Platform::GetConfigBool(Platform::ExternalBIOSEnable))
{
auto fw = Platform::OpenFile("firmware.bin", Platform::FileMode::Read);
if (!fw)
{
return "Failed to obtain firmware!";
}
firmware = SPI_Firmware::Firmware(fw);
if (firmware.Buffer())
{
// sanitize header, wifi calibration, and AP points
SanitizeExternalFirmware(firmware);
}
}
else
{
fwSettings.OverrideSettings = true;
}
if (!firmware.Buffer())
{
return "Failed to load firmware!";
}
for (SPI_Firmware::UserData& userData : firmware.UserData())
{
FixFirmwareTouchscreenCalibration(userData);
userData.UpdateChecksum();
}
if (fwSettings.OverrideSettings)
{
for (SPI_Firmware::UserData& userData : firmware.UserData())
{
SetFirmwareSettings(userData, fwSettings);
userData.UpdateChecksum();
}
SPI_Firmware::MacAddress mac;
Platform::GetConfigArray(Platform::Firm_MAC, &mac);
auto& header = firmware.Header();
header.MacAddress = mac;
header.UpdateChecksum();
}
if (!SPI_Firmware::InstallFirmware(std::move(firmware)))
{
return "Failed to install firmware!";
}
return nullptr;
}
// Inits DSi BIOS7i and BIOS9i
const char* InitDSiBIOS()
{
if (NDS::ConsoleType == 0)
{
return "Tried to init DSi BIOSes in NDS mode";
}
if (!LoadBIOS("bios7i.bin", DSi::ARM7iBIOS, sizeof(DSi::ARM7iBIOS)))
{
return "Failed to load BIOS7i!";
}
if (!LoadBIOS("bios9i.bin", DSi::ARM9iBIOS, sizeof(DSi::ARM9iBIOS)))
{
return "Failed to load BIOS9i!";
}
if (!Platform::GetConfigBool(Platform::DSi_FullBIOSBoot))
{
// upstream applies this patch, for whatever reason
static const u8 patch[] = { 0xFE, 0xFF, 0xFF, 0xEA };
memcpy(DSi::ARM7iBIOS, patch, sizeof(patch));
memcpy(DSi::ARM9iBIOS, patch, sizeof(patch));
}
return nullptr;
}
static u8 GetDefaultCountryCode(DSi_NAND::ConsoleRegion region)
{
// TODO: CountryCode probably should be configurable
// these defaults are also completely arbitrary
switch (region)
{
case DSi_NAND::ConsoleRegion::Japan: return 0x01; // Japan
case DSi_NAND::ConsoleRegion::USA: return 0x31; // United States
case DSi_NAND::ConsoleRegion::Europe: return 0x6E; // United Kingdom
case DSi_NAND::ConsoleRegion::Australia: return 0x41; // Australia
case DSi_NAND::ConsoleRegion::China: return 0xA0; // China
case DSi_NAND::ConsoleRegion::Korea: return 0x88; // Korea
default: return 0x31; // ???
}
}
static void SanitizeNANDSettings(DSi_NAND::DSiFirmwareSystemSettings& settings, DSi_NAND::ConsoleRegion region)
{
memset(settings.Zero00, 0, sizeof(settings.Zero00));
settings.Version = 1;
settings.UpdateCounter = 0;
memset(settings.Zero01, 0, sizeof(settings.Zero01));
settings.BelowRAMAreaSize = 0x128;
// bit 0-1 are unknown (but usually 1)
// bit 2 indicates language set (?)
// bit 3 is wifi enable (really wifi LED enable; usually set)
// bit 24 set will indicate EULA is "agreed" to
settings.ConfigFlags = 0x0100000F;
settings.Zero02 = 0;
settings.CountryCode = GetDefaultCountryCode(region);
settings.RTCYear = 0;
settings.RTCOffset = 0;
memset(settings.Zero3, 0, sizeof(settings.Zero3));
settings.EULAVersion = 1;
memset(settings.Zero04, 0, sizeof(settings.Zero04));
settings.AlarmHour = 0;
settings.AlarmMinute = 0;
memset(settings.Zero05, 0, sizeof(settings.Zero05));
settings.AlarmEnable = false;
memset(settings.Zero06, 0, sizeof(settings.Zero06));
settings.Unknown0 = 0;
settings.Unknown1 = 3; // apparently 2 or 3
memset(settings.Zero07, 0, sizeof(settings.Zero07));
settings.SystemMenuMostRecentTitleID.fill(0);
settings.Unknown2[0] = 0x9C;
settings.Unknown2[1] = 0x20;
settings.Unknown2[2] = 0x01;
settings.Unknown2[3] = 0x02;
memset(settings.Zero08, 0, sizeof(settings.Zero08));
settings.Zero09 = 0;
settings.ParentalControlsFlags = 0;
memset(settings.Zero10, 0, sizeof(settings.Zero10));
settings.ParentalControlsRegion = 0;
settings.ParentalControlsYearsOfAgeRating = 0;
settings.ParentalControlsSecretQuestion = 0;
settings.Unknown3 = 0; // apparently 0 or 6 or 7
memset(settings.Zero11, 0, sizeof(settings.Zero11));
memset(settings.ParentalControlsPIN, 0, sizeof(settings.ParentalControlsPIN));
memset(settings.ParentalControlsSecretAnswer, 0, sizeof(settings.ParentalControlsSecretAnswer));
}
const char* InitNAND(FirmwareSettings& fwSettings, bool clearNand, bool dsiWare)
{
auto nand = DSi_NAND::NANDImage(Platform::OpenFile("nand.bin", Platform::FileMode::ReadWrite), &DSi::ARM7iBIOS[0x8308]);
if (!nand)
{
return "Failed to parse DSi NAND!";
}
{
auto mount = DSi_NAND::NANDMount(nand);
if (!mount)
{
return "Failed to mount DSi NAND!";
}
DSi_NAND::DSiFirmwareSystemSettings settings{};
if (!mount.ReadUserData(settings))
{
return "Failed to read DSi NAND user data";
}
// serial data will contain the NAND's region
DSi_NAND::DSiSerialData serialData;
if (!mount.ReadSerialData(serialData))
{
return "Failed to obtain serial data!";
}
if (fwSettings.OverrideSettings)
{
SanitizeNANDSettings(settings, serialData.Region);
memset(settings.Nickname, 0, sizeof(settings.Nickname));
memcpy(settings.Nickname, fwSettings.Username, fwSettings.UsernameLength * 2);
settings.Language = static_cast<SPI_Firmware::Language>(fwSettings.Language & SPI_Firmware::Language::Reserved);
settings.FavoriteColor = fwSettings.Color;
settings.BirthdayMonth = fwSettings.BirthdayMonth;
settings.BirthdayDay = fwSettings.BirthdayDay;
memset(settings.Message, 0, sizeof(settings.Message));
memcpy(settings.Message, fwSettings.Message, fwSettings.MessageLength * 2);
if (!((1 << static_cast<u8>(settings.Language)) & serialData.SupportedLanguages))
{
// English is valid among all NANDs
settings.Language = SPI_Firmware::Language::English;
}
}
settings.TouchCalibrationADC1[0] = 0;
settings.TouchCalibrationADC1[1] = 0;
settings.TouchCalibrationPixel1[0] = 0;
settings.TouchCalibrationPixel1[1] = 0;
settings.TouchCalibrationADC2[0] = 255 << 4;
settings.TouchCalibrationADC2[1] = 191 << 4;
settings.TouchCalibrationPixel2[0] = 255;
settings.TouchCalibrationPixel2[1] = 191;
settings.UpdateHash();
if (!mount.ApplyUserData(settings))
{
return "Failed to write DSi NAND user data";
}
if (clearNand)
{
std::vector<u32> titlelist;
mount.ListTitles(DSIWARE_CATEGORY, titlelist);
for (auto& title : titlelist)
{
mount.DeleteTitle(DSIWARE_CATEGORY, title);
}
}
if (dsiWare)
{
auto rom = GetFileData("dsiware.rom");
if (!rom)
{
return "Failed to obtain DSiWare ROM!";
}
auto tmdData = GetFileData("tmd.rom");
if (!tmdData)
{
return "Failed to obtain TMD!";
}
if (tmdData->second != sizeof(DSi_TMD::TitleMetadata))
{
return "TMD is the wrong size!";
}
DSi_TMD::TitleMetadata tmd;
memcpy(&tmd, tmdData->first.get(), sizeof(DSi_TMD::TitleMetadata));
if (!mount.ImportTitle(rom->first.get(), rom->second, tmd, false))
{
return "Loading DSiWare failed!";
}
// verify that the imported title is supported by this NAND
// it will not actually appear otherwise
auto regionFlags = rom->second > 0x1B0 ? rom->first[0x1B0] : 0;
if (!(regionFlags & (1 << static_cast<u8>(serialData.Region))))
{
return "Loaded NAND region does not support this DSiWare title!";
}
if (rom->second >= 0x238)
{
memcpy(&DSiWareID, &rom->first[0x230], sizeof(DSiWareID));
}
}
}
DSi::NANDImage = std::make_unique<DSi_NAND::NANDImage>(std::move(nand));
return nullptr;
}
enum class GBASaveType
{
NONE,
SRAM,
EEPROM512,
EEPROM,
FLASH512,
FLASH1M,
};
#include "GBASaveOverrides.h"
static GBASaveType FindGbaSaveType(const u8* gbaRomData, size_t gbaRomSize)
{
u32 crc = CRC32(gbaRomData, gbaRomSize);
if (auto saveOverride = GbaCrcSaveTypeOverrides.find(crc); saveOverride != GbaCrcSaveTypeOverrides.end())
{
return saveOverride->second;
}
if (gbaRomSize >= 0xB0)
{
char gameId[4];
std::memcpy(gameId, &gbaRomData[0xAC], 4);
if (auto saveOverride = GbaGameIdSaveTypeOverrides.find(std::string(gameId, 4)); saveOverride != GbaGameIdSaveTypeOverrides.end())
{
return saveOverride->second;
}
}
if (memmem(gbaRomData, gbaRomSize, "EEPROM_V", strlen("EEPROM_V")))
{
return GBASaveType::EEPROM512;
}
if (memmem(gbaRomData, gbaRomSize, "SRAM_V", strlen("SRAM_V")))
{
return GBASaveType::SRAM;
}
if (memmem(gbaRomData, gbaRomSize, "FLASH_V", strlen("FLASH_V"))
|| memmem(gbaRomData, gbaRomSize, "FLASH512_V", strlen("FLASH512_V")))
{
return GBASaveType::FLASH512;
}
if (memmem(gbaRomData, gbaRomSize, "FLASH1M_V", strlen("FLASH1M_V")))
{
return GBASaveType::FLASH1M;
}
return GBASaveType::NONE;
}
static std::pair<std::unique_ptr<u8[]>, size_t> CreateBlankGbaSram(const u8* gbaRomData, size_t gbaRomSize)
{
auto saveType = FindGbaSaveType(gbaRomData, gbaRomSize);
if (saveType == GBASaveType::NONE)
{
return std::make_pair(nullptr, 0);
}
size_t size;
switch (saveType)
{
case GBASaveType::SRAM:
size = 0x8000;
break;
case GBASaveType::EEPROM512:
size = 0x200;
break;
case GBASaveType::EEPROM:
size = 0x2000;
break;
case GBASaveType::FLASH512:
size = 0x10000;
break;
case GBASaveType::FLASH1M:
size = 0x20000;
break;
default:
__builtin_unreachable();
}
auto data = std::make_unique<u8[]>(size);
memset(data.get(), 0xFF, size);
return std::make_pair(std::move(data), size);
}
const char* InitCarts(bool gba)
{
auto ndsRom = GetFileData("nds.rom");
if (!ndsRom)
{
return "Failed to obtain NDS ROM!";
}
if (!NDS::LoadCart(ndsRom->first.get(), ndsRom->second, nullptr, 0))
{
return "Failed to load NDS ROM!";
}
if (ndsRom->second >= 0x15C && NDS::IsLoadedARM9BIOSBuiltIn())
{
// copy logo to the ARM9 bios
// this is only needed for the builtin bios, which omits the logo
memcpy(&NDS::ARM9BIOS[0x20], &ndsRom->first[0xC0], 0x9C);
}
if (gba)
{
auto gbaRom = GetFileData("gba.rom");
if (!gbaRom)
{
return "Failed to obtain GBA ROM!";
}
auto gbaSram = CreateBlankGbaSram(gbaRom->first.get(), gbaRom->second);
if (!NDS::LoadGBACart(gbaRom->first.get(), gbaRom->second, gbaSram.first.get(), gbaSram.second))
{
return "Failed to load GBA ROM!";
}
}
return nullptr;
}
#pragma pack(push, 1)
struct DSiAutoLoad
{
u8 ID[4]; // "TLNC"
u8 Unknown1; // "usually 01h"
u8 Length; // starting from PrevTitleId
u16 CRC16; // covering Length bytes ("18h=norm")
u8 PrevTitleID[8]; // can be 0 ("anonymous")
u8 NewTitleID[8];
u32 Flags; // bit 0: is valid, bit 1-3: boot type ("01h=Cartridge, 02h=Landing, 03h=DSiware"), other bits unknown/unused
u32 Unused1; // this part is typically still checksummed
u8 Unused2[0xE0]; // this part isn't checksummed, but is 0 filled on erasing autoload data
};
#pragma pack(pop)
static_assert(sizeof(DSiAutoLoad) == 0x100, "DSiAutoLoad wrong size");
void SetupDirectBoot()
{
if (NDSCart::Cart)
{
NDS::SetupDirectBoot("nds.rom");
}
else
{
// set warm boot flag
DSi_BPTWL::Registers[0x70] |= 1;
// setup "auto-load" feature
DSiAutoLoad dsiAutoLoad;
memset(&dsiAutoLoad, 0, sizeof(dsiAutoLoad));
memcpy(dsiAutoLoad.ID, "TLNC", sizeof(dsiAutoLoad.ID));
dsiAutoLoad.Unknown1 = 0x01;
dsiAutoLoad.Length = 0x18;
memcpy(dsiAutoLoad.NewTitleID, DSiWareID, sizeof(DSiWareID));
dsiAutoLoad.Flags |= (0x03 << 1) | 0x01;
dsiAutoLoad.Flags |= (1 << 4); // unknown bit, seems to be required to boot into games (errors otherwise?)
dsiAutoLoad.CRC16 = SPI_Firmware::CRC16((u8*)&dsiAutoLoad.PrevTitleID, dsiAutoLoad.Length, 0xFFFF);
memcpy(&NDS::MainRAM[0x300], &dsiAutoLoad, sizeof(dsiAutoLoad));
}
}
}

View File

@ -1,29 +0,0 @@
#ifndef BIZFILEMANAGER_H
#define BIZFILEMANAGER_H
namespace FileManager
{
struct FirmwareSettings
{
bool OverrideSettings;
int UsernameLength;
char16_t Username[10];
int Language;
int BirthdayMonth;
int BirthdayDay;
int Color;
int MessageLength;
char16_t Message[26];
};
const char* InitNDSBIOS();
const char* InitFirmware(FirmwareSettings& fwSettings);
const char* InitDSiBIOS();
const char* InitNAND(FirmwareSettings& fwSettings, bool clearNand, bool dsiWare);
const char* InitCarts(bool gba);
void SetupDirectBoot();
}
#endif

View File

@ -1,3 +1,4 @@
#include "NDS.h"
#include "GPU.h" #include "GPU.h"
#include "OpenGLSupport.h" #include "OpenGLSupport.h"
#include "frontend/FrontendUtil.h" #include "frontend/FrontendUtil.h"
@ -58,11 +59,12 @@ out vec4 oColor;
void main() void main()
{ {
vec4 pixel = texture(ScreenTex, fTexcoord); vec4 pixel = texture(ScreenTex, fTexcoord);
oColor = vec4(pixel.bgr, 1.0); oColor = vec4(pixel.bgr, 1.0);
} }
)"; )";
ECL_INVISIBLE static GLuint ScreenShaderProgram[3]; ECL_INVISIBLE static GLuint ScreenShaderProgram;
ECL_INVISIBLE static GLuint ScreenShaderTransformULoc, ScreenShaderSizeULoc; ECL_INVISIBLE static GLuint ScreenShaderTransformULoc, ScreenShaderSizeULoc;
ECL_INVISIBLE static GLuint VertexBuffer, VertexArray; ECL_INVISIBLE static GLuint VertexBuffer, VertexArray;
@ -81,20 +83,18 @@ ECL_INVISIBLE static GLuint OutputPboID;
void Init(u32 scale) void Init(u32 scale)
{ {
OpenGL::BuildShaderProgram(ScreenVS, ScreenFS, ScreenShaderProgram, "GLPresenterShader"); Frontend::OpenGL::CompileVertexFragmentProgram(
ScreenShaderProgram,
ScreenVS, ScreenFS,
"GLPresenterShader",
{{"vPosition", 0}, {"vTexcoord", 1}},
{{"oColor", 0}});
GLuint pid = ScreenShaderProgram[2]; glUseProgram(ScreenShaderProgram);
glBindAttribLocation(pid, 0, "vPosition"); glUniform1i(glGetUniformLocation(ScreenShaderProgram, "ScreenTex"), 0);
glBindAttribLocation(pid, 1, "vTexcoord");
glBindFragDataLocation(pid, 0, "oColor");
OpenGL::LinkShaderProgram(ScreenShaderProgram); ScreenShaderSizeULoc = glGetUniformLocation(ScreenShaderProgram, "uScreenSize");
ScreenShaderTransformULoc = glGetUniformLocation(ScreenShaderProgram, "uScreenTransform");
glUseProgram(pid);
glUniform1i(glGetUniformLocation(pid, "ScreenTex"), 0);
ScreenShaderSizeULoc = glGetUniformLocation(pid, "uScreenSize");
ScreenShaderTransformULoc = glGetUniformLocation(pid, "uScreenTransform");
constexpr int paddedHeight = NDS_HEIGHT + 2; constexpr int paddedHeight = NDS_HEIGHT + 2;
constexpr float padPixels = 1.f / paddedHeight; constexpr float padPixels = 1.f / paddedHeight;
@ -143,7 +143,7 @@ void Init(u32 scale)
GLScale = scale; GLScale = scale;
} }
std::pair<u32, u32> Present() std::pair<u32, u32> Present(melonDS::GPU& gpu)
{ {
glBindFramebuffer(GL_FRAMEBUFFER, OutputFboID); glBindFramebuffer(GL_FRAMEBUFFER, OutputFboID);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
@ -155,20 +155,21 @@ std::pair<u32, u32> Present()
glViewport(0, 0, Width, Height); glViewport(0, 0, Width, Height);
glUseProgram(ScreenShaderProgram[2]); glUseProgram(ScreenShaderProgram);
glUniform2f(ScreenShaderSizeULoc, Width, Height); glUniform2f(ScreenShaderSizeULoc, Width, Height);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
if (GPU3D::CurrentRenderer->Accelerated) auto& renderer3d = gpu.GetRenderer3D();
if (renderer3d.Accelerated)
{ {
GPU::CurGLCompositor->BindOutputTexture(GPU::FrontBuffer); renderer3d.BindOutputTexture(gpu.FrontBuffer);
} }
else else
{ {
glBindTexture(GL_TEXTURE_2D, InputTextureID); glBindTexture(GL_TEXTURE_2D, InputTextureID);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, NDS_WIDTH, NDS_HEIGHT / 2, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, GPU::Framebuffer[GPU::FrontBuffer][0]); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, NDS_WIDTH, NDS_HEIGHT / 2, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, gpu.Framebuffer[gpu.FrontBuffer][0].get());
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, NDS_HEIGHT / 2 + 2, NDS_WIDTH, NDS_HEIGHT / 2, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, GPU::Framebuffer[GPU::FrontBuffer][1]); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, NDS_HEIGHT / 2 + 2, NDS_WIDTH, NDS_HEIGHT / 2, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, gpu.Framebuffer[gpu.FrontBuffer][1].get());
} }
glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer);
@ -182,15 +183,10 @@ std::pair<u32, u32> Present()
glFlush(); glFlush();
GLint oldPbo;
glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &oldPbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, OutputPboID); glBindBuffer(GL_PIXEL_PACK_BUFFER, OutputPboID);
glBufferData(GL_PIXEL_PACK_BUFFER, Width * Height * sizeof(u32), nullptr, GL_STREAM_READ); glBufferData(GL_PIXEL_PACK_BUFFER, Width * Height * sizeof(u32), nullptr, GL_STREAM_READ);
glReadPixels(0, 0, Width, Height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (void*)(0)); glReadPixels(0, 0, Width, Height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (void*)(0));
glBindBuffer(GL_PIXEL_PACK_BUFFER, oldPbo);
return std::make_pair(Width, Height); return std::make_pair(Width, Height);
} }
@ -201,9 +197,6 @@ ECL_EXPORT u32 GetGLTexture()
ECL_EXPORT void ReadFrameBuffer(u32* buffer) ECL_EXPORT void ReadFrameBuffer(u32* buffer)
{ {
GLint oldPbo;
glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &oldPbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, OutputPboID); glBindBuffer(GL_PIXEL_PACK_BUFFER, OutputPboID);
const auto p = static_cast<const u32*>(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY)); const auto p = static_cast<const u32*>(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY));
if (p) if (p)
@ -219,8 +212,6 @@ ECL_EXPORT void ReadFrameBuffer(u32* buffer)
glUnmapBuffer(GL_PIXEL_PACK_BUFFER); glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
} }
glBindBuffer(GL_PIXEL_PACK_BUFFER, oldPbo);
} }
struct ScreenSettings struct ScreenSettings
@ -268,7 +259,7 @@ static std::pair<u32, u32> GetScreenSize(const ScreenSettings* screenSettings, u
} }
} }
ECL_EXPORT void SetScreenSettings(const ScreenSettings* screenSettings, u32* width, u32* height, u32* vwidth, u32* vheight) ECL_EXPORT void SetScreenSettings(melonDS::NDS* nds, const ScreenSettings* screenSettings, u32* width, u32* height, u32* vwidth, u32* vheight)
{ {
auto [w, h] = GetScreenSize(screenSettings, GLScale); auto [w, h] = GetScreenSize(screenSettings, GLScale);
if (w != Width || h != Height) if (w != Width || h != Height)
@ -304,7 +295,7 @@ ECL_EXPORT void SetScreenSettings(const ScreenSettings* screenSettings, u32* wid
NumScreens = Frontend::GetScreenTransforms(ScreenMatrix, ScreenKinds); NumScreens = Frontend::GetScreenTransforms(ScreenMatrix, ScreenKinds);
Present(); Present(nds->GPU);
*width = w; *width = w;
*height = h; *height = h;

View File

@ -1,13 +1,14 @@
#ifndef BIZGLPRESENTER_H #ifndef BIZGLPRESENTER_H
#define BIZGLPRESENTER_H #define BIZGLPRESENTER_H
#include "types.h" #include "GPU.h"
#include "BizTypes.h"
namespace GLPresenter namespace GLPresenter
{ {
void Init(u32 scale); void Init(u32 scale);
std::pair<u32, u32> Present(); std::pair<u32, u32> Present(melonDS::GPU& gpu);
} }

View File

@ -5,158 +5,52 @@
#include "GBACart.h" #include "GBACart.h"
#include "frontend/mic_blow.h" #include "frontend/mic_blow.h"
#include "BizPlatform/BizConfig.h"
#include "BizPlatform/BizFile.h"
#include "BizPlatform/BizLog.h"
#include "BizPlatform/BizOGL.h" #include "BizPlatform/BizOGL.h"
#include "BizFileManager.h"
#include "BizGLPresenter.h" #include "BizGLPresenter.h"
#include <emulibc.h> #include <emulibc.h>
#include <waterboxcore.h> #include <waterboxcore.h>
static bool SkipFW;
static bool GLPresentation; static bool GLPresentation;
struct NDSTime ECL_EXPORT const char* InitGL(BizOGL::LoadGLProc loadGLProc, int threeDeeRenderer, int scaleFactor, bool isWinApi)
{ {
int Year; // 0-99 switch (threeDeeRenderer)
int Month; // 1-12
int Day; // 1-(28/29/30/31 depending on month/year)
int Hour; // 0-23
int Minute; // 0-59
int Second; // 0-59
};
struct InitConfig
{
bool SkipFW;
bool HasGBACart;
bool DSi;
bool ClearNAND;
bool LoadDSiWare;
bool IsWinApi;
int ThreeDeeRenderer;
GPU::RenderSettings RenderSettings;
NDSTime StartTime;
FileManager::FirmwareSettings FirmwareSettings;
};
ECL_EXPORT const char* Init(InitConfig* initConfig,
Platform::ConfigCallbackInterface* configCallbackInterface,
Platform::FileCallbackInterface* fileCallbackInterface,
Platform::LogCallback_t logCallback,
BizOGL::LoadGLProc loadGLProc)
{
Platform::SetConfigCallbacks(*configCallbackInterface);
Platform::SetFileCallbacks(*fileCallbackInterface);
Platform::SetLogCallback(logCallback);
SkipFW = initConfig->SkipFW;
NDS::SetConsoleType(initConfig->DSi);
if (const char* error = FileManager::InitNDSBIOS())
{ {
return error; case 0:
BizOGL::LoadGL(loadGLProc, BizOGL::LoadGLVersion::V3_1, isWinApi);
break;
case 1:
BizOGL::LoadGL(loadGLProc, BizOGL::LoadGLVersion::V3_2, isWinApi);
break;
case 2:
BizOGL::LoadGL(loadGLProc, BizOGL::LoadGLVersion::V4_3, isWinApi);
break;
default:
return "Unknown 3DRenderer!";
} }
if (const char* error = FileManager::InitFirmware(initConfig->FirmwareSettings)) GLPresenter::Init(threeDeeRenderer ? scaleFactor : 1);
{ GLPresentation = true;
return error;
}
if (initConfig->DSi)
{
if (const char* error = FileManager::InitDSiBIOS())
{
return error;
}
if (const char* error = FileManager::InitNAND(initConfig->FirmwareSettings, initConfig->ClearNAND, initConfig->LoadDSiWare))
{
return error;
}
}
if (!NDS::Init())
{
return "Failed to init core!";
}
RTC::SetDateTime(initConfig->StartTime.Year, initConfig->StartTime.Month, initConfig->StartTime.Day,
initConfig->StartTime.Hour, initConfig->StartTime.Minute, initConfig->StartTime.Second);
if (loadGLProc)
{
switch (initConfig->ThreeDeeRenderer)
{
case 0:
BizOGL::LoadGL(loadGLProc, BizOGL::LoadGLVersion::V3_1, initConfig->IsWinApi);
break;
case 1:
BizOGL::LoadGL(loadGLProc, BizOGL::LoadGLVersion::V3_2, initConfig->IsWinApi);
break;
#if false // OpenGL Compute Renderer isn't released yet
case 2:
BizOGL::LoadGL(loadGLProc, BizOGL::LoadGLVersion::V4_3, initConfig->IsWinApi);
break;
#endif
default:
return "Unknown 3DRenderer!";
}
GLPresenter::Init(initConfig->ThreeDeeRenderer ? initConfig->RenderSettings.GL_ScaleFactor : 1);
GLPresentation = true;
}
else
{
GLPresentation = false;
}
GPU::InitRenderer(initConfig->ThreeDeeRenderer);
GPU::SetRenderSettings(initConfig->ThreeDeeRenderer, initConfig->RenderSettings);
NDS::Reset();
if (!initConfig->LoadDSiWare)
{
if (const char* error = FileManager::InitCarts(initConfig->HasGBACart))
{
return error;
}
}
if (SkipFW || NDS::NeedsDirectBoot())
{
FileManager::SetupDirectBoot();
}
NDS::Start();
return nullptr; return nullptr;
} }
struct MyFrameInfo : public FrameInfo struct MyFrameInfo : public FrameInfo
{ {
melonDS::NDS* NDS;
u32 Keys; u32 Keys;
u8 TouchX; u8 TouchX;
u8 TouchY; u8 TouchY;
s8 MicVolume; u8 MicVolume;
s8 GBALightSensor; u8 GBALightSensor;
bool ConsiderAltLag; bool ConsiderAltLag;
}; };
static s16 biz_mic_input[735]; static s16 biz_mic_input[735];
static bool ValidRange(s8 sensor)
{
return sensor >= 0 && sensor <= 10;
}
static int sampPos = 0; static int sampPos = 0;
static void MicFeedNoise(s8 vol) static void MicFeedNoise(u8 vol)
{ {
int sampLen = sizeof(mic_blow) / sizeof(*mic_blow); int sampLen = sizeof(mic_blow) / sizeof(*mic_blow);
@ -173,105 +67,102 @@ ECL_EXPORT void FrameAdvance(MyFrameInfo* f)
{ {
RunningFrame = true; RunningFrame = true;
if (f->Keys & 0x8000) f->NDS->SetKeyMask(~f->Keys & 0xFFF);
{
NDS::Reset();
if (SkipFW || NDS::NeedsDirectBoot())
{
FileManager::SetupDirectBoot();
}
NDS::Start();
}
NDS::SetKeyMask(~f->Keys & 0xFFF);
if (f->Keys & 0x1000) if (f->Keys & 0x1000)
{ {
// move touch coords incrementally to our new touch point // move touch coords incrementally to our new touch point
NDS::MoveTouch(f->TouchX, f->TouchY); f->NDS->MoveTouch(f->TouchX, f->TouchY);
} }
else else
{ {
NDS::ReleaseScreen(); f->NDS->ReleaseScreen();
} }
if (f->Keys & 0x2000) if (f->Keys & 0x2000)
{ {
NDS::SetLidClosed(false); f->NDS->SetLidClosed(false);
} }
else if (f->Keys & 0x4000) else if (f->Keys & 0x4000)
{ {
NDS::SetLidClosed(true); f->NDS->SetLidClosed(true);
} }
MicFeedNoise(f->MicVolume); MicFeedNoise(f->MicVolume);
NDS::MicInputFrame(biz_mic_input, 735); f->NDS->MicInputFrame(biz_mic_input, 735);
int sensor = GBACart::SetInput(0, 1); if (auto* gbaCart = f->NDS->GetGBACart())
if (sensor != -1 && ValidRange(f->GBALightSensor))
{ {
if (sensor > f->GBALightSensor) int sensor = gbaCart->SetInput(melonDS::GBACart::Input_SolarSensorDown, 1);
if (sensor != -1)
{ {
while (GBACart::SetInput(0, 1) != f->GBALightSensor); if (f->GBALightSensor > 10) f->GBALightSensor = 10;
}
else if (sensor < f->GBALightSensor) if (sensor > f->GBALightSensor)
{ {
while (GBACart::SetInput(1, 1) != f->GBALightSensor); while (gbaCart->SetInput(melonDS::GBACart::Input_SolarSensorDown, 1) != f->GBALightSensor);
}
else if (sensor < f->GBALightSensor)
{
while (gbaCart->SetInput(melonDS::GBACart::Input_SolarSensorUp, 1) != f->GBALightSensor);
}
} }
} }
NDS::RunFrame(); f->NDS->RunFrame();
if (f->Keys & 0x1000) if (f->Keys & 0x1000)
{ {
// finalize touch after emulation finishes // finalize touch after emulation finishes
NDS::TouchScreen(f->TouchX, f->TouchY); f->NDS->TouchScreen(f->TouchX, f->TouchY);
} }
if (!GPU3D::CurrentRenderer->Accelerated) auto& renderer3d = f->NDS->GetRenderer3D();
if (!renderer3d.Accelerated)
{ {
auto softRenderer = reinterpret_cast<GPU3D::SoftRenderer*>(GPU3D::CurrentRenderer.get()); auto& softRenderer = static_cast<melonDS::SoftRenderer&>(renderer3d);
softRenderer->StopRenderThread(); softRenderer.StopRenderThread();
} }
if (GLPresentation) if (GLPresentation)
{ {
std::tie(f->Width, f->Height) = GLPresenter::Present(); std::tie(f->Width, f->Height) = GLPresenter::Present(f->NDS->GPU);
} }
else else
{ {
constexpr u32 SingleScreenSize = 256 * 192; constexpr u32 SingleScreenSize = 256 * 192;
memcpy(f->VideoBuffer, GPU::Framebuffer[GPU::FrontBuffer][0], SingleScreenSize * sizeof(u32));
memcpy(f->VideoBuffer + SingleScreenSize, GPU::Framebuffer[GPU::FrontBuffer][1], SingleScreenSize * sizeof(u32)); auto& gpu = f->NDS->GPU;
memcpy(f->VideoBuffer, gpu.Framebuffer[gpu.FrontBuffer][0].get(), SingleScreenSize * sizeof(u32));
memcpy(f->VideoBuffer + SingleScreenSize, gpu.Framebuffer[gpu.FrontBuffer][1].get(), SingleScreenSize * sizeof(u32));
f->Width = 256; f->Width = 256;
f->Height = 384; f->Height = 384;
} }
f->Samples = SPU::ReadOutput(f->SoundBuffer); f->Samples = f->NDS->SPU.ReadOutput(f->SoundBuffer);
if (f->Samples == 0) // hack when core decides to stop outputting audio altogether (lid closed or power off) if (f->Samples == 0) // hack when core decides to stop outputting audio altogether (lid closed or power off)
{ {
memset(f->SoundBuffer, 0, 737 * 2 * sizeof(u16)); memset(f->SoundBuffer, 0, 737 * 2 * sizeof(u16));
f->Samples = 737; f->Samples = 737;
} }
f->Cycles = NDS::GetSysClockCycles(2); f->Cycles = f->NDS->GetSysClockCycles(2);
// if we want to consider other lag sources, use that lag flag if we haven't unlagged already // if we want to consider other lag sources, use that lag flag if we haven't unlagged already
if (f->ConsiderAltLag && NDS::LagFrameFlag) if (f->ConsiderAltLag && f->NDS->LagFrameFlag)
{ {
f->Lagged = NDS::AltLagFrameFlag; f->Lagged = f->NDS->AltLagFrameFlag;
} }
else else
{ {
f->Lagged = NDS::LagFrameFlag; f->Lagged = f->NDS->LagFrameFlag;
} }
RunningFrame = false; RunningFrame = false;
} }
ECL_EXPORT u32 GetCallbackCycleOffset() ECL_EXPORT u32 GetCallbackCycleOffset(melonDS::NDS* nds)
{ {
return RunningFrame ? NDS::GetSysClockCycles(2) : 0; return RunningFrame ? nds->GetSysClockCycles(2) : 0;
} }

View File

@ -1,45 +0,0 @@
#include "BizConfig.h"
#include <emulibc.h>
namespace Platform
{
struct ConfigCallbackInterface
{
bool (*GetBoolean)(ConfigEntry entry);
int (*GetInteger)(ConfigEntry entry);
void (*GetString)(ConfigEntry entry, char* buffer, int bufferSize);
bool (*GetArray)(ConfigEntry entry, void* buffer);
};
ECL_INVISIBLE static ConfigCallbackInterface ConfigCallbacks;
void SetConfigCallbacks(ConfigCallbackInterface& configCallbackInterface)
{
ConfigCallbacks = configCallbackInterface;
}
bool GetConfigBool(ConfigEntry entry)
{
return ConfigCallbacks.GetBoolean(entry);
}
int GetConfigInt(ConfigEntry entry)
{
return ConfigCallbacks.GetInteger(entry);
}
std::string GetConfigString(ConfigEntry entry)
{
char buffer[4096]{};
ConfigCallbacks.GetString(entry, buffer, sizeof(buffer));
return buffer;
}
bool GetConfigArray(ConfigEntry entry, void* data)
{
return ConfigCallbacks.GetArray(entry, data);
}
}

View File

@ -1,14 +0,0 @@
#ifndef BIZCONFIG_H
#define BIZCONFIG_H
#include "Platform.h"
namespace Platform
{
struct ConfigCallbackInterface;
void SetConfigCallbacks(ConfigCallbackInterface& configCallbackInterface);
}
#endif

View File

@ -4,33 +4,35 @@
#include <stdarg.h> #include <stdarg.h>
namespace Platform namespace melonDS::Platform
{ {
struct FileCallbackInterface
{
int (*GetLength)(const char* path);
void (*GetData)(const char* path, u8* buffer);
};
ECL_INVISIBLE static FileCallbackInterface FileCallbacks;
void SetFileCallbacks(FileCallbackInterface& fileCallbackInterface)
{
FileCallbacks = fileCallbackInterface;
}
struct FileHandle struct FileHandle
{ {
public: public:
FileHandle(std::shared_ptr<u8[]> data_, size_t size_, FileMode mode_) virtual ~FileHandle() = default;
: data(data_) virtual bool IsEndOfFile() = 0;
virtual bool ReadLine(char* str, int count) = 0;
virtual bool Seek(s64 offset, FileSeekOrigin origin) = 0;
virtual void Rewind() = 0;
virtual size_t Read(void* data, u64 count) = 0;
virtual bool Flush() = 0;
virtual size_t Write(const void* data, u64 count) = 0;
virtual size_t Length() = 0;
};
struct MemoryFile final : FileHandle
{
public:
MemoryFile(std::unique_ptr<u8[]> data_, size_t size_)
: data(std::move(data_))
, pos(0) , pos(0)
, size(size_) , size(size_)
, mode(mode_)
{ {
} }
~MemoryFile() = default;
bool IsEndOfFile() bool IsEndOfFile()
{ {
return pos == size; return pos == size;
@ -38,11 +40,6 @@ public:
bool ReadLine(char* str, int count) bool ReadLine(char* str, int count)
{ {
if (!Readable())
{
return false;
}
if (count < 1) if (count < 1)
{ {
return false; return false;
@ -91,24 +88,19 @@ public:
size_t Read(void* data_, u64 count) size_t Read(void* data_, u64 count)
{ {
if (!Readable())
{
return 0;
}
count = std::min(count, (u64)(size - pos)); count = std::min(count, (u64)(size - pos));
memcpy(data_, &data[pos], count); memcpy(data_, &data[pos], count);
pos += count; pos += count;
return count; return count;
} }
bool Flush()
{
return true;
}
size_t Write(const void* data_, u64 count) size_t Write(const void* data_, u64 count)
{ {
if (!Writable())
{
return 0;
}
count = std::min(count, (u64)(size - pos)); count = std::min(count, (u64)(size - pos));
memcpy(&data[pos], data_, count); memcpy(&data[pos], data_, count);
pos += count; pos += count;
@ -121,46 +113,115 @@ public:
} }
private: private:
std::shared_ptr<u8[]> data; std::unique_ptr<u8[]> data;
size_t pos, size; size_t pos, size;
FileMode mode;
bool Readable()
{
return (mode & FileMode::Read) == FileMode::Read;
}
bool Writable()
{
return (mode & FileMode::Write) == FileMode::Write;
}
}; };
static std::unordered_map<std::string, std::pair<std::shared_ptr<u8[]>, size_t>> FileBufferCache; // private memory file creation API
FileHandle* CreateMemoryFile(u8* fileData, u32 fileLength)
{
std::unique_ptr<u8[]> data(new u8[fileLength]);
memcpy(data.get(), fileData, fileLength);
return new MemoryFile(std::move(data), fileLength);
}
struct CFile final : FileHandle
{
public:
CFile(FILE* file_)
: file(file_)
{
}
~CFile()
{
fclose(file);
}
bool IsEndOfFile()
{
return feof(file) != 0;
}
bool ReadLine(char* str, int count)
{
return fgets(str, count, file) != nullptr;
}
bool Seek(s64 offset, FileSeekOrigin origin)
{
int forigin;
switch (origin)
{
case FileSeekOrigin::Start:
forigin = SEEK_SET;
break;
case FileSeekOrigin::Current:
forigin = SEEK_CUR;
break;
case FileSeekOrigin::End:
forigin = SEEK_END;
break;
default:
return false;
}
return fseek(file, offset, forigin) == 0;
}
void Rewind()
{
rewind(file);
}
size_t Read(void* data, u64 count)
{
return fread(data, 1, count, file);
}
bool Flush()
{
return fflush(file) == 0;
}
size_t Write(const void* data, u64 count)
{
return fwrite(data, 1, count, file);
}
size_t Length()
{
long pos = ftell(file);
fseek(file, 0, SEEK_END);
long len = ftell(file);
fseek(file, pos, SEEK_SET);
return len;
}
private:
FILE* file;
};
// public APIs open C files
FileHandle* OpenFile(const std::string& path, FileMode mode) FileHandle* OpenFile(const std::string& path, FileMode mode)
{ {
if ((mode & FileMode::ReadWrite) == FileMode::None) const char* fmode;
if (mode & FileMode::Write)
{ {
// something went wrong here fmode = "rb+";
return nullptr; }
else
{
fmode = "rb";
} }
if (auto cache = FileBufferCache.find(path); cache != FileBufferCache.end()) FILE* f = fopen(path.c_str(), fmode);
{ if (!f)
return new FileHandle(cache->second.first, cache->second.second, mode);
}
size_t size = FileCallbacks.GetLength(path.c_str());
if (size == 0)
{ {
return nullptr; return nullptr;
} }
std::shared_ptr<u8[]> data(new u8[size]); return new CFile(f);
FileCallbacks.GetData(path.c_str(), data.get());
FileBufferCache.emplace(path, std::make_pair(data, size));
return new FileHandle(data, size, mode);
} }
FileHandle* OpenLocalFile(const std::string& path, FileMode mode) FileHandle* OpenLocalFile(const std::string& path, FileMode mode)
@ -170,12 +231,10 @@ FileHandle* OpenLocalFile(const std::string& path, FileMode mode)
bool FileExists(const std::string& name) bool FileExists(const std::string& name)
{ {
if (auto cache = FileBufferCache.find(name); cache != FileBufferCache.end()) FILE* f = fopen(name.c_str(), "rb");
{ bool exists = f != nullptr;
return true; fclose(f);
} return exists;
return FileCallbacks.GetLength(name.c_str()) > 0;
} }
bool LocalFileExists(const std::string& name) bool LocalFileExists(const std::string& name)
@ -216,7 +275,7 @@ u64 FileRead(void* data, u64 size, u64 count, FileHandle* file)
bool FileFlush(FileHandle* file) bool FileFlush(FileHandle* file)
{ {
return true; return file->Flush();
} }
u64 FileWrite(const void* data, u64 size, u64 count, FileHandle* file) u64 FileWrite(const void* data, u64 size, u64 count, FileHandle* file)
@ -224,26 +283,10 @@ u64 FileWrite(const void* data, u64 size, u64 count, FileHandle* file)
return file->Write(data, size * count); return file->Write(data, size * count);
} }
// only used for FATStorage (i.e. SD cards), not supported
u64 FileWriteFormatted(FileHandle* file, const char* fmt, ...) u64 FileWriteFormatted(FileHandle* file, const char* fmt, ...)
{ {
va_list args; return 0;
va_start(args, fmt);
size_t bufferSize = vsnprintf(nullptr, 0, fmt, args);
va_end(args);
if ((int)bufferSize < 0)
{
return 0;
}
auto buffer = std::make_unique<char[]>(bufferSize + 1);
va_start(args, fmt);
vsnprintf(buffer.get(), bufferSize + 1, fmt, args);
va_end(args);
return file->Write(buffer.get(), bufferSize);
} }
u64 FileLength(FileHandle* file) u64 FileLength(FileHandle* file)

View File

@ -3,11 +3,10 @@
#include "Platform.h" #include "Platform.h"
namespace Platform namespace melonDS::Platform
{ {
struct FileCallbackInterface; FileHandle* CreateMemoryFile(u8* fileData, u32 fileLength);
void SetFileCallbacks(FileCallbackInterface& fileCallbackInterface);
} }

View File

@ -1,15 +1,18 @@
#include "BizLog.h" #include "Platform.h"
#include <emulibc.h> #include <emulibc.h>
#include <stdarg.h> #include <stdarg.h>
namespace Platform namespace melonDS::Platform
{ {
ECL_INVISIBLE static LogCallback_t LogCallback; using LogCallback_t = void (*)(LogLevel level, const char* message);
void SetLogCallback(LogCallback_t logCallback) ECL_INVISIBLE static LogCallback_t LogCallback;
ECL_INVISIBLE static char LogBuffer[1 << 16];
ECL_EXPORT void SetLogCallback(LogCallback_t logCallback)
{ {
LogCallback = logCallback; LogCallback = logCallback;
} }
@ -17,23 +20,16 @@ void SetLogCallback(LogCallback_t logCallback)
void Log(LogLevel level, const char* fmt, ...) void Log(LogLevel level, const char* fmt, ...)
{ {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
size_t bufferSize = vsnprintf(nullptr, 0, fmt, args); int bufferSize = vsnprintf(LogBuffer, sizeof(LogBuffer), fmt, args);
va_end(args); va_end(args);
if ((int)bufferSize < 0) if (bufferSize < 0)
{ {
return; return;
} }
auto buffer = std::make_unique<char[]>(bufferSize + 1); LogCallback(level, LogBuffer);
va_start(args, fmt);
vsnprintf(buffer.get(), bufferSize + 1, fmt, args);
va_end(args);
LogCallback(level, buffer.get());
} }
} }

View File

@ -1,14 +0,0 @@
#ifndef BIZLOG_H
#define BIZLOG_H
#include "Platform.h"
namespace Platform
{
using LogCallback_t = void (*)(LogLevel level, const char* message);
void SetLogCallback(LogCallback_t logCallback);
}
#endif

View File

@ -64,6 +64,11 @@ public:
return glProc == proc; return glProc == proc;
} }
bool operator!=(const std::nullptr_t& nullptr_) const
{
return glProc != nullptr_;
}
private: private:
GLFunc glProc = nullptr; GLFunc glProc = nullptr;
}; };

View File

@ -1,6 +1,6 @@
#include "Platform.h" #include "Platform.h"
namespace Platform namespace melonDS::Platform
{ {
void Init(int argc, char** argv) void Init(int argc, char** argv)

View File

@ -5,7 +5,7 @@
#include "DSi_NAND.h" #include "DSi_NAND.h"
#include "Platform.h" #include "Platform.h"
namespace Platform namespace melonDS::Platform
{ {
constexpr u32 DSIWARE_CATEGORY = 0x00030004; constexpr u32 DSIWARE_CATEGORY = 0x00030004;
@ -13,47 +13,47 @@ constexpr u32 DSIWARE_CATEGORY = 0x00030004;
static bool NdsSaveRamIsDirty = false; static bool NdsSaveRamIsDirty = false;
static bool GbaSaveRamIsDirty = false; static bool GbaSaveRamIsDirty = false;
ECL_EXPORT void PutSaveRam(u8* data, u32 len) ECL_EXPORT void PutSaveRam(melonDS::NDS* nds, u8* data, u32 len)
{ {
const u32 ndsSaveLen = NDSCart::GetSaveMemoryLength(); const u32 ndsSaveLen = nds->GetNDSSaveLength();
const u32 gbaSaveLen = GBACart::GetSaveMemoryLength(); const u32 gbaSaveLen = nds->GetGBASaveLength();
if (len >= ndsSaveLen) if (len >= ndsSaveLen)
{ {
NDS::LoadSave(data, len); nds->SetNDSSave(data, len);
NdsSaveRamIsDirty = false; NdsSaveRamIsDirty = false;
if (gbaSaveLen && len >= (ndsSaveLen + gbaSaveLen)) if (gbaSaveLen && len >= (ndsSaveLen + gbaSaveLen))
{ {
// don't use GBACart::LoadSave! it will re-allocate the save buffer (bad!) // don't use SetGBASave! it will re-allocate the save buffer (bad!)
// NDS::LoadSave is fine (and should be used) // SetNDSSave is fine (and should be used)
memcpy(GBACart::GetSaveMemory(), data + ndsSaveLen, gbaSaveLen); memcpy(nds->GetGBASave(), data + ndsSaveLen, gbaSaveLen);
GbaSaveRamIsDirty = false; GbaSaveRamIsDirty = false;
} }
} }
} }
ECL_EXPORT void GetSaveRam(u8* data) ECL_EXPORT void GetSaveRam(melonDS::NDS* nds, u8* data)
{ {
const u32 ndsSaveLen = NDSCart::GetSaveMemoryLength(); const u32 ndsSaveLen = nds->GetNDSSaveLength();
const u32 gbaSaveLen = GBACart::GetSaveMemoryLength(); const u32 gbaSaveLen = nds->GetGBASaveLength();
if (ndsSaveLen) if (ndsSaveLen)
{ {
memcpy(data, NDSCart::GetSaveMemory(), ndsSaveLen); memcpy(data, nds->GetNDSSave(), ndsSaveLen);
NdsSaveRamIsDirty = false; NdsSaveRamIsDirty = false;
} }
if (gbaSaveLen) if (gbaSaveLen)
{ {
memcpy(data + ndsSaveLen, GBACart::GetSaveMemory(), gbaSaveLen); memcpy(data + ndsSaveLen, nds->GetGBASave(), gbaSaveLen);
GbaSaveRamIsDirty = false; GbaSaveRamIsDirty = false;
} }
} }
ECL_EXPORT u32 GetSaveRamLength() ECL_EXPORT u32 GetSaveRamLength(melonDS::NDS* nds)
{ {
return NDSCart::GetSaveMemoryLength() + GBACart::GetSaveMemoryLength(); return nds->GetNDSSaveLength() + nds->GetGBASaveLength();
} }
ECL_EXPORT bool SaveRamIsDirty() ECL_EXPORT bool SaveRamIsDirty()
@ -61,73 +61,42 @@ ECL_EXPORT bool SaveRamIsDirty()
return NdsSaveRamIsDirty || GbaSaveRamIsDirty; return NdsSaveRamIsDirty || GbaSaveRamIsDirty;
} }
static void ImportTitleData(DSi_NAND::NANDMount& mount, u32 titleId, int which, const char* path, u8** in) ECL_EXPORT void ImportDSiWareSavs(melonDS::DSi* dsi, u32 titleId)
{ {
if (auto file = Platform::OpenFile(path, Platform::FileMode::Write)) if (auto& nand = dsi->GetNAND())
{ {
auto len = Platform::FileLength(file); if (auto mount = melonDS::DSi_NAND::NANDMount(nand))
Platform::FileRewind(file);
Platform::FileWrite(*in, len, 1, file);
Platform::CloseFile(file);
*in += len;
}
mount.ImportTitleData(DSIWARE_CATEGORY, titleId, which, path);
}
ECL_EXPORT void ImportDSiWareSavs(u32 titleId, u8* data)
{
auto& nand = DSi::NANDImage;
if (nand && *nand)
{
if (auto mount = DSi_NAND::NANDMount(*nand))
{ {
ImportTitleData(mount, titleId, DSi_NAND::TitleData_PublicSav, "public.sav", &data); mount.ImportTitleData(DSIWARE_CATEGORY, titleId, melonDS::DSi_NAND::TitleData_PublicSav, "public.sav");
ImportTitleData(mount, titleId, DSi_NAND::TitleData_PrivateSav, "private.sav", &data); mount.ImportTitleData(DSIWARE_CATEGORY, titleId, melonDS::DSi_NAND::TitleData_PrivateSav, "private.sav");
ImportTitleData(mount, titleId, DSi_NAND::TitleData_BannerSav, "banner.sav", &data); mount.ImportTitleData(DSIWARE_CATEGORY, titleId, melonDS::DSi_NAND::TitleData_BannerSav, "banner.sav");
} }
} }
} }
static void ExportTitleData(DSi_NAND::NANDMount& mount, u32 titleId, int which, const char* path, u8** out) ECL_EXPORT void ExportDSiWareSavs(melonDS::DSi* dsi, u32 titleId)
{ {
mount.ExportTitleData(DSIWARE_CATEGORY, titleId, which, path); if (auto& nand = dsi->GetNAND())
if (auto file = Platform::OpenFile(path, Platform::FileMode::Read))
{ {
auto len = Platform::FileLength(file); if (auto mount = melonDS::DSi_NAND::NANDMount(nand))
Platform::FileRewind(file);
Platform::FileRead(*out, len, 1, file);
Platform::CloseFile(file);
*out += len;
}
}
ECL_EXPORT void ExportDSiWareSavs(u32 titleId, u8* data)
{
auto& nand = DSi::NANDImage;
if (nand && *nand)
{
if (auto mount = DSi_NAND::NANDMount(*nand))
{ {
ExportTitleData(mount, titleId, DSi_NAND::TitleData_PublicSav, "public.sav", &data); mount.ExportTitleData(DSIWARE_CATEGORY, titleId, melonDS::DSi_NAND::TitleData_PublicSav, "public.sav");
ExportTitleData(mount, titleId, DSi_NAND::TitleData_PrivateSav, "private.sav", &data); mount.ExportTitleData(DSIWARE_CATEGORY, titleId, melonDS::DSi_NAND::TitleData_PrivateSav, "private.sav");
ExportTitleData(mount, titleId, DSi_NAND::TitleData_BannerSav, "banner.sav", &data); mount.ExportTitleData(DSIWARE_CATEGORY, titleId, melonDS::DSi_NAND::TitleData_BannerSav, "banner.sav");
} }
} }
} }
ECL_EXPORT void DSiWareSavsLength(u32 titleId, u32* publicSavSize, u32* privateSavSize, u32* bannerSavSize) ECL_EXPORT void DSiWareSavsLength(melonDS::DSi* dsi, u32 titleId, u32* publicSavSize, u32* privateSavSize, u32* bannerSavSize)
{ {
*publicSavSize = *privateSavSize = *bannerSavSize = 0; *publicSavSize = *privateSavSize = *bannerSavSize = 0;
auto& nand = DSi::NANDImage; if (auto& nand = dsi->GetNAND())
if (nand && *nand)
{ {
if (auto mount = DSi_NAND::NANDMount(*nand)) if (auto mount = melonDS::DSi_NAND::NANDMount(nand))
{ {
u32 version; u32 version;
NDSHeader header{}; melonDS::NDSHeader header{};
mount.GetTitleInfo(DSIWARE_CATEGORY, titleId, version, &header, nullptr); mount.GetTitleInfo(DSIWARE_CATEGORY, titleId, version, &header, nullptr);
*publicSavSize = header.DSiPublicSavSize; *publicSavSize = header.DSiPublicSavSize;
@ -141,26 +110,24 @@ ECL_EXPORT void DSiWareSavsLength(u32 titleId, u32* publicSavSize, u32* privateS
// Perhaps instead it would be better to use FileFlush to write to disk // Perhaps instead it would be better to use FileFlush to write to disk
// (guarded by frontend determinism switch, of course) // (guarded by frontend determinism switch, of course)
ECL_EXPORT u32 GetNANDSize() ECL_EXPORT u32 GetNANDSize(melonDS::DSi* dsi)
{ {
auto& nand = DSi::NANDImage; if (auto& nand = dsi->GetNAND())
if (nand && *nand)
{ {
return nand->GetLength(); return nand.GetLength();
} }
return 0; return 0;
} }
ECL_EXPORT void GetNANDData(u8* buf) ECL_EXPORT void GetNANDData(melonDS::DSi* dsi, u8* buf)
{ {
auto& nand = DSi::NANDImage; if (auto& nand = dsi->GetNAND())
if (nand && *nand)
{ {
auto len = nand->GetLength(); auto len = nand.GetLength();
auto file = nand->GetFile(); auto file = nand.GetFile();
Platform::FileRewind(file); melonDS::Platform::FileRewind(file);
Platform::FileRead(buf, 1, len, file); melonDS::Platform::FileRead(buf, 1, len, file);
} }
} }
@ -174,8 +141,8 @@ void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen
GbaSaveRamIsDirty = true; GbaSaveRamIsDirty = true;
} }
void WriteFirmware(const SPI_Firmware::Firmware& firmware, u32 writeoffset, u32 writelen) void WriteFirmware(const melonDS::Firmware& firmware, u32 writeoffset, u32 writelen)
{ {
} }
void WriteDateTime(int year, int month, int day, int hour, int minute, int second) void WriteDateTime(int year, int month, int day, int hour, int minute, int second)

View File

@ -2,7 +2,7 @@
#include <emulibc.h> #include <emulibc.h>
namespace Platform namespace melonDS::Platform
{ {
static uintptr_t FrameThreadProc = 0; static uintptr_t FrameThreadProc = 0;

15
waterbox/melon/BizTypes.h Executable file
View File

@ -0,0 +1,15 @@
#ifndef BIZTYPES_H
#define BIZTYPES_H
#include "types.h"
using melonDS::u8;
using melonDS::u16;
using melonDS::u32;
using melonDS::u64;
using melonDS::s8;
using melonDS::s16;
using melonDS::s32;
using melonDS::s64;
#endif

View File

@ -1,6 +1,6 @@
CCFLAGS := -Wno-incompatible-pointer-types-discards-qualifiers -Wno-pointer-sign CCFLAGS := -Wno-incompatible-pointer-types-discards-qualifiers -Wno-pointer-sign
CXXFLAGS := -DMELONDS_VERSION="" -DOGLRENDERER_ENABLED \ CXXFLAGS := -DOGLRENDERER_ENABLED \
-I. -I./melonDS/src -I./melonDS/src/teakra/include \ -I. -I./melonDS/src -I./melonDS/src/teakra/include \
-Wall -Wextra -Werror=int-to-pointer-cast \ -Wall -Wextra -Werror=int-to-pointer-cast \
-Wfatal-errors -Wno-unused-parameter -Wno-unused-variable \ -Wfatal-errors -Wno-unused-parameter -Wno-unused-variable \
@ -8,11 +8,13 @@ CXXFLAGS := -DMELONDS_VERSION="" -DOGLRENDERER_ENABLED \
-Wno-sign-compare -Wno-deprecated-declarations \ -Wno-sign-compare -Wno-deprecated-declarations \
-Wno-missing-braces -Wno-bitwise-instead-of-logical \ -Wno-missing-braces -Wno-bitwise-instead-of-logical \
-Wno-unused-private-field -Wno-logical-op-parentheses \ -Wno-unused-private-field -Wno-logical-op-parentheses \
-Wno-mismatched-tags -Wno-reorder-ctor \
-fno-strict-aliasing -fwrapv -std=c++17 -fno-strict-aliasing -fwrapv -std=c++17
TARGET = melonDS.wbx TARGET = melonDS.wbx
CORE_SRCS = \ CORE_SRCS = \
ARCodeFile.cpp \
AREngine.cpp \ AREngine.cpp \
ARM.cpp \ ARM.cpp \
ARMInterpreter.cpp \ ARMInterpreter.cpp \
@ -42,20 +44,25 @@ CORE_SRCS = \
GPU2D_Soft.cpp \ GPU2D_Soft.cpp \
GPU3D.cpp \ GPU3D.cpp \
GPU3D_Soft.cpp \ GPU3D_Soft.cpp \
GPU3D_Texcache.cpp \
NDS.cpp \ NDS.cpp \
NDSCart.cpp \ NDSCart.cpp \
NDSCartR4.cpp \
ROMList.cpp \ ROMList.cpp \
RTC.cpp \ RTC.cpp \
Savestate.cpp \ Savestate.cpp \
SPI.cpp \ SPI.cpp \
SPI_Firmware.cpp \ SPI_Firmware.cpp \
SPU.cpp \ SPU.cpp \
Utils.cpp \
Wifi.cpp \ Wifi.cpp \
WifiAP.cpp WifiAP.cpp
CORE_GL_SRCS = \ CORE_GL_SRCS = \
GPU_OpenGL.cpp \ GPU_OpenGL.cpp \
GPU3D_OpenGL.cpp \ GPU3D_OpenGL.cpp \
GPU3D_Compute.cpp \
GPU3D_TexcacheOpenGL.cpp \
OpenGLSupport.cpp \ OpenGLSupport.cpp \
frontend/Util_Video.cpp frontend/Util_Video.cpp
@ -82,7 +89,6 @@ MISC_SRCS = \
blip_buf/blip_buf.c \ blip_buf/blip_buf.c \
BIZPLATFORM_SRCS = \ BIZPLATFORM_SRCS = \
BizPlatform/BizConfig.cpp \
BizPlatform/BizFile.cpp \ BizPlatform/BizFile.cpp \
BizPlatform/BizLog.cpp \ BizPlatform/BizLog.cpp \
BizPlatform/BizOGL.cpp \ BizPlatform/BizOGL.cpp \
@ -97,8 +103,8 @@ SRCS = \
$(addprefix melonDS/src/fatfs/,$(FATFS_SRCS)) \ $(addprefix melonDS/src/fatfs/,$(FATFS_SRCS)) \
$(addprefix melonDS/src/,$(MISC_SRCS)) \ $(addprefix melonDS/src/,$(MISC_SRCS)) \
$(BIZPLATFORM_SRCS) \ $(BIZPLATFORM_SRCS) \
BizConsoleCreator.cpp \
BizDebugging.cpp \ BizDebugging.cpp \
BizFileManager.cpp \
BizGLPresenter.cpp \ BizGLPresenter.cpp \
BizInterface.cpp \ BizInterface.cpp \
dthumb.c dthumb.c

@ -1 +1 @@
Subproject commit 3c9510523bfbdf7d39e5185dcfbc72146bb8b52f Subproject commit c6df63393872fc0198d7e4b30899855d1321f94a

6
waterbox/melon/version.h Executable file
View File

@ -0,0 +1,6 @@
#ifndef VERSION_H
#define VERSION_H
#define MELONDS_VERSION_BASE "BizHawk"
#endif