merging with latest
This commit is contained in:
commit
a311140976
|
@ -88,3 +88,7 @@
|
|||
[submodule "waterbox/dosbox/dosbox-x"]
|
||||
path = waterbox/dosbox/dosbox-x
|
||||
url = https://github.com/TASEmulators/dosbox-x.git
|
||||
[submodule "waterbox/dsda/core"]
|
||||
path = waterbox/dsda/core
|
||||
url = https://github.com/TASEmulators/dsda-doom.git
|
||||
branch = wbx
|
||||
|
|
|
@ -1117,6 +1117,32 @@
|
|||
"GamepadPrefix": "X1 ",
|
||||
"Prescale": 1.0
|
||||
}
|
||||
},
|
||||
"Gameboy Controller": {
|
||||
"Rumble": {
|
||||
"Channels": "Left+Right",
|
||||
"GamepadPrefix": "X1 ",
|
||||
"Prescale": 1.0
|
||||
}
|
||||
},
|
||||
"GBA Controller": {
|
||||
"Rumble": {
|
||||
"Channels": "Left+Right",
|
||||
"GamepadPrefix": "X1 ",
|
||||
"Prescale": 1.0
|
||||
}
|
||||
},
|
||||
"PSX Front Panel": {
|
||||
"P1 Rumble Left (strong)": {
|
||||
"Channels": "Left",
|
||||
"GamepadPrefix": "X1 ",
|
||||
"Prescale": 1.0
|
||||
},
|
||||
"P1 Rumble Right (weak)": {
|
||||
"Channels": "Right",
|
||||
"GamepadPrefix": "X1 ",
|
||||
"Prescale": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -18,7 +18,11 @@ namespace BizHawk.Client.Common
|
|||
get
|
||||
{
|
||||
ICollection<string> keys = _movieSession.UserBag.Keys;
|
||||
#if NET5_0_OR_GREATER
|
||||
return (keys as KeyCollectionType) ?? keys.ToHashSet();
|
||||
#else
|
||||
return (keys as KeyCollectionType) ?? keys.ToList();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,11 @@ namespace BizHawk.Client.Common
|
|||
|
||||
private readonly Dictionary<string, FeedbackBind> _feedbackBindings = new Dictionary<string, FeedbackBind>();
|
||||
|
||||
#if BIZHAWKBUILD_SUPERHAWK
|
||||
public bool AnyInputHeld
|
||||
=> _buttons.ContainsValue(true) || _axes.Any(kvp => kvp.Value != _axisRanges[kvp.Key].Neutral);
|
||||
#endif
|
||||
|
||||
public bool this[string button] => IsPressed(button);
|
||||
|
||||
// Looks for bindings which are activated by the supplied physical button.
|
||||
|
|
|
@ -990,6 +990,8 @@ namespace BizHawk.Client.Common
|
|||
|
||||
public static readonly IReadOnlyCollection<string> DOS = new[] { "ima", "img" };
|
||||
|
||||
public static readonly IReadOnlyCollection<string> Doom = new[] { "wad" };
|
||||
|
||||
public static readonly IReadOnlyCollection<string> GB = new[] { "gb", "gbc", "sgb" };
|
||||
|
||||
public static readonly IReadOnlyCollection<string> GBA = new[] { "gba" };
|
||||
|
@ -1048,6 +1050,7 @@ namespace BizHawk.Client.Common
|
|||
.Concat(C64)
|
||||
.Concat(Coleco)
|
||||
.Concat(DOS)
|
||||
.Concat(Doom)
|
||||
.Concat(GB)
|
||||
.Concat(GBA)
|
||||
.Concat(GEN)
|
||||
|
@ -1091,6 +1094,7 @@ namespace BizHawk.Client.Common
|
|||
new FilesystemFilter(/*VSystemID.Raw.C64*/"SID Commodore 64 Music File", Array.Empty<string>(), devBuildExtraExts: new[] { "sid" }, devBuildAddArchiveExts: true),
|
||||
new FilesystemFilter(/*VSystemID.Raw.DOS*/"DOS", RomFileExtensions.DOS),
|
||||
new FilesystemFilter(/*VSystemID.Raw.Coleco*/"ColecoVision", RomFileExtensions.Coleco, addArchiveExts: true),
|
||||
new FilesystemFilter(/*VSystemID.Raw.Doom*/"Doom / Hexen / Heretic WAD File", RomFileExtensions.Doom),
|
||||
new FilesystemFilter(/*VSystemID.Raw.GB*/"Gameboy", RomFileExtensions.GB.Concat(new[] { "gbs" }).ToList(), addArchiveExts: true),
|
||||
new FilesystemFilter(/*VSystemID.Raw.GBA*/"Gameboy Advance", RomFileExtensions.GBA, addArchiveExts: true),
|
||||
new FilesystemFilter(/*VSystemID.Raw.GEN*/"Genesis", RomFileExtensions.GEN.Concat(FilesystemFilter.DiscExtensions).ToList(), addArchiveExts: true),
|
||||
|
|
|
@ -115,6 +115,7 @@ namespace BizHawk.Client.Common
|
|||
|
||||
private Dictionary<string, int> TargetZoomFactors { get; set; } = new()
|
||||
{
|
||||
[VSystemID.Raw.Doom] = 1,
|
||||
[VSystemID.Raw.GB] = 3,
|
||||
[VSystemID.Raw.GBA] = 3,
|
||||
[VSystemID.Raw.GBC] = 3,
|
||||
|
@ -203,6 +204,9 @@ namespace BizHawk.Client.Common
|
|||
public bool Unthrottled { get; set; } = false;
|
||||
public bool AutoMinimizeSkipping { get; set; } = true;
|
||||
public bool VSyncThrottle { get; set; } = false;
|
||||
#if BIZHAWKBUILD_SUPERHAWK
|
||||
public bool SuperHawkThrottle { get; set; } = false;
|
||||
#endif
|
||||
|
||||
public RewindConfig Rewind { get; set; } = new RewindConfig();
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
bool UseFixedRewindInterval { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Desired frame length (number of emulated frames you can go back before running out of buffer)
|
||||
/// Desired minimum rewind range (number of emulated frames you can go back before running out of buffer)
|
||||
/// </summary>
|
||||
int TargetFrameLength { get; }
|
||||
|
||||
|
|
|
@ -164,7 +164,7 @@ namespace BizHawk.Client.Common
|
|||
long addr,
|
||||
int length,
|
||||
string domain)
|
||||
=> APIs.Memory.WriteByteRange(addr, new List<byte>(APIs.Comm.MMF.ReadBytesFromFile(mmf_filename, length)), domain);
|
||||
=> APIs.Memory.WriteByteRange(addr, APIs.Comm.MMF.ReadBytesFromFile(mmf_filename, length), domain);
|
||||
|
||||
[LuaMethod("mmfRead", "Reads a string from a memory mapped file")]
|
||||
public string MmfRead(string mmf_filename, int expectedSize)
|
||||
|
|
|
@ -82,12 +82,6 @@ namespace BizHawk.Client.Common
|
|||
else if (Movie.IsPlaying())
|
||||
{
|
||||
LatchInputToLog();
|
||||
// if we're at the movie's end and the MovieEndAction is record, just continue recording in play mode
|
||||
// TODO change to TAStudio check
|
||||
if (Movie is ITasMovie && Movie.Emulator.Frame == Movie.FrameCount && Settings.MovieEndAction == MovieEndAction.Record)
|
||||
{
|
||||
Movie.RecordFrame(Movie.Emulator.Frame, MovieOut.Source);
|
||||
}
|
||||
}
|
||||
else if (Movie.IsRecording())
|
||||
{
|
||||
|
@ -96,16 +90,14 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
}
|
||||
|
||||
public void HandleFrameAfter()
|
||||
public void HandleFrameAfter(bool ignoreMovieEndAction)
|
||||
{
|
||||
if (Movie is ITasMovie tasMovie)
|
||||
{
|
||||
tasMovie.GreenzoneCurrentFrame();
|
||||
// TODO change to TAStudio check
|
||||
if (Settings.MovieEndAction == MovieEndAction.Record) return;
|
||||
}
|
||||
|
||||
if (Movie.IsPlaying() && Movie.Emulator.Frame >= Movie.FrameCount)
|
||||
if (!ignoreMovieEndAction && Movie.IsPlaying() && Movie.Emulator.Frame == Movie.FrameCount)
|
||||
{
|
||||
HandlePlaybackEnd();
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ namespace BizHawk.Client.Common
|
|||
// note: ChannelF II PAL timings might be slightly different...
|
||||
["ChannelF_PAL"] = 15625.0 / 312.0, // 4000000 / (256 * 312)
|
||||
["Coleco"] = 59.9227510135505,
|
||||
["Doom"] = 35.0,
|
||||
["FDS"] = 60.098813897440515532,
|
||||
["FDS_PAL"] = 50.006977968268290849,
|
||||
["GEN"] = 53693175 / (3420.0 * 262),
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores;
|
||||
|
||||
namespace BizHawk.Client.Common.movie.import
|
||||
{
|
||||
// ReSharper disable once UnusedMember.Global
|
||||
[ImporterFor("BizHawk", ".bkm")]
|
||||
internal class BkmImport : MovieImporter
|
||||
{
|
||||
|
@ -13,15 +14,20 @@ namespace BizHawk.Client.Common.movie.import
|
|||
|
||||
for (var i = 0; i < bkm.InputLogLength; i++)
|
||||
{
|
||||
// TODO: this is currently broken because Result.Movie.Emulator is no longer getting set,
|
||||
// however using that was sketchy anyway because it relied on the currently loaded core for import
|
||||
var input = bkm.GetInputState(i, Result.Movie.Emulator.ControllerDefinition, bkm.Header[HeaderKeys.Platform]);
|
||||
var input = bkm.GetInputState(i, bkm.Header[HeaderKeys.Platform]);
|
||||
Result.Movie.AppendFrame(input);
|
||||
}
|
||||
|
||||
Result.Movie.LogKey = bkm.GenerateLogKey;
|
||||
|
||||
Result.Movie.HeaderEntries.Clear();
|
||||
foreach (var (k, v) in bkm.Header) Result.Movie.HeaderEntries[k] = v;
|
||||
|
||||
// migrate some stuff, probably incomplete
|
||||
if (Result.Movie.HeaderEntries[HeaderKeys.Core] is "QuickNes") Result.Movie.HeaderEntries[HeaderKeys.Core] = CoreNames.QuickNes;
|
||||
if (Result.Movie.HeaderEntries[HeaderKeys.Core] is "EMU7800") Result.Movie.HeaderEntries[HeaderKeys.Core] = CoreNames.A7800Hawk;
|
||||
if (Result.Movie.HeaderEntries[HeaderKeys.Platform] is "DGB") Result.Movie.HeaderEntries[HeaderKeys.Platform] = VSystemID.Raw.GBL;
|
||||
|
||||
Result.Movie.SyncSettingsJson = bkm.SyncSettingsJson;
|
||||
|
||||
Result.Movie.Comments.Clear();
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
using BizHawk.Common.IOExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores;
|
||||
using BizHawk.Emulation.Cores.Computers.Doom;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
// LMP file format: https://doomwiki.org/wiki/Demo#Technical_information
|
||||
// In better detail, from archive.org: http://web.archive.org/web/20070630072856/http://demospecs.planetquake.gamespy.com/lmp/lmp.html
|
||||
[ImporterFor("Doom", ".lmp")]
|
||||
internal class DoomLmpImport : MovieImporter
|
||||
{
|
||||
protected override void RunImport()
|
||||
{
|
||||
var input = SourceFile.OpenRead().ReadAllBytes();
|
||||
var i = 0;
|
||||
Result.Movie.HeaderEntries[HeaderKeys.Core] = CoreNames.DSDA;
|
||||
Result.Movie.SystemID = VSystemID.Raw.Doom;
|
||||
|
||||
// Try to decide game version based on signature
|
||||
var signature = input[i];
|
||||
DSDA.CompatibilityLevel presumedCompatibilityLevel;
|
||||
if (signature <= 102)
|
||||
{
|
||||
// there is no signature, the first byte is the skill level, so don't advance
|
||||
Console.WriteLine("Reading DOOM LMP demo version: <=1.12");
|
||||
presumedCompatibilityLevel = DSDA.CompatibilityLevel.C0;
|
||||
}
|
||||
else
|
||||
{
|
||||
i++;
|
||||
Console.WriteLine("Reading DOOM LMP demo version: {0}", signature);
|
||||
presumedCompatibilityLevel = signature < 109
|
||||
? DSDA.CompatibilityLevel.C1 // 1.666
|
||||
: DSDA.CompatibilityLevel.C2; // 1.9
|
||||
}
|
||||
|
||||
DSDA.DoomSyncSettings syncSettings = new()
|
||||
{
|
||||
InputFormat = DoomControllerTypes.Doom,
|
||||
CompatibilityMode = presumedCompatibilityLevel,
|
||||
SkillLevel = (DSDA.SkillLevel) (1 + input[i++]),
|
||||
InitialEpisode = input[i++],
|
||||
InitialMap = input[i++],
|
||||
MultiplayerMode = (DSDA.MultiplayerMode) input[i++],
|
||||
MonstersRespawn = input[i++] is not 0,
|
||||
FastMonsters = input[i++] is not 0,
|
||||
NoMonsters = input[i++] is not 0,
|
||||
};
|
||||
_ = input[i++]; // DisplayPlayer is a non-sync setting so importers can't* set it
|
||||
syncSettings.Player1Present = input[i++] is not 0;
|
||||
syncSettings.Player2Present = input[i++] is not 0;
|
||||
syncSettings.Player3Present = input[i++] is not 0;
|
||||
syncSettings.Player4Present = input[i++] is not 0;
|
||||
Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(syncSettings);
|
||||
|
||||
var doomController1 = new DoomController(1);
|
||||
var controller = new SimpleController(doomController1.Definition);
|
||||
controller.Definition.BuildMnemonicsCache(Result.Movie.SystemID);
|
||||
void ParsePlayer(string playerPfx)
|
||||
{
|
||||
controller.AcceptNewAxis(playerPfx + "Run Speed", unchecked((sbyte) input[i++]));
|
||||
|
||||
controller.AcceptNewAxis(playerPfx + "Strafing Speed", unchecked((sbyte) input[i++]));
|
||||
|
||||
controller.AcceptNewAxis(playerPfx + "Turning Speed", unchecked((sbyte) input[i++]));
|
||||
|
||||
var specialValue = input[i++];
|
||||
controller[playerPfx + "Fire"] = (specialValue & 0b00000001) is not 0;
|
||||
controller[playerPfx + "Action"] = (specialValue & 0b00000010) is not 0;
|
||||
controller.AcceptNewAxis(playerPfx + "Weapon Select", (specialValue & 0b00011100) >> 2);
|
||||
controller[playerPfx + "Alt Weapon"] = (specialValue & 0b00100000) is not 0;
|
||||
}
|
||||
do
|
||||
{
|
||||
if (syncSettings.Player1Present) ParsePlayer("P1 ");
|
||||
if (syncSettings.Player2Present) ParsePlayer("P2 ");
|
||||
if (syncSettings.Player3Present) ParsePlayer("P3 ");
|
||||
if (syncSettings.Player4Present) ParsePlayer("P4 ");
|
||||
Result.Movie.AppendFrame(controller);
|
||||
if (i == input.Length) throw new Exception("Reached end of input movie stream without finalization byte");
|
||||
}
|
||||
while (input[i] is not 0x80);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,6 +11,17 @@ namespace BizHawk.Client.Common
|
|||
[ImporterFor("DeSmuME", ".dsm")]
|
||||
internal class DsmImport : MovieImporter
|
||||
{
|
||||
[Flags]
|
||||
private enum MovieCommand
|
||||
{
|
||||
MIC = 1,
|
||||
RESET = 2,
|
||||
LID = 4
|
||||
}
|
||||
|
||||
private bool _lidOpen = true;
|
||||
private int _countLid;
|
||||
|
||||
private static readonly ControllerDefinition DeSmuMEControllerDef = new ControllerDefinition("NDS Controller")
|
||||
{
|
||||
BoolButtons =
|
||||
|
@ -143,7 +154,30 @@ namespace BizHawk.Client.Common
|
|||
|
||||
private void ProcessCmd(string cmd, SimpleController controller)
|
||||
{
|
||||
// TODO
|
||||
MovieCommand command = (MovieCommand) int.Parse(cmd);
|
||||
|
||||
controller["Microphone"] = command.HasFlag(MovieCommand.MIC);
|
||||
|
||||
bool hasPowerCommand = command.HasFlag(MovieCommand.RESET);
|
||||
controller["Power"] = hasPowerCommand;
|
||||
if (hasPowerCommand)
|
||||
{
|
||||
_lidOpen = true;
|
||||
_countLid = 0;
|
||||
}
|
||||
|
||||
bool hasLidCommand = command.HasFlag(MovieCommand.LID);
|
||||
controller["LidClose"] = hasLidCommand && _lidOpen && _countLid == 0;
|
||||
controller["LidOpen"] = hasLidCommand && !_lidOpen && _countLid == 0;
|
||||
if (hasLidCommand && _countLid == 0)
|
||||
{
|
||||
_countLid = 30;
|
||||
_lidOpen = !_lidOpen;
|
||||
}
|
||||
else if (_countLid > 0)
|
||||
{
|
||||
_countLid--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
using BizHawk.Common.IOExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores;
|
||||
using BizHawk.Emulation.Cores.Computers.Doom;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
// LMP file format: https://doomwiki.org/wiki/Demo#Technical_information
|
||||
// In better detail, from archive.org: http://web.archive.org/web/20070630072856/http://demospecs.planetquake.gamespy.com/lmp/lmp.html
|
||||
[ImporterFor("Heretic", ".hereticlmp")]
|
||||
internal class HereticLmpImport : MovieImporter
|
||||
{
|
||||
protected override void RunImport()
|
||||
{
|
||||
var input = SourceFile.OpenRead().ReadAllBytes();
|
||||
var i = 0;
|
||||
Result.Movie.HeaderEntries[HeaderKeys.Core] = CoreNames.DSDA;
|
||||
Result.Movie.SystemID = VSystemID.Raw.Doom;
|
||||
DSDA.DoomSyncSettings syncSettings = new()
|
||||
{
|
||||
InputFormat = DoomControllerTypes.Heretic,
|
||||
MultiplayerMode = DSDA.MultiplayerMode.M0,
|
||||
MonstersRespawn = false,
|
||||
FastMonsters = false,
|
||||
NoMonsters = false,
|
||||
CompatibilityMode = DSDA.CompatibilityLevel.C0,
|
||||
SkillLevel = (DSDA.SkillLevel) (1 + input[i++]),
|
||||
InitialEpisode = input[i++],
|
||||
InitialMap = input[i++],
|
||||
Player1Present = input[i++] is not 0,
|
||||
Player2Present = input[i++] is not 0,
|
||||
Player3Present = input[i++] is not 0,
|
||||
Player4Present = input[i++] is not 0,
|
||||
};
|
||||
Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(syncSettings);
|
||||
|
||||
var hereticController = new HereticController(1);
|
||||
var controller = new SimpleController(hereticController.Definition);
|
||||
controller.Definition.BuildMnemonicsCache(Result.Movie.SystemID);
|
||||
void ParsePlayer(string playerPfx)
|
||||
{
|
||||
controller.AcceptNewAxis(playerPfx + "Run Speed", unchecked((sbyte) input[i++]));
|
||||
|
||||
controller.AcceptNewAxis(playerPfx + "Strafing Speed", unchecked((sbyte) input[i++]));
|
||||
|
||||
controller.AcceptNewAxis(playerPfx + "Turning Speed", unchecked((sbyte) input[i++]));
|
||||
|
||||
var specialValue = input[i++];
|
||||
controller[playerPfx + "Fire"] = (specialValue & 0b00000001) is not 0;
|
||||
controller[playerPfx + "Action"] = (specialValue & 0b00000010) is not 0;
|
||||
controller.AcceptNewAxis(playerPfx + "Weapon Select", (specialValue & 0b00011100) >> 2);
|
||||
controller[playerPfx + "Alt Weapon"] = (specialValue & 0b00100000) is not 0;
|
||||
|
||||
controller.AcceptNewAxis(playerPfx + "Fly / Look", unchecked((sbyte) input[i++]));
|
||||
|
||||
controller.AcceptNewAxis(playerPfx + "Use Artifact", unchecked((sbyte) input[i++]));
|
||||
}
|
||||
do
|
||||
{
|
||||
if (syncSettings.Player1Present) ParsePlayer("P1 ");
|
||||
if (syncSettings.Player2Present) ParsePlayer("P2 ");
|
||||
if (syncSettings.Player3Present) ParsePlayer("P3 ");
|
||||
if (syncSettings.Player4Present) ParsePlayer("P4 ");
|
||||
Result.Movie.AppendFrame(controller);
|
||||
if (i == input.Length) throw new Exception("Reached end of input movie stream without finalization byte");
|
||||
}
|
||||
while (input[i] is not 0x80);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
using BizHawk.Common.IOExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores;
|
||||
using BizHawk.Emulation.Cores.Computers.Doom;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
// LMP file format: https://doomwiki.org/wiki/Demo#Technical_information
|
||||
// In better detail, from archive.org: http://web.archive.org/web/20070630072856/http://demospecs.planetquake.gamespy.com/lmp/lmp.html
|
||||
[ImporterFor("Hexen", ".hexenlmp")]
|
||||
internal class HexenLmpImport : MovieImporter
|
||||
{
|
||||
protected override void RunImport()
|
||||
{
|
||||
var input = SourceFile.OpenRead().ReadAllBytes();
|
||||
var i = 0;
|
||||
Result.Movie.HeaderEntries[HeaderKeys.Core] = CoreNames.DSDA;
|
||||
Result.Movie.SystemID = VSystemID.Raw.Doom;
|
||||
DSDA.DoomSyncSettings syncSettings = new()
|
||||
{
|
||||
InputFormat = DoomControllerTypes.Hexen,
|
||||
MultiplayerMode = DSDA.MultiplayerMode.M0,
|
||||
MonstersRespawn = false,
|
||||
FastMonsters = false,
|
||||
NoMonsters = false,
|
||||
CompatibilityMode = DSDA.CompatibilityLevel.C0,
|
||||
SkillLevel = (DSDA.SkillLevel) (1 + input[i++]),
|
||||
InitialEpisode = input[i++],
|
||||
InitialMap = input[i++],
|
||||
Player1Present = input[i++] is not 0,
|
||||
Player1Class = (DSDA.HexenClass) input[i++],
|
||||
Player2Present = input[i++] is not 0,
|
||||
Player2Class = (DSDA.HexenClass) input[i++],
|
||||
Player3Present = input[i++] is not 0,
|
||||
Player3Class = (DSDA.HexenClass) input[i++],
|
||||
Player4Present = input[i++] is not 0,
|
||||
Player4Class = (DSDA.HexenClass) input[i++],
|
||||
};
|
||||
_ = input[i++]; // player 5 isPresent
|
||||
_ = input[i++]; // player 5 class
|
||||
_ = input[i++]; // player 6 isPresent
|
||||
_ = input[i++]; // player 6 class
|
||||
_ = input[i++]; // player 7 isPresent
|
||||
_ = input[i++]; // player 7 class
|
||||
_ = input[i++]; // player 8 isPresent
|
||||
_ = input[i++]; // player 8 class
|
||||
Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(syncSettings);
|
||||
|
||||
var hexenController = new HexenController(1);
|
||||
var controller = new SimpleController(hexenController.Definition);
|
||||
controller.Definition.BuildMnemonicsCache(Result.Movie.SystemID);
|
||||
void ParsePlayer(string playerPfx)
|
||||
{
|
||||
controller.AcceptNewAxis(playerPfx + "Run Speed", unchecked((sbyte) input[i++]));
|
||||
|
||||
controller.AcceptNewAxis(playerPfx + "Strafing Speed", unchecked((sbyte) input[i++]));
|
||||
|
||||
controller.AcceptNewAxis(playerPfx + "Turning Speed", unchecked((sbyte) input[i++]));
|
||||
|
||||
var specialValue = input[i++];
|
||||
controller[playerPfx + "Fire"] = (specialValue & 0b00000001) is not 0;
|
||||
controller[playerPfx + "Action"] = (specialValue & 0b00000010) is not 0;
|
||||
controller.AcceptNewAxis(playerPfx + "Weapon Select", (specialValue & 0b00011100) >> 2);
|
||||
controller[playerPfx + "Alt Weapon"] = (specialValue & 0b00100000) is not 0;
|
||||
|
||||
controller.AcceptNewAxis(playerPfx + "Fly / Look", unchecked((sbyte) input[i++]));
|
||||
|
||||
var useArtifact = input[i++];
|
||||
controller.AcceptNewAxis(playerPfx + "Use Artifact", useArtifact & 0b00111111);
|
||||
controller[playerPfx + "End Player"] = (useArtifact & 0b01000000) is not 0;
|
||||
controller[playerPfx + "Jump"] = (useArtifact & 0b10000000) is not 0;
|
||||
}
|
||||
do
|
||||
{
|
||||
if (syncSettings.Player1Present) ParsePlayer("P1 ");
|
||||
if (syncSettings.Player2Present) ParsePlayer("P2 ");
|
||||
if (syncSettings.Player3Present) ParsePlayer("P3 ");
|
||||
if (syncSettings.Player4Present) ParsePlayer("P4 ");
|
||||
Result.Movie.AppendFrame(controller);
|
||||
if (i == input.Length) throw new Exception("Reached end of input movie stream without finalization byte");
|
||||
}
|
||||
while (input[i] is not 0x80);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using BizHawk.Common.CollectionExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
@ -7,32 +8,10 @@ namespace BizHawk.Client.Common
|
|||
{
|
||||
internal class BkmControllerAdapter : IController
|
||||
{
|
||||
public BkmControllerAdapter(ControllerDefinition definition, string systemId)
|
||||
public BkmControllerAdapter(string systemId)
|
||||
{
|
||||
// We do need to map the definition name to the legacy
|
||||
// controller names that were used back in the bkm days
|
||||
var name = systemId switch
|
||||
{
|
||||
"Lynx" => "Lynx Controller",
|
||||
"SNES" => "SNES Controller",
|
||||
"C64" => "Commodore 64 Controller",
|
||||
"GBA" => "GBA Controller",
|
||||
"A78" => "Atari 7800 ProLine Joystick Controller",
|
||||
"DGB" => "Dual Gameboy Controller",
|
||||
"WSWAN" => "WonderSwan Controller",
|
||||
"N64" => "Nintendo 64 Controller",
|
||||
"SAT" => "Saturn Controller",
|
||||
"GEN" => "GPGX Genesis Controller",
|
||||
"NES" => "NES Controller",
|
||||
"GB" => "Gameboy Controller",
|
||||
"A26" => "Atari 2600 Basic Controller",
|
||||
"TI83" => "TI83 Controller",
|
||||
"Coleco" => "ColecoVision Basic Controller",
|
||||
"SMS Controller" => "SMS",
|
||||
_ => "Null Controller",
|
||||
};
|
||||
Definition = new(copyFrom: definition, withName: name);
|
||||
Definition.BuildMnemonicsCache(systemId); //TODO these aren't the same...
|
||||
Definition = BkmMnemonicConstants.ControllerDefinitions[systemId];
|
||||
Definition.BuildMnemonicsCache(systemId);
|
||||
}
|
||||
|
||||
public ControllerDefinition Definition { get; set; }
|
||||
|
@ -43,167 +22,126 @@ namespace BizHawk.Client.Common
|
|||
public int AxisValue(string name)
|
||||
=> _myAxisControls.GetValueOrDefault(name);
|
||||
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => throw new NotImplementedException(); // no idea --yoshi
|
||||
public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => throw new NotSupportedException();
|
||||
|
||||
public void SetHapticChannelStrength(string name, int strength) => throw new NotImplementedException(); // no idea --yoshi
|
||||
public void SetHapticChannelStrength(string name, int strength) => throw new NotSupportedException();
|
||||
|
||||
private void SetFromMnemonic(ReadOnlySpan<char> mnemonic)
|
||||
{
|
||||
if (mnemonic.IsEmpty) return;
|
||||
var iterator = 0;
|
||||
|
||||
foreach ((string buttonName, AxisSpec? axisSpec) in Definition.ControlsOrdered.Skip(1).SelectMany(static x => x))
|
||||
{
|
||||
while (mnemonic[iterator] == '|') iterator++;
|
||||
|
||||
if (axisSpec.HasValue)
|
||||
{
|
||||
var separatorIndex = iterator + mnemonic[iterator..].IndexOfAny(',', '|');
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
|
||||
var val = int.Parse(mnemonic[iterator..separatorIndex]);
|
||||
#else
|
||||
var axisValueString = mnemonic[iterator..separatorIndex].ToString();
|
||||
var val = int.Parse(axisValueString);
|
||||
#endif
|
||||
_myAxisControls[buttonName] = val;
|
||||
|
||||
iterator = separatorIndex + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_myBoolButtons[buttonName] = mnemonic[iterator] != '.';
|
||||
iterator++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// latches all buttons from the supplied mnemonic string
|
||||
/// </summary>
|
||||
public void SetControllersAsMnemonic(string mnemonic)
|
||||
{
|
||||
switch (ControlType)
|
||||
// _myBoolButtons.Clear();
|
||||
|
||||
switch (Definition.Name)
|
||||
{
|
||||
case "Null Controller":
|
||||
return;
|
||||
case "Lynx Controller":
|
||||
SetLynxControllersAsMnemonic(mnemonic);
|
||||
return;
|
||||
case "SNES Controller":
|
||||
SetSNESControllersAsMnemonic(mnemonic);
|
||||
return;
|
||||
case "Commodore 64 Controller":
|
||||
SetC64ControllersAsMnemonic(mnemonic);
|
||||
return;
|
||||
case "Gameboy Controller":
|
||||
Force("Power", mnemonic[1] == 'P');
|
||||
SetFromMnemonic(mnemonic.AsSpan(3));
|
||||
break;
|
||||
case "GBA Controller":
|
||||
SetGBAControllersAsMnemonic(mnemonic);
|
||||
return;
|
||||
case "Atari 7800 ProLine Joystick Controller":
|
||||
SetAtari7800AsMnemonic(mnemonic);
|
||||
return;
|
||||
case "Dual Gameboy Controller":
|
||||
SetDualGameBoyControllerAsMnemonic(mnemonic);
|
||||
return;
|
||||
case "WonderSwan Controller":
|
||||
SetWonderSwanControllerAsMnemonic(mnemonic);
|
||||
return;
|
||||
case "Nintendo 64 Controller":
|
||||
SetN64ControllersAsMnemonic(mnemonic);
|
||||
return;
|
||||
case "Saturn Controller":
|
||||
SetSaturnControllersAsMnemonic(mnemonic);
|
||||
return;
|
||||
Force("Power", mnemonic[1] == 'P');
|
||||
SetFromMnemonic(mnemonic.AsSpan(3));
|
||||
break;
|
||||
case "GPGX Genesis Controller":
|
||||
{
|
||||
if (IsGenesis6Button())
|
||||
{
|
||||
SetGenesis6ControllersAsMnemonic(mnemonic);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetGenesis3ControllersAsMnemonic(mnemonic);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var c = new MnemonicChecker(mnemonic);
|
||||
|
||||
_myBoolButtons.Clear();
|
||||
|
||||
int start = 3;
|
||||
if (ControlType == "NES Controller")
|
||||
{
|
||||
if (mnemonic.Length < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mnemonic[1])
|
||||
{
|
||||
case 'P':
|
||||
Force("Power", true);
|
||||
break;
|
||||
case 'E':
|
||||
Force("FDS Eject", true);
|
||||
break;
|
||||
case '0':
|
||||
Force("FDS Insert 0", true);
|
||||
break;
|
||||
case '1':
|
||||
Force("FDS Insert 1", true);
|
||||
break;
|
||||
case '2':
|
||||
Force("FDS Insert 2", true);
|
||||
break;
|
||||
case '3':
|
||||
Force("FDS Insert 3", true);
|
||||
break;
|
||||
case 'c':
|
||||
Force("VS Coin 1", true);
|
||||
break;
|
||||
case 'C':
|
||||
Force("VS Coin 2", true);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
if (mnemonic[1] != '.')
|
||||
{
|
||||
Force("Reset", true);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ControlType == "Gameboy Controller")
|
||||
{
|
||||
if (mnemonic.Length < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Force("Power", mnemonic[1] != '.');
|
||||
}
|
||||
|
||||
if (ControlType == "SMS Controller" || ControlType == "TI83 Controller" || ControlType == "ColecoVision Basic Controller")
|
||||
{
|
||||
start = 1;
|
||||
}
|
||||
|
||||
if (ControlType == "Atari 2600 Basic Controller")
|
||||
{
|
||||
if (mnemonic.Length < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Force("Reset", mnemonic[1] != '.' && mnemonic[1] != '0');
|
||||
Force("Select", mnemonic[2] != '.' && mnemonic[2] != '0');
|
||||
start = 4;
|
||||
}
|
||||
|
||||
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
|
||||
{
|
||||
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
|
||||
int ctr = start;
|
||||
if (mnemonic.Length < srcIndex + ctr + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string prefix = "";
|
||||
if (ControlType != "Gameboy Controller" && ControlType != "TI83 Controller")
|
||||
{
|
||||
prefix = $"P{player} ";
|
||||
}
|
||||
|
||||
foreach (string button in BkmMnemonicConstants.Buttons[ControlType].Keys)
|
||||
{
|
||||
Force(prefix + button, c[srcIndex + ctr++]);
|
||||
}
|
||||
}
|
||||
|
||||
if (ControlType == "SMS Controller")
|
||||
{
|
||||
int srcIndex = BkmMnemonicConstants.Players[ControlType] * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
|
||||
int ctr = start;
|
||||
foreach (var command in BkmMnemonicConstants.Commands[ControlType].Keys)
|
||||
{
|
||||
Force(command, c[srcIndex + ctr++]);
|
||||
}
|
||||
Force("Power", mnemonic[1] == 'P');
|
||||
Force("Reset", mnemonic[1] == 'r');
|
||||
SetFromMnemonic(mnemonic.AsSpan(3));
|
||||
break;
|
||||
case "NES Controller":
|
||||
Force("Power", mnemonic[1] == 'P');
|
||||
Force("Reset", mnemonic[1] == 'r');
|
||||
Force("FDS Eject", mnemonic[1] == 'E');
|
||||
Force("FDS Insert 0", mnemonic[1] == '0');
|
||||
Force("FDS Insert 1", mnemonic[1] == '1');
|
||||
Force("FDS Insert 2", mnemonic[1] == '2');
|
||||
Force("FDS Insert 3", mnemonic[1] == '3');
|
||||
Force("VS Coin 1", mnemonic[1] == 'c');
|
||||
Force("VS Coin 2", mnemonic[1] == 'C');
|
||||
SetFromMnemonic(mnemonic.AsSpan(3));
|
||||
break;
|
||||
case "SNES Controller":
|
||||
Force("Power", mnemonic[1] == 'P');
|
||||
Force("Reset", mnemonic[1] == 'r');
|
||||
SetFromMnemonic(mnemonic.AsSpan(3));
|
||||
break;
|
||||
case "PC Engine Controller":
|
||||
SetFromMnemonic(mnemonic.AsSpan(3));
|
||||
break;
|
||||
case "SMS Controller":
|
||||
SetFromMnemonic(mnemonic.AsSpan(1));
|
||||
Force("Pause", mnemonic[^3] == 'p');
|
||||
Force("Reset", mnemonic[^2] == 'r');
|
||||
break;
|
||||
case "TI83 Controller":
|
||||
SetFromMnemonic(mnemonic.AsSpan(1));
|
||||
break;
|
||||
case "Atari 2600 Basic Controller":
|
||||
Force("Reset", mnemonic[1] == 'r');
|
||||
Force("Select", mnemonic[2] == 's');
|
||||
SetFromMnemonic(mnemonic.AsSpan(4));
|
||||
break;
|
||||
case "Atari 7800 ProLine Joystick Controller":
|
||||
Force("Power", mnemonic[1] == 'P');
|
||||
Force("Reset", mnemonic[2] == 'r');
|
||||
Force("Select", mnemonic[3] == 's');
|
||||
Force("Pause", mnemonic[4] == 'p');
|
||||
SetFromMnemonic(mnemonic.AsSpan(6));
|
||||
break;
|
||||
case "Commodore 64 Controller":
|
||||
SetFromMnemonic(mnemonic.AsSpan(1));
|
||||
break;
|
||||
case "ColecoVision Basic Controller":
|
||||
SetFromMnemonic(mnemonic.AsSpan(1));
|
||||
break;
|
||||
case "Nintento 64 Controller":
|
||||
Force("Power", mnemonic[1] == 'P');
|
||||
Force("Reset", mnemonic[1] == 'r');
|
||||
SetFromMnemonic(mnemonic.AsSpan(3));
|
||||
break;
|
||||
case "Saturn Controller":
|
||||
Force("Power", mnemonic[1] == 'P');
|
||||
Force("Reset", mnemonic[1] == 'r');
|
||||
SetFromMnemonic(mnemonic.AsSpan(3));
|
||||
break;
|
||||
case "Dual Gameboy Controller":
|
||||
SetFromMnemonic(mnemonic.AsSpan());
|
||||
break;
|
||||
case "WonderSwan Controller":
|
||||
SetFromMnemonic(mnemonic.AsSpan(1));
|
||||
Force("Power", mnemonic[^3] == 'P');
|
||||
Force("Rotate", mnemonic[^2] == 'R');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,381 +149,9 @@ namespace BizHawk.Client.Common
|
|||
|
||||
private readonly Dictionary<string, bool> _myBoolButtons = new();
|
||||
|
||||
private bool IsGenesis6Button() => Definition.BoolButtons.Contains("P1 X");
|
||||
|
||||
private void Force(string button, bool state)
|
||||
{
|
||||
_myBoolButtons[button] = state;
|
||||
}
|
||||
|
||||
private void Force(string name, int state)
|
||||
{
|
||||
_myAxisControls[name] = state;
|
||||
}
|
||||
|
||||
private string ControlType => Definition.Name;
|
||||
|
||||
private void SetGBAControllersAsMnemonic(string mnemonic)
|
||||
{
|
||||
var c = new MnemonicChecker(mnemonic);
|
||||
_myBoolButtons.Clear();
|
||||
if (mnemonic.Length < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mnemonic[1] == 'P')
|
||||
{
|
||||
Force("Power", true);
|
||||
}
|
||||
|
||||
int start = 3;
|
||||
foreach (string button in BkmMnemonicConstants.Buttons[ControlType].Keys)
|
||||
{
|
||||
Force(button, c[start++]);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetGenesis6ControllersAsMnemonic(string mnemonic)
|
||||
{
|
||||
var c = new MnemonicChecker(mnemonic);
|
||||
_myBoolButtons.Clear();
|
||||
|
||||
if (mnemonic.Length < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mnemonic[1] == 'P')
|
||||
{
|
||||
Force("Power", true);
|
||||
}
|
||||
else if (mnemonic[1] != '.' && mnemonic[1] != '0')
|
||||
{
|
||||
Force("Reset", true);
|
||||
}
|
||||
|
||||
if (mnemonic.Length < 9)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
|
||||
{
|
||||
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
|
||||
|
||||
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int start = 3;
|
||||
foreach (string button in BkmMnemonicConstants.Buttons[ControlType].Keys)
|
||||
{
|
||||
Force($"P{player} {button}", c[srcIndex + start++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetGenesis3ControllersAsMnemonic(string mnemonic)
|
||||
{
|
||||
var c = new MnemonicChecker(mnemonic);
|
||||
_myBoolButtons.Clear();
|
||||
|
||||
if (mnemonic.Length < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mnemonic[1] == 'P')
|
||||
{
|
||||
Force("Power", true);
|
||||
}
|
||||
else if (mnemonic[1] != '.' && mnemonic[1] != '0')
|
||||
{
|
||||
Force("Reset", true);
|
||||
}
|
||||
|
||||
if (mnemonic.Length < 9)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
|
||||
{
|
||||
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons["GPGX 3-Button Controller"].Count + 1);
|
||||
|
||||
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons["GPGX 3-Button Controller"].Count - 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int start = 3;
|
||||
foreach (string button in BkmMnemonicConstants.Buttons["GPGX 3-Button Controller"].Keys)
|
||||
{
|
||||
Force($"P{player} {button}", c[srcIndex + start++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetSNESControllersAsMnemonic(string mnemonic)
|
||||
{
|
||||
var c = new MnemonicChecker(mnemonic);
|
||||
_myBoolButtons.Clear();
|
||||
|
||||
if (mnemonic.Length < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mnemonic[1] == 'P')
|
||||
{
|
||||
Force("Power", true);
|
||||
}
|
||||
else if (mnemonic[1] != '.' && mnemonic[1] != '0')
|
||||
{
|
||||
Force("Reset", true);
|
||||
}
|
||||
|
||||
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
|
||||
{
|
||||
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
|
||||
|
||||
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int start = 3;
|
||||
foreach (var button in BkmMnemonicConstants.Buttons[ControlType].Keys)
|
||||
{
|
||||
Force($"P{player} {button}", c[srcIndex + start++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetLynxControllersAsMnemonic(string mnemonic)
|
||||
{
|
||||
var c = new MnemonicChecker(mnemonic);
|
||||
_myBoolButtons.Clear();
|
||||
|
||||
if (mnemonic.Length < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mnemonic[1] == 'P')
|
||||
{
|
||||
Force("Power", true);
|
||||
}
|
||||
|
||||
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
|
||||
{
|
||||
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
|
||||
|
||||
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int start = 3;
|
||||
foreach (var button in BkmMnemonicConstants.Buttons[ControlType].Keys)
|
||||
{
|
||||
Force(button, c[srcIndex + start++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetN64ControllersAsMnemonic(string mnemonic)
|
||||
{
|
||||
var c = new MnemonicChecker(mnemonic);
|
||||
_myBoolButtons.Clear();
|
||||
|
||||
if (mnemonic.Length < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mnemonic[1] == 'P')
|
||||
{
|
||||
Force("Power", true);
|
||||
}
|
||||
else if (mnemonic[1] != '.' && mnemonic[1] != '0')
|
||||
{
|
||||
Force("Reset", true);
|
||||
}
|
||||
|
||||
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
|
||||
{
|
||||
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + (BkmMnemonicConstants.Analogs[ControlType].Count * 4) + 1 + 1);
|
||||
|
||||
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int start = 3;
|
||||
foreach (string button in BkmMnemonicConstants.Buttons[ControlType].Keys)
|
||||
{
|
||||
Force($"P{player} {button}", c[srcIndex + start++]);
|
||||
}
|
||||
|
||||
foreach (string name in BkmMnemonicConstants.Analogs[ControlType].Keys)
|
||||
{
|
||||
Force($"P{player} {name}", int.Parse(mnemonic.Substring(srcIndex + start, 4)));
|
||||
start += 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetSaturnControllersAsMnemonic(string mnemonic)
|
||||
{
|
||||
var c = new MnemonicChecker(mnemonic);
|
||||
_myBoolButtons.Clear();
|
||||
|
||||
if (mnemonic.Length < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mnemonic[1] == 'P')
|
||||
{
|
||||
Force("Power", true);
|
||||
}
|
||||
else if (mnemonic[1] != '.' && mnemonic[1] != '0')
|
||||
{
|
||||
Force("Reset", true);
|
||||
}
|
||||
|
||||
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
|
||||
{
|
||||
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
|
||||
|
||||
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int start = 3;
|
||||
foreach (string button in BkmMnemonicConstants.Buttons[ControlType].Keys)
|
||||
{
|
||||
Force($"P{player} {button}", c[srcIndex + start++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetAtari7800AsMnemonic(string mnemonic)
|
||||
{
|
||||
var c = new MnemonicChecker(mnemonic);
|
||||
_myBoolButtons.Clear();
|
||||
|
||||
if (mnemonic.Length < 5)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mnemonic[1] == 'P')
|
||||
{
|
||||
Force("Power", true);
|
||||
}
|
||||
|
||||
if (mnemonic[2] == 'r')
|
||||
{
|
||||
Force("Reset", true);
|
||||
}
|
||||
|
||||
if (mnemonic[3] == 's')
|
||||
{
|
||||
Force("Select", true);
|
||||
}
|
||||
|
||||
if (mnemonic[4] == 'p')
|
||||
{
|
||||
Force("Pause", true);
|
||||
}
|
||||
|
||||
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
|
||||
{
|
||||
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
|
||||
int start = 6;
|
||||
if (mnemonic.Length < srcIndex + start + BkmMnemonicConstants.Buttons[ControlType].Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string button in BkmMnemonicConstants.Buttons[ControlType].Keys)
|
||||
{
|
||||
Force($"P{player} {button}", c[srcIndex + start++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDualGameBoyControllerAsMnemonic(string mnemonic)
|
||||
{
|
||||
var checker = new MnemonicChecker(mnemonic);
|
||||
_myBoolButtons.Clear();
|
||||
for (int i = 0; i < BkmMnemonicConstants.DgbMnemonic.Length; i++)
|
||||
{
|
||||
var t = BkmMnemonicConstants.DgbMnemonic[i];
|
||||
if (t.Item1 != null)
|
||||
{
|
||||
Force(t.Item1, checker[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetWonderSwanControllerAsMnemonic(string mnemonic)
|
||||
{
|
||||
var checker = new MnemonicChecker(mnemonic);
|
||||
_myBoolButtons.Clear();
|
||||
for (int i = 0; i < BkmMnemonicConstants.WsMnemonic.Length; i++)
|
||||
{
|
||||
var t = BkmMnemonicConstants.WsMnemonic[i];
|
||||
if (t.Item1 != null)
|
||||
{
|
||||
Force(t.Item1, checker[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetC64ControllersAsMnemonic(string mnemonic)
|
||||
{
|
||||
var c = new MnemonicChecker(mnemonic);
|
||||
_myBoolButtons.Clear();
|
||||
|
||||
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
|
||||
{
|
||||
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
|
||||
|
||||
if (mnemonic.Length < srcIndex + 1 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int start = 1;
|
||||
foreach (var button in BkmMnemonicConstants.Buttons[ControlType].Keys)
|
||||
{
|
||||
Force($"P{player} {button}", c[srcIndex + start++]);
|
||||
}
|
||||
}
|
||||
|
||||
int startKey = 13;
|
||||
foreach (string button in BkmMnemonicConstants.Buttons["Commodore 64 Keyboard"].Keys)
|
||||
{
|
||||
Force(button, c[startKey++]);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class MnemonicChecker
|
||||
{
|
||||
private readonly string _mnemonic;
|
||||
|
||||
public MnemonicChecker(string mnemonic)
|
||||
{
|
||||
_mnemonic = mnemonic;
|
||||
}
|
||||
|
||||
public bool this[int c] => !string.IsNullOrEmpty(_mnemonic) && _mnemonic[c] != '.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
using System.Collections.Generic;
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
internal class BkmControllerDefinition(string name) : ControllerDefinition(name)
|
||||
{
|
||||
// same as ControllerDefinition.GenOrderedControls, just with Axes after BoolButtons
|
||||
protected override IReadOnlyList<IReadOnlyList<(string Name, AxisSpec? AxisSpec)>> GenOrderedControls()
|
||||
{
|
||||
var ret = new List<(string, AxisSpec?)>[PlayerCount + 1];
|
||||
for (var i = 0; i < ret.Length; i++) ret[i] = new();
|
||||
foreach (var btn in BoolButtons) ret[PlayerNumber(btn)].Add((btn, null));
|
||||
foreach ((string buttonName, var axisSpec) in Axes) ret[PlayerNumber(buttonName)].Add((buttonName, axisSpec));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,203 +1,156 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
internal static class BkmMnemonicConstants
|
||||
{
|
||||
public static readonly IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> Buttons = new Dictionary<string, IReadOnlyDictionary<string, string>>
|
||||
public static readonly IReadOnlyDictionary<string, ControllerDefinition> ControllerDefinitions = new Dictionary<string, ControllerDefinition>
|
||||
{
|
||||
["Gameboy Controller"] = new Dictionary<string, string>
|
||||
[VSystemID.Raw.GB] = new BkmControllerDefinition("Gameboy Controller")
|
||||
{
|
||||
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Select"] = "s", ["Start"] = "S", ["B"] = "B", ["A"] = "A"
|
||||
},
|
||||
["Lynx Controller"] = new Dictionary<string, string>
|
||||
BoolButtons = new[] { "Up", "Down", "Left", "Right", "Select", "Start", "B", "A" }.Select(b => $"P1 {b}")
|
||||
.Append("Power")
|
||||
.ToArray()
|
||||
}.MakeImmutable(),
|
||||
[VSystemID.Raw.GBA] = new BkmControllerDefinition("GBA Controller")
|
||||
{
|
||||
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Select"] = "s", ["Start"] = "S", ["B"] = "B", ["A"] = "A"
|
||||
},
|
||||
["GBA Controller"] = new Dictionary<string, string>
|
||||
BoolButtons = new[] { "Up", "Down", "Left", "Right", "Select", "Start", "B", "A", "L", "R" }.Select(b => $"P1 {b}")
|
||||
.Append("Power")
|
||||
.ToArray()
|
||||
}.MakeImmutable(),
|
||||
[VSystemID.Raw.GEN] = new BkmControllerDefinition("GPGX Genesis Controller")
|
||||
{
|
||||
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Select"] = "s", ["Start"] = "S", ["B"] = "B", ["A"] = "A", ["L"] = "L", ["R"] = "R"
|
||||
},
|
||||
["Genesis 3-Button Controller"] = new Dictionary<string, string>
|
||||
BoolButtons = Enumerable.Range(1, 2)
|
||||
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "A", "B", "C", "Start", "X", "Y", "Z", "Mode" }
|
||||
.Select(b => $"P{i} {b}"))
|
||||
.Concat([ "Reset", "Power" ])
|
||||
.ToArray()
|
||||
}.MakeImmutable(),
|
||||
[VSystemID.Raw.NES] = new BkmControllerDefinition("NES Controller")
|
||||
{
|
||||
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Start"] = "S", ["A"] = "A", ["B"] = "B", ["C"] = "C"
|
||||
},
|
||||
["GPGX Genesis Controller"] = new Dictionary<string, string>
|
||||
BoolButtons = Enumerable.Range(1, 4)
|
||||
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "Select", "Start", "B", "A" }
|
||||
.Select(b => $"P{i} {b}"))
|
||||
.Concat([ "Reset", "Power", "FDS Eject", "FDS Insert 0", "FDS Insert 1", "FDS Insert 2", "FDS Insert 3", "VS Coin 1", "VS Coin 2" ])
|
||||
.ToArray()
|
||||
}.MakeImmutable(),
|
||||
[VSystemID.Raw.SNES] = new BkmControllerDefinition("SNES Controller")
|
||||
{
|
||||
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["A"] = "A", ["B"] = "B", ["C"] = "C", ["Start"] = "S", ["X"] = "X", ["Y"] = "Y", ["Z"] = "Z", ["Mode"] = "M"
|
||||
},
|
||||
["GPGX 3-Button Controller"] = new Dictionary<string, string>
|
||||
BoolButtons = Enumerable.Range(1, 4)
|
||||
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "Select", "Start", "B", "A", "X", "Y", "L", "R" }
|
||||
.Select(b => $"P{i} {b}"))
|
||||
.Concat([ "Reset", "Power" ])
|
||||
.ToArray()
|
||||
}.MakeImmutable(),
|
||||
[VSystemID.Raw.PCE] = new BkmControllerDefinition("PC Engine Controller")
|
||||
{
|
||||
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["A"] = "A", ["B"] = "B", ["C"] = "C", ["Start"] = "S"
|
||||
},
|
||||
["NES Controller"] = new Dictionary<string, string>
|
||||
BoolButtons = Enumerable.Range(1, 5)
|
||||
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "Select", "Run", "B2", "B1" }
|
||||
.Select(b => $"P{i} {b}"))
|
||||
.ToArray()
|
||||
}.MakeImmutable(),
|
||||
[VSystemID.Raw.SMS] = new BkmControllerDefinition("SMS Controller")
|
||||
{
|
||||
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Select"] = "s", ["Start"] = "S", ["B"] = "B", ["A"] = "A"
|
||||
},
|
||||
["SNES Controller"] = new Dictionary<string, string>
|
||||
BoolButtons = Enumerable.Range(1, 2)
|
||||
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "B1", "B2" }
|
||||
.Select(b => $"P{i} {b}"))
|
||||
.Concat([ "Pause", "Reset" ])
|
||||
.ToArray()
|
||||
}.MakeImmutable(),
|
||||
[VSystemID.Raw.TI83] = new BkmControllerDefinition("TI83 Controller")
|
||||
{
|
||||
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Select"] = "s", ["Start"] = "S", ["B"] = "B", ["A"] = "A", ["X"] = "X", ["Y"] = "Y", ["L"] = "L", ["R"] = "R"
|
||||
},
|
||||
["PC Engine Controller"] = new Dictionary<string, string>
|
||||
BoolButtons = new[] {
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "DOT", "ON", "ENTER",
|
||||
"UP", "DOWN", "LEFT", "RIGHT", "PLUS", "MINUS", "MULTIPLY", "DIVIDE",
|
||||
"CLEAR", "EXP", "DASH", "PARAOPEN", "PARACLOSE", "TAN", "VARS", "COS",
|
||||
"PRGM", "STAT", "MATRIX", "X", "STO", "LN", "LOG", "SQUARED", "NEG1",
|
||||
"MATH", "ALPHA", "GRAPH", "TRACE", "ZOOM", "WINDOW", "Y", "2ND", "MODE"
|
||||
}.Select(b => $"P1 {b}")
|
||||
.ToArray()
|
||||
}.MakeImmutable(),
|
||||
[VSystemID.Raw.A26] = new BkmControllerDefinition("Atari 2600 Basic Controller")
|
||||
{
|
||||
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Select"] = "s", ["Run"] = "r", ["B2"] = "2", ["B1"] = "1"
|
||||
},
|
||||
["SMS Controller"] = new Dictionary<string, string>
|
||||
BoolButtons = Enumerable.Range(1, 2)
|
||||
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "Button" }
|
||||
.Select(b => $"P{i} {b}"))
|
||||
.Concat([ "Reset", "Select" ])
|
||||
.ToArray()
|
||||
}.MakeImmutable(),
|
||||
[VSystemID.Raw.A78] = new BkmControllerDefinition("Atari 7800 ProLine Joystick Controller")
|
||||
{
|
||||
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["B1"] = "1", ["B2"] = "2"
|
||||
},
|
||||
["TI83 Controller"] = new Dictionary<string, string>
|
||||
BoolButtons = Enumerable.Range(1, 2)
|
||||
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "Trigger", "Trigger 2" }
|
||||
.Select(b => $"P{i} {b}"))
|
||||
.Concat([ "Reset", "Power", "Select", "Pause" ])
|
||||
.ToArray()
|
||||
}.MakeImmutable(),
|
||||
[VSystemID.Raw.C64] = new BkmControllerDefinition("Commodore 64 Controller")
|
||||
{
|
||||
["0"] = "0", ["1"] = "1", ["2"] = "2", ["3"] = "3", ["4"] = "4", ["5"] = "5", ["6"] = "6", ["7"] = "7", ["8"] = "8", ["9"] = "9",
|
||||
["DOT"] = "`", ["ON"] = "O", ["ENTER"] = "=", ["UP"] = "U", ["DOWN"] = "D", ["LEFT"] = "L", ["RIGHT"] = "R", ["PLUS"] = "+", ["MINUS"] = "_", ["MULTIPLY"] = "*",
|
||||
["DIVIDE"] = "/", ["CLEAR"] = "c", ["EXP"] = "^", ["DASH"] = "-", ["PARAOPEN"] = "(", ["PARACLOSE"] = ")", ["TAN"] = "T", ["VARS"] = "V", ["COS"] = "C", ["PRGM"] = "P",
|
||||
["STAT"] = "s", ["MATRIX"] = "m", ["X"] = "X", ["STO"] = ">", ["LN"] = "n", ["LOG"] = "L", ["SQUARED"] = "2", ["NEG1"] = "1", ["MATH"] = "H", ["ALPHA"] = "A",
|
||||
["GRAPH"] = "G", ["TRACE"] = "t", ["ZOOM"] = "Z", ["WINDOW"] = "W", ["Y"] = "Y", ["2ND"] = "&", ["MODE"] = "O"
|
||||
},
|
||||
["Atari 2600 Basic Controller"] = new Dictionary<string, string>
|
||||
BoolButtons = Enumerable.Range(1, 2)
|
||||
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "Button" }
|
||||
.Select(b => $"P{i} {b}"))
|
||||
.Concat([ "Key F1", "Key F3", "Key F5", "Key F7", "Key Left Arrow", "Key 1",
|
||||
"Key 2", "Key 3", "Key 4", "Key 5", "Key 6", "Key 7", "Key 8", "Key 9", "Key 0", "Key Plus",
|
||||
"Key Minus", "Key Pound", "Key Clear/Home", "Key Insert/Delete", "Key Control", "Key Q", "Key W", "Key E",
|
||||
"Key R", "Key T", "Key Y", "Key U", "Key I", "Key O", "Key P", "Key At", "Key Asterisk", "Key Up Arrow",
|
||||
"Key Restore", "Key Run/Stop", "Key Lck", "Key A", "Key S", "Key D", "Key F", "Key G", "Key H", "Key J",
|
||||
"Key K", "Key L", "Key Colon", "Key Semicolon", "Key Equal", "Key Return", "Key Commodore", "Key Left Shift",
|
||||
"Key Z", "Key X", "Key C", "Key V", "Key B", "Key N", "Key M", "Key Comma", "Key Period",
|
||||
"Key Slash", "Key Right Shift", "Key Cursor Up/Down", "Key Cursor Left/Right", "Key Space"
|
||||
])
|
||||
.ToArray()
|
||||
}.MakeImmutable(),
|
||||
[VSystemID.Raw.Coleco] = new BkmControllerDefinition("ColecoVision Basic Controller")
|
||||
{
|
||||
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Button"] = "B"
|
||||
},
|
||||
["Atari 7800 ProLine Joystick Controller"] = new Dictionary<string, string>
|
||||
BoolButtons = Enumerable.Range(1, 2)
|
||||
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "L", "R",
|
||||
"Key1", "Key2", "Key3", "Key4", "Key5", "Key6",
|
||||
"Key7", "Key8", "Key9", "Star", "Key0", "Pound"
|
||||
}.Select(b => $"P{i} {b}"))
|
||||
.ToArray()
|
||||
}.MakeImmutable(),
|
||||
[VSystemID.Raw.N64] = new BkmControllerDefinition("Nintento 64 Controller")
|
||||
{
|
||||
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Trigger"] = "1", ["Trigger 2"] = "2"
|
||||
},
|
||||
["Commodore 64 Controller"] = new Dictionary<string, string>
|
||||
BoolButtons = Enumerable.Range(1, 4)
|
||||
.SelectMany(i => new[] {
|
||||
"DPad U", "DPad D", "DPad L", "DPad R",
|
||||
"B", "A", "Z", "Start", "L", "R",
|
||||
"C Up", "C Down", "C Left", "C Right"
|
||||
}.Select(b => $"P{i} {b}"))
|
||||
.Concat([ "Reset", "Power" ])
|
||||
.ToArray()
|
||||
}.AddXYPair("P1 {0} Axis", AxisPairOrientation.RightAndUp, (-128).RangeTo(127), 0)
|
||||
.AddXYPair("P2 {0} Axis", AxisPairOrientation.RightAndUp, (-128).RangeTo(127), 0)
|
||||
.AddXYPair("P3 {0} Axis", AxisPairOrientation.RightAndUp, (-128).RangeTo(127), 0)
|
||||
.AddXYPair("P4 {0} Axis", AxisPairOrientation.RightAndUp, (-128).RangeTo(127), 0)
|
||||
.MakeImmutable(),
|
||||
[VSystemID.Raw.SAT] = new BkmControllerDefinition("Saturn Controller")
|
||||
{
|
||||
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Button"] = "B"
|
||||
},
|
||||
["Commodore 64 Keyboard"] = new Dictionary<string, string>
|
||||
BoolButtons = Enumerable.Range(1, 2)
|
||||
.SelectMany(i => new[] { "Up", "Down", "Left", "Right", "Start", "X", "Y", "Z", "A", "B", "C", "L", "R" }
|
||||
.Select(b => $"P{i} {b}"))
|
||||
.Concat([ "Reset", "Power" ])
|
||||
.ToArray()
|
||||
}.MakeImmutable(),
|
||||
["DGB"] = new BkmControllerDefinition("Dual Gameboy Controller")
|
||||
{
|
||||
["Key F1"] = "1", ["Key F3"] = "3", ["Key F5"] = "5", ["Key F7"] = "7", ["Key Left Arrow"] = "l", ["Key 1"] = "1",
|
||||
["Key 2"] = "2", ["Key 3"] = "3", ["Key 4"] = "4", ["Key 5"] = "5", ["Key 6"] = "6", ["Key 7"] = "7", ["Key 8"] = "8", ["Key 9"] = "9", ["Key 0"] = "0", ["Key Plus"] = "+",
|
||||
["Key Minus"] = "-", ["Key Pound"] = "l", ["Key Clear/Home"] = "c", ["Key Insert/Delete"] = "i", ["Key Control"] = "c", ["Key Q"] = "Q", ["Key W"] = "W", ["Key E"] = "E",
|
||||
["Key R"] = "R", ["Key T"] = "T", ["Key Y"] = "Y", ["Key U"] = "U", ["Key I"] = "I", ["Key O"] = "O", ["Key P"] = "P", ["Key At"] = "@", ["Key Asterisk"] = "*", ["Key Up Arrow"] = "u",
|
||||
["Key Restore"] = "r", ["Key Run/Stop"] = "s", ["Key Lck"] = "k", ["Key A"] = "A", ["Key S"] = "S", ["Key D"] = "D", ["Key F"] = "F", ["Key G"] = "G", ["Key H"] = "H", ["Key J"] = "J",
|
||||
["Key K"] = "K", ["Key L"] = "L", ["Key Colon"] = ":", ["Key Semicolon"] = ";", ["Key Equal"] = "=", ["Key Return"] = "e", ["Key Commodore"] = "o", ["Key Left Shift"] = "s",
|
||||
["Key Z"] = "Z", ["Key X"] = "X", ["Key C"] = "C", ["Key V"] = "V", ["Key B"] = "B", ["Key N"] = "N", ["Key M"] = "M", ["Key Comma"] = ",",
|
||||
["Key Period"] = ">", ["Key Slash"] = "/", ["Key Right Shift"] = "s", ["Key Cursor Up/Down"] = "u", ["Key Cursor Left/Right"] = "l", ["Key Space"] = "_"
|
||||
},
|
||||
["ColecoVision Basic Controller"] = new Dictionary<string, string>
|
||||
BoolButtons = Enumerable.Range(1, 2)
|
||||
.SelectMany(i => new[] { "Power", "Up", "Down", "Left", "Right", "Select", "Start", "B", "A" }
|
||||
.Select(b => $"P{i} {b}"))
|
||||
.ToArray()
|
||||
}.MakeImmutable(),
|
||||
[VSystemID.Raw.WSWAN] = new BkmControllerDefinition("WonderSwan Controller")
|
||||
{
|
||||
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["L"] = "l", ["R"] = "r",
|
||||
["Key1"] = "1", ["Key2"] = "2", ["Key3"] = "3", ["Key4"] = "4", ["Key5"] = "5", ["Key6"] = "6",
|
||||
["Key7"] = "7", ["Key8"] = "8", ["Key9"] = "9", ["Star"] = "*", ["Key0"] = "0", ["Pound"] = "#"
|
||||
},
|
||||
["Nintendo 64 Controller"] = new Dictionary<string, string>
|
||||
{
|
||||
["DPad U"] = "U", ["DPad D"] = "D", ["DPad L"] = "L", ["DPad R"] = "R",
|
||||
["B"] = "B", ["A"] = "A", ["Z"] = "Z", ["Start"] = "S", ["L"] = "L", ["R"] = "R",
|
||||
["C Up"] = "u", ["C Down"] = "d", ["C Left"] = "l", ["C Right"] = "r"
|
||||
},
|
||||
["Saturn Controller"] = new Dictionary<string, string>
|
||||
{
|
||||
["Up"] = "U", ["Down"] = "D", ["Left"] = "L", ["Right"] = "R", ["Start"] = "S", ["X"] = "X", ["Y"] = "Y", ["Z"] = "Z", ["A"] = "A", ["B"] = "B", ["C"] = "C", ["L"] = "l", ["R"] = "r"
|
||||
}
|
||||
};
|
||||
|
||||
public static readonly IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> Analogs = new Dictionary<string, IReadOnlyDictionary<string, string>>
|
||||
{
|
||||
["Nintendo 64 Controller"] = new Dictionary<string, string> { ["X Axis"] = "X" , ["Y Axis"] = "Y" }
|
||||
};
|
||||
|
||||
public static readonly IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> Commands = new Dictionary<string, IReadOnlyDictionary<string, string>>
|
||||
{
|
||||
["Atari 2600 Basic Controller"] = new Dictionary<string, string> { ["Reset"] = "r", ["Select"] = "s" },
|
||||
["Atari 7800 ProLine Joystick Controller"] = new Dictionary<string, string> { ["Reset"] = "r", ["Select"] = "s" },
|
||||
["Gameboy Controller"] = new Dictionary<string, string> { ["Power"] = "P" },
|
||||
["GBA Controller"] = new Dictionary<string, string> { ["Power"] = "P" },
|
||||
["Genesis 3-Button Controller"] = new Dictionary<string, string> { ["Reset"] = "r" },
|
||||
["GPGX Genesis Controller"] = new Dictionary<string, string> { ["Power"] = "P", ["Reset"] = "r" },
|
||||
["NES Controller"] = new Dictionary<string, string> { ["Reset"] = "r", ["Power"] = "P", ["FDS Eject"] = "E", ["FDS Insert 0"] = "0", ["FDS Insert 1"] = "1", ["VS Coin 1"] = "c", ["VS Coin 2"] = "C" },
|
||||
["SNES Controller"] = new Dictionary<string, string> { ["Power"] = "P", ["Reset"] = "r" },
|
||||
["PC Engine Controller"] = new Dictionary<string, string>(),
|
||||
["SMS Controller"] = new Dictionary<string, string> { ["Pause"] = "p", ["Reset"] = "r" },
|
||||
["TI83 Controller"] = new Dictionary<string, string>(),
|
||||
["Nintendo 64 Controller"] = new Dictionary<string, string> { ["Power"] = "P", ["Reset"] = "r" },
|
||||
["Saturn Controller"] = new Dictionary<string, string> { ["Power"] = "P", ["Reset"] = "r" },
|
||||
["GPGX 3-Button Controller"] = new Dictionary<string, string> { ["Power"] = "P", ["Reset"] = "r" }
|
||||
};
|
||||
|
||||
public static readonly IReadOnlyDictionary<string, int> Players = new Dictionary<string, int>
|
||||
{
|
||||
["Gameboy Controller"] = 1,
|
||||
["GBA Controller"] = 1,
|
||||
["Genesis 3-Button Controller"] = 2,
|
||||
["GPGX Genesis Controller"] = 2,
|
||||
["NES Controller"] = 4,
|
||||
["SNES Controller"] = 4,
|
||||
["PC Engine Controller"] = 5,
|
||||
["SMS Controller"] = 2,
|
||||
["TI83 Controller"] = 1,
|
||||
["Atari 2600 Basic Controller"] = 2,
|
||||
["Atari 7800 ProLine Joystick Controller"] = 2,
|
||||
["ColecoVision Basic Controller"] = 2,
|
||||
["Commodore 64 Controller"] = 2,
|
||||
["Nintendo 64 Controller"] = 4,
|
||||
["Saturn Controller"] = 2,
|
||||
["GPGX 3-Button Controller"] = 2,
|
||||
["Lynx Controller"] = 1
|
||||
};
|
||||
|
||||
// just experimenting with different possibly more painful ways to handle mnemonics
|
||||
// |P|UDLRsSBA|
|
||||
public static readonly (string, char)[] DgbMnemonic =
|
||||
{
|
||||
(null, '|'),
|
||||
("P1 Power", 'P'),
|
||||
(null, '|'),
|
||||
("P1 Up", 'U'),
|
||||
("P1 Down", 'D'),
|
||||
("P1 Left", 'L'),
|
||||
("P1 Right", 'R'),
|
||||
("P1 Select", 's'),
|
||||
("P1 Start", 'S'),
|
||||
("P1 B", 'B'),
|
||||
("P1 A", 'A'),
|
||||
(null, '|'),
|
||||
("P2 Power", 'P'),
|
||||
(null, '|'),
|
||||
("P2 Up", 'U'),
|
||||
("P2 Down", 'D'),
|
||||
("P2 Left", 'L'),
|
||||
("P2 Right", 'R'),
|
||||
("P2 Select", 's'),
|
||||
("P2 Start", 'S'),
|
||||
("P2 B", 'B'),
|
||||
("P2 A", 'A'),
|
||||
(null, '|')
|
||||
};
|
||||
|
||||
public static readonly (string, char)[] WsMnemonic =
|
||||
{
|
||||
(null, '|'),
|
||||
("P1 X1", '1'),
|
||||
("P1 X3", '3'),
|
||||
("P1 X4", '4'),
|
||||
("P1 X2", '2'),
|
||||
("P1 Y1", '1'),
|
||||
("P1 Y3", '3'),
|
||||
("P1 Y4", '4'),
|
||||
("P1 Y2", '2'),
|
||||
("P1 Start", 'S'),
|
||||
("P1 B", 'B'),
|
||||
("P1 A", 'A'),
|
||||
(null, '|'),
|
||||
("P2 X1", '1'),
|
||||
("P2 X3", '3'),
|
||||
("P2 X4", '4'),
|
||||
("P2 X2", '2'),
|
||||
("P2 Y1", '1'),
|
||||
("P2 Y3", '3'),
|
||||
("P2 Y4", '4'),
|
||||
("P2 Y2", '2'),
|
||||
("P2 Start", 'S'),
|
||||
("P2 B", 'B'),
|
||||
("P2 A", 'A'),
|
||||
(null, '|'),
|
||||
("Power", 'P'),
|
||||
("Rotate", 'R'),
|
||||
(null, '|')
|
||||
BoolButtons = Enumerable.Range(1, 2)
|
||||
.SelectMany(i => new[] { "X1", "X3", "X4", "X2", "Y1", "Y3", "Y4", "Y2", "Start", "B", "A" }
|
||||
.Select(b => $"P{i} {b}"))
|
||||
.Concat([ "Power", "Rotate" ])
|
||||
.ToArray()
|
||||
}.MakeImmutable()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,25 +2,26 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
|
||||
using BizHawk.Common.StringExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
internal class BkmMovie
|
||||
{
|
||||
private BkmControllerAdapter _adapter;
|
||||
|
||||
private readonly List<string> _log = new List<string>();
|
||||
public BkmHeader Header { get; } = new BkmHeader();
|
||||
public string Filename { get; set; } = "";
|
||||
public bool Loaded { get; private set; }
|
||||
public int InputLogLength => Loaded ? _log.Count : 0;
|
||||
|
||||
public BkmControllerAdapter GetInputState(int frame, ControllerDefinition definition, string sytemId)
|
||||
public BkmControllerAdapter GetInputState(int frame, string sytemId)
|
||||
{
|
||||
if (frame < InputLogLength && frame >= 0)
|
||||
{
|
||||
var adapter = new BkmControllerAdapter(definition, sytemId);
|
||||
adapter.SetControllersAsMnemonic(_log[frame]);
|
||||
return adapter;
|
||||
_adapter ??= new BkmControllerAdapter(sytemId);
|
||||
_adapter.SetControllersAsMnemonic(_log[frame]);
|
||||
return _adapter;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -30,6 +31,8 @@ namespace BizHawk.Client.Common
|
|||
|
||||
public IList<string> Comments => Header.Comments;
|
||||
|
||||
public string GenerateLogKey => Bk2LogEntryGenerator.GenerateLogKey(_adapter.Definition);
|
||||
|
||||
public string SyncSettingsJson
|
||||
{
|
||||
get => Header[HeaderKeys.SyncSettings];
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace BizHawk.Client.Common
|
|||
IMovieController GenerateMovieController(ControllerDefinition definition = null, string logKey = null);
|
||||
|
||||
void HandleFrameBefore();
|
||||
void HandleFrameAfter();
|
||||
void HandleFrameAfter(bool ignoreMovieEndAction);
|
||||
void HandleSaveState(TextWriter writer);
|
||||
|
||||
bool CheckSavestateTimeline(TextReader reader);
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
#if NET8_0_OR_GREATER
|
||||
using System.Linq;
|
||||
#endif
|
||||
|
||||
using BizHawk.Common.CollectionExtensions;
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace BizHawk.Client.Common
|
|||
public int CurrentBufferSize { get; set; } = 256;
|
||||
|
||||
[DisplayName("Current - Target Frame Length")]
|
||||
[Description("Desired frame length (number of emulated frames you can go back before running out of buffer)\n\nThe Current buffer is the primary buffer used near the last edited frame. This should be the largest buffer to ensure minimal gaps during editing.")]
|
||||
[Description("Desired minimum rewind range (number of emulated frames you can go back before running out of buffer)\n\nThe Current buffer is the primary buffer used near the last edited frame. This should be the largest buffer to ensure minimal gaps during editing.")]
|
||||
[Range(1, int.MaxValue)]
|
||||
[TypeConverter(typeof(ConstrainedIntConverter))]
|
||||
public int CurrentTargetFrameLength { get; set; } = 500;
|
||||
|
@ -67,7 +67,7 @@ namespace BizHawk.Client.Common
|
|||
public int RecentBufferSize { get; set; } = 128;
|
||||
|
||||
[DisplayName("Recent - Target Frame Length")]
|
||||
[Description("Desired frame length (number of emulated frames you can go back before running out of buffer).\n\nThe Recent buffer is where the current frames decay as the buffer fills up. The goal of this buffer is to maximize the amount of movie that can be fairly quickly navigated to. Therefore, a high target frame length is ideal here.")]
|
||||
[Description("Desired minimum rewind range (number of emulated frames you can go back before running out of buffer).\n\nThe Recent buffer is where the current frames decay as the buffer fills up. The goal of this buffer is to maximize the amount of movie that can be fairly quickly navigated to. Therefore, a high target frame length is ideal here.")]
|
||||
[Range(1, int.MaxValue)]
|
||||
[TypeConverter(typeof(ConstrainedIntConverter))]
|
||||
public int RecentTargetFrameLength { get; set; } = 2000;
|
||||
|
@ -90,7 +90,7 @@ namespace BizHawk.Client.Common
|
|||
public int GapsBufferSize { get; set; } = 64;
|
||||
|
||||
[DisplayName("Gaps - Target Frame Length")]
|
||||
[Description("Desired frame length (number of emulated frames you can go back before running out of buffer)\n\nThe Gap buffer is used for temporary storage when replaying older segment of the run without editing. It is used to 're-greenzone' large gaps while navigating around in an older area of the movie. This buffer can be small, and a similar size to target frame length ratio as current is ideal.")]
|
||||
[Description("Desired minimum rewind range (number of emulated frames you can go back before running out of buffer)\n\nThe Gap buffer is used for temporary storage when replaying older segment of the run without editing. It is used to 're-greenzone' large gaps while navigating around in an older area of the movie. This buffer can be small, and a similar size to target frame length ratio as current is ideal.")]
|
||||
[Range(1, int.MaxValue)]
|
||||
[TypeConverter(typeof(ConstrainedIntConverter))]
|
||||
public int GapsTargetFrameLength { get; set; } = 125;
|
||||
|
|
|
@ -145,7 +145,7 @@ namespace BizHawk.Client.Common
|
|||
var sizeRatio = Size / (float)_states[HeadStateIndex].Size;
|
||||
var frameRatio = _targetFrameLength / sizeRatio;
|
||||
|
||||
var idealInterval = (int)Math.Round(frameRatio);
|
||||
var idealInterval = (int)Math.Ceiling(frameRatio);
|
||||
return Math.Max(idealInterval, 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -267,6 +267,7 @@ namespace BizHawk.Client.Common.RamSearchEngine
|
|||
|
||||
public void AddRange(IEnumerable<long> addresses, bool append)
|
||||
{
|
||||
using var @lock = Domain.EnterExit();
|
||||
var list = _settings.Size switch
|
||||
{
|
||||
WatchSize.Byte => addresses.ToBytes(_settings),
|
||||
|
@ -278,6 +279,54 @@ namespace BizHawk.Client.Common.RamSearchEngine
|
|||
_watchList = (append ? _watchList.Concat(list) : list).ToArray();
|
||||
}
|
||||
|
||||
public void ConvertTo(WatchSize size)
|
||||
{
|
||||
using var @lock = Domain.EnterExit();
|
||||
var maxAddress = Domain.Size - (int)size;
|
||||
var addresses = AllAddresses().Where(address => address <= maxAddress);
|
||||
_watchList = size switch
|
||||
{
|
||||
WatchSize.Byte => addresses.ToBytes(_settings).ToArray(),
|
||||
WatchSize.Word when _settings.CheckMisAligned => addresses.ToWords(_settings).ToArray(),
|
||||
WatchSize.Word => addresses.Where(static address => address % 2 == 0).ToWords(_settings).ToArray(),
|
||||
WatchSize.DWord when _settings.CheckMisAligned => addresses.ToDWords(_settings).ToArray(),
|
||||
WatchSize.DWord => addresses.Where(static address => address % 4 == 0).ToDWords(_settings).ToArray(),
|
||||
_ => _watchList
|
||||
};
|
||||
|
||||
_settings.Size = size;
|
||||
}
|
||||
|
||||
private IEnumerable<long> AllAddresses()
|
||||
{
|
||||
foreach (var watch in _watchList)
|
||||
{
|
||||
if (_settings.CheckMisAligned)
|
||||
{
|
||||
yield return watch.Address;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (_settings.Size)
|
||||
{
|
||||
case WatchSize.Word:
|
||||
yield return watch.Address;
|
||||
yield return watch.Address + 1;
|
||||
break;
|
||||
case WatchSize.DWord:
|
||||
yield return watch.Address;
|
||||
yield return watch.Address + 1;
|
||||
yield return watch.Address + 2;
|
||||
yield return watch.Address + 3;
|
||||
break;
|
||||
default:
|
||||
yield return watch.Address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Sort(string column, bool reverse)
|
||||
{
|
||||
switch (column)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#nullable enable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
using BizHawk.Common;
|
||||
|
@ -91,6 +92,11 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
public sealed class CellList : SortedList<Cell>
|
||||
{
|
||||
public CellList() {}
|
||||
|
||||
public CellList(IEnumerable<Cell> collection)
|
||||
: base(collection) {}
|
||||
|
||||
/// <remarks>restore the distinctness invariant from <see cref="System.Collections.Generic.SortedSet{T}"/>; though I don't think we actually rely on it anywhere --yoshi</remarks>
|
||||
public override void Add(Cell item)
|
||||
{
|
||||
|
@ -117,6 +123,9 @@ namespace BizHawk.Client.EmuHawk
|
|||
return i >= 0 && _list[i].RowIndex == rowIndex;
|
||||
}
|
||||
#endif
|
||||
|
||||
public new CellList Slice(int start, int length)
|
||||
=> new(SliceImpl(start: start, length: length));
|
||||
}
|
||||
|
||||
public static class CellExtensions
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
private readonly IControlRenderer _renderer;
|
||||
|
||||
private readonly CellList _selectedItems = new();
|
||||
private CellList _selectedItems = new();
|
||||
|
||||
// scrollbar location(s) are calculated later (e.g. on resize)
|
||||
private readonly VScrollBar _vBar = new VScrollBar { Visible = false };
|
||||
|
@ -269,10 +269,10 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
_rowCount = value;
|
||||
|
||||
//TODO replace this with a binary search + truncate
|
||||
if (_selectedItems.LastOrDefault()?.RowIndex >= _rowCount)
|
||||
{
|
||||
_selectedItems.RemoveAll(i => i.RowIndex >= _rowCount);
|
||||
var iLastToKeep = _selectedItems.LowerBoundBinarySearch(static c => c.RowIndex ?? -1, _rowCount);
|
||||
_selectedItems = _selectedItems.Slice(start: 0, length: iLastToKeep + 1);
|
||||
}
|
||||
|
||||
RecalculateScrollBars();
|
||||
|
|
|
@ -61,5 +61,7 @@
|
|||
bool WantsToControlRestartMovie { get; }
|
||||
|
||||
bool RestartMovie();
|
||||
|
||||
bool WantsToBypassMovieEndAction { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -183,13 +183,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
MovieEndRecordMenuItem.Checked = Config.Movies.MovieEndAction == MovieEndAction.Record;
|
||||
MovieEndStopMenuItem.Checked = Config.Movies.MovieEndAction == MovieEndAction.Stop;
|
||||
MovieEndPauseMenuItem.Checked = Config.Movies.MovieEndAction == MovieEndAction.Pause;
|
||||
|
||||
// Arguably an IControlMainForm property should be set here, but in reality only Tastudio is ever going to interfere with this logic
|
||||
MovieEndFinishMenuItem.Enabled =
|
||||
MovieEndRecordMenuItem.Enabled =
|
||||
MovieEndStopMenuItem.Enabled =
|
||||
MovieEndPauseMenuItem.Enabled =
|
||||
!Tools.Has<TAStudio>();
|
||||
}
|
||||
|
||||
private void AVSubMenu_DropDownOpened(object sender, EventArgs e)
|
||||
|
|
|
@ -21,6 +21,7 @@ using BizHawk.Emulation.Cores.Computers.DOS;
|
|||
using BizHawk.Emulation.Cores.Computers.AmstradCPC;
|
||||
using BizHawk.Emulation.Cores.Computers.AppleII;
|
||||
using BizHawk.Emulation.Cores.Computers.Commodore64;
|
||||
using BizHawk.Emulation.Cores.Computers.Doom;
|
||||
using BizHawk.Emulation.Cores.Computers.MSX;
|
||||
using BizHawk.Emulation.Cores.Computers.SinclairSpectrum;
|
||||
using BizHawk.Emulation.Cores.Computers.TIC80;
|
||||
|
@ -1181,7 +1182,10 @@ namespace BizHawk.Client.EmuHawk
|
|||
// DOSBox
|
||||
items.Add(CreateCoreSubmenu(VSystemCategory.PCs, CoreNames.DOSBox, CreateGenericCoreConfigItem<DOSBox>(CoreNames.DOSBox)));
|
||||
|
||||
// Emu83
|
||||
// DSDA-Doom
|
||||
items.Add(CreateCoreSubmenu(VSystemCategory.Other, CoreNames.DSDA, CreateGenericCoreConfigItem<DSDA>(CoreNames.DSDA)));
|
||||
|
||||
// Emu83
|
||||
items.Add(CreateCoreSubmenu(VSystemCategory.Other, CoreNames.Emu83, CreateSettingsItem("Palette...", (_, _) => OpenTI83PaletteSettingsDialog(GetSettingsAdapterFor<Emu83>()))));
|
||||
|
||||
// Encore
|
||||
|
@ -1523,6 +1527,24 @@ namespace BizHawk.Client.EmuHawk
|
|||
DisplayDefaultCoreMenu();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
this.ClientSize = new System.Drawing.Size(284, 261);
|
||||
this.Name = "MainForm";
|
||||
this.Load += new System.EventHandler(this.MainForm_Load);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
private void MainForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,6 +75,15 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
}
|
||||
|
||||
#if BIZHAWKBUILD_SUPERHAWK
|
||||
ToolStripMenuItemEx superHawkThrottleMenuItem = new() { Text = "SUPER·HAWK" };
|
||||
superHawkThrottleMenuItem.Click += (_, _) => Config.SuperHawkThrottle = !Config.SuperHawkThrottle;
|
||||
SpeedSkipSubMenu.DropDownItems.Insert(
|
||||
SpeedSkipSubMenu.DropDownItems.IndexOf(MinimizeSkippingMenuItem),
|
||||
superHawkThrottleMenuItem);
|
||||
ConfigSubMenu.DropDownOpened += (_, _) => superHawkThrottleMenuItem.Checked = Config.SuperHawkThrottle;
|
||||
#endif
|
||||
|
||||
foreach (var (appliesTo, coreNames) in Config.CorePickerUIData)
|
||||
{
|
||||
var submenu = new ToolStripMenuItem { Text = string.Join(" | ", appliesTo) };
|
||||
|
@ -1109,6 +1118,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
private IControlMainform ToolControllingStopMovie => Tools.FirstOrNull<IControlMainform>(tool => tool.WantsToControlStopMovie);
|
||||
private IControlMainform ToolControllingRestartMovie => Tools.FirstOrNull<IControlMainform>(tool => tool.WantsToControlRestartMovie);
|
||||
private IControlMainform ToolControllingReadOnly => Tools.FirstOrNull<IControlMainform>(tool => tool.WantsToControlReadOnly);
|
||||
private IControlMainform ToolBypassingMovieEndAction => Tools.FirstOrNull<IControlMainform>(tool => tool.WantsToBypassMovieEndAction);
|
||||
|
||||
private DisplayManager DisplayManager;
|
||||
|
||||
|
@ -2913,7 +2923,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
private void StepRunLoop_Core(bool force = false)
|
||||
{
|
||||
var runFrame = false;
|
||||
_runloopFrameAdvance = false;
|
||||
var currentTimestamp = Stopwatch.GetTimestamp();
|
||||
|
||||
double frameAdvanceTimestampDeltaMs = (double)(currentTimestamp - _frameAdvanceTimestamp) / Stopwatch.Frequency * 1000.0;
|
||||
|
@ -2937,14 +2946,21 @@ namespace BizHawk.Client.EmuHawk
|
|||
else
|
||||
{
|
||||
PauseEmulator();
|
||||
oldFrameAdvanceCondition = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (oldFrameAdvanceCondition || FrameInch)
|
||||
bool frameAdvance = oldFrameAdvanceCondition || FrameInch;
|
||||
if (!frameAdvance && _runloopFrameAdvance && _runloopFrameProgress)
|
||||
{
|
||||
// handle release of frame advance
|
||||
_runloopFrameProgress = false;
|
||||
PauseEmulator();
|
||||
}
|
||||
|
||||
_runloopFrameAdvance = frameAdvance;
|
||||
if (frameAdvance)
|
||||
{
|
||||
FrameInch = false;
|
||||
_runloopFrameAdvance = true;
|
||||
|
||||
// handle the initial trigger of a frame advance
|
||||
if (_frameAdvanceTimestamp == 0)
|
||||
|
@ -2966,22 +2982,20 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
else
|
||||
{
|
||||
// handle release of frame advance: do we need to deactivate FrameProgress?
|
||||
if (_runloopFrameProgress)
|
||||
{
|
||||
_runloopFrameProgress = false;
|
||||
PauseEmulator();
|
||||
}
|
||||
|
||||
_frameAdvanceTimestamp = 0;
|
||||
}
|
||||
|
||||
#if BIZHAWKBUILD_SUPERHAWK
|
||||
if (!EmulatorPaused && (!Config.SuperHawkThrottle || InputManager.ClientControls.AnyInputHeld))
|
||||
#else
|
||||
if (!EmulatorPaused)
|
||||
#endif
|
||||
{
|
||||
runFrame = true;
|
||||
}
|
||||
|
||||
bool isRewinding = Rewind(ref runFrame, currentTimestamp, out var returnToRecording);
|
||||
_runloopFrameProgress |= isRewinding;
|
||||
|
||||
float atten = 0;
|
||||
|
||||
|
@ -3068,7 +3082,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
bool render = !InvisibleEmulation && (!_throttle.skipNextFrame || _currAviWriter?.UsesVideo is true || atTurboSeekEnd);
|
||||
bool newFrame = Emulator.FrameAdvance(InputManager.ControllerOutput, render, renderSound);
|
||||
|
||||
MovieSession.HandleFrameAfter();
|
||||
MovieSession.HandleFrameAfter(ToolBypassingMovieEndAction is not null);
|
||||
|
||||
if (returnToRecording)
|
||||
{
|
||||
|
|
|
@ -14,6 +14,8 @@ using BizHawk.Common.StringExtensions;
|
|||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.DiscSystem;
|
||||
|
||||
using CE = BizHawk.Common.CollectionExtensions.CollectionExtensions;
|
||||
|
||||
#pragma warning disable BHI1007 // target-typed Exception TODO don't
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
|
@ -38,7 +40,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
};
|
||||
|
||||
var buf2048 = new byte[2048];
|
||||
var buffer = new List<byte>();
|
||||
List<ArraySegment<byte>> bitsToHash = new();
|
||||
|
||||
int FirstDataTrackLBA()
|
||||
{
|
||||
|
@ -58,13 +60,13 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
var slba = FirstDataTrackLBA();
|
||||
dsr.ReadLBA_2048(slba + 1, buf2048, 0);
|
||||
buffer.AddRange(new ArraySegment<byte>(buf2048, 128 - 22, 22));
|
||||
bitsToHash.Add(new(buf2048, offset: 128 - 22, count: 22));
|
||||
var bootSector = (buf2048[0] << 16) | (buf2048[1] << 8) | buf2048[2];
|
||||
var numSectors = buf2048[3];
|
||||
for (var i = 0; i < numSectors; i++)
|
||||
{
|
||||
dsr.ReadLBA_2048(slba + bootSector + i, buf2048, 0);
|
||||
buffer.AddRange(buf2048);
|
||||
bitsToHash.Add(new(buf2048));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -72,13 +74,13 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
var slba = FirstDataTrackLBA();
|
||||
dsr.ReadLBA_2048(slba + 1, buf2048, 0);
|
||||
buffer.AddRange(new ArraySegment<byte>(buf2048, 0, 128));
|
||||
bitsToHash.Add(new(buf2048, offset: 0, count: 128));
|
||||
var bootSector = (buf2048[35] << 24) | (buf2048[34] << 16) | (buf2048[33] << 8) | buf2048[32];
|
||||
var numSectors = (buf2048[39] << 24) | (buf2048[38] << 16) | (buf2048[37] << 8) | buf2048[36];
|
||||
for (var i = 0; i < numSectors; i++)
|
||||
{
|
||||
dsr.ReadLBA_2048(slba + bootSector + i, buf2048, 0);
|
||||
buffer.AddRange(buf2048);
|
||||
bitsToHash.Add(new(buf2048));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -191,7 +193,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
exePath = exePath[index..endIndex];
|
||||
}
|
||||
|
||||
buffer.AddRange(Encoding.ASCII.GetBytes(exePath));
|
||||
bitsToHash.Add(new(Encoding.ASCII.GetBytes(exePath)));
|
||||
|
||||
// get sector for exe
|
||||
sector = GetFileSector(exePath, out var exeSize);
|
||||
|
@ -204,13 +206,13 @@ namespace BizHawk.Client.EmuHawk
|
|||
exeSize = ((buf2048[31] << 24) | (buf2048[30] << 16) | (buf2048[29] << 8) | buf2048[28]) + 2048;
|
||||
}
|
||||
|
||||
buffer.AddRange(new ArraySegment<byte>(buf2048, 0, Math.Min(2048, exeSize)));
|
||||
bitsToHash.Add(new(buf2048, offset: 0, count: Math.Min(2048, exeSize)));
|
||||
exeSize -= 2048;
|
||||
|
||||
while (exeSize > 0)
|
||||
{
|
||||
dsr.ReadLBA_2048(sector++, buf2048, 0);
|
||||
buffer.AddRange(new ArraySegment<byte>(buf2048, 0, Math.Min(2048, exeSize)));
|
||||
bitsToHash.Add(new(buf2048, offset: 0, count: Math.Min(2048, exeSize)));
|
||||
exeSize -= 2048;
|
||||
}
|
||||
|
||||
|
@ -219,7 +221,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
case ConsoleID.SegaCD:
|
||||
case ConsoleID.Saturn:
|
||||
dsr.ReadLBA_2048(0, buf2048, 0);
|
||||
buffer.AddRange(new ArraySegment<byte>(buf2048, 0, 512));
|
||||
bitsToHash.Add(new(buf2048, offset: 0, count: 512));
|
||||
break;
|
||||
case ConsoleID.JaguarCD:
|
||||
var discHasher = new DiscHasher(disc);
|
||||
|
@ -230,7 +232,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
};
|
||||
}
|
||||
|
||||
var hash = MD5Checksum.ComputeDigestHex(buffer.ToArray());
|
||||
var hash = MD5Checksum.ComputeDigestHex(CE.ConcatArrays(bitsToHash));
|
||||
return IdentifyHash(hash);
|
||||
}
|
||||
|
||||
|
|
|
@ -258,7 +258,7 @@
|
|||
//
|
||||
// TargetFrameLengthNumeric
|
||||
//
|
||||
this.TargetFrameLengthNumeric.Location = new System.Drawing.Point(146, 138);
|
||||
this.TargetFrameLengthNumeric.Location = new System.Drawing.Point(236, 138);
|
||||
this.TargetFrameLengthNumeric.Maximum = new decimal(new int[] {
|
||||
500000,
|
||||
0,
|
||||
|
@ -277,6 +277,7 @@
|
|||
0,
|
||||
0,
|
||||
0});
|
||||
this.TargetFrameLengthNumeric.ValueChanged += new System.EventHandler(this.FrameLength_ValueChanged);
|
||||
//
|
||||
// TargetRewindIntervalNumeric
|
||||
//
|
||||
|
@ -299,6 +300,7 @@
|
|||
0,
|
||||
0,
|
||||
0});
|
||||
this.TargetRewindIntervalNumeric.ValueChanged += new System.EventHandler(this.RewindInterval_ValueChanged);
|
||||
//
|
||||
// EstTimeLabel
|
||||
//
|
||||
|
@ -537,7 +539,7 @@
|
|||
this.TargetFrameLengthRadioButton.Size = new System.Drawing.Size(125, 17);
|
||||
this.TargetFrameLengthRadioButton.TabIndex = 48;
|
||||
this.TargetFrameLengthRadioButton.TabStop = true;
|
||||
this.TargetFrameLengthRadioButton.Text = "Desired frame length:";
|
||||
this.TargetFrameLengthRadioButton.Text = "Desired minimum rewind range in frames:";
|
||||
this.TargetFrameLengthRadioButton.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// TargetRewindIntervalRadioButton
|
||||
|
@ -550,6 +552,7 @@
|
|||
this.TargetRewindIntervalRadioButton.TabStop = true;
|
||||
this.TargetRewindIntervalRadioButton.Text = "Rewinds every fixed number of frames: ";
|
||||
this.TargetRewindIntervalRadioButton.UseVisualStyleBackColor = true;
|
||||
this.TargetRewindIntervalRadioButton.CheckedChanged += new System.EventHandler(this.RewindInterval_CheckedChanged);
|
||||
//
|
||||
// RewindConfig
|
||||
//
|
||||
|
@ -636,4 +639,4 @@
|
|||
private System.Windows.Forms.RadioButton TargetFrameLengthRadioButton;
|
||||
private System.Windows.Forms.RadioButton TargetRewindIntervalRadioButton;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
FullnessLabel.Text = $"{fullnessRatio:P2}";
|
||||
var stateCount = rewinder.Count;
|
||||
RewindFramesUsedLabel.Text = stateCount.ToString();
|
||||
_avgStateSize = stateCount is 0 ? 0UL : (ulong) Math.Round(rewinder.Size * fullnessRatio / stateCount);
|
||||
_avgStateSize = stateCount is 0 ? (ulong) _statableCore.CloneSavestate().Length : (ulong) Math.Round(rewinder.Size * fullnessRatio / stateCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -155,17 +155,26 @@ namespace BizHawk.Client.EmuHawk
|
|||
private void CalculateEstimates()
|
||||
{
|
||||
double estFrames = 0.0;
|
||||
var bufferSize = 1L << (int) BufferSizeUpDown.Value;
|
||||
labelEx1.Text = bufferSize.ToString();
|
||||
int calculatedRewindInterval = TargetRewindIntervalRadioButton.Checked ? (int) TargetRewindIntervalNumeric.Value : 1;
|
||||
if (_avgStateSize is not 0UL)
|
||||
{
|
||||
var bufferSize = 1L << (int) BufferSizeUpDown.Value;
|
||||
labelEx1.Text = bufferSize.ToString();
|
||||
bufferSize *= 1024 * 1024;
|
||||
estFrames = bufferSize / (double) _avgStateSize;
|
||||
if (TargetFrameLengthRadioButton.Checked)
|
||||
calculatedRewindInterval = (int) Math.Ceiling((int) TargetFrameLengthNumeric.Value / estFrames);
|
||||
}
|
||||
ApproxFramesLabel.Text = $"{estFrames:n0} frames";
|
||||
EstTimeLabel.Text = $"{estFrames / _framerate / 60.0:n} minutes";
|
||||
EstTimeLabel.Text = $"{estFrames / _framerate / 60.0 * calculatedRewindInterval:n} minutes";
|
||||
}
|
||||
|
||||
private void FrameLength_ValueChanged(object sender, EventArgs e) => CalculateEstimates();
|
||||
|
||||
private void RewindInterval_ValueChanged(object sender, EventArgs e) => CalculateEstimates();
|
||||
|
||||
private void RewindInterval_CheckedChanged(object sender, EventArgs e) => CalculateEstimates();
|
||||
|
||||
private void BufferSizeUpDown_ValueChanged(object sender, EventArgs e)
|
||||
{
|
||||
CalculateEstimates();
|
||||
|
|
|
@ -45,6 +45,8 @@
|
|||
public bool RestartMovie()
|
||||
=> false;
|
||||
|
||||
public bool WantsToBypassMovieEndAction => false;
|
||||
|
||||
// TODO: We want to prevent movies and probably other things
|
||||
}
|
||||
}
|
||||
|
|
|
@ -500,7 +500,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
if (Engaged())
|
||||
{
|
||||
Tastudio.QueryItemBgColorCallback = (index, name) => _th.SafeParseColor(luaf.Call(index, name)?[0]);
|
||||
Tastudio.QueryItemBgColorCallback = (index, name) => _th.SafeParseColor(luaf.Call(index, name)?.FirstOrDefault());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -523,7 +523,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
Tastudio.QueryItemIconCallback = (index, name) =>
|
||||
{
|
||||
var result = luaf.Call(index, name);
|
||||
if (result?[0] != null)
|
||||
if (result?.FirstOrDefault() is not null)
|
||||
{
|
||||
return _iconCache.GetValueOrPutNew1(result[0].ToString()).ToBitmap();
|
||||
}
|
||||
|
|
|
@ -48,7 +48,8 @@
|
|||
this.MultiDiskMenuStrip.ImageScalingSize = new System.Drawing.Size(20, 20);
|
||||
this.MultiDiskMenuStrip.Location = new System.Drawing.Point(0, 0);
|
||||
this.MultiDiskMenuStrip.Name = "MultiDiskMenuStrip";
|
||||
this.MultiDiskMenuStrip.Size = new System.Drawing.Size(675, 24);
|
||||
this.MultiDiskMenuStrip.Padding = new System.Windows.Forms.Padding(4, 2, 0, 2);
|
||||
this.MultiDiskMenuStrip.Size = new System.Drawing.Size(506, 24);
|
||||
this.MultiDiskMenuStrip.TabIndex = 0;
|
||||
this.MultiDiskMenuStrip.Text = "menuStrip1";
|
||||
//
|
||||
|
@ -56,10 +57,9 @@
|
|||
//
|
||||
this.SaveRunButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.SaveRunButton.Enabled = false;
|
||||
this.SaveRunButton.Location = new System.Drawing.Point(424, 405);
|
||||
this.SaveRunButton.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.SaveRunButton.Location = new System.Drawing.Point(318, 329);
|
||||
this.SaveRunButton.Name = "SaveRunButton";
|
||||
this.SaveRunButton.Size = new System.Drawing.Size(113, 28);
|
||||
this.SaveRunButton.Size = new System.Drawing.Size(85, 23);
|
||||
this.SaveRunButton.TabIndex = 9;
|
||||
this.SaveRunButton.Text = "Save and &Run";
|
||||
this.SaveRunButton.UseVisualStyleBackColor = true;
|
||||
|
@ -69,10 +69,9 @@
|
|||
//
|
||||
this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.CancelBtn.Location = new System.Drawing.Point(545, 405);
|
||||
this.CancelBtn.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.CancelBtn.Location = new System.Drawing.Point(409, 329);
|
||||
this.CancelBtn.Name = "CancelBtn";
|
||||
this.CancelBtn.Size = new System.Drawing.Size(113, 28);
|
||||
this.CancelBtn.Size = new System.Drawing.Size(85, 23);
|
||||
this.CancelBtn.TabIndex = 10;
|
||||
this.CancelBtn.Text = "&Close";
|
||||
this.CancelBtn.UseVisualStyleBackColor = true;
|
||||
|
@ -84,11 +83,9 @@
|
|||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.grpName.Controls.Add(this.BrowseBtn);
|
||||
this.grpName.Controls.Add(this.NameBox);
|
||||
this.grpName.Location = new System.Drawing.Point(11, 34);
|
||||
this.grpName.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.grpName.Location = new System.Drawing.Point(8, 28);
|
||||
this.grpName.Name = "grpName";
|
||||
this.grpName.Padding = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.grpName.Size = new System.Drawing.Size(648, 55);
|
||||
this.grpName.Size = new System.Drawing.Size(486, 45);
|
||||
this.grpName.TabIndex = 11;
|
||||
this.grpName.TabStop = false;
|
||||
this.grpName.Text = "Output Bundle Path";
|
||||
|
@ -96,10 +93,9 @@
|
|||
// BrowseBtn
|
||||
//
|
||||
this.BrowseBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.BrowseBtn.Location = new System.Drawing.Point(556, 22);
|
||||
this.BrowseBtn.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.BrowseBtn.Location = new System.Drawing.Point(417, 18);
|
||||
this.BrowseBtn.Name = "BrowseBtn";
|
||||
this.BrowseBtn.Size = new System.Drawing.Size(84, 28);
|
||||
this.BrowseBtn.Size = new System.Drawing.Size(63, 23);
|
||||
this.BrowseBtn.TabIndex = 14;
|
||||
this.BrowseBtn.Text = "Browse...";
|
||||
this.BrowseBtn.UseVisualStyleBackColor = true;
|
||||
|
@ -109,10 +105,9 @@
|
|||
//
|
||||
this.NameBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.NameBox.Location = new System.Drawing.Point(8, 23);
|
||||
this.NameBox.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.NameBox.Location = new System.Drawing.Point(6, 19);
|
||||
this.NameBox.Name = "NameBox";
|
||||
this.NameBox.Size = new System.Drawing.Size(539, 22);
|
||||
this.NameBox.Size = new System.Drawing.Size(405, 20);
|
||||
this.NameBox.TabIndex = 0;
|
||||
this.NameBox.TextChanged += new System.EventHandler(this.NameBox_TextChanged);
|
||||
//
|
||||
|
@ -124,21 +119,20 @@
|
|||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.FileSelectorPanel.AutoScroll = true;
|
||||
this.FileSelectorPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.FileSelectorPanel.Location = new System.Drawing.Point(11, 124);
|
||||
this.FileSelectorPanel.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.FileSelectorPanel.Location = new System.Drawing.Point(8, 101);
|
||||
this.FileSelectorPanel.Name = "FileSelectorPanel";
|
||||
this.FileSelectorPanel.Size = new System.Drawing.Size(647, 263);
|
||||
this.FileSelectorPanel.Size = new System.Drawing.Size(486, 214);
|
||||
this.FileSelectorPanel.TabIndex = 12;
|
||||
this.FileSelectorPanel.DragDrop += new System.Windows.Forms.DragEventHandler(this.OnDragDrop);
|
||||
this.FileSelectorPanel.DragEnter += new System.Windows.Forms.DragEventHandler(this.OnDragEnter);
|
||||
this.FileSelectorPanel.Paint += new System.Windows.Forms.PaintEventHandler(this.FileSelectorPanel_Paint);
|
||||
//
|
||||
// AddButton
|
||||
//
|
||||
this.AddButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.AddButton.Location = new System.Drawing.Point(11, 405);
|
||||
this.AddButton.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.AddButton.Location = new System.Drawing.Point(8, 329);
|
||||
this.AddButton.Name = "AddButton";
|
||||
this.AddButton.Size = new System.Drawing.Size(80, 28);
|
||||
this.AddButton.Size = new System.Drawing.Size(60, 23);
|
||||
this.AddButton.TabIndex = 13;
|
||||
this.AddButton.Text = "Add";
|
||||
this.AddButton.UseVisualStyleBackColor = true;
|
||||
|
@ -149,28 +143,25 @@
|
|||
this.SystemDropDown.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.SystemDropDown.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.SystemDropDown.FormattingEnabled = true;
|
||||
this.SystemDropDown.Location = new System.Drawing.Point(540, 92);
|
||||
this.SystemDropDown.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.SystemDropDown.Location = new System.Drawing.Point(405, 75);
|
||||
this.SystemDropDown.Name = "SystemDropDown";
|
||||
this.SystemDropDown.Size = new System.Drawing.Size(117, 24);
|
||||
this.SystemDropDown.Size = new System.Drawing.Size(89, 21);
|
||||
this.SystemDropDown.TabIndex = 14;
|
||||
this.SystemDropDown.SelectedIndexChanged += new System.EventHandler(this.SystemDropDown_SelectedIndexChanged);
|
||||
//
|
||||
// SystemLabel
|
||||
//
|
||||
this.SystemLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.SystemLabel.Location = new System.Drawing.Point(473, 96);
|
||||
this.SystemLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.SystemLabel.Location = new System.Drawing.Point(355, 78);
|
||||
this.SystemLabel.Name = "SystemLabel";
|
||||
this.SystemLabel.Text = "System:";
|
||||
//
|
||||
// btnRemove
|
||||
//
|
||||
this.btnRemove.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.btnRemove.Location = new System.Drawing.Point(99, 405);
|
||||
this.btnRemove.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.btnRemove.Location = new System.Drawing.Point(74, 329);
|
||||
this.btnRemove.Name = "btnRemove";
|
||||
this.btnRemove.Size = new System.Drawing.Size(80, 28);
|
||||
this.btnRemove.Size = new System.Drawing.Size(60, 23);
|
||||
this.btnRemove.TabIndex = 16;
|
||||
this.btnRemove.Text = "Remove";
|
||||
this.btnRemove.UseVisualStyleBackColor = true;
|
||||
|
@ -180,10 +171,9 @@
|
|||
//
|
||||
this.SaveButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.SaveButton.Enabled = false;
|
||||
this.SaveButton.Location = new System.Drawing.Point(303, 405);
|
||||
this.SaveButton.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.SaveButton.Location = new System.Drawing.Point(227, 329);
|
||||
this.SaveButton.Name = "SaveButton";
|
||||
this.SaveButton.Size = new System.Drawing.Size(113, 28);
|
||||
this.SaveButton.Size = new System.Drawing.Size(85, 23);
|
||||
this.SaveButton.TabIndex = 18;
|
||||
this.SaveButton.Text = "&Save";
|
||||
this.SaveButton.UseVisualStyleBackColor = true;
|
||||
|
@ -191,9 +181,9 @@
|
|||
//
|
||||
// MultiDiskBundler
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(675, 448);
|
||||
this.ClientSize = new System.Drawing.Size(506, 364);
|
||||
this.Controls.Add(this.SaveButton);
|
||||
this.Controls.Add(this.btnRemove);
|
||||
this.Controls.Add(this.SystemLabel);
|
||||
|
@ -205,7 +195,6 @@
|
|||
this.Controls.Add(this.SaveRunButton);
|
||||
this.Controls.Add(this.MultiDiskMenuStrip);
|
||||
this.MainMenuStrip = this.MultiDiskMenuStrip;
|
||||
this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.Name = "MultiDiskBundler";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Load += new System.EventHandler(this.MultiDiskBundler_Load);
|
||||
|
|
|
@ -39,6 +39,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
VSystemID.Raw.Arcade,
|
||||
VSystemID.Raw.C64,
|
||||
VSystemID.Raw.DOS,
|
||||
VSystemID.Raw.Doom,
|
||||
VSystemID.Raw.GBL,
|
||||
VSystemID.Raw.GEN,
|
||||
VSystemID.Raw.GGL,
|
||||
|
@ -344,5 +345,10 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
|
||||
}
|
||||
|
||||
private void FileSelectorPanel_Paint(object sender, PaintEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,5 +113,7 @@
|
|||
|
||||
public bool WantsToControlReboot => false;
|
||||
public void RebootCore() => throw new NotSupportedException("This should never be called");
|
||||
|
||||
public bool WantsToBypassMovieEndAction => true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,16 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
}
|
||||
|
||||
protected override void UpdateBefore()
|
||||
{
|
||||
if (CurrentTasMovie.IsAtEnd())
|
||||
{
|
||||
CurrentTasMovie.RecordFrame(CurrentTasMovie.Emulator.Frame, MovieSession.StickySource);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void FastUpdateBefore() => UpdateBefore();
|
||||
|
||||
protected override void GeneralUpdate()
|
||||
{
|
||||
RefreshDialog();
|
||||
|
|
|
@ -357,7 +357,11 @@ namespace BizHawk.Client.EmuHawk
|
|||
catch (Exception ex)
|
||||
{
|
||||
text = "";
|
||||
DialogController.ShowMessageBox($"oops\n{ex}");
|
||||
DialogController.ShowMessageBox("Encountered unrecoverable error while drawing the input roll.\n" +
|
||||
"The current movie will be closed without saving.\n" +
|
||||
$"The exception was:\n\n{ex}", caption: "Failed to draw input roll");
|
||||
TastudioStopMovie();
|
||||
StartNewTasMovie();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
private readonly List<TasClipboardEntry> _tasClipboard = new List<TasClipboardEntry>();
|
||||
private const string CursorColumnName = "CursorColumn";
|
||||
private const string FrameColumnName = "FrameColumn";
|
||||
private MovieEndAction _originalEndAction; // The movie end behavior selected by the user (that is overridden by TAStudio)
|
||||
private UndoHistoryForm _undoForm;
|
||||
private Timer _autosaveTimer;
|
||||
|
||||
|
@ -288,14 +287,11 @@ namespace BizHawk.Client.EmuHawk
|
|||
// Attempts to load failed, abort
|
||||
if (!success)
|
||||
{
|
||||
Disengage();
|
||||
return false;
|
||||
}
|
||||
|
||||
MainForm.AddOnScreenMessage("TAStudio engaged");
|
||||
_originalEndAction = Config.Movies.MovieEndAction;
|
||||
MainForm.DisableRewind();
|
||||
Config.Movies.MovieEndAction = MovieEndAction.Record;
|
||||
MainForm.SetMainformMovieInfo();
|
||||
MovieSession.ReadOnly = true;
|
||||
SetSplicer();
|
||||
|
@ -674,7 +670,6 @@ namespace BizHawk.Client.EmuHawk
|
|||
_engaged = false;
|
||||
MainForm.PauseOnFrame = null;
|
||||
MainForm.AddOnScreenMessage("TAStudio disengaged");
|
||||
Config.Movies.MovieEndAction = _originalEndAction;
|
||||
WantsToControlRewind = false;
|
||||
MainForm.EnableRewind(true);
|
||||
MainForm.SetMainformMovieInfo();
|
||||
|
|
|
@ -315,7 +315,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
MessageLabel.Text = "Search restarted";
|
||||
DoDomainSizeCheck();
|
||||
_dropdownDontfire = true;
|
||||
SetSize(_settings.Size); // Calls NewSearch() automatically
|
||||
SetSize(_settings.Size);
|
||||
NewSearch();
|
||||
_dropdownDontfire = false;
|
||||
HardSetDisplayTypeDropDown(_settings.Type);
|
||||
}
|
||||
|
@ -691,7 +692,12 @@ namespace BizHawk.Client.EmuHawk
|
|||
DifferenceBox.ByteSize = size;
|
||||
DifferentByBox.ByteSize = size;
|
||||
|
||||
NewSearch();
|
||||
_searches.ConvertTo(_settings.Size);
|
||||
_searches.SetType(_settings.Type);
|
||||
UpdateList();
|
||||
_searches.ClearHistory();
|
||||
|
||||
ToggleSearchDependentToolBarItems();
|
||||
}
|
||||
|
||||
private void PopulateTypeDropDown()
|
||||
|
|
|
@ -1,26 +1,50 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
public class SortedList<T> : ICollection<T>
|
||||
public class SortedList<T> : IList<T>
|
||||
where T : IComparable<T>
|
||||
{
|
||||
private const string ERR_MSG_OUT_OF_ORDER = "setting/inserting elements must preserve ordering";
|
||||
|
||||
protected readonly List<T> _list;
|
||||
|
||||
public virtual int Count => _list.Count;
|
||||
|
||||
public virtual bool IsReadOnly { get; } = false;
|
||||
|
||||
public SortedList() => _list = new List<T>();
|
||||
protected SortedList(List<T> wrapped)
|
||||
=> _list = wrapped;
|
||||
|
||||
public SortedList()
|
||||
: this(new()) {}
|
||||
|
||||
public SortedList(IEnumerable<T> collection)
|
||||
: this(new(collection))
|
||||
{
|
||||
_list = new List<T>(collection);
|
||||
_list.Sort();
|
||||
}
|
||||
|
||||
public virtual T this[int index] => _list[index];
|
||||
public virtual T this[int index]
|
||||
{
|
||||
get => _list[index];
|
||||
set
|
||||
{
|
||||
// NOT allowing appends, to match BCL `List<T>`
|
||||
if (index < 0 || Count <= index) throw new ArgumentOutOfRangeException(paramName: nameof(index), index, message: $"index must be in 0..<{Count}");
|
||||
if (Count is 0)
|
||||
{
|
||||
_list.Add(value);
|
||||
return;
|
||||
}
|
||||
var willBeGeqPrevious = index is 0 || value.CompareTo(_list[index - 1]) >= 0;
|
||||
var willBeLeqFollowing = index == Count - 1 || _list[index + 1].CompareTo(value) >= 0;
|
||||
if (willBeGeqPrevious && willBeLeqFollowing) _list[index] = value;
|
||||
else throw new NotSupportedException(ERR_MSG_OUT_OF_ORDER);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Add(T item)
|
||||
{
|
||||
|
@ -55,6 +79,21 @@ namespace BizHawk.Common
|
|||
return i < 0 ? -1 : i;
|
||||
}
|
||||
|
||||
public virtual void Insert(int index, T item)
|
||||
{
|
||||
// allowing appends per `IList<T>` docs
|
||||
if (index < 0 || Count < index) throw new ArgumentOutOfRangeException(paramName: nameof(index), index, message: $"index must be in 0..{Count}");
|
||||
if (Count is 0)
|
||||
{
|
||||
_list.Add(item);
|
||||
return;
|
||||
}
|
||||
var willBeGeqPrevious = index is 0 || item.CompareTo(_list[index - 1]) >= 0;
|
||||
var willBeLeqFollowing = index >= Count - 1 || _list[index].CompareTo(item) >= 0;
|
||||
if (willBeGeqPrevious && willBeLeqFollowing) _list.Insert(index, item);
|
||||
else throw new NotSupportedException(ERR_MSG_OUT_OF_ORDER);
|
||||
}
|
||||
|
||||
public T LastOrDefault()
|
||||
=> _list.Count is 0 ? default! : _list[_list.Count - 1];
|
||||
|
||||
|
@ -97,6 +136,12 @@ namespace BizHawk.Common
|
|||
}
|
||||
}
|
||||
|
||||
public SortedList<T> Slice(int start, int length)
|
||||
=> new(SliceImpl(start: start, length: length));
|
||||
|
||||
protected List<T> SliceImpl(int start, int length)
|
||||
=> _list.Skip(start).Take(length).ToList();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,6 +149,36 @@ namespace BizHawk.Common.CollectionExtensions
|
|||
return combined;
|
||||
}
|
||||
|
||||
/// <returns>freshly-allocated array</returns>
|
||||
public static T[] ConcatArrays<T>(/*params*/ IReadOnlyCollection<T[]> arrays)
|
||||
{
|
||||
var combinedLength = arrays.Sum(static a => a.Length); //TODO detect overflow
|
||||
if (combinedLength is 0) return Array.Empty<T>();
|
||||
var combined = new T[combinedLength];
|
||||
var i = 0;
|
||||
foreach (var arr in arrays)
|
||||
{
|
||||
arr.AsSpan().CopyTo(combined.AsSpan(start: i));
|
||||
i += arr.Length;
|
||||
}
|
||||
return combined;
|
||||
}
|
||||
|
||||
/// <returns>freshly-allocated array</returns>
|
||||
public static T[] ConcatArrays<T>(/*params*/ IReadOnlyCollection<ArraySegment<T>> arrays)
|
||||
{
|
||||
var combinedLength = arrays.Sum(static a => a.Count); //TODO detect overflow
|
||||
if (combinedLength is 0) return Array.Empty<T>();
|
||||
var combined = new T[combinedLength];
|
||||
var i = 0;
|
||||
foreach (var arr in arrays)
|
||||
{
|
||||
arr.AsSpan().CopyTo(combined.AsSpan(start: i));
|
||||
i += arr.Count;
|
||||
}
|
||||
return combined;
|
||||
}
|
||||
|
||||
public static bool CountIsAtLeast<T>(this IEnumerable<T> collection, int n)
|
||||
=> collection is ICollection countable
|
||||
? countable.Count >= n
|
||||
|
@ -165,10 +195,6 @@ namespace BizHawk.Common.CollectionExtensions
|
|||
/// If the key is not present, returns default(TValue).
|
||||
/// backported from .NET Core 2.0
|
||||
/// </summary>
|
||||
public static TValue? GetValueOrDefault<TKey, TValue>(IDictionary<TKey, TValue> dictionary, TKey key)
|
||||
=> dictionary.TryGetValue(key, out var found) ? found : default;
|
||||
|
||||
/// <inheritdoc cref="GetValueOrDefault{K,V}(IDictionary{K,V},K)"/>
|
||||
public static TValue? GetValueOrDefault<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key)
|
||||
=> dictionary.TryGetValue(key, out var found) ? found : default;
|
||||
|
||||
|
@ -177,10 +203,6 @@ namespace BizHawk.Common.CollectionExtensions
|
|||
/// If the key is not present, returns <paramref name="defaultValue"/>.
|
||||
/// backported from .NET Core 2.0
|
||||
/// </summary>
|
||||
public static TValue? GetValueOrDefault<TKey, TValue>(IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue)
|
||||
=> dictionary.TryGetValue(key, out var found) ? found : defaultValue;
|
||||
|
||||
/// <inheritdoc cref="GetValueOrDefault{K,V}(IDictionary{K,V},K,V)"/>
|
||||
public static TValue? GetValueOrDefault<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue)
|
||||
=> dictionary.TryGetValue(key, out var found) ? found : defaultValue;
|
||||
#endif
|
||||
|
@ -310,9 +332,11 @@ namespace BizHawk.Common.CollectionExtensions
|
|||
return str.Substring(startIndex: offset, length: length);
|
||||
}
|
||||
|
||||
#if !NET8_0_OR_GREATER
|
||||
/// <summary>shallow clone</summary>
|
||||
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> list)
|
||||
public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> list) where TKey : notnull
|
||||
=> list.ToDictionary(static kvp => kvp.Key, static kvp => kvp.Value);
|
||||
#endif
|
||||
|
||||
public static bool IsSortedAsc<T>(this IReadOnlyList<T> list)
|
||||
where T : IComparable<T>
|
||||
|
|
|
@ -5,16 +5,13 @@
|
|||
* and https://github.com/dotnet/runtime/blob/v9.0.0/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ValueListBuilder.cs
|
||||
*/
|
||||
|
||||
#if !NET8_0_OR_GREATER
|
||||
#if !NET9_0_OR_GREATER
|
||||
#pragma warning disable RS0030 // `Debug.Assert` w/o message, breaks BizHawk convention
|
||||
#pragma warning disable SA1514 // "Element documentation header should be preceded by blank line"
|
||||
|
||||
using System.Buffers;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
using System.Runtime.Intrinsics;
|
||||
#endif
|
||||
|
||||
namespace BizHawk.Common
|
||||
{
|
||||
|
@ -534,13 +531,6 @@ namespace BizHawk.Common
|
|||
sep0 = separators[0];
|
||||
sep1 = separators.Length > 1 ? separators[1] : sep0;
|
||||
sep2 = separators.Length > 2 ? separators[2] : sep1;
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
if (Vector128.IsHardwareAccelerated && source.Length >= Vector128<ushort>.Count * 2)
|
||||
{
|
||||
MakeSeparatorListVectorized(source, ref sepListBuilder, sep0, sep1, sep2);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < source.Length; i++)
|
||||
{
|
||||
|
@ -583,120 +573,6 @@ namespace BizHawk.Common
|
|||
}
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_0_OR_GREATER
|
||||
private static void MakeSeparatorListVectorized(ReadOnlySpan<char> sourceSpan, ref ValueListBuilder<int> sepListBuilder, char c, char c2, char c3)
|
||||
{
|
||||
// Redundant test so we won't prejit remainder of this method
|
||||
// on platforms where it is not supported
|
||||
if (!Vector128.IsHardwareAccelerated)
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
Debug.Assert(sourceSpan.Length >= Vector128<ushort>.Count);
|
||||
nuint lengthToExamine = (uint)sourceSpan.Length;
|
||||
nuint offset = 0;
|
||||
ref char source = ref MemoryMarshal.GetReference(sourceSpan);
|
||||
|
||||
if (Vector512.IsHardwareAccelerated && lengthToExamine >= (uint)Vector512<ushort>.Count*2)
|
||||
{
|
||||
Vector512<ushort> v1 = Vector512.Create((ushort)c);
|
||||
Vector512<ushort> v2 = Vector512.Create((ushort)c2);
|
||||
Vector512<ushort> v3 = Vector512.Create((ushort)c3);
|
||||
|
||||
do
|
||||
{
|
||||
Vector512<ushort> vector = Vector512.LoadUnsafe(ref source, offset);
|
||||
Vector512<ushort> v1Eq = Vector512.Equals(vector, v1);
|
||||
Vector512<ushort> v2Eq = Vector512.Equals(vector, v2);
|
||||
Vector512<ushort> v3Eq = Vector512.Equals(vector, v3);
|
||||
Vector512<byte> cmp = (v1Eq | v2Eq | v3Eq).AsByte();
|
||||
|
||||
if (cmp != Vector512<byte>.Zero)
|
||||
{
|
||||
// Skip every other bit
|
||||
ulong mask = cmp.ExtractMostSignificantBits() & 0x5555555555555555;
|
||||
do
|
||||
{
|
||||
uint bitPos = (uint)BitOperations.TrailingZeroCount(mask) / sizeof(char);
|
||||
sepListBuilder.Append((int)(offset + bitPos));
|
||||
mask = BitOperations.ResetLowestSetBit(mask);
|
||||
} while (mask != 0);
|
||||
}
|
||||
|
||||
offset += (nuint)Vector512<ushort>.Count;
|
||||
} while (offset <= lengthToExamine - (nuint)Vector512<ushort>.Count);
|
||||
}
|
||||
else if (Vector256.IsHardwareAccelerated && lengthToExamine >= (uint)Vector256<ushort>.Count*2)
|
||||
{
|
||||
Vector256<ushort> v1 = Vector256.Create((ushort)c);
|
||||
Vector256<ushort> v2 = Vector256.Create((ushort)c2);
|
||||
Vector256<ushort> v3 = Vector256.Create((ushort)c3);
|
||||
|
||||
do
|
||||
{
|
||||
Vector256<ushort> vector = Vector256.LoadUnsafe(ref source, offset);
|
||||
Vector256<ushort> v1Eq = Vector256.Equals(vector, v1);
|
||||
Vector256<ushort> v2Eq = Vector256.Equals(vector, v2);
|
||||
Vector256<ushort> v3Eq = Vector256.Equals(vector, v3);
|
||||
Vector256<byte> cmp = (v1Eq | v2Eq | v3Eq).AsByte();
|
||||
|
||||
if (cmp != Vector256<byte>.Zero)
|
||||
{
|
||||
// Skip every other bit
|
||||
uint mask = cmp.ExtractMostSignificantBits() & 0x55555555;
|
||||
do
|
||||
{
|
||||
uint bitPos = (uint)BitOperations.TrailingZeroCount(mask) / sizeof(char);
|
||||
sepListBuilder.Append((int)(offset + bitPos));
|
||||
mask = BitOperations.ResetLowestSetBit(mask);
|
||||
} while (mask != 0);
|
||||
}
|
||||
|
||||
offset += (nuint)Vector256<ushort>.Count;
|
||||
} while (offset <= lengthToExamine - (nuint)Vector256<ushort>.Count);
|
||||
}
|
||||
else if (Vector128.IsHardwareAccelerated)
|
||||
{
|
||||
Vector128<ushort> v1 = Vector128.Create((ushort)c);
|
||||
Vector128<ushort> v2 = Vector128.Create((ushort)c2);
|
||||
Vector128<ushort> v3 = Vector128.Create((ushort)c3);
|
||||
|
||||
do
|
||||
{
|
||||
Vector128<ushort> vector = Vector128.LoadUnsafe(ref source, offset);
|
||||
Vector128<ushort> v1Eq = Vector128.Equals(vector, v1);
|
||||
Vector128<ushort> v2Eq = Vector128.Equals(vector, v2);
|
||||
Vector128<ushort> v3Eq = Vector128.Equals(vector, v3);
|
||||
Vector128<byte> cmp = (v1Eq | v2Eq | v3Eq).AsByte();
|
||||
|
||||
if (cmp != Vector128<byte>.Zero)
|
||||
{
|
||||
// Skip every other bit
|
||||
uint mask = cmp.ExtractMostSignificantBits() & 0x5555;
|
||||
do
|
||||
{
|
||||
uint bitPos = (uint)BitOperations.TrailingZeroCount(mask) / sizeof(char);
|
||||
sepListBuilder.Append((int)(offset + bitPos));
|
||||
mask = BitOperations.ResetLowestSetBit(mask);
|
||||
} while (mask != 0);
|
||||
}
|
||||
|
||||
offset += (nuint)Vector128<ushort>.Count;
|
||||
} while (offset <= lengthToExamine - (nuint)Vector128<ushort>.Count);
|
||||
}
|
||||
|
||||
while (offset < lengthToExamine)
|
||||
{
|
||||
char curr = Unsafe.Add(ref source, offset);
|
||||
if (curr == c || curr == c2 || curr == c3)
|
||||
{
|
||||
sepListBuilder.Append((int)offset);
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Uses ValueListBuilder to create list that holds indexes of separators in string.
|
||||
/// </summary>
|
||||
|
|
|
@ -923,6 +923,28 @@ namespace BizHawk.Emulation.Common
|
|||
["HOLD"] = 'H',
|
||||
["START"] = 'S',
|
||||
["RESET"] = 'r',
|
||||
},
|
||||
[VSystemID.Raw.Doom] = new()
|
||||
{
|
||||
["Action"] = 'A',
|
||||
["Fire"] = 'F',
|
||||
["Alt Weapon"] = 'X',
|
||||
["Jump"] = 'J',
|
||||
["End Player"] = 'E',
|
||||
["Forward"] = 'f',
|
||||
["Backward"] = 'b',
|
||||
["Turn Left"] = 'l',
|
||||
["Turn Right"] = 'r',
|
||||
["Strafe Left"] = 'L',
|
||||
["Strafe Right"] = 'R',
|
||||
["Shift Run"] = 's',
|
||||
["Weapon Select 1"] = '1',
|
||||
["Weapon Select 2"] = '2',
|
||||
["Weapon Select 3"] = '3',
|
||||
["Weapon Select 4"] = '4',
|
||||
["Weapon Select 5"] = '5',
|
||||
["Weapon Select 6"] = '6',
|
||||
["Weapon Select 7"] = '7',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -973,6 +995,17 @@ namespace BizHawk.Emulation.Common
|
|||
["Mouse Position Y"] = "mpY",
|
||||
["Mouse Scroll X"] = "msX",
|
||||
["Mouse Scroll Y"] = "msY",
|
||||
},
|
||||
[VSystemID.Raw.Doom] = new()
|
||||
{
|
||||
["Run Speed"] = "R",
|
||||
["Strafing Speed"] = "S",
|
||||
["Turning Speed"] = "T",
|
||||
["Weapon Select"] = "W",
|
||||
["Fly / Look"] = "L",
|
||||
["Use Artifact"] = "U",
|
||||
["Mouse Running"] = "mR",
|
||||
["Mouse Turning"] = "mT"
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ namespace BizHawk.Emulation.Common
|
|||
/// </summary>
|
||||
public IDictionary<string, string> CategoryLabels { get; private set; } = new Dictionary<string, string>();
|
||||
|
||||
public void ApplyAxisConstraints(string constraintClass, IDictionary<string, int> axes)
|
||||
public void ApplyAxisConstraints(string constraintClass, Dictionary<string, int> axes)
|
||||
{
|
||||
if (!Axes.HasContraints) return;
|
||||
foreach (var (k, v) in Axes)
|
||||
|
@ -101,8 +101,8 @@ namespace BizHawk.Emulation.Common
|
|||
var xAxis = k;
|
||||
var yAxis = circular.PairedAxis;
|
||||
(axes[xAxis], axes[yAxis]) = circular.ApplyTo(
|
||||
CollectionExtensions.GetValueOrDefault(axes, xAxis),
|
||||
CollectionExtensions.GetValueOrDefault(axes, yAxis));
|
||||
axes.GetValueOrDefault(xAxis),
|
||||
axes.GetValueOrDefault(yAxis));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -484,6 +484,10 @@ namespace BizHawk.Emulation.Common
|
|||
game.AddOption("VEC", "true");
|
||||
break;
|
||||
|
||||
case ".WAD":
|
||||
game.System = VSystemID.Raw.Doom;
|
||||
break;
|
||||
|
||||
case ".ZIP":
|
||||
case ".7Z":
|
||||
game.System = VSystemID.Raw.Arcade;
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace BizHawk.Emulation.Common
|
|||
[VSystemID.Raw.Coleco] = "ColecoVision",
|
||||
[VSystemID.Raw.DOS] = "DOS",
|
||||
// DEBUG
|
||||
[VSystemID.Raw.Doom] = "Doom",
|
||||
[VSystemID.Raw.GBL] = "Game Boy Link",
|
||||
[VSystemID.Raw.GB] = "GB",
|
||||
[VSystemID.Raw.SGB] = "SGB",
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace BizHawk.Emulation.Common
|
|||
public const string ChannelF = "ChannelF";
|
||||
public const string Coleco = "Coleco";
|
||||
public const string DEBUG = "DEBUG";
|
||||
public const string Doom = "Doom";
|
||||
public const string Dreamcast = "Dreamcast";
|
||||
public const string DOS = "DOS";
|
||||
public const string GameCube = "GameCube";
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
<Compile Update="Computers/Amiga/UAE.*.cs" DependentUpon="UAE.cs" />
|
||||
<Compile Update="Computers/AppleII/AppleII.*.cs" DependentUpon="AppleII.cs" />
|
||||
<Compile Update="Computers/Commodore64/C64.*.cs" DependentUpon="C64.cs" />
|
||||
<Compile Update="Computers/Doom/DSDA.*.cs" DependentUpon="DSDA.cs" />
|
||||
<Compile Update="Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.*.cs" DependentUpon="NECUPD765.cs" />
|
||||
<Compile Update="Computers/SinclairSpectrum/Hardware/Disk/NECUPS765.Static.cs" DependentUpon="NECUPD765.cs" />
|
||||
<Compile Update="Computers/SinclairSpectrum/Machine/Pentagon128K/Pentagon128.*.cs" DependentUpon="Pentagon128.cs" />
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Components.I8048
|
||||
|
@ -267,23 +266,18 @@ namespace BizHawk.Emulation.Cores.Components.I8048
|
|||
|
||||
public static string Disassemble(ushort addr, Func<ushort, byte> reader, out ushort size)
|
||||
{
|
||||
StringBuilder ret = new();
|
||||
ushort origaddr = addr;
|
||||
List<byte> bytes = new List<byte>();
|
||||
bytes.Add(reader(addr++));
|
||||
|
||||
string result = table[bytes[0]];
|
||||
|
||||
ret.AppendFormat("{0:X4}: ", origaddr);
|
||||
var opcode = reader(addr++);
|
||||
ret.AppendFormat("{0:X2} ", opcode);
|
||||
var result = table[opcode];
|
||||
if (result.Contains("i8"))
|
||||
{
|
||||
byte d = reader(addr++);
|
||||
bytes.Add(d);
|
||||
ret.AppendFormat("{0:X2} ", d);
|
||||
result = result.Replace("i8", string.Format("#{0:X2}h", d));
|
||||
}
|
||||
|
||||
StringBuilder ret = new StringBuilder();
|
||||
ret.Append(string.Format("{0:X4}: ", origaddr));
|
||||
foreach (var b in bytes)
|
||||
ret.Append(string.Format("{0:X2} ", b));
|
||||
while (ret.Length < 22)
|
||||
ret.Append(' ');
|
||||
ret.Append(result);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Components.LR35902
|
||||
|
@ -1041,51 +1040,52 @@ namespace BizHawk.Emulation.Cores.Components.LR35902
|
|||
|
||||
public static string Disassemble(ushort addr, Func<ushort, byte> reader, bool rgbds, out ushort size)
|
||||
{
|
||||
StringBuilder ret = new();
|
||||
ushort origaddr = addr;
|
||||
var bytes = new List<byte>
|
||||
ret.AppendFormat("{0:X4}: ", origaddr);
|
||||
var opcode = reader(addr++);
|
||||
ret.AppendFormat("{0:X2} ", opcode);
|
||||
string result;
|
||||
if (opcode is 0xCB)
|
||||
{
|
||||
reader(addr++)
|
||||
};
|
||||
|
||||
string result = (rgbds ? rgbds_table : table)[bytes[0]];
|
||||
if (bytes[0] == 0xcb)
|
||||
{
|
||||
bytes.Add(reader(addr++));
|
||||
result = (rgbds ? rgbds_table : table)[bytes[1] + 256];
|
||||
var opcode1 = reader(addr++);
|
||||
ret.AppendFormat("{0:X2} ", opcode1);
|
||||
result = (rgbds ? rgbds_table : table)[opcode1 + 256];
|
||||
}
|
||||
else
|
||||
{
|
||||
result = (rgbds ? rgbds_table : table)[opcode];
|
||||
}
|
||||
|
||||
if (result.Contains("d8"))
|
||||
{
|
||||
byte d = reader(addr++);
|
||||
bytes.Add(d);
|
||||
ret.AppendFormat("{0:X2} ", d);
|
||||
result = result.Replace("d8", rgbds ? $"${d:X2}" : $"#{d:X2}h");
|
||||
}
|
||||
else if (result.Contains("d16"))
|
||||
{
|
||||
byte dlo = reader(addr++);
|
||||
byte dhi = reader(addr++);
|
||||
bytes.Add(dlo);
|
||||
bytes.Add(dhi);
|
||||
ret.AppendFormat("{0:X2} {1:X2} ", dlo, dhi);
|
||||
result = result.Replace("d16", rgbds ? $"${dhi:X2}{dlo:X2}" : $"#{dhi:X2}{dlo:X2}h");
|
||||
}
|
||||
else if (result.Contains("a16"))
|
||||
{
|
||||
byte dlo = reader(addr++);
|
||||
byte dhi = reader(addr++);
|
||||
bytes.Add(dlo);
|
||||
bytes.Add(dhi);
|
||||
ret.AppendFormat("{0:X2} {1:X2} ", dlo, dhi);
|
||||
result = result.Replace("a16", rgbds ? $"${dhi:X2}{dlo:X2}" : $"#{dhi:X2}{dlo:X2}h");
|
||||
}
|
||||
else if (result.Contains("a8"))
|
||||
{
|
||||
byte d = reader(addr++);
|
||||
bytes.Add(d);
|
||||
ret.AppendFormat("{0:X2} ", d);
|
||||
result = result.Replace("a8", rgbds ? $"$FF{d:X2}" : $"#FF{d:X2}h");
|
||||
}
|
||||
else if (result.Contains("r8"))
|
||||
{
|
||||
byte d = reader(addr++);
|
||||
bytes.Add(d);
|
||||
ret.AppendFormat("{0:X2} ", d);
|
||||
int offs = d;
|
||||
if (offs >= 128)
|
||||
offs -= 256;
|
||||
|
@ -1095,15 +1095,12 @@ namespace BizHawk.Emulation.Cores.Components.LR35902
|
|||
else if (result.Contains("e8"))
|
||||
{
|
||||
byte d = reader(addr++);
|
||||
bytes.Add(d);
|
||||
ret.AppendFormat("{0:X2} ", d);
|
||||
int offs = (d >= 128) ? (256 - d) : d;
|
||||
string sign = (d >= 128) ? "-" : "";
|
||||
result = result.Replace("e8", rgbds ? sign + $"${offs:X2}" : sign + $"{offs:X2}h");
|
||||
}
|
||||
var ret = new StringBuilder();
|
||||
ret.Append($"{origaddr:X4}: ");
|
||||
foreach (var b in bytes)
|
||||
ret.Append($"{b:X2} ");
|
||||
// else noop
|
||||
while (ret.Length < 17)
|
||||
ret.Append(' ');
|
||||
ret.Append(result);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Components.MC6800
|
||||
|
@ -267,47 +266,40 @@ namespace BizHawk.Emulation.Cores.Components.MC6800
|
|||
|
||||
public static string Disassemble(ushort addr, Func<ushort, byte> reader, out ushort size)
|
||||
{
|
||||
StringBuilder ret = new();
|
||||
ushort origaddr = addr;
|
||||
List<byte> bytes = new List<byte>();
|
||||
bytes.Add(reader(addr++));
|
||||
|
||||
string result = table[bytes[0]];
|
||||
|
||||
ret.AppendFormat("{0:X4}: ", origaddr);
|
||||
var opcode = reader(addr++);
|
||||
ret.AppendFormat("{0:X2} ", opcode);
|
||||
var result = table[opcode];
|
||||
if (result.Contains("i8"))
|
||||
{
|
||||
byte d = reader(addr++);
|
||||
bytes.Add(d);
|
||||
ret.AppendFormat("{0:X2} ", d);
|
||||
result = result.Replace("i8", string.Format("#{0:X2}h", d));
|
||||
}
|
||||
else if (result.Contains("i16"))
|
||||
{
|
||||
byte dhi = reader(addr++);
|
||||
byte dlo = reader(addr++);
|
||||
bytes.Add(dhi);
|
||||
bytes.Add(dlo);
|
||||
ret.AppendFormat("{0:X2} {1:X2} ", dhi, dlo);
|
||||
result = result.Replace("i16", string.Format("#{0:X2}{1:X2}h", dhi, dlo));
|
||||
}
|
||||
else if (result.Contains("ex16"))
|
||||
{
|
||||
byte dhi = reader(addr++);
|
||||
byte dlo = reader(addr++);
|
||||
bytes.Add(dhi);
|
||||
bytes.Add(dlo);
|
||||
ret.AppendFormat("{0:X2} {1:X2} ", dhi, dlo);
|
||||
result = result.Replace("ex16", "(" + string.Format("#{0:X2}{1:X2}h", dhi, dlo) + ")");
|
||||
}
|
||||
else if (result.Contains("ix16"))
|
||||
{
|
||||
byte d = reader(addr++);
|
||||
bytes.Add(d);
|
||||
|
||||
ret.AppendFormat("{0:X2} ", d);
|
||||
result = result.Replace("ix16", "X + " + "ea");
|
||||
result = result.Replace("ea", string.Format("{0:N}h", d));
|
||||
}
|
||||
|
||||
StringBuilder ret = new StringBuilder();
|
||||
ret.Append(string.Format("{0:X4}: ", origaddr));
|
||||
foreach (var b in bytes)
|
||||
ret.Append(string.Format("{0:X2} ", b));
|
||||
// else noop
|
||||
while (ret.Length < 22)
|
||||
ret.Append(' ');
|
||||
ret.Append(result);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Components.MC6809
|
||||
|
@ -787,49 +786,50 @@ namespace BizHawk.Emulation.Cores.Components.MC6809
|
|||
|
||||
public static string Disassemble(ushort addr, Func<ushort, byte> reader, out ushort size)
|
||||
{
|
||||
StringBuilder ret = new();
|
||||
ushort origaddr = addr;
|
||||
List<byte> bytes = new List<byte>();
|
||||
bytes.Add(reader(addr++));
|
||||
ret.AppendFormat("{0:X4}: ", origaddr);
|
||||
var opcode = reader(addr++);
|
||||
ret.AppendFormat("{0:X2} ", opcode);
|
||||
|
||||
string result = table[bytes[0]];
|
||||
if (bytes[0] == 0x10)
|
||||
string result = table[opcode];
|
||||
if (opcode is 0x10)
|
||||
{
|
||||
bytes.Add(reader(addr++));
|
||||
result = table2[bytes[1]];
|
||||
var opcode1 = reader(addr++);
|
||||
ret.AppendFormat("{0:X2} ", opcode1);
|
||||
result = table2[opcode1];
|
||||
}
|
||||
|
||||
if (bytes[0] == 0x11)
|
||||
else if (opcode is 0x11)
|
||||
{
|
||||
bytes.Add(reader(addr++));
|
||||
result = table3[bytes[1]];
|
||||
var opcode1 = reader(addr++);
|
||||
ret.AppendFormat("{0:X2} ", opcode1);
|
||||
result = table3[opcode1];
|
||||
}
|
||||
|
||||
if (result.Contains("i8"))
|
||||
{
|
||||
byte d = reader(addr++);
|
||||
bytes.Add(d);
|
||||
ret.AppendFormat("{0:X2} ", d);
|
||||
result = result.Replace("i8", string.Format("#{0:X2}h", d));
|
||||
}
|
||||
else if (result.Contains("i16"))
|
||||
{
|
||||
byte dhi = reader(addr++);
|
||||
byte dlo = reader(addr++);
|
||||
bytes.Add(dhi);
|
||||
bytes.Add(dlo);
|
||||
ret.AppendFormat("{0:X2} {1:X2} ", dhi, dlo);
|
||||
result = result.Replace("i16", string.Format("#{0:X2}{1:X2}h", dhi, dlo));
|
||||
}
|
||||
else if (result.Contains("ex16"))
|
||||
{
|
||||
byte dhi = reader(addr++);
|
||||
byte dlo = reader(addr++);
|
||||
bytes.Add(dhi);
|
||||
bytes.Add(dlo);
|
||||
ret.AppendFormat("{0:X2} {1:X2} ", dhi, dlo);
|
||||
result = result.Replace("ex16", "(" + string.Format("#{0:X2}{1:X2}h", dhi, dlo) + ")");
|
||||
}
|
||||
else if (result.Contains("ix16"))
|
||||
{
|
||||
byte d = reader(addr++);
|
||||
bytes.Add(d);
|
||||
ret.AppendFormat("{0:X2} ", d);
|
||||
|
||||
string temp_reg = "";
|
||||
|
||||
|
@ -882,15 +882,14 @@ namespace BizHawk.Emulation.Cores.Components.MC6809
|
|||
break;
|
||||
case 0x8:
|
||||
byte e = reader(addr++);
|
||||
bytes.Add(e);
|
||||
ret.AppendFormat("{0:X2} ", e);
|
||||
result = result.Replace("ix16", "(" + temp_reg + " + ea)");
|
||||
result = result.Replace("ea", string.Format("{0:X2}h", e));
|
||||
break;
|
||||
case 0x9:
|
||||
byte f = reader(addr++);
|
||||
bytes.Add(f);
|
||||
byte g = reader(addr++);
|
||||
bytes.Add(g);
|
||||
ret.AppendFormat("{0:X2} {1:X2} ", f, g);
|
||||
result = result.Replace("ix16", "(" + temp_reg + " + ea)");
|
||||
result = result.Replace("ea", string.Format("{0:X2}{1:X2}h", f, g));
|
||||
break;
|
||||
|
@ -903,16 +902,15 @@ namespace BizHawk.Emulation.Cores.Components.MC6809
|
|||
case 0xC:
|
||||
temp_reg = "PC";
|
||||
byte h = reader(addr++);
|
||||
bytes.Add(h);
|
||||
ret.AppendFormat("{0:X2} ", h);
|
||||
result = result.Replace("ix16", "(" + temp_reg + " + ea)");
|
||||
result = result.Replace("ea", string.Format("{0:X2}h", h));
|
||||
break;
|
||||
case 0xD:
|
||||
temp_reg = "PC";
|
||||
byte i = reader(addr++);
|
||||
bytes.Add(i);
|
||||
byte j = reader(addr++);
|
||||
bytes.Add(j);
|
||||
ret.AppendFormat("{0:X2} {1:X2} ", i, j);
|
||||
result = result.Replace("ix16", "(" + temp_reg + " + ea)");
|
||||
result = result.Replace("ea", string.Format("{0:X2}{1:X2}h", i, j));
|
||||
break;
|
||||
|
@ -923,9 +921,8 @@ namespace BizHawk.Emulation.Cores.Components.MC6809
|
|||
if (((d >> 5) & 3) == 0)
|
||||
{
|
||||
byte k = reader(addr++);
|
||||
bytes.Add(k);
|
||||
byte l = reader(addr++);
|
||||
bytes.Add(l);
|
||||
ret.AppendFormat("{0:X2} {1:X2} ", k, l);
|
||||
result = result.Replace("ix16", "(" + string.Format("{0:X2}{1:X2}h", k, l) + ")");
|
||||
}
|
||||
else
|
||||
|
@ -965,15 +962,14 @@ namespace BizHawk.Emulation.Cores.Components.MC6809
|
|||
break;
|
||||
case 0x8:
|
||||
byte e = reader(addr++);
|
||||
bytes.Add(e);
|
||||
ret.AppendFormat("{0:X2} ", e);
|
||||
result = result.Replace("ix16", temp_reg + " + ea");
|
||||
result = result.Replace("ea", string.Format("{0:X2}h", e));
|
||||
break;
|
||||
case 0x9:
|
||||
byte f = reader(addr++);
|
||||
bytes.Add(f);
|
||||
byte g = reader(addr++);
|
||||
bytes.Add(g);
|
||||
ret.AppendFormat("{0:X2} {1:X2} ", f, g);
|
||||
result = result.Replace("ix16", temp_reg + " + ea");
|
||||
result = result.Replace("ea", string.Format("{0:X2}{1:X2}h", f, g));
|
||||
break;
|
||||
|
@ -986,16 +982,15 @@ namespace BizHawk.Emulation.Cores.Components.MC6809
|
|||
case 0xC:
|
||||
temp_reg = "PC";
|
||||
byte h = reader(addr++);
|
||||
bytes.Add(h);
|
||||
ret.AppendFormat("{0:X2} ", h);
|
||||
result = result.Replace("ix16", temp_reg + " + ea");
|
||||
result = result.Replace("ea", string.Format("{0:X2}h", h));
|
||||
break;
|
||||
case 0xD:
|
||||
temp_reg = "PC";
|
||||
byte i = reader(addr++);
|
||||
bytes.Add(i);
|
||||
byte j = reader(addr++);
|
||||
bytes.Add(j);
|
||||
ret.AppendFormat("{0:X2} {1:X2} ", i, j);
|
||||
result = result.Replace("ix16", temp_reg + " + ea");
|
||||
result = result.Replace("ea", string.Format("{0:X2}{1:X2}h", i, j));
|
||||
break;
|
||||
|
@ -1009,11 +1004,8 @@ namespace BizHawk.Emulation.Cores.Components.MC6809
|
|||
}
|
||||
}
|
||||
}
|
||||
// else noop
|
||||
|
||||
StringBuilder ret = new StringBuilder();
|
||||
ret.Append(string.Format("{0:X4}: ", origaddr));
|
||||
foreach (var b in bytes)
|
||||
ret.Append(string.Format("{0:X2} ", b));
|
||||
while (ret.Length < 22)
|
||||
ret.Append(' ');
|
||||
ret.Append(result);
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using BizHawk.Common.CollectionExtensions;
|
||||
using BizHawk.Common.StringExtensions;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
||||
|
@ -118,7 +120,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
if (!sec.ContainsMultipleWeakSectors)
|
||||
{
|
||||
byte[] origData = sec.SectorData.ToArray();
|
||||
List<byte> data = new List<byte>();
|
||||
List<byte> data = new(); //TODO pretty sure the length and indices here are known in advance and this can just be an array --yoshi
|
||||
for (int m = 0; m < 3; m++)
|
||||
{
|
||||
for (int i = 0; i < 512; i++)
|
||||
|
@ -442,7 +444,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
// we are going to create a total of 5 weak sector copies
|
||||
// keeping the original copy
|
||||
byte[] origData = sec.SectorData.ToArray();
|
||||
List<byte> data = new List<byte>();
|
||||
List<byte> data = new(); //TODO pretty sure the length and indices here are known in advance and this can just be an array --yoshi
|
||||
//Random rnd = new Random();
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
|
@ -589,19 +591,7 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
/// (including any multiple weak/random data)
|
||||
/// </summary>
|
||||
public byte[] TrackSectorData
|
||||
{
|
||||
get
|
||||
{
|
||||
List<byte> list = new List<byte>();
|
||||
|
||||
foreach (var sec in Sectors)
|
||||
{
|
||||
list.AddRange(sec.ActualData);
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
=> CollectionExtensions.ConcatArrays(Sectors.Select(static sec => sec.ActualData).ToArray());
|
||||
}
|
||||
|
||||
public class Sector
|
||||
|
@ -652,15 +642,12 @@ namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
|
|||
int size = 0x80 << SectorSize;
|
||||
if (size > ActualDataByteLength)
|
||||
{
|
||||
List<byte> l = new List<byte>();
|
||||
l.AddRange(SectorData);
|
||||
for (int i = 0; i < size - ActualDataByteLength; i++)
|
||||
{
|
||||
//l.Add(SectorData[i]);
|
||||
l.Add(SectorData[SectorData.Length - 1]);
|
||||
}
|
||||
|
||||
return l.ToArray();
|
||||
var buf = new byte[SectorData.Length + size - ActualDataByteLength];
|
||||
SectorData.AsSpan().CopyTo(buf);
|
||||
// SectorData.AsSpan(start: 0, length: buf.Length - SectorData.Length)
|
||||
// .CopyTo(buf.AsSpan(start: SectorData.Length));
|
||||
buf.AsSpan(start: SectorData.Length).Fill(SectorData[SectorData.Length - 1]);
|
||||
return buf;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BizHawk.Common.CollectionExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.Doom
|
||||
{
|
||||
public class DoomControllerDeck
|
||||
{
|
||||
public DoomControllerDeck(DoomControllerTypes controllerType, bool player1Present, bool player2Present, bool player3Present, bool player4Present)
|
||||
{
|
||||
Definition = new("Doom Demo LMP 1.9 Input Format") { };
|
||||
|
||||
if (player1Present) Port1 = ControllerCtors[controllerType](1);
|
||||
if (player2Present) Port2 = ControllerCtors[controllerType](2);
|
||||
if (player3Present) Port3 = ControllerCtors[controllerType](3);
|
||||
if (player4Present) Port4 = ControllerCtors[controllerType](4);
|
||||
|
||||
if (player1Present) Definition.BoolButtons.AddRange(Port1.Definition.BoolButtons.ToList());
|
||||
if (player2Present) Definition.BoolButtons.AddRange(Port2.Definition.BoolButtons.ToList());
|
||||
if (player3Present) Definition.BoolButtons.AddRange(Port3.Definition.BoolButtons.ToList());
|
||||
if (player4Present) Definition.BoolButtons.AddRange(Port4.Definition.BoolButtons.ToList());
|
||||
|
||||
if (player1Present) foreach (var kvp in Port1.Definition.Axes) Definition.Axes.Add(kvp);
|
||||
if (player2Present) foreach (var kvp in Port2.Definition.Axes) Definition.Axes.Add(kvp);
|
||||
if (player3Present) foreach (var kvp in Port3.Definition.Axes) Definition.Axes.Add(kvp);
|
||||
if (player4Present) foreach (var kvp in Port4.Definition.Axes) Definition.Axes.Add(kvp);
|
||||
|
||||
Definition.MakeImmutable();
|
||||
}
|
||||
|
||||
public byte ReadPort1(IController c)
|
||||
=> Port1.Read(c);
|
||||
|
||||
public byte ReadPort2(IController c)
|
||||
=> Port2.Read(c);
|
||||
|
||||
public byte ReadPort3(IController c)
|
||||
=> Port3.Read(c);
|
||||
|
||||
public byte ReadPort4(IController c)
|
||||
=> Port4.Read(c);
|
||||
|
||||
public int ReadPot1(IController c, int pot)
|
||||
=> Port1.Read_Pot(c, pot);
|
||||
|
||||
public int ReadPot2(IController c, int pot)
|
||||
=> Port2.Read_Pot(c, pot);
|
||||
|
||||
public int ReadPot3(IController c, int pot)
|
||||
=> Port3.Read_Pot(c, pot);
|
||||
|
||||
public int ReadPot4(IController c, int pot)
|
||||
=> Port4.Read_Pot(c, pot);
|
||||
|
||||
public ControllerDefinition Definition { get; }
|
||||
|
||||
private readonly IPort Port1;
|
||||
private readonly IPort Port2;
|
||||
private readonly IPort Port3;
|
||||
private readonly IPort Port4;
|
||||
|
||||
private static IReadOnlyDictionary<DoomControllerTypes, Func<int, IPort>> _controllerCtors;
|
||||
|
||||
public static IReadOnlyDictionary<DoomControllerTypes, Func<int, IPort>> ControllerCtors => _controllerCtors
|
||||
??= new Dictionary<DoomControllerTypes, Func<int, IPort>>
|
||||
{
|
||||
[DoomControllerTypes.Doom] = portNum => new DoomController(portNum),
|
||||
[DoomControllerTypes.Heretic] = portNum => new HereticController(portNum),
|
||||
[DoomControllerTypes.Hexen] = portNum => new HexenController(portNum),
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
using static BizHawk.Emulation.Cores.Computers.Doom.CInterface;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.Doom
|
||||
{
|
||||
public partial class DSDA : IEmulator
|
||||
{
|
||||
public IEmulatorServiceProvider ServiceProvider { get; }
|
||||
|
||||
public ControllerDefinition ControllerDefinition => _controllerDeck.Definition;
|
||||
|
||||
public bool FrameAdvance(IController controller, bool renderVideo, bool renderAudio)
|
||||
{
|
||||
// Declaring inputs
|
||||
PackedPlayerInput player1Inputs = new PackedPlayerInput();
|
||||
PackedPlayerInput player2Inputs = new PackedPlayerInput();
|
||||
PackedPlayerInput player3Inputs = new PackedPlayerInput();
|
||||
PackedPlayerInput player4Inputs = new PackedPlayerInput();
|
||||
|
||||
if (_syncSettings.Player1Present)
|
||||
{
|
||||
player1Inputs._RunSpeed = _controllerDeck.ReadPot1(controller, 0);
|
||||
player1Inputs._StrafingSpeed = _controllerDeck.ReadPot1(controller, 1);
|
||||
player1Inputs._TurningSpeed = _controllerDeck.ReadPot1(controller, 2);
|
||||
player1Inputs._WeaponSelect = _controllerDeck.ReadPot1(controller, 3);
|
||||
var actionsBitfield = _controllerDeck.ReadPort1(controller);
|
||||
player1Inputs._Fire = actionsBitfield & 0b00001;
|
||||
player1Inputs._Action = (actionsBitfield & 0b00010) >> 1;
|
||||
player1Inputs._AltWeapon = (actionsBitfield & 0b00100) >> 2;
|
||||
|
||||
// Handling mouse-driven running
|
||||
int mouseRunningSpeed = _controllerDeck.ReadPot1(controller, 4);
|
||||
if (_player1LastMouseRunningValue > MOUSE_NO_INPUT)
|
||||
{
|
||||
int mouseRunningDelta = _player1LastMouseRunningValue - mouseRunningSpeed;
|
||||
player1Inputs._RunSpeed += mouseRunningDelta * _syncSettings.MouseRunSensitivity;
|
||||
if (player1Inputs._RunSpeed > 50) player1Inputs._RunSpeed = 50;
|
||||
if (player1Inputs._RunSpeed < -50) player1Inputs._RunSpeed = -50;
|
||||
}
|
||||
_player1LastMouseRunningValue = mouseRunningSpeed;
|
||||
|
||||
// Handling mouse-driven turning
|
||||
int mouseTurningSpeed = _controllerDeck.ReadPot1(controller, 5);
|
||||
if (_player1LastMouseTurningValue > MOUSE_NO_INPUT)
|
||||
{
|
||||
int mouseTurningDelta = _player1LastMouseTurningValue - mouseTurningSpeed;
|
||||
player1Inputs._TurningSpeed += mouseTurningDelta * _syncSettings.MouseTurnSensitivity;
|
||||
}
|
||||
_player1LastMouseTurningValue = mouseTurningSpeed;
|
||||
|
||||
// Raven Games
|
||||
if (_syncSettings.InputFormat is DoomControllerTypes.Heretic or DoomControllerTypes.Hexen)
|
||||
{
|
||||
player1Inputs._FlyLook = _controllerDeck.ReadPot1(controller, 6);
|
||||
player1Inputs._ArtifactUse = _controllerDeck.ReadPot1(controller, 7);
|
||||
if (_syncSettings.InputFormat is DoomControllerTypes.Hexen)
|
||||
{
|
||||
player1Inputs._Jump = (actionsBitfield & 0b01000) >> 3;
|
||||
player1Inputs._EndPlayer = (actionsBitfield & 0b10000) >> 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_syncSettings.Player2Present)
|
||||
{
|
||||
player2Inputs._RunSpeed = _controllerDeck.ReadPot2(controller, 0);
|
||||
player2Inputs._StrafingSpeed = _controllerDeck.ReadPot2(controller, 1);
|
||||
player2Inputs._TurningSpeed = _controllerDeck.ReadPot2(controller, 2);
|
||||
player2Inputs._WeaponSelect = _controllerDeck.ReadPot2(controller, 3);
|
||||
var actionsBitfield = _controllerDeck.ReadPort2(controller);
|
||||
player2Inputs._Fire = actionsBitfield & 0b00001;
|
||||
player2Inputs._Action = (actionsBitfield & 0b00010) >> 1;
|
||||
player2Inputs._AltWeapon = (actionsBitfield & 0b00100) >> 2;
|
||||
|
||||
// Handling mouse-driven running
|
||||
int mouseRunningSpeed = _controllerDeck.ReadPot2(controller, 4);
|
||||
if (_player2LastMouseRunningValue > MOUSE_NO_INPUT)
|
||||
{
|
||||
int mouseRunningDelta = _player2LastMouseRunningValue - mouseRunningSpeed;
|
||||
player2Inputs._RunSpeed += mouseRunningDelta * _syncSettings.MouseRunSensitivity;
|
||||
if (player2Inputs._RunSpeed > 50) player2Inputs._RunSpeed = 50;
|
||||
if (player2Inputs._RunSpeed < -50) player2Inputs._RunSpeed = -50;
|
||||
}
|
||||
_player2LastMouseRunningValue = mouseRunningSpeed;
|
||||
|
||||
// Handling mouse-driven turning
|
||||
int mouseTurningSpeed = _controllerDeck.ReadPot2(controller, 5);
|
||||
if (_player2LastMouseTurningValue > MOUSE_NO_INPUT)
|
||||
{
|
||||
int mouseTurningDelta = _player2LastMouseTurningValue - mouseTurningSpeed;
|
||||
player2Inputs._TurningSpeed += mouseTurningDelta * _syncSettings.MouseTurnSensitivity;
|
||||
}
|
||||
_player2LastMouseTurningValue = mouseTurningSpeed;
|
||||
|
||||
// Raven Games
|
||||
if (_syncSettings.InputFormat is DoomControllerTypes.Heretic or DoomControllerTypes.Hexen)
|
||||
{
|
||||
player2Inputs._FlyLook = _controllerDeck.ReadPot2(controller, 4);
|
||||
player2Inputs._ArtifactUse = _controllerDeck.ReadPot2(controller, 5);
|
||||
if (_syncSettings.InputFormat is DoomControllerTypes.Hexen)
|
||||
{
|
||||
player2Inputs._Jump = (actionsBitfield & 0b01000) >> 3;
|
||||
player2Inputs._EndPlayer = (actionsBitfield & 0b10000) >> 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_syncSettings.Player3Present)
|
||||
{
|
||||
player3Inputs._RunSpeed = _controllerDeck.ReadPot3(controller, 0);
|
||||
player3Inputs._StrafingSpeed = _controllerDeck.ReadPot3(controller, 1);
|
||||
player3Inputs._TurningSpeed = _controllerDeck.ReadPot3(controller, 2);
|
||||
player3Inputs._WeaponSelect = _controllerDeck.ReadPot3(controller, 3);
|
||||
var actionsBitfield = _controllerDeck.ReadPort3(controller);
|
||||
player3Inputs._Fire = actionsBitfield & 0b00001;
|
||||
player3Inputs._Action = (actionsBitfield & 0b00010) >> 1;
|
||||
player3Inputs._AltWeapon = (actionsBitfield & 0b00100) >> 2;
|
||||
|
||||
// Handling mouse-driven running
|
||||
int mouseRunningSpeed = _controllerDeck.ReadPot3(controller, 4);
|
||||
if (_player3LastMouseRunningValue > MOUSE_NO_INPUT)
|
||||
{
|
||||
int mouseRunningDelta = _player3LastMouseRunningValue - mouseRunningSpeed;
|
||||
player3Inputs._RunSpeed += mouseRunningDelta * _syncSettings.MouseRunSensitivity;
|
||||
if (player3Inputs._RunSpeed > 50) player3Inputs._RunSpeed = 50;
|
||||
if (player3Inputs._RunSpeed < -50) player3Inputs._RunSpeed = -50;
|
||||
}
|
||||
_player3LastMouseRunningValue = mouseRunningSpeed;
|
||||
|
||||
// Handling mouse-driven turning
|
||||
int mouseTurningSpeed = _controllerDeck.ReadPot3(controller, 5);
|
||||
if (_player3LastMouseTurningValue > MOUSE_NO_INPUT)
|
||||
{
|
||||
int mouseTurningDelta = _player3LastMouseTurningValue - mouseTurningSpeed;
|
||||
player3Inputs._TurningSpeed += mouseTurningDelta * _syncSettings.MouseTurnSensitivity;
|
||||
}
|
||||
_player3LastMouseTurningValue = mouseTurningSpeed;
|
||||
|
||||
// Raven Games
|
||||
if (_syncSettings.InputFormat is DoomControllerTypes.Heretic or DoomControllerTypes.Hexen)
|
||||
{
|
||||
player3Inputs._FlyLook = _controllerDeck.ReadPot3(controller, 6);
|
||||
player3Inputs._ArtifactUse = _controllerDeck.ReadPot3(controller, 7);
|
||||
if (_syncSettings.InputFormat is DoomControllerTypes.Hexen)
|
||||
{
|
||||
player3Inputs._Jump = (actionsBitfield & 0b01000) >> 3;
|
||||
player3Inputs._EndPlayer = (actionsBitfield & 0b10000) >> 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_syncSettings.Player4Present)
|
||||
{
|
||||
player4Inputs._RunSpeed = _controllerDeck.ReadPot4(controller, 0);
|
||||
player4Inputs._StrafingSpeed = _controllerDeck.ReadPot4(controller, 1);
|
||||
player4Inputs._TurningSpeed = _controllerDeck.ReadPot4(controller, 2);
|
||||
player4Inputs._WeaponSelect = _controllerDeck.ReadPot4(controller, 3);
|
||||
var actionsBitfield = _controllerDeck.ReadPort4(controller);
|
||||
player4Inputs._Fire = actionsBitfield & 0b00001;
|
||||
player4Inputs._Action = (actionsBitfield & 0b00010) >> 1;
|
||||
player4Inputs._AltWeapon = (actionsBitfield & 0b00100) >> 2;
|
||||
|
||||
// Handling mouse-driven running
|
||||
int mouseRunningSpeed = _controllerDeck.ReadPot4(controller, 4);
|
||||
if (_player4LastMouseRunningValue > MOUSE_NO_INPUT)
|
||||
{
|
||||
int mouseRunningDelta = _player4LastMouseRunningValue - mouseRunningSpeed;
|
||||
player4Inputs._RunSpeed += mouseRunningDelta * _syncSettings.MouseRunSensitivity;
|
||||
if (player4Inputs._RunSpeed > 50) player4Inputs._RunSpeed = 50;
|
||||
if (player4Inputs._RunSpeed < -50) player4Inputs._RunSpeed = -50;
|
||||
}
|
||||
_player4LastMouseRunningValue = mouseRunningSpeed;
|
||||
|
||||
// Handling mouse-driven turning
|
||||
int mouseTurningSpeed = _controllerDeck.ReadPot4(controller, 5);
|
||||
if (_player4LastMouseTurningValue > MOUSE_NO_INPUT)
|
||||
{
|
||||
int mouseTurningDelta = _player4LastMouseTurningValue - mouseTurningSpeed;
|
||||
player4Inputs._TurningSpeed += mouseTurningDelta * _syncSettings.MouseTurnSensitivity;
|
||||
}
|
||||
_player4LastMouseTurningValue = mouseTurningSpeed;
|
||||
|
||||
// Raven Games
|
||||
if (_syncSettings.InputFormat is DoomControllerTypes.Heretic or DoomControllerTypes.Hexen)
|
||||
{
|
||||
player4Inputs._FlyLook = _controllerDeck.ReadPot4(controller, 4);
|
||||
player4Inputs._ArtifactUse = _controllerDeck.ReadPot4(controller, 5);
|
||||
if (_syncSettings.InputFormat is DoomControllerTypes.Hexen)
|
||||
{
|
||||
player4Inputs._Jump = (actionsBitfield & 0b01000) >> 3;
|
||||
player4Inputs._EndPlayer = (actionsBitfield & 0b10000) >> 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PackedRenderInfo renderInfo = new PackedRenderInfo();
|
||||
renderInfo._RenderVideo = renderVideo ? 1 : 0;
|
||||
renderInfo._RenderAudio = renderAudio ? 1 : 0;
|
||||
renderInfo._PlayerPointOfView = _settings.DisplayPlayer - 1;
|
||||
|
||||
Core.dsda_frame_advance(
|
||||
ref player1Inputs,
|
||||
ref player2Inputs,
|
||||
ref player3Inputs,
|
||||
ref player4Inputs,
|
||||
ref renderInfo);
|
||||
|
||||
if (renderVideo)
|
||||
UpdateVideo();
|
||||
|
||||
if (renderAudio)
|
||||
UpdateAudio();
|
||||
|
||||
Frame++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int Frame { get; private set; }
|
||||
|
||||
public string SystemId => VSystemID.Raw.Doom;
|
||||
|
||||
public bool DeterministicEmulation => true;
|
||||
|
||||
public void ResetCounters()
|
||||
{
|
||||
Frame = 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_elf.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.Doom
|
||||
{
|
||||
public partial class DSDA : IInputPollable
|
||||
{
|
||||
public int LagCount { get; set; }
|
||||
|
||||
public bool IsLagFrame { get; set; }
|
||||
|
||||
public IInputCallbackSystem InputCallbacks => _inputCallbacks;
|
||||
|
||||
private readonly InputCallbackSystem _inputCallbacks = [ ];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.Doom
|
||||
{
|
||||
public partial class DSDA
|
||||
{
|
||||
private IMemoryDomains MemoryDomains;
|
||||
|
||||
private void SetupMemoryDomains()
|
||||
{
|
||||
var domains = new List<MemoryDomain>
|
||||
{
|
||||
new MemoryDomainDelegate(
|
||||
"Things",
|
||||
0x1000000,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr =>
|
||||
{
|
||||
if (addr > 0xFFFFFF) throw new ArgumentOutOfRangeException(paramName: nameof(addr), addr, message: "address out of range");
|
||||
return Core.dsda_read_memory_array(CInterface.MemoryArrayType.Things, (uint)addr);
|
||||
},
|
||||
null,
|
||||
1),
|
||||
};
|
||||
|
||||
MemoryDomains = new MemoryDomainList(domains) { };
|
||||
((BasicServiceProvider)ServiceProvider).Register(MemoryDomains);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,316 @@
|
|||
using System.ComponentModel;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Common;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.Doom
|
||||
{
|
||||
public partial class DSDA : ISettable<DSDA.DoomSettings, DSDA.DoomSyncSettings>
|
||||
{
|
||||
public enum CompatibilityLevel : int
|
||||
{
|
||||
[Display(Name = "0 - Doom v1.2")]
|
||||
C0 = 0,
|
||||
[Display(Name = "1 - Doom v1.666")]
|
||||
C1 = 1,
|
||||
[Display(Name = "2 - Doom v1.9")]
|
||||
C2 = 2,
|
||||
[Display(Name = "3 - Ultimate Doom & Doom95")]
|
||||
C3 = 3,
|
||||
[Display(Name = "4 - Final Doom")]
|
||||
C4 = 4,
|
||||
[Display(Name = "5 - DOSDoom")]
|
||||
C5 = 5,
|
||||
[Display(Name = "6 - TASDoom")]
|
||||
C6 = 6,
|
||||
[Display(Name = "7 - Boom's Inaccurate Vanilla Compatibility Mode")]
|
||||
C7 = 7,
|
||||
[Display(Name = "8 - Boom v2.01")]
|
||||
C8 = 8,
|
||||
[Display(Name = "9 - Boom v2.02")]
|
||||
C9 = 9,
|
||||
[Display(Name = "10 - LxDoom")]
|
||||
C10 = 10,
|
||||
[Display(Name = "11 - MBF")]
|
||||
C11 = 11,
|
||||
[Display(Name = "12 - PrBoom v2.03beta")]
|
||||
C12 = 12,
|
||||
[Display(Name = "13 - PrBoom v2.1.0")]
|
||||
C13 = 13,
|
||||
[Display(Name = "14 - PrBoom v2.1.1 - 2.2.6")]
|
||||
C14 = 14,
|
||||
[Display(Name = "15 - PrBoom v2.3.x")]
|
||||
C15 = 15,
|
||||
[Display(Name = "16 - PrBoom v2.4.0")]
|
||||
C16 = 16,
|
||||
[Display(Name = "17 - PrBoom Latest Default")]
|
||||
C17 = 17,
|
||||
[Display(Name = "21 - MBF21")]
|
||||
C21 = 21
|
||||
}
|
||||
|
||||
public enum SkillLevel : int
|
||||
{
|
||||
[Display(Name = "1 - I'm too young to die")]
|
||||
S1 = 1,
|
||||
[Display(Name = "2 - Hey, not too rough")]
|
||||
S2 = 2,
|
||||
[Display(Name = "3 - Hurt me plenty")]
|
||||
S3 = 3,
|
||||
[Display(Name = "4 - Ultra-Violence")]
|
||||
S4 = 4,
|
||||
[Display(Name = "5 - Nightmare!")]
|
||||
S5 = 5
|
||||
|
||||
}
|
||||
|
||||
public enum MultiplayerMode : int
|
||||
{
|
||||
[Display(Name = "0 - Single Player / Coop")]
|
||||
M0 = 0,
|
||||
[Display(Name = "1 - Deathmatch")]
|
||||
M1 = 1,
|
||||
[Display(Name = "2 - Altdeath")]
|
||||
M2 = 2
|
||||
}
|
||||
|
||||
public enum HexenClass : int
|
||||
{
|
||||
[Display(Name = "Fighter")]
|
||||
C1 = 1,
|
||||
[Display(Name = "Cleric")]
|
||||
C2 = 2,
|
||||
[Display(Name = "Mage")]
|
||||
C3 = 3
|
||||
}
|
||||
|
||||
public const int TURBO_AUTO = -1;
|
||||
|
||||
private DoomSettings _settings;
|
||||
private DoomSyncSettings _syncSettings;
|
||||
|
||||
public DoomSettings GetSettings()
|
||||
=> _settings.Clone();
|
||||
|
||||
public DoomSyncSettings GetSyncSettings()
|
||||
=> _syncSettings.Clone();
|
||||
|
||||
public PutSettingsDirtyBits PutSettings(object o)
|
||||
=> PutSettingsDirtyBits.None;
|
||||
|
||||
public PutSettingsDirtyBits PutSyncSettings(DoomSyncSettings o)
|
||||
{
|
||||
var ret = DoomSyncSettings.NeedsReboot(_syncSettings, o);
|
||||
_syncSettings = o;
|
||||
return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None;
|
||||
}
|
||||
|
||||
[CoreSettings]
|
||||
public class DoomSettings
|
||||
{
|
||||
[JsonIgnore]
|
||||
[DisplayName("Player Point of View")]
|
||||
[Description("Which of the players' point of view to use during rendering")]
|
||||
[Range(1, 4)]
|
||||
[DefaultValue(1)]
|
||||
[TypeConverter(typeof(ConstrainedIntConverter))]
|
||||
public int DisplayPlayer { get; set; }
|
||||
|
||||
public DoomSettings()
|
||||
=> SettingsUtil.SetDefaultValues(this);
|
||||
|
||||
public DoomSettings Clone()
|
||||
=> (DoomSettings) MemberwiseClone();
|
||||
|
||||
public static bool NeedsReboot(DoomSettings x, DoomSettings y)
|
||||
=> false;
|
||||
}
|
||||
public PutSettingsDirtyBits PutSettings(DoomSettings o)
|
||||
{
|
||||
var ret = DoomSettings.NeedsReboot(_settings, o);
|
||||
_settings = o;
|
||||
if (_settings.DisplayPlayer == 1 && !_syncSettings.Player1Present) throw new Exception($"Trying to set display player '{_settings.DisplayPlayer}' but it is not active in this movie.");
|
||||
if (_settings.DisplayPlayer == 2 && !_syncSettings.Player2Present) throw new Exception($"Trying to set display player '{_settings.DisplayPlayer}' but it is not active in this movie.");
|
||||
if (_settings.DisplayPlayer == 3 && !_syncSettings.Player3Present) throw new Exception($"Trying to set display player '{_settings.DisplayPlayer}' but it is not active in this movie.");
|
||||
if (_settings.DisplayPlayer == 4 && !_syncSettings.Player4Present) throw new Exception($"Trying to set display player '{_settings.DisplayPlayer}' but it is not active in this movie.");
|
||||
return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None;
|
||||
}
|
||||
|
||||
[CoreSettings]
|
||||
public class DoomSyncSettings
|
||||
{
|
||||
[DefaultValue(DoomControllerTypes.Doom)]
|
||||
[DisplayName("Input Format")]
|
||||
[Description("The format provided for the players' input.")]
|
||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
||||
public DoomControllerTypes InputFormat { get; set; }
|
||||
|
||||
[DisplayName("Player 1 Present")]
|
||||
[Description("Specifies if player 1 is present")]
|
||||
[DefaultValue(true)]
|
||||
public bool Player1Present { get; set; }
|
||||
|
||||
[DisplayName("Player 2 Present")]
|
||||
[Description("Specifies if player 2 is present")]
|
||||
[DefaultValue(false)]
|
||||
public bool Player2Present { get; set; }
|
||||
|
||||
[DisplayName("Player 3 Present")]
|
||||
[Description("Specifies if player 3 is present")]
|
||||
[DefaultValue(false)]
|
||||
public bool Player3Present { get; set; }
|
||||
|
||||
[DisplayName("Player 4 Present")]
|
||||
[Description("Specifies if player 4 is present")]
|
||||
[DefaultValue(false)]
|
||||
public bool Player4Present { get; set; }
|
||||
|
||||
[DisplayName("Compatibility Mode")]
|
||||
[Description("The version of Doom or its ports that this movie is meant to emulate.")]
|
||||
[DefaultValue(CompatibilityLevel.C2)]
|
||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
||||
public CompatibilityLevel CompatibilityMode { get; set; }
|
||||
|
||||
[DisplayName("Skill Level")]
|
||||
[Description("Establishes the general difficulty settings.")]
|
||||
[DefaultValue(SkillLevel.S4)]
|
||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
||||
public SkillLevel SkillLevel { get; set; }
|
||||
|
||||
[DisplayName("Multiplayer Mode")]
|
||||
[Description("Indicates the multiplayer mode")]
|
||||
[DefaultValue(MultiplayerMode.M0)]
|
||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
||||
public MultiplayerMode MultiplayerMode { get; set; }
|
||||
|
||||
[DisplayName("Initial Episode")]
|
||||
[Description("Selects the initial episode. Use '0' for non-episodic IWads (e.g., DOOM2)")]
|
||||
[DefaultValue(0)]
|
||||
public int InitialEpisode { get; set; }
|
||||
|
||||
[DisplayName("Initial Map")]
|
||||
[Description("Selects the initial map.")]
|
||||
[DefaultValue(1)]
|
||||
public int InitialMap { get; set; }
|
||||
|
||||
[DisplayName("Turbo")]
|
||||
[Description("Modifies the player running / strafing speed [0-255]. -1 means Disabled.")]
|
||||
[Range(TURBO_AUTO, 255)]
|
||||
[DefaultValue(TURBO_AUTO)]
|
||||
[TypeConverter(typeof(ConstrainedIntConverter))]
|
||||
public int Turbo { get; set; }
|
||||
|
||||
[DisplayName("Fast Monsters")]
|
||||
[Description("Makes monsters move and attack much faster (overriden to true when playing Nightmare! difficulty)")]
|
||||
[DefaultValue(false)]
|
||||
public bool FastMonsters { get; set; }
|
||||
|
||||
[DisplayName("Monsters Respawn")]
|
||||
[Description("Makes monsters respawn shortly after dying (overriden to true when playing Nightmare! difficulty)")]
|
||||
[DefaultValue(false)]
|
||||
public bool MonstersRespawn { get; set; }
|
||||
|
||||
[DisplayName("No Monsters")]
|
||||
[Description("Removes all monsters from the level.")]
|
||||
[DefaultValue(false)]
|
||||
public bool NoMonsters { get; set; }
|
||||
|
||||
[DisplayName("Player 1 Hexen Class")]
|
||||
[Description("The Hexen class to use for player 1. Has no effect for Doom / Heretic")]
|
||||
[DefaultValue(HexenClass.C1)]
|
||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
||||
public HexenClass Player1Class { get; set; }
|
||||
|
||||
[DisplayName("Player 2 Hexen Class")]
|
||||
[Description("The Hexen class to use for player 2. Has no effect for Doom / Heretic")]
|
||||
[DefaultValue(HexenClass.C1)]
|
||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
||||
public HexenClass Player2Class { get; set; }
|
||||
|
||||
[DisplayName("Player 3 Hexen Class")]
|
||||
[Description("The Hexen class to use for player 3. Has no effect for Doom / Heretic")]
|
||||
[DefaultValue(HexenClass.C1)]
|
||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
||||
public HexenClass Player3Class { get; set; }
|
||||
|
||||
[DisplayName("Player 4 Hexen Class")]
|
||||
[Description("The Hexen class to use for player 4. Has no effect for Doom / Heretic")]
|
||||
[DefaultValue(HexenClass.C1)]
|
||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
||||
public HexenClass Player4Class { get; set; }
|
||||
|
||||
[DisplayName("Chain Episodes")]
|
||||
[Description("Completing one episode leads to the next without interruption.")]
|
||||
[DefaultValue(false)]
|
||||
public bool ChainEpisodes { get; set; }
|
||||
|
||||
[DisplayName("Strict Mode")]
|
||||
[Description("Sets strict mode restrictions, preventing TAS-only inputs.")]
|
||||
[DefaultValue(true)]
|
||||
public bool StrictMode { get; set; }
|
||||
|
||||
[DisplayName("Prevent Level Exit")]
|
||||
[Description("Level exit triggers won't have an effect. This is useful for debugging / optimizing / botting purposes.")]
|
||||
[DefaultValue(false)]
|
||||
public bool PreventLevelExit { get; set; }
|
||||
|
||||
[DisplayName("Prevent Game End")]
|
||||
[Description("Game end triggers won't have an effect. This is useful for debugging / optimizing / botting purposes.")]
|
||||
[DefaultValue(false)]
|
||||
public bool PreventGameEnd { get; set; }
|
||||
|
||||
[DisplayName("Mouse Running Sensitivity")]
|
||||
[Description("How fast the Doom player will run when using the mouse")]
|
||||
[DefaultValue(10)]
|
||||
public int MouseRunSensitivity { get; set; }
|
||||
|
||||
[DisplayName("Mouse Turning Sensitivity")]
|
||||
[Description("How fast the Doom player will turn when using the mouse")]
|
||||
[DefaultValue(1)]
|
||||
public int MouseTurnSensitivity { get; set; }
|
||||
|
||||
public CInterface.InitSettings GetNativeSettings(GameInfo game)
|
||||
{
|
||||
return new CInterface.InitSettings
|
||||
{
|
||||
_Player1Present = Player1Present ? 1 : 0,
|
||||
_Player2Present = Player2Present ? 1 : 0,
|
||||
_Player3Present = Player3Present ? 1 : 0,
|
||||
_Player4Present = Player4Present ? 1 : 0,
|
||||
_CompatibilityMode = (int)CompatibilityMode,
|
||||
_SkillLevel = (int) SkillLevel,
|
||||
_MultiplayerMode = (int) MultiplayerMode,
|
||||
_InitialEpisode = InitialEpisode,
|
||||
_InitialMap = InitialMap,
|
||||
_Turbo = Turbo,
|
||||
_FastMonsters = FastMonsters ? 1 : 0,
|
||||
_MonstersRespawn = MonstersRespawn ? 1 : 0,
|
||||
_NoMonsters = NoMonsters ? 1 : 0,
|
||||
_Player1Class = (int) Player1Class,
|
||||
_Player2Class = (int) Player2Class,
|
||||
_Player3Class = (int) Player3Class,
|
||||
_Player4Class = (int) Player4Class,
|
||||
_ChainEpisodes = ChainEpisodes ? 1 : 0,
|
||||
_StrictMode = StrictMode ? 1 : 0,
|
||||
_PreventLevelExit = PreventLevelExit ? 1 : 0,
|
||||
_PreventGameEnd = PreventGameEnd ? 1 : 0
|
||||
// MouseRunSensitivity is handled at Bizhawk level
|
||||
// MouseTurnSensitivity is handled at Bizhawk level
|
||||
};
|
||||
}
|
||||
|
||||
public DoomSyncSettings Clone()
|
||||
=> (DoomSyncSettings)MemberwiseClone();
|
||||
|
||||
public DoomSyncSettings()
|
||||
{
|
||||
SettingsUtil.SetDefaultValues(this);
|
||||
}
|
||||
|
||||
public static bool NeedsReboot(DoomSyncSettings x, DoomSyncSettings y)
|
||||
=> !DeepEquality.DeepEquals(x, y);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.Doom
|
||||
{
|
||||
public partial class DSDA : ISoundProvider
|
||||
{
|
||||
private readonly short[] _samples = new short[6280];
|
||||
private int _nsamp;
|
||||
|
||||
public bool CanProvideAsync => false;
|
||||
|
||||
public void GetSamplesSync(out short[] samples, out int nsamp)
|
||||
{
|
||||
nsamp = _nsamp;
|
||||
samples = _samples;
|
||||
_nsamp = 0;
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
=> _nsamp = 0;
|
||||
|
||||
public void SetSyncMode(SyncSoundMode mode)
|
||||
{
|
||||
if (mode == SyncSoundMode.Async)
|
||||
{
|
||||
throw new NotSupportedException("Async mode is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
|
||||
|
||||
public void GetSamplesAsync(short[] samples)
|
||||
=> throw new InvalidOperationException("Async mode is not supported.");
|
||||
|
||||
private unsafe void UpdateAudio()
|
||||
{
|
||||
var src = IntPtr.Zero;
|
||||
Core.dsda_get_audio(ref _nsamp, ref src);
|
||||
|
||||
if (src != IntPtr.Zero)
|
||||
{
|
||||
using (_elf.EnterExit())
|
||||
{
|
||||
Marshal.Copy(src, _samples, 0, _nsamp * 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_nsamp = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
using System.IO;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.Doom
|
||||
{
|
||||
public partial class DSDA : IStatable
|
||||
{
|
||||
public bool AvoidRewind => false;
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
_elf.LoadStateBinary(reader);
|
||||
|
||||
// Getting last mouse positions
|
||||
_player1LastMouseRunningValue = reader.ReadInt32();
|
||||
_player1LastMouseTurningValue = reader.ReadInt32();
|
||||
_player2LastMouseRunningValue = reader.ReadInt32();
|
||||
_player2LastMouseTurningValue = reader.ReadInt32();
|
||||
_player3LastMouseRunningValue = reader.ReadInt32();
|
||||
_player3LastMouseTurningValue = reader.ReadInt32();
|
||||
_player4LastMouseRunningValue = reader.ReadInt32();
|
||||
_player4LastMouseTurningValue = reader.ReadInt32();
|
||||
|
||||
Frame = reader.ReadInt32();
|
||||
// any managed pointers that we sent to the core need to be resent now!
|
||||
//Core.stella_set_input_callback(_inputCallback);
|
||||
UpdateVideo();
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
_elf.SaveStateBinary(writer);
|
||||
|
||||
// Writing last mouse positions
|
||||
writer.Write(_player1LastMouseRunningValue);
|
||||
writer.Write(_player1LastMouseTurningValue);
|
||||
writer.Write(_player2LastMouseRunningValue);
|
||||
writer.Write(_player2LastMouseTurningValue);
|
||||
writer.Write(_player3LastMouseRunningValue);
|
||||
writer.Write(_player3LastMouseTurningValue);
|
||||
writer.Write(_player4LastMouseRunningValue);
|
||||
writer.Write(_player4LastMouseTurningValue);
|
||||
|
||||
writer.Write(Frame);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.Doom
|
||||
{
|
||||
public partial class DSDA : IVideoProvider
|
||||
{
|
||||
public int[] GetVideoBuffer() => _vidBuff;
|
||||
|
||||
public int VirtualWidth => BufferWidth;
|
||||
|
||||
public int VirtualHeight => BufferHeight;
|
||||
|
||||
public int PaletteSize { get; private set; }
|
||||
|
||||
public int BufferWidth { get; private set; }
|
||||
|
||||
public int BufferHeight { get; private set; }
|
||||
|
||||
public int BackgroundColor => unchecked((int)0xff000000);
|
||||
|
||||
public int VsyncNumerator { get; }
|
||||
|
||||
public int VsyncDenominator { get; }
|
||||
|
||||
private int[] _palBuffer = [ ];
|
||||
private int[] _vidBuff = [ ];
|
||||
|
||||
private unsafe void UpdateVideo()
|
||||
{
|
||||
using (_elf.EnterExit())
|
||||
{
|
||||
var videoBufferSrc = IntPtr.Zero;
|
||||
var palletteBufferSrc = IntPtr.Zero;
|
||||
Core.dsda_get_video(out var width, out var height, out var pitch, ref videoBufferSrc, out var paletteSize, ref palletteBufferSrc);
|
||||
|
||||
// Handling pallette buffer
|
||||
PaletteSize = paletteSize;
|
||||
if (_palBuffer.Length < PaletteSize) _palBuffer = new int[PaletteSize];
|
||||
var paletteBuffer = (int*) palletteBufferSrc.ToPointer();
|
||||
for (var i = 0; i < _palBuffer.Length; i++) _palBuffer[i] = paletteBuffer[i];
|
||||
|
||||
// Handling video buffer
|
||||
BufferWidth = width;
|
||||
BufferHeight = height;
|
||||
if (_vidBuff.Length < BufferWidth * BufferHeight) _vidBuff = new int[BufferWidth * BufferHeight];
|
||||
var videoBuffer = (byte*) videoBufferSrc.ToPointer();
|
||||
for (var i = 0; i < _vidBuff.Length; i++) _vidBuff[i] = _palBuffer[videoBuffer[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,218 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.BizInvoke;
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Common.PathExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Properties;
|
||||
using BizHawk.Emulation.Cores.Waterbox;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.Doom
|
||||
{
|
||||
[PortedCore(
|
||||
name: CoreNames.DSDA,
|
||||
author: "The DSDA Team",
|
||||
portedVersion: "0.28.2 (fe0dfa0)",
|
||||
portedUrl: "https://github.com/kraflab/dsda-doom")]
|
||||
[ServiceNotApplicable(typeof(ISaveRam))]
|
||||
public partial class DSDA : IRomInfo
|
||||
{
|
||||
[CoreConstructor(VSystemID.Raw.Doom)]
|
||||
public DSDA(CoreLoadParameters<DoomSettings, DoomSyncSettings> lp)
|
||||
{
|
||||
var ser = new BasicServiceProvider(this);
|
||||
ServiceProvider = ser;
|
||||
_syncSettings = lp.SyncSettings ?? new DoomSyncSettings();
|
||||
_settings = lp.Settings ?? new DoomSettings();
|
||||
_controllerDeck = new DoomControllerDeck(_syncSettings.InputFormat, _syncSettings.Player1Present, _syncSettings.Player2Present, _syncSettings.Player3Present, _syncSettings.Player4Present);
|
||||
_loadCallback = LoadCallback;
|
||||
|
||||
// Gathering information for the rest of the wads
|
||||
_wadFiles = lp.Roms;
|
||||
|
||||
// Checking for correct IWAD configuration
|
||||
bool foundIWAD = false;
|
||||
string IWADName = "";
|
||||
foreach (var wadFile in _wadFiles)
|
||||
{
|
||||
bool recognized = false;
|
||||
if (wadFile.RomData is [ (byte) 'I', (byte) 'W', (byte) 'A', (byte) 'D', .. ])
|
||||
{
|
||||
// Check not more than one IWAD is provided
|
||||
if (foundIWAD) throw new Exception($"More than one IWAD provided. Trying to load '{wadFile.RomPath}', but IWAD '{IWADName}' was already provided");
|
||||
IWADName = wadFile.RomPath;
|
||||
foundIWAD = true;
|
||||
recognized = true;
|
||||
}
|
||||
else if (wadFile.RomData is [ (byte) 'P', (byte) 'W', (byte) 'A', (byte) 'D', .. ])
|
||||
{
|
||||
recognized = true;
|
||||
}
|
||||
if (!recognized) throw new Exception($"Unrecognized WAD provided: '{wadFile.RomPath}' has non-standard header.");
|
||||
}
|
||||
|
||||
// Check at least one IWAD was provided
|
||||
if (!foundIWAD) throw new Exception($"No IWAD was provided");
|
||||
|
||||
// Getting dsda-doom.wad -- required by DSDA
|
||||
_dsdaWadFileData = Zstd.DecompressZstdStream(new MemoryStream(Resources.DSDA_DOOM_WAD.Value)).ToArray();
|
||||
|
||||
// Getting sum of wad sizes for the accurate calculation of the invisible heap
|
||||
uint totalWadSize = (uint)_dsdaWadFileData.Length;
|
||||
foreach (var wadFile in _wadFiles) totalWadSize += (uint) wadFile.FileData.Length;
|
||||
uint totalWadSizeKb = (totalWadSize / 1024) + 1;
|
||||
Console.WriteLine("Reserving {0}kb for WAD file memory", totalWadSizeKb);
|
||||
|
||||
_elf = new WaterboxHost(new WaterboxOptions
|
||||
{
|
||||
Path = PathUtils.DllDirectoryPath,
|
||||
Filename = "dsda.wbx",
|
||||
SbrkHeapSizeKB = 64 * 1024, // This core loads quite a bunch of things on global mem -- reserve enough memory
|
||||
SealedHeapSizeKB = 4 * 1024,
|
||||
InvisibleHeapSizeKB = totalWadSizeKb + 4 * 1024, // Make sure there's enough space for the wads
|
||||
PlainHeapSizeKB = 4 * 1024,
|
||||
MmapHeapSizeKB = 1024 * 1024 * 2, // Allow the game to malloc quite a lot of objects to support those big wads
|
||||
SkipCoreConsistencyCheck = lp.Comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck),
|
||||
SkipMemoryConsistencyCheck = lp.Comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck),
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
var callingConventionAdapter = CallingConventionAdapters.MakeWaterbox(
|
||||
[
|
||||
_loadCallback
|
||||
], _elf);
|
||||
|
||||
using (_elf.EnterExit())
|
||||
{
|
||||
Core = BizInvoker.GetInvoker<CInterface>(_elf, _elf, callingConventionAdapter);
|
||||
|
||||
// Adding dsda-doom wad file
|
||||
Core.dsda_add_wad_file(_dsdaWadFileName, _dsdaWadFileData.Length, _loadCallback);
|
||||
|
||||
// Adding rom files
|
||||
foreach (var wadFile in _wadFiles)
|
||||
{
|
||||
var loadWadResult = Core.dsda_add_wad_file(wadFile.RomPath, wadFile.RomData.Length, _loadCallback);
|
||||
if (!loadWadResult) throw new Exception($"Could not load WAD file: '{wadFile.RomPath}'");
|
||||
}
|
||||
|
||||
var initSettings = _syncSettings.GetNativeSettings(lp.Game);
|
||||
var initResult = Core.dsda_init(ref initSettings);
|
||||
if (!initResult) throw new Exception($"{nameof(Core.dsda_init)}() failed");
|
||||
|
||||
int fps = 35;
|
||||
VsyncNumerator = fps;
|
||||
VsyncDenominator = 1;
|
||||
|
||||
RomDetails = $"{lp.Game.Name}\r\n{SHA1Checksum.ComputePrefixedHex(_wadFiles[0].RomData)}\r\n{MD5Checksum.ComputePrefixedHex(_wadFiles[0].RomData)}";
|
||||
|
||||
_elf.Seal();
|
||||
}
|
||||
|
||||
// pull the default video size from the core
|
||||
UpdateVideo();
|
||||
|
||||
// Registering memory domains
|
||||
SetupMemoryDomains();
|
||||
}
|
||||
catch
|
||||
{
|
||||
Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// Remembering mouse position
|
||||
private const int MOUSE_NO_INPUT = -65535;
|
||||
private int _player1LastMouseRunningValue = MOUSE_NO_INPUT;
|
||||
private int _player1LastMouseTurningValue = MOUSE_NO_INPUT;
|
||||
private int _player2LastMouseRunningValue = MOUSE_NO_INPUT;
|
||||
private int _player2LastMouseTurningValue = MOUSE_NO_INPUT;
|
||||
private int _player3LastMouseRunningValue = MOUSE_NO_INPUT;
|
||||
private int _player3LastMouseTurningValue = MOUSE_NO_INPUT;
|
||||
private int _player4LastMouseRunningValue = MOUSE_NO_INPUT;
|
||||
private int _player4LastMouseTurningValue = MOUSE_NO_INPUT;
|
||||
|
||||
// IRegionable
|
||||
public DisplayType Region { get; }
|
||||
|
||||
// IRomInfo
|
||||
public string RomDetails { get; }
|
||||
|
||||
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
|
||||
private readonly CInterface.load_archive_cb _loadCallback;
|
||||
|
||||
private readonly string _dsdaWadFileName = "dsda-doom.wad";
|
||||
private readonly byte[] _dsdaWadFileData;
|
||||
private List<IRomAsset> _wadFiles;
|
||||
|
||||
private readonly CInterface Core;
|
||||
private readonly WaterboxHost _elf;
|
||||
|
||||
private readonly DoomControllerDeck _controllerDeck;
|
||||
|
||||
/// <summary>
|
||||
/// core callback for file loading
|
||||
/// </summary>
|
||||
/// <param name="filename">string identifying file to be loaded</param>
|
||||
/// <param name="buffer">buffer to load file to</param>
|
||||
/// <param name="maxsize">maximum length buffer can hold</param>
|
||||
/// <returns>actual size loaded, or 0 on failure</returns>
|
||||
private int LoadCallback(string filename, IntPtr buffer, int maxsize)
|
||||
{
|
||||
byte[] srcdata = null;
|
||||
|
||||
if (buffer == IntPtr.Zero)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because buffer == NULL", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (filename == _dsdaWadFileName)
|
||||
{
|
||||
if (_dsdaWadFileData == null)
|
||||
{
|
||||
Console.WriteLine("Could not read from 'dsda-doom.wad'. File must be missing from the Resources folder.");
|
||||
return 0;
|
||||
}
|
||||
srcdata = _dsdaWadFileData;
|
||||
}
|
||||
|
||||
foreach (var wadFile in _wadFiles)
|
||||
{
|
||||
if (filename == wadFile.RomPath)
|
||||
{
|
||||
if (wadFile.FileData == null)
|
||||
{
|
||||
Console.WriteLine("Could not read from WAD file '{0}'", filename);
|
||||
return 0;
|
||||
}
|
||||
srcdata = wadFile.FileData;
|
||||
}
|
||||
}
|
||||
|
||||
if (srcdata != null)
|
||||
{
|
||||
if (srcdata.Length > maxsize)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because {1} > {2}", filename, srcdata.Length, maxsize);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Copying Data from " + srcdata + " to " + buffer + " Size: " + srcdata.Length);
|
||||
Marshal.Copy(srcdata, 0, buffer, srcdata.Length);
|
||||
Console.WriteLine("Firmware request {0} satisfied at size {1}", filename, srcdata.Length);
|
||||
return srcdata.Length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Unknown error processing file '{filename}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,378 @@
|
|||
using System.Linq;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.Doom
|
||||
{
|
||||
public enum DoomControllerTypes
|
||||
{
|
||||
Doom,
|
||||
Heretic,
|
||||
Hexen
|
||||
}
|
||||
|
||||
public interface IPort
|
||||
{
|
||||
byte Read(IController c);
|
||||
|
||||
int Read_Pot(IController c, int pot);
|
||||
|
||||
ControllerDefinition Definition { get; }
|
||||
|
||||
int PortNum { get; }
|
||||
}
|
||||
|
||||
public class DoomController : IPort
|
||||
{
|
||||
public DoomController(int portNum)
|
||||
{
|
||||
PortNum = portNum;
|
||||
Definition = new ControllerDefinition("Doom Input Format")
|
||||
{
|
||||
BoolButtons = BaseDefinition
|
||||
.Select(b => $"P{PortNum} " + b)
|
||||
.ToList()
|
||||
}.AddAxis($"P{PortNum} Run Speed", (-50).RangeTo(50), 0)
|
||||
.AddAxis($"P{PortNum} Strafing Speed", (-128).RangeTo(127), 0)
|
||||
.AddAxis($"P{PortNum} Turning Speed", (-128).RangeTo(127), 0)
|
||||
.AddAxis($"P{PortNum} Weapon Select", (0).RangeTo(7), 0)
|
||||
.AddAxis($"P{PortNum} Mouse Running", (-128).RangeTo(127), 0)
|
||||
.AddAxis($"P{PortNum} Mouse Turning", (-128).RangeTo(127), 0)
|
||||
.MakeImmutable();
|
||||
}
|
||||
|
||||
public int PortNum { get; }
|
||||
|
||||
public ControllerDefinition Definition { get; }
|
||||
|
||||
private static readonly string[] BaseDefinition =
|
||||
[
|
||||
"Fire",
|
||||
"Action",
|
||||
"Alt Weapon",
|
||||
"Key Forward",
|
||||
"Key Backward",
|
||||
"Key Turn Left",
|
||||
"Key Turn Right",
|
||||
"Key Strafe Left",
|
||||
"Key Strafe Right",
|
||||
"Key Shift Run",
|
||||
"Key Weapon Select 1",
|
||||
"Key Weapon Select 2",
|
||||
"Key Weapon Select 3",
|
||||
"Key Weapon Select 4",
|
||||
"Key Weapon Select 5",
|
||||
"Key Weapon Select 6",
|
||||
"Key Weapon Select 7",
|
||||
];
|
||||
|
||||
public byte Read(IController c)
|
||||
{
|
||||
byte result = 0;
|
||||
|
||||
if (c.IsPressed($"P{PortNum} Fire")) { result |= 0b0001; }
|
||||
if (c.IsPressed($"P{PortNum} Action")) { result |= 0b0010; }
|
||||
if (c.IsPressed($"P{PortNum} Alt Weapon")) { result |= 0b0100; }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int Read_Pot(IController c, int pot)
|
||||
{
|
||||
int x = c.AxisValue(Definition.Axes[pot]);
|
||||
|
||||
// Handling running keys overriding axes values
|
||||
if (Definition.Axes[pot] == $"P{PortNum} Run Speed")
|
||||
{
|
||||
if (c.IsPressed($"P{PortNum} Key Forward"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? 50 : 25;
|
||||
}
|
||||
|
||||
if (c.IsPressed($"P{PortNum} Key Backward"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? -50 : -25;
|
||||
}
|
||||
}
|
||||
|
||||
// Handling strafing keys overriding axes values
|
||||
if (Definition.Axes[pot] == $"P{PortNum} Strafing Speed")
|
||||
{
|
||||
if (c.IsPressed($"P{PortNum} Key Strafe Right"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? 40 : 24;
|
||||
}
|
||||
|
||||
if (c.IsPressed($"P{PortNum} Key Strafe Left"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? -40 : -24;
|
||||
}
|
||||
}
|
||||
|
||||
// Handling turning keys overriding axes values
|
||||
if (Definition.Axes[pot] == $"P{PortNum} Turning Speed")
|
||||
{
|
||||
if (c.IsPressed($"P{PortNum} Key Turn Left"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? 5 : 2;
|
||||
}
|
||||
|
||||
if (c.IsPressed($"P{PortNum} Key Turn Right"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? -5 : -2;
|
||||
}
|
||||
}
|
||||
|
||||
// Handling weapon select keys overriding axes values
|
||||
if (Definition.Axes[pot] == $"P{PortNum} Weapon Select")
|
||||
{
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 1")) x = 1;
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 2")) x = 2;
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 3")) x = 3;
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 4")) x = 4;
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 5")) x = 5;
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 6")) x = 6;
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 7")) x = 7;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
public class HereticController : IPort
|
||||
{
|
||||
public HereticController(int portNum)
|
||||
{
|
||||
PortNum = portNum;
|
||||
Definition = new ControllerDefinition("Heretic Input Format")
|
||||
{
|
||||
BoolButtons = BaseDefinition
|
||||
.Select(b => $"P{PortNum} " + b)
|
||||
.ToList()
|
||||
}.AddAxis($"P{PortNum} Run Speed", (-50).RangeTo(50), 0)
|
||||
.AddAxis($"P{PortNum} Strafing Speed", (-128).RangeTo(127), 0)
|
||||
.AddAxis($"P{PortNum} Turning Speed", (-128).RangeTo(127), 0)
|
||||
.AddAxis($"P{PortNum} Weapon Select", (0).RangeTo(7), 0)
|
||||
.AddAxis($"P{PortNum} Mouse Running", (-128).RangeTo(127), 0)
|
||||
.AddAxis($"P{PortNum} Mouse Turning", (-128).RangeTo(127), 0)
|
||||
.AddAxis($"P{PortNum} Fly / Look", (-7).RangeTo(7), 0)
|
||||
.AddAxis($"P{PortNum} Use Artifact", (0).RangeTo(10), 0)
|
||||
.MakeImmutable();
|
||||
}
|
||||
|
||||
public int PortNum { get; }
|
||||
|
||||
public ControllerDefinition Definition { get; }
|
||||
|
||||
private static readonly string[] BaseDefinition =
|
||||
[
|
||||
"Fire",
|
||||
"Action",
|
||||
"Alt Weapon",
|
||||
"Key Forward",
|
||||
"Key Backward",
|
||||
"Key Turn Left",
|
||||
"Key Turn Right",
|
||||
"Key Strafe Left",
|
||||
"Key Strafe Right",
|
||||
"Key Shift Run",
|
||||
"Key Weapon Select 1",
|
||||
"Key Weapon Select 2",
|
||||
"Key Weapon Select 3",
|
||||
"Key Weapon Select 4",
|
||||
"Key Weapon Select 5",
|
||||
"Key Weapon Select 6",
|
||||
"Key Weapon Select 7",
|
||||
];
|
||||
|
||||
public byte Read(IController c)
|
||||
{
|
||||
byte result = 0;
|
||||
|
||||
if (c.IsPressed($"P{PortNum} Fire")) { result |= 0b0001; }
|
||||
if (c.IsPressed($"P{PortNum} Action")) { result |= 0b0010; }
|
||||
if (c.IsPressed($"P{PortNum} Alt Weapon")) { result |= 0b0100; }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int Read_Pot(IController c, int pot)
|
||||
{
|
||||
int x = c.AxisValue(Definition.Axes[pot]);
|
||||
|
||||
// Handling running keys overriding axes values
|
||||
if (Definition.Axes[pot] == $"P{PortNum} Run Speed")
|
||||
{
|
||||
if (c.IsPressed($"P{PortNum} Key Forward"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? 50 : 25;
|
||||
}
|
||||
|
||||
if (c.IsPressed($"P{PortNum} Key Backward"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? -50 : -25;
|
||||
}
|
||||
}
|
||||
|
||||
// Handling strafing keys overriding axes values
|
||||
if (Definition.Axes[pot] == $"P{PortNum} Strafing Speed")
|
||||
{
|
||||
if (c.IsPressed($"P{PortNum} Key Strafe Right"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? 40 : 24;
|
||||
}
|
||||
|
||||
if (c.IsPressed($"P{PortNum} Key Strafe Left"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? -40 : -24;
|
||||
}
|
||||
}
|
||||
|
||||
// Handling turning keys overriding axes values
|
||||
if (Definition.Axes[pot] == $"P{PortNum} Turning Speed")
|
||||
{
|
||||
if (c.IsPressed($"P{PortNum} Key Turn Left"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? 5 : 2;
|
||||
}
|
||||
|
||||
if (c.IsPressed($"P{PortNum} Key Turn Right"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? -5 : -2;
|
||||
}
|
||||
}
|
||||
|
||||
// Handling weapon select keys overriding axes values
|
||||
if (Definition.Axes[pot] == $"P{PortNum} Weapon Select")
|
||||
{
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 1")) x = 1;
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 2")) x = 2;
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 3")) x = 3;
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 4")) x = 4;
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 5")) x = 5;
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 6")) x = 6;
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 7")) x = 7;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
public class HexenController : IPort
|
||||
{
|
||||
public HexenController(int portNum)
|
||||
{
|
||||
PortNum = portNum;
|
||||
Definition = new ControllerDefinition("Hexen Input Format")
|
||||
{
|
||||
BoolButtons = BaseDefinition
|
||||
.Select(b => $"P{PortNum} " + b)
|
||||
.ToList()
|
||||
}.AddAxis($"P{PortNum} Run Speed", (-50).RangeTo(50), 0)
|
||||
.AddAxis($"P{PortNum} Strafing Speed", (-128).RangeTo(127), 0)
|
||||
.AddAxis($"P{PortNum} Turning Speed", (-128).RangeTo(127), 0)
|
||||
.AddAxis($"P{PortNum} Weapon Select", (1).RangeTo(4), 0)
|
||||
.AddAxis($"P{PortNum} Mouse Running", (-128).RangeTo(127), 0)
|
||||
.AddAxis($"P{PortNum} Mouse Turning", (-128).RangeTo(127), 0)
|
||||
.AddAxis($"P{PortNum} Fly / Look", (-7).RangeTo(7), 0)
|
||||
.AddAxis($"P{PortNum} Use Artifact", (0).RangeTo(33), 0)
|
||||
.MakeImmutable();
|
||||
}
|
||||
|
||||
public int PortNum { get; }
|
||||
|
||||
public ControllerDefinition Definition { get; }
|
||||
|
||||
private static readonly string[] BaseDefinition =
|
||||
[
|
||||
"Fire",
|
||||
"Action",
|
||||
"Alt Weapon",
|
||||
"Jump",
|
||||
"End Player",
|
||||
"Key Forward",
|
||||
"Key Backward",
|
||||
"Key Turn Left",
|
||||
"Key Turn Right",
|
||||
"Key Strafe Left",
|
||||
"Key Strafe Right",
|
||||
"Key Shift Run",
|
||||
"Key Weapon Select 1",
|
||||
"Key Weapon Select 2",
|
||||
"Key Weapon Select 3",
|
||||
"Key Weapon Select 4"
|
||||
];
|
||||
|
||||
public byte Read(IController c)
|
||||
{
|
||||
byte result = 0;
|
||||
|
||||
if (c.IsPressed($"P{PortNum} Fire")) { result |= 0b00001; }
|
||||
if (c.IsPressed($"P{PortNum} Action")) { result |= 0b00010; }
|
||||
if (c.IsPressed($"P{PortNum} Alt Weapon")) { result |= 0b00100; }
|
||||
if (c.IsPressed($"P{PortNum} Jump")) { result |= 0b01000; }
|
||||
if (c.IsPressed($"P{PortNum} End Player")) { result |= 0b10000; }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int Read_Pot(IController c, int pot)
|
||||
{
|
||||
int x = c.AxisValue(Definition.Axes[pot]);
|
||||
|
||||
// Handling running keys overriding axes values
|
||||
if (Definition.Axes[pot] == $"P{PortNum} Run Speed")
|
||||
{
|
||||
if (c.IsPressed($"P{PortNum} Key Forward"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? 50 : 25;
|
||||
}
|
||||
|
||||
if (c.IsPressed($"P{PortNum} Key Backward"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? -50 : -25;
|
||||
}
|
||||
}
|
||||
|
||||
// Handling strafing keys overriding axes values
|
||||
if (Definition.Axes[pot] == $"P{PortNum} Strafing Speed")
|
||||
{
|
||||
if (c.IsPressed($"P{PortNum} Key Strafe Right"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? 40 : 24;
|
||||
}
|
||||
|
||||
if (c.IsPressed($"P{PortNum} Key Strafe Left"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? -40 : -24;
|
||||
}
|
||||
}
|
||||
|
||||
// Handling turning keys overriding axes values
|
||||
if (Definition.Axes[pot] == $"P{PortNum} Turning Speed")
|
||||
{
|
||||
if (c.IsPressed($"P{PortNum} Key Turn Left"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? 5 : 2;
|
||||
}
|
||||
|
||||
if (c.IsPressed($"P{PortNum} Key Turn Right"))
|
||||
{
|
||||
x = c.IsPressed($"P{PortNum} Key Shift Run") ? -5 : -2;
|
||||
}
|
||||
}
|
||||
|
||||
// Handling weapon select keys overriding axes values
|
||||
if (Definition.Axes[pot] == $"P{PortNum} Weapon Select")
|
||||
{
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 1")) x = 1;
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 2")) x = 2;
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 3")) x = 3;
|
||||
if (c.IsPressed($"P{PortNum} Key Weapon Select 4")) x = 4;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.BizInvoke;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.Doom
|
||||
{
|
||||
public abstract class CInterface
|
||||
{
|
||||
public enum MemoryArrayType : int
|
||||
{
|
||||
Things = 0,
|
||||
Lines = 1,
|
||||
Sectors = 2
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate int load_archive_cb(string filename, IntPtr buffer, int maxsize);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct InitSettings
|
||||
{
|
||||
public int _Player1Present;
|
||||
public int _Player2Present;
|
||||
public int _Player3Present;
|
||||
public int _Player4Present;
|
||||
public int _CompatibilityMode;
|
||||
public int _SkillLevel;
|
||||
public int _MultiplayerMode;
|
||||
public int _InitialEpisode;
|
||||
public int _InitialMap;
|
||||
public int _Turbo;
|
||||
public int _FastMonsters;
|
||||
public int _MonstersRespawn;
|
||||
public int _NoMonsters;
|
||||
public int _Player1Class;
|
||||
public int _Player2Class;
|
||||
public int _Player3Class;
|
||||
public int _Player4Class;
|
||||
public int _ChainEpisodes;
|
||||
public int _StrictMode;
|
||||
public int _PreventLevelExit;
|
||||
public int _PreventGameEnd;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct PackedPlayerInput
|
||||
{
|
||||
public int _RunSpeed;
|
||||
public int _StrafingSpeed;
|
||||
public int _TurningSpeed;
|
||||
public int _WeaponSelect;
|
||||
public int _Fire;
|
||||
public int _Action;
|
||||
public int _AltWeapon;
|
||||
|
||||
// Hexen + Heretic (Raven Games)
|
||||
public int _FlyLook;
|
||||
public int _ArtifactUse;
|
||||
|
||||
// Hexen only
|
||||
public int _Jump;
|
||||
public int _EndPlayer;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct PackedRenderInfo
|
||||
{
|
||||
public int _RenderVideo;
|
||||
public int _RenderAudio;
|
||||
public int _PlayerPointOfView;
|
||||
}
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void dsda_get_audio(ref int n, ref IntPtr buffer);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract bool dsda_init(ref InitSettings settings);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void dsda_frame_advance(
|
||||
ref PackedPlayerInput player1Inputs,
|
||||
ref PackedPlayerInput player2Inputs,
|
||||
ref PackedPlayerInput player3Inputs,
|
||||
ref PackedPlayerInput player4Inputs,
|
||||
ref PackedRenderInfo renderInfo);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void dsda_get_video(out int w, out int h, out int pitch, ref IntPtr buffer, out int palSize, ref IntPtr palBuffer);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract bool dsda_add_wad_file(
|
||||
string fileName,
|
||||
int fileSize,
|
||||
load_archive_cb feload_archive_cb);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract byte dsda_read_memory_array(
|
||||
MemoryArrayType type,
|
||||
uint addr);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using BizHawk.Common.CollectionExtensions;
|
||||
using BizHawk.Common.StringExtensions;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
||||
|
@ -118,7 +120,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
if (!sec.ContainsMultipleWeakSectors)
|
||||
{
|
||||
byte[] origData = sec.SectorData.ToArray();
|
||||
List<byte> data = new List<byte>();
|
||||
List<byte> data = new(); //TODO pretty sure the length and indices here are known in advance and this can just be an array --yoshi
|
||||
for (int m = 0; m < 3; m++)
|
||||
{
|
||||
for (int i = 0; i < 512; i++)
|
||||
|
@ -442,7 +444,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
// we are going to create a total of 5 weak sector copies
|
||||
// keeping the original copy
|
||||
byte[] origData = sec.SectorData.ToArray();
|
||||
List<byte> data = new List<byte>();
|
||||
List<byte> data = new(); //TODO pretty sure the length and indices here are known in advance and this can just be an array --yoshi
|
||||
//Random rnd = new Random();
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
|
@ -594,19 +596,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
/// (including any multiple weak/random data)
|
||||
/// </summary>
|
||||
public virtual byte[] TrackSectorData
|
||||
{
|
||||
get
|
||||
{
|
||||
List<byte> list = new List<byte>();
|
||||
|
||||
foreach (var sec in Sectors)
|
||||
{
|
||||
list.AddRange(sec.ActualData);
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
=> CollectionExtensions.ConcatArrays(Sectors.Select(static sec => sec.ActualData).ToArray());
|
||||
}
|
||||
|
||||
public class Sector
|
||||
|
@ -655,15 +645,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
int size = 0x80 << SectorSize;
|
||||
if (size > ActualDataByteLength)
|
||||
{
|
||||
List<byte> l = new List<byte>();
|
||||
l.AddRange(SectorData);
|
||||
for (int i = 0; i < size - ActualDataByteLength; i++)
|
||||
{
|
||||
//l.Add(SectorData[i]);
|
||||
l.Add(SectorData[SectorData.Length - 1]);
|
||||
}
|
||||
|
||||
return l.ToArray();
|
||||
var buf = new byte[SectorData.Length + size - ActualDataByteLength];
|
||||
SectorData.AsSpan().CopyTo(buf);
|
||||
// SectorData.AsSpan(start: 0, length: buf.Length - SectorData.Length)
|
||||
// .CopyTo(buf.AsSpan(start: SectorData.Length));
|
||||
buf.AsSpan(start: SectorData.Length).Fill(SectorData[SectorData.Length - 1]);
|
||||
return buf;
|
||||
}
|
||||
|
||||
return SectorData;
|
||||
|
|
|
@ -232,7 +232,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
|
||||
List<ushort> s0 = new List<ushort>();
|
||||
List<ushort> s1 = new List<ushort>();
|
||||
List<byte> dData = new List<byte>();
|
||||
|
||||
uint initPulseLevel = 1;
|
||||
int dCount = 1;
|
||||
|
@ -269,12 +268,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
s1.Add(s);
|
||||
}
|
||||
|
||||
for (int i = 0; i < Math.Ceiling((decimal)dCount / 8); i++)
|
||||
{
|
||||
var buff = b[pos++];
|
||||
dData.Add(buff);
|
||||
}
|
||||
|
||||
var dData = b.AsSpan(start: pos, length: (dCount + 7) >> 3/* == `ceil(dCount/8)` */);
|
||||
pos += dData.Length;
|
||||
foreach (var by in dData)
|
||||
{
|
||||
for (int i = 7; i >= 0; i--)
|
||||
|
@ -307,8 +302,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
|
|||
bLevel = !bLevel;
|
||||
t.DataLevels.Add(bLevel);
|
||||
}
|
||||
|
||||
dData.Clear();
|
||||
}
|
||||
|
||||
// convert to tape block
|
||||
|
|
|
@ -232,12 +232,12 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
|
|||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
private static bool IsProbablySC(IList<byte> rom)
|
||||
private static bool IsProbablySC(ReadOnlySpan<byte> rom)
|
||||
{
|
||||
// We assume a Superchip cart contains the same bytes for its entire
|
||||
// RAM area; obviously this test will fail if it doesn't
|
||||
// The RAM area will be the first 256 bytes of each 4K bank
|
||||
var numBanks = rom.Count / 4096;
|
||||
var numBanks = rom.Length / 4096;
|
||||
for (var i = 0; i < numBanks; i++)
|
||||
{
|
||||
var first = rom[i * 4096];
|
||||
|
@ -273,15 +273,12 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
|
|||
});
|
||||
}
|
||||
|
||||
private static bool IsProbably4A50(IList<byte> rom)
|
||||
private static bool IsProbably4A50(ReadOnlySpan<byte> rom)
|
||||
{
|
||||
// 4A50 carts store address $4A50 at the NMI vector, which
|
||||
// in this scheme is always in the last page of ROM at
|
||||
// $1FFA - $1FFB (at least this is true in rev 1 of the format)
|
||||
if (rom[rom.Count - 6] == 0x50 && rom[rom.Count - 5] == 0x4A)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (rom is [ .., 0x50, 0x4A, _, _, _, _ ]) return true;
|
||||
|
||||
// Program starts at $1Fxx with NOP $6Exx or NOP $6Fxx?
|
||||
if ((rom[0xFFFD] & 0x1F) is 0x1F
|
||||
|
|
|
@ -367,7 +367,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
|||
}
|
||||
}
|
||||
|
||||
private static (ulong Full, uint Upper, uint Lower) GetDSiTitleId(IReadOnlyList<byte> file)
|
||||
private static (ulong Full, uint Upper, uint Lower) GetDSiTitleId(ReadOnlySpan<byte> file)
|
||||
{
|
||||
ulong titleId = 0;
|
||||
for (var i = 0; i < 8; i++)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||
{
|
||||
|
@ -18,24 +17,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
return true;
|
||||
}
|
||||
|
||||
private readonly List<List<byte>> prg_perm = new List<List<byte>>
|
||||
{
|
||||
new List<byte> { 0, 1, 2, 3, },
|
||||
new List<byte> { 3, 2, 1, 0, },
|
||||
new List<byte> { 0, 2, 1, 3, },
|
||||
new List<byte> { 3, 1, 2, 0, },
|
||||
private readonly byte[,] prg_perm = {
|
||||
{ 0, 1, 2, 3 },
|
||||
{ 3, 2, 1, 0 },
|
||||
{ 0, 2, 1, 3 },
|
||||
{ 3, 1, 2, 0 },
|
||||
};
|
||||
|
||||
private readonly List<List<byte>> chr_perm = new List<List<byte>>
|
||||
{
|
||||
new List<byte> { 0, 1, 2, 3, 4, 5, 6, 7, },
|
||||
new List<byte> { 0, 2, 1, 3, 4, 6, 5, 7, },
|
||||
new List<byte> { 0, 1, 4, 5, 2, 3, 6, 7, },
|
||||
new List<byte> { 0, 4, 1, 5, 2, 6, 3, 7, },
|
||||
new List<byte> { 0, 4, 2, 6, 1, 5, 3, 7, },
|
||||
new List<byte> { 0, 2, 4, 6, 1, 3, 5, 7, },
|
||||
new List<byte> { 7, 6, 5, 4, 3, 2, 1, 0, },
|
||||
new List<byte> { 7, 6, 5, 4, 3, 2, 1, 0, }
|
||||
private readonly byte[,] chr_perm = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 },
|
||||
{ 0, 2, 1, 3, 4, 6, 5, 7 },
|
||||
{ 0, 1, 4, 5, 2, 3, 6, 7 },
|
||||
{ 0, 4, 1, 5, 2, 6, 3, 7 },
|
||||
{ 0, 4, 2, 6, 1, 5, 3, 7 },
|
||||
{ 0, 2, 4, 6, 1, 3, 5, 7 },
|
||||
{ 7, 6, 5, 4, 3, 2, 1, 0 },
|
||||
{ 7, 6, 5, 4, 3, 2, 1, 0 },
|
||||
};
|
||||
|
||||
private int _chrRegister = 0;
|
||||
|
@ -67,11 +64,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
{
|
||||
if ((value & 0x08) > 0)
|
||||
{
|
||||
_chrRegister = chr_perm[(value >> 4) & 7][value & 7];
|
||||
_chrRegister = chr_perm[(value >> 4) & 7, value & 7];
|
||||
}
|
||||
else
|
||||
{
|
||||
_prgRegister = prg_perm[(value >> 4) & 3][value & 3];
|
||||
_prgRegister = prg_perm[(value >> 4) & 3, value & 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
}
|
||||
}
|
||||
|
||||
public string PCRegisterName => "M68K PC";
|
||||
public string PCRegisterName { get; }
|
||||
|
||||
public IEnumerable<string> AvailableCpus { get; } = [ "M68000" ];
|
||||
|
||||
|
|
|
@ -48,6 +48,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
_ => throw new InvalidOperationException("Invalid system id")
|
||||
};
|
||||
}
|
||||
PCRegisterName = SystemId is VSystemID.Raw.GEN ? "M68K PC" : "Z80 pc";
|
||||
if (SystemId is not VSystemID.Raw.GEN) ((BasicServiceProvider) ServiceProvider).Unregister<IDisassemblable>();
|
||||
|
||||
// three or six button?
|
||||
// http://www.sega-16.com/forum/showthread.php?4398-Forgotten-Worlds-giving-you-GAME-OVER-immediately-Fix-inside&highlight=forgotten%20worlds
|
||||
|
|
|
@ -21,6 +21,7 @@ namespace BizHawk.Emulation.Cores
|
|||
public const string CPCHawk = "CPCHawk";
|
||||
public const string Cygne = "Cygne/Mednafen";
|
||||
public const string DOSBox = "DOSBox";
|
||||
public const string DSDA = "DSDA-Doom";
|
||||
public const string Emu83 = "Emu83";
|
||||
public const string Encore = "Encore";
|
||||
public const string Faust = "Faust";
|
||||
|
|
|
@ -47,6 +47,9 @@ namespace BizHawk.Emulation.Cores
|
|||
JAD, SBI,
|
||||
M3U,
|
||||
|
||||
// Doom IWad/PWad File Types
|
||||
WAD,
|
||||
|
||||
//audio codec formats
|
||||
WAV, APE, MPC, FLAC,
|
||||
MP3, //can't be ID'd very readily..
|
||||
|
@ -376,6 +379,9 @@ namespace BizHawk.Emulation.Cores
|
|||
// DOS Floppy Disks
|
||||
{ "IMA", new ExtensionInfo(FileIDType.DOS_FLOPPY, null ) },
|
||||
{ "IMG", new ExtensionInfo(FileIDType.DOS_FLOPPY, null ) },
|
||||
|
||||
// Doom IWad / PWad
|
||||
{ "WAD", new ExtensionInfo(FileIDType.WAD, null ) },
|
||||
|
||||
//for now
|
||||
{ "ROM", new ExtensionInfo(FileIDType.Multiple, null ) }, //could be MSX too
|
||||
|
|
|
@ -30,5 +30,6 @@ namespace BizHawk.Emulation.Cores.Properties {
|
|||
internal static readonly Lazy<byte[]> JAGUAR_KSERIES_ROM = new(() => ReadEmbeddedByteArray("JAGUAR_KSERIES.ROM.zst"));
|
||||
internal static readonly Lazy<byte[]> JAGUAR_MSERIES_ROM = new(() => ReadEmbeddedByteArray("JAGUAR_MSERIES.ROM.zst"));
|
||||
internal static readonly Lazy<byte[]> JAGUAR_MEMTRACK_ROM = new(() => ReadEmbeddedByteArray("JAGUAR_MEMTRACK.ROM.zst"));
|
||||
internal static readonly Lazy<byte[]> DSDA_DOOM_WAD = new(() => ReadEmbeddedByteArray("dsda-doom.zst"));
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||
using System.Text;
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Common.BufferExtensions;
|
||||
using BizHawk.Common.CollectionExtensions;
|
||||
|
||||
namespace BizHawk.Emulation.DiscSystem
|
||||
{
|
||||
|
@ -137,7 +138,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
{
|
||||
const string _jaguarHeader = "ATARI APPROVED DATA HEADER ATRI";
|
||||
const string _jaguarBSHeader = "TARA IPARPVODED TA AEHDAREA RT";
|
||||
var buffer = new List<byte>();
|
||||
List<ArraySegment<byte>> bitsToHash = new();
|
||||
var buf2352 = new byte[2352];
|
||||
|
||||
// find the boot track header
|
||||
|
@ -199,7 +200,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
EndiannessUtils.MutatingByteSwap16(buf2352.AsSpan());
|
||||
}
|
||||
|
||||
buffer.AddRange(new ArraySegment<byte>(buf2352, bootOff, Math.Min(2352 - bootOff, bootLen)));
|
||||
bitsToHash.Add(new(buf2352, offset: bootOff, count: Math.Min(2352 - bootOff, bootLen)));
|
||||
bootLen -= 2352 - bootOff;
|
||||
|
||||
while (bootLen > 0)
|
||||
|
@ -211,11 +212,11 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
EndiannessUtils.MutatingByteSwap16(buf2352.AsSpan());
|
||||
}
|
||||
|
||||
buffer.AddRange(new ArraySegment<byte>(buf2352, 0, Math.Min(2352, bootLen)));
|
||||
bitsToHash.Add(new(buf2352, offset: 0, count: Math.Min(2352, bootLen)));
|
||||
bootLen -= 2352;
|
||||
}
|
||||
|
||||
return MD5Checksum.ComputeDigestHex(buffer.ToArray());
|
||||
return MD5Checksum.ComputeDigestHex(CollectionExtensions.ConcatArrays(bitsToHash));
|
||||
}
|
||||
|
||||
var jaguarHash = HashJaguar(disc.Sessions[2].Tracks[1], dsr, false);
|
||||
|
|
|
@ -72,6 +72,19 @@ namespace BizHawk.Tests.Common.CollectionExtensions
|
|||
Assert.AreEqual(0, Array.Empty<int>().ConcatArray(Array.Empty<int>()).Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestConcatArrays()
|
||||
{
|
||||
Assert.IsTrue(
|
||||
CE.ConcatArrays([ [ 1, 2 ], [ 3 ], [ ], [ 4, 5, 6 ] ])
|
||||
.SequenceEqual([ 1, 2, 3, 4, 5, 6 ]),
|
||||
"array");
|
||||
Assert.IsTrue(
|
||||
CE.ConcatArrays([ new ArraySegment<int>([ 1, 2 ]), new ArraySegment<int>([ 3 ]), [ ], new ArraySegment<int>([ 4, 5, 6 ]) ])
|
||||
.SequenceEqual([ 1, 2, 3, 4, 5, 6 ]),
|
||||
"ArraySegment");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestLowerBoundBinarySearch()
|
||||
{
|
||||
|
|
|
@ -26,6 +26,23 @@ namespace BizHawk.Tests.Common.CustomCollections
|
|||
Assert.IsTrue(list.Contains(11)); // `Contains` when `BinarySearch` returns non-negative
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestSortedListInsert()
|
||||
{
|
||||
SortedList<int> list = new([ 1, 4, 7 ]);
|
||||
Assert.ThrowsException<NotSupportedException>(() => list.Insert(index: 3, item: 0), "setting [^0] (appending) out-of-order should throw");
|
||||
list.Insert(index: 3, item: 10);
|
||||
Assert.IsTrue(list.SequenceEqual([ 1, 4, 7, 10 ]), "expecting [ 1, 4, 7, 10 ]");
|
||||
Assert.ThrowsException<NotSupportedException>(() => list.Insert(index: 3, item: 0), "setting [^1] out-of-order should throw");
|
||||
list.Insert(index: 3, item: 9);
|
||||
Assert.IsTrue(list.SequenceEqual([ 1, 4, 7, 9, 10 ]), "expecting [ 1, 4, 7, 9, 10 ]");
|
||||
Assert.ThrowsException<NotSupportedException>(() => list.Insert(index: 1, item: 9), "setting [1] out-of-order should throw");
|
||||
list.Insert(index: 1, item: 3);
|
||||
Assert.IsTrue(list.SequenceEqual([ 1, 3, 4, 7, 9, 10 ]), "expecting [ 1, 3, 4, 7, 9, 10 ]");
|
||||
Assert.ThrowsException<NotSupportedException>(() => list.Insert(index: 0, item: 9), "setting [0] out-of-order should throw");
|
||||
list.Insert(index: 0, item: 0);
|
||||
Assert.IsTrue(list.SequenceEqual([ 0, 1, 3, 4, 7, 9, 10 ]), "expecting [ 0, 1, 3, 4, 7, 9, 10 ]");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow(new[] {1, 5, 9, 10, 11, 12}, new[] {1, 5, 9}, 9)]
|
||||
|
@ -37,5 +54,21 @@ namespace BizHawk.Tests.Common.CustomCollections
|
|||
sortlist.RemoveAfter(removeItem);
|
||||
Assert.IsTrue(sortlist.ToArray().SequenceEqual(after));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestSortedListSetIndexer()
|
||||
{
|
||||
SortedList<int> list = new([ 1, 3, 4 ]);
|
||||
Assert.ThrowsException<NotSupportedException>(() => list[1] = 9, "setting [1] out-of-order should throw");
|
||||
list[1] = 2;
|
||||
Assert.IsTrue(list.SequenceEqual([ 1, 2, 4 ]), "expecting [ 1, 2, 4 ]");
|
||||
Assert.ThrowsException<NotSupportedException>(() => list[0] = 9, "setting [0] out-of-order should throw");
|
||||
list[0] = 0;
|
||||
Assert.ThrowsException<NotSupportedException>(() => list[2] = 0, "setting [^1] out-of-order should throw");
|
||||
list[2] = 9;
|
||||
Assert.ThrowsException</*NotSupportedException*/ArgumentOutOfRangeException>(() => list[3] = 0, "setting [^0] (appending) out-of-order should throw");
|
||||
Assert.ThrowsException<ArgumentOutOfRangeException>(() => list[3] = 10, "setting [^0] (appending) properly should throw"); // to match BCL `List<T>`
|
||||
Assert.IsTrue(list.SequenceEqual([ 0, 2, 9 ]), "expecting [ 0, 2, 9 ]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,344 @@
|
|||
#include "BizhawkInterface.hxx"
|
||||
|
||||
ECL_EXPORT void dsda_get_audio(int *n, void **buffer)
|
||||
{
|
||||
int nSamples = 0;
|
||||
void* audioBuffer = nullptr;
|
||||
audioBuffer = I_CaptureAudio(&nSamples);
|
||||
// printf("audioBuffer: %p - nSamples: %d\n", audioBuffer, nSamples);
|
||||
|
||||
if (n)
|
||||
*n = nSamples;
|
||||
if (buffer)
|
||||
*buffer = audioBuffer;
|
||||
}
|
||||
|
||||
ECL_EXPORT void dsda_get_video(int& w, int& h, int& pitch, uint8_t*& buffer, int& paletteSize, uint32_t*& paletteBuffer)
|
||||
{
|
||||
buffer = (uint8_t*)headlessGetVideoBuffer();
|
||||
w = headlessGetVideoWidth();
|
||||
h = headlessGetVideoHeight();
|
||||
pitch = headlessGetVideoPitch();
|
||||
paletteSize = PALETTE_SIZE;
|
||||
|
||||
auto palette = headlessGetPallette();
|
||||
for (size_t i = 0; i < PALETTE_SIZE; i++)
|
||||
{
|
||||
uint8_t* srcColor = (uint8_t*)&palette[i];
|
||||
uint8_t* dstColor = (uint8_t*)&_convertedPaletteBuffer[i];
|
||||
dstColor[0] = srcColor[2];
|
||||
dstColor[1] = srcColor[1];
|
||||
dstColor[2] = srcColor[0];
|
||||
dstColor[3] = srcColor[3];
|
||||
}
|
||||
|
||||
paletteBuffer = _convertedPaletteBuffer;
|
||||
}
|
||||
|
||||
ECL_EXPORT void dsda_frame_advance(struct PackedPlayerInput *player1Inputs, struct PackedPlayerInput *player2Inputs, struct PackedPlayerInput *player3Inputs, struct PackedPlayerInput *player4Inputs, struct PackedRenderInfo *renderInfo)
|
||||
{
|
||||
// Setting inputs
|
||||
headlessClearTickCommand();
|
||||
|
||||
// Setting Player 1 inputs
|
||||
headlessSetTickCommand
|
||||
(
|
||||
0,
|
||||
player1Inputs->_RunSpeed,
|
||||
player1Inputs->_StrafingSpeed,
|
||||
player1Inputs->_TurningSpeed,
|
||||
player1Inputs->_Fire,
|
||||
player1Inputs->_Action,
|
||||
player1Inputs->_WeaponSelect,
|
||||
player1Inputs->_AltWeapon,
|
||||
player1Inputs->_FlyLook,
|
||||
player1Inputs->_ArtifactUse,
|
||||
player1Inputs->_Jump,
|
||||
player1Inputs->_EndPlayer
|
||||
);
|
||||
|
||||
// Setting Player 2 inputs
|
||||
headlessSetTickCommand
|
||||
(
|
||||
1,
|
||||
player2Inputs->_RunSpeed,
|
||||
player2Inputs->_StrafingSpeed,
|
||||
player2Inputs->_TurningSpeed,
|
||||
player2Inputs->_Fire,
|
||||
player2Inputs->_Action,
|
||||
player2Inputs->_WeaponSelect,
|
||||
player2Inputs->_AltWeapon,
|
||||
player2Inputs->_FlyLook,
|
||||
player2Inputs->_ArtifactUse,
|
||||
player2Inputs->_Jump,
|
||||
player2Inputs->_EndPlayer
|
||||
);
|
||||
|
||||
// Setting Player 3 inputs
|
||||
headlessSetTickCommand
|
||||
(
|
||||
2,
|
||||
player3Inputs->_RunSpeed,
|
||||
player3Inputs->_StrafingSpeed,
|
||||
player3Inputs->_TurningSpeed,
|
||||
player3Inputs->_Fire,
|
||||
player3Inputs->_Action,
|
||||
player3Inputs->_WeaponSelect,
|
||||
player3Inputs->_AltWeapon,
|
||||
player3Inputs->_FlyLook,
|
||||
player3Inputs->_ArtifactUse,
|
||||
player3Inputs->_Jump,
|
||||
player3Inputs->_EndPlayer
|
||||
);
|
||||
|
||||
// Setting Player 4 inputs
|
||||
headlessSetTickCommand
|
||||
(
|
||||
3,
|
||||
player4Inputs->_RunSpeed,
|
||||
player4Inputs->_StrafingSpeed,
|
||||
player4Inputs->_TurningSpeed,
|
||||
player4Inputs->_Fire,
|
||||
player4Inputs->_Action,
|
||||
player4Inputs->_WeaponSelect,
|
||||
player4Inputs->_AltWeapon,
|
||||
player4Inputs->_FlyLook,
|
||||
player4Inputs->_ArtifactUse,
|
||||
player4Inputs->_Jump,
|
||||
player4Inputs->_EndPlayer
|
||||
);
|
||||
|
||||
// Enabling/Disabling rendering, as required
|
||||
if (renderInfo->_RenderVideo == 0) headlessDisableVideoRendering();
|
||||
if (renderInfo->_RenderVideo == 1) headlessEnableVideoRendering();
|
||||
if (renderInfo->_RenderAudio == 0) headlessDisableAudioRendering();
|
||||
if (renderInfo->_RenderAudio == 1) headlessEnableAudioRendering();
|
||||
|
||||
// Running a single tick
|
||||
headlessRunSingleTick();
|
||||
|
||||
// Updating video
|
||||
if (renderInfo->_RenderVideo == 1)
|
||||
{
|
||||
displayplayer = consoleplayer = renderInfo->_PlayerPointOfView;
|
||||
headlessUpdateVideo();
|
||||
}
|
||||
}
|
||||
|
||||
ECL_ENTRY void (*input_callback_cb)(void);
|
||||
|
||||
void real_input_callback(void)
|
||||
{
|
||||
if (input_callback_cb)
|
||||
input_callback_cb();
|
||||
}
|
||||
|
||||
ECL_EXPORT void dsda_set_input_callback(ECL_ENTRY void (*fecb)(void))
|
||||
{
|
||||
input_callback_cb = fecb;
|
||||
}
|
||||
|
||||
bool foundIWAD = false;
|
||||
|
||||
ECL_EXPORT int dsda_init(struct InitSettings *settings)
|
||||
{
|
||||
// Creating arguments
|
||||
int argc = 0;
|
||||
char** argv = (char**) alloc_invisible (sizeof(char*) * 512);
|
||||
|
||||
bool _noMonsters = false;
|
||||
bool _monstersRespawn = false;
|
||||
|
||||
// Specifying executable name
|
||||
char arg0[] = "dsda";
|
||||
argv[argc++] = arg0;
|
||||
|
||||
// Eliminating restrictions to TAS inputs
|
||||
if (settings->_StrictMode == 0)
|
||||
{
|
||||
char arg2[] = "-tas";
|
||||
argv[argc++] = arg2;
|
||||
}
|
||||
|
||||
// Specifying skill level
|
||||
char arg3[] = "-skill";
|
||||
argv[argc++] = arg3;
|
||||
char argSkill[512];
|
||||
sprintf(argSkill, "%d", settings->_SkillLevel);
|
||||
argv[argc++] = argSkill;
|
||||
|
||||
// Specifying episode and map
|
||||
char arg4[] = "-warp";
|
||||
argv[argc++] = arg4;
|
||||
char argEpisode[512];
|
||||
{
|
||||
sprintf(argEpisode, "%d", settings->_InitialEpisode);
|
||||
argv[argc++] = argEpisode;
|
||||
}
|
||||
char argMap[512];
|
||||
sprintf(argMap, "%d", settings->_InitialMap);
|
||||
argv[argc++] = argMap;
|
||||
|
||||
// Specifying comp level
|
||||
char arg5[] = "-complevel";
|
||||
argv[argc++] = arg5;
|
||||
char argCompatibilityLevel[512];
|
||||
sprintf(argCompatibilityLevel, "%d", settings->_CompatibilityMode);
|
||||
argv[argc++] = argCompatibilityLevel;
|
||||
|
||||
// Specifying fast monsters
|
||||
char arg6[] = "-fast";
|
||||
if (settings->_FastMonsters == 1) argv[argc++] = arg6;
|
||||
|
||||
// Specifying monsters respawn
|
||||
char arg7[] = "-respawn";
|
||||
if (settings->_MonstersRespawn == 1) argv[argc++] = arg7;
|
||||
|
||||
// Specifying no monsters
|
||||
char arg8[] = "-nomonsters";
|
||||
if (settings->_NoMonsters == 1) argv[argc++] = arg8;
|
||||
|
||||
char arg9[] = "-chain_episodes";
|
||||
if (settings->_ChainEpisodes == 1) argv[argc++] = arg9;
|
||||
|
||||
// Specifying Turbo
|
||||
char arg10[] = "-turbo";
|
||||
char argTurbo[512];
|
||||
if (settings->_Turbo >= 0)
|
||||
{
|
||||
sprintf(argTurbo, "%d", settings->_Turbo);
|
||||
argv[argc++] = arg10;
|
||||
argv[argc++] = argTurbo;
|
||||
}
|
||||
|
||||
printf("Passing arguments: \n");
|
||||
for (int i = 0; i < argc; i++) printf("%s ", argv[i]);
|
||||
printf("\n");
|
||||
|
||||
// Setting players in game
|
||||
playeringame[0] = settings->_Player1Present;
|
||||
playeringame[1] = settings->_Player2Present;
|
||||
playeringame[2] = settings->_Player3Present;
|
||||
playeringame[3] = settings->_Player4Present;
|
||||
|
||||
// Getting player count
|
||||
auto playerCount = settings->_Player1Present + settings->_Player2Present + settings->_Player3Present + settings->_Player4Present;
|
||||
char arg12[] = "-solo-net";
|
||||
if (playerCount > 1) argv[argc++] = arg12;
|
||||
|
||||
// Set multiplayer mode
|
||||
char arg13[] = "-deathmatch";
|
||||
if (settings->_MultiplayerMode == 1) argv[argc++] = arg13;
|
||||
char arg14[] = "-altdeath";
|
||||
if (settings->_MultiplayerMode == 2) argv[argc++] = arg14;
|
||||
|
||||
// Handle class
|
||||
PlayerClass[0] = (pclass_t)settings->_Player1Class;
|
||||
PlayerClass[1] = (pclass_t)settings->_Player2Class;
|
||||
PlayerClass[2] = (pclass_t)settings->_Player3Class;
|
||||
PlayerClass[3] = (pclass_t)settings->_Player4Class;
|
||||
|
||||
// Initializing DSDA core
|
||||
headlessMain(argc, argv);
|
||||
printf("DSDA Initialized\n");
|
||||
|
||||
// Initializing audio
|
||||
I_SetSoundCap();
|
||||
I_InitSound();
|
||||
printf("Audio Initialized\n");
|
||||
|
||||
// If, required prevent level exit and game end triggers
|
||||
preventLevelExit = settings->_PreventLevelExit;
|
||||
preventGameEnd = settings->_PreventGameEnd;
|
||||
|
||||
printf("Prevent Level Exit: %d\n", preventLevelExit);
|
||||
printf("Prevent Game End: %d\n", preventGameEnd);
|
||||
|
||||
// Enabling DSDA output, for debugging
|
||||
enableOutput = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
ECL_EXPORT int dsda_add_wad_file(const char *filename, const int size, ECL_ENTRY int (*feload_archive_cb)(const char *filename, unsigned char *buffer, int maxsize))
|
||||
{
|
||||
printf("Loading WAD '%s' of size %d...\n", filename, size);
|
||||
auto wadFileBuffer = (unsigned char*) alloc_invisible(size);
|
||||
|
||||
if (wadFileBuffer == NULL) { fprintf(stderr, "Error creating buffer. Do we have enough memory in the waterbox?\n"); return 0; }
|
||||
else printf("Created buffer at address: %p\n", wadFileBuffer);
|
||||
|
||||
int loadSize = feload_archive_cb(filename, wadFileBuffer, size);
|
||||
if (loadSize != size) { fprintf(stderr, "Error loading '%s': read %d bytes, but expected %d bytes\n", filename, loadSize, size); return 0; }
|
||||
|
||||
// Check size is enough
|
||||
if (size < 5) { fprintf(stderr, "Error loading '%s': read %d bytes, which is too small\n", filename, size); return 0; }
|
||||
|
||||
// Getting wad header
|
||||
char header[5];
|
||||
header[0] = wadFileBuffer[0];
|
||||
header[1] = wadFileBuffer[1];
|
||||
header[2] = wadFileBuffer[2];
|
||||
header[3] = wadFileBuffer[3];
|
||||
header[4] = '\0';
|
||||
|
||||
// Getting string
|
||||
std::string headerString(header);
|
||||
|
||||
// Safety checks
|
||||
bool recognizedFormat = false;
|
||||
|
||||
// Loading PWAD
|
||||
if (headerString == "PWAD")
|
||||
{
|
||||
recognizedFormat = true;
|
||||
|
||||
// Loading PWAD
|
||||
D_AddFile(filename, source_pwad, wadFileBuffer, size);
|
||||
printf("Loaded PWAD '%s' correctly\n", filename);
|
||||
}
|
||||
|
||||
// Loading IWAD
|
||||
if (headerString == "IWAD")
|
||||
{
|
||||
recognizedFormat = true;
|
||||
|
||||
// Checking for repeated IWAD
|
||||
if (foundIWAD == true) { fprintf(stderr, "Error with '%s': an IWAD was already loaded before\n", filename); return 0; }
|
||||
foundIWAD = true;
|
||||
|
||||
// Loading IWAD
|
||||
printf("Loading IWAD '%s'...\n", filename);
|
||||
AddIWAD(filename, wadFileBuffer, size);
|
||||
printf("Loaded IWAD '%s' correctly\n", filename);
|
||||
}
|
||||
|
||||
// Checking for correct header
|
||||
if (recognizedFormat == false) { fprintf(stderr, "Error with '%s': it contains an unrecognized header '%s'\n", filename, header); return 0; }
|
||||
|
||||
// Return 1 for all ok
|
||||
return 1;
|
||||
}
|
||||
|
||||
// the Doom engine doesn't have traditional memory regions because it's not an emulator
|
||||
// but there's still useful data in memory that we can expose
|
||||
// so we turn it into artificial memory domains, one for each entity array
|
||||
// TODO: expose sectors and linedefs like xdre does (but better)
|
||||
ECL_EXPORT char dsda_read_memory_array(int type, unsigned int addr)
|
||||
{
|
||||
char out_of_bounts = 0xFF;
|
||||
char null_thing = 0x88;
|
||||
int padded_size = 512; // sizeof(mobj_t) is 464 but we pad for nice representation
|
||||
|
||||
if (addr >= numthings * padded_size) return out_of_bounts;
|
||||
|
||||
int index = addr / padded_size;
|
||||
int offset = addr % padded_size;
|
||||
mobj_t *mobj = mobj_ptrs[index];
|
||||
|
||||
if (mobj == NULL) return null_thing;
|
||||
|
||||
char *data = (char *)mobj + offset;
|
||||
return *data;
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
|
||||
#include "emulibc.h"
|
||||
#include "d_player.h"
|
||||
#include "w_wad.h"
|
||||
#include "p_mobj.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
int headlessMain(int argc, char **argv);
|
||||
void headlessRunSingleTick();
|
||||
void headlessUpdateSounds(void);
|
||||
void headlessClearTickCommand();
|
||||
void headlessSetTickCommand(int playerId, int forwardSpeed, int strafingSpeed, int turningSpeed, int fire, int action, int weapon, int altWeapon, int lookfly, int artifact, int jump, int endPlayer);
|
||||
|
||||
// Video-related functions
|
||||
void headlessUpdateVideo(void);
|
||||
void* headlessGetVideoBuffer();
|
||||
int headlessGetVideoPitch();
|
||||
int headlessGetVideoWidth();
|
||||
int headlessGetVideoHeight();
|
||||
void headlessEnableVideoRendering();
|
||||
void headlessDisableVideoRendering();
|
||||
void headlessEnableAudioRendering();
|
||||
void headlessDisableAudioRendering();
|
||||
uint32_t* headlessGetPallette();
|
||||
|
||||
void headlessSetSaveStatePointer(void* savePtr, int saveStateSize);
|
||||
size_t headlessGetEffectiveSaveSize();
|
||||
void dsda_ArchiveAll(void);
|
||||
void dsda_UnArchiveAll(void);
|
||||
void headlessGetMapName(char* outString);
|
||||
|
||||
void D_AddFile (const char *file, wad_source_t source, void* const buffer, const size_t size);
|
||||
void AddIWAD(const char *iwad, void* const buffer, const size_t size);
|
||||
unsigned char * I_CaptureAudio (int* nsamples);
|
||||
void I_InitSound(void);
|
||||
void I_SetSoundCap (void);
|
||||
}
|
||||
|
||||
// Players information
|
||||
extern "C" int enableOutput;
|
||||
extern "C" player_t players[MAX_MAXPLAYERS];
|
||||
extern "C" int preventLevelExit;
|
||||
extern "C" int preventGameEnd;
|
||||
extern "C" int reachedLevelExit;
|
||||
extern "C" int reachedGameEnd;
|
||||
extern "C" int gamemap;
|
||||
extern "C" int gametic;
|
||||
extern "C" dboolean playeringame[MAX_MAXPLAYERS];
|
||||
extern "C" int consoleplayer;
|
||||
extern "C" int displayplayer;
|
||||
extern "C" pclass_t PlayerClass[MAX_MAXPLAYERS];
|
||||
extern int numthings;
|
||||
extern mobj_t **mobj_ptrs;
|
||||
|
||||
#define PALETTE_SIZE 256
|
||||
uint32_t _convertedPaletteBuffer[PALETTE_SIZE];
|
||||
|
||||
enum MemoryArrayType
|
||||
{
|
||||
ARRAY_THINGS = 0,
|
||||
ARRAY_LINES = 1,
|
||||
ARRAY_SECTORS = 2
|
||||
};
|
||||
|
||||
struct InitSettings
|
||||
{
|
||||
int _Player1Present;
|
||||
int _Player2Present;
|
||||
int _Player3Present;
|
||||
int _Player4Present;
|
||||
int _CompatibilityMode;
|
||||
int _SkillLevel;
|
||||
int _MultiplayerMode;
|
||||
int _InitialEpisode;
|
||||
int _InitialMap;
|
||||
int _Turbo;
|
||||
int _FastMonsters;
|
||||
int _MonstersRespawn;
|
||||
int _NoMonsters;
|
||||
int _Player1Class;
|
||||
int _Player2Class;
|
||||
int _Player3Class;
|
||||
int _Player4Class;
|
||||
int _ChainEpisodes;
|
||||
int _StrictMode;
|
||||
int _PreventLevelExit;
|
||||
int _PreventGameEnd;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct PackedPlayerInput
|
||||
{
|
||||
int _RunSpeed;
|
||||
int _StrafingSpeed;
|
||||
int _TurningSpeed;
|
||||
int _WeaponSelect;
|
||||
int _Fire;
|
||||
int _Action;
|
||||
int _AltWeapon;
|
||||
int _FlyLook;
|
||||
int _ArtifactUse;
|
||||
int _Jump;
|
||||
int _EndPlayer;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct PackedRenderInfo
|
||||
{
|
||||
int _RenderVideo;
|
||||
int _RenderAudio;
|
||||
int _PlayerPointOfView;
|
||||
} __attribute__((packed));
|
|
@ -0,0 +1,269 @@
|
|||
CCFLAGS := \
|
||||
-I. \
|
||||
-I./core/prboom2/src \
|
||||
-Wfatal-errors \
|
||||
-DHAVE_CONFIG_H \
|
||||
-Dstricmp=strcasecmp \
|
||||
-Dstrnicmp=strncasecmp \
|
||||
-DNDEBUG \
|
||||
-ffast-math \
|
||||
-Wno-unused-function \
|
||||
-Wno-switch \
|
||||
-Wno-pointer-sign \
|
||||
-Wno-sign-compare \
|
||||
-Wno-stringop-overflow \
|
||||
-Wno-format-truncation \
|
||||
-Wno-unused-but-set-variable \
|
||||
-Wno-unused-variable
|
||||
|
||||
CXXFLAGS := \
|
||||
-I. \
|
||||
-I./core/prboom2/src \
|
||||
-Wfatal-errors \
|
||||
-DHAVE_CONFIG_H \
|
||||
-Dstricmp=strcasecmp \
|
||||
-Dstrnicmp=strncasecmp \
|
||||
-DNDEBUG \
|
||||
-ffast-math \
|
||||
-Wno-unused-function \
|
||||
-Wno-switch \
|
||||
-Wno-pointer-sign \
|
||||
-Wno-sign-compare \
|
||||
-Wno-stringop-overflow \
|
||||
-Wno-format-truncation \
|
||||
-Wno-unused-but-set-variable \
|
||||
-Wno-unused-variable
|
||||
|
||||
LDFLAGS :=
|
||||
|
||||
TARGET := dsda.wbx
|
||||
|
||||
SRCS = \
|
||||
BizhawkInterface.cxx \
|
||||
core/prboom2/src/am_map.c \
|
||||
core/prboom2/src/doomdef.c \
|
||||
core/prboom2/src/doomstat.c \
|
||||
core/prboom2/src/dsda.c \
|
||||
core/prboom2/src/dsda/aim.c \
|
||||
core/prboom2/src/dsda/ambient.cpp \
|
||||
core/prboom2/src/dsda/analysis.c \
|
||||
core/prboom2/src/dsda/args.c \
|
||||
core/prboom2/src/dsda/brute_force.c \
|
||||
core/prboom2/src/dsda/build.c \
|
||||
core/prboom2/src/dsda/compatibility.c \
|
||||
core/prboom2/src/dsda/configuration.c \
|
||||
core/prboom2/src/dsda/console.c \
|
||||
core/prboom2/src/dsda/cr_table.c \
|
||||
core/prboom2/src/dsda/data_organizer.c \
|
||||
core/prboom2/src/dsda/death.c \
|
||||
core/prboom2/src/dsda/deh_hash.c \
|
||||
core/prboom2/src/dsda/demo.c \
|
||||
core/prboom2/src/dsda/destructible.c \
|
||||
core/prboom2/src/dsda/endoom.c \
|
||||
core/prboom2/src/dsda/episode.c \
|
||||
core/prboom2/src/dsda/excmd.c \
|
||||
core/prboom2/src/dsda/exdemo.c \
|
||||
core/prboom2/src/dsda/exhud.c \
|
||||
core/prboom2/src/dsda/features.c \
|
||||
core/prboom2/src/dsda/font.c \
|
||||
core/prboom2/src/dsda/game_controller.c \
|
||||
core/prboom2/src/dsda/ghost.c \
|
||||
core/prboom2/src/dsda/global.c \
|
||||
core/prboom2/src/dsda/hud_components/ammo_text.c \
|
||||
core/prboom2/src/dsda/hud_components/armor_text.c \
|
||||
core/prboom2/src/dsda/hud_components/attempts.c \
|
||||
core/prboom2/src/dsda/hud_components/base.c \
|
||||
core/prboom2/src/dsda/hud_components/big_ammo.c \
|
||||
core/prboom2/src/dsda/hud_components/big_armor.c \
|
||||
core/prboom2/src/dsda/hud_components/big_armor_text.c \
|
||||
core/prboom2/src/dsda/hud_components/big_artifact.c \
|
||||
core/prboom2/src/dsda/hud_components/big_health.c \
|
||||
core/prboom2/src/dsda/hud_components/big_health_text.c \
|
||||
core/prboom2/src/dsda/hud_components/color_test.c \
|
||||
core/prboom2/src/dsda/hud_components/command_display.c \
|
||||
core/prboom2/src/dsda/hud_components/composite_time.c \
|
||||
core/prboom2/src/dsda/hud_components/coordinate_display.c \
|
||||
core/prboom2/src/dsda/hud_components/event_split.c \
|
||||
core/prboom2/src/dsda/hud_components/fps.c \
|
||||
core/prboom2/src/dsda/hud_components/free_text.c \
|
||||
core/prboom2/src/dsda/hud_components/health_text.c \
|
||||
core/prboom2/src/dsda/hud_components/keys.c \
|
||||
core/prboom2/src/dsda/hud_components/level_splits.c \
|
||||
core/prboom2/src/dsda/hud_components/line_display.c \
|
||||
core/prboom2/src/dsda/hud_components/line_distance_tracker.c \
|
||||
core/prboom2/src/dsda/hud_components/line_tracker.c \
|
||||
core/prboom2/src/dsda/hud_components/local_time.c \
|
||||
core/prboom2/src/dsda/hud_components/map_coordinates.c \
|
||||
core/prboom2/src/dsda/hud_components/map_time.c \
|
||||
core/prboom2/src/dsda/hud_components/map_title.c \
|
||||
core/prboom2/src/dsda/hud_components/map_totals.c \
|
||||
core/prboom2/src/dsda/hud_components/message.c \
|
||||
core/prboom2/src/dsda/hud_components/minimap.c \
|
||||
core/prboom2/src/dsda/hud_components/mobj_tracker.c \
|
||||
core/prboom2/src/dsda/hud_components/null.c \
|
||||
core/prboom2/src/dsda/hud_components/player_tracker.c \
|
||||
core/prboom2/src/dsda/hud_components/ready_ammo_text.c \
|
||||
core/prboom2/src/dsda/hud_components/render_stats.c \
|
||||
core/prboom2/src/dsda/hud_components/secret_message.c \
|
||||
core/prboom2/src/dsda/hud_components/sector_tracker.c \
|
||||
core/prboom2/src/dsda/hud_components/speed_text.c \
|
||||
core/prboom2/src/dsda/hud_components/stat_totals.c \
|
||||
core/prboom2/src/dsda/hud_components/tracker.c \
|
||||
core/prboom2/src/dsda/hud_components/weapon_text.c \
|
||||
core/prboom2/src/dsda/id_list.c \
|
||||
core/prboom2/src/dsda/input.c \
|
||||
core/prboom2/src/dsda/key_frame.c \
|
||||
core/prboom2/src/dsda/map_format.c \
|
||||
core/prboom2/src/dsda/mapinfo.c \
|
||||
core/prboom2/src/dsda/mapinfo/doom.c \
|
||||
core/prboom2/src/dsda/mapinfo/doom/parser.cpp \
|
||||
core/prboom2/src/dsda/mapinfo/hexen.c \
|
||||
core/prboom2/src/dsda/mapinfo/legacy.c \
|
||||
core/prboom2/src/dsda/mapinfo/u.c \
|
||||
core/prboom2/src/dsda/memory.c \
|
||||
core/prboom2/src/dsda/messenger.c \
|
||||
core/prboom2/src/dsda/mobjinfo.c \
|
||||
core/prboom2/src/dsda/mouse.c \
|
||||
core/prboom2/src/dsda/msecnode.c \
|
||||
core/prboom2/src/dsda/music.c \
|
||||
core/prboom2/src/dsda/name.c \
|
||||
core/prboom2/src/dsda/options.c \
|
||||
core/prboom2/src/dsda/palette.c \
|
||||
core/prboom2/src/dsda/pause.c \
|
||||
core/prboom2/src/dsda/pclass.c \
|
||||
core/prboom2/src/dsda/playback.c \
|
||||
core/prboom2/src/dsda/preferences.c \
|
||||
core/prboom2/src/dsda/quake.c \
|
||||
core/prboom2/src/dsda/render_stats.c \
|
||||
core/prboom2/src/dsda/save.c \
|
||||
core/prboom2/src/dsda/scroll.c \
|
||||
core/prboom2/src/dsda/settings.c \
|
||||
core/prboom2/src/dsda/sfx.c \
|
||||
core/prboom2/src/dsda/skill_info.c \
|
||||
core/prboom2/src/dsda/skip.c \
|
||||
core/prboom2/src/dsda/sndinfo.c \
|
||||
core/prboom2/src/dsda/spawn_number.c \
|
||||
core/prboom2/src/dsda/split_tracker.c \
|
||||
core/prboom2/src/dsda/sprite.c \
|
||||
core/prboom2/src/dsda/state.c \
|
||||
core/prboom2/src/dsda/stretch.c \
|
||||
core/prboom2/src/dsda/text_color.c \
|
||||
core/prboom2/src/dsda/text_file.c \
|
||||
core/prboom2/src/dsda/thing_id.c \
|
||||
core/prboom2/src/dsda/time.c \
|
||||
core/prboom2/src/dsda/tracker.c \
|
||||
core/prboom2/src/dsda/tranmap.c \
|
||||
core/prboom2/src/dsda/udmf.cpp \
|
||||
core/prboom2/src/dsda/utility.c \
|
||||
core/prboom2/src/dsda/utility/string_view.c \
|
||||
core/prboom2/src/dsda/wad_stats.c \
|
||||
core/prboom2/src/dstrings.c \
|
||||
core/prboom2/src/d_deh.c \
|
||||
core/prboom2/src/d_items.c \
|
||||
core/prboom2/src/d_main.c \
|
||||
core/prboom2/src/e6y.c \
|
||||
core/prboom2/src/f_finale.c \
|
||||
core/prboom2/src/f_wipe.c \
|
||||
core/prboom2/src/g_game.c \
|
||||
core/prboom2/src/g_overflow.c \
|
||||
core/prboom2/src/heretic/d_main.c \
|
||||
core/prboom2/src/heretic/f_finale.c \
|
||||
core/prboom2/src/heretic/info.c \
|
||||
core/prboom2/src/heretic/in_lude.c \
|
||||
core/prboom2/src/heretic/level_names.c \
|
||||
core/prboom2/src/heretic/mn_menu.c \
|
||||
core/prboom2/src/heretic/sb_bar.c \
|
||||
core/prboom2/src/heretic/sounds.c \
|
||||
core/prboom2/src/hexen/a_action.c \
|
||||
core/prboom2/src/hexen/info.c \
|
||||
core/prboom2/src/hexen/f_finale.c \
|
||||
core/prboom2/src/hexen/h2_main.c \
|
||||
core/prboom2/src/hexen/in_lude.c \
|
||||
core/prboom2/src/hexen/p_acs.c \
|
||||
core/prboom2/src/hexen/p_anim.c \
|
||||
core/prboom2/src/hexen/p_things.c \
|
||||
core/prboom2/src/hexen/po_man.c \
|
||||
core/prboom2/src/hexen/sn_sonix.c \
|
||||
core/prboom2/src/hexen/sounds.c \
|
||||
core/prboom2/src/hexen/sv_save.c \
|
||||
core/prboom2/src/hu_lib.c \
|
||||
core/prboom2/src/hu_stuff.c \
|
||||
core/prboom2/src/info.c \
|
||||
core/prboom2/src/i_capture.c \
|
||||
core/prboom2/src/i_glob.c \
|
||||
core/prboom2/src/lprintf.c \
|
||||
core/prboom2/src/md5.c \
|
||||
core/prboom2/src/m_argv.c \
|
||||
core/prboom2/src/m_bbox.c \
|
||||
core/prboom2/src/m_cheat.c \
|
||||
core/prboom2/src/m_file.c \
|
||||
core/prboom2/src/m_menu.c \
|
||||
core/prboom2/src/m_misc.c \
|
||||
core/prboom2/src/m_random.c \
|
||||
core/prboom2/src/p_ceilng.c \
|
||||
core/prboom2/src/p_doors.c \
|
||||
core/prboom2/src/p_enemy.c \
|
||||
core/prboom2/src/p_floor.c \
|
||||
core/prboom2/src/p_genlin.c \
|
||||
core/prboom2/src/p_inter.c \
|
||||
core/prboom2/src/p_lights.c \
|
||||
core/prboom2/src/p_map.c \
|
||||
core/prboom2/src/p_maputl.c \
|
||||
core/prboom2/src/p_mobj.c \
|
||||
core/prboom2/src/p_plats.c \
|
||||
core/prboom2/src/p_pspr.c \
|
||||
core/prboom2/src/p_saveg.c \
|
||||
core/prboom2/src/p_setup.c \
|
||||
core/prboom2/src/p_sight.c \
|
||||
core/prboom2/src/p_spec.c \
|
||||
core/prboom2/src/p_switch.c \
|
||||
core/prboom2/src/p_telept.c \
|
||||
core/prboom2/src/p_tick.c \
|
||||
core/prboom2/src/p_user.c \
|
||||
core/prboom2/src/r_bsp.c \
|
||||
core/prboom2/src/r_data.c \
|
||||
core/prboom2/src/r_draw.c \
|
||||
core/prboom2/src/r_fps.c \
|
||||
core/prboom2/src/r_main.c \
|
||||
core/prboom2/src/r_patch.c \
|
||||
core/prboom2/src/r_plane.c \
|
||||
core/prboom2/src/r_segs.c \
|
||||
core/prboom2/src/r_sky.c \
|
||||
core/prboom2/src/r_things.c \
|
||||
core/prboom2/src/scanner.cpp \
|
||||
core/prboom2/src/sc_man.c \
|
||||
core/prboom2/src/smooth.c \
|
||||
core/prboom2/src/sounds.c \
|
||||
core/prboom2/src/st_lib.c \
|
||||
core/prboom2/src/st_stuff.c \
|
||||
core/prboom2/src/s_advsound.c \
|
||||
core/prboom2/src/s_sound.c \
|
||||
core/prboom2/src/tables.c \
|
||||
core/prboom2/src/umapinfo.cpp \
|
||||
core/prboom2/src/v_video.c \
|
||||
core/prboom2/src/wadtbl.c \
|
||||
core/prboom2/src/wi_stuff.c \
|
||||
core/prboom2/src/w_wad.c \
|
||||
core/prboom2/src/z_bmalloc.c \
|
||||
core/prboom2/src/z_zone.c \
|
||||
core/prboom2/src/d_client.c \
|
||||
core/prboom2/src/w_mmap.c \
|
||||
core/prboom2/src/memio.c \
|
||||
core/prboom2/src/mus2mid.c \
|
||||
core/prboom2/src/SDL/i_main.c \
|
||||
core/prboom2/src/SDL/i_sound.c \
|
||||
core/prboom2/src/SDL/i_sshot.c \
|
||||
core/prboom2/src/SDL/i_system.c \
|
||||
core/prboom2/src/SDL/i_video.c \
|
||||
core/prboom2/src/MUSIC/dumbplayer.c \
|
||||
core/prboom2/src/MUSIC/flplayer.c \
|
||||
core/prboom2/src/MUSIC/madplayer.c \
|
||||
core/prboom2/src/MUSIC/midifile.c \
|
||||
core/prboom2/src/MUSIC/opl.c \
|
||||
core/prboom2/src/MUSIC/opl3.c \
|
||||
core/prboom2/src/MUSIC/oplplayer.c \
|
||||
core/prboom2/src/MUSIC/opl_queue.c \
|
||||
core/prboom2/src/MUSIC/portmidiplayer.c \
|
||||
core/prboom2/src/MUSIC/vorbisplayer.c
|
||||
|
||||
include ../common.mak
|
|
@ -0,0 +1,35 @@
|
|||
#define PACKAGE_NAME "dsda-doom"
|
||||
#define PACKAGE_TARNAME "dsda-doom"
|
||||
#define WAD_DATA "dsda-doom.wad"
|
||||
#define PACKAGE_VERSION "0.28.2"
|
||||
#define PACKAGE_STRING "dsda-doom 0.28.2"
|
||||
|
||||
#define DOOMWADDIR "/usr/local/share/games/doom"
|
||||
#define DSDA_ABSOLUTE_PWAD_PATH "/usr/local/share/games/doom"
|
||||
|
||||
/* #undef WORDS_BIGENDIAN */
|
||||
|
||||
#define HAVE_GETOPT
|
||||
#define HAVE_MMAP
|
||||
/* #undef HAVE_CREATE_FILE_MAPPING */
|
||||
#define HAVE_STRSIGNAL
|
||||
#define HAVE_MKSTEMP
|
||||
#define HAVE_GETPWUID
|
||||
|
||||
#define HAVE_SYS_WAIT_H
|
||||
#define HAVE_UNISTD_H
|
||||
//#define HAVE_ASM_BYTEORDER_H
|
||||
#define HAVE_DIRENT_H
|
||||
|
||||
/* #undef HAVE_LIBSDL2_IMAGE */
|
||||
/* #undef HAVE_LIBSDL2_MIXER */
|
||||
|
||||
/* #undef HAVE_LIBMAD */
|
||||
/* #undef HAVE_LIBFLUIDSYNTH */
|
||||
/* #undef HAVE_LIBDUMB */
|
||||
/* #undef HAVE_LIBVORBISFILE */
|
||||
/* #undef HAVE_LIBPORTMIDI */
|
||||
|
||||
/* #undef SIMPLECHECKS */
|
||||
|
||||
/* #undef RANGECHECK */
|
|
@ -0,0 +1 @@
|
|||
Subproject commit e1a352844f260e61ac0aa75d2a63941b26c4ab0e
|
|
@ -24,6 +24,7 @@ CCFLAGS := -Iutil \
|
|||
-DHOOK_CPU \
|
||||
-DINLINE=static\ __inline__ \
|
||||
-DcdStream=cdStream \
|
||||
-DMAXROMSIZE=33554432 \
|
||||
-fcommon
|
||||
|
||||
LDFLAGS :=
|
||||
|
|
|
@ -5,6 +5,7 @@ cd emulibc && make -f Makefile $1 -j && cd -
|
|||
cd libco && make -f Makefile $1 -j && cd -
|
||||
cd ares64 && ./make-both.sh $1 && cd -
|
||||
cd bsnescore && make -f Makefile $1 -j && cd -
|
||||
cd dsda && 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 -
|
||||
|
|
|
@ -80,7 +80,7 @@ impl<T> Return<T> {
|
|||
|
||||
/// write bytes. Return 0 on success, or < 0 on failure.
|
||||
/// Must write all provided bytes in one call or fail, not permitted to write less (unlike reader).
|
||||
pub type WriteCallback = extern fn(userdata: usize, data: *const u8, size: usize) -> i32;
|
||||
pub type WriteCallback = extern "C" fn(userdata: usize, data: *const u8, size: usize) -> i32;
|
||||
struct CWriter {
|
||||
/// will be passed to callback
|
||||
pub userdata: usize,
|
||||
|
@ -107,7 +107,7 @@ impl Write for CWriter {
|
|||
/// Read bytes into the buffer. Return number of bytes read on success, or < 0 on failure.
|
||||
/// permitted to read less than the provided buffer size, but must always read at least 1
|
||||
/// byte if EOF is not reached. If EOF is reached, should return 0.
|
||||
pub type ReadCallback = extern fn(userdata: usize, data: *mut u8, size: usize) -> isize;
|
||||
pub type ReadCallback = extern "C" fn(userdata: usize, data: *mut u8, size: usize) -> isize;
|
||||
struct CReader {
|
||||
pub userdata: usize,
|
||||
pub callback: ReadCallback,
|
||||
|
@ -126,7 +126,7 @@ impl Read for CReader {
|
|||
// #[repr(C)]
|
||||
// pub struct MissingFileCallback {
|
||||
// pub userdata: usize,
|
||||
// pub callback: extern fn(userdata: usize, name: *const c_char) -> *mut MissingFileResult,
|
||||
// pub callback: extern "C" fn(userdata: usize, name: *const c_char) -> *mut MissingFileResult,
|
||||
// }
|
||||
|
||||
// #[repr(C)]
|
||||
|
@ -152,7 +152,7 @@ fn read_whole_file(reader: &mut CReader) -> anyhow::Result<Vec<u8>> {
|
|||
/// Given a guest executable and a memory layout, create a new host environment. All data will be immediately consumed from the reader,
|
||||
/// which will not be used after this call.
|
||||
#[no_mangle]
|
||||
pub extern fn wbx_create_host(layout: &MemoryLayoutTemplate, module_name: *const c_char, callback: ReadCallback, userdata: usize, ret: &mut Return<*mut WaterboxHost>) {
|
||||
pub extern "C" fn wbx_create_host(layout: &MemoryLayoutTemplate, module_name: *const c_char, callback: ReadCallback, userdata: usize, ret: &mut Return<*mut WaterboxHost>) {
|
||||
let mut reader = CReader {
|
||||
userdata,
|
||||
callback
|
||||
|
@ -166,7 +166,7 @@ pub extern fn wbx_create_host(layout: &MemoryLayoutTemplate, module_name: *const
|
|||
|
||||
/// Tear down a host environment. If called while the environment is active, will deactivate it first.
|
||||
#[no_mangle]
|
||||
pub extern fn wbx_destroy_host(obj: *mut WaterboxHost, ret: &mut Return<()>) {
|
||||
pub extern "C" fn wbx_destroy_host(obj: *mut WaterboxHost, ret: &mut Return<()>) {
|
||||
let res = (|| {
|
||||
unsafe {
|
||||
drop(Box::from_raw(obj));
|
||||
|
@ -181,7 +181,7 @@ pub extern fn wbx_destroy_host(obj: *mut WaterboxHost, ret: &mut Return<()>) {
|
|||
/// while active. Uses a mutex internally so as to not stomp over other host environments in the same 4GiB slice.
|
||||
/// Ignored if host is already active.
|
||||
#[no_mangle]
|
||||
pub extern fn wbx_activate_host(obj: &mut WaterboxHost, ret: &mut Return<()>) {
|
||||
pub extern "C" fn wbx_activate_host(obj: &mut WaterboxHost, ret: &mut Return<()>) {
|
||||
let res = (|| {
|
||||
obj.activate();
|
||||
Ok(())
|
||||
|
@ -192,7 +192,7 @@ pub extern fn wbx_activate_host(obj: &mut WaterboxHost, ret: &mut Return<()>) {
|
|||
/// Deactivates a host environment, and releases the mutex.
|
||||
/// Ignored if host is not active
|
||||
#[no_mangle]
|
||||
pub extern fn wbx_deactivate_host(obj: &mut WaterboxHost, ret: &mut Return<()>) {
|
||||
pub extern "C" fn wbx_deactivate_host(obj: &mut WaterboxHost, ret: &mut Return<()>) {
|
||||
obj.deactivate();
|
||||
ret.put(Ok(()));
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ pub extern fn wbx_deactivate_host(obj: &mut WaterboxHost, ret: &mut Return<()>)
|
|||
/// while the host is active. A missing proc is not an error and simply returns 0. The guest function must be,
|
||||
/// and the returned callback will be, sysv abi, and will only pass up to 6 int/ptr args and no other arg types.
|
||||
#[no_mangle]
|
||||
pub extern fn wbx_get_proc_addr(obj: &mut WaterboxHost, name: *const c_char, ret: &mut Return<usize>) {
|
||||
pub extern "C" fn wbx_get_proc_addr(obj: &mut WaterboxHost, name: *const c_char, ret: &mut Return<usize>) {
|
||||
match arg_to_str(name) {
|
||||
Ok(s) => {
|
||||
ret.put(obj.get_proc_addr(&s));
|
||||
|
@ -216,14 +216,14 @@ pub extern fn wbx_get_proc_addr(obj: &mut WaterboxHost, name: *const c_char, ret
|
|||
/// only needed if the guest exposes callin pointers that aren't named exports (for instance, if a function returns
|
||||
/// a pointer to another function).
|
||||
#[no_mangle]
|
||||
pub extern fn wbx_get_callin_addr(obj: &mut WaterboxHost, ptr: usize, ret: &mut Return<usize>) {
|
||||
pub extern "C" fn wbx_get_callin_addr(obj: &mut WaterboxHost, ptr: usize, ret: &mut Return<usize>) {
|
||||
ret.put(obj.get_external_callin_ptr(ptr));
|
||||
}
|
||||
/// Returns the raw address of a function exported from the guest. `wbx_get_proc_addr()` is equivalent to
|
||||
/// `wbx_get_callin_addr(wbx_get_proc_addr_raw()). Most things should not use this directly, as the returned
|
||||
/// pointer will not have proper stack hygiene and will crash on syscalls from the guest.
|
||||
#[no_mangle]
|
||||
pub extern fn wbx_get_proc_addr_raw(obj: &mut WaterboxHost, name: *const c_char, ret: &mut Return<usize>) {
|
||||
pub extern "C" fn wbx_get_proc_addr_raw(obj: &mut WaterboxHost, name: *const c_char, ret: &mut Return<usize>) {
|
||||
match arg_to_str(name) {
|
||||
Ok(s) => {
|
||||
ret.put(obj.get_proc_addr_raw(&s));
|
||||
|
@ -240,13 +240,13 @@ pub extern fn wbx_get_proc_addr_raw(obj: &mut WaterboxHost, name: *const c_char,
|
|||
/// in the guest because `foo` was bound to the same slot and a particular slot gives a consistent pointer.
|
||||
/// The returned thunk will be, and the callback must be, sysv abi and will only pass up to 6 int/ptr args and no other arg types.
|
||||
#[no_mangle]
|
||||
pub extern fn wbx_get_callback_addr(obj: &mut WaterboxHost, callback: ExternalCallback, slot: usize, ret: &mut Return<usize>) {
|
||||
pub extern "C" fn wbx_get_callback_addr(obj: &mut WaterboxHost, callback: ExternalCallback, slot: usize, ret: &mut Return<usize>) {
|
||||
ret.put(obj.get_external_callback_ptr(callback, slot));
|
||||
}
|
||||
|
||||
/// Calls the seal operation, which is a one time action that prepares the host to save states.
|
||||
#[no_mangle]
|
||||
pub extern fn wbx_seal(obj: &mut WaterboxHost, ret: &mut Return<()>) {
|
||||
pub extern "C" fn wbx_seal(obj: &mut WaterboxHost, ret: &mut Return<()>) {
|
||||
ret.put(obj.seal());
|
||||
}
|
||||
|
||||
|
@ -254,7 +254,7 @@ pub extern fn wbx_seal(obj: &mut WaterboxHost, ret: &mut Return<()>) {
|
|||
/// 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<()>) {
|
||||
pub extern "C" fn wbx_mount_file(obj: &mut WaterboxHost, name: *const c_char, callback: ReadCallback, userdata: usize, writable: bool, ret: &mut Return<()>) {
|
||||
let mut reader = CReader {
|
||||
userdata,
|
||||
callback
|
||||
|
@ -270,7 +270,7 @@ pub extern fn wbx_mount_file(obj: &mut WaterboxHost, name: *const c_char, callba
|
|||
/// 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<()>) {
|
||||
pub extern "C" fn wbx_unmount_file(obj: &mut WaterboxHost, name: *const c_char, callback_opt: Option<WriteCallback>, userdata: usize, ret: &mut Return<()>) {
|
||||
let res: anyhow::Result<()> = (|| {
|
||||
let data = obj.unmount_file(&arg_to_str(name)?)?;
|
||||
if let Some(callback) = callback_opt {
|
||||
|
@ -291,7 +291,7 @@ pub extern fn wbx_unmount_file(obj: &mut WaterboxHost, name: *const c_char, call
|
|||
/// in the callback. If the MissingFileResult is provided, it will be consumed immediately and will have the same effect
|
||||
/// as wbx_mount_file(). You may free resources associated with the MissingFileResult whenever control next returns to your code.
|
||||
// #[no_mangle]
|
||||
// pub extern fn wbx_set_missing_file_callback(obj: &mut WaterboxHost, mfc_o: Option<&MissingFileCallback>) {
|
||||
// pub extern "C" fn wbx_set_missing_file_callback(obj: &mut WaterboxHost, mfc_o: Option<&MissingFileCallback>) {
|
||||
// match mfc_o {
|
||||
// None => obj.set_missing_file_callback(None),
|
||||
// Some(mfc) => {
|
||||
|
@ -321,7 +321,7 @@ pub extern fn wbx_unmount_file(obj: &mut WaterboxHost, name: *const c_char, call
|
|||
/// Save state. Must not be called before seal. Must not be called with any writable files mounted.
|
||||
/// Must always be called with the same sequence and contents of readonly files.
|
||||
#[no_mangle]
|
||||
pub extern fn wbx_save_state(obj: &mut WaterboxHost, callback: WriteCallback, userdata: usize, ret: &mut Return<()>) {
|
||||
pub extern "C" fn wbx_save_state(obj: &mut WaterboxHost, callback: WriteCallback, userdata: usize, ret: &mut Return<()>) {
|
||||
let mut writer = CWriter {
|
||||
userdata,
|
||||
callback
|
||||
|
@ -337,7 +337,7 @@ pub extern fn wbx_save_state(obj: &mut WaterboxHost, callback: WriteCallback, us
|
|||
/// Must be called with the same wbx executable and memory layout as in the savestate.
|
||||
/// Errors generally poison the environment; sorry!
|
||||
#[no_mangle]
|
||||
pub extern fn wbx_load_state(obj: &mut WaterboxHost, callback: ReadCallback, userdata: usize, ret: &mut Return<()>) {
|
||||
pub extern "C" fn wbx_load_state(obj: &mut WaterboxHost, callback: ReadCallback, userdata: usize, ret: &mut Return<()>) {
|
||||
let mut reader = CReader {
|
||||
userdata,
|
||||
callback
|
||||
|
@ -349,7 +349,7 @@ pub extern fn wbx_load_state(obj: &mut WaterboxHost, callback: ReadCallback, use
|
|||
/// this should be set to false. Set to true to help catch dangling pointer issues. Will be ignored (and forced to true)
|
||||
/// if waterboxhost was built in debug mode. This is a single global setting.
|
||||
#[no_mangle]
|
||||
pub extern fn wbx_set_always_evict_blocks(_val: bool) {
|
||||
pub extern "C" fn wbx_set_always_evict_blocks(_val: bool) {
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
unsafe { ALWAYS_EVICT_BLOCKS = _val; }
|
||||
|
@ -358,7 +358,7 @@ pub extern fn wbx_set_always_evict_blocks(_val: bool) {
|
|||
|
||||
/// Retrieve the number of pages of guest memory that this host is tracking
|
||||
#[no_mangle]
|
||||
pub extern fn wbx_get_page_len(obj: &mut WaterboxHost, ret: &mut Return<usize>) {
|
||||
pub extern "C" fn wbx_get_page_len(obj: &mut WaterboxHost, ret: &mut Return<usize>) {
|
||||
ret.put(Ok(obj.page_len()))
|
||||
}
|
||||
|
||||
|
@ -371,7 +371,7 @@ pub extern fn wbx_get_page_len(obj: &mut WaterboxHost, ret: &mut Return<usize>)
|
|||
/// 0x40 - invisible
|
||||
/// 0x80 - dirty
|
||||
#[no_mangle]
|
||||
pub extern fn wbx_get_page_data(obj: &mut WaterboxHost, index: usize, ret: &mut Return<u8>) {
|
||||
pub extern "C" fn wbx_get_page_data(obj: &mut WaterboxHost, index: usize, ret: &mut Return<u8>) {
|
||||
if index >= obj.page_len() {
|
||||
ret.put(Err(anyhow!("Index out of range")))
|
||||
} else {
|
||||
|
|
|
@ -31,7 +31,7 @@ unsafe impl Sync for jit_descriptor {}
|
|||
|
||||
#[no_mangle]
|
||||
#[inline(never)]
|
||||
extern fn __jit_debug_register_code() {}
|
||||
extern "C" fn __jit_debug_register_code() {}
|
||||
|
||||
#[no_mangle]
|
||||
static mut __jit_debug_descriptor: jit_descriptor = jit_descriptor {
|
||||
|
|
|
@ -196,7 +196,7 @@ impl IStateable for WaterboxHost {
|
|||
|
||||
fn unimp(nr: SyscallNumber) -> SyscallResult {
|
||||
eprintln!("Stopped on unimplemented syscall {}", lookup_syscall(&nr));
|
||||
unsafe { std::intrinsics::breakpoint() }
|
||||
std::intrinsics::breakpoint();
|
||||
Err(ENOSYS)
|
||||
}
|
||||
|
||||
|
|
|
@ -118,14 +118,14 @@ mod trip_pal {
|
|||
use libc::*;
|
||||
use super::*;
|
||||
|
||||
type SaHandler = unsafe extern fn(i32) -> ();
|
||||
type SaSigaction = unsafe extern fn(i32, *const siginfo_t, *const ucontext_t) -> ();
|
||||
type SaHandler = unsafe extern "C" fn(i32) -> ();
|
||||
type SaSigaction = unsafe extern "C" fn(i32, *const siginfo_t, *const ucontext_t) -> ();
|
||||
static mut SA_OLD: Option<Box<sigaction>> = None;
|
||||
|
||||
pub fn initialize() {
|
||||
use std::mem::{transmute, zeroed};
|
||||
|
||||
unsafe extern fn handler(sig: i32, info: *const siginfo_t, ucontext: *const ucontext_t) {
|
||||
unsafe extern "C" fn handler(sig: i32, info: *const siginfo_t, ucontext: *const ucontext_t) {
|
||||
let fault_address = (*info).si_addr() as usize;
|
||||
let write = (*ucontext).uc_mcontext.gregs[REG_ERR as usize] & 2 != 0;
|
||||
let rethrow = !write || match trip(fault_address) {
|
||||
|
|
|
@ -176,7 +176,7 @@ impl GuestThreadSet {
|
|||
|
||||
pub fn exit(&mut self, context: &mut Context) -> SyscallReturn {
|
||||
if self.active_tid == 1 {
|
||||
unsafe { std::intrinsics::breakpoint() }
|
||||
std::intrinsics::breakpoint()
|
||||
}
|
||||
let addr = self.threads.get_mut(&self.active_tid).unwrap().tid_address;
|
||||
if addr != 0 {
|
||||
|
|
Loading…
Reference in New Issue