[melonDS] Redo porting, waterbox style now (#2945)
Co-authored-by: nattthebear <goyuken@gmail.com>
This commit is contained in:
parent
f14b057d94
commit
41128abc37
|
@ -15,10 +15,10 @@
|
|||
path = submodules/mgba
|
||||
url = https://github.com/TASEmulators/mgba.git
|
||||
branch = bizhawk-0.9
|
||||
[submodule "melonds"]
|
||||
path = melonds
|
||||
[submodule "waterbox/melon/melonDS"]
|
||||
path = waterbox/melon/melonDS
|
||||
url = https://github.com/TASEmulators/melonDS.git
|
||||
branch = SuuperW_DSHawk
|
||||
branch = melonbiz
|
||||
[submodule "waterbox/musl"]
|
||||
path = waterbox/musl
|
||||
url = https://github.com/nattthebear/musl.git
|
||||
|
@ -37,3 +37,6 @@
|
|||
[submodule "submodules/libdarm"]
|
||||
path = submodules/libdarm
|
||||
url = https://github.com/jbremer/darm.git
|
||||
[submodule "submodules/libfwunpack"]
|
||||
path = submodules/libfwunpack
|
||||
url = https://github.com/TASEmulators/fwunpack
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
-- Gives a cross hair UI for the stylus for DS games
|
||||
|
||||
local upColor = 'white'
|
||||
local downColor = 'green'
|
||||
local dotColor = 'red'
|
||||
local upColor = 'lime'
|
||||
local downColor = 'red'
|
||||
local dotColor = 'blue'
|
||||
|
||||
client.setwindowsize(client.getwindowsize()) -- assert a sane resolution
|
||||
|
||||
invert = false
|
||||
-- FIXME: No idea how to get lua to detect inverted screens, set this to true if inverted screens are active
|
||||
-- (typing "invert = true" without the quotes in the console works)
|
||||
|
||||
function Draw(x, y, maxX, maxY, isDown)
|
||||
color = upColor
|
||||
|
@ -10,11 +16,24 @@ function Draw(x, y, maxX, maxY, isDown)
|
|||
color = downColor
|
||||
end
|
||||
|
||||
gui.drawLine(0, y - 1, maxX, y - 1, color)
|
||||
gui.drawLine(0, y, maxX, y, color)
|
||||
gui.drawLine(0, y + 1, maxX, y + 1, color)
|
||||
|
||||
gui.drawLine(x - 1, 0, x - 1, maxY, color)
|
||||
gui.drawLine(x, 0, x, maxY, color)
|
||||
gui.drawLine(x + 1, 0, x + 1, maxY, color)
|
||||
|
||||
if isDown then
|
||||
gui.drawPixel(x - 1, y - 1, dotColor)
|
||||
gui.drawPixel(x, y - 1, dotColor)
|
||||
gui.drawPixel(x + 1, y - 1, dotColor)
|
||||
gui.drawPixel(x - 1, y, dotColor)
|
||||
gui.drawPixel(x, y, dotColor)
|
||||
gui.drawPixel(x + 1, y, dotColor)
|
||||
gui.drawPixel(x - 1, y + 1, dotColor)
|
||||
gui.drawPixel(x, y + 1, dotColor)
|
||||
gui.drawPixel(x + 1, y + 1, dotColor)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -30,21 +49,33 @@ while true do
|
|||
btns = movie.getinput(emu.framecount() - 1)
|
||||
end
|
||||
|
||||
local x = btns['TouchX']
|
||||
local y = btns['TouchY']
|
||||
local isDown = btns['Touch']
|
||||
local wsz = client.getwindowsize()
|
||||
local w = client.screenwidth() / wsz
|
||||
local xo = 0
|
||||
local yo = 0
|
||||
|
||||
-- A bit of a hack to ensure it is not drawing while mouse moving
|
||||
-- on the top screen
|
||||
if y == 0 then
|
||||
x = 0
|
||||
if w == 512 then -- horizontal
|
||||
if invert then
|
||||
xo = xo - 256
|
||||
else
|
||||
error("Non-inverted horizontal screens are unsupported")
|
||||
end
|
||||
else -- vertical
|
||||
if invert then
|
||||
yo = yo - 192
|
||||
else
|
||||
-- don't need to do anything here
|
||||
end
|
||||
end
|
||||
|
||||
local x = btns['Touch X'] + xo
|
||||
local y = btns['Touch Y'] + yo
|
||||
local isDown = btns['Touch']
|
||||
|
||||
pts = client.transformPoint(x, y)
|
||||
local tx = pts["x"];
|
||||
local ty = pts["y"];
|
||||
gui.DrawNew("native")
|
||||
Draw(tx, ty, 10000, 10000, isDown)
|
||||
Draw(tx / wsz, ty / wsz, 10000, 10000, isDown)
|
||||
|
||||
emu.frameadvance()
|
||||
emu.yield()
|
||||
end
|
|
@ -1117,12 +1117,12 @@
|
|||
}
|
||||
},
|
||||
"NDS Controller": {
|
||||
"TouchX": {
|
||||
"Touch X": {
|
||||
"Value": "WMouse X",
|
||||
"Mult": 1.0,
|
||||
"Deadzone": 0.0
|
||||
},
|
||||
"TouchY": {
|
||||
"Touch Y": {
|
||||
"Value": "WMouse Y",
|
||||
"Mult": 1.0,
|
||||
"Deadzone": 0.0
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -12,6 +12,7 @@
|
|||
;#include gamedb_msx1-L.txt (legacy?)
|
||||
;#include gamedb_msx2-L.txt (legacy?)
|
||||
#include gamedb_n64.txt
|
||||
#include gamedb_nds.txt
|
||||
#include gamedb_nes.txt
|
||||
#include gamedb_Odyssey2.txt
|
||||
#include gamedb_pce_cd.txt
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -218,6 +218,7 @@ namespace BizHawk.DATTool
|
|||
GB3x,
|
||||
GB4x,
|
||||
O2,
|
||||
MSX
|
||||
MSX,
|
||||
NDS
|
||||
}
|
||||
}
|
||||
|
|
1
melonds
1
melonds
|
@ -1 +0,0 @@
|
|||
Subproject commit cbd9adac31ac12469f43efe9486cd8b4fadb011d
|
|
@ -488,7 +488,7 @@ namespace BizHawk.Client.Common
|
|||
|
||||
private BaseFilter CreateCoreScreenControl()
|
||||
{
|
||||
if (GlobalEmulator is MelonDS nds)
|
||||
if (GlobalEmulator is NDS nds)
|
||||
{
|
||||
//TODO: need to pipe layout settings into here now
|
||||
var filter = new ScreenControlNDS(nds);
|
||||
|
|
|
@ -174,7 +174,7 @@ namespace BizHawk.Client.Common.Filters
|
|||
public IGL GL;
|
||||
public IGuiRenderer GuiRenderer;
|
||||
|
||||
private readonly MelonDS nds;
|
||||
private readonly NDS nds;
|
||||
|
||||
//TODO: actually use this
|
||||
private bool nop = false;
|
||||
|
@ -195,7 +195,7 @@ namespace BizHawk.Client.Common.Filters
|
|||
return new Vector2(r.X, r.Y);
|
||||
}
|
||||
|
||||
public ScreenControlNDS(MelonDS nds)
|
||||
public ScreenControlNDS(NDS nds)
|
||||
{
|
||||
//not sure if we actually need this nds instance yet
|
||||
this.nds = nds;
|
||||
|
@ -218,27 +218,27 @@ namespace BizHawk.Client.Common.Filters
|
|||
var settings = nds.GetSettings();
|
||||
|
||||
//gap only applies to vertical, I guess
|
||||
if (settings.ScreenLayout == MelonDS.ScreenLayoutKind.Vertical)
|
||||
if (settings.ScreenLayout == NDS.ScreenLayoutKind.Vertical)
|
||||
{
|
||||
bot.Translate(0, 192);
|
||||
bot.Translate(0, settings.ScreenGap);
|
||||
}
|
||||
else if (settings.ScreenLayout == MelonDS.ScreenLayoutKind.Horizontal)
|
||||
else if (settings.ScreenLayout == NDS.ScreenLayoutKind.Horizontal)
|
||||
{
|
||||
bot.Translate(256, 0);
|
||||
}
|
||||
else if (settings.ScreenLayout == MelonDS.ScreenLayoutKind.Top)
|
||||
else if (settings.ScreenLayout == NDS.ScreenLayoutKind.Top)
|
||||
{
|
||||
//do nothing here, we'll discard bottom screen
|
||||
}
|
||||
|
||||
//this doesn't make any sense, it's likely to be too much for a monitor to gracefully handle too
|
||||
if (settings.ScreenLayout != MelonDS.ScreenLayoutKind.Horizontal)
|
||||
if (settings.ScreenLayout != NDS.ScreenLayoutKind.Horizontal)
|
||||
{
|
||||
int rot = 0;
|
||||
if (settings.ScreenRotation == MelonDS.ScreenRotationKind.Rotate90) rot = 90;
|
||||
if (settings.ScreenRotation == MelonDS.ScreenRotationKind.Rotate180) rot = 180;
|
||||
if (settings.ScreenRotation == MelonDS.ScreenRotationKind.Rotate270) rot = 270;
|
||||
if (settings.ScreenRotation == NDS.ScreenRotationKind.Rotate90) rot = 90;
|
||||
if (settings.ScreenRotation == NDS.ScreenRotationKind.Rotate180) rot = 180;
|
||||
if (settings.ScreenRotation == NDS.ScreenRotationKind.Rotate270) rot = 270;
|
||||
top.RotateZ(rot);
|
||||
bot.RotateZ(rot);
|
||||
}
|
||||
|
@ -283,14 +283,14 @@ namespace BizHawk.Client.Common.Filters
|
|||
|
||||
//the size can now be determined in a kind of fluffily magical way by transforming edges and checking the bounds
|
||||
float fxmin = 100000, fymin = 100000, fxmax = -100000, fymax = -100000;
|
||||
if (settings.ScreenLayout != MelonDS.ScreenLayoutKind.Bottom)
|
||||
if (settings.ScreenLayout != NDS.ScreenLayoutKind.Bottom)
|
||||
{
|
||||
fxmin = Math.Min(Math.Min(Math.Min(Math.Min(top_TL.X, top_TR.X), top_BL.X), top_BR.X), fxmin);
|
||||
fymin = Math.Min(Math.Min(Math.Min(Math.Min(top_TL.Y, top_TR.Y), top_BL.Y), top_BR.Y), fymin);
|
||||
fxmax = Math.Max(Math.Max(Math.Max(Math.Max(top_TL.X, top_TR.X), top_BL.X), top_BR.X), fxmax);
|
||||
fymax = Math.Max(Math.Max(Math.Max(Math.Max(top_TL.Y, top_TR.Y), top_BL.Y), top_BR.Y), fymax);
|
||||
}
|
||||
if (settings.ScreenLayout != MelonDS.ScreenLayoutKind.Top)
|
||||
if (settings.ScreenLayout != NDS.ScreenLayoutKind.Top)
|
||||
{
|
||||
fxmin = Math.Min(Math.Min(Math.Min(Math.Min(bot_TL.X, bot_TR.X), bot_BL.X), bot_BR.X), fxmin);
|
||||
fymin = Math.Min(Math.Min(Math.Min(Math.Min(bot_TL.Y, bot_TR.Y), bot_BL.Y), bot_BR.Y), fymin);
|
||||
|
@ -339,7 +339,12 @@ namespace BizHawk.Client.Common.Filters
|
|||
|
||||
public override Vector2 UntransformPoint(string channel, Vector2 point)
|
||||
{
|
||||
point = Transform(matBotInvert, point);
|
||||
var settings = nds.GetSettings();
|
||||
bool invert = settings.ScreenInvert
|
||||
&& settings.ScreenLayout != NDS.ScreenLayoutKind.Top
|
||||
&& settings.ScreenLayout != NDS.ScreenLayoutKind.Bottom;
|
||||
|
||||
point = Transform(invert ? matTopInvert : matBotInvert, point);
|
||||
|
||||
//hack to accomodate input tracking system's float-point sense (based on the core's VideoBuffer height)
|
||||
//actually, this is needed for a reason similar to the "TouchScreenStart" that I removed.
|
||||
|
@ -348,8 +353,7 @@ namespace BizHawk.Client.Common.Filters
|
|||
point.Y *= 2;
|
||||
|
||||
//in case we're in this layout, we get confused, so fix it
|
||||
var settings = nds.GetSettings();
|
||||
if (settings.ScreenLayout == MelonDS.ScreenLayoutKind.Top) point = new(0.0f, 0.0f);
|
||||
if (settings.ScreenLayout == NDS.ScreenLayoutKind.Top) point = new(0.0f, 0.0f);
|
||||
|
||||
//TODO: we probably need more subtle logic here.
|
||||
//some capability to return -1,-1 perhaps in case the cursor is nowhere.
|
||||
|
@ -385,15 +389,17 @@ namespace BizHawk.Client.Common.Filters
|
|||
bool renderTop = false;
|
||||
bool renderBottom = false;
|
||||
var settings = nds.GetSettings();
|
||||
if (settings.ScreenLayout == MelonDS.ScreenLayoutKind.Bottom) renderBottom = true;
|
||||
if (settings.ScreenLayout == MelonDS.ScreenLayoutKind.Top) renderTop = true;
|
||||
if (settings.ScreenLayout == MelonDS.ScreenLayoutKind.Vertical) renderTop = renderBottom = true;
|
||||
if (settings.ScreenLayout == MelonDS.ScreenLayoutKind.Horizontal) renderTop = renderBottom = true;
|
||||
if (settings.ScreenLayout == NDS.ScreenLayoutKind.Bottom) renderBottom = true;
|
||||
if (settings.ScreenLayout == NDS.ScreenLayoutKind.Top) renderTop = true;
|
||||
if (settings.ScreenLayout == NDS.ScreenLayoutKind.Vertical) renderTop = renderBottom = true;
|
||||
if (settings.ScreenLayout == NDS.ScreenLayoutKind.Horizontal) renderTop = renderBottom = true;
|
||||
|
||||
bool invert = settings.ScreenInvert && renderTop && renderBottom;
|
||||
|
||||
if (renderTop)
|
||||
{
|
||||
GuiRenderer.Modelview.Push();
|
||||
GuiRenderer.Modelview.PreMultiplyMatrix(matTop);
|
||||
GuiRenderer.Modelview.PreMultiplyMatrix(invert ? matBot : matTop);
|
||||
GuiRenderer.DrawSubrect(InputTexture, 0, 0, 256, 192, 0.0f, 0.0f, 1.0f, 0.5f);
|
||||
GuiRenderer.Modelview.Pop();
|
||||
}
|
||||
|
@ -401,7 +407,7 @@ namespace BizHawk.Client.Common.Filters
|
|||
if (renderBottom)
|
||||
{
|
||||
GuiRenderer.Modelview.Push();
|
||||
GuiRenderer.Modelview.PreMultiplyMatrix(matBot);
|
||||
GuiRenderer.Modelview.PreMultiplyMatrix(invert ? matTop : matBot);
|
||||
GuiRenderer.DrawSubrect(InputTexture, 0, 0, 256, 192, 0.0f, 0.5f, 1.0f, 1.0f);
|
||||
GuiRenderer.Modelview.Pop();
|
||||
}
|
||||
|
|
|
@ -208,9 +208,11 @@ namespace BizHawk.Client.Common
|
|||
|
||||
// compute its hash
|
||||
// NDS's firmware file contains user settings; these are over-written by sync settings, so we shouldn't allow them to impact the hash
|
||||
var rff = reader.Read(fr.ID == NDS_FIRMWARE
|
||||
? new FileInfo(Emulation.Cores.Consoles.Nintendo.NDS.MelonDS.CreateModifiedFirmware(userSpec))
|
||||
: fi);
|
||||
// fixme: there's way more stuff in nds firmware which can affect the hash. rtc fuckery, mac address, wifi settings, etc etc. a proper way to clear these out needs to come
|
||||
/*var rff = reader.Read(fr.ID == NDS_FIRMWARE
|
||||
? new FileInfo(Emulation.Cores.Consoles.Nintendo.NDS.NDS.CreateModifiedFirmware(userSpec))
|
||||
: fi);*/
|
||||
var rff = reader.Read(fi);
|
||||
ri.Size = fi.Length;
|
||||
ri.Hash = rff.Hash;
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ namespace BizHawk.Client.Common
|
|||
// It probably should be moved to a separate place.
|
||||
["GB_Clock"] = 2097152.0,
|
||||
["GBA"] = 262144.0 / 4389.0, // 59.7275005696
|
||||
["NDS"] = 33513982.0 / 560190.0, // 59.8260982881
|
||||
["GEN"] = 53693175 / (3420.0 * 262),
|
||||
["GEN_PAL"] = 53203424 / (3420.0 * 313),
|
||||
|
||||
|
|
|
@ -1,146 +1,149 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores;
|
||||
using BizHawk.Emulation.Cores.Consoles.Nintendo.NDS;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
[ImporterFor("DeSmuME", ".dsm")]
|
||||
internal class DsmImport : MovieImporter
|
||||
{
|
||||
protected override void RunImport()
|
||||
{
|
||||
Result.Movie.HeaderEntries[HeaderKeys.Platform] = "NDS";
|
||||
|
||||
var syncSettings = new MelonDS.MelonSyncSettings();
|
||||
|
||||
using var sr = SourceFile.OpenText();
|
||||
string line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line[0] == '|')
|
||||
{
|
||||
ImportInputFrame(line);
|
||||
}
|
||||
else if (line.StartsWith("rerecordCount"))
|
||||
{
|
||||
int.TryParse(ParseHeader(line, "rerecordCount"), out var rerecordCount);
|
||||
Result.Movie.Rerecords = (ulong)rerecordCount;
|
||||
}
|
||||
else if (line.StartsWith("firmNickname"))
|
||||
{
|
||||
syncSettings.Nickname = ParseHeader(line, "firmNickname");
|
||||
}
|
||||
else if (line.StartsWith("firmFavColour"))
|
||||
{
|
||||
syncSettings.FavoriteColor = byte.Parse(ParseHeader(line, "firmFavColour"));
|
||||
}
|
||||
else if (line.StartsWith("firmBirthDay"))
|
||||
{
|
||||
syncSettings.BirthdayDay = byte.Parse(ParseHeader(line, "firmBirthDay"));
|
||||
}
|
||||
else if (line.StartsWith("firmBirthMonth"))
|
||||
{
|
||||
syncSettings.BirthdayMonth = byte.Parse(ParseHeader(line, "firmBirthMonth"));
|
||||
}
|
||||
else if (line.StartsWith("rtcStartNew"))
|
||||
{
|
||||
//TODO: what is this format?? 2010-JAN-01 00:00:00:000
|
||||
//var time = DateTime.Parse(ParseHeader(line, "rtcStartNew"));
|
||||
//syncSettings.TimeAtBoot = (uint)new DateTimeOffset(time.ToLocalTime()).ToUnixTimeSeconds();
|
||||
}
|
||||
else if (line.StartsWith("comment author"))
|
||||
{
|
||||
Result.Movie.HeaderEntries[HeaderKeys.Author] = ParseHeader(line, "comment author");
|
||||
}
|
||||
else if (line.StartsWith("comment"))
|
||||
{
|
||||
Result.Movie.Comments.Add(ParseHeader(line, "comment"));
|
||||
}
|
||||
else if (line.ToLower().StartsWith("guid"))
|
||||
{
|
||||
// We no longer care to keep this info
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.Movie.Comments.Add(line); // Everything not explicitly defined is treated as a comment.
|
||||
}
|
||||
|
||||
Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(syncSettings);
|
||||
}
|
||||
|
||||
Result.Movie.HeaderEntries[HeaderKeys.Core] = CoreNames.MelonDS;
|
||||
}
|
||||
|
||||
private readonly string[] _buttons = { "Left", "Right", "Up", "Down", "A", "B", "X", "Y", "L", "R", "Start", "Select" };
|
||||
|
||||
private void ImportInputFrame(string line)
|
||||
{
|
||||
var controller = new SimpleController
|
||||
{
|
||||
Definition = new ControllerDefinition
|
||||
{
|
||||
BoolButtons =
|
||||
{
|
||||
"Left", "Right", "Up", "Down",
|
||||
"A", "B", "X", "Y", "L", "R", "Start", "Select", "LidOpen", "LidClose", "Power", "Touch"
|
||||
}
|
||||
}.AddXYPair("Touch{0}", AxisPairOrientation.RightAndUp, 0.RangeTo(255), 128, 0.RangeTo(191), 96) //TODO verify direction against hardware
|
||||
};
|
||||
|
||||
controller["LidOpen"] = false;
|
||||
controller["LidClose"] = false;
|
||||
controller["Power"] = false;
|
||||
|
||||
string[] sections = line.Split(new[] {'|'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (sections.Length > 0)
|
||||
{
|
||||
ProcessCmd(sections[0], controller);
|
||||
}
|
||||
|
||||
if (sections.Length > 1)
|
||||
{
|
||||
var mnemonics = sections[1].Take(_buttons.Length).ToList();
|
||||
|
||||
controller["Left"] = mnemonics[1] != '.';
|
||||
controller["Right"] = mnemonics[0] != '.';
|
||||
controller["Up"] = mnemonics[3] != '.';
|
||||
controller["Down"] = mnemonics[2] != '.';
|
||||
controller["A"] = mnemonics[7] != '.';
|
||||
controller["B"] = mnemonics[6] != '.';
|
||||
controller["X"] = mnemonics[9] != '.';
|
||||
controller["Y"] = mnemonics[8] != '.';
|
||||
controller["L"] = mnemonics[10] != '.';
|
||||
controller["R"] = mnemonics[11] != '.';
|
||||
controller["Start"] = mnemonics[4] != '.';
|
||||
controller["Select"] = mnemonics[5] != '.';
|
||||
|
||||
controller["Touch"] = sections[1].Substring(21, 1) != "0";
|
||||
|
||||
var touchX = int.Parse(sections[1].Substring(13, 3));
|
||||
var touchY = int.Parse(sections[1].Substring(17, 3));
|
||||
|
||||
controller.AcceptNewAxes(new[]
|
||||
{
|
||||
("TouchX", touchX),
|
||||
("TouchY", touchY)
|
||||
});
|
||||
}
|
||||
|
||||
Result.Movie.AppendFrame(controller);
|
||||
}
|
||||
|
||||
private void ProcessCmd(string cmd, SimpleController controller)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores;
|
||||
using BizHawk.Emulation.Cores.Consoles.Nintendo.NDS;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
[ImporterFor("DeSmuME", ".dsm")]
|
||||
internal class DsmImport : MovieImporter
|
||||
{
|
||||
protected override void RunImport()
|
||||
{
|
||||
Result.Movie.HeaderEntries[HeaderKeys.Platform] = "NDS";
|
||||
|
||||
var syncSettings = new NDS.SyncSettings();
|
||||
|
||||
using var sr = SourceFile.OpenText();
|
||||
string line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line[0] == '|')
|
||||
{
|
||||
ImportInputFrame(line);
|
||||
}
|
||||
else if (line.StartsWith("rerecordCount"))
|
||||
{
|
||||
int.TryParse(ParseHeader(line, "rerecordCount"), out var rerecordCount);
|
||||
Result.Movie.Rerecords = (ulong)rerecordCount;
|
||||
}
|
||||
else if (line.StartsWith("firmNickname"))
|
||||
{
|
||||
syncSettings.FirmwareUsername = ParseHeader(line, "firmNickname");
|
||||
}
|
||||
else if (line.StartsWith("firmFavColour"))
|
||||
{
|
||||
syncSettings.FirmwareFavouriteColour = (NDS.SyncSettings.Color)byte.Parse(ParseHeader(line, "firmFavColour"));
|
||||
}
|
||||
else if (line.StartsWith("firmBirthDay"))
|
||||
{
|
||||
syncSettings.FirmwareBirthdayDay = byte.Parse(ParseHeader(line, "firmBirthDay"));
|
||||
}
|
||||
else if (line.StartsWith("firmBirthMonth"))
|
||||
{
|
||||
syncSettings.FirmwareBirthdayMonth = (NDS.SyncSettings.Month)byte.Parse(ParseHeader(line, "firmBirthMonth"));
|
||||
}
|
||||
else if (line.StartsWith("rtcStartNew"))
|
||||
{
|
||||
//TODO: what is this format?? 2010-JAN-01 00:00:00:000
|
||||
//var time = DateTime.Parse(ParseHeader(line, "rtcStartNew"));
|
||||
//syncSettings.TimeAtBoot = (uint)new DateTimeOffset(time.ToLocalTime()).ToUnixTimeSeconds();
|
||||
}
|
||||
else if (line.StartsWith("comment author"))
|
||||
{
|
||||
Result.Movie.HeaderEntries[HeaderKeys.Author] = ParseHeader(line, "comment author");
|
||||
}
|
||||
else if (line.StartsWith("comment"))
|
||||
{
|
||||
Result.Movie.Comments.Add(ParseHeader(line, "comment"));
|
||||
}
|
||||
else if (line.ToLower().StartsWith("guid"))
|
||||
{
|
||||
// We no longer care to keep this info
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.Movie.Comments.Add(line); // Everything not explicitly defined is treated as a comment.
|
||||
}
|
||||
|
||||
Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(syncSettings);
|
||||
}
|
||||
|
||||
Result.Movie.HeaderEntries[HeaderKeys.Core] = CoreNames.MelonDS;
|
||||
}
|
||||
|
||||
private readonly string[] _buttons = { "Left", "Right", "Up", "Down", "A", "B", "X", "Y", "L", "R", "Start", "Select" };
|
||||
|
||||
private void ImportInputFrame(string line)
|
||||
{
|
||||
var controller = new SimpleController
|
||||
{
|
||||
Definition = new ControllerDefinition
|
||||
{
|
||||
BoolButtons =
|
||||
{
|
||||
"Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Y", "X", "L", "R", "LidOpen", "LidClose", "Touch", "Power"
|
||||
}
|
||||
}.AddXYPair("Touch {0}", AxisPairOrientation.RightAndUp, 0.RangeTo(255), 128, 0.RangeTo(191), 96) //TODO verify direction against hardware
|
||||
.AddAxis("Mic Input", 0.RangeTo(2047), 0)
|
||||
.AddAxis("GBA Light Sensor", 0.RangeTo(10), 0)
|
||||
};
|
||||
|
||||
controller["LidOpen"] = false;
|
||||
controller["LidClose"] = false;
|
||||
controller["Power"] = false;
|
||||
|
||||
string[] sections = line.Split(new[] {'|'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (sections.Length > 0)
|
||||
{
|
||||
ProcessCmd(sections[0], controller);
|
||||
}
|
||||
|
||||
if (sections.Length > 1)
|
||||
{
|
||||
var mnemonics = sections[1].Take(_buttons.Length).ToList();
|
||||
|
||||
controller["Left"] = mnemonics[1] != '.';
|
||||
controller["Right"] = mnemonics[0] != '.';
|
||||
controller["Up"] = mnemonics[3] != '.';
|
||||
controller["Down"] = mnemonics[2] != '.';
|
||||
controller["A"] = mnemonics[7] != '.';
|
||||
controller["B"] = mnemonics[6] != '.';
|
||||
controller["X"] = mnemonics[9] != '.';
|
||||
controller["Y"] = mnemonics[8] != '.';
|
||||
controller["L"] = mnemonics[10] != '.';
|
||||
controller["R"] = mnemonics[11] != '.';
|
||||
controller["Start"] = mnemonics[4] != '.';
|
||||
controller["Select"] = mnemonics[5] != '.';
|
||||
|
||||
controller["Touch"] = sections[1].Substring(21, 1) != "0";
|
||||
|
||||
var touchX = int.Parse(sections[1].Substring(13, 3));
|
||||
var touchY = int.Parse(sections[1].Substring(17, 3));
|
||||
|
||||
controller.AcceptNewAxes(new[]
|
||||
{
|
||||
("Touch X", touchX),
|
||||
("Touch Y", touchY),
|
||||
("Mic Input", 0),
|
||||
("GBA Light Sensor", 0)
|
||||
});
|
||||
}
|
||||
|
||||
Result.Movie.AppendFrame(controller);
|
||||
}
|
||||
|
||||
private void ProcessCmd(string cmd, SimpleController controller)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace BizHawk.Client.EmuHawk.CoreExtensions
|
|||
Snes9x => Properties.Resources.Snes9X,
|
||||
MAME => Properties.Resources.Mame,
|
||||
MGBAHawk => Properties.Resources.Mgba,
|
||||
MelonDS => Properties.Resources.MelonDS,
|
||||
NDS => Properties.Resources.MelonDS,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
|
|
@ -246,7 +246,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
this.GBPrinterViewerMenuItem = new BizHawk.WinForms.Controls.ToolStripMenuItemEx();
|
||||
this.NDSSubMenu = new BizHawk.WinForms.Controls.ToolStripMenuItemEx();
|
||||
this.NdsSettingsMenuItem = new BizHawk.WinForms.Controls.ToolStripMenuItemEx();
|
||||
this.NdsSyncSettingsMenuItem = new BizHawk.WinForms.Controls.ToolStripMenuItemEx();
|
||||
this.PSXSubMenu = new BizHawk.WinForms.Controls.ToolStripMenuItemEx();
|
||||
this.PSXControllerSettingsMenuItem = new BizHawk.WinForms.Controls.ToolStripMenuItemEx();
|
||||
this.PSXOptionsMenuItem = new BizHawk.WinForms.Controls.ToolStripMenuItemEx();
|
||||
|
@ -1640,21 +1639,14 @@ namespace BizHawk.Client.EmuHawk
|
|||
// NDSSubMenu
|
||||
//
|
||||
this.NDSSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.NdsSettingsMenuItem,
|
||||
this.NdsSyncSettingsMenuItem});
|
||||
this.NdsSettingsMenuItem });
|
||||
this.NDSSubMenu.Text = "NDS";
|
||||
this.NDSSubMenu.DropDownOpened += new System.EventHandler(this.NDSSubMenu_DropDownOpened);
|
||||
//
|
||||
// NdsSettingsMenuItem
|
||||
//
|
||||
this.NdsSettingsMenuItem.Text = "Settings...";
|
||||
this.NdsSettingsMenuItem.Click += new System.EventHandler(this.NDSSettingsMenuItem_Click);
|
||||
//
|
||||
// NdsSyncSettingsMenuItem
|
||||
//
|
||||
this.NdsSyncSettingsMenuItem.Text = "Sync Settings...";
|
||||
this.NdsSyncSettingsMenuItem.Click += new System.EventHandler(this.NDSSyncSettingsMenuItem_Click);
|
||||
//
|
||||
// PSXSubMenu
|
||||
//
|
||||
this.PSXSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
|
@ -2796,7 +2788,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
private BizHawk.WinForms.Controls.ToolStripMenuItemEx cpcd1ToolStripMenuItem;
|
||||
private BizHawk.WinForms.Controls.ToolStripMenuItemEx AmstradCPCNonSyncSettingsToolStripMenuItem;
|
||||
private BizHawk.WinForms.Controls.ToolStripMenuItemEx NDSSubMenu;
|
||||
private BizHawk.WinForms.Controls.ToolStripMenuItemEx NdsSyncSettingsMenuItem;
|
||||
private BizHawk.WinForms.Controls.ToolStripMenuItemEx NdsSettingsMenuItem;
|
||||
private BizHawk.WinForms.Controls.ToolStripSeparatorEx toolStripSeparator8;
|
||||
private System.Windows.Forms.ToolStripMenuItem CaptureLuaMenuItem;
|
||||
|
|
|
@ -1586,26 +1586,12 @@ namespace BizHawk.Client.EmuHawk
|
|||
Tools.Load<GBPrinterView>();
|
||||
}
|
||||
|
||||
private void NDSSubMenu_DropDownOpened(object sender, EventArgs e)
|
||||
{
|
||||
NdsSyncSettingsMenuItem.Enabled = MovieSession.Movie.NotActive();
|
||||
}
|
||||
|
||||
private void NDSSettingsMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
GenericCoreConfig.DoDialog(this, "NDS Settings", false, true);
|
||||
GenericCoreConfig.DoDialog(this, "NDS Settings", false, false);
|
||||
FrameBufferResized();
|
||||
}
|
||||
|
||||
private void NDSSyncSettingsMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (Emulator is MelonDS ds)
|
||||
{
|
||||
using var form = new NdsSyncSettings(this, ds.GetSyncSettings().Clone());
|
||||
form.ShowDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private void PsxSubMenu_DropDownOpened(object sender, EventArgs e)
|
||||
{
|
||||
PSXControllerSettingsMenuItem.Enabled = MovieSession.Movie.NotActive();
|
||||
|
|
|
@ -789,15 +789,15 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
private void IncrementDSScreenRotate()
|
||||
{
|
||||
if (Emulator is MelonDS ds)
|
||||
if (Emulator is NDS ds)
|
||||
{
|
||||
var settings = ds.GetSettings();
|
||||
settings.ScreenRotation = settings.ScreenRotation switch
|
||||
{
|
||||
MelonDS.ScreenRotationKind.Rotate0 => settings.ScreenRotation = MelonDS.ScreenRotationKind.Rotate90,
|
||||
MelonDS.ScreenRotationKind.Rotate90 => settings.ScreenRotation = MelonDS.ScreenRotationKind.Rotate180,
|
||||
MelonDS.ScreenRotationKind.Rotate180 => settings.ScreenRotation = MelonDS.ScreenRotationKind.Rotate270,
|
||||
MelonDS.ScreenRotationKind.Rotate270 => settings.ScreenRotation = MelonDS.ScreenRotationKind.Rotate0,
|
||||
NDS.ScreenRotationKind.Rotate0 => settings.ScreenRotation = NDS.ScreenRotationKind.Rotate90,
|
||||
NDS.ScreenRotationKind.Rotate90 => settings.ScreenRotation = NDS.ScreenRotationKind.Rotate180,
|
||||
NDS.ScreenRotationKind.Rotate180 => settings.ScreenRotation = NDS.ScreenRotationKind.Rotate270,
|
||||
NDS.ScreenRotationKind.Rotate270 => settings.ScreenRotation = NDS.ScreenRotationKind.Rotate0,
|
||||
_ => settings.ScreenRotation
|
||||
};
|
||||
ds.PutSettings(settings);
|
||||
|
@ -809,7 +809,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
private void IncrementDSLayout(int delta)
|
||||
{
|
||||
bool decrement = delta == -1;
|
||||
if (Emulator is MelonDS ds)
|
||||
if (Emulator is NDS ds)
|
||||
{
|
||||
var settings = ds.GetSettings();
|
||||
var num = (int)settings.ScreenLayout;
|
||||
|
@ -822,8 +822,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
num++;
|
||||
}
|
||||
|
||||
var next = (MelonDS.ScreenLayoutKind)Enum.Parse(typeof(MelonDS.ScreenLayoutKind), num.ToString());
|
||||
if (typeof(MelonDS.ScreenLayoutKind).IsEnumDefined(next))
|
||||
var next = (NDS.ScreenLayoutKind)Enum.Parse(typeof(NDS.ScreenLayoutKind), num.ToString());
|
||||
if (typeof(NDS.ScreenLayoutKind).IsEnumDefined(next))
|
||||
{
|
||||
settings.ScreenLayout = next;
|
||||
|
||||
|
|
|
@ -1,272 +0,0 @@
|
|||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
partial class NdsSyncSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.chkBootToFirmware = new System.Windows.Forms.CheckBox();
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.btnSave = new System.Windows.Forms.Button();
|
||||
this.ttipFirmware = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.txtName = new System.Windows.Forms.TextBox();
|
||||
this.label1 = new BizHawk.WinForms.Controls.LocLabelEx();
|
||||
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
||||
this.numBirthDay = new System.Windows.Forms.NumericUpDown();
|
||||
this.numBirthMonth = new System.Windows.Forms.NumericUpDown();
|
||||
this.label3 = new BizHawk.WinForms.Controls.LocLabelEx();
|
||||
this.cbxFavColor = new System.Windows.Forms.ComboBox();
|
||||
this.label2 = new BizHawk.WinForms.Controls.LocLabelEx();
|
||||
this.btnDefault = new System.Windows.Forms.Button();
|
||||
this.dtpStartupTime = new System.Windows.Forms.DateTimePicker();
|
||||
this.label4 = new BizHawk.WinForms.Controls.LocLabelEx();
|
||||
this.groupBox1.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.numBirthDay)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.numBirthMonth)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// chkBootToFirmware
|
||||
//
|
||||
this.chkBootToFirmware.AutoSize = true;
|
||||
this.chkBootToFirmware.Location = new System.Drawing.Point(12, 12);
|
||||
this.chkBootToFirmware.Name = "chkBootToFirmware";
|
||||
this.chkBootToFirmware.Size = new System.Drawing.Size(102, 17);
|
||||
this.chkBootToFirmware.TabIndex = 0;
|
||||
this.chkBootToFirmware.Text = "Boot to firmware";
|
||||
this.ttipFirmware.SetToolTip(this.chkBootToFirmware, "This option requires that a firmware file be in use, as well as both bios files. " +
|
||||
"See Config -> Firmwares.");
|
||||
this.chkBootToFirmware.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.btnCancel.Location = new System.Drawing.Point(163, 179);
|
||||
this.btnCancel.Name = "btnCancel";
|
||||
this.btnCancel.Size = new System.Drawing.Size(60, 23);
|
||||
this.btnCancel.TabIndex = 1;
|
||||
this.btnCancel.Text = "Cancel";
|
||||
this.btnCancel.UseVisualStyleBackColor = true;
|
||||
this.btnCancel.Click += new System.EventHandler(this.CancelBtn_Click);
|
||||
//
|
||||
// btnSave
|
||||
//
|
||||
this.btnSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnSave.Location = new System.Drawing.Point(97, 179);
|
||||
this.btnSave.Name = "btnSave";
|
||||
this.btnSave.Size = new System.Drawing.Size(60, 23);
|
||||
this.btnSave.TabIndex = 1;
|
||||
this.btnSave.Text = "Save";
|
||||
this.btnSave.UseVisualStyleBackColor = true;
|
||||
this.btnSave.Click += new System.EventHandler(this.SaveBtn_Click);
|
||||
//
|
||||
// txtName
|
||||
//
|
||||
this.txtName.Location = new System.Drawing.Point(50, 16);
|
||||
this.txtName.MaxLength = 10;
|
||||
this.txtName.Name = "txtName";
|
||||
this.txtName.Size = new System.Drawing.Size(94, 20);
|
||||
this.txtName.TabIndex = 2;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.Location = new System.Drawing.Point(6, 19);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Text = "Name:";
|
||||
//
|
||||
// groupBox1
|
||||
//
|
||||
this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.groupBox1.Controls.Add(this.numBirthDay);
|
||||
this.groupBox1.Controls.Add(this.numBirthMonth);
|
||||
this.groupBox1.Controls.Add(this.label3);
|
||||
this.groupBox1.Controls.Add(this.cbxFavColor);
|
||||
this.groupBox1.Controls.Add(this.label2);
|
||||
this.groupBox1.Controls.Add(this.txtName);
|
||||
this.groupBox1.Controls.Add(this.label1);
|
||||
this.groupBox1.Location = new System.Drawing.Point(12, 35);
|
||||
this.groupBox1.Name = "groupBox1";
|
||||
this.groupBox1.Size = new System.Drawing.Size(211, 110);
|
||||
this.groupBox1.TabIndex = 4;
|
||||
this.groupBox1.TabStop = false;
|
||||
this.groupBox1.Text = "Firmware settings";
|
||||
//
|
||||
// numBirthDay
|
||||
//
|
||||
this.numBirthDay.Location = new System.Drawing.Point(133, 69);
|
||||
this.numBirthDay.Maximum = new decimal(new int[] {
|
||||
31,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.numBirthDay.Name = "numBirthDay";
|
||||
this.numBirthDay.Size = new System.Drawing.Size(36, 20);
|
||||
this.numBirthDay.TabIndex = 7;
|
||||
this.numBirthDay.Value = new decimal(new int[] {
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
//
|
||||
// numBirthMonth
|
||||
//
|
||||
this.numBirthMonth.Location = new System.Drawing.Point(91, 69);
|
||||
this.numBirthMonth.Maximum = new decimal(new int[] {
|
||||
12,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.numBirthMonth.Name = "numBirthMonth";
|
||||
this.numBirthMonth.Size = new System.Drawing.Size(36, 20);
|
||||
this.numBirthMonth.TabIndex = 6;
|
||||
this.numBirthMonth.Value = new decimal(new int[] {
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.numBirthMonth.ValueChanged += new System.EventHandler(this.numBirthMonth_ValueChanged);
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.Location = new System.Drawing.Point(6, 71);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Text = "Birthday (M/D):";
|
||||
//
|
||||
// cbxFavColor
|
||||
//
|
||||
this.cbxFavColor.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.cbxFavColor.FormattingEnabled = true;
|
||||
this.cbxFavColor.Items.AddRange(new object[] {
|
||||
"Gray",
|
||||
"Brown",
|
||||
"Red",
|
||||
"Pink",
|
||||
"Orange",
|
||||
"Yellow",
|
||||
"Lime Green",
|
||||
"Green",
|
||||
"Dark Green",
|
||||
"Sea Green",
|
||||
"Turquoise",
|
||||
"Blue",
|
||||
"Dark Blue",
|
||||
"Dark Purple",
|
||||
"Violet",
|
||||
"Magenta"});
|
||||
this.cbxFavColor.Location = new System.Drawing.Point(50, 42);
|
||||
this.cbxFavColor.Name = "cbxFavColor";
|
||||
this.cbxFavColor.Size = new System.Drawing.Size(94, 21);
|
||||
this.cbxFavColor.TabIndex = 5;
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.Location = new System.Drawing.Point(6, 45);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Text = "Color:";
|
||||
//
|
||||
// btnDefault
|
||||
//
|
||||
this.btnDefault.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.btnDefault.Location = new System.Drawing.Point(12, 179);
|
||||
this.btnDefault.Name = "btnDefault";
|
||||
this.btnDefault.Size = new System.Drawing.Size(60, 23);
|
||||
this.btnDefault.TabIndex = 1;
|
||||
this.btnDefault.Text = "Default";
|
||||
this.btnDefault.UseVisualStyleBackColor = true;
|
||||
this.btnDefault.Click += new System.EventHandler(this.DefaultBtn_Click);
|
||||
//
|
||||
// dtpStartupTime
|
||||
//
|
||||
this.dtpStartupTime.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.dtpStartupTime.CustomFormat = "yyyy-MM-dd HH:mm:ss";
|
||||
this.dtpStartupTime.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
|
||||
this.dtpStartupTime.Location = new System.Drawing.Point(75, 151);
|
||||
this.dtpStartupTime.MaxDate = new System.DateTime(2099, 12, 31, 23, 59, 59, 0);
|
||||
this.dtpStartupTime.MinDate = new System.DateTime(2000, 1, 1, 0, 0, 0, 0);
|
||||
this.dtpStartupTime.Name = "dtpStartupTime";
|
||||
this.dtpStartupTime.Size = new System.Drawing.Size(148, 20);
|
||||
this.dtpStartupTime.TabIndex = 5;
|
||||
this.dtpStartupTime.Value = new System.DateTime(2000, 1, 1, 0, 0, 0, 0);
|
||||
//
|
||||
// label4
|
||||
//
|
||||
this.label4.Location = new System.Drawing.Point(12, 154);
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Text = "Date/time:";
|
||||
//
|
||||
// NdsSettings
|
||||
//
|
||||
this.AcceptButton = this.btnDefault;
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.CancelButton = this.btnCancel;
|
||||
this.ClientSize = new System.Drawing.Size(243, 222);
|
||||
this.Controls.Add(this.label4);
|
||||
this.Controls.Add(this.dtpStartupTime);
|
||||
this.Controls.Add(this.groupBox1);
|
||||
this.Controls.Add(this.btnDefault);
|
||||
this.Controls.Add(this.btnSave);
|
||||
this.Controls.Add(this.btnCancel);
|
||||
this.Controls.Add(this.chkBootToFirmware);
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.MinimumSize = new System.Drawing.Size(251, 249);
|
||||
this.Name = "NdsSettings";
|
||||
this.ShowIcon = false;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "NDS Sync Settings";
|
||||
this.Load += new System.EventHandler(this.NDSSettings_Load);
|
||||
this.groupBox1.ResumeLayout(false);
|
||||
this.groupBox1.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.numBirthDay)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.numBirthMonth)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.CheckBox chkBootToFirmware;
|
||||
private System.Windows.Forms.Button btnCancel;
|
||||
private System.Windows.Forms.Button btnSave;
|
||||
private System.Windows.Forms.ToolTip ttipFirmware;
|
||||
private System.Windows.Forms.TextBox txtName;
|
||||
private BizHawk.WinForms.Controls.LocLabelEx label1;
|
||||
private System.Windows.Forms.GroupBox groupBox1;
|
||||
private BizHawk.WinForms.Controls.LocLabelEx label3;
|
||||
private System.Windows.Forms.ComboBox cbxFavColor;
|
||||
private BizHawk.WinForms.Controls.LocLabelEx label2;
|
||||
private System.Windows.Forms.NumericUpDown numBirthDay;
|
||||
private System.Windows.Forms.NumericUpDown numBirthMonth;
|
||||
private System.Windows.Forms.Button btnDefault;
|
||||
private System.Windows.Forms.DateTimePicker dtpStartupTime;
|
||||
private BizHawk.WinForms.Controls.LocLabelEx label4;
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using BizHawk.Client.Common;
|
||||
using BizHawk.Emulation.Cores.Consoles.Nintendo.NDS;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
public partial class NdsSyncSettings : Form
|
||||
{
|
||||
private readonly IMainFormForConfig _mainForm;
|
||||
private readonly MelonDS.MelonSyncSettings _syncSettings;
|
||||
|
||||
public NdsSyncSettings(
|
||||
IMainFormForConfig mainForm,
|
||||
MelonDS.MelonSyncSettings syncSettings)
|
||||
{
|
||||
_mainForm = mainForm;
|
||||
_syncSettings = syncSettings;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void NDSSettings_Load(object sender, EventArgs e)
|
||||
{
|
||||
chkBootToFirmware.Checked = _syncSettings.BootToFirmware;
|
||||
txtName.Text = _syncSettings.Nickname;
|
||||
cbxFavColor.SelectedIndex = _syncSettings.FavoriteColor;
|
||||
numBirthDay.Value = _syncSettings.BirthdayDay;
|
||||
numBirthMonth.Value = _syncSettings.BirthdayMonth;
|
||||
dtpStartupTime.Value = DateTimeOffset.FromUnixTimeSeconds(_syncSettings.TimeAtBoot).UtcDateTime;
|
||||
}
|
||||
|
||||
private void numBirthMonth_ValueChanged(object sender, EventArgs e)
|
||||
{
|
||||
switch (numBirthMonth.Value)
|
||||
{
|
||||
case 1:
|
||||
case 3:
|
||||
case 5:
|
||||
case 7:
|
||||
case 8:
|
||||
case 10:
|
||||
case 12:
|
||||
numBirthDay.Maximum = 31;
|
||||
break;
|
||||
case 4:
|
||||
case 6:
|
||||
case 9:
|
||||
case 11:
|
||||
numBirthDay.Maximum = 30;
|
||||
break;
|
||||
case 2:
|
||||
numBirthDay.Maximum = 29;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelBtn_Click(object sender, EventArgs e)
|
||||
{
|
||||
_mainForm.AddOnScreenMessage("Core emulator settings aborted");
|
||||
DialogResult = DialogResult.Cancel;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void SaveBtn_Click(object sender, EventArgs e)
|
||||
{
|
||||
_syncSettings.BootToFirmware = chkBootToFirmware.Checked;
|
||||
_syncSettings.Nickname = txtName.Text;
|
||||
_syncSettings.FavoriteColor = (byte)cbxFavColor.SelectedIndex;
|
||||
_syncSettings.BirthdayDay = (byte)numBirthDay.Value;
|
||||
_syncSettings.BirthdayMonth = (byte)numBirthMonth.Value;
|
||||
|
||||
// Converting to local time is necessary, because user-set values are "unspecified" which ToUnixTimeSeconds assumes are local.
|
||||
// But ToLocalTime assumes these are UTC. So here we are adding and then subtracting the UTC-to-local offset.
|
||||
_syncSettings.TimeAtBoot = (uint)new DateTimeOffset(dtpStartupTime.Value.ToLocalTime()).ToUnixTimeSeconds();
|
||||
|
||||
_mainForm.PutCoreSyncSettings(_syncSettings);
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void DefaultBtn_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (_mainForm.DialogController.ShowMessageBox2("Revert to and save default settings?", "default settings", useOKCancel: true))
|
||||
{
|
||||
_mainForm.PutCoreSyncSettings(new MelonDS.MelonSyncSettings());
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="ttipFirmware.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="ttipFirmware.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
</root>
|
|
@ -144,6 +144,7 @@
|
|||
"GB3x",
|
||||
"GB4x",
|
||||
"GEN",
|
||||
"NDS",
|
||||
"PCFX",
|
||||
"PSX",
|
||||
"SAT",
|
||||
|
@ -227,4 +228,4 @@
|
|||
private System.Windows.Forms.Button btnRemove;
|
||||
private System.Windows.Forms.Button SaveButton;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,7 +114,20 @@ namespace BizHawk.Emulation.Common
|
|||
|
||||
FirmwareAndOption("24F67BDEA115A2C847C8813A262502EE1607B7DF", 16384, "NDS", "bios7", "NDS_Bios7.bin", "ARM7 BIOS");
|
||||
FirmwareAndOption("BFAAC75F101C135E32E2AAF541DE6B1BE4C8C62D", 4096, "NDS", "bios9", "NDS_Bios9.bin", "ARM9 BIOS");
|
||||
FirmwareAndOption("22A7547DBC302BCBFB4005CFB5A2D426D3F85AC6", 262144, "NDS", "firmware", "NDS_Firmware.bin", "NDS Firmware (note: given hash is with blank user data)");
|
||||
Firmware("NDS", "firmware", "NDS Firmware");
|
||||
// throwing a ton of hashes from the interwebs
|
||||
var knownhack1 = File("22A7547DBC302BCBFB4005CFB5A2D426D3F85AC6", 262144, "NDS_Firmware [b1].bin", "NDS Firmware", "known hack", true);
|
||||
var knownhack2 = File("AE22DE59FBF3F35CCFBEACAEBA6FA87AC5E7B14B", 262144, "NDS_Firmware [b2].bin", "NDS Firmware", "known hack", true);
|
||||
var knownhack3 = File("1CF9E67C2C703BB9961BBCDD39CD2C7E319A803B", 262144, "NDS_Firmware [b3].bin", "NDS Firmware", "known hack", true);
|
||||
var likelygood1 = File("EDE9ADD041614EAA232059C63D8613B83FE4E954", 262144, "NDS_Firmware.bin", "NDS Firmware", "likely good");
|
||||
var likelygood2 = File("2EF20B45D12CF00657D4B1BD37A5CC8506923440", 262144, "NDS_Firmware.bin", "NDS Firmware", "likely good");
|
||||
var likelygood3 = File("87DAE2500E889737AF51F4A5B5845770A62482F5", 262144, "NDS_Lite_Firmware.bin", "NDS-Lite Firmware", "likely good");
|
||||
Option("NDS", "firmware", in knownhack1);
|
||||
Option("NDS", "firmware", in knownhack2);
|
||||
Option("NDS", "firmware", in knownhack3);
|
||||
Option("NDS", "firmware", in likelygood1);
|
||||
Option("NDS", "firmware", in likelygood2);
|
||||
Option("NDS", "firmware", in likelygood3);
|
||||
|
||||
FirmwareAndOption("E4ED47FAE31693E016B081C6BDA48DA5B70D7CCB", 512, "Lynx", "Boot", "LYNX_boot.img", "Boot Rom");
|
||||
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
using BizHawk.BizInvoke;
|
||||
using BizHawk.Emulation.Cores.Waterbox;
|
||||
using System;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||
{
|
||||
public abstract class LibMelonDS : LibWaterboxCore
|
||||
{
|
||||
[Flags]
|
||||
public enum Buttons : uint
|
||||
{
|
||||
A = 0x0001,
|
||||
B = 0x0002,
|
||||
SELECT = 0x0004,
|
||||
START = 0x0008,
|
||||
RIGHT = 0x0010,
|
||||
LEFT = 0x0020,
|
||||
UP = 0x0040,
|
||||
DOWN = 0x0080,
|
||||
R = 0x0100,
|
||||
L = 0x0200,
|
||||
X = 0x0400,
|
||||
Y = 0x0800,
|
||||
TOUCH = 0x1000,
|
||||
LIDOPEN = 0x2000,
|
||||
LIDCLOSE = 0x4000,
|
||||
POWER = 0x8000,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum LoadFlags : uint
|
||||
{
|
||||
NONE = 0x00,
|
||||
USE_REAL_BIOS = 0x01,
|
||||
SKIP_FIRMWARE = 0x02,
|
||||
GBA_CART_PRESENT = 0x04,
|
||||
ACCURATE_AUDIO_BITRATE = 0x08,
|
||||
FIRMWARE_OVERRIDE = 0x10,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public new class FrameInfo : LibWaterboxCore.FrameInfo
|
||||
{
|
||||
public long Time;
|
||||
public Buttons Keys;
|
||||
public byte TouchX;
|
||||
public byte TouchY;
|
||||
public short MicInput;
|
||||
public byte GBALightSensor;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class FirmwareSettings
|
||||
{
|
||||
public IntPtr FirmwareUsername; // max 10 length (then terminator)
|
||||
public int FirmwareUsernameLength;
|
||||
public NDS.SyncSettings.Language FirmwareLanguage;
|
||||
public NDS.SyncSettings.Month FirmwareBirthdayMonth;
|
||||
public int FirmwareBirthdayDay;
|
||||
public NDS.SyncSettings.Color FirmwareFavouriteColour;
|
||||
public IntPtr FirmwareMessage; // max 26 length (then terminator)
|
||||
public int FirmwareMessageLength;
|
||||
};
|
||||
|
||||
[BizImport(CC)]
|
||||
public abstract bool Init(LoadFlags flags, FirmwareSettings fwSettings);
|
||||
|
||||
[BizImport(CC)]
|
||||
public abstract void PutSaveRam(byte[] data, uint len);
|
||||
|
||||
[BizImport(CC)]
|
||||
public abstract void GetSaveRam(byte[] data);
|
||||
|
||||
[BizImport(CC)]
|
||||
public abstract int GetSaveRamLength();
|
||||
|
||||
[BizImport(CC)]
|
||||
public abstract bool SaveRamIsDirty();
|
||||
|
||||
[BizImport(CC)]
|
||||
public abstract void GetRegs(uint[] regs);
|
||||
|
||||
[BizImport(CC)]
|
||||
public abstract void SetReg(int ncpu, int index, int val);
|
||||
|
||||
// bit 0 -> ARM9 or ARM7
|
||||
// bit 1 -> ARM or THUMB mode
|
||||
public enum CpuTypes : uint
|
||||
{
|
||||
ARM9,
|
||||
ARM7,
|
||||
ARM9_THUMB,
|
||||
ARM7_THUMB,
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CC)]
|
||||
public delegate void TraceCallback(CpuTypes _cpu, IntPtr _regs, uint _opcode, long _ccoffset);
|
||||
|
||||
[BizImport(CC)]
|
||||
public abstract void SetTraceCallback(TraceCallback callback);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||
{
|
||||
partial class NDS : IDebuggable
|
||||
{
|
||||
[FeatureNotImplemented]
|
||||
public IMemoryCallbackSystem MemoryCallbacks => throw new NotImplementedException(); // https://github.com/TASEmulators/BizHawk/issues/2585
|
||||
|
||||
public long TotalExecutedCycles => CycleCount;
|
||||
|
||||
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
|
||||
{
|
||||
uint[] regs = new uint[2 * 16];
|
||||
_core.GetRegs(regs);
|
||||
|
||||
var ret = new Dictionary<string, RegisterValue>();
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
int ncpu = i == 0 ? 9 : 7;
|
||||
for (int j = 0; j < 16; j++)
|
||||
{
|
||||
ret["ARM" + ncpu + " r" + j] = regs[i * 16 + j];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void SetCpuRegister(string register, int value)
|
||||
{
|
||||
if (register.Length != 7 && register.Length != 8)
|
||||
{
|
||||
throw new InvalidOperationException("Wrong String Length???");
|
||||
}
|
||||
int ncpu = int.Parse(register.Substring(3, 1));
|
||||
if (ncpu != 9 && ncpu != 7)
|
||||
{
|
||||
throw new InvalidOperationException("Invalid CPU???");
|
||||
}
|
||||
int index = int.Parse(register.Substring(6, register.Length - 6));
|
||||
if (index < 0 || index > 15)
|
||||
{
|
||||
throw new InvalidOperationException("Invalid Reg Index???");
|
||||
}
|
||||
_core.SetReg(ncpu == 9 ? 0 : 1, index, value);
|
||||
}
|
||||
|
||||
public bool CanStep(StepType type) => false;
|
||||
|
||||
[FeatureNotImplemented]
|
||||
public void Step(StepType type) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||
{
|
||||
partial class NDS : ISaveRam
|
||||
{
|
||||
public new bool SaveRamModified => _core.SaveRamIsDirty();
|
||||
|
||||
public new byte[] CloneSaveRam()
|
||||
{
|
||||
int length = _core.GetSaveRamLength();
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
byte[] ret = new byte[length];
|
||||
_core.GetSaveRam(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
public new void StoreSaveRam(byte[] data)
|
||||
{
|
||||
if (data.Length > 0)
|
||||
{
|
||||
_core.PutSaveRam(data, (uint)data.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,321 @@
|
|||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||
{
|
||||
partial class NDS : ISettable<NDS.Settings, NDS.SyncSettings>
|
||||
{
|
||||
private Settings _settings;
|
||||
private SyncSettings _syncSettings;
|
||||
|
||||
public enum ScreenLayoutKind
|
||||
{
|
||||
Vertical,
|
||||
Horizontal,
|
||||
Top,
|
||||
Bottom,
|
||||
}
|
||||
|
||||
public enum ScreenRotationKind
|
||||
{
|
||||
Rotate0,
|
||||
Rotate90,
|
||||
Rotate180,
|
||||
Rotate270
|
||||
}
|
||||
|
||||
public class Settings
|
||||
{
|
||||
[DisplayName("Screen Layout")]
|
||||
[Description("Adjusts the layout of the screens")]
|
||||
[DefaultValue(ScreenLayoutKind.Vertical)]
|
||||
public ScreenLayoutKind ScreenLayout { get; set; }
|
||||
|
||||
[DisplayName("Invert Screens")]
|
||||
[Description("Inverts the order of the screens.")]
|
||||
[DefaultValue(false)]
|
||||
public bool ScreenInvert { get; set; }
|
||||
|
||||
[DisplayName("Rotation")]
|
||||
[Description("Adjusts the orientation of the screens")]
|
||||
[DefaultValue(ScreenRotationKind.Rotate0)]
|
||||
public ScreenRotationKind ScreenRotation { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
private int _screengap;
|
||||
|
||||
[DisplayName("Screen Gap")]
|
||||
[Description("Gap between the screens")]
|
||||
[DefaultValue(0)]
|
||||
public int ScreenGap
|
||||
{
|
||||
get => _screengap;
|
||||
set => _screengap = Math.Max(0, Math.Min(128, value));
|
||||
}
|
||||
|
||||
[DisplayName("Accurate Audio Bitrate")]
|
||||
[Description("If true, the audio bitrate will be set to 10. Otherwise, it will be set to 16.")]
|
||||
[DefaultValue(true)]
|
||||
public bool AccurateAudioBitrate { get; set; }
|
||||
|
||||
public Settings Clone()
|
||||
{
|
||||
return (Settings)MemberwiseClone();
|
||||
}
|
||||
|
||||
public static bool NeedsReboot(Settings x, Settings y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public Settings()
|
||||
{
|
||||
SettingsUtil.SetDefaultValues(this);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly DateTime minDate = new DateTime(2000, 1, 1);
|
||||
private static readonly DateTime maxDate = new DateTime(2099, 12, 31, 23, 59, 59);
|
||||
|
||||
public class SyncSettings
|
||||
{
|
||||
[JsonIgnore]
|
||||
private DateTime _initaltime;
|
||||
|
||||
[DisplayName("Initial Time")]
|
||||
[Description("Initial time of emulation.")]
|
||||
[DefaultValue(typeof(DateTime), "2000-01-01")]
|
||||
public DateTime InitialTime
|
||||
{
|
||||
get => _initaltime;
|
||||
set
|
||||
{
|
||||
if (DateTime.Compare(minDate, value) > 0)
|
||||
{
|
||||
_initaltime = minDate;
|
||||
}
|
||||
else if (DateTime.Compare(maxDate, value) < 0)
|
||||
{
|
||||
_initaltime = maxDate;
|
||||
}
|
||||
else
|
||||
{
|
||||
_initaltime = value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[DisplayName("Use Real Time")]
|
||||
[Description("If true, RTC clock will be based off of real time instead of emulated time. Ignored (set to false) when recording a movie.")]
|
||||
[DefaultValue(false)]
|
||||
public bool UseRealTime { get; set; }
|
||||
|
||||
[DisplayName("Use Real BIOS")]
|
||||
[Description("If true, real BIOS files will be used.")]
|
||||
[DefaultValue(false)]
|
||||
public bool UseRealBIOS { get; set; }
|
||||
|
||||
[DisplayName("Skip Firmware")]
|
||||
[Description("If true, initial firmware boot will be skipped. Forced true if firmware cannot be booted (no real bios or missing firmware).")]
|
||||
[DefaultValue(false)]
|
||||
public bool SkipFirmware { get; set; }
|
||||
|
||||
[DisplayName("Firmware Override")]
|
||||
[Description("If true, the firmware settings will be overriden by provided settings. Forced true when recording a movie.")]
|
||||
[DefaultValue(false)]
|
||||
public bool FirmwareOverride { get; set; }
|
||||
|
||||
public enum StartUp : int
|
||||
{
|
||||
AutoBoot,
|
||||
ManualBoot,
|
||||
}
|
||||
|
||||
[DisplayName("Firmware Start-Up")]
|
||||
[Description("The way firmware is booted. Auto Boot will go to the game immediately, while Manual Boot will go into the firmware menu. Only applicable if firmware override is in effect.")]
|
||||
[DefaultValue(StartUp.AutoBoot)]
|
||||
public StartUp FirmwareStartUp { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
private string _firmwareusername;
|
||||
|
||||
[DisplayName("Firmware Username")]
|
||||
[Description("Username in firmware. Only applicable if firmware override is in effect.")]
|
||||
[DefaultValue("MelonDS")]
|
||||
public string FirmwareUsername
|
||||
{
|
||||
get => _firmwareusername;
|
||||
set => _firmwareusername = value.Substring(0, Math.Min(10, value.Length));
|
||||
}
|
||||
|
||||
public enum Language : int
|
||||
{
|
||||
Japanese,
|
||||
English,
|
||||
French,
|
||||
German,
|
||||
Italian,
|
||||
Spanish,
|
||||
}
|
||||
|
||||
[DisplayName("Firmware Language")]
|
||||
[Description("Language in firmware. Only applicable if firmware override is in effect.")]
|
||||
[DefaultValue(Language.English)]
|
||||
public Language FirmwareLanguage { get; set; }
|
||||
|
||||
public enum Month : int
|
||||
{
|
||||
January = 1,
|
||||
February,
|
||||
March,
|
||||
April,
|
||||
May,
|
||||
June,
|
||||
July,
|
||||
August,
|
||||
September,
|
||||
October,
|
||||
November,
|
||||
December,
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
private Month _firmwarebirthdaymonth;
|
||||
|
||||
[JsonIgnore]
|
||||
private int _firmwarebirthdayday;
|
||||
|
||||
[DisplayName("Firmware Birthday Month")]
|
||||
[Description("Birthday month in firmware. Only applicable if firmware override is in effect.")]
|
||||
[DefaultValue(Month.May)]
|
||||
public Month FirmwareBirthdayMonth
|
||||
{
|
||||
get => _firmwarebirthdaymonth;
|
||||
set
|
||||
{
|
||||
FirmwareBirthdayDay = SanitizeBirthdayDay(FirmwareBirthdayDay, value);
|
||||
_firmwarebirthdaymonth = value;
|
||||
}
|
||||
}
|
||||
|
||||
[DisplayName("Firmware Birthday Day")]
|
||||
[Description("Birthday day in firmware. Only applicable if firmware override is in effect.")]
|
||||
[DefaultValue(15)]
|
||||
public int FirmwareBirthdayDay
|
||||
{
|
||||
get => _firmwarebirthdayday;
|
||||
set => _firmwarebirthdayday = SanitizeBirthdayDay(value, FirmwareBirthdayMonth);
|
||||
}
|
||||
|
||||
public enum Color : int
|
||||
{
|
||||
GreyishBlue,
|
||||
Brown,
|
||||
Red,
|
||||
LightPink,
|
||||
Orange,
|
||||
Yellow,
|
||||
Lime,
|
||||
LightGreen,
|
||||
DarkGreen,
|
||||
Turqoise,
|
||||
LightBlue,
|
||||
Blue,
|
||||
DarkBlue,
|
||||
DarkPurple,
|
||||
LightPurple,
|
||||
DarkPink,
|
||||
}
|
||||
|
||||
[DisplayName("Firmware Favorite Color")]
|
||||
[Description("Favorite color in firmware. Only applicable if firmware override is in effect.")]
|
||||
[DefaultValue(Color.Red)]
|
||||
public Color FirmwareFavouriteColour { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
private string _firmwaremessage;
|
||||
|
||||
[DisplayName("Firmware Message")]
|
||||
[Description("Message in firmware. Only applicable if firmware override is in effect.")]
|
||||
[DefaultValue("Melons Taste Great!")]
|
||||
public string FirmwareMessage
|
||||
{
|
||||
get => _firmwaremessage;
|
||||
set => _firmwaremessage = value.Substring(0, Math.Min(26, value.Length));
|
||||
}
|
||||
|
||||
public SyncSettings Clone()
|
||||
{
|
||||
return (SyncSettings)MemberwiseClone();
|
||||
}
|
||||
|
||||
public static bool NeedsReboot(SyncSettings x, SyncSettings y)
|
||||
{
|
||||
return !DeepEquality.DeepEquals(x, y);
|
||||
}
|
||||
|
||||
public SyncSettings()
|
||||
{
|
||||
SettingsUtil.SetDefaultValues(this);
|
||||
}
|
||||
}
|
||||
|
||||
public Settings GetSettings()
|
||||
{
|
||||
return _settings.Clone();
|
||||
}
|
||||
|
||||
public SyncSettings GetSyncSettings()
|
||||
{
|
||||
return _syncSettings.Clone();
|
||||
}
|
||||
|
||||
public PutSettingsDirtyBits PutSettings(Settings o)
|
||||
{
|
||||
var ret = Settings.NeedsReboot(_settings, o);
|
||||
_settings = o;
|
||||
return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None;
|
||||
}
|
||||
|
||||
public PutSettingsDirtyBits PutSyncSettings(SyncSettings o)
|
||||
{
|
||||
var ret = SyncSettings.NeedsReboot(_syncSettings, o);
|
||||
_syncSettings = o;
|
||||
return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None;
|
||||
}
|
||||
|
||||
private static int SanitizeBirthdayDay(int day, SyncSettings.Month fwMonth)
|
||||
{
|
||||
int maxdays;
|
||||
switch (fwMonth)
|
||||
{
|
||||
case SyncSettings.Month.February:
|
||||
{
|
||||
maxdays = 29;
|
||||
break;
|
||||
}
|
||||
case SyncSettings.Month.April:
|
||||
case SyncSettings.Month.June:
|
||||
case SyncSettings.Month.September:
|
||||
case SyncSettings.Month.November:
|
||||
{
|
||||
maxdays = 30;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
maxdays = 31;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Math.Max(1, Math.Min(day, maxdays));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||
{
|
||||
partial class NDS
|
||||
{
|
||||
private ITraceable Tracer { get; }
|
||||
private readonly LibMelonDS.TraceCallback _tracecb;
|
||||
|
||||
private void MakeTrace(LibMelonDS.CpuTypes _cpu, IntPtr _regs, uint _opcode, long _ccoffset)
|
||||
{
|
||||
string cpu = _cpu switch
|
||||
{
|
||||
LibMelonDS.CpuTypes.ARM9 => "ARM9",
|
||||
LibMelonDS.CpuTypes.ARM7 => "ARM7",
|
||||
LibMelonDS.CpuTypes.ARM9_THUMB => "ARM9 (Thumb)",
|
||||
LibMelonDS.CpuTypes.ARM7_THUMB => "ARM7 (Thumb)",
|
||||
_ => throw new InvalidOperationException("Invalid CPU Mode???"),
|
||||
};
|
||||
|
||||
int[] regs = new int[16];
|
||||
Marshal.Copy(_regs, regs, 0, 16);
|
||||
|
||||
bool isthumb = ((uint)_cpu & 2u) == 2u;
|
||||
uint pc = (uint)regs[15] - (isthumb ? 2u : 4u); // handle prefetch
|
||||
|
||||
Tracer.Put(new(
|
||||
disassembly: string.Format("{0:x8}", pc).PadRight(12) + _disassembler.Trace(pc, _opcode, isthumb).PadRight(32),
|
||||
registerInfo: string.Format(
|
||||
"r0:{0:x8} r1:{1:x8} r2:{2:x8} r3:{3:x8} r4:{4:x8} r5:{5:x8} r6:{6:x8} r7:{7:x8} r8:{8:x8} r9:{9:x8} r10:{10:x8} r11:{11:x8} r12:{12:x8} r13:{13:x8} r14:{14:x8} r15:{15:x8} Cy:{16} {17}",
|
||||
(uint)regs[0],
|
||||
(uint)regs[1],
|
||||
(uint)regs[2],
|
||||
(uint)regs[3],
|
||||
(uint)regs[4],
|
||||
(uint)regs[5],
|
||||
(uint)regs[6],
|
||||
(uint)regs[7],
|
||||
(uint)regs[8],
|
||||
(uint)regs[9],
|
||||
(uint)regs[10],
|
||||
(uint)regs[11],
|
||||
(uint)regs[12],
|
||||
(uint)regs[13],
|
||||
(uint)regs[14],
|
||||
(uint)regs[15],
|
||||
CycleCount + _ccoffset,
|
||||
cpu)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,187 +1,289 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.IO;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||
{
|
||||
[PortedCore(CoreNames.MelonDS, "Arisotura", "0.8.2", "http://melonds.kuribo64.net/", singleInstance: true, isReleased: false)]
|
||||
public unsafe partial class MelonDS : IEmulator
|
||||
{
|
||||
private readonly BasicServiceProvider _serviceProvider;
|
||||
public IEmulatorServiceProvider ServiceProvider => _serviceProvider;
|
||||
|
||||
public ControllerDefinition ControllerDefinition { get; }
|
||||
|
||||
public int Frame => GetFrameCount();
|
||||
|
||||
public string SystemId => "NDS";
|
||||
|
||||
public bool DeterministicEmulation => true;
|
||||
|
||||
internal CoreComm CoreComm { get; }
|
||||
|
||||
private bool _disposed;
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
Deinit();
|
||||
_resampler.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool FrameAdvance(IController controller, bool render, bool renderSound = true)
|
||||
{
|
||||
int buttons = (controller.IsPressed("A") ? 1 : 0) | (controller.IsPressed("B") ? 2 : 0)
|
||||
| (controller.IsPressed("Select") ? 4 : 0) | (controller.IsPressed("Start") ? 8 : 0)
|
||||
| (controller.IsPressed("Right") ? 0x10 : 0) | (controller.IsPressed("Left") ? 0x20 : 0)
|
||||
| (controller.IsPressed("Up") ? 0x40 : 0) | (controller.IsPressed("Down") ? 0x80 : 0)
|
||||
| (controller.IsPressed("R") ? 0x100 : 0) | (controller.IsPressed("L") ? 0x200 : 0)
|
||||
| (controller.IsPressed("X") ? 0x400 : 0) | (controller.IsPressed("Y") ? 0x800 : 0)
|
||||
| (controller.IsPressed("Touch") ? 0x2000 : 0)
|
||||
| (controller.IsPressed("LidOpen") ? 0x4000 : 0) | (controller.IsPressed("LidClose") ? 0x8000 : 0)
|
||||
| (controller.IsPressed("Power") ? 0x10000 : 0);
|
||||
FrameAdvance((uint)buttons, (byte)controller.AxisValue("TouchX"), (byte)controller.AxisValue("TouchY"));
|
||||
_getNewBuffer = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ResetCounters()
|
||||
{
|
||||
_ResetCounters();
|
||||
}
|
||||
|
||||
// debug path/build for easier testing
|
||||
//const string dllPath = "../../MelonDS/build/libmelonDS.dll";
|
||||
private const string dllPath = "dll/libmelonDS.dll";
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern bool Init();
|
||||
[DllImport(dllPath)]
|
||||
private static extern void Deinit();
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern void LoadROM(byte* file, int fileSize);
|
||||
|
||||
[DllImport(dllPath, EntryPoint = "ResetCounters")]
|
||||
private static extern void _ResetCounters();
|
||||
[DllImport(dllPath)]
|
||||
private static extern int GetFrameCount();
|
||||
[DllImport(dllPath)]
|
||||
private static extern void SetFrameCount(uint count);
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern void FrameAdvance(uint buttons, byte touchX, byte touchY);
|
||||
|
||||
[CoreConstructor("NDS")]
|
||||
public MelonDS(byte[] file, CoreComm comm, MelonSettings settings, MelonSyncSettings syncSettings)
|
||||
{
|
||||
_serviceProvider = new BasicServiceProvider(this);
|
||||
ControllerDefinition = new ControllerDefinition { Name = "NDS Controller" };
|
||||
ControllerDefinition.BoolButtons.Add("Left");
|
||||
ControllerDefinition.BoolButtons.Add("Right");
|
||||
ControllerDefinition.BoolButtons.Add("Up");
|
||||
ControllerDefinition.BoolButtons.Add("Down");
|
||||
ControllerDefinition.BoolButtons.Add("A");
|
||||
ControllerDefinition.BoolButtons.Add("B");
|
||||
ControllerDefinition.BoolButtons.Add("X");
|
||||
ControllerDefinition.BoolButtons.Add("Y");
|
||||
ControllerDefinition.BoolButtons.Add("L");
|
||||
ControllerDefinition.BoolButtons.Add("R");
|
||||
ControllerDefinition.BoolButtons.Add("Start");
|
||||
ControllerDefinition.BoolButtons.Add("Select");
|
||||
|
||||
ControllerDefinition.BoolButtons.Add("LidOpen");
|
||||
ControllerDefinition.BoolButtons.Add("LidClose");
|
||||
ControllerDefinition.BoolButtons.Add("Power");
|
||||
|
||||
ControllerDefinition.BoolButtons.Add("Touch");
|
||||
ControllerDefinition.AddXYPair("Touch{0}", AxisPairOrientation.RightAndUp, 0.RangeTo(255), 128, 0.RangeTo(191), 96); //TODO verify direction against hardware
|
||||
|
||||
CoreComm = comm;
|
||||
_resampler = new SpeexResampler(SpeexResampler.Quality.QUALITY_DEFAULT, 32768, 44100, 32768, 44100);
|
||||
|
||||
SetUpFiles();
|
||||
|
||||
PutSettings(settings as MelonSettings);
|
||||
PutSyncSettings(syncSettings as MelonSyncSettings);
|
||||
|
||||
if (!Init())
|
||||
throw new Exception("Failed to init NDS.");
|
||||
InitMemoryDomains();
|
||||
|
||||
fixed (byte* f = file)
|
||||
{
|
||||
LoadROM(f, file.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MelonDS expects bios and firmware files at a specific location.
|
||||
/// This should never be called without an accompanying call to PutSyncSettings.
|
||||
/// </summary>
|
||||
private void SetUpFiles()
|
||||
{
|
||||
Directory.CreateDirectory("melon");
|
||||
|
||||
byte[] fwBytes;
|
||||
bool missingAny = false;
|
||||
fwBytes = CoreComm.CoreFileProvider.GetFirmware(new("NDS", "bios7"));
|
||||
if (fwBytes != null)
|
||||
File.WriteAllBytes("melon/bios7.bin", fwBytes);
|
||||
else
|
||||
{
|
||||
File.Delete("melon/bios7.bin");
|
||||
missingAny = true;
|
||||
}
|
||||
|
||||
fwBytes = CoreComm.CoreFileProvider.GetFirmware(new("NDS", "bios9"));
|
||||
if (fwBytes != null)
|
||||
File.WriteAllBytes("melon/bios9.bin", fwBytes);
|
||||
else
|
||||
{
|
||||
File.Delete("melon/bios9.bin");
|
||||
missingAny = true;
|
||||
}
|
||||
|
||||
fwBytes = CoreComm.CoreFileProvider.GetFirmware(new("NDS", "firmware"));
|
||||
if (fwBytes != null)
|
||||
File.WriteAllBytes("melon/firmware.bin", fwBytes);
|
||||
else
|
||||
{
|
||||
File.Delete("melon/firmware.bin");
|
||||
missingAny = true;
|
||||
}
|
||||
|
||||
if (missingAny)
|
||||
CoreComm.Notify("NDS bios and firmware files are recommended; at least one is missing.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a modified copy of the given firmware file, with the user settings erased.
|
||||
/// </summary>
|
||||
/// <returns>Returns a path to the new file.</returns>
|
||||
public static string CreateModifiedFirmware(string firmwarePath)
|
||||
{
|
||||
Directory.CreateDirectory("melon");
|
||||
|
||||
const string newPath = "melon/tohash.bin";
|
||||
byte[] bytes = File.ReadAllBytes(firmwarePath);
|
||||
|
||||
// There are two regions for user settings
|
||||
int settingsLength = GetUserSettingsLength();
|
||||
for (int i = bytes.Length - 0x200; i < bytes.Length - 0x200 + settingsLength; i++)
|
||||
bytes[i] = 0xFF;
|
||||
for (int i = bytes.Length - 0x100; i < bytes.Length - 0x100 + settingsLength; i++)
|
||||
bytes[i] = 0xFF;
|
||||
|
||||
|
||||
File.WriteAllBytes(newPath, bytes);
|
||||
return newPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Waterbox;
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||
{
|
||||
[PortedCore(CoreNames.MelonDS, "Arisotura", "0.9.3", "http://melonds.kuribo64.net/", singleInstance: true, isReleased: false)]
|
||||
[ServiceNotApplicable(new[] { typeof(IDriveLight), typeof(IRegionable) })]
|
||||
public partial class NDS : WaterboxCore
|
||||
{
|
||||
private readonly LibMelonDS _core;
|
||||
private readonly NDSDisassembler _disassembler;
|
||||
private SpeexResampler _resampler;
|
||||
|
||||
[CoreConstructor("NDS")]
|
||||
public NDS(CoreLoadParameters<Settings, SyncSettings> lp)
|
||||
: base(lp.Comm, new Configuration
|
||||
{
|
||||
DefaultWidth = 256,
|
||||
DefaultHeight = 384,
|
||||
MaxWidth = 256,
|
||||
MaxHeight = 384,
|
||||
MaxSamples = 1024,
|
||||
DefaultFpsNumerator = 33513982,
|
||||
DefaultFpsDenominator = 560190,
|
||||
SystemId = "NDS"
|
||||
})
|
||||
{
|
||||
var roms = lp.Roms.Select(r => r.RomData).ToList();
|
||||
|
||||
if (roms.Count > 3)
|
||||
{
|
||||
throw new InvalidOperationException("Wrong number of ROMs!");
|
||||
}
|
||||
|
||||
bool gbacartpresent = roms.Count > 1;
|
||||
|
||||
_tracecb = MakeTrace;
|
||||
|
||||
_core = PreInit<LibMelonDS>(new WaterboxOptions
|
||||
{
|
||||
Filename = "melonDS.wbx",
|
||||
SbrkHeapSizeKB = 2 * 1024,
|
||||
SealedHeapSizeKB = 4,
|
||||
InvisibleHeapSizeKB = 4,
|
||||
PlainHeapSizeKB = 4,
|
||||
MmapHeapSizeKB = 512 * 1024,
|
||||
SkipCoreConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck),
|
||||
SkipMemoryConsistencyCheck = CoreComm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck),
|
||||
}, new Delegate[] { _tracecb });
|
||||
|
||||
_syncSettings = lp.SyncSettings ?? new SyncSettings();
|
||||
_settings = lp.Settings ?? new Settings();
|
||||
|
||||
var bios7 = _syncSettings.UseRealBIOS
|
||||
? CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios7"))
|
||||
: null;
|
||||
|
||||
var bios9 = _syncSettings.UseRealBIOS
|
||||
? CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios9"))
|
||||
: null;
|
||||
|
||||
var fw = CoreComm.CoreFileProvider.GetFirmware(new("NDS", "firmware"));
|
||||
|
||||
bool skipfw = _syncSettings.SkipFirmware || !_syncSettings.UseRealBIOS || fw == null;
|
||||
|
||||
LibMelonDS.LoadFlags flags = LibMelonDS.LoadFlags.NONE;
|
||||
|
||||
if (_syncSettings.UseRealBIOS)
|
||||
flags |= LibMelonDS.LoadFlags.USE_REAL_BIOS;
|
||||
if (skipfw)
|
||||
flags |= LibMelonDS.LoadFlags.SKIP_FIRMWARE;
|
||||
if (gbacartpresent)
|
||||
flags |= LibMelonDS.LoadFlags.GBA_CART_PRESENT;
|
||||
if (_settings.AccurateAudioBitrate)
|
||||
flags |= LibMelonDS.LoadFlags.ACCURATE_AUDIO_BITRATE;
|
||||
if (_syncSettings.FirmwareOverride || lp.DeterministicEmulationRequested)
|
||||
flags |= LibMelonDS.LoadFlags.FIRMWARE_OVERRIDE;
|
||||
|
||||
var fwSettings = new LibMelonDS.FirmwareSettings();
|
||||
var name = Encoding.UTF8.GetBytes(_syncSettings.FirmwareUsername);
|
||||
fwSettings.FirmwareUsernameLength = name.Length;
|
||||
fwSettings.FirmwareLanguage = _syncSettings.FirmwareLanguage;
|
||||
if (_syncSettings.FirmwareStartUp == SyncSettings.StartUp.AutoBoot) fwSettings.FirmwareLanguage |= (SyncSettings.Language)0x40;
|
||||
fwSettings.FirmwareBirthdayMonth = _syncSettings.FirmwareBirthdayMonth;
|
||||
fwSettings.FirmwareBirthdayDay = _syncSettings.FirmwareBirthdayDay;
|
||||
fwSettings.FirmwareFavouriteColour = _syncSettings.FirmwareFavouriteColour;
|
||||
var message = Encoding.UTF8.GetBytes(_syncSettings.FirmwareMessage);
|
||||
fwSettings.FirmwareMessageLength = message.Length;
|
||||
|
||||
_exe.AddReadonlyFile(roms[0], "game.rom");
|
||||
if (gbacartpresent)
|
||||
{
|
||||
_exe.AddReadonlyFile(roms[1], "gba.rom");
|
||||
if (roms[2] != null)
|
||||
{
|
||||
_exe.AddReadonlyFile(roms[2], "gba.ram");
|
||||
}
|
||||
}
|
||||
if (_syncSettings.UseRealBIOS)
|
||||
{
|
||||
_exe.AddReadonlyFile(bios7, "bios7.rom");
|
||||
_exe.AddReadonlyFile(bios9, "bios9.rom");
|
||||
}
|
||||
if (fw != null)
|
||||
{
|
||||
if (NDSFirmware.MaybeWarnIfBadFw(fw, CoreComm))
|
||||
{
|
||||
if (_syncSettings.FirmwareOverride || lp.DeterministicEmulationRequested)
|
||||
{
|
||||
NDSFirmware.SanitizeFw(fw);
|
||||
}
|
||||
}
|
||||
_exe.AddReadonlyFile(fw, "firmware.bin");
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* namePtr = &name[0], messagePtr = &message[0])
|
||||
{
|
||||
fwSettings.FirmwareUsername = (IntPtr)namePtr;
|
||||
fwSettings.FirmwareMessage = (IntPtr)messagePtr;
|
||||
if (!_core.Init(flags, fwSettings))
|
||||
{
|
||||
throw new InvalidOperationException("Init returned false!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_exe.RemoveReadonlyFile("game.rom");
|
||||
if (gbacartpresent)
|
||||
{
|
||||
_exe.RemoveReadonlyFile("gba.rom");
|
||||
if (roms[2] != null)
|
||||
{
|
||||
_exe.RemoveReadonlyFile("gba.ram");
|
||||
}
|
||||
}
|
||||
if (_syncSettings.UseRealBIOS)
|
||||
{
|
||||
_exe.RemoveReadonlyFile("bios7.rom");
|
||||
_exe.RemoveReadonlyFile("bios9.rom");
|
||||
}
|
||||
if (fw != null)
|
||||
{
|
||||
_exe.RemoveReadonlyFile("firmware.bin");
|
||||
}
|
||||
|
||||
PostInit();
|
||||
|
||||
((MemoryDomainList)this.AsMemoryDomains()).SystemBus = new NDSSystemBus(this.AsMemoryDomains()["ARM9 System Bus"], this.AsMemoryDomains()["ARM7 System Bus"]);
|
||||
|
||||
DeterministicEmulation = lp.DeterministicEmulationRequested || (!_syncSettings.UseRealTime);
|
||||
InitializeRtc(_syncSettings.InitialTime);
|
||||
|
||||
_resampler = new SpeexResampler(SpeexResampler.Quality.QUALITY_DEFAULT, 32768, 44100, 32768, 44100, null, this);
|
||||
_serviceProvider.Register<ISoundProvider>(_resampler);
|
||||
|
||||
_disassembler = new NDSDisassembler();
|
||||
_serviceProvider.Register<IDisassemblable>(_disassembler);
|
||||
|
||||
const string TRACE_HEADER = "ARM9+ARM7: PC, opcode, registers (r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, Cy, CpuMode)";
|
||||
Tracer = new TraceBuffer(TRACE_HEADER);
|
||||
_serviceProvider.Register<ITraceable>(Tracer);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
if (_resampler != null)
|
||||
{
|
||||
_resampler.Dispose();
|
||||
_resampler = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override ControllerDefinition ControllerDefinition => NDSController;
|
||||
|
||||
public static readonly ControllerDefinition NDSController = new ControllerDefinition
|
||||
{
|
||||
Name = "NDS Controller",
|
||||
BoolButtons =
|
||||
{
|
||||
"Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Y", "X", "L", "R", "LidOpen", "LidClose", "Touch", "Power"
|
||||
}
|
||||
}.AddXYPair("Touch {0}", AxisPairOrientation.RightAndUp, 0.RangeTo(255), 128, 0.RangeTo(191), 96)
|
||||
.AddAxis("Mic Input", (-1).RangeTo(2047), 0)
|
||||
.AddAxis("GBA Light Sensor", 0.RangeTo(10), 0);
|
||||
private LibMelonDS.Buttons GetButtons(IController c)
|
||||
{
|
||||
LibMelonDS.Buttons b = 0;
|
||||
if (c.IsPressed("Up"))
|
||||
b |= LibMelonDS.Buttons.UP;
|
||||
if (c.IsPressed("Down"))
|
||||
b |= LibMelonDS.Buttons.DOWN;
|
||||
if (c.IsPressed("Left"))
|
||||
b |= LibMelonDS.Buttons.LEFT;
|
||||
if (c.IsPressed("Right"))
|
||||
b |= LibMelonDS.Buttons.RIGHT;
|
||||
if (c.IsPressed("Start"))
|
||||
b |= LibMelonDS.Buttons.START;
|
||||
if (c.IsPressed("Select"))
|
||||
b |= LibMelonDS.Buttons.SELECT;
|
||||
if (c.IsPressed("B"))
|
||||
b |= LibMelonDS.Buttons.B;
|
||||
if (c.IsPressed("A"))
|
||||
b |= LibMelonDS.Buttons.A;
|
||||
if (c.IsPressed("Y"))
|
||||
b |= LibMelonDS.Buttons.Y;
|
||||
if (c.IsPressed("X"))
|
||||
b |= LibMelonDS.Buttons.X;
|
||||
if (c.IsPressed("L"))
|
||||
b |= LibMelonDS.Buttons.L;
|
||||
if (c.IsPressed("R"))
|
||||
b |= LibMelonDS.Buttons.R;
|
||||
if (c.IsPressed("LidOpen"))
|
||||
b |= LibMelonDS.Buttons.LIDOPEN;
|
||||
if (c.IsPressed("LidClose"))
|
||||
b |= LibMelonDS.Buttons.LIDCLOSE;
|
||||
if (c.IsPressed("Touch"))
|
||||
b |= LibMelonDS.Buttons.TOUCH;
|
||||
if (c.IsPressed("Power"))
|
||||
b |= LibMelonDS.Buttons.POWER;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
private bool _renderSound;
|
||||
|
||||
protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound)
|
||||
{
|
||||
_renderSound = rendersound;
|
||||
_core.SetTraceCallback(Tracer.IsEnabled() ? _tracecb : null);
|
||||
return new LibMelonDS.FrameInfo
|
||||
{
|
||||
Time = GetRtcTime(!DeterministicEmulation),
|
||||
Keys = GetButtons(controller),
|
||||
TouchX = (byte)controller.AxisValue("Touch X"),
|
||||
TouchY = (byte)controller.AxisValue("Touch Y"),
|
||||
MicInput = (short)controller.AxisValue("Mic Input"),
|
||||
GBALightSensor = (byte)controller.AxisValue("GBA Light Sensor"),
|
||||
};
|
||||
}
|
||||
|
||||
protected override void FrameAdvancePost()
|
||||
{
|
||||
// the core SHOULD produce 547 or 548 samples each frame
|
||||
// however, it seems in some cases (first few frames on power on and lid closed) it doesn't for some reason
|
||||
// hack around it here
|
||||
if (_numSamples < 547 && _renderSound)
|
||||
{
|
||||
for (int i = _numSamples * 2; i < (547 * 2); i++)
|
||||
{
|
||||
_soundBuffer[i] = 0;
|
||||
}
|
||||
_numSamples = 547;
|
||||
}
|
||||
}
|
||||
|
||||
// omega hack
|
||||
public class NDSSystemBus : MemoryDomain
|
||||
{
|
||||
private readonly MemoryDomain Arm9Bus;
|
||||
private readonly MemoryDomain Arm7Bus;
|
||||
|
||||
public NDSSystemBus(MemoryDomain arm9, MemoryDomain arm7)
|
||||
{
|
||||
Name = "System Bus";
|
||||
Size = 1L << 32;
|
||||
WordSize = 4;
|
||||
EndianType = Endian.Little;
|
||||
Writable = false;
|
||||
|
||||
Arm9Bus = arm9;
|
||||
Arm7Bus = arm7;
|
||||
}
|
||||
|
||||
public bool UseArm9 { get; set; } = true;
|
||||
|
||||
public override byte PeekByte(long addr) => UseArm9 ? Arm9Bus.PeekByte(addr) : Arm7Bus.PeekByte(addr);
|
||||
|
||||
public override void PokeByte(long addr, byte val) => throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||
{
|
||||
partial class MelonDS : IInputPollable
|
||||
{
|
||||
public int LagCount
|
||||
{
|
||||
get => GetLagFrameCount();
|
||||
set => SetLagFrameCount((uint)value);
|
||||
}
|
||||
|
||||
public bool IsLagFrame
|
||||
{
|
||||
get => _IsLagFrame();
|
||||
set => SetIsLagFrame(value);
|
||||
}
|
||||
|
||||
public IInputCallbackSystem InputCallbacks => throw new NotImplementedException();
|
||||
|
||||
[DllImport(dllPath, EntryPoint = "IsLagFrame")]
|
||||
private static extern bool _IsLagFrame();
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern int GetLagFrameCount();
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern void SetIsLagFrame(bool isLag);
|
||||
[DllImport(dllPath)]
|
||||
private static extern void SetLagFrameCount(uint count);
|
||||
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||
{
|
||||
unsafe partial class MelonDS : IMemoryDomains
|
||||
{
|
||||
private SortedList<string, MemoryDomain> domains;
|
||||
|
||||
private void InitMemoryDomains()
|
||||
{
|
||||
MainMemory = new MemoryDomainIntPtr("RAM", MemoryDomain.Endian.Little, (IntPtr)GetMainMemory(), GetMainMemorySize(), true, 4);
|
||||
SystemBus = new MelonSystemBus();
|
||||
|
||||
domains = new SortedList<string, MemoryDomain>
|
||||
{
|
||||
{ "RAM", MainMemory },
|
||||
{ "System Bus", SystemBus }
|
||||
};
|
||||
}
|
||||
|
||||
public MemoryDomain this[string name] => domains[name];
|
||||
public bool Has(string name)
|
||||
{
|
||||
return domains.ContainsKey(name);
|
||||
}
|
||||
|
||||
public MemoryDomain MainMemory { get; private set; }
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern byte* GetMainMemory();
|
||||
[DllImport(dllPath)]
|
||||
private static extern int GetMainMemorySize();
|
||||
|
||||
// NDS has two CPUs, with different memory mappings.
|
||||
public bool HasSystemBus => true;
|
||||
public MemoryDomain SystemBus { get; private set; }
|
||||
private class MelonSystemBus : MemoryDomain
|
||||
{
|
||||
[DllImport(dllPath)]
|
||||
private static extern byte ARM9Read8(uint addr);
|
||||
[DllImport(dllPath)]
|
||||
private static extern void ARM9Write8(uint addr, byte value);
|
||||
[DllImport(dllPath)]
|
||||
private static extern ushort ARM9Read16(uint addr);
|
||||
[DllImport(dllPath)]
|
||||
private static extern void ARM9Write16(uint addr, ushort value);
|
||||
[DllImport(dllPath)]
|
||||
private static extern uint ARM9Read32(uint addr);
|
||||
[DllImport(dllPath)]
|
||||
private static extern void ARM9Write32(uint addr, uint value);
|
||||
|
||||
public MelonSystemBus()
|
||||
{
|
||||
Name = "System Bus";
|
||||
Size = 0x0B00_0000;
|
||||
WordSize = 4;
|
||||
EndianType = Endian.Little;
|
||||
Writable = true;
|
||||
}
|
||||
|
||||
public override byte PeekByte(long addr)
|
||||
{
|
||||
return ARM9Read8((uint)addr);
|
||||
}
|
||||
public override void PokeByte(long addr, byte val)
|
||||
{
|
||||
ARM9Write8((uint)addr, val);
|
||||
}
|
||||
public override ushort PeekUshort(long addr, bool bigEndian)
|
||||
{
|
||||
ushort ret = ARM9Read16((uint)addr);
|
||||
if (bigEndian)
|
||||
ret = SwapEndianness(ret);
|
||||
return ret;
|
||||
}
|
||||
public override void PokeUshort(long addr, ushort val, bool bigEndian)
|
||||
{
|
||||
if (bigEndian)
|
||||
val = SwapEndianness(val);
|
||||
ARM9Write16((uint)addr, val);
|
||||
}
|
||||
|
||||
public override uint PeekUint(long addr, bool bigEndian)
|
||||
{
|
||||
uint ret = ARM9Read32((uint)addr);
|
||||
if (bigEndian)
|
||||
ret = SwapEndianness(ret);
|
||||
return ret;
|
||||
|
||||
}
|
||||
public override void PokeUint(long addr, uint val, bool bigEndian)
|
||||
{
|
||||
if (bigEndian)
|
||||
val = SwapEndianness(val);
|
||||
ARM9Write32((uint)addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<MemoryDomain> GetEnumerator()
|
||||
{
|
||||
return domains.Values.GetEnumerator();
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
|
||||
|
||||
public static ushort SwapEndianness(ushort value)
|
||||
{
|
||||
return (ushort)((value >> 8) | (value << 8));
|
||||
}
|
||||
|
||||
public static uint SwapEndianness(uint value)
|
||||
{
|
||||
return (value >> 24) | ((value & 0x00ff0000) >> 8) | ((value & 0x0000ff00) << 8) | (value << 24);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||
{
|
||||
unsafe partial class MelonDS : ISaveRam
|
||||
{
|
||||
public bool SaveRamModified => IsSRAMModified();
|
||||
|
||||
public byte[] CloneSaveRam()
|
||||
{
|
||||
int length = GetSRAMLength();
|
||||
byte[] data = new byte[length];
|
||||
fixed (byte* dst = data)
|
||||
{
|
||||
GetSRAM(dst, length);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public void StoreSaveRam(byte[] data)
|
||||
{
|
||||
fixed (byte* src = data)
|
||||
{
|
||||
SetSRAM(src, data.Length);
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern int GetSRAMLength();
|
||||
[DllImport(dllPath)]
|
||||
private static extern bool IsSRAMModified();
|
||||
[DllImport(dllPath)]
|
||||
private static extern void GetSRAM(byte* dst, int length);
|
||||
[DllImport(dllPath)]
|
||||
private static extern void SetSRAM(byte* src, int length);
|
||||
}
|
||||
}
|
|
@ -1,199 +0,0 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||
{
|
||||
unsafe partial class MelonDS : ISettable<MelonDS.MelonSettings, MelonDS.MelonSyncSettings>
|
||||
{
|
||||
private MelonSettings _settings = new MelonSettings();
|
||||
|
||||
public MelonSettings GetSettings() => _settings.Clone();
|
||||
|
||||
public MelonSyncSettings GetSyncSettings()
|
||||
{
|
||||
var ret = new MelonSyncSettings();
|
||||
fixed (byte* ptr = ret.UserSettings)
|
||||
{
|
||||
if (!GetUserSettings(ptr))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
ret.BootToFirmware = !GetDirectBoot();
|
||||
ret.TimeAtBoot = GetTimeAtBoot();
|
||||
return ret;
|
||||
}
|
||||
|
||||
public PutSettingsDirtyBits PutSettings(MelonSettings o)
|
||||
{
|
||||
bool screenChanged = false;
|
||||
if (o != null)
|
||||
{
|
||||
screenChanged |= _settings.ScaleFactor != o.ScaleFactor;
|
||||
screenChanged |= _settings.ScreenGap != o.ScreenGap;
|
||||
screenChanged |= _settings.ScreenLayout != o.ScreenLayout;
|
||||
screenChanged |= _settings.ScreenRotation != o.ScreenRotation;
|
||||
}
|
||||
_settings = o ?? new MelonSettings();
|
||||
SetScaleFactor(_settings.ScaleFactor);
|
||||
return screenChanged ? PutSettingsDirtyBits.None : PutSettingsDirtyBits.None;
|
||||
}
|
||||
|
||||
public PutSettingsDirtyBits PutSyncSettings(MelonSyncSettings o)
|
||||
{
|
||||
if (o == null)
|
||||
{
|
||||
o = new MelonSyncSettings();
|
||||
SetUserSettings(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed (byte* ptr = o.UserSettings)
|
||||
SetUserSettings(ptr);
|
||||
}
|
||||
|
||||
SetDirectBoot(!o.BootToFirmware);
|
||||
SetTimeAtBoot(o.TimeAtBoot);
|
||||
|
||||
// At present, no sync settings can be modified without requiring a reboot.
|
||||
return PutSettingsDirtyBits.RebootCore;
|
||||
}
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern bool GetUserSettings(byte* dst);
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern int GetUserSettingsLength();
|
||||
|
||||
private static readonly int UserSettingsLength = GetUserSettingsLength();
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern void SetUserSettings(byte* src);
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern bool GetDirectBoot();
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern void SetDirectBoot(bool value);
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern void SetTimeAtBoot(uint value);
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern uint GetTimeAtBoot();
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern uint GetScaleFactor();
|
||||
[DllImport(dllPath)]
|
||||
private static extern void SetScaleFactor(uint value);
|
||||
|
||||
public enum ScreenLayoutKind
|
||||
{
|
||||
Vertical, Horizontal, Top, Bottom
|
||||
}
|
||||
|
||||
public enum ScreenRotationKind
|
||||
{
|
||||
Rotate0, Rotate90, Rotate180, Rotate270
|
||||
}
|
||||
|
||||
public class MelonSettings
|
||||
{
|
||||
public MelonSettings Clone() => (MelonSettings)MemberwiseClone();
|
||||
|
||||
[DisplayName("Screen Layout")]
|
||||
[Description("Adjusts the layout of the screens")]
|
||||
public ScreenLayoutKind ScreenLayout { get; set; } = ScreenLayoutKind.Vertical;
|
||||
|
||||
[DisplayName("Rotation")]
|
||||
[Description("Adjusts the orientation of the screens")]
|
||||
public ScreenRotationKind ScreenRotation { get; set; } = ScreenRotationKind.Rotate0;
|
||||
|
||||
[DisplayName("Screen Gap")]
|
||||
public int ScreenGap { get; set; }
|
||||
|
||||
[DisplayName("Scale Factor")]
|
||||
public uint ScaleFactor { get; set; } = 1;
|
||||
}
|
||||
|
||||
public class MelonSyncSettings
|
||||
{
|
||||
public MelonSyncSettings()
|
||||
{
|
||||
UserSettings = new byte[UserSettingsLength];
|
||||
}
|
||||
|
||||
public MelonSyncSettings Clone() => (MelonSyncSettings)MemberwiseClone();
|
||||
|
||||
public bool BootToFirmware { get; set; }
|
||||
public uint TimeAtBoot { get; set; } = 946684800; // 2000-01-01 00:00:00 (earliest date possible on a DS)
|
||||
|
||||
[JsonProperty]
|
||||
internal byte[] UserSettings;
|
||||
|
||||
[JsonIgnore]
|
||||
public byte FavoriteColor
|
||||
{
|
||||
get => UserSettings[2];
|
||||
set => UserSettings[2] = value;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public byte BirthdayMonth
|
||||
{
|
||||
get => UserSettings[3];
|
||||
set => UserSettings[3] = value;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public byte BirthdayDay
|
||||
{
|
||||
get => UserSettings[4];
|
||||
set => UserSettings[4] = value;
|
||||
}
|
||||
|
||||
private const int MaxNicknameLength = 10;
|
||||
|
||||
[JsonIgnore]
|
||||
public string Nickname
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed (byte* ptr = UserSettings)
|
||||
return Encoding.Unicode.GetString(ptr + 6, NicknameLength * 2);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value.Length > MaxNicknameLength) value = value.Substring(0, MaxNicknameLength);
|
||||
byte[] nick = new byte[MaxNicknameLength * 2 + 2];
|
||||
// I do not know how an actual NDS would handle characters that require more than 2 bytes to encode.
|
||||
// They can't be input normally, so I will ignore attempts to set a nickname that uses them.
|
||||
if (Encoding.Unicode.GetBytes(value, 0, value.Length, nick, 0) != value.Length * 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The extra 2 bytes on the end will overwrite nickname length, which is set immediately after
|
||||
nick.CopyTo(UserSettings, 6);
|
||||
UserSettings[0x1A] = (byte)value.Length;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public short NicknameLength => UserSettings[0x1A];
|
||||
|
||||
[JsonIgnore]
|
||||
public long RtcOffset
|
||||
{
|
||||
get => BitConverter.ToInt64(UserSettings, 0x68);
|
||||
set => BitConverter.GetBytes(value).CopyTo(UserSettings, 0x68);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||
{
|
||||
unsafe partial class MelonDS : ISoundProvider
|
||||
{
|
||||
private readonly SpeexResampler _resampler;
|
||||
|
||||
public bool CanProvideAsync => false;
|
||||
|
||||
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
_DiscardSamples();
|
||||
}
|
||||
|
||||
public void GetSamplesAsync(short[] samples)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
public void GetSamplesSync(out short[] samples, out int nsamp)
|
||||
{
|
||||
nsamp = GetSampleCount();
|
||||
samples = new short[nsamp * 2]; //*2 for stereo sound
|
||||
fixed (short* data = samples)
|
||||
{
|
||||
GetSamples(data, nsamp);
|
||||
}
|
||||
// BizHawk requires a sample rate of 44100 Hz.
|
||||
_resampler.EnqueueSamples(samples, nsamp);
|
||||
_resampler.GetSamplesSync(out samples, out nsamp);
|
||||
}
|
||||
|
||||
public void SetSyncMode(SyncSoundMode mode)
|
||||
{
|
||||
if (mode == SyncSoundMode.Async)
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern int GetSampleCount();
|
||||
[DllImport(dllPath)]
|
||||
private static extern void GetSamples(short* data, int count);
|
||||
[DllImport(dllPath, EntryPoint = "DiscardSamples")]
|
||||
private static extern void _DiscardSamples();
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||
{
|
||||
unsafe partial class MelonDS : IStatable
|
||||
{
|
||||
private byte[] _stateBuffer = new byte[0];
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
var mStream = new MemoryStream();
|
||||
reader.BaseStream.CopyTo(mStream);
|
||||
|
||||
LoadStateByteArray(mStream.GetBuffer(), (int)mStream.Length);
|
||||
}
|
||||
|
||||
private void LoadStateByteArray(byte[] data, int length = -1)
|
||||
{
|
||||
if (length == -1) length = data.Length;
|
||||
fixed (byte* ptr = data)
|
||||
{
|
||||
if (!UseSavestate(ptr, length))
|
||||
CoreComm.Notify("Savestate load failed! See log window for details.");
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
int len = GetSavestateSize();
|
||||
if (len > _stateBuffer.Length)
|
||||
{
|
||||
_stateBuffer = new byte[len];
|
||||
}
|
||||
|
||||
fixed (byte* ptr = _stateBuffer)
|
||||
{
|
||||
GetSavestateData(ptr, len);
|
||||
}
|
||||
|
||||
writer.Write(_stateBuffer, 0, len);
|
||||
}
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern bool UseSavestate(byte* data, int len);
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern int GetSavestateSize();
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern void GetSavestateData(byte* data, int size);
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||
{
|
||||
unsafe partial class MelonDS : IVideoProvider
|
||||
{
|
||||
public int VirtualWidth => 256;
|
||||
public int VirtualHeight => 384;
|
||||
|
||||
public int BufferWidth => 256;
|
||||
public int BufferHeight => 384;
|
||||
|
||||
public int VsyncNumerator => 60;
|
||||
|
||||
public int VsyncDenominator => 1;
|
||||
|
||||
public int BackgroundColor => 0;
|
||||
|
||||
[DllImport(dllPath)]
|
||||
private static extern int* GetTopScreenBuffer();
|
||||
[DllImport(dllPath)]
|
||||
private static extern int* GetBottomScreenBuffer();
|
||||
|
||||
// BizHawk needs to be able to modify the buffer when loading savestates.
|
||||
private const int SingleScreenLength = 256 * 192;
|
||||
private readonly int[] _buffer = new int[256 * 192 * 2];
|
||||
private bool _getNewBuffer = true;
|
||||
|
||||
public int[] GetVideoBuffer()
|
||||
{
|
||||
if (_getNewBuffer)
|
||||
{
|
||||
_getNewBuffer = false;
|
||||
PopulateBuffer();
|
||||
}
|
||||
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
private void PopulateBuffer()
|
||||
{
|
||||
var top = GetTopScreenBuffer();
|
||||
var bottom = GetBottomScreenBuffer();
|
||||
|
||||
for (var i = 0; i < SingleScreenLength; i++)
|
||||
{
|
||||
_buffer[i] = top[i];
|
||||
_buffer[SingleScreenLength + i] = bottom[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.BizInvoke;
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Components.ARM;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||
{
|
||||
public class NDSDisassembler : VerifiedDisassembler
|
||||
{
|
||||
private static readonly Darm _libdarm = BizInvoker.GetInvoker<Darm>(
|
||||
new DynamicLibraryImportResolver(OSTailoredCode.IsUnixHost ? "libdarm.so" : "libdarm.dll", hasLimitedLifetime: false),
|
||||
CallingConventionAdapters.Native
|
||||
);
|
||||
|
||||
public override IEnumerable<string> AvailableCpus => new[]
|
||||
{
|
||||
"ARM v5",
|
||||
"ARM v5 (Thumb)",
|
||||
"ARM v4",
|
||||
"ARM v4 (Thumb)",
|
||||
};
|
||||
|
||||
public override string PCRegisterName => int.Parse(Cpu.Substring(5, 1)) == 5 ? "ARM9 r15" : "ARM7 r15";
|
||||
|
||||
public override string Disassemble(MemoryDomain m, uint addr, out int length)
|
||||
{
|
||||
if (m is NDS.NDSSystemBus NdsSysBus)
|
||||
{
|
||||
NdsSysBus.UseArm9 = int.Parse(Cpu.Substring(5, 1)) == 5;
|
||||
m = NdsSysBus;
|
||||
}
|
||||
|
||||
if (Cpu.Length == 14)
|
||||
{
|
||||
addr &= ~1u;
|
||||
int op = m.PeekByte(addr) | m.PeekByte(addr + 1) << 8;
|
||||
string ret = _libdarm.DisassembleStuff(addr | 1, (uint)op);
|
||||
length = 2;
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr &= ~3u;
|
||||
int op = m.PeekByte(addr)
|
||||
| m.PeekByte(addr + 1) << 8
|
||||
| m.PeekByte(addr + 2) << 16
|
||||
| m.PeekByte(addr + 3) << 24;
|
||||
string ret = _libdarm.DisassembleStuff(addr, (uint)op);
|
||||
length = 4;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public string Trace(uint pc, uint op, bool isthumb)
|
||||
{
|
||||
if (isthumb)
|
||||
{
|
||||
pc &= ~1u;
|
||||
string ret = _libdarm.DisassembleStuff(pc | 1, op);
|
||||
if (ret == null)
|
||||
{
|
||||
ret = "Can't disassemble???";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
pc &= ~3u;
|
||||
string ret = _libdarm.DisassembleStuff(pc, op);
|
||||
if (ret == null)
|
||||
{
|
||||
ret = "Can't disassemble???";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
using BizHawk.Common.BufferExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||
{
|
||||
// mostly a c++ -> c# port of melon's firmware verification code
|
||||
public class NDSFirmware
|
||||
{
|
||||
public static bool MaybeWarnIfBadFw(byte[] fw, CoreComm comm)
|
||||
{
|
||||
if (fw.Length != 0x20000 && fw.Length != 0x40000 && fw.Length != 0x80000)
|
||||
{
|
||||
comm.ShowMessage("Bad firmware length detected! Firmware might not work!");
|
||||
return false;
|
||||
}
|
||||
if (fw[0x17C] != 0xFF)
|
||||
{
|
||||
comm.ShowMessage("Hacked firmware detected! Firmware might not work!");
|
||||
return false;
|
||||
}
|
||||
int fwMask = fw.Length - 1;
|
||||
string badCrc16s = "";
|
||||
if (!VerifyCrc16(fw, 0x2C, (fw[0x2C + 1] << 8) | fw[0x2C], 0x0000, 0x2A))
|
||||
{
|
||||
badCrc16s += " Wifi ";
|
||||
}
|
||||
if (!VerifyCrc16(fw, 0x7FA00 & fwMask, 0xFE, 0x0000, 0x7FAFE & fwMask))
|
||||
{
|
||||
badCrc16s += " AP1 ";
|
||||
}
|
||||
if (!VerifyCrc16(fw, 0x7FB00 & fwMask, 0xFE, 0x0000, 0x7FBFE & fwMask))
|
||||
{
|
||||
badCrc16s += " AP2 ";
|
||||
}
|
||||
if (!VerifyCrc16(fw, 0x7FC00 & fwMask, 0xFE, 0x0000, 0x7FCFE & fwMask))
|
||||
{
|
||||
badCrc16s += " AP3 ";
|
||||
}
|
||||
if (!VerifyCrc16(fw, 0x7FE00 & fwMask, 0x70, 0xFFFF, 0x7FE72 & fwMask))
|
||||
{
|
||||
badCrc16s += " USER0 ";
|
||||
}
|
||||
if (!VerifyCrc16(fw, 0x7FF00 & fwMask, 0x70, 0xFFFF, 0x7FF72 & fwMask))
|
||||
{
|
||||
badCrc16s += " USER1 ";
|
||||
}
|
||||
if (badCrc16s != "")
|
||||
{
|
||||
comm.ShowMessage("Bad Firmware CRC16(s) detected! Firmware might not work! Bad CRC16(s): " + badCrc16s);
|
||||
return false;
|
||||
}
|
||||
|
||||
return CheckDecryptedCodeChecksum(fw, comm);
|
||||
}
|
||||
|
||||
public static void SanitizeFw(byte[] fw)
|
||||
{
|
||||
int fwMask = fw.Length - 1;
|
||||
int[] apstart = new int[3] { 0x07FA00 & fwMask, 0x07FB00 & fwMask, 0x07FC00 & fwMask };
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
for (int j = 0; j < 0x100; j++)
|
||||
{
|
||||
fw[apstart[i] + j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// gbatek marks these as unknown, they seem to depend on the mac address???
|
||||
// bytes 4 (upper nibble only) and 5 also seem to be just random?
|
||||
// various combinations noted (noting last 2 bytes are crc16)
|
||||
// F8 98 C1 E6 CC DD A9 E1 85 D4 9B
|
||||
// F8 98 C1 E6 CC 1D 66 E1 85 D8 A4
|
||||
// F8 98 C1 E6 CC 9D 6B E1 85 60 A7
|
||||
// F8 98 C1 E6 CC 5D 92 E1 85 8C 96
|
||||
// different mac address
|
||||
// 18 90 15 E9 7C 1D F1 E1 85 74 02
|
||||
byte[] macdependentbytes = new byte[11] { 0xF8, 0x98, 0xC1, 0xE6, 0xCC, 0x9D, 0xBE, 0xE1, 0x85, 0x71, 0x5F };
|
||||
|
||||
int apoffset = 0xF5;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
for (int j = 0; j < 11; j++)
|
||||
{
|
||||
fw[apstart[i] + apoffset + j] = macdependentbytes[j];
|
||||
}
|
||||
}
|
||||
|
||||
int ffoffset = 0xE7;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
fw[apstart[i] + ffoffset] = 0xFF;
|
||||
}
|
||||
|
||||
// slot 3 doesn't have those mac dependent bytes???
|
||||
fw[apstart[2] + 0xFE] = 0x0A;
|
||||
fw[apstart[2] + 0xFF] = 0xF0;
|
||||
|
||||
int[] usersettings = new int[2] { 0x7FE00 & fwMask, 0x7FF00 & fwMask };
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* us = &fw[usersettings[i]])
|
||||
{
|
||||
// alarm settings
|
||||
us[0x52] = 0;
|
||||
us[0x53] = 0;
|
||||
us[0x56] = 0;
|
||||
// year of first boot
|
||||
us[0x66] = 0;
|
||||
// rtc offset
|
||||
us[0x68] = 0;
|
||||
us[0x69] = 0;
|
||||
us[0x6A] = 0;
|
||||
us[0x6B] = 0;
|
||||
// update counter
|
||||
us[0x70] = 0;
|
||||
us[0x71] = 0;
|
||||
// fix crc16 (probably redundant)
|
||||
ushort crc16 = Crc16(us, 0x70, 0xFFFF);
|
||||
us[0x72] = (byte)(crc16 & 0xFF);
|
||||
us[0x73] = (byte)(crc16 >> 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe ushort Crc16(byte* data, int len, int seed)
|
||||
{
|
||||
var poly = new ushort[8] { 0xC0C1, 0xC181, 0xC301, 0xC601, 0xCC01, 0xD801, 0xF001, 0xA001 };
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
seed ^= data[i];
|
||||
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
if ((seed & 0x1) != 0)
|
||||
{
|
||||
seed >>= 1;
|
||||
seed ^= (poly[j] << (7 - j));
|
||||
}
|
||||
else
|
||||
{
|
||||
seed >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (ushort)(seed & 0xFFFF);
|
||||
}
|
||||
|
||||
private static unsafe bool VerifyCrc16(byte[] fw, int startaddr, int len, int seed, int crcaddr)
|
||||
{
|
||||
ushort storedCrc16 = (ushort)((fw[crcaddr + 1] << 8) | fw[crcaddr]);
|
||||
fixed (byte* start = &fw[startaddr])
|
||||
{
|
||||
ushort actualCrc16 = Crc16(start, len, seed);
|
||||
return storedCrc16 == actualCrc16;
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("libfwunpack", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool GetDecryptedFirmware(byte[] fw, int fwlen, out IntPtr decryptedFw, out int decryptedlen);
|
||||
|
||||
[DllImport("libfwunpack", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern void FreeDecryptedFirmware(IntPtr decryptedFw);
|
||||
|
||||
private static string[] goodhashes = new string[]
|
||||
{
|
||||
"D83861C66796665A9777B4E9078E9CC8EB13D880", // MACP nds (one of v1-v4), supposedly the most common
|
||||
"F87038265D24677419FE0AF9EED63B4CE1378CC9", // MACg nds (v5)
|
||||
"674639373F16539F718C728D6CA0C83A2DB66770", // MACh nds-lite (v6)
|
||||
};
|
||||
|
||||
private static bool CheckDecryptedCodeChecksum(byte[] fw, CoreComm comm)
|
||||
{
|
||||
if (!GetDecryptedFirmware(fw, fw.Length, out IntPtr decryptedfw, out int decrypedfwlen))
|
||||
{
|
||||
comm.ShowMessage("Firmware could not be decryped for verification! This firmware might be not work!");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] DecryptedFirmware = new byte[decrypedfwlen];
|
||||
Marshal.Copy(decryptedfw, DecryptedFirmware, 0, decrypedfwlen);
|
||||
FreeDecryptedFirmware(decryptedfw);
|
||||
var hash = BufferExtensions.HashSHA1(DecryptedFirmware, 0, decrypedfwlen);
|
||||
if (hash != goodhashes[0] && hash != goodhashes[1] && hash != goodhashes[2])
|
||||
{
|
||||
comm.ShowMessage("Potentially bad firmware dump! Decrypted hash " + hash + " does not match known good dumps.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -210,6 +210,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
Frame++;
|
||||
if (IsLagFrame = frame.Lagged != 0)
|
||||
LagCount++;
|
||||
CycleCount += frame.Cycles;
|
||||
AdvanceRtc();
|
||||
|
||||
if (render)
|
||||
|
@ -247,10 +248,14 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
public int Frame { get; private set; }
|
||||
public int LagCount { get; set; }
|
||||
public bool IsLagFrame { get; set; }
|
||||
public long CycleCount { get; private set; }
|
||||
|
||||
public void ResetCounters()
|
||||
{
|
||||
Frame = 0;
|
||||
LagCount = 0;
|
||||
IsLagFrame = false;
|
||||
CycleCount = 0;
|
||||
}
|
||||
|
||||
protected readonly BasicServiceProvider _serviceProvider;
|
||||
|
@ -269,6 +274,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
Frame = reader.ReadInt32();
|
||||
LagCount = reader.ReadInt32();
|
||||
IsLagFrame = reader.ReadBoolean();
|
||||
CycleCount = reader.ReadInt64();
|
||||
BufferWidth = reader.ReadInt32();
|
||||
BufferHeight = reader.ReadInt32();
|
||||
_clockTime = reader.ReadInt64();
|
||||
|
@ -289,6 +295,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
writer.Write(Frame);
|
||||
writer.Write(LagCount);
|
||||
writer.Write(IsLagFrame);
|
||||
writer.Write(CycleCount);
|
||||
writer.Write(BufferWidth);
|
||||
writer.Write(BufferHeight);
|
||||
writer.Write(_clockTime);
|
||||
|
|
|
@ -186,9 +186,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
|
||||
/// <summary>
|
||||
/// Mounts a file in the environment. All data will be immediately consumed from the reader, which will not be used after this call.
|
||||
/// To prevent nondeterminism, adding and removing files is very limited WRT savestates. If a file is writable, it must never exist
|
||||
/// when save_state is called, and can only be used for transient operations. If a file is readable, it can appear in savestates,
|
||||
/// but it must exist in every savestate and the exact sequence of add_file calls must be consistent from savestate to savestate.
|
||||
/// To prevent nondeterminism, adding and removing files is very limited WRT savestates. Every file added must either exist
|
||||
/// in every savestate, or never appear in any savestates. All savestateable files must be added in the same order for every run.
|
||||
/// </summary>
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void wbx_mount_file(IntPtr /*WaterboxHost*/ obj, string name, ReadCallback reader, IntPtr userdata, bool writable, ReturnData /*void*/ ret);
|
||||
|
@ -196,6 +195,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
|||
/// <summary>
|
||||
/// Remove a file previously added. Writer is optional; if provided, the contents of the file at time of removal will be dumped to it.
|
||||
/// It is an error to remove a file which is currently open in the guest.
|
||||
/// If the file has been used in savestates, it does not make sense to remove it here, but nothing will stop you.
|
||||
/// </summary>
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void wbx_unmount_file(IntPtr /*WaterboxHost*/ obj, string name, WriteCallback writer, IntPtr userdata, ReturnData /*void*/ ret);
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace BizHawk.Emulation.Cores
|
|||
new ButtonSchema(366, 86, "A"),
|
||||
|
||||
// Screen
|
||||
new TargetedPairSchema(72, 35, "TouchX")
|
||||
new TargetedPairSchema(72, 35, "Touch X")
|
||||
{
|
||||
TargetSize = new Size(256, 192)
|
||||
},
|
||||
|
@ -49,11 +49,25 @@ namespace BizHawk.Emulation.Cores
|
|||
{
|
||||
return new ConsoleSchema
|
||||
{
|
||||
Size = new Size(160, 45),
|
||||
Buttons = new[]
|
||||
Size = new Size(240, 260),
|
||||
Buttons = new PadSchemaControl[]
|
||||
{
|
||||
new ButtonSchema(8, 18, "LidOpen") { DisplayName = "Lid Open" },
|
||||
new ButtonSchema(68, 18, "LidClose") { DisplayName = "Lid Close" }
|
||||
new ButtonSchema(68, 18, "LidClose") { DisplayName = "Lid Close" },
|
||||
new ButtonSchema(128, 18, "Power"),
|
||||
|
||||
new SingleAxisSchema(10, 63, "Mic Input")
|
||||
{
|
||||
TargetSize = new Size(226, 69),
|
||||
MinValue = -1,
|
||||
MaxValue = 2047,
|
||||
},
|
||||
|
||||
new SingleAxisSchema(10, 137, "GBA Light Sensor")
|
||||
{
|
||||
TargetSize = new Size(226, 69),
|
||||
MaxValue = 10,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit acf90125257bced76f7a5112ec87d45127bcc34b
|
|
@ -5,6 +5,7 @@ cd emulibc && make -f Makefile $1 -j && cd -
|
|||
cd libco && make -f Makefile $1 -j && cd -
|
||||
cd gpgx && make -f Makefile $1 -j && cd -
|
||||
cd libsnes && make -f Makefile $1 -j && cd -
|
||||
cd melon && make -f Makefile $1 -j && cd -
|
||||
cd picodrive && make -f Makefile $1 -j && cd -
|
||||
cd snes9x && make -f Makefile $1 -j && cd -
|
||||
cd uzem && make -f Makefile $1 -j && cd -
|
||||
|
|
|
@ -0,0 +1,365 @@
|
|||
#include "NDS.h"
|
||||
#include "GPU.h"
|
||||
#include "SPU.h"
|
||||
#include "RTC.h"
|
||||
#include "ARM.h"
|
||||
#include "NDSCart.h"
|
||||
#include "NDSCart_SRAMManager.h"
|
||||
#include "GBACart.h"
|
||||
#include "Platform.h"
|
||||
#include "Config.h"
|
||||
#include "types.h"
|
||||
#include "frontend/mic_blow.h"
|
||||
|
||||
#include "../emulibc/emulibc.h"
|
||||
#include "../emulibc/waterboxcore.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <time.h>
|
||||
|
||||
#define EXPORT extern "C" ECL_EXPORT
|
||||
|
||||
static GPU::RenderSettings biz_render_settings { false, 1, false };
|
||||
static bool biz_skip_fw;
|
||||
static time_t biz_time;
|
||||
|
||||
static time_t BizRtcCallback()
|
||||
{
|
||||
return biz_time;
|
||||
}
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NONE = 0x00,
|
||||
USE_REAL_BIOS = 0x01,
|
||||
SKIP_FIRMWARE = 0x02,
|
||||
GBA_CART_PRESENT = 0x04,
|
||||
ACCURATE_AUDIO_BITRATE = 0x08,
|
||||
FIRMWARE_OVERRIDE = 0x10,
|
||||
} LoadFlags;
|
||||
|
||||
static const char* bios9_path = "bios9.rom";
|
||||
static const char* bios7_path = "bios7.rom";
|
||||
static const char* firmware_path = "firmware.bin";
|
||||
static const char* rom_path = "game.rom";
|
||||
static const char* sram_path = "save.ram";
|
||||
static const char* gba_rom_path = "gba.rom";
|
||||
static const char* gba_sram_path = "gba.ram";
|
||||
static const char* no_path = "";
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char* FirmwareUsername; // max 10 length (then terminator)
|
||||
s32 FirmwareUsernameLength;
|
||||
s32 FirmwareLanguage;
|
||||
s32 FirmwareBirthdayMonth;
|
||||
s32 FirmwareBirthdayDay;
|
||||
s32 FirmwareFavouriteColour;
|
||||
char* FirmwareMessage; // max 26 length (then terminator)
|
||||
s32 FirmwareMessageLength;
|
||||
} FirmwareSettings;
|
||||
|
||||
EXPORT bool Init(LoadFlags flags, FirmwareSettings* fwSettings)
|
||||
{
|
||||
Config::ExternalBIOSEnable = !!(flags & USE_REAL_BIOS);
|
||||
|
||||
strncpy(Config::BIOS9Path, Config::ExternalBIOSEnable ? bios9_path : no_path, 1023);
|
||||
Config::BIOS9Path[1023] = '\0';
|
||||
strncpy(Config::BIOS7Path, Config::ExternalBIOSEnable ? bios7_path : no_path, 1023);
|
||||
Config::BIOS7Path[1023] = '\0';
|
||||
strncpy(Config::FirmwarePath, firmware_path, 1023);
|
||||
Config::FirmwarePath[1023] = '\0';
|
||||
|
||||
NDS::SetConsoleType(0);
|
||||
// time calls are deterministic under wbx, so this will force the mac address to a constant value instead of relying on whatever is in the firmware
|
||||
// fixme: might want to allow the user to specify mac address?
|
||||
srand(time(NULL));
|
||||
Config::RandomizeMAC = true;
|
||||
Config::AudioBitrate = !!(flags & ACCURATE_AUDIO_BITRATE) ? 1 : 2;
|
||||
Config::FixedBootTime = true;
|
||||
Config::UseRealTime = false;
|
||||
Config::TimeAtBoot = 0;
|
||||
biz_time = 0;
|
||||
RTC::RtcCallback = BizRtcCallback;
|
||||
|
||||
Config::FirmwareOverrideSettings = !!(flags & FIRMWARE_OVERRIDE);
|
||||
if (Config::FirmwareOverrideSettings)
|
||||
{
|
||||
memcpy(Config::FirmwareUsername, fwSettings->FirmwareUsername, fwSettings->FirmwareUsernameLength);
|
||||
Config::FirmwareUsername[fwSettings->FirmwareUsernameLength] = 0;
|
||||
Config::FirmwareLanguage = fwSettings->FirmwareLanguage;
|
||||
Config::FirmwareBirthdayMonth = fwSettings->FirmwareBirthdayMonth;
|
||||
Config::FirmwareBirthdayDay = fwSettings->FirmwareBirthdayDay;
|
||||
Config::FirmwareFavouriteColour = fwSettings->FirmwareFavouriteColour;
|
||||
memcpy(Config::FirmwareMessage, fwSettings->FirmwareMessage, fwSettings->FirmwareMessageLength);
|
||||
Config::FirmwareMessage[fwSettings->FirmwareMessageLength] = 0;
|
||||
}
|
||||
|
||||
if (!NDS::Init()) return false;
|
||||
GPU::InitRenderer(false);
|
||||
GPU::SetRenderSettings(false, biz_render_settings);
|
||||
biz_skip_fw = !!(flags & SKIP_FIRMWARE);
|
||||
if (!NDS::LoadROM(rom_path, no_path, biz_skip_fw)) return false;
|
||||
if (flags & GBA_CART_PRESENT) { if (!NDS::LoadGBAROM(gba_rom_path, gba_sram_path)) return false; }
|
||||
Config::FirmwareOverrideSettings = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
EXPORT void PutSaveRam(u8* data, u32 len)
|
||||
{
|
||||
if (NDSCart_SRAMManager::SecondaryBufferLength > 0)
|
||||
NDSCart_SRAMManager::UpdateBuffer(data, len);
|
||||
}
|
||||
|
||||
EXPORT void GetSaveRam(u8* data)
|
||||
{
|
||||
if (NDSCart_SRAMManager::SecondaryBufferLength > 0)
|
||||
NDSCart_SRAMManager::FlushSecondaryBuffer(data, NDSCart_SRAMManager::SecondaryBufferLength);
|
||||
}
|
||||
|
||||
EXPORT s32 GetSaveRamLength()
|
||||
{
|
||||
return NDSCart_SRAMManager::SecondaryBufferLength;
|
||||
}
|
||||
|
||||
EXPORT bool SaveRamIsDirty()
|
||||
{
|
||||
return NDSCart_SRAMManager::NeedsFlush();
|
||||
}
|
||||
|
||||
/* excerpted from gbatek
|
||||
|
||||
NDS9 Memory Map
|
||||
|
||||
00000000h Instruction TCM (32KB) (not moveable) (mirror-able to 1000000h)
|
||||
0xxxx000h Data TCM (16KB) (moveable)
|
||||
02000000h Main Memory (4MB)
|
||||
03000000h Shared WRAM (0KB, 16KB, or 32KB can be allocated to ARM9)
|
||||
04000000h ARM9-I/O Ports
|
||||
05000000h Standard Palettes (2KB) (Engine A BG/OBJ, Engine B BG/OBJ)
|
||||
06000000h VRAM - Engine A, BG VRAM (max 512KB)
|
||||
06200000h VRAM - Engine B, BG VRAM (max 128KB)
|
||||
06400000h VRAM - Engine A, OBJ VRAM (max 256KB)
|
||||
06600000h VRAM - Engine B, OBJ VRAM (max 128KB)
|
||||
06800000h VRAM - "LCDC"-allocated (max 656KB)
|
||||
07000000h OAM (2KB) (Engine A, Engine B)
|
||||
08000000h GBA Slot ROM (max 32MB)
|
||||
0A000000h GBA Slot RAM (max 64KB)
|
||||
FFFF0000h ARM9-BIOS (32KB) (only 3K used)
|
||||
|
||||
NDS7 Memory Map
|
||||
|
||||
00000000h ARM7-BIOS (16KB)
|
||||
02000000h Main Memory (4MB)
|
||||
03000000h Shared WRAM (0KB, 16KB, or 32KB can be allocated to ARM7)
|
||||
03800000h ARM7-WRAM (64KB)
|
||||
04000000h ARM7-I/O Ports
|
||||
04800000h Wireless Communications Wait State 0 (8KB RAM at 4804000h)
|
||||
04808000h Wireless Communications Wait State 1 (I/O Ports at 4808000h)
|
||||
06000000h VRAM allocated as Work RAM to ARM7 (max 256K)
|
||||
08000000h GBA Slot ROM (max 32MB)
|
||||
0A000000h GBA Slot RAM (max 64KB)
|
||||
|
||||
Further Memory (not mapped to ARM9/ARM7 bus)
|
||||
|
||||
3D Engine Polygon RAM (52KBx2)
|
||||
3D Engine Vertex RAM (72KBx2)
|
||||
Firmware (256KB) (built-in serial flash memory)
|
||||
GBA-BIOS (16KB) (not used in NDS mode)
|
||||
NDS Slot ROM (serial 8bit-bus, max 4GB with default protocol)
|
||||
NDS Slot FLASH/EEPROM/FRAM (serial 1bit-bus)
|
||||
|
||||
*/
|
||||
|
||||
template<bool arm9>
|
||||
static bool SafeToPeek(u32 addr)
|
||||
{
|
||||
if (arm9)
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
case 0x04000130:
|
||||
case 0x04000131:
|
||||
case 0x04000600:
|
||||
case 0x04000601:
|
||||
case 0x04000602:
|
||||
case 0x04000603:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else // arm7
|
||||
{
|
||||
if (addr >= 0x04800000 && addr <= 0x04810000)
|
||||
{
|
||||
if (addr & 1) addr--;
|
||||
addr &= 0x7FFE;
|
||||
if (addr == 0x044 || addr == 0x060)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ARM9Access(u8* buffer, s64 address, s64 count, bool write)
|
||||
{
|
||||
if (write)
|
||||
while (count--) NDS::ARM9Write8(address++, *buffer++);
|
||||
else
|
||||
while (count--) *buffer++ = SafeToPeek<true>(address) ? NDS::ARM9Read8(address) : 0, address++;
|
||||
}
|
||||
|
||||
static void ARM7Access(u8* buffer, s64 address, s64 count, bool write)
|
||||
{
|
||||
if (write)
|
||||
while (count--) NDS::ARM7Write8(address++, *buffer++);
|
||||
else
|
||||
while (count--) *buffer++ = SafeToPeek<false>(address) ? NDS::ARM7Read8(address) : 0, address++;
|
||||
}
|
||||
|
||||
EXPORT void GetMemoryAreas(MemoryArea *m)
|
||||
{
|
||||
m[0].Data = NDS::ARM9BIOS;
|
||||
m[0].Name = "ARM9 BIOS";
|
||||
m[0].Size = sizeof NDS::ARM9BIOS;
|
||||
m[0].Flags = MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE;
|
||||
|
||||
m[1].Data = NDS::ARM7BIOS;
|
||||
m[1].Name = "ARM7 BIOS";
|
||||
m[1].Size = sizeof NDS::ARM7BIOS;
|
||||
m[1].Flags = MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE;
|
||||
|
||||
m[2].Data = NDS::MainRAM;
|
||||
m[2].Name = "Main RAM";
|
||||
m[2].Size = NDS::MainRAMMaxSize;
|
||||
m[2].Flags = MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_PRIMARY;
|
||||
|
||||
m[3].Data = NDS::SharedWRAM;
|
||||
m[3].Name = "Shared WRAM";
|
||||
m[3].Size = NDS::SharedWRAMSize;
|
||||
m[3].Flags = MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE;
|
||||
|
||||
m[4].Data = NDS::ARM7WRAM;
|
||||
m[4].Name = "ARM7 WRAM";
|
||||
m[4].Size = NDS::ARM7WRAMSize;
|
||||
m[4].Flags = MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE;
|
||||
|
||||
m[5].Data = (void*)ARM9Access;
|
||||
m[5].Name = "ARM9 System Bus";
|
||||
m[5].Size = 1ull << 32;
|
||||
m[5].Flags = MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_FUNCTIONHOOK;
|
||||
|
||||
m[6].Data = (void*)ARM7Access;
|
||||
m[6].Name = "ARM7 System Bus";
|
||||
m[6].Size = 1ull << 32;
|
||||
m[6].Flags = MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_FUNCTIONHOOK;
|
||||
|
||||
// fixme: include more shit
|
||||
}
|
||||
|
||||
struct MyFrameInfo : public FrameInfo
|
||||
{
|
||||
s64 Time;
|
||||
u32 Keys;
|
||||
s8 TouchX;
|
||||
s8 TouchY;
|
||||
s16 MicInput;
|
||||
s8 GBALightSensor;
|
||||
};
|
||||
|
||||
static s16 biz_mic_input[735];
|
||||
|
||||
static bool ValidRange(s8 sensor)
|
||||
{
|
||||
return (sensor >= 0) && (sensor <= 10);
|
||||
}
|
||||
|
||||
static int sampPos = 0;
|
||||
|
||||
static void MicFeedNoise()
|
||||
{
|
||||
int sampLen = sizeof mic_blow / sizeof mic_blow[0];
|
||||
|
||||
for (int i = 0; i < 735; i++)
|
||||
{
|
||||
biz_mic_input[i] = mic_blow[sampPos++];
|
||||
if (sampPos >= sampLen) sampPos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT void FrameAdvance(MyFrameInfo* f)
|
||||
{
|
||||
if (f->Keys & 0x8000)
|
||||
{
|
||||
NDS::LoadBIOS(false);
|
||||
if (biz_skip_fw) NDS::SetupDirectBoot();
|
||||
}
|
||||
|
||||
NDS::SetKeyMask(~f->Keys & 0xFFF);
|
||||
|
||||
if (f->Keys & 0x1000)
|
||||
NDS::TouchScreen(f->TouchX, f->TouchY);
|
||||
else
|
||||
NDS::ReleaseScreen();
|
||||
|
||||
if (f->Keys & 0x2000)
|
||||
NDS::SetLidClosed(false);
|
||||
else if (f->Keys & 0x4000)
|
||||
NDS::SetLidClosed(true);
|
||||
|
||||
if (f->MicInput < 0)
|
||||
MicFeedNoise();
|
||||
else
|
||||
std::fill_n(biz_mic_input, 735, f->MicInput << 4);
|
||||
|
||||
NDS::MicInputFrame(biz_mic_input, 735);
|
||||
|
||||
int sensor = GBACart::SetInput(0, 1);
|
||||
if (sensor != -1 && ValidRange(f->GBALightSensor))
|
||||
{
|
||||
if (sensor > f->GBALightSensor)
|
||||
{
|
||||
while (GBACart::SetInput(0, 1) != f->GBALightSensor) {}
|
||||
}
|
||||
else if (sensor < f->GBALightSensor)
|
||||
{
|
||||
while (GBACart::SetInput(1, 1) != f->GBALightSensor) {}
|
||||
}
|
||||
}
|
||||
|
||||
biz_time = f->Time;
|
||||
NDS::RunFrame();
|
||||
for (int i = 0; i < (256 * 192); i++)
|
||||
{
|
||||
f->VideoBuffer[i] = GPU::Framebuffer[GPU::FrontBuffer][0][i];
|
||||
f->VideoBuffer[(256 * 192) + i] = GPU::Framebuffer[GPU::FrontBuffer][1][i];
|
||||
}
|
||||
f->Width = 256;
|
||||
f->Height = 384;
|
||||
f->Samples = SPU::GetOutputSize() / 2;
|
||||
SPU::ReadOutput(f->SoundBuffer, f->Samples);
|
||||
f->Cycles = NDS::GetSysClockCycles(2);
|
||||
f->Lagged = NDS::LagFrameFlag;
|
||||
}
|
||||
|
||||
void (*InputCallback)();
|
||||
|
||||
EXPORT void SetInputCallback(void (*callback)())
|
||||
{
|
||||
InputCallback = callback;
|
||||
}
|
||||
|
||||
EXPORT void GetRegs(u32* regs)
|
||||
{
|
||||
NDS::GetRegs(regs);
|
||||
}
|
||||
|
||||
EXPORT void SetReg(s32 ncpu, s32 index, s32 val)
|
||||
{
|
||||
NDS::SetReg(ncpu, index, val);
|
||||
}
|
||||
|
||||
EXPORT void SetTraceCallback(void (*callback)(u32 cpu, u32* regs, u32 opcode, s64 ccoffset))
|
||||
{
|
||||
TraceCallback = callback;
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <semaphore.h>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include "Platform.h"
|
||||
|
||||
namespace Platform
|
||||
{
|
||||
|
||||
void Init(int argc, char** argv)
|
||||
{
|
||||
}
|
||||
|
||||
void DeInit()
|
||||
{
|
||||
}
|
||||
|
||||
void StopEmu()
|
||||
{
|
||||
}
|
||||
|
||||
FILE* OpenFile(const char* path, const char* mode, bool mustexist)
|
||||
{
|
||||
return fopen(path, mode);
|
||||
}
|
||||
|
||||
FILE* OpenLocalFile(const char* path, const char* mode)
|
||||
{
|
||||
return fopen(path, mode);
|
||||
}
|
||||
|
||||
Thread* Thread_Create(std::function<void()> func)
|
||||
{
|
||||
std::thread* t = new std::thread(func);
|
||||
return (Thread*) t;
|
||||
}
|
||||
|
||||
void Thread_Free(Thread* thread)
|
||||
{
|
||||
delete (std::thread*) thread;
|
||||
}
|
||||
|
||||
void Thread_Wait(Thread* thread)
|
||||
{
|
||||
((std::thread*) thread)->join();
|
||||
}
|
||||
|
||||
Semaphore* Semaphore_Create()
|
||||
{
|
||||
sem_t* s = new sem_t;
|
||||
sem_init(s, 0, 1);
|
||||
return (Semaphore*) s;
|
||||
}
|
||||
|
||||
void Semaphore_Free(Semaphore* sema)
|
||||
{
|
||||
sem_destroy((sem_t*) sema);
|
||||
delete (sem_t*) sema;
|
||||
}
|
||||
|
||||
void Semaphore_Reset(Semaphore* sema)
|
||||
{
|
||||
while (!sem_trywait((sem_t*) sema)) {};
|
||||
}
|
||||
|
||||
void Semaphore_Wait(Semaphore* sema)
|
||||
{
|
||||
sem_wait((sem_t*) sema);
|
||||
}
|
||||
|
||||
void Semaphore_Post(Semaphore* sema, int count)
|
||||
{
|
||||
while (count--) sem_post((sem_t*) sema);
|
||||
}
|
||||
|
||||
Mutex* Mutex_Create()
|
||||
{
|
||||
std::mutex* m = new std::mutex();
|
||||
return (Mutex*) m;
|
||||
}
|
||||
|
||||
void Mutex_Free(Mutex* mutex)
|
||||
{
|
||||
delete (std::mutex*) mutex;
|
||||
}
|
||||
|
||||
void Mutex_Lock(Mutex* mutex)
|
||||
{
|
||||
((std::mutex*) mutex)->lock();
|
||||
}
|
||||
|
||||
void Mutex_Unlock(Mutex* mutex)
|
||||
{
|
||||
((std::mutex*) mutex)->unlock();
|
||||
}
|
||||
|
||||
bool Mutex_TryLock(Mutex* mutex)
|
||||
{
|
||||
return ((std::mutex*) mutex)->try_lock();
|
||||
}
|
||||
|
||||
bool MP_Init()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void MP_DeInit()
|
||||
{
|
||||
}
|
||||
|
||||
int MP_SendPacket(u8* data, int len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MP_RecvPacket(u8* data, bool block)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool LAN_Init()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void LAN_DeInit()
|
||||
{
|
||||
}
|
||||
|
||||
int LAN_SendPacket(u8* data, int len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LAN_RecvPacket(u8* data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Sleep(u64 usecs)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
CXXFLAGS := -DMELONDS_VERSION="" \
|
||||
-I./melonDS/src -I./melonDS/src/teakra/include \
|
||||
-Wall -Wextra -Werror=int-to-pointer-cast \
|
||||
-Wcast-qual -Wfatal-errors -Wno-missing-braces \
|
||||
-Wno-unused-parameter -Wno-parentheses -Wno-sign-compare \
|
||||
-Wno-unused-variable -Wno-unused-function \
|
||||
-pedantic -std=c++17
|
||||
|
||||
TARGET = melonDS.wbx
|
||||
|
||||
CORE_SRCS = \
|
||||
AREngine.cpp \
|
||||
ARM.cpp \
|
||||
ARMInterpreter.cpp \
|
||||
ARMInterpreter_ALU.cpp \
|
||||
ARMInterpreter_Branch.cpp \
|
||||
ARMInterpreter_LoadStore.cpp \
|
||||
Config.cpp \
|
||||
CP15.cpp \
|
||||
CRC32.cpp \
|
||||
DMA.cpp \
|
||||
DSi.cpp \
|
||||
DSi_AES.cpp \
|
||||
DSi_Camera.cpp \
|
||||
DSi_DSP.cpp \
|
||||
DSi_I2C.cpp \
|
||||
DSi_NAND.cpp \
|
||||
DSi_NDMA.cpp \
|
||||
DSi_NWifi.cpp \
|
||||
DSi_SD.cpp \
|
||||
DSi_SPI_TSC.cpp \
|
||||
GBACart.cpp \
|
||||
GPU.cpp \
|
||||
GPU2D.cpp \
|
||||
GPU2D_Soft.cpp \
|
||||
GPU3D.cpp \
|
||||
GPU3D_Soft.cpp \
|
||||
NDS.cpp \
|
||||
NDSCart.cpp \
|
||||
NDSCart_SRAMManager.cpp \
|
||||
RTC.cpp \
|
||||
Savestate.cpp \
|
||||
SPI.cpp \
|
||||
SPU.cpp \
|
||||
Wifi.cpp \
|
||||
WifiAP.cpp
|
||||
|
||||
TEAKRA_SRCS = \
|
||||
ahbm.cpp \
|
||||
apbp.cpp \
|
||||
btdmp.cpp \
|
||||
dma.cpp \
|
||||
memory_interface.cpp \
|
||||
mmio.cpp \
|
||||
processor.cpp \
|
||||
teakra.cpp \
|
||||
timer.cpp
|
||||
|
||||
FATFS_SRCS = \
|
||||
diskio.c \
|
||||
ff.c \
|
||||
ffsystem.c \
|
||||
ffunicode.c
|
||||
|
||||
MISC_SRCS = \
|
||||
sha1/sha1.c \
|
||||
tiny-AES-c/aes.c \
|
||||
xxhash/xxhash.c
|
||||
|
||||
SRCS = \
|
||||
$(addprefix melonDS/src/,$(CORE_SRCS)) \
|
||||
$(addprefix melonDS/src/teakra/src/,$(TEAKRA_SRCS)) \
|
||||
$(addprefix melonDS/src/fatfs/,$(FATFS_SRCS)) \
|
||||
$(addprefix melonDS/src/,$(MISC_SRCS)) \
|
||||
BizInterface.cpp \
|
||||
BizPlatform.cpp
|
||||
|
||||
include ../common.mak
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 7aa90df298c1d322852b1016937c2cb57aabe6ce
|
|
@ -49,6 +49,7 @@ It consists of a modified musl libc, and build scripts to tie it all together.
|
|||
7. You are now ready to start building cores. Each supports `make` and `make install`, as well as `make debug` and `make install-debug` for local development. From the root directory, the following should all be valid:
|
||||
cd gpgx && make install
|
||||
cd libsnes && make install
|
||||
cd melon && make install
|
||||
cd nyma && make -f faust.mak install
|
||||
cd nyma && make -f ngp.mak install
|
||||
cd nyma && make -f turbo.mak install
|
||||
|
|
|
@ -250,9 +250,8 @@ pub extern fn wbx_seal(obj: &mut WaterboxHost, ret: &mut Return<()>) {
|
|||
}
|
||||
|
||||
/// Mounts a file in the environment. All data will be immediately consumed from the reader, which will not be used after this call.
|
||||
/// To prevent nondeterminism, adding and removing files is very limited WRT savestates. If a file is writable, it must never exist
|
||||
/// when save_state is called, and can only be used for transient operations. If a file is readable, it can appear in savestates,
|
||||
/// but it must exist in every savestate and the exact sequence of add_file calls must be consistent from savestate to savestate.
|
||||
/// To prevent nondeterminism, adding and removing files is very limited WRT savestates. Every file added must either exist
|
||||
/// in every savestate, or never appear in any savestates. All savestateable files must be added in the same order for every run.
|
||||
#[no_mangle]
|
||||
pub extern fn wbx_mount_file(obj: &mut WaterboxHost, name: *const c_char, callback: ReadCallback, userdata: usize, writable: bool, ret: &mut Return<()>) {
|
||||
let mut reader = CReader {
|
||||
|
@ -268,6 +267,7 @@ pub extern fn wbx_mount_file(obj: &mut WaterboxHost, name: *const c_char, callba
|
|||
|
||||
/// Remove a file previously added. Writer is optional; if provided, the contents of the file at time of removal will be dumped to it.
|
||||
/// It is an error to remove a file which is currently open in the guest.
|
||||
/// If the file has been used in savestates, it does not make sense to remove it here, but nothing will stop you.
|
||||
#[no_mangle]
|
||||
pub extern fn wbx_unmount_file(obj: &mut WaterboxHost, name: *const c_char, callback_opt: Option<WriteCallback>, userdata: usize, ret: &mut Return<()>) {
|
||||
let res: anyhow::Result<()> = (|| {
|
||||
|
|
|
@ -127,9 +127,8 @@ impl FileSystem {
|
|||
// pub fn set_missing_file_callback(&mut self, cb: Option<MissingFileCallback>) {
|
||||
// self.missing_file_callback = cb;
|
||||
// }
|
||||
/// Accept a file from the outside world. Writable files may never appear in a savestate,
|
||||
/// and readonly files must not be added or removed from savestate to savestate, so all uses
|
||||
/// are either transient or read only resources that last for the life of emulation.
|
||||
/// Accept a file from the outside world. Files must not be added or removed from savestate
|
||||
/// to savestate, so all uses are either transient or resources that last for the life of emulation.
|
||||
pub fn mount(&mut self, name: String, data: Vec<u8>, writable: bool) -> anyhow::Result<()> {
|
||||
if self.files.iter().any(|f| f.name == name) {
|
||||
return Err(anyhow!("File with name {} already mounted.", name))
|
||||
|
@ -339,6 +338,54 @@ mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rw_state() -> TestResult {
|
||||
let mut fs = FileSystem::new();
|
||||
fs.mount("myfile".to_string(),
|
||||
"The quick brown fox jumps over the lazy dog.".to_string().into_bytes(), true)?;
|
||||
|
||||
let mut buff = vec![0u8; 8];
|
||||
|
||||
{
|
||||
let fdr = fs.open("myfile", O_RDONLY, 0)?;
|
||||
assert_eq!(fdr.0, 3);
|
||||
// TODO: If a file is mounted as writable, opening it as readonly still allows it to be written to.
|
||||
// assert!(fs.write(fdr, &buff[..]).is_err());
|
||||
assert_eq!(fs.read(fdr, &mut buff[..])?, 8);
|
||||
assert_eq!(buff, "The quic".as_bytes());
|
||||
fs.close(fdr)?;
|
||||
}
|
||||
|
||||
let fd = fs.open("myfile", O_RDWR, 0)?;
|
||||
assert_eq!(fd.0, 3);
|
||||
|
||||
let mut state0 = Vec::new();
|
||||
fs.save_state(&mut state0)?;
|
||||
assert_eq!(fs.read(fd, &mut buff[..])?, 8);
|
||||
assert_eq!(buff, "The quic".as_bytes());
|
||||
fs.write(fd, &buff[..])?;
|
||||
fs.seek(fd, 0, SEEK_SET)?;
|
||||
assert_eq!(fs.read(fd, &mut buff[..])?, 8);
|
||||
assert_eq!(buff, "The quic".as_bytes());
|
||||
assert_eq!(fs.read(fd, &mut buff[..])?, 8);
|
||||
assert_eq!(buff, "The quic".as_bytes());
|
||||
|
||||
fs.load_state(&mut &state0[..])?;
|
||||
assert_eq!(fs.read(fd, &mut buff[..])?, 8);
|
||||
assert_eq!(buff, "The quic".as_bytes());
|
||||
assert_eq!(fs.read(fd, &mut buff[..])?, 8);
|
||||
assert_eq!(buff, "k brown ".as_bytes());
|
||||
assert_eq!(fs.read(fd, &mut buff[..])?, 8);
|
||||
assert_eq!(buff, "fox jump".as_bytes());
|
||||
assert_eq!(fs.read(fd, &mut buff[..])?, 8);
|
||||
assert_eq!(buff, "s over t".as_bytes());
|
||||
assert_eq!(fs.read(fd, &mut buff[..])?, 8);
|
||||
assert_eq!(buff, "he lazy ".as_bytes());
|
||||
assert_eq!(fs.read(fd, &mut buff[..])?, 4);
|
||||
assert_eq!(&buff[0..4], "dog.".as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negative() -> TestResult {
|
||||
let mut fs = FileSystem::new();
|
||||
|
@ -347,7 +394,6 @@ mod tests {
|
|||
assert!(fs.unmount("/dev/stdout").is_err()); // unmounting permanent file
|
||||
fs.mount("oopopo".to_string(), Vec::new(), true)?;
|
||||
let mut state0 = Vec::new();
|
||||
assert!(fs.save_state(&mut state0).is_err()); // save state with transient file
|
||||
state0.resize(0, 0);
|
||||
fs.unmount("oopopo")?;
|
||||
fs.mount("oopopo".to_string(), Vec::new(), false)?;
|
||||
|
|
|
@ -30,10 +30,15 @@ impl IStateable for RegularFile {
|
|||
bin::write_magic(stream, "RegularFile")?;
|
||||
bin::write_hash(stream, &hash[..])?;
|
||||
bin::write(stream, &self.position)?;
|
||||
Ok(())
|
||||
|
||||
Ok(())
|
||||
},
|
||||
None => {
|
||||
bin::write_magic(stream, "RegularFile(RW)")?;
|
||||
bin::writeval(stream, self.data.len())?;
|
||||
stream.write_all(&self.data[..])?;
|
||||
bin::write(stream, &self.position)?;
|
||||
Ok(())
|
||||
},
|
||||
None => Err(anyhow!("Cannot save state while transient files are active"))
|
||||
}
|
||||
}
|
||||
fn load_state(&mut self, stream: &mut dyn Read) -> anyhow::Result<()> {
|
||||
|
@ -42,9 +47,18 @@ impl IStateable for RegularFile {
|
|||
bin::verify_magic(stream, "RegularFile")?;
|
||||
bin::verify_hash(stream, &hash[..])?;
|
||||
bin::read(stream, &mut self.position)?;
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
None => Err(anyhow!("Cannot load state while transient files are active"))
|
||||
None => {
|
||||
bin::verify_magic(stream, "RegularFile(RW)")?;
|
||||
let len = bin::readval(stream)?;
|
||||
if len != self.data.len() {
|
||||
self.data.resize(len, 0);
|
||||
}
|
||||
stream.read_exact(&mut self.data[..])?;
|
||||
bin::read(stream, &mut self.position)?;
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -416,7 +416,7 @@ impl MemoryBlock {
|
|||
struct Chunk {
|
||||
addr: AddressRange,
|
||||
prot: Protection,
|
||||
};
|
||||
}
|
||||
let mut start = range.start;
|
||||
let chunks = range.iter()
|
||||
.map(|p| {
|
||||
|
|
Loading…
Reference in New Issue