Update melonDS
This commit is contained in:
parent
75502e7ffb
commit
b7265e2f39
Binary file not shown.
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)) :
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
#ifndef BIZCONFIG_H
|
|
||||||
#define BIZCONFIG_H
|
|
||||||
|
|
||||||
#include "Platform.h"
|
|
||||||
|
|
||||||
namespace Platform
|
|
||||||
{
|
|
||||||
|
|
||||||
struct ConfigCallbackInterface;
|
|
||||||
void SetConfigCallbacks(ConfigCallbackInterface& configCallbackInterface);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef VERSION_H
|
||||||
|
#define VERSION_H
|
||||||
|
|
||||||
|
#define MELONDS_VERSION_BASE "BizHawk"
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue