Change SmvImport to import to Snes9x core; various fixes
the importer is still far from perfect, but it should handle some things better than before - closes #3177
This commit is contained in:
parent
0af4d125b2
commit
9131c8ab2e
|
@ -1,10 +1,11 @@
|
|||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SNES;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SNES9X;
|
||||
|
||||
namespace BizHawk.Client.Common.movie.import
|
||||
{
|
||||
|
@ -13,11 +14,9 @@ namespace BizHawk.Client.Common.movie.import
|
|||
[ImporterFor("Snes9x", ".smv")]
|
||||
internal class SmvImport : MovieImporter
|
||||
{
|
||||
private LibsnesControllerDeck _deck;
|
||||
|
||||
protected override void RunImport()
|
||||
{
|
||||
Result.Movie.HeaderEntries[HeaderKeys.Core] = CoreNames.Bsnes; //TODO set to Snes9x instead? or give the user the choice?
|
||||
Result.Movie.HeaderEntries[HeaderKeys.Core] = CoreNames.Snes9X;
|
||||
|
||||
using var fs = SourceFile.Open(FileMode.Open, FileAccess.Read);
|
||||
using var r = new BinaryReader(fs);
|
||||
|
@ -74,32 +73,23 @@ namespace BizHawk.Client.Common.movie.import
|
|||
controllersUsed[controller - 1] = ((controllerFlags >> (controller - 1)) & 0x1) != 0;
|
||||
}
|
||||
|
||||
var controllerCount = controllersUsed.Count(c => c);
|
||||
int highestControllerIndex = 1 + Array.LastIndexOf(controllersUsed, true);
|
||||
var ss = new Snes9x.SyncSettings();
|
||||
|
||||
var ss = new LibsnesCore.SnesSyncSettings
|
||||
switch (highestControllerIndex)
|
||||
{
|
||||
LeftPort = LibsnesControllerDeck.ControllerType.Gamepad,
|
||||
RightPort = LibsnesControllerDeck.ControllerType.Gamepad
|
||||
};
|
||||
|
||||
if (controllerCount == 1)
|
||||
{
|
||||
ss.RightPort = LibsnesControllerDeck.ControllerType.Unplugged;
|
||||
case 1:
|
||||
ss.RightPort = LibSnes9x.RightPortDevice.None;
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
ss.LeftPort = LibSnes9x.LeftPortDevice.Multitap;
|
||||
ss.RightPort = LibSnes9x.RightPortDevice.None;
|
||||
break;
|
||||
case 5:
|
||||
ss.LeftPort = LibSnes9x.LeftPortDevice.Multitap;
|
||||
break;
|
||||
}
|
||||
else if (controllerCount > 2)
|
||||
{
|
||||
// More than 2 controllers means a multi-tap on the first port
|
||||
// Snes9x only supported up to 5 controllers, so right port would never be multitap
|
||||
ss.LeftPort = LibsnesControllerDeck.ControllerType.Multitap;
|
||||
|
||||
// Unless there are exactly 5, right port is unplugged, as the multitap will handle 4 controllers
|
||||
if (controllerCount < 5)
|
||||
{
|
||||
ss.RightPort = LibsnesControllerDeck.ControllerType.Unplugged;
|
||||
}
|
||||
}
|
||||
|
||||
_deck = new LibsnesControllerDeck(ss);
|
||||
|
||||
// 015 1-byte flags "movie options"
|
||||
byte movieFlags = r.ReadByte();
|
||||
|
@ -200,7 +190,9 @@ namespace BizHawk.Client.Common.movie.import
|
|||
Result.Movie.HeaderEntries[HeaderKeys.GameName] = gameName;
|
||||
}
|
||||
|
||||
SimpleController controllers = new(_deck.Definition);
|
||||
var _controllers = new Snes9xControllers(ss);
|
||||
Result.Movie.LogKey = new Bk2LogEntryGenerator("SNES", new Bk2Controller(_controllers.ControllerDefinition)).GenerateLogKey();
|
||||
SimpleController controllers = new(_controllers.ControllerDefinition);
|
||||
|
||||
r.BaseStream.Position = firstFrameOffset;
|
||||
/*
|
||||
|
@ -252,15 +244,25 @@ namespace BizHawk.Client.Common.movie.import
|
|||
controllers["Reset"] = false;
|
||||
}
|
||||
|
||||
/*
|
||||
While the meaning of controller data (for 1.51 and up) for a single standard SNES controller pad
|
||||
remains the same, each frame of controller data can contain additional bytes if input for peripherals
|
||||
is being recorded.
|
||||
*/
|
||||
if (version != "1.43" && player <= controllerTypes.Length)
|
||||
ushort controllerState = (ushort)(((controllerState1 << 4) & 0x0F00) | controllerState2);
|
||||
for (int button = 0; button < buttons.Length; button++)
|
||||
{
|
||||
controllers[$"P{player} {buttons[button]}"] =
|
||||
((controllerState >> button) & 0x1) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
While the meaning of controller data (for 1.51 and up) for a single standard SNES controller pad
|
||||
remains the same, each frame of controller data can contain additional bytes if input for peripherals
|
||||
is being recorded.
|
||||
*/
|
||||
if (version != "1.43")
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
var peripheral = "";
|
||||
switch (controllerTypes[player - 1])
|
||||
switch (controllerTypes[i])
|
||||
{
|
||||
case 0: // NONE
|
||||
continue;
|
||||
|
@ -292,13 +294,6 @@ namespace BizHawk.Client.Common.movie.import
|
|||
Result.Warnings.Add($"Unable to import {peripheral}. Not supported yet");
|
||||
}
|
||||
}
|
||||
|
||||
ushort controllerState = (ushort)(((controllerState1 << 4) & 0x0F00) | controllerState2);
|
||||
for (int button = 0; button < buttons.Length; button++)
|
||||
{
|
||||
controllers[$"P{player} {buttons[button]}"] =
|
||||
((controllerState >> button) & 0x1) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
// The controller data contains <number_of_frames + 1> frames.
|
||||
|
@ -311,7 +306,6 @@ namespace BizHawk.Client.Common.movie.import
|
|||
}
|
||||
|
||||
Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(ss);
|
||||
MaybeSetCorePreference(VSystemID.Raw.SNES, CoreNames.Bsnes, fileExt: ".smv");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X
|
|||
{
|
||||
public enum LeftPortDevice : uint
|
||||
{
|
||||
//None = 0, // something in the libretro spaghetti input goes wonky with None
|
||||
Joypad = 1
|
||||
None = 0,
|
||||
Joypad = 1,
|
||||
Multitap = 2
|
||||
}
|
||||
|
||||
public enum RightPortDevice : uint
|
||||
{
|
||||
//None = 0, // something in the libretro spaghetti input goes wonky with None
|
||||
None = 0,
|
||||
Joypad = 1,
|
||||
Multitap = 2,
|
||||
Mouse = 3,
|
||||
|
|
|
@ -71,168 +71,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X
|
|||
PutSettings(settings);
|
||||
}
|
||||
|
||||
private readonly short[] _inputState = new short[16 * 8];
|
||||
private List<ControlDefUnMerger> _cdums;
|
||||
private readonly List<IControlDevice> _controllers = new List<IControlDevice>();
|
||||
|
||||
private void InitControllers()
|
||||
{
|
||||
_core.biz_set_port_devices(_syncSettings.LeftPort, _syncSettings.RightPort);
|
||||
|
||||
switch (_syncSettings.LeftPort)
|
||||
{
|
||||
case LibSnes9x.LeftPortDevice.Joypad:
|
||||
_controllers.Add(new Joypad());
|
||||
break;
|
||||
}
|
||||
switch (_syncSettings.RightPort)
|
||||
{
|
||||
case LibSnes9x.RightPortDevice.Joypad:
|
||||
_controllers.Add(new Joypad());
|
||||
break;
|
||||
case LibSnes9x.RightPortDevice.Multitap:
|
||||
_controllers.Add(new Joypad());
|
||||
_controllers.Add(new Joypad());
|
||||
_controllers.Add(new Joypad());
|
||||
_controllers.Add(new Joypad());
|
||||
break;
|
||||
case LibSnes9x.RightPortDevice.Mouse:
|
||||
_controllers.Add(new Mouse());
|
||||
break;
|
||||
case LibSnes9x.RightPortDevice.SuperScope:
|
||||
_controllers.Add(new SuperScope());
|
||||
break;
|
||||
case LibSnes9x.RightPortDevice.Justifier:
|
||||
_controllers.Add(new Justifier());
|
||||
break;
|
||||
}
|
||||
|
||||
_controllerDefinition = ControllerDefinitionMerger.GetMerged(
|
||||
"SNES Controller",
|
||||
_controllers.Select(c => c.Definition),
|
||||
out _cdums);
|
||||
|
||||
// add buttons that the core itself will handle
|
||||
_controllerDefinition.BoolButtons.Add("Reset");
|
||||
_controllerDefinition.BoolButtons.Add("Power");
|
||||
|
||||
_controllerDefinition.MakeImmutable();
|
||||
_controllers = new Snes9xControllers(_syncSettings);
|
||||
}
|
||||
|
||||
private void UpdateControls(IController c)
|
||||
{
|
||||
Array.Clear(_inputState, 0, 16 * 8);
|
||||
for (int i = 0, offset = 0; i < _controllers.Count; i++, offset += 16)
|
||||
{
|
||||
_controllers[i].ApplyState(_cdums[i].UnMerge(c), _inputState, offset);
|
||||
}
|
||||
}
|
||||
private Snes9xControllers _controllers;
|
||||
|
||||
private interface IControlDevice
|
||||
{
|
||||
ControllerDefinition Definition { get; }
|
||||
void ApplyState(IController controller, short[] input, int offset);
|
||||
}
|
||||
|
||||
private class Joypad : IControlDevice
|
||||
{
|
||||
private static readonly string[] Buttons =
|
||||
{
|
||||
"0B",
|
||||
"0Y",
|
||||
"0Select",
|
||||
"0Start",
|
||||
"0Up",
|
||||
"0Down",
|
||||
"0Left",
|
||||
"0Right",
|
||||
"0A",
|
||||
"0X",
|
||||
"0L",
|
||||
"0R"
|
||||
};
|
||||
|
||||
private static int ButtonOrder(string btn)
|
||||
{
|
||||
var order = new Dictionary<string, int>
|
||||
{
|
||||
["0Up"] = 0,
|
||||
["0Down"] = 1,
|
||||
["0Left"] = 2,
|
||||
["0Right"] = 3,
|
||||
|
||||
["0Select"] = 4,
|
||||
["0Start"] = 5,
|
||||
|
||||
["0Y"] = 6,
|
||||
["0B"] = 7,
|
||||
|
||||
["0X"] = 8,
|
||||
["0A"] = 9,
|
||||
|
||||
["0L"] = 10,
|
||||
["0R"] = 11
|
||||
};
|
||||
|
||||
return order[btn];
|
||||
}
|
||||
|
||||
private static readonly ControllerDefinition _definition = new("(SNES Controller fragment)")
|
||||
{
|
||||
BoolButtons = Buttons.OrderBy(ButtonOrder).ToList()
|
||||
};
|
||||
|
||||
public ControllerDefinition Definition { get; } = _definition;
|
||||
|
||||
public void ApplyState(IController controller, short[] input, int offset)
|
||||
{
|
||||
for (int i = 0; i < Buttons.Length; i++)
|
||||
input[offset + i] = (short)(controller.IsPressed(Buttons[i]) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class Analog : IControlDevice
|
||||
{
|
||||
public abstract ControllerDefinition Definition { get; }
|
||||
|
||||
public void ApplyState(IController controller, short[] input, int offset)
|
||||
{
|
||||
foreach (var s in Definition.Axes.Keys)
|
||||
input[offset++] = (short)controller.AxisValue(s);
|
||||
foreach (var s in Definition.BoolButtons)
|
||||
input[offset++] = (short)(controller.IsPressed(s) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
private class Mouse : Analog
|
||||
{
|
||||
private static readonly ControllerDefinition _definition
|
||||
= new ControllerDefinition("(SNES Controller fragment)") { BoolButtons = { "0Mouse Left", "0Mouse Right" } }
|
||||
.AddXYPair("0Mouse {0}", AxisPairOrientation.RightAndDown, (-127).RangeTo(127), 0); //TODO verify direction against hardware
|
||||
|
||||
public override ControllerDefinition Definition => _definition;
|
||||
}
|
||||
|
||||
private class SuperScope : Analog
|
||||
{
|
||||
private static readonly ControllerDefinition _definition
|
||||
= new ControllerDefinition("(SNES Controller fragment)") { BoolButtons = { "0Trigger", "0Cursor", "0Turbo", "0Pause", "0Offscreen" } }
|
||||
.AddLightGun("0Scope {0}");
|
||||
|
||||
public override ControllerDefinition Definition => _definition;
|
||||
}
|
||||
|
||||
private class Justifier : Analog
|
||||
{
|
||||
private static readonly ControllerDefinition _definition
|
||||
= new ControllerDefinition("(SNES Controller fragment)") { BoolButtons = { "0Trigger", "0Start", "0Offscreen" } }
|
||||
.AddLightGun("0Justifier {0}");
|
||||
|
||||
public override ControllerDefinition Definition => _definition;
|
||||
}
|
||||
|
||||
private ControllerDefinition _controllerDefinition;
|
||||
public override ControllerDefinition ControllerDefinition => _controllerDefinition;
|
||||
public override ControllerDefinition ControllerDefinition => _controllers.ControllerDefinition;
|
||||
|
||||
public DisplayType Region { get; }
|
||||
|
||||
|
@ -242,8 +90,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X
|
|||
_core.biz_hard_reset();
|
||||
else if (controller.IsPressed("Reset"))
|
||||
_core.biz_soft_reset();
|
||||
UpdateControls(controller);
|
||||
_core.SetButtons(_inputState);
|
||||
_controllers.UpdateControls(controller);
|
||||
_core.SetButtons(_controllers.inputState);
|
||||
|
||||
return new LibWaterboxCore.FrameInfo();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SNES;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.SNES9X
|
||||
{
|
||||
public class Snes9xControllers
|
||||
{
|
||||
private readonly List<ControlDefUnMerger> _cdums;
|
||||
private readonly List<IControlDevice> _controllers = new();
|
||||
|
||||
internal readonly short[] inputState = new short[16 * 8];
|
||||
|
||||
public ControllerDefinition ControllerDefinition { get; }
|
||||
|
||||
public Snes9xControllers(Snes9x.SyncSettings ss)
|
||||
{
|
||||
switch (ss.LeftPort)
|
||||
{
|
||||
case LibSnes9x.LeftPortDevice.Joypad:
|
||||
_controllers.Add(new Joypad());
|
||||
break;
|
||||
case LibSnes9x.LeftPortDevice.Multitap:
|
||||
_controllers.Add(new Joypad());
|
||||
_controllers.Add(new Joypad());
|
||||
_controllers.Add(new Joypad());
|
||||
_controllers.Add(new Joypad());
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ss.RightPort)
|
||||
{
|
||||
case LibSnes9x.RightPortDevice.Joypad:
|
||||
_controllers.Add(new Joypad());
|
||||
break;
|
||||
case LibSnes9x.RightPortDevice.Multitap:
|
||||
_controllers.Add(new Joypad());
|
||||
_controllers.Add(new Joypad());
|
||||
_controllers.Add(new Joypad());
|
||||
_controllers.Add(new Joypad());
|
||||
break;
|
||||
case LibSnes9x.RightPortDevice.Mouse:
|
||||
_controllers.Add(new Mouse());
|
||||
break;
|
||||
case LibSnes9x.RightPortDevice.SuperScope:
|
||||
_controllers.Add(new SuperScope());
|
||||
break;
|
||||
case LibSnes9x.RightPortDevice.Justifier:
|
||||
_controllers.Add(new Justifier());
|
||||
break;
|
||||
}
|
||||
|
||||
ControllerDefinition = ControllerDefinitionMerger.GetMerged(
|
||||
"SNES Controller",
|
||||
_controllers.Select(c => c.Definition),
|
||||
out _cdums);
|
||||
|
||||
// add buttons that the core itself will handle
|
||||
ControllerDefinition.BoolButtons.Add("Reset");
|
||||
ControllerDefinition.BoolButtons.Add("Power");
|
||||
|
||||
ControllerDefinition.MakeImmutable();
|
||||
}
|
||||
|
||||
internal void UpdateControls(IController c)
|
||||
{
|
||||
Array.Clear(inputState, 0, 16 * 8);
|
||||
for (int i = 0, offset = 0; i < _controllers.Count; i++, offset += 16)
|
||||
{
|
||||
_controllers[i]
|
||||
.ApplyState(_cdums[i]
|
||||
.UnMerge(c), inputState, offset);
|
||||
}
|
||||
}
|
||||
|
||||
private interface IControlDevice
|
||||
{
|
||||
ControllerDefinition Definition { get; }
|
||||
void ApplyState(IController controller, short[] input, int offset);
|
||||
}
|
||||
|
||||
private class Joypad : IControlDevice
|
||||
{
|
||||
private static readonly string[] Buttons =
|
||||
{
|
||||
"0B", "0Y", "0Select", "0Start", "0Up", "0Down", "0Left", "0Right", "0A", "0X", "0L", "0R"
|
||||
};
|
||||
|
||||
private static int ButtonOrder(string btn)
|
||||
{
|
||||
var order = new Dictionary<string, int>
|
||||
{
|
||||
["0Up"] = 0, ["0Down"] = 1, ["0Left"] = 2, ["0Right"] = 3, ["0Select"] = 4, ["0Start"] = 5, ["0Y"] = 6, ["0B"] = 7, ["0X"] = 8, ["0A"] = 9
|
||||
, ["0L"] = 10, ["0R"] = 11
|
||||
};
|
||||
|
||||
return order[btn];
|
||||
}
|
||||
|
||||
private static readonly ControllerDefinition _definition = new("(SNES Controller fragment)")
|
||||
{
|
||||
BoolButtons = Buttons.OrderBy(ButtonOrder)
|
||||
.ToList()
|
||||
};
|
||||
|
||||
public ControllerDefinition Definition { get; } = _definition;
|
||||
|
||||
public void ApplyState(IController controller, short[] input, int offset)
|
||||
{
|
||||
for (int i = 0; i < Buttons.Length; i++)
|
||||
input[offset + i] = (short)(controller.IsPressed(Buttons[i]) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class Analog : IControlDevice
|
||||
{
|
||||
public abstract ControllerDefinition Definition { get; }
|
||||
|
||||
public void ApplyState(IController controller, short[] input, int offset)
|
||||
{
|
||||
foreach (var s in Definition.Axes.Keys)
|
||||
input[offset++] = (short)controller.AxisValue(s);
|
||||
foreach (var s in Definition.BoolButtons)
|
||||
input[offset++] = (short)(controller.IsPressed(s) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
private class Mouse : Analog
|
||||
{
|
||||
private static readonly ControllerDefinition _definition
|
||||
= new ControllerDefinition("(SNES Controller fragment)") { BoolButtons = { "0Mouse Left", "0Mouse Right" } }
|
||||
.AddXYPair("0Mouse {0}", AxisPairOrientation.RightAndDown, (-127).RangeTo(127), 0); //TODO verify direction against hardware
|
||||
|
||||
public override ControllerDefinition Definition => _definition;
|
||||
}
|
||||
|
||||
private class SuperScope : Analog
|
||||
{
|
||||
private static readonly ControllerDefinition _definition
|
||||
= new ControllerDefinition("(SNES Controller fragment)") { BoolButtons = { "0Trigger", "0Cursor", "0Turbo", "0Pause", "0Offscreen" } }
|
||||
.AddLightGun("0Scope {0}");
|
||||
|
||||
public override ControllerDefinition Definition => _definition;
|
||||
}
|
||||
|
||||
private class Justifier : Analog
|
||||
{
|
||||
private static readonly ControllerDefinition _definition
|
||||
= new ControllerDefinition("(SNES Controller fragment)") { BoolButtons = { "0Trigger", "0Start", "0Offscreen" } }
|
||||
.AddLightGun("0Justifier {0}");
|
||||
|
||||
public override ControllerDefinition Definition => _definition;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue