This commit is contained in:
feos 2020-03-21 22:25:50 +03:00
commit 10d3d764b3
674 changed files with 15159 additions and 17175 deletions

View File

@ -12,7 +12,7 @@ build:
- output
when: always
script:
- dotnet build BizHawk.sln -c Release -m -p:MachineNuGetPackageDir=$HOME/.nuget/packages
- Dist/BuildRelease.sh -p:MachineRunAnalyzersDuringBuild=true
stage: build
deploy_artifact:

View File

@ -340,6 +340,22 @@
"R": "E, J1 B6, X1 RightShoulder",
"Power": ""
},
"NDS Controller": {
"A": "X, J1 B3, X1 B",
"B": "Z, J1 B2, X1 A",
"X": "S, J1 B4, X1 Y",
"Y": "A, J1 B1, X1 X",
"Up": "Up, J1 POV1U, X1 DpadUp, X1 LStickUp",
"Down": "Down, J1 POV1D, X1 DpadDown, X1 LStickDown",
"Left": "Left, J1 POV1L, X1 DpadLeft, X1 LStickLeft",
"Right": "Right, J1 POV1R, X1 DpadRight, X1 LStickRight",
"L": "W, J1 B5, X1 LeftShoulder",
"R": "E, J1 B6, X1 RightShoulder",
"Select": "Space, J1 B9, X1 Back",
"Start": "Return, J1 B10, X1 Start",
"Lid": "",
"Touch": "WMouse L"
},
"Atari 2600 Basic Controller": {
"Reset": "D, J1 B9, X1 Back",
"Select": "S, J1 B10, X1 Start",
@ -1616,7 +1632,19 @@
"Deadzone": 0.0
}
},
"Nintendo 64 Controller": {
"NDS Controller": {
"TouchX": {
"Value": "WMouse X",
"Mult": 1.0,
"Deadzone": 0.0
},
"TouchY": {
"Value": "WMouse Y",
"Mult": 1.0,
"Deadzone": 0.0
}
},
"Nintento 64 Controller": {
"P1 X Axis": {
"Value": "X1 LeftThumbX Axis",
"Mult": 1.0,

View File

@ -3,17 +3,17 @@
;;;
;;; Homebrew (but not derived from TOSEC)
SHA1:26A044C68FF08F574F6649B731B82CACA8C0E797 D Death Race O2
SHA1:E04D12B6290370B76F90E76D9DE75B6470B4F913 D redgreen O2
SHA1:38D4AAB263E10B1DAC3410C120536EE079826BCB D Fatso 2 O2
SHA1:3720DD6B5EE3DC62C5AF2EA9D915A2B83DE9463D D Chief Chef O2
SHA1:FEB358E28587DE70D1E89BF0F9A3209CE0B67C57 D Haunted House O2
SHA1:B1D65BEDB56FE7A9CF60AA054A9FD9BB7F65B77C D 3D Box O2
SHA1:0270047E2B1FC07581BF0FF9E55035925CF0EFF0 D Guiseppe apr14 O2
SHA1:0D6B44E2445DBB3BF70C4E05F222822845C29762 D Boob O2
SHA1:DE888B7AA2716C3134CFF812A4E07E86B2479537 D Shoot the B O2
SHA1:85A44A99B254D92A7433EE46E4CAA91483D7FEA2 D Go Sub 2 O2
SHA1:2B34EF0E1A8C0371F00A33D6950E0807F3CB886E D Happy Emu O2
SHA1:26A044C68FF08F574F6649B731B82CACA8C0E797 D Death Race O2 US
SHA1:E04D12B6290370B76F90E76D9DE75B6470B4F913 D redgreen O2 US
SHA1:38D4AAB263E10B1DAC3410C120536EE079826BCB D Fatso 2 O2 US
SHA1:3720DD6B5EE3DC62C5AF2EA9D915A2B83DE9463D D Chief Chef O2 US
SHA1:FEB358E28587DE70D1E89BF0F9A3209CE0B67C57 D Haunted House O2 US
SHA1:B1D65BEDB56FE7A9CF60AA054A9FD9BB7F65B77C D 3D Box O2 US
SHA1:0270047E2B1FC07581BF0FF9E55035925CF0EFF0 D Guiseppe apr14 O2 US
SHA1:0D6B44E2445DBB3BF70C4E05F222822845C29762 D Boob O2 US
SHA1:DE888B7AA2716C3134CFF812A4E07E86B2479537 D Shoot the B O2 US
SHA1:85A44A99B254D92A7433EE46E4CAA91483D7FEA2 D Go Sub 2 O2 US
SHA1:2B34EF0E1A8C0371F00A33D6950E0807F3CB886E D Happy Emu O2 US

View File

@ -16,8 +16,10 @@ namespace BizHawk.BizInvoke
public MemoryBlockUnix(ulong start, ulong size) : base(start, size)
{
throw new NotImplementedException($"{nameof(MemoryBlockUnix)} ctor");
#if false
_fd = memfd_create("MemoryBlockUnix", 0);
if (_fd == -1) throw new InvalidOperationException($"{nameof(memfd_create)}() returned -1");
#endif
}
/// <exception cref="InvalidOperationException"><see cref="MemoryBlockBase.Active"/> is <see langword="true"/> or failed to map memory</exception>

View File

@ -25,120 +25,51 @@ namespace BizHawk.Client.ApiHawk
/// <exception cref="IndexOutOfRangeException">Thrown when SystemId hasn't been found</exception>
public object Convert(object value, Type targetType, object parameter, CultureInfo cultureInfo)
{
switch ((string)value)
return (string) value switch
{
case "AppleII":
return CoreSystem.AppleII;
case "A26":
return CoreSystem.Atari2600;
case "A78":
return CoreSystem.Atari7800;
case "Coleco":
return CoreSystem.ColecoVision;
case "C64":
return CoreSystem.Commodore64;
case "DGB":
return CoreSystem.DualGameBoy;
case "GB":
return CoreSystem.GameBoy;
case "GBA":
return CoreSystem.GameBoyAdvance;
case "GEN":
return CoreSystem.Genesis;
case "INTV":
return CoreSystem.Intellivision;
case "Libretro":
return CoreSystem.Libretro;
case "Lynx":
return CoreSystem.Lynx;
case "SMS":
return CoreSystem.MasterSystem;
case "NES":
return CoreSystem.NES;
case "N64":
return CoreSystem.Nintendo64;
case "NULL":
return CoreSystem.Null;
case "PCE":
case "PCECD":
case "SGX":
return CoreSystem.PCEngine;
case "PSX":
return CoreSystem.Playstation;
case "PSP":
return CoreSystem.PSP;
case "SAT":
return CoreSystem.Saturn;
case "SNES":
return CoreSystem.SNES;
case "TI83":
return CoreSystem.TI83;
case "VEC":
return CoreSystem.Vectrex;
case "WSWAN":
return CoreSystem.WonderSwan;
case "ZXSpectrum":
return CoreSystem.ZXSpectrum;
case "AmstradCPC":
return CoreSystem.AmstradCPC;
case "GGL":
return CoreSystem.GGL;
case "ChannelF":
return CoreSystem.ChannelF;
case "GB3x":
return CoreSystem.GB3x;
case "GB4x":
return CoreSystem.GB4x;
case "MAME":
return CoreSystem.MAME;
case "O2":
return CoreSystem.Odyssey2;
case "MSX":
return CoreSystem.MSX;
case "VB":
case "NGP":
case "DNGP":
case "SGB":
case "UZE":
case "PCFX":
return 0; // like I give a shit
default:
throw new IndexOutOfRangeException($"{value} is missing in convert list");
}
"AppleII" => CoreSystem.AppleII,
"A26" => CoreSystem.Atari2600,
"A78" => CoreSystem.Atari7800,
"Coleco" => CoreSystem.ColecoVision,
"C64" => CoreSystem.Commodore64,
"DGB" => CoreSystem.DualGameBoy,
"GB" => CoreSystem.GameBoy,
"GBA" => CoreSystem.GameBoyAdvance,
"GEN" => CoreSystem.Genesis,
"INTV" => CoreSystem.Intellivision,
"Libretro" => CoreSystem.Libretro,
"Lynx" => CoreSystem.Lynx,
"SMS" => CoreSystem.MasterSystem,
"NDS" => CoreSystem.NintendoDS,
"NES" => CoreSystem.NES,
"N64" => CoreSystem.Nintendo64,
"NULL" => CoreSystem.Null,
"PCE" => CoreSystem.PCEngine,
"PCECD" => CoreSystem.PCEngine,
"SGX" => CoreSystem.PCEngine,
"PSX" => CoreSystem.Playstation,
"SAT" => CoreSystem.Saturn,
"SNES" => CoreSystem.SNES,
"TI83" => CoreSystem.TI83,
"VEC" => CoreSystem.Vectrex,
"WSWAN" => CoreSystem.WonderSwan,
"ZXSpectrum" => CoreSystem.ZXSpectrum,
"AmstradCPC" => CoreSystem.AmstradCPC,
"GGL" => CoreSystem.GGL,
"ChannelF" => CoreSystem.ChannelF,
"GB3x" => CoreSystem.GB3x,
"GB4x" => CoreSystem.GB4x,
"MAME" => CoreSystem.MAME,
"O2" => CoreSystem.Odyssey2,
"MSX" => CoreSystem.MSX,
"VB" => CoreSystem.VirtualBoy,
"NGP" => CoreSystem.NeoGeoPocket,
"DNGP" => CoreSystem.NeoGeoPocket,
"SGB" => CoreSystem.SuperGameBoy,
"UZE" => CoreSystem.UzeBox,
"PCFX" => CoreSystem.PcFx,
_ => throw new IndexOutOfRangeException($"{value} is missing in convert list")
};
}
@ -165,89 +96,35 @@ namespace BizHawk.Client.ApiHawk
/// <exception cref="IndexOutOfRangeException">Thrown when <see cref="CoreSystem"/> hasn't been found</exception>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo cultureInfo)
{
switch ((CoreSystem)value)
return (CoreSystem) value switch
{
case CoreSystem.AppleII:
return "AppleII";
case CoreSystem.Atari2600:
return "A26";
case CoreSystem.Atari7800:
return "A78";
case CoreSystem.ColecoVision:
return "Coleco";
case CoreSystem.Commodore64:
return "C64";
case CoreSystem.DualGameBoy:
return "DGB";
case CoreSystem.GameBoy:
return "GB";
case CoreSystem.GameBoyAdvance:
return "GBA";
case CoreSystem.Genesis:
return "GEN";
case CoreSystem.Intellivision:
return "INTV";
case CoreSystem.Libretro:
return "Libretro";
case CoreSystem.Lynx:
return "Lynx";
case CoreSystem.MasterSystem:
return "SMS";
case CoreSystem.NES:
return "NES";
case CoreSystem.Nintendo64:
return "N64";
case CoreSystem.Null:
return "NULL";
case CoreSystem.PCEngine:
return "PCE";
case CoreSystem.Playstation:
return "PSX";
case CoreSystem.PSP:
return "PSP";
case CoreSystem.Saturn:
return "SAT";
case CoreSystem.SNES:
return "SNES";
case CoreSystem.TI83:
return "TI83";
case CoreSystem.WonderSwan:
return "WSWAN";
case CoreSystem.ZXSpectrum:
return "ZXSpectrum";
case CoreSystem.AmstradCPC:
return "AmstradCPC";
case CoreSystem.Odyssey2:
return "O2";
default:
throw new IndexOutOfRangeException($"{value} is missing in convert list");
}
CoreSystem.AppleII => "AppleII",
CoreSystem.Atari2600 => "A26",
CoreSystem.Atari7800 => "A78",
CoreSystem.ColecoVision => "Coleco",
CoreSystem.Commodore64 => "C64",
CoreSystem.DualGameBoy => "DGB",
CoreSystem.GameBoy => "GB",
CoreSystem.GameBoyAdvance => "GBA",
CoreSystem.Genesis => "GEN",
CoreSystem.Intellivision => "INTV",
CoreSystem.Libretro => "Libretro",
CoreSystem.Lynx => "Lynx",
CoreSystem.MasterSystem => "SMS",
CoreSystem.NES => "NES",
CoreSystem.Nintendo64 => "N64",
CoreSystem.Null => "NULL",
CoreSystem.PCEngine => "PCE",
CoreSystem.Playstation => "PSX",
CoreSystem.Saturn => "SAT",
CoreSystem.SNES => "SNES",
CoreSystem.TI83 => "TI83",
CoreSystem.WonderSwan => "WSWAN",
CoreSystem.ZXSpectrum => "ZXSpectrum",
CoreSystem.AmstradCPC => "AmstradCPC",
CoreSystem.Odyssey2 => "O2",
_ => throw new IndexOutOfRangeException($"{value} is missing in convert list")
};
}

View File

@ -162,7 +162,7 @@ namespace BizHawk.Client.ApiHawk
/// <param name="name">Savestate friendly name</param>
public static void LoadState(string name)
{
InvokeMainFormMethod("LoadState", new object[] { Path.Combine(PathManager.GetSaveStatePath(Global.Game), $"{name}.State"), name, false, false });
InvokeMainFormMethod("LoadState", new object[] { Path.Combine(Global.Config.PathEntries.SaveStateAbsolutePath(Global.Game.System), $"{name}.State"), name, false, false });
}
@ -245,7 +245,7 @@ namespace BizHawk.Client.ApiHawk
/// <param name="name">Savestate friendly name</param>
public static void SaveState(string name)
{
InvokeMainFormMethod("SaveState", new object[] { Path.Combine(PathManager.GetSaveStatePath(Global.Game), $"{name}.State"), name, false });
InvokeMainFormMethod("SaveState", new object[] { Path.Combine(Global.Config.PathEntries.SaveStateAbsolutePath(Global.Game.System), $"{name}.State"), name, false });
}
/// <summary>
@ -359,7 +359,7 @@ namespace BizHawk.Client.ApiHawk
{
if (joypad.Inputs == 0)
{
AutoFireStickyXorAdapter joypadAdapter = Global.AutofireStickyXORAdapter;
AutoFireStickyXorAdapter joypadAdapter = Global.InputManager.AutofireStickyXorAdapter;
joypadAdapter.ClearStickies();
}
else
@ -368,7 +368,7 @@ namespace BizHawk.Client.ApiHawk
{
if (joypad.Inputs.HasFlag(button))
{
AutoFireStickyXorAdapter joypadAdapter = Global.AutofireStickyXORAdapter;
AutoFireStickyXorAdapter joypadAdapter = Global.InputManager.AutofireStickyXorAdapter;
joypadAdapter.SetSticky(
RunningSystem == SystemInfo.GB
? $"{JoypadConverter.ConvertBack(button, RunningSystem)}"
@ -408,7 +408,7 @@ namespace BizHawk.Client.ApiHawk
/// </summary>
private static void GetAllInputs()
{
var joypadAdapter = Global.AutofireStickyXORAdapter;
var joypadAdapter = Global.InputManager.AutofireStickyXorAdapter;
var pressedButtons = joypadAdapter.Definition.BoolButtons
.Where(b => joypadAdapter.IsPressed(b));
@ -530,13 +530,10 @@ namespace BizHawk.Client.ApiHawk
public static void SetSoundOn(bool enable)
{
Global.Config.SoundEnabled = enable;
if (enable != Global.Config.SoundEnabled) InvokeMainFormMethod("ToggleSound");
}
public static bool GetSoundOn()
{
return Global.Config.SoundEnabled;
}
public static bool GetSoundOn() => Global.Config.SoundEnabled;
public static bool IsPaused()
{

View File

@ -18,12 +18,12 @@ namespace BizHawk.Client.Common
public IDictionary<string, dynamic> Get(int? controller = null)
{
return Global.AutofireStickyXORAdapter.ToDictionary(controller);
return Global.InputManager.AutofireStickyXorAdapter.ToDictionary(controller);
}
public IDictionary<string, dynamic> GetImmediate(int? controller = null)
{
return Global.ActiveController.ToDictionary(controller);
return Global.InputManager.ActiveController.ToDictionary(controller);
}
public void SetFromMnemonicStr(string inputLogEntry)
@ -38,13 +38,13 @@ namespace BizHawk.Client.Common
LogCallback($"invalid mnemonic string: {inputLogEntry}");
return;
}
foreach (var button in lg.Definition.BoolButtons) Global.ButtonOverrideAdaptor.SetButton(button, lg.IsPressed(button));
foreach (var floatButton in lg.Definition.FloatControls) Global.ButtonOverrideAdaptor.SetFloat(floatButton, lg.GetFloat(floatButton));
foreach (var button in lg.Definition.BoolButtons) Global.InputManager.ButtonOverrideAdapter.SetButton(button, lg.IsPressed(button));
foreach (var floatButton in lg.Definition.FloatControls) Global.InputManager.ButtonOverrideAdapter.SetFloat(floatButton, lg.GetFloat(floatButton));
}
public void Set(Dictionary<string, bool> buttons, int? controller = null)
{
foreach (var button in Global.ActiveController.Definition.BoolButtons)
foreach (var button in Global.InputManager.ActiveController.Definition.BoolButtons)
{
Set(button, buttons.TryGetValue(button, out var state) ? state : (bool?) null, controller);
}
@ -55,9 +55,9 @@ namespace BizHawk.Client.Common
try
{
var buttonToSet = controller == null ? button : $"P{controller} {button}";
if (state == null) Global.ButtonOverrideAdaptor.UnSet(buttonToSet);
else Global.ButtonOverrideAdaptor.SetButton(buttonToSet, state.Value);
Global.ActiveController.Overrides(Global.ButtonOverrideAdaptor);
if (state == null) Global.InputManager.ButtonOverrideAdapter.UnSet(buttonToSet);
else Global.InputManager.ButtonOverrideAdapter.SetButton(buttonToSet, state.Value);
Global.InputManager.ActiveController.Overrides(Global.InputManager.ButtonOverrideAdapter);
}
catch
{
@ -74,7 +74,7 @@ namespace BizHawk.Client.Common
{
try
{
Global.StickyXORAdapter.SetFloat(controller == null ? control : $"P{controller} {control}", value);
Global.InputManager.StickyXorAdapter.SetFloat(controller == null ? control : $"P{controller} {control}", value);
}
catch
{

View File

@ -135,8 +135,8 @@ namespace BizHawk.Client.Common
}
return new PlatformFrameRates()[
movie.HeaderEntries[HeaderKeys.PLATFORM],
movie.HeaderEntries.TryGetValue(HeaderKeys.PAL, out var isPal) && isPal == "1"
movie.HeaderEntries[HeaderKeys.Platform],
movie.HeaderEntries.TryGetValue(HeaderKeys.Pal, out var isPal) && isPal == "1"
];
}
}

View File

@ -17,6 +17,7 @@
GameBoy,
DualGameBoy,
GameBoyAdvance,
NintendoDS,
Nintendo64,
NES,
SNES,
@ -24,7 +25,6 @@
Genesis,
Saturn,
MasterSystem,
PSP,
Playstation,
WonderSwan,
Libretro,
@ -39,6 +39,9 @@
ChannelF,
Odyssey2,
MAME,
MSX
MSX,
SuperGameBoy,
UzeBox,
PcFx
}
}

View File

@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public class AutofireController : IController
{
public AutofireController(ControllerDefinition definition, IEmulator emulator)
{
On = Global.Config.AutofireOn < 1 ? 0 : Global.Config.AutofireOn;
Off = Global.Config.AutofireOff < 1 ? 0 : Global.Config.AutofireOff;
Definition = definition;
_emulator = emulator;
}
private readonly IEmulator _emulator;
private readonly WorkingDictionary<string, List<string>> _bindings = new WorkingDictionary<string, List<string>>();
private readonly WorkingDictionary<string, bool> _buttons = new WorkingDictionary<string, bool>();
private readonly WorkingDictionary<string, int> _buttonStarts = new WorkingDictionary<string, int>();
private bool _autofire = true;
public int On { get; set; }
public int Off { get; set; }
public ControllerDefinition Definition { get; }
public bool IsPressed(string button)
{
if (_autofire)
{
var a = (_emulator.Frame - _buttonStarts[button]) % (On + Off);
return a < On && _buttons[button];
}
return _buttons[button];
}
public void ClearStarts()
{
_buttonStarts.Clear();
}
/// <exception cref="NotImplementedException">always</exception>
public float GetFloat(string name)
{
throw new NotImplementedException();
}
/// <summary>
/// uses the bindings to latch our own logical button state from the source controller's button state (which are assumed to be the physical side of the binding).
/// this will clobber any existing data (use OR_* or other functions to layer in additional input sources)
/// </summary>
public void LatchFromPhysical(IController controller)
{
foreach (var kvp in _bindings)
{
foreach (var boundBtn in kvp.Value)
{
if (_buttons[kvp.Key] == false && controller.IsPressed(boundBtn))
{
_buttonStarts[kvp.Key] = _emulator.Frame;
}
}
}
_buttons.Clear();
foreach (var kvp in _bindings)
{
_buttons[kvp.Key] = false;
foreach (var button in kvp.Value)
{
if (controller.IsPressed(button))
{
_buttons[kvp.Key] = true;
}
}
}
}
public void BindMulti(string button, string controlString)
{
if (!string.IsNullOrEmpty(controlString))
{
var controlBindings = controlString.Split(',');
foreach (var control in controlBindings)
{
_bindings[button].Add(control.Trim());
}
}
}
public void IncrementStarts()
{
foreach (var key in _buttonStarts.Keys.ToArray())
{
_buttonStarts[key]++;
}
}
public List<string> PressedButtons => _buttons
.Where(kvp => kvp.Value)
.Select(kvp => kvp.Key)
.ToList();
}
}

View File

@ -5,6 +5,7 @@ using System.Linq;
using System.Reflection;
using ICSharpCode.SharpZipLib.Zip;
// ReSharper disable UnusedAutoPropertyAccessor.Local
namespace BizHawk.Client.Common
{
public class BinaryStateLump

View File

@ -1,330 +1,197 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public class Controller : IController
{
public Controller(ControllerDefinition definition)
{
_type = definition;
for (int i = 0; i < _type.FloatControls.Count; i++)
{
_floatButtons[_type.FloatControls[i]] = _type.FloatRanges[i].Mid;
_floatRanges[_type.FloatControls[i]] = _type.FloatRanges[i];
}
}
public ControllerDefinition Definition => _type;
public bool IsPressed(string button)
{
return _buttons[button];
}
public float GetFloat(string name)
{
return _floatButtons[name];
}
private readonly WorkingDictionary<string, List<string>> _bindings = new WorkingDictionary<string, List<string>>();
private readonly WorkingDictionary<string, bool> _buttons = new WorkingDictionary<string, bool>();
private readonly WorkingDictionary<string, float> _floatButtons = new WorkingDictionary<string, float>();
private readonly Dictionary<string, ControllerDefinition.FloatRange> _floatRanges = new WorkingDictionary<string, ControllerDefinition.FloatRange>();
private readonly Dictionary<string, AnalogBind> _floatBinds = new Dictionary<string, AnalogBind>();
private ControllerDefinition _type;
/// <summary>don't do this</summary>
public void ForceType(ControllerDefinition newType) { _type = newType; }
public bool this[string button] => IsPressed(button);
// Looks for bindings which are activated by the supplied physical button.
public List<string> SearchBindings(string button)
{
return (from kvp in _bindings from boundButton in kvp.Value where boundButton == button select kvp.Key).ToList();
}
// Searches bindings for the controller and returns true if this binding is mapped somewhere in this controller
public bool HasBinding(string button)
{
return _bindings
.SelectMany(kvp => kvp.Value)
.Any(boundButton => boundButton == button);
}
public void NormalizeFloats(IController controller)
{
foreach (var kvp in _floatBinds)
{
var input = _floatButtons[kvp.Key];
string outKey = kvp.Key;
float multiplier = kvp.Value.Mult;
float deadZone = kvp.Value.Deadzone;
if (_floatRanges.TryGetValue(outKey, out var range))
{
// input range is assumed to be -10000,0,10000
// first, modify for deadZone
float absInput = Math.Abs(input);
float zeroPoint = deadZone * 10000.0f;
if (absInput < zeroPoint)
{
input = 0.0f;
}
else
{
absInput -= zeroPoint;
absInput *= 10000.0f;
absInput /= 10000.0f - zeroPoint;
input = absInput * Math.Sign(input);
}
// zero 09-mar-2015 - not sure if adding + 1 here is correct.. but... maybe?
float output = 0;
if (range.Max < range.Min)
{
output = (((input * multiplier) + 10000.0f) * (range.Min - range.Max + 1) / 20000.0f) + range.Max;
}
else
{
output = (((input * multiplier) + 10000.0f) * (range.Max - range.Min + 1) / 20000.0f) + range.Min;
}
// zero 09-mar-2015 - at this point, we should only have integers, since that's all 100% of consoles ever see
// if this becomes a problem we can add flags to the range and update GUIs to be able to display floats
output = (int)output;
float lowerBound = Math.Min(range.Min, range.Max);
float upperBound = Math.Max(range.Min, range.Max);
if (output < lowerBound)
{
output = lowerBound;
}
if (output > upperBound)
{
output = upperBound;
}
_floatButtons[outKey] = output;
}
}
}
/// <summary>
/// uses the bindings to latch our own logical button state from the source controller's button state (which are assumed to be the physical side of the binding).
/// this will clobber any existing data (use OR_* or other functions to layer in additional input sources)
/// </summary>
public void LatchFromPhysical(IController controller)
{
_buttons.Clear();
foreach (var kvp in _bindings)
{
_buttons[kvp.Key] = false;
foreach (var button in kvp.Value)
{
if (controller.IsPressed(button))
{
_buttons[kvp.Key] = true;
}
}
}
foreach (var kvp in _floatBinds)
{
var input = controller.GetFloat(kvp.Value.Value);
string outKey = kvp.Key;
if (_floatRanges.ContainsKey(outKey))
{
_floatButtons[outKey] = input;
}
}
// it's not sure where this should happen, so for backwards compatibility.. do it every time
NormalizeFloats(controller);
}
public void ApplyAxisConstraints(string constraintClass)
{
_type.ApplyAxisConstraints(constraintClass, _floatButtons);
}
/// <summary>
/// merges pressed logical buttons from the supplied controller, effectively ORing it with the current state
/// </summary>
public void OR_FromLogical(IController controller)
{
// change: or from each button that the other input controller has
// foreach (string button in type.BoolButtons)
if (controller.Definition != null)
{
foreach (var button in controller.Definition.BoolButtons)
{
if (controller.IsPressed(button))
{
_buttons[button] = true;
}
}
}
}
public void Overrides(OverrideAdapter controller)
{
foreach (var button in controller.Overrides)
{
_buttons[button] = controller.IsPressed(button);
}
foreach (var button in controller.FloatOverrides)
{
_floatButtons[button] = controller.GetFloat(button);
}
foreach (var button in controller.InversedButtons)
{
_buttons[button] ^= true;
}
}
public void BindMulti(string button, string controlString)
{
if (string.IsNullOrEmpty(controlString))
{
return;
}
var controlBindings = controlString.Split(',');
foreach (var control in controlBindings)
{
_bindings[button].Add(control.Trim());
}
}
public void BindFloat(string button, AnalogBind bind)
{
_floatBinds[button] = bind;
}
public List<string> PressedButtons => _buttons
.Where(kvp => kvp.Value)
.Select(kvp => kvp.Key)
.ToList();
}
public class AutofireController : IController
{
public AutofireController(ControllerDefinition definition, IEmulator emulator)
{
On = Global.Config.AutofireOn < 1 ? 0 : Global.Config.AutofireOn;
Off = Global.Config.AutofireOff < 1 ? 0 : Global.Config.AutofireOff;
Definition = definition;
_emulator = emulator;
}
private readonly IEmulator _emulator;
private readonly WorkingDictionary<string, List<string>> _bindings = new WorkingDictionary<string, List<string>>();
private readonly WorkingDictionary<string, bool> _buttons = new WorkingDictionary<string, bool>();
private readonly WorkingDictionary<string, int> _buttonStarts = new WorkingDictionary<string, int>();
private bool _autofire = true;
public bool Autofire
{
get => false;
set => _autofire = value;
}
public int On { get; set; }
public int Off { get; set; }
public ControllerDefinition Definition { get; }
public bool IsPressed(string button)
{
if (_autofire)
{
var a = (_emulator.Frame - _buttonStarts[button]) % (On + Off);
return a < On && _buttons[button];
}
return _buttons[button];
}
public void ClearStarts()
{
_buttonStarts.Clear();
}
/// <exception cref="NotImplementedException">always</exception>
public float GetFloat(string name)
{
throw new NotImplementedException();
}
/// <summary>
/// uses the bindings to latch our own logical button state from the source controller's button state (which are assumed to be the physical side of the binding).
/// this will clobber any existing data (use OR_* or other functions to layer in additional input sources)
/// </summary>
public void LatchFromPhysical(IController controller)
{
foreach (var kvp in _bindings)
{
foreach (var boundBtn in kvp.Value)
{
if (_buttons[kvp.Key] == false && controller.IsPressed(boundBtn))
{
_buttonStarts[kvp.Key] = _emulator.Frame;
}
}
}
_buttons.Clear();
foreach (var kvp in _bindings)
{
_buttons[kvp.Key] = false;
foreach (var button in kvp.Value)
{
if (controller.IsPressed(button))
{
_buttons[kvp.Key] = true;
}
}
}
}
public void BindMulti(string button, string controlString)
{
if (!string.IsNullOrEmpty(controlString))
{
var controlBindings = controlString.Split(',');
foreach (var control in controlBindings)
{
_bindings[button].Add(control.Trim());
}
}
}
public void IncrementStarts()
{
foreach (var key in _buttonStarts.Keys.ToArray())
{
_buttonStarts[key]++;
}
}
public List<string> PressedButtons => _buttons
.Where(kvp => kvp.Value)
.Select(kvp => kvp.Key)
.ToList();
}
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public class Controller : IController
{
public Controller(ControllerDefinition definition)
{
Definition = definition;
for (int i = 0; i < Definition.FloatControls.Count; i++)
{
_floatButtons[Definition.FloatControls[i]] = Definition.FloatRanges[i].Mid;
_floatRanges[Definition.FloatControls[i]] = Definition.FloatRanges[i];
}
}
public ControllerDefinition Definition { get; private set; }
public bool IsPressed(string button) => _buttons[button];
public float GetFloat(string name) => _floatButtons[name];
private readonly WorkingDictionary<string, List<string>> _bindings = new WorkingDictionary<string, List<string>>();
private readonly WorkingDictionary<string, bool> _buttons = new WorkingDictionary<string, bool>();
private readonly WorkingDictionary<string, float> _floatButtons = new WorkingDictionary<string, float>();
private readonly Dictionary<string, ControllerDefinition.AxisRange> _floatRanges = new WorkingDictionary<string, ControllerDefinition.AxisRange>();
private readonly Dictionary<string, AnalogBind> _floatBinds = new Dictionary<string, AnalogBind>();
/// <summary>don't do this</summary>
public void ForceType(ControllerDefinition newType) => Definition = newType;
public bool this[string button] => IsPressed(button);
// Looks for bindings which are activated by the supplied physical button.
public List<string> SearchBindings(string button) =>
_bindings
.Where(b => b.Value.Any(v => v == button))
.Select(b => b.Key)
.ToList();
// Searches bindings for the controller and returns true if this binding is mapped somewhere in this controller
public bool HasBinding(string button) =>
_bindings
.SelectMany(kvp => kvp.Value)
.Any(boundButton => boundButton == button);
public void NormalizeFloats(IController controller)
{
foreach (var kvp in _floatBinds)
{
var input = _floatButtons[kvp.Key];
string outKey = kvp.Key;
float multiplier = kvp.Value.Mult;
float deadZone = kvp.Value.Deadzone;
if (_floatRanges.TryGetValue(outKey, out var range))
{
// input range is assumed to be -10000,0,10000
// first, modify for deadZone
float absInput = Math.Abs(input);
float zeroPoint = deadZone * 10000.0f;
if (absInput < zeroPoint)
{
input = 0.0f;
}
else
{
absInput -= zeroPoint;
absInput *= 10000.0f;
absInput /= 10000.0f - zeroPoint;
input = absInput * Math.Sign(input);
}
// zero 09-mar-2015 - not sure if adding + 1 here is correct.. but... maybe?
float output;
if (range.IsReversed)
{
output = (((input * multiplier) + 10000.0f) * (range.Min - range.Max + 1) / 20000.0f) + range.Max;
}
else
{
output = (((input * multiplier) + 10000.0f) * (range.Max - range.Min + 1) / 20000.0f) + range.Min;
}
// zero 09-mar-2015 - at this point, we should only have integers, since that's all 100% of consoles ever see
// if this becomes a problem we can add flags to the range and update GUIs to be able to display floats
_floatButtons[outKey] = output.ConstrainWithin(range.FloatRange);
}
}
}
/// <summary>
/// uses the bindings to latch our own logical button state from the source controller's button state (which are assumed to be the physical side of the binding).
/// this will clobber any existing data (use OR_* or other functions to layer in additional input sources)
/// </summary>
public void LatchFromPhysical(IController controller)
{
_buttons.Clear();
foreach (var kvp in _bindings)
{
_buttons[kvp.Key] = false;
foreach (var button in kvp.Value)
{
if (controller.IsPressed(button))
{
_buttons[kvp.Key] = true;
}
}
}
foreach (var kvp in _floatBinds)
{
var input = controller.GetFloat(kvp.Value.Value);
string outKey = kvp.Key;
if (_floatRanges.ContainsKey(outKey))
{
_floatButtons[outKey] = input;
}
}
// it's not sure where this should happen, so for backwards compatibility.. do it every time
NormalizeFloats(controller);
}
public void ApplyAxisConstraints(string constraintClass)
=> Definition.ApplyAxisConstraints(constraintClass, _floatButtons);
/// <summary>
/// merges pressed logical buttons from the supplied controller, effectively ORing it with the current state
/// </summary>
public void OR_FromLogical(IController controller)
{
// change: or from each button that the other input controller has
// foreach (string button in type.BoolButtons)
if (controller.Definition != null)
{
foreach (var button in controller.Definition.BoolButtons)
{
if (controller.IsPressed(button))
{
_buttons[button] = true;
}
}
}
}
public void Overrides(OverrideAdapter controller)
{
foreach (var button in controller.Overrides)
{
_buttons[button] = controller.IsPressed(button);
}
foreach (var button in controller.FloatOverrides)
{
_floatButtons[button] = controller.GetFloat(button);
}
foreach (var button in controller.InversedButtons)
{
_buttons[button] ^= true;
}
}
public void BindMulti(string button, string controlString)
{
if (string.IsNullOrEmpty(controlString))
{
return;
}
var controlBindings = controlString.Split(',');
foreach (var control in controlBindings)
{
_bindings[button].Add(control.Trim());
}
}
public void BindFloat(string button, AnalogBind bind)
{
_floatBinds[button] = bind;
}
public List<string> PressedButtons => _buttons
.Where(kvp => kvp.Value)
.Select(kvp => kvp.Key)
.ToList();
}
}

View File

@ -1,48 +1,40 @@
using System;
using System.Collections.Generic;
using System.IO;
using BizHawk.Common.PathExtensions;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public class CoreFileProvider : ICoreFileProvider
{
public string SubfileDirectory { get; set; }
public FirmwareManager FirmwareManager { get; set; }
private readonly FirmwareManager _firmwareManager;
private readonly Action<string> _showWarning;
private readonly PathEntryCollection _pathEntries;
private readonly IDictionary<string, string> _firmwareUserSpecifications;
public CoreFileProvider(Action<string> showWarning)
public CoreFileProvider(
Action<string> showWarning,
FirmwareManager firmwareManager,
PathEntryCollection pathEntries,
IDictionary<string, string> firmwareUserSpecifications)
{
_showWarning = showWarning;
_firmwareManager = firmwareManager;
_pathEntries = pathEntries;
_firmwareManager = firmwareManager;
_firmwareUserSpecifications = firmwareUserSpecifications;
}
public string PathSubfile(string fname)
{
return Path.Combine(SubfileDirectory ?? "", fname);
}
public string DllPath() => PathUtils.DllDirectoryPath;
public string DllPath()
{
return Path.Combine(PathManager.GetExeDirectoryAbsolute(), "dll");
}
// Poop
public string GetRetroSaveRAMDirectory(GameInfo game)
=> _pathEntries.RetroSaveRamAbsolutePath(game, Global.MovieSession.Movie.IsActive(), Global.MovieSession.Movie.Filename);
public string GetRetroSaveRAMDirectory()
{
return PathManager.RetroSaveRAMDirectory(Global.Game);
}
public string GetRetroSystemPath()
{
return PathManager.RetroSystemPath(Global.Game);
}
public string GetGameBasePath()
{
return PathManager.GetGameBasePath(Global.Game);
}
#region EmuLoadHelper api
// Poop
public string GetRetroSystemPath(GameInfo game)
=> _pathEntries.RetroSystemAbsolutePath(game);
private void FirmwareWarn(string sysID, string firmwareID, bool required, string msg = null)
{
@ -59,84 +51,48 @@ namespace BizHawk.Client.Common
}
}
/// <exception cref="MissingFirmwareException">not found and <paramref name="required"/> is true</exception>
public string GetFirmwarePath(string sysId, string firmwareId, bool required, string msg = null)
{
var path = FirmwareManager.Request(sysId, firmwareId);
if (path != null && !File.Exists(path))
{
path = null;
}
if (path == null)
{
FirmwareWarn(sysId, firmwareId, required, msg);
}
return path;
}
private byte[] GetFirmwareWithPath(string sysId, string firmwareId, bool required, string msg, out string path)
{
byte[] ret = null;
var path_ = GetFirmwarePath(sysId, firmwareId, required, msg);
if (path_ != null && File.Exists(path_))
{
try
{
ret = File.ReadAllBytes(path_);
}
catch (IOException)
{
}
}
var firmwarePath = _firmwareManager.Request(
_pathEntries,
_firmwareUserSpecifications,
sysId,
firmwareId);
if (ret == null && path_ != null)
if (firmwarePath == null || !File.Exists(firmwarePath))
{
path = null;
FirmwareWarn(sysId, firmwareId, required, msg);
return null;
}
path = path_;
return ret;
try
{
var ret = File.ReadAllBytes(firmwarePath);
path = firmwarePath;
return ret;
}
catch (IOException)
{
path = null;
FirmwareWarn(sysId, firmwareId, required, msg);
return null;
}
}
/// <exception cref="MissingFirmwareException">not found and <paramref name="required"/> is true</exception>
public byte[] GetFirmware(string sysId, string firmwareId, bool required, string msg = null)
{
string unused;
return GetFirmwareWithPath(sysId, firmwareId, required, msg, out unused);
}
=> GetFirmwareWithPath(sysId, firmwareId, required, msg, out _);
/// <exception cref="MissingFirmwareException">not found and <paramref name="required"/> is true</exception>
public byte[] GetFirmwareWithGameInfo(string sysId, string firmwareId, bool required, out GameInfo gi, string msg = null)
{
byte[] ret = GetFirmwareWithPath(sysId, firmwareId, required, msg, out var path);
if (ret != null && path != null)
{
gi = Database.GetGameInfo(ret, path);
}
else
{
gi = null;
}
gi = ret != null && path != null
? Database.GetGameInfo(ret, path)
: null;
return ret;
}
#endregion
// this should go away now
public static void SyncCoreCommInputSignals(CoreComm target)
{
string superhack = null;
if (target.CoreFileProvider is CoreFileProvider)
{
superhack = ((CoreFileProvider)target.CoreFileProvider).SubfileDirectory;
}
var cfp = new CoreFileProvider(target.ShowMessage) { SubfileDirectory = superhack };
target.CoreFileProvider = cfp;
cfp.FirmwareManager = Global.FirmwareManager;
}
}
}

View File

@ -18,7 +18,7 @@ namespace BizHawk.Client.Common
public string Hash { get; set; }
}
public List<FirmwareEventArgs> RecentlyServed { get; }
public List<FirmwareEventArgs> RecentlyServed { get; } = new List<FirmwareEventArgs>();
public class ResolutionInfo
{
@ -41,23 +41,13 @@ namespace BizHawk.Client.Common
public string FirmwareId { get; set; }
}
public FirmwareManager()
{
RecentlyServed = new List<FirmwareEventArgs>();
}
public ResolutionInfo Resolve(string sysId, string firmwareId)
{
return Resolve(FirmwareDatabase.LookupFirmwareRecord(sysId, firmwareId));
}
public ResolutionInfo Resolve(FirmwareDatabase.FirmwareRecord record, bool forbidScan = false)
public ResolutionInfo Resolve(PathEntryCollection pathEntries, IDictionary<string, string> userSpecifications, FirmwareDatabase.FirmwareRecord record, bool forbidScan = false)
{
// purpose of forbidScan: sometimes this is called from a loop in Scan(). we don't want to repeatedly DoScanAndResolve in that case, its already been done.
bool first = true;
RETRY:
_resolutionDictionary.TryGetValue(record, out var resolved);
_resolutionDictionary.TryGetValue(record, out var resolved);
// couldn't find it! do a scan and resolve to try harder
// NOTE: this could result in bad performance in some cases if the scanning happens repeatedly..
@ -65,7 +55,7 @@ namespace BizHawk.Client.Common
{
if (!forbidScan)
{
DoScanAndResolve();
DoScanAndResolve(pathEntries, userSpecifications);
}
first = false;
@ -76,9 +66,9 @@ namespace BizHawk.Client.Common
}
// Requests the specified firmware. tries really hard to scan and resolve as necessary
public string Request(string sysId, string firmwareId)
public string Request(PathEntryCollection pathEntries, IDictionary<string, string> userSpecifications, string sysId, string firmwareId)
{
var resolved = Resolve(sysId, firmwareId);
var resolved = Resolve(pathEntries, userSpecifications, FirmwareDatabase.LookupFirmwareRecord(sysId, firmwareId));
if (resolved == null)
{
return null;
@ -97,7 +87,6 @@ namespace BizHawk.Client.Common
private class RealFirmwareReader : IDisposable
{
private readonly List<RealFirmwareFile> _files = new List<RealFirmwareFile>();
private SHA1 _sha1 = SHA1.Create();
public void Dispose()
@ -109,7 +98,6 @@ namespace BizHawk.Client.Common
public RealFirmwareFile Read(FileInfo fi)
{
var rff = new RealFirmwareFile { FileInfo = fi };
long len = fi.Length;
using (var fs = fi.OpenRead())
{
@ -118,7 +106,6 @@ namespace BizHawk.Client.Common
rff.Hash = _sha1.Hash.BytesToHexString();
Dict[rff.Hash] = rff;
_files.Add(rff);
return rff;
}
@ -151,7 +138,7 @@ namespace BizHawk.Client.Common
return false;
}
public void DoScanAndResolve()
public void DoScanAndResolve(PathEntryCollection pathEntries, IDictionary<string, string> userSpecifications)
{
// build a list of file sizes. Only those will be checked during scanning
var sizes = new HashSet<long>();
@ -161,9 +148,10 @@ namespace BizHawk.Client.Common
}
using var reader = new RealFirmwareReader();
// build a list of files under the global firmwares path, and build a hash for each of them while we're at it
var todo = new Queue<DirectoryInfo>();
todo.Enqueue(new DirectoryInfo(PathManager.MakeAbsolutePath(Global.Config.PathEntries.FirmwaresPathFragment, null)));
todo.Enqueue(new DirectoryInfo(pathEntries.AbsolutePathFor(pathEntries.FirmwaresPathFragment, null)));
while (todo.Count != 0)
{
@ -175,9 +163,9 @@ namespace BizHawk.Client.Common
}
// we're going to allow recursing into subdirectories, now. its been verified to work OK
foreach (var disub in di.GetDirectories())
foreach (var subDir in di.GetDirectories())
{
todo.Enqueue(disub);
todo.Enqueue(subDir);
}
foreach (var fi in di.GetFiles())
@ -197,10 +185,10 @@ namespace BizHawk.Client.Common
// get all options for this firmware (in order)
var fr1 = fr;
var options =
from fo in FirmwareDatabase.FirmwareOptions
where fo.SystemId == fr1.SystemId && fo.FirmwareId == fr1.FirmwareId && fo.IsAcceptableOrIdeal
select fo;
var options = FirmwareDatabase.FirmwareOptions
.Where(fo => fo.SystemId == fr1.SystemId
&& fo.FirmwareId == fr1.FirmwareId
&& fo.IsAcceptableOrIdeal);
// try each option
foreach (var fo in options)
@ -230,7 +218,7 @@ namespace BizHawk.Client.Common
foreach (var fr in FirmwareDatabase.FirmwareRecords)
{
// do we have a user specification for this firmware record?
if (Global.Config.FirmwareUserSpecifications.TryGetValue(fr.ConfigKey, out var userSpec))
if (userSpecifications.TryGetValue(fr.ConfigKey, out var userSpec))
{
// flag it as user specified
if (!_resolutionDictionary.TryGetValue(fr, out ResolutionInfo ri))
@ -252,21 +240,24 @@ namespace BizHawk.Client.Common
continue;
}
// compute its hash
var rff = reader.Read(fi);
ri.Size = fi.Length;
ri.Hash = rff.Hash;
// compute its hash
RealFirmwareFile rff;
// NDS's firmware file contains user settings; these are over-written by sync settings, so we shouldn't allow them to impact the hash
if (fr.SystemId == "NDS" && fr.FirmwareId == "firmware")
rff = reader.Read(new FileInfo(Emulation.Cores.Consoles.Nintendo.NDS.MelonDS.CreateModifiedFirmware(userSpec)));
else
rff = reader.Read(fi);
ri.Size = fi.Length;
ri.Hash = rff.Hash;
// check whether it was a known file anyway, and go ahead and bind to the known file, as a perk (the firmwares config doesnt really use this information right now)
// check whether it was a known file anyway, and go ahead and bind to the known file, as a perk (the firmwares config doesn't really use this information right now)
if (FirmwareDatabase.FirmwareFilesByHash.TryGetValue(rff.Hash, out var ff))
{
ri.KnownFirmwareFile = ff;
// if the known firmware file is for a different firmware, flag it so we can show a warning
var option =
(from fo in FirmwareDatabase.FirmwareOptions
where fo.Hash == rff.Hash && fo.ConfigKey != fr.ConfigKey
select fr).FirstOrDefault();
var option = FirmwareDatabase.FirmwareOptions
.FirstOrDefault(fo => fo.Hash == rff.Hash && fo.ConfigKey != fr.ConfigKey);
if (option != null)
{

View File

@ -197,24 +197,24 @@ namespace BizHawk.Client.Common
_localHeader[26] = (byte)nameb.Length;
_localHeader[27] = (byte)(nameb.Length >> 8);
var localHeaderOffset = (int)(_output.Position);
var localHeaderOffset = (int)_output.Position;
_output.Write(_localHeader, 0, _localHeader.Length);
_output.Write(nameb, 0, nameb.Length);
var fileStart = (int)(_output.Position);
var fileStart = (int)_output.Position;
var s2 = new DeflateStream(_output, _level, true);
var s3 = new CRC32Stream(s2);
callback(s3);
s2.Flush();
var fileEnd = (int)(_output.Position);
var fileEnd = (int)_output.Position;
var crc = s3.Crc;
var compressedSize = fileEnd - fileStart;
var uncompressedSize = s3.Size;
var descriptor = new byte[]
var descriptor = new[]
{
(byte)crc,
(byte)(crc >> 8),
@ -248,12 +248,12 @@ namespace BizHawk.Client.Common
private void WriteFooter()
{
var centralHeaderOffset = (int)(_output.Position);
var centralHeaderOffset = (int)_output.Position;
foreach (var blob in _endBlobs)
_output.Write(blob, 0, blob.Length);
var centralHeaderEnd = (int)(_output.Position);
var centralHeaderEnd = (int)_output.Position;
var centralHeaderSize = centralHeaderEnd - centralHeaderOffset;

View File

@ -6,68 +6,26 @@ namespace BizHawk.Client.Common
{
public static class Global
{
public static IEmulator Emulator;
public static Config Config;
public static GameInfo Game;
public static CheatCollection CheatList;
public static FirmwareManager FirmwareManager;
public static IEmulator Emulator { get; set; }
public static Config Config { get; set; }
public static GameInfo Game { get; set; }
public static CheatCollection CheatList { get; set; } = new CheatCollection();
public static FirmwareManager FirmwareManager { get; set; }
public static IMovieSession MovieSession = new MovieSession();
public static IMovieSession MovieSession { get; set; } = new MovieSession();
/// <summary>
/// Used to disable secondary throttling (e.g. vsync, audio) for unthrottled modes or when the primary (clock) throttle is taking over (e.g. during fast forward/rewind).
/// </summary>
public static bool DisableSecondaryThrottling;
public static bool DisableSecondaryThrottling { get; set; }
/// <summary>
/// The maximum number of milliseconds the sound output buffer can go below full before causing a noticeable sound interruption.
/// </summary>
public static int SoundMaxBufferDeficitMs;
public static int SoundMaxBufferDeficitMs { get; set; }
// the movie will be spliced in between these if it is present
public static readonly CopyControllerAdapter MovieInputSourceAdapter = new CopyControllerAdapter();
public static readonly CopyControllerAdapter MovieOutputHardpoint = new CopyControllerAdapter();
public static readonly MultitrackRewiringControllerAdapter MultitrackRewiringAdapter = new MultitrackRewiringControllerAdapter();
public static InputManager InputManager { get; } = new InputManager();
// don't take my word for it, since the final word is actually in RewireInputChain, but here is a guide...
// user -> Input -> ActiveController -> UDLR -> StickyXORPlayerInputAdapter -> TurboAdapter(TBD) -> Lua(?TBD?) -> ..
// .. -> MultitrackRewiringControllerAdapter -> MovieInputSourceAdapter -> (MovieSession) -> MovieOutputAdapter -> ControllerOutput(1) -> Game
// (1)->Input Display
// the original source controller, bound to the user, sort of the "input" port for the chain, i think
public static Controller ActiveController;
// rapid fire version on the user controller, has its own key bindings and is OR'ed against ActiveController
public static AutofireController AutoFireController;
// the "output" port for the controller chain.
public static readonly CopyControllerAdapter ControllerOutput = new CopyControllerAdapter();
public static readonly UdlrControllerAdapter UD_LR_ControllerAdapter = new UdlrControllerAdapter();
public static readonly AutoFireStickyXorAdapter AutofireStickyXORAdapter = new AutoFireStickyXorAdapter();
/// <summary>
/// provides an opportunity to mutate the player's input in an autohold style
/// </summary>
public static readonly StickyXorAdapter StickyXORAdapter = new StickyXorAdapter();
/// <summary>
/// Used to AND to another controller, used for <see cref="JoypadApi.Set(System.Collections.Generic.Dictionary{string,bool},System.Nullable{int})">JoypadApi.Set</see>
/// </summary>
public static readonly OverrideAdapter ButtonOverrideAdaptor = new OverrideAdapter();
/// <summary>
/// fire off one-frame logical button clicks here. useful for things like ti-83 virtual pad and reset buttons
/// </summary>
public static readonly ClickyVirtualPadController ClickyVirtualPadController = new ClickyVirtualPadController();
public static Controller ClientControls;
// Input state which has been estine for game controller inputs are coalesce here
// This relies on a client specific implementation!
public static SimpleController ControllerInputCoalescer;
public static Dictionary<string, object> UserBag = new Dictionary<string, object>();
public static Dictionary<string, object> UserBag { get; set; } = new Dictionary<string, object>();
}
}

View File

@ -10,7 +10,7 @@ namespace BizHawk.Client.Common
var ipsHeader = new byte[5];
patch.Read(ipsHeader, 0, 5);
string header = "PATCH";
const string header = "PATCH";
for (int i = 0; i < 5; i++)
{
if (ipsHeader[i] != header[i])
@ -21,7 +21,7 @@ namespace BizHawk.Client.Common
}
// header verified, loop over patch entries
uint eof = ('E' * 0x10000) + ('O' * 0x100) + 'F';
const uint eof = ('E' * 0x10000) + ('O' * 0x100) + 'F';
var ret = new MemoryStream(rom.Length);
ret.Write(rom, 0, rom.Length);

View File

@ -37,12 +37,11 @@ namespace BizHawk.Client.Common
public class OpenAdvancedSerializer
{
public static IOpenAdvanced ParseWithLegacy(string text)
{
if (text.StartsWith("*"))
return Deserialize(text.Substring(1));
return new OpenAdvanced_OpenRom { Path = text };
return text.StartsWith("*")
? Deserialize(text.Substring(1))
: new OpenAdvanced_OpenRom { Path = text };
}
private static IOpenAdvanced Deserialize(string text)
@ -50,28 +49,15 @@ namespace BizHawk.Client.Common
int idx = text.IndexOf('*');
string type = text.Substring(0, idx);
string token = text.Substring(idx + 1);
IOpenAdvanced ioa;
if (type == OpenAdvancedTypes.OpenRom)
var ioa = type switch
{
ioa = new OpenAdvanced_OpenRom();
}
else if (type == OpenAdvancedTypes.Libretro)
{
ioa = new OpenAdvanced_Libretro();
}
else if (type == OpenAdvancedTypes.LibretroNoGame)
{
ioa = new OpenAdvanced_LibretroNoGame();
}
else if (type == OpenAdvancedTypes.MAME)
{
ioa = new OpenAdvanced_MAME();
}
else
{
ioa = null;
}
OpenAdvancedTypes.OpenRom => (IOpenAdvanced)new OpenAdvanced_OpenRom(),
OpenAdvancedTypes.Libretro => new OpenAdvanced_Libretro(),
OpenAdvancedTypes.LibretroNoGame => new OpenAdvanced_LibretroNoGame(),
OpenAdvancedTypes.MAME => new OpenAdvanced_MAME(),
_ => null
};
if (ioa == null)
{
@ -84,7 +70,7 @@ namespace BizHawk.Client.Common
public static string Serialize(IOpenAdvanced ioa)
{
StringWriter sw = new StringWriter();
var sw = new StringWriter();
sw.Write("{0}*", ioa.TypeName);
ioa.Serialize(sw);
return sw.ToString();
@ -93,14 +79,11 @@ namespace BizHawk.Client.Common
public class OpenAdvanced_Libretro : IOpenAdvanced, IOpenAdvancedLibretro
{
public OpenAdvanced_Libretro()
{
}
public struct Token
{
public string Path, CorePath;
}
public Token token;
public string TypeName => "Libretro";
@ -138,37 +121,28 @@ namespace BizHawk.Client.Common
public OpenAdvanced_LibretroNoGame(string corePath)
{
_corePath = corePath;
CorePath = corePath;
}
string _corePath;
public string TypeName => "LibretroNoGame";
public string DisplayName => Path.GetFileName(_corePath); // assume we like the filename of the core
public string DisplayName => Path.GetFileName(CorePath); // assume we like the filename of the core
public string SimplePath => ""; // effectively a signal to not use a game
public void Deserialize(string str)
{
_corePath = str;
CorePath = str;
}
public void Serialize(TextWriter tw)
{
tw.Write(_corePath);
tw.Write(CorePath);
}
public string CorePath
{
get => _corePath;
set => _corePath = value;
}
public string CorePath { get; set; }
}
public class OpenAdvanced_OpenRom : IOpenAdvanced
{
public OpenAdvanced_OpenRom()
{}
public string Path;
public string TypeName => "OpenRom";
@ -188,9 +162,6 @@ namespace BizHawk.Client.Common
public class OpenAdvanced_MAME : IOpenAdvanced
{
public OpenAdvanced_MAME()
{ }
public string Path;
public string TypeName => "MAME";

View File

@ -1,503 +0,0 @@
using System;
using System.Linq;
using System.IO;
using System.Reflection;
using BizHawk.Common;
using BizHawk.Common.StringExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Nintendo.SNES;
using BizHawk.Emulation.Cores.Nintendo.SNES9X;
namespace BizHawk.Client.Common
{
public static class PathManager
{
static PathManager()
{
SetDefaultIniPath(MakeProgramRelativePath("config.ini"));
}
public static string GetExeDirectoryAbsolute()
{
var path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
if (path.EndsWith(Path.DirectorySeparatorChar.ToString()))
{
path = path.Remove(path.Length - 1, 1);
}
return path;
}
/// <summary>
/// Makes a path relative to the %exe% directory
/// </summary>
public static string MakeProgramRelativePath(string path)
{
return MakeAbsolutePath($"%exe%/{path}", null);
}
public static string GetDllDirectory()
{
return Path.Combine(GetExeDirectoryAbsolute(), "dll");
}
/// <summary>
/// The location of the default INI file
/// </summary>
public static string DefaultIniPath { get; private set; }
public static void SetDefaultIniPath(string newDefaultIniPath)
{
DefaultIniPath = newDefaultIniPath;
}
/// <summary>
/// Gets absolute base as derived from EXE
/// </summary>
public static string GetGlobalBasePathAbsolute()
{
var gbase = Global.Config.PathEntries.GlobalBaseFragment;
// if %exe% prefixed then substitute exe path and repeat
if(gbase.StartsWith("%exe%",StringComparison.InvariantCultureIgnoreCase))
gbase = GetExeDirectoryAbsolute() + gbase.Substring(5);
//rooted paths get returned without change
//(this is done after keyword substitution to avoid problems though)
if (Path.IsPathRooted(gbase))
return gbase;
//not-rooted things are relative to exe path
gbase = Path.Combine(GetExeDirectoryAbsolute(), gbase);
return gbase;
}
public static string GetPlatformBase(string system)
{
return Global.Config.PathEntries[system, "Base"].Path;
}
public static string StandardFirmwareName(string name)
{
return Path.Combine(MakeAbsolutePath(Global.Config.PathEntries.FirmwaresPathFragment, null), name);
}
public static string MakeAbsolutePath(string path, string system)
{
//warning: supposedly Path.GetFullPath accesses directories (and needs permissions)
//if this poses a problem, we need to paste code from .net or mono sources and fix them to not pose problems, rather than homebrew stuff
return Path.GetFullPath(MakeAbsolutePathInner(path, system));
}
static string MakeAbsolutePathInner(string path, string system)
{
// Hack
if (system == "Global")
{
system = null;
}
// This function translates relative path and special identifiers in absolute paths
if (path.Length < 1)
{
return GetGlobalBasePathAbsolute();
}
if (path == "%recent%")
{
return Environment.SpecialFolder.Recent.ToString();
}
if (path.StartsWith("%exe%"))
return GetExeDirectoryAbsolute() + path.Substring(5);
if (path.StartsWith("%rom%"))
return Global.Config.LastRomPath + path.Substring(5);
if (path[0] == '.')
{
if (!string.IsNullOrWhiteSpace(system))
{
path = path.Remove(0, 1);
path = path.Insert(0, GetPlatformBase(system));
}
if (path.Length == 1)
{
return GetGlobalBasePathAbsolute();
}
if (path[0] == '.')
{
path = path.Remove(0, 1);
path = path.Insert(0, GetGlobalBasePathAbsolute());
}
return path;
}
if (Path.IsPathRooted(path))
return path;
//handling of initial .. was removed (Path.GetFullPath can handle it)
//handling of file:// or file:\\ was removed (can Path.GetFullPath handle it? not sure)
// all bad paths default to EXE
return GetExeDirectoryAbsolute();
}
public static string RemoveParents(string path, string workingpath)
{
// determines number of parents, then removes directories from working path, return absolute path result
// Ex: "..\..\Bob\", "C:\Projects\Emulators\Bizhawk" will return "C:\Projects\Bob\"
int x = NumParentDirectories(path);
if (x > 0)
{
int y = path.HowMany("..\\");
int z = workingpath.HowMany("\\");
if (y >= z)
{
// Return drive letter only, working path must be absolute?
}
return "";
}
return path;
}
public static int NumParentDirectories(string path)
{
// determine the number of parent directories in path and return result
int x = path.HowMany('\\');
if (x > 0)
{
return path.HowMany("..\\");
}
return 0;
}
public static bool IsRecent(string path)
{
return path == "%recent%";
}
public static string GetLuaPath()
{
return MakeAbsolutePath(Global.Config.PathEntries.LuaPathFragment, null);
}
// Decides if a path is non-empty, not . and not .\
private static bool PathIsSet(string path)
{
if (!string.IsNullOrWhiteSpace(path))
{
return path != "." && path != ".\\";
}
return false;
}
public static string GetRomsPath(string sysId)
{
if (Global.Config.UseRecentForRoms)
{
return Environment.SpecialFolder.Recent.ToString();
}
var path = Global.Config.PathEntries[sysId, "ROM"];
if (path == null || !PathIsSet(path.Path))
{
path = Global.Config.PathEntries["Global", "ROM"];
if (path != null && PathIsSet(path.Path))
{
return MakeAbsolutePath(path.Path, null);
}
}
return MakeAbsolutePath(path.Path, sysId);
}
public static string RemoveInvalidFileSystemChars(string name)
{
var newStr = name;
var chars = Path.GetInvalidFileNameChars();
return chars.Aggregate(newStr, (current, c) => current.Replace(c.ToString(), ""));
}
public static string FilesystemSafeName(GameInfo game)
{
var filesystemSafeName = game.Name
.Replace("|", "+")
.Replace(":", " -") // adelikat - Path.GetFileName scraps everything to the left of a colon unfortunately, so we need this hack here
.Replace("\"", "") // adelikat - Ivan Ironman Stewart's Super Off-Road has quotes in game name
.Replace("/", "+"); // Narry - Mario Bros / Duck hunt has a slash in the name which GetDirectoryName and GetFileName treat as if it were a folder
// zero 06-nov-2015 - regarding the below, i changed my mind. for libretro i want subdirectories here.
var filesystemDir = Path.GetDirectoryName(filesystemSafeName);
filesystemSafeName = Path.GetFileName(filesystemSafeName);
filesystemSafeName = RemoveInvalidFileSystemChars(filesystemSafeName);
// zero 22-jul-2012 - i don't think this is used the same way it used to. game.Name shouldn't be a path, so this stuff is illogical.
// if game.Name is a path, then someone shouldve made it not-a-path already.
// return Path.Combine(Path.GetDirectoryName(filesystemSafeName), Path.GetFileNameWithoutExtension(filesystemSafeName));
// adelikat:
// This hack is to prevent annoying things like Super Mario Bros..bk2
if (filesystemSafeName.EndsWith("."))
{
filesystemSafeName = filesystemSafeName.Remove(filesystemSafeName.Length - 1, 1);
}
return Path.Combine(filesystemDir, filesystemSafeName);
}
public static string SaveRamPath(GameInfo game)
{
var name = FilesystemSafeName(game);
if (Global.MovieSession.Movie.IsActive())
{
name += $".{Path.GetFileNameWithoutExtension(Global.MovieSession.Movie.Filename)}";
}
var pathEntry = Global.Config.PathEntries[game.System, "Save RAM"] ??
Global.Config.PathEntries[game.System, "Base"];
return $"{Path.Combine(MakeAbsolutePath(pathEntry.Path, game.System), name)}.SaveRAM";
}
public static string AutoSaveRamPath(GameInfo game)
{
var path = SaveRamPath(game);
return path.Insert(path.Length - 8, ".AutoSaveRAM");
}
public static string RetroSaveRAMDirectory(GameInfo game)
{
// hijinx here to get the core name out of the game name
var name = FilesystemSafeName(game);
name = Path.GetDirectoryName(name);
if (name == "")
{
name = FilesystemSafeName(game);
}
if (Global.MovieSession.Movie.IsActive())
{
name = Path.Combine(name, $"movie-{Path.GetFileNameWithoutExtension(Global.MovieSession.Movie.Filename)}");
}
var pathEntry = Global.Config.PathEntries[game.System, "Save RAM"] ??
Global.Config.PathEntries[game.System, "Base"];
return Path.Combine(MakeAbsolutePath(pathEntry.Path, game.System), name);
}
public static string RetroSystemPath(GameInfo game)
{
// hijinx here to get the core name out of the game name
var name = FilesystemSafeName(game);
name = Path.GetDirectoryName(name);
if (name == "")
{
name = FilesystemSafeName(game);
}
var pathEntry = Global.Config.PathEntries[game.System, "System"] ??
Global.Config.PathEntries[game.System, "Base"];
return Path.Combine(MakeAbsolutePath(pathEntry.Path, game.System), name);
}
public static string GetGameBasePath(GameInfo game)
{
var name = FilesystemSafeName(game);
var pathEntry = Global.Config.PathEntries[game.System, "Base"];
return MakeAbsolutePath(pathEntry.Path, game.System);
}
public static string GetSaveStatePath(GameInfo game)
{
var pathEntry = Global.Config.PathEntries[game.System, "Savestates"] ??
Global.Config.PathEntries[game.System, "Base"];
return MakeAbsolutePath(pathEntry.Path, game.System);
}
public static string SaveStatePrefix(GameInfo game)
{
var name = FilesystemSafeName(game);
// Neshawk and Quicknes have incompatible savestates, store the name to keep them separate
if (Global.Emulator.SystemId == "NES")
{
name += $".{Global.Emulator.Attributes().CoreName}";
}
// Gambatte and GBHawk have incompatible savestates, store the name to keep them separate
if (Global.Emulator.SystemId == "GB")
{
name += $".{Global.Emulator.Attributes().CoreName}";
}
if (Global.Emulator is Snes9x) // Keep snes9x savestate away from libsnes, we want to not be too tedious so bsnes names will just have the profile name not the core name
{
name += $".{Global.Emulator.Attributes().CoreName}";
}
// Bsnes profiles have incompatible savestates so save the profile name
if (Global.Emulator is LibsnesCore)
{
name += $".{((LibsnesCore)Global.Emulator).CurrentProfile}";
}
if (Global.Emulator.SystemId == "GBA")
{
name += $".{Global.Emulator.Attributes().CoreName}";
}
if (Global.MovieSession.Movie.IsActive())
{
name += $".{Path.GetFileNameWithoutExtension(Global.MovieSession.Movie.Filename)}";
}
var pathEntry = Global.Config.PathEntries[game.System, "Savestates"] ??
Global.Config.PathEntries[game.System, "Base"];
return Path.Combine(MakeAbsolutePath(pathEntry.Path, game.System), name);
}
public static string GetCheatsPath(GameInfo game)
{
var pathEntry = Global.Config.PathEntries[game.System, "Cheats"] ??
Global.Config.PathEntries[game.System, "Base"];
return MakeAbsolutePath(pathEntry.Path, game.System);
}
public static string GetPathType(string system, string type)
{
var path = GetPathEntryWithFallback(type, system).Path;
return MakeAbsolutePath(path, system);
}
public static string ScreenshotPrefix(GameInfo game)
{
var name = FilesystemSafeName(game);
var pathEntry = Global.Config.PathEntries[game.System, "Screenshots"] ??
Global.Config.PathEntries[game.System, "Base"];
return Path.Combine(MakeAbsolutePath(pathEntry.Path, game.System), name);
}
/// <summary>
/// Takes an absolute path and attempts to convert it to a relative, based on the system,
/// or global base if no system is supplied, if it is not a subfolder of the base, it will return the path unaltered
/// </summary>
public static string TryMakeRelative(string absolutePath, string system = null)
{
var parentPath = string.IsNullOrWhiteSpace(system)
? GetGlobalBasePathAbsolute()
: MakeAbsolutePath(GetPlatformBase(system), system);
#if true
if (!IsSubfolder(parentPath, absolutePath)) return absolutePath;
return OSTailoredCode.IsUnixHost
? "./" + OSTailoredCode.SimpleSubshell("realpath", $"--relative-to=\"{parentPath}\" \"{absolutePath}\"", $"invalid path {absolutePath} or missing realpath binary")
: absolutePath.Replace(parentPath, ".");
#else // written for Unix port but may be useful for .NET Core
if (!IsSubfolder(parentPath, absolutePath))
{
return OSTailoredCode.IsUnixHost && parentPath.TrimEnd('.') == $"{absolutePath}/" ? "." : absolutePath;
}
return OSTailoredCode.IsUnixHost
? absolutePath.Replace(parentPath.TrimEnd('.'), "./")
: absolutePath.Replace(parentPath, ".");
#endif
}
public static string MakeRelativeTo(string absolutePath, string basePath)
{
if (IsSubfolder(basePath, absolutePath))
{
return absolutePath.Replace(basePath, ".");
}
return absolutePath;
}
/// <remarks>Algorithm for Windows taken from https://stackoverflow.com/a/7710620/7467292</remarks>
public static bool IsSubfolder(string parentPath, string childPath)
{
if (OSTailoredCode.IsUnixHost)
{
#if true
return OSTailoredCode.SimpleSubshell("realpath", $"-L \"{childPath}\"", $"invalid path {childPath} or missing realpath binary")
.StartsWith(OSTailoredCode.SimpleSubshell("realpath", $"-L \"{parentPath}\"", $"invalid path {parentPath} or missing realpath binary"));
#else // written for Unix port but may be useful for Windows when moving to .NET Core
var parentUriPath = new Uri(parentPath.TrimEnd('.')).AbsolutePath.TrimEnd('/');
try
{
for (var childUri = new DirectoryInfo(childPath).Parent; childUri != null; childUri = childUri?.Parent)
{
if (new Uri(childUri.FullName).AbsolutePath.TrimEnd('/') == parentUriPath) return true;
}
}
catch
{
// ignored
}
return false;
#endif
}
var parentUri = new Uri(parentPath);
for (var childUri = new DirectoryInfo(childPath).Parent; childUri != null; childUri = childUri?.Parent)
{
if (new Uri(childUri.FullName) == parentUri) return true;
}
return false;
}
/// <summary>
/// Don't only valid system ids to system ID, pathType is ROM, Screenshot, etc
/// Returns the desired path, if does not exist, returns platform base, else it returns base
/// </summary>
private static PathEntry GetPathEntryWithFallback(string pathType, string systemId)
{
var entry = Global.Config.PathEntries[systemId, pathType];
if (entry == null)
{
entry = Global.Config.PathEntries[systemId, "Base"];
}
if (entry == null)
{
entry = Global.Config.PathEntries["Global", "Base"];
}
return entry;
}
/// <summary>
/// Puts the currently configured temp path into the environment for use as actual temp directory
/// </summary>
public static void RefreshTempPath()
{
if (Global.Config.PathEntries.TempFilesFragment != "")
{
//TODO - BUG - needs to route through PathManager.MakeAbsolutePath or something similar, but how?
string target = Global.Config.PathEntries.TempFilesFragment;
BizHawk.Common.TempFileManager.HelperSetTempPath(target);
}
}
}
}

View File

@ -40,28 +40,10 @@ namespace BizHawk.Client.Common
[JsonIgnore]
public string MostRecent => recentlist.Any() ? recentlist[0] : "";
public string this[int index]
{
get
{
if (recentlist.Any())
{
return recentlist[index];
}
public string this[int index] => recentlist.Any() ? recentlist[index] : "";
return "";
}
}
public IEnumerator<string> GetEnumerator()
{
return recentlist.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<string> GetEnumerator() => recentlist.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Clear()
{

View File

@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using BizHawk.Common;
using BizHawk.Common.PathExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores;
using BizHawk.Emulation.Cores.Libretro;
@ -19,7 +20,6 @@ using BizHawk.Emulation.Cores.Nintendo.GBHawkLink4x;
using BizHawk.Emulation.Cores.Nintendo.SNES;
using BizHawk.Emulation.Cores.PCEngine;
using BizHawk.Emulation.Cores.Sega.GGHawkLink;
using BizHawk.Emulation.Cores.Sony.PSP;
using BizHawk.Emulation.Cores.Sony.PSX;
using BizHawk.Emulation.Cores.Computers.SinclairSpectrum;
using BizHawk.Emulation.Cores.Arcades.MAME;
@ -243,7 +243,7 @@ namespace BizHawk.Client.Common
return discs;
}
public bool LoadRom(string path, CoreComm nextComm, bool forceAccurateCore = false,
public bool LoadRom(string path, CoreComm nextComm, string launchLibretroCore, bool forceAccurateCore = false,
int recursiveCount = 0) // forceAccurateCore is currently just for Quicknes vs Neshawk but could be used for other situations
{
if (recursiveCount > 1) // hack to stop recursive calls from endlessly rerunning if we can't load it
@ -291,24 +291,20 @@ namespace BizHawk.Client.Common
if (OpenAdvanced is OpenAdvanced_Libretro)
{
string codePathPart = Path.GetFileNameWithoutExtension(nextComm.LaunchLibretroCore);
var retro = new LibretroCore(nextComm, nextComm.LaunchLibretroCore);
nextEmulator = retro;
// kind of dirty.. we need to stash this, and then we can unstash it in a moment, in case the core doesn't fail
var oldGame = Global.Game;
// must be done before LoadNoGame (which triggers retro_init and the paths to be consumed by the core)
// game name == name of core
string codePathPart = Path.GetFileNameWithoutExtension(launchLibretroCore);
Global.Game = game = new GameInfo { Name = codePathPart, System = "Libretro" };
var retro = new LibretroCore(nextComm, game, launchLibretroCore);
nextEmulator = retro;
if (retro.Description.SupportsNoGame && string.IsNullOrEmpty(path))
{
// must be done before LoadNoGame (which triggers retro_init and the paths to be consumed by the core)
// game name == name of core
var gameName = codePathPart;
Global.Game = game = new GameInfo { Name = gameName, System = "Libretro" };
// if we are allowed to run NoGame and we don't have a game, boot up the core that way
bool ret = retro.LoadNoGame();
Global.Game = oldGame;
if (!ret)
@ -322,11 +318,6 @@ namespace BizHawk.Client.Common
{
bool ret;
// must be done before LoadNoGame (which triggers retro_init and the paths to be consumed by the core)
// game name == name of core + extensionless_game_filename
var gameName = Path.Combine(codePathPart, Path.GetFileNameWithoutExtension(file.Name));
Global.Game = game = new GameInfo { Name = gameName, System = "Libretro" };
// if the core requires an archive file, then try passing the filename of the archive
// (but do we ever need to actually load the contents of the archive file into ram?)
if (retro.Description.NeedsArchives)
@ -470,8 +461,7 @@ namespace BizHawk.Client.Common
sw.WriteLine("-------------------------");
}
nextEmulator = new Octoshock(nextComm, discs, discNames, null, GetCoreSettings<Octoshock>(), GetCoreSyncSettings<Octoshock>());
nextEmulator.CoreComm.RomStatusDetails = sw.ToString();
nextEmulator = new Octoshock(nextComm, discs, discNames, null, GetCoreSettings<Octoshock>(), GetCoreSyncSettings<Octoshock>(), sw.ToString());
game = new GameInfo
{
Name = Path.GetFileNameWithoutExtension(file.Name),
@ -574,14 +564,11 @@ namespace BizHawk.Client.Common
nextEmulator = new Saturnus(nextComm, new[] { disc }, Deterministic,
(Saturnus.Settings)GetCoreSettings<Saturnus>(), (Saturnus.SyncSettings)GetCoreSyncSettings<Saturnus>());
break;
case "PSP":
nextEmulator = new PSP(nextComm, file.Name);
break;
case "PSX":
nextEmulator = new Octoshock(nextComm, new List<Disc>(new[] { disc }), new List<string>(new[] { Path.GetFileNameWithoutExtension(path) }), null, GetCoreSettings<Octoshock>(), GetCoreSyncSettings<Octoshock>());
string romDetails;
if (game.IsRomStatusBad() || game.Status == RomStatus.NotInDatabase)
{
nextEmulator.CoreComm.RomStatusDetails = "Disc could not be identified as known-good. Look for a better rip.";
romDetails = "Disc could not be identified as known-good. Look for a better rip.";
}
else
{
@ -591,9 +578,10 @@ namespace BizHawk.Client.Common
sw.WriteLine("According to redump.org, the ideal hash for entire disc is: CRC32:{0:X8}", game.GetStringValue("dh"));
sw.WriteLine("The file you loaded hasn't been hashed entirely (it would take too long)");
sw.WriteLine("Compare it with the full hash calculated by the PSX menu's Hash Discs tool");
nextEmulator.CoreComm.RomStatusDetails = sw.ToString();
romDetails = sw.ToString();
}
nextEmulator = new Octoshock(nextComm, new List<Disc>(new[] { disc }), new List<string>(new[] { Path.GetFileNameWithoutExtension(path) }), null, GetCoreSettings<Octoshock>(), GetCoreSyncSettings<Octoshock>(), romDetails);
break;
case "PCFX":
nextEmulator = new Tst(nextComm, new[] { disc },
@ -695,11 +683,9 @@ namespace BizHawk.Client.Common
break;
case "AppleII":
var assets = xmlGame.Assets.Select(a => Database.GetGameInfo(a.Value, a.Key));
var roms = xmlGame.Assets.Select(a => a.Value);
nextEmulator = new AppleII(
nextComm,
assets,
roms,
(AppleII.Settings)GetCoreSettings<AppleII>());
break;
@ -804,8 +790,7 @@ namespace BizHawk.Client.Common
}
// todo: copy pasta from PSX .cue section
nextEmulator = new Octoshock(nextComm, discs, discNames, null, GetCoreSettings<Octoshock>(), GetCoreSyncSettings<Octoshock>());
nextEmulator.CoreComm.RomStatusDetails = sw.ToString();
nextEmulator = new Octoshock(nextComm, discs, discNames, null, GetCoreSettings<Octoshock>(), GetCoreSyncSettings<Octoshock>(), sw.ToString());
game = new GameInfo
{
Name = Path.GetFileNameWithoutExtension(file.Name),
@ -867,14 +852,13 @@ namespace BizHawk.Client.Common
{
// need to get rid of this hack at some point
rom = new RomGame(file);
((CoreFileProvider)nextComm.CoreFileProvider).SubfileDirectory = Path.GetDirectoryName(path.Replace("|", "")); // Dirty hack to get around archive filenames (since we are just getting the directory path, it is safe to mangle the filename
byte[] romData = null;
var basePath = Path.GetDirectoryName(path.Replace("|", "")); // Dirty hack to get around archive filenames (since we are just getting the directory path, it is safe to mangle the filename
byte[] xmlData = rom.FileData;
game = rom.GameInfo;
game.System = "SNES";
var snes = new LibsnesCore(game, romData, xmlData, nextComm, GetCoreSettings<LibsnesCore>(), GetCoreSyncSettings<LibsnesCore>());
var snes = new LibsnesCore(game, null, xmlData, basePath, nextComm, GetCoreSettings<LibsnesCore>(), GetCoreSyncSettings<LibsnesCore>());
nextEmulator = snes;
}
catch
@ -897,7 +881,6 @@ namespace BizHawk.Client.Common
PSF psf = new PSF();
psf.Load(path, cbDeflater);
nextEmulator = new Octoshock(nextComm, psf, GetCoreSettings<Octoshock>(), GetCoreSyncSettings<Octoshock>());
nextEmulator.CoreComm.RomStatusDetails = "It's a PSF, what do you want. Oh, tags maybe?";
// total garbage, this
rom = new RomGame(file);
@ -963,13 +946,15 @@ namespace BizHawk.Client.Common
// The user picked nothing in the Core picker
break;
case "83P":
var ti83Bios = ((CoreFileProvider)nextComm.CoreFileProvider).GetFirmware("TI83", "Rom", true);
var ti83BiosPath = ((CoreFileProvider)nextComm.CoreFileProvider).GetFirmwarePath("TI83", "Rom", true);
var ti83Bios = nextComm.CoreFileProvider.GetFirmware("TI83", "Rom", true);
// TODO: make the ti-83 a proper firmware file
var ti83BiosPath = Global.FirmwareManager.Request(Global.Config.PathEntries, Global.Config.FirmwareUserSpecifications, "TI83", "Rom");
using (var ti83AsHawkFile = new HawkFile(ti83BiosPath))
{
var ti83BiosAsRom = new RomGame(ti83AsHawkFile);
var ti83 = new TI83(nextComm, ti83BiosAsRom.GameInfo, ti83Bios, GetCoreSettings<TI83>());
ti83.LinkPort.SendFileToCalc(File.OpenRead(path), false);
var ti83 = new TI83(ti83BiosAsRom.GameInfo, ti83Bios, GetCoreSettings<TI83>());
ti83.LinkPort.SendFileToCalc(File.OpenRead(path.Split('|').First()), false);
nextEmulator = ti83;
}
@ -995,10 +980,10 @@ namespace BizHawk.Client.Common
else
{
// need to get rid of this hack at some point
((CoreFileProvider)nextComm.CoreFileProvider).SubfileDirectory = Path.GetDirectoryName(path.Replace("|", "")); // Dirty hack to get around archive filenames (since we are just getting the directory path, it is safe to mangle the filename
var basePath = Path.GetDirectoryName(path.Replace("|", "")); // Dirty hack to get around archive filenames (since we are just getting the directory path, it is safe to mangle the filename
var romData = isXml ? null : rom.FileData;
var xmlData = isXml ? rom.FileData : null;
var snes = new LibsnesCore(game, romData, xmlData, nextComm, GetCoreSettings<LibsnesCore>(), GetCoreSyncSettings<LibsnesCore>());
var snes = new LibsnesCore(game, romData, xmlData, basePath, nextComm, GetCoreSettings<LibsnesCore>(), GetCoreSyncSettings<LibsnesCore>());
nextEmulator = snes;
}
@ -1058,7 +1043,7 @@ namespace BizHawk.Client.Common
{
game.System = "SNES";
game.AddOption("SGB");
var snes = new LibsnesCore(game, rom.FileData, null, nextComm, GetCoreSettings<LibsnesCore>(), GetCoreSyncSettings<LibsnesCore>());
var snes = new LibsnesCore(game, rom.FileData, null, null, nextComm, GetCoreSettings<LibsnesCore>(), GetCoreSyncSettings<LibsnesCore>());
nextEmulator = snes;
}
else
@ -1087,7 +1072,7 @@ namespace BizHawk.Client.Common
{
game.System = "SNES";
game.AddOption("SGB");
var snes = new LibsnesCore(game, rom.FileData, null, nextComm, GetCoreSettings<LibsnesCore>(), GetCoreSyncSettings<LibsnesCore>());
var snes = new LibsnesCore(game, rom.FileData, null, null, nextComm, GetCoreSettings<LibsnesCore>(), GetCoreSyncSettings<LibsnesCore>());
nextEmulator = snes;
}
else
@ -1097,7 +1082,7 @@ namespace BizHawk.Client.Common
}
break;
case "A78":
var gameDbPath = Path.Combine(PathManager.GetExeDirectoryAbsolute(), "gamedb", "gamedb_a7800.csv");
var gameDbPath = Path.Combine(PathUtils.ExeDirectoryPath, "gamedb", "gamedb_a7800.csv");
nextEmulator = new A7800Hawk(nextComm, game, rom.RomData, gameDbPath, GetCoreSettings<A7800Hawk>(), GetCoreSyncSettings<A7800Hawk>());
break;
case "C64":
@ -1126,11 +1111,10 @@ namespace BizHawk.Client.Common
: CoreInventory.Instance["GBA", "VBA-Next"];
break;
case "PSX":
nextEmulator = new Octoshock(nextComm, null, null, rom.FileData, GetCoreSettings<Octoshock>(), GetCoreSyncSettings<Octoshock>());
nextEmulator.CoreComm.RomStatusDetails = "PSX etc.";
nextEmulator = new Octoshock(nextComm, null, null, rom.FileData, GetCoreSettings<Octoshock>(), GetCoreSyncSettings<Octoshock>(), "PSX etc.");
break;
case "Arcade":
nextEmulator = new MAME(nextComm, file.Directory, file.CanonicalName, GetCoreSyncSettings<MAME>(), out var gameName);
nextEmulator = new MAME(file.Directory, file.CanonicalName, GetCoreSyncSettings<MAME>(), out var gameName);
rom.GameInfo.Name = gameName;
break;
case "GEN":
@ -1195,7 +1179,7 @@ namespace BizHawk.Client.Common
DoMessageCallback("Unable to use quicknes, using NESHawk instead");
}
return LoadRom(path, nextComm, true, recursiveCount + 1);
return LoadRom(path, nextComm, launchLibretroCore, true, recursiveCount + 1);
}
if (ex is MissingFirmwareException)
@ -1208,7 +1192,7 @@ namespace BizHawk.Client.Common
// To avoid catch-22, disable SGB mode
Global.Config.GbAsSgb = false;
DoMessageCallback("Failed to load a GB rom in SGB mode. Disabling SGB Mode.");
return LoadRom(path, nextComm, false, recursiveCount + 1);
return LoadRom(path, nextComm, launchLibretroCore, false, recursiveCount + 1);
}
// handle exceptions thrown by the new detected systems that BizHawk does not have cores for

View File

@ -10,12 +10,7 @@ namespace BizHawk.Client.Common
private readonly bool[] _slots = new bool[10];
private readonly bool[] _redo = new bool[10];
public SaveSlotManager()
{
Update();
}
public void Update()
public void Update(string saveStatePrefix)
{
if (Global.Game == null || Global.Emulator == null)
{
@ -35,7 +30,7 @@ namespace BizHawk.Client.Common
}
else
{
var file = new FileInfo($"{PathManager.SaveStatePrefix(Global.Game)}.QuickSave{i}.State");
var file = new FileInfo($"{saveStatePrefix}.QuickSave{i}.State");
if (file.Directory != null && file.Directory.Exists == false)
{
file.Directory.Create();
@ -46,7 +41,7 @@ namespace BizHawk.Client.Common
}
}
public bool HasSlot(int slot)
public bool HasSlot(int slot, string savestatePrefix)
{
if (!Global.Emulator.HasSavestates())
{
@ -58,7 +53,7 @@ namespace BizHawk.Client.Common
return false;
}
Update();
Update(savestatePrefix);
return _slots[slot];
}

View File

@ -102,6 +102,11 @@ namespace BizHawk.Client.Common
/// </summary>
public static SystemInfo GBC { get; } = new SystemInfo("Gameboy Color", CoreSystem.GameBoy, 1, StandardButtons);
/// <summary>
/// Gets the <see cref="SystemInfo"/> instance for Nintendo DS
/// </summary>
public static SystemInfo NDS { get; } = new SystemInfo("NDS", CoreSystem.NintendoDS, 1, StandardButtons | JoypadButton.L | JoypadButton.R | JoypadButton.X | JoypadButton.Y);
/// <summary>
/// Gets the <see cref="SystemInfo"/> instance for Genesis
/// </summary>

View File

@ -36,7 +36,7 @@ namespace BizHawk.Client.Common.cheats
code = code.Remove(0, 2);
var addrStr = code.Remove(0, 2);
addrStr = addrStr + code.Remove(2, 2);
addrStr += code.Remove(2, 2);
result.Value = int.Parse(valueStr, NumberStyles.HexNumber);
result.Address = int.Parse(addrStr, NumberStyles.HexNumber);

View File

@ -19,35 +19,35 @@ namespace BizHawk.Client.Common.cheats
}
var parseString = code.Remove(0, 2);
switch (code.Length)
return code.Length switch
{
case 9:
// Sample Code of 1-Byte:
// FFF761:64
// Becomes:
// Address: F761
// Value: 64
return new DecodeResult
{
Address = int.Parse(parseString.Remove(4, 3), NumberStyles.HexNumber),
Value = int.Parse(parseString.Remove(0, 5), NumberStyles.HexNumber),
Size = WatchSize.Byte
};
case 11:
// Sample Code of 2-Byte:
// FFF761:6411
// Becomes:
// Address: F761
// Value: 6411
return new DecodeResult
{
Address = int.Parse(parseString.Remove(4, 5), NumberStyles.HexNumber),
Value = int.Parse(parseString.Remove(0, 5), NumberStyles.HexNumber),
Size = WatchSize.Word
};
default:
return new InvalidCheatCode("Action Replay/Pro Action Replay Codes need to be either 9 or 11 characters.");
}
9 =>
// Sample Code of 1-Byte:
// FFF761:64
// Becomes:
// Address: F761
// Value: 64
new DecodeResult
{
Address = int.Parse(parseString.Remove(4, 3), NumberStyles.HexNumber)
, Value = int.Parse(parseString.Remove(0, 5), NumberStyles.HexNumber)
, Size = WatchSize.Byte
},
11 =>
// Sample Code of 2-Byte:
// FFF761:6411
// Becomes:
// Address: F761
// Value: 6411
new DecodeResult
{
Address = int.Parse(parseString.Remove(4, 5), NumberStyles.HexNumber)
, Value = int.Parse(parseString.Remove(0, 5), NumberStyles.HexNumber)
, Size = WatchSize.Word
},
_ => new InvalidCheatCode(
"Action Replay/Pro Action Replay Codes need to be either 9 or 11 characters.")
};
}
}
}

View File

@ -1,13 +1,22 @@
using System;
using System.Collections.Generic;
using System.IO;
using BizHawk.Common;
using BizHawk.Common.PathExtensions;
namespace BizHawk.Client.Common
{
public class Config
{
public static string ControlDefaultPath => PathManager.MakeProgramRelativePath("defctrl.json");
public static string ControlDefaultPath => Path.Combine(PathUtils.ExeDirectoryPath, "defctrl.json");
public static string DefaultIniPath { get; private set; } = Path.Combine(PathUtils.ExeDirectoryPath, "config.ini");
// Shenanigans
public static void SetDefaultIniPath(string newDefaultIniPath)
{
DefaultIniPath = newDefaultIniPath;
}
public Config()
{
@ -24,7 +33,7 @@ namespace BizHawk.Client.Common
{
PathEntries.ResolveWithDefaults();
HotkeyBindings.ResolveWithDefaults();
PathManager.RefreshTempPath();
PathEntries.RefreshTempPath();
}
// Core preference for generic file extension, key: file extension, value: a systemID or empty if no preference
@ -37,9 +46,6 @@ namespace BizHawk.Client.Common
[".cue"] = ""
};
// Path Settings ************************************/
public bool UseRecentForRoms { get; set; }
public string LastRomPath { get; set; } = ".";
public PathEntryCollection PathEntries { get; set; } = new PathEntryCollection();
// BIOS Paths

View File

@ -50,12 +50,7 @@ namespace BizHawk.Client.Common
throw new InvalidOperationException("Config Error", ex);
}
if (config == null)
{
return new T();
}
return config;
return config ?? new T();
}
public static void Save(string filepath, object config)

View File

@ -1,7 +1,4 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq;
namespace BizHawk.Client.Common
{
@ -13,400 +10,9 @@ namespace BizHawk.Client.Common
public string System { get; set; }
public int Ordinal { get; set; }
public bool HasSystem(string systemID)
internal bool IsSystem(string systemID)
{
return systemID == System || System.Split('_').Contains(systemID);
}
}
[Newtonsoft.Json.JsonObject]
public class PathEntryCollection : IEnumerable<PathEntry>
{
public List<PathEntry> Paths { get; }
public PathEntryCollection()
{
Paths = new List<PathEntry>();
Paths.AddRange(DefaultValues);
}
[Newtonsoft.Json.JsonConstructor]
public PathEntryCollection(List<PathEntry> paths)
{
Paths = paths;
}
public void Add(PathEntry p)
{
Paths.Add(p);
}
public IEnumerator<PathEntry> GetEnumerator() => Paths.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public PathEntry this[string system, string type] =>
Paths.FirstOrDefault(p => p.HasSystem(system) && p.Type == type)
?? TryGetDebugPath(system, type);
private PathEntry TryGetDebugPath(string system, string type)
{
if (Paths.Any(p => p.HasSystem(system)))
{
// we have the system, but not the type. don't attempt to add an unknown type
return null;
}
// we don't have anything for the system in question. add a set of stock paths
var systemPath = $"{PathManager.RemoveInvalidFileSystemChars(system)}_INTERIM";
var systemDisp = $"{system} (INTERIM)";
Paths.AddRange(new[]
{
new PathEntry { System = system, SystemDisplayName = systemDisp, Type = "Base", Path = Path.Combine(".", systemPath), Ordinal = 0 },
new PathEntry { System = system, SystemDisplayName = systemDisp, Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = system, SystemDisplayName = systemDisp, Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = system, SystemDisplayName = systemDisp, Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = system, SystemDisplayName = systemDisp, Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = system, SystemDisplayName = systemDisp, Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 }
});
return this[system, type];
}
public void ResolveWithDefaults()
{
// Add missing entries
foreach (PathEntry defaultPath in DefaultValues)
{
var path = Paths.FirstOrDefault(p => p.System == defaultPath.System && p.Type == defaultPath.Type);
if (path == null)
{
Paths.Add(defaultPath);
}
}
var entriesToRemove = new List<PathEntry>();
// Remove entries that no longer exist in defaults
foreach (PathEntry pathEntry in Paths)
{
var path = DefaultValues.FirstOrDefault(p => p.System == pathEntry.System && p.Type == pathEntry.Type);
if (path == null)
{
entriesToRemove.Add(pathEntry);
}
}
foreach (PathEntry entry in entriesToRemove)
{
Paths.Remove(entry);
}
// Add missing display names
var missingDisplayPaths = Paths.Where(p => p.SystemDisplayName == null);
foreach (PathEntry path in missingDisplayPaths)
{
path.SystemDisplayName = DefaultValues.First(p => p.System == path.System).SystemDisplayName;
}
}
private static string ResolveToolsPath(string subPath)
{
if (Path.IsPathRooted(subPath) || subPath.StartsWith("%"))
{
return subPath;
}
var toolsPath = Global.Config.PathEntries["Global", "Tools"].Path;
// Hack for backwards compatibility, prior to 1.11.5, .wch files were in .\Tools, we don't want that to turn into .Tools\Tools
if (subPath == "Tools")
{
return toolsPath;
}
return Path.Combine(toolsPath, subPath);
}
// Some frequently requested paths, made into a property for convenience
public string ToolsPathFragment => Global.Config.PathEntries["Global", "Tools"].Path;
public string WatchPathFragment => ResolveToolsPath(Global.Config.PathEntries["Global", "Watch (.wch)"].Path);
public string MultiDiskBundlesFragment => ResolveToolsPath(Global.Config.PathEntries["Global", "Multi-Disk Bundles"].Path);
public string LogPathFragment => ResolveToolsPath(Global.Config.PathEntries["Global", "Debug Logs"].Path);
public string MoviesPathFragment => Global.Config.PathEntries["Global", "Movies"].Path;
public string MoviesBackupsPathFragment => Global.Config.PathEntries["Global", "Movie backups"].Path;
public string LuaPathFragment => Global.Config.PathEntries["Global", "Lua"].Path;
public string FirmwaresPathFragment => Global.Config.PathEntries["Global", "Firmware"].Path;
public string AvPathFragment => Global.Config.PathEntries["Global", "A/V Dumps"].Path;
public string GlobalRomFragment => Global.Config.PathEntries["Global", "ROM"].Path;
public string TempFilesFragment => Global.Config.PathEntries["Global", "Temp Files"].Path;
// this one is special
public string GlobalBaseFragment => Global.Config.PathEntries["Global", "Base"].Path;
public static List<PathEntry> DefaultValues => new List<PathEntry>
{
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Base", Path = ".", Ordinal = 1 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "ROM", Path = ".", Ordinal = 2 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Firmware", Path = Path.Combine(".", "Firmware"), Ordinal = 3 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Movies", Path = Path.Combine(".", "Movies"), Ordinal = 4 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Movie backups", Path = Path.Combine(".", "Movies", "backup"), Ordinal = 5 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "A/V Dumps", Path = ".", Ordinal = 6 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Tools", Path = Path.Combine(".", "Tools"), Ordinal = 7 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Lua", Path = Path.Combine(".", "Lua"), Ordinal = 8 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Watch (.wch)", Path = Path.Combine(".", "."), Ordinal = 9 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Debug Logs", Path = Path.Combine(".", ""), Ordinal = 10 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Macros", Path = Path.Combine(".", "Movies", "Macros"), Ordinal = 11 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "TAStudio states", Path = Path.Combine(".", "Movies", "TAStudio states"), Ordinal = 12 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Multi-Disk Bundles", Path = Path.Combine(".", ""), Ordinal = 13 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "External Tools", Path = Path.Combine(".", "ExternalTools"), Ordinal = 14 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Temp Files", Path = "", Ordinal = 15 },
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "Base", Path = Path.Combine(".", "Intellivision"), Ordinal = 0 },
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "Palettes", Path = Path.Combine(".", "Palettes"), Ordinal = 6 },
new PathEntry { System = "NES", SystemDisplayName = "NES", Type = "Base", Path = Path.Combine(".", "NES"), Ordinal = 0 },
new PathEntry { System = "NES", SystemDisplayName = "NES", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "NES", SystemDisplayName = "NES", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "NES", SystemDisplayName = "NES", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "NES", SystemDisplayName = "NES", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "NES", SystemDisplayName = "NES", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "NES", SystemDisplayName = "NES", Type = "Palettes", Path = Path.Combine(".", "Palettes"), Ordinal = 6 },
new PathEntry { System = "SNES_SGB", SystemDisplayName = "SNES", Type = "Base", Path = Path.Combine(".", "SNES"), Ordinal = 0 },
new PathEntry { System = "SNES_SGB", SystemDisplayName = "SNES", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "SNES_SGB", SystemDisplayName = "SNES", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "SNES_SGB", SystemDisplayName = "SNES", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "SNES_SGB", SystemDisplayName = "SNES", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "SNES_SGB", SystemDisplayName = "SNES", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "GBA", SystemDisplayName = "GBA", Type = "Base", Path = Path.Combine(".", "GBA"), Ordinal = 0 },
new PathEntry { System = "GBA", SystemDisplayName = "GBA", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "GBA", SystemDisplayName = "GBA", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "GBA", SystemDisplayName = "GBA", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "GBA", SystemDisplayName = "GBA", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "GBA", SystemDisplayName = "GBA", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "SMS", SystemDisplayName = "SMS", Type = "Base", Path = Path.Combine(".", "SMS"), Ordinal = 0 },
new PathEntry { System = "SMS", SystemDisplayName = "SMS", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "SMS", SystemDisplayName = "SMS", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "SMS", SystemDisplayName = "SMS", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "SMS", SystemDisplayName = "SMS", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "SMS", SystemDisplayName = "SMS", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "GG", SystemDisplayName = "GG", Type = "Base", Path = Path.Combine(".", "Game Gear"), Ordinal = 0 },
new PathEntry { System = "GG", SystemDisplayName = "GG", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "GG", SystemDisplayName = "GG", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "GG", SystemDisplayName = "GG", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "GG", SystemDisplayName = "GG", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "GG", SystemDisplayName = "GG", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "SG", SystemDisplayName = "SG", Type = "Base", Path = Path.Combine(".", "SG-1000"), Ordinal = 0 },
new PathEntry { System = "SG", SystemDisplayName = "SG", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "SG", SystemDisplayName = "SG", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "SG", SystemDisplayName = "SG", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "SG", SystemDisplayName = "SG", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "SG", SystemDisplayName = "SG", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "GEN", SystemDisplayName = "Genesis", Type = "Base", Path = Path.Combine(".", "Genesis"), Ordinal = 0 },
new PathEntry { System = "GEN", SystemDisplayName = "Genesis", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "GEN", SystemDisplayName = "Genesis", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "GEN", SystemDisplayName = "Genesis", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "GEN", SystemDisplayName = "Genesis", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "GEN", SystemDisplayName = "Genesis", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "PCE_PCECD_SGX", SystemDisplayName = "PC Engine", Type = "Base", Path = Path.Combine(".", "PC Engine"), Ordinal = 0 },
new PathEntry { System = "PCE_PCECD_SGX", SystemDisplayName = "PC Engine", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "PCE_PCECD_SGX", SystemDisplayName = "PC Engine", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "PCE_PCECD_SGX", SystemDisplayName = "PC Engine", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "PCE_PCECD_SGX", SystemDisplayName = "PC Engine", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "PCE_PCECD_SGX", SystemDisplayName = "PC Engine", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "GB_GBC", SystemDisplayName = "Gameboy", Type = "Base", Path = Path.Combine(".", "Gameboy"), Ordinal = 0 },
new PathEntry { System = "GB_GBC", SystemDisplayName = "Gameboy", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "GB_GBC", SystemDisplayName = "Gameboy", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "GB_GBC", SystemDisplayName = "Gameboy", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "GB_GBC", SystemDisplayName = "Gameboy", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "GB_GBC", SystemDisplayName = "Gameboy", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "GB_GBC", SystemDisplayName = "Gameboy", Type = "Palettes", Path = Path.Combine(".", "Palettes"), Ordinal = 6 },
new PathEntry { System = "DGB", SystemDisplayName = "Dual Gameboy", Type = "Base", Path = Path.Combine(".", "Dual Gameboy"), Ordinal = 0 },
new PathEntry { System = "DGB", SystemDisplayName = "Dual Gameboy", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "DGB", SystemDisplayName = "Dual Gameboy", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "DGB", SystemDisplayName = "Dual Gameboy", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "DGB", SystemDisplayName = "Dual Gameboy", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "DGB", SystemDisplayName = "Dual Gameboy", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "DGB", SystemDisplayName = "Dual Gameboy", Type = "Palettes", Path = Path.Combine(".", "Palettes"), Ordinal = 6 },
new PathEntry { System = "TI83", SystemDisplayName = "TI83", Type = "Base", Path = Path.Combine(".", "TI83"), Ordinal = 0 },
new PathEntry { System = "TI83", SystemDisplayName = "TI83", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "TI83", SystemDisplayName = "TI83", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "TI83", SystemDisplayName = "TI83", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "TI83", SystemDisplayName = "TI83", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "TI83", SystemDisplayName = "TI83", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "A26", SystemDisplayName = "Atari 2600", Type = "Base", Path = Path.Combine(".", "Atari 2600"), Ordinal = 0 },
new PathEntry { System = "A26", SystemDisplayName = "Atari 2600", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "A26", SystemDisplayName = "Atari 2600", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "A26", SystemDisplayName = "Atari 2600", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "A26", SystemDisplayName = "Atari 2600", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "A78", SystemDisplayName = "Atari 7800", Type = "Base", Path = Path.Combine(".", "Atari 7800"), Ordinal = 0 },
new PathEntry { System = "A78", SystemDisplayName = "Atari 7800", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "A78", SystemDisplayName = "Atari 7800", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "A78", SystemDisplayName = "Atari 7800", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "A78", SystemDisplayName = "Atari 7800", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "A78", SystemDisplayName = "Atari 7800", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "C64", SystemDisplayName = "Commodore 64", Type = "Base", Path = Path.Combine(".", "C64"), Ordinal = 0 },
new PathEntry { System = "C64", SystemDisplayName = "Commodore 64", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "C64", SystemDisplayName = "Commodore 64", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "C64", SystemDisplayName = "Commodore 64", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "C64", SystemDisplayName = "Commodore 64", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Base", Path = Path.Combine(".", "ZXSpectrum"), Ordinal = 0 },
new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "AmstradCPC", SystemDisplayName = "Amstrad CPC", Type = "Base", Path = Path.Combine(".", "AmstradCPC"), Ordinal = 0 },
new PathEntry { System = "AmstradCPC", SystemDisplayName = "Amstrad CPC", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "AmstradCPC", SystemDisplayName = "Amstrad CPC", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "AmstradCPC", SystemDisplayName = "Amstrad CPC", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "AmstradCPC", SystemDisplayName = "Amstrad CPC", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Base", Path = Path.Combine(".", "PSX"), Ordinal = 0 },
new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "Coleco", SystemDisplayName = "Coleco", Type = "Base", Path = Path.Combine(".", "Coleco"), Ordinal = 0 },
new PathEntry { System = "Coleco", SystemDisplayName = "Coleco", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "Coleco", SystemDisplayName = "Coleco", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "Coleco", SystemDisplayName = "Coleco", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "Coleco", SystemDisplayName = "Coleco", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "N64", SystemDisplayName = "N64", Type = "Base", Path = Path.Combine(".", "N64"), Ordinal = 0 },
new PathEntry { System = "N64", SystemDisplayName = "N64", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "N64", SystemDisplayName = "N64", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "N64", SystemDisplayName = "N64", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "N64", SystemDisplayName = "N64", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "N64", SystemDisplayName = "N64", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "SAT", SystemDisplayName = "Saturn", Type = "Base", Path = Path.Combine(".", "Saturn"), Ordinal = 0 },
new PathEntry { System = "SAT", SystemDisplayName = "Saturn", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "SAT", SystemDisplayName = "Saturn", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "SAT", SystemDisplayName = "Saturn", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "SAT", SystemDisplayName = "Saturn", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "SAT", SystemDisplayName = "Saturn", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "WSWAN", SystemDisplayName = "WonderSwan", Type = "Base", Path = Path.Combine(".", "WonderSwan"), Ordinal = 0 },
new PathEntry { System = "WSWAN", SystemDisplayName = "WonderSwan", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "WSWAN", SystemDisplayName = "WonderSwan", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "WSWAN", SystemDisplayName = "WonderSwan", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "WSWAN", SystemDisplayName = "WonderSwan", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "WSWAN", SystemDisplayName = "WonderSwan", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "Lynx", SystemDisplayName = "Lynx", Type = "Base", Path = Path.Combine(".", "Lynx"), Ordinal = 0 },
new PathEntry { System = "Lynx", SystemDisplayName = "Lynx", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "Lynx", SystemDisplayName = "Lynx", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "Lynx", SystemDisplayName = "Lynx", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "Lynx", SystemDisplayName = "Lynx", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "Lynx", SystemDisplayName = "Lynx", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "AppleII", SystemDisplayName = "Apple II", Type = "Base", Path = Path.Combine(".", "Apple II"), Ordinal = 0 },
new PathEntry { System = "AppleII", SystemDisplayName = "Apple II", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "AppleII", SystemDisplayName = "Apple II", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "AppleII", SystemDisplayName = "Apple II", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "AppleII", SystemDisplayName = "Apple II", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Base", Path = Path.Combine(".", "Libretro"), Ordinal = 0 },
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Cores", Path = Path.Combine(".", "Cores"), Ordinal = 1 },
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "System", Path = Path.Combine(".", "System"), Ordinal = 2 },
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 3 },
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 4 },
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 5 },
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 6 },
new PathEntry { System = "VB", SystemDisplayName = "VB", Type = "Base", Path = Path.Combine(".", "VB"), Ordinal = 0 },
new PathEntry { System = "VB", SystemDisplayName = "VB", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "VB", SystemDisplayName = "VB", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "VB", SystemDisplayName = "VB", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "VB", SystemDisplayName = "VB", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "VB", SystemDisplayName = "VB", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "NGP", SystemDisplayName = "NGP", Type = "Base", Path = Path.Combine(".", "NGP"), Ordinal = 0 },
new PathEntry { System = "NGP", SystemDisplayName = "NGP", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "NGP", SystemDisplayName = "NGP", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "NGP", SystemDisplayName = "NGP", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "NGP", SystemDisplayName = "NGP", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "NGP", SystemDisplayName = "NGP", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "PCFX", SystemDisplayName = "PCFX", Type = "Base", Path = Path.Combine(".", "PCFX"), Ordinal = 0 },
new PathEntry { System = "PCFX", SystemDisplayName = "PCFX", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "PCFX", SystemDisplayName = "PCFX", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "PCFX", SystemDisplayName = "PCFX", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "PCFX", SystemDisplayName = "PCFX", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "PCFX", SystemDisplayName = "PCFX", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "ChannelF", SystemDisplayName = "Fairchild Channel F", Type = "Base", Path = Path.Combine(".", "ZXSpectrum"), Ordinal = 0 },
new PathEntry { System = "ChannelF", SystemDisplayName = "Fairchild Channel F", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "ChannelF", SystemDisplayName = "Fairchild Channel F", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "ChannelF", SystemDisplayName = "Fairchild Channel F", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "ChannelF", SystemDisplayName = "Fairchild Channel F", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "Base", Path = Path.Combine(".", "GB3x"), Ordinal = 0 },
new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "GB4x", SystemDisplayName = "GB4x", Type = "Base", Path = Path.Combine(".", "GB4x"), Ordinal = 0 },
new PathEntry { System = "GB4x", SystemDisplayName = "GB4x", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "GB4x", SystemDisplayName = "GB4x", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "GB4x", SystemDisplayName = "GB4x", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "GB4x", SystemDisplayName = "GB4x", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "GB4x", SystemDisplayName = "GB4x", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "Base", Path = Path.Combine(".", "VEC"), Ordinal = 0 },
new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "O2", SystemDisplayName = "O2", Type = "Base", Path = Path.Combine(".", "O2"), Ordinal = 0 },
new PathEntry { System = "O2", SystemDisplayName = "O2", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "O2", SystemDisplayName = "O2", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "O2", SystemDisplayName = "O2", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "O2", SystemDisplayName = "O2", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "O2", SystemDisplayName = "O2", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "MSX", SystemDisplayName = "MSX", Type = "Base", Path = Path.Combine(".", "MSX"), Ordinal = 0 },
new PathEntry { System = "MSX", SystemDisplayName = "MSX", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "MSX", SystemDisplayName = "MSX", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "MSX", SystemDisplayName = "MSX", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "MSX", SystemDisplayName = "MSX", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "MSX", SystemDisplayName = "MSX", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
};
}
}

View File

@ -0,0 +1,363 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BizHawk.Common.PathExtensions;
using Newtonsoft.Json;
namespace BizHawk.Client.Common
{
[JsonObject]
public class PathEntryCollection : IEnumerable<PathEntry>
{
public List<PathEntry> Paths { get; }
public PathEntryCollection()
{
Paths = new List<PathEntry>();
Paths.AddRange(DefaultValues);
}
[JsonConstructor]
public PathEntryCollection(List<PathEntry> paths)
{
Paths = paths;
}
public void Add(PathEntry p)
{
Paths.Add(p);
}
public bool UseRecentForRoms { get; set; }
public string LastRomPath { get; set; } = ".";
public IEnumerator<PathEntry> GetEnumerator() => Paths.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public PathEntry this[string system, string type] =>
Paths.FirstOrDefault(p => p.IsSystem(system) && p.Type == type)
?? TryGetDebugPath(system, type);
private PathEntry TryGetDebugPath(string system, string type)
{
if (Paths.Any(p => p.IsSystem(system)))
{
// we have the system, but not the type. don't attempt to add an unknown type
return null;
}
// we don't have anything for the system in question. add a set of stock paths
var systemPath = $"{system.RemoveInvalidFileSystemChars()}_INTERIM";
var systemDisp = $"{system} (INTERIM)";
Paths.AddRange(new[]
{
new PathEntry { System = system, SystemDisplayName = systemDisp, Type = "Base", Path = Path.Combine(".", systemPath), Ordinal = 0 },
new PathEntry { System = system, SystemDisplayName = systemDisp, Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = system, SystemDisplayName = systemDisp, Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = system, SystemDisplayName = systemDisp, Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = system, SystemDisplayName = systemDisp, Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = system, SystemDisplayName = systemDisp, Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 }
});
return this[system, type];
}
public void ResolveWithDefaults()
{
// Add missing entries
foreach (PathEntry defaultPath in DefaultValues)
{
var path = Paths.FirstOrDefault(p => p.System == defaultPath.System && p.Type == defaultPath.Type);
if (path == null)
{
Paths.Add(defaultPath);
}
}
var entriesToRemove = new List<PathEntry>();
// Remove entries that no longer exist in defaults
foreach (PathEntry pathEntry in Paths)
{
var path = DefaultValues.FirstOrDefault(p => p.System == pathEntry.System && p.Type == pathEntry.Type);
if (path == null)
{
entriesToRemove.Add(pathEntry);
}
}
foreach (PathEntry entry in entriesToRemove)
{
Paths.Remove(entry);
}
// Add missing display names
var missingDisplayPaths = Paths.Where(p => p.SystemDisplayName == null);
foreach (PathEntry path in missingDisplayPaths)
{
path.SystemDisplayName = DefaultValues.First(p => p.System == path.System).SystemDisplayName;
}
}
public string FirmwaresPathFragment => this["Global", "Firmware"].Path;
internal string TempFilesFragment => this["Global", "Temp Files"].Path;
public static List<PathEntry> DefaultValues => new List<PathEntry>
{
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Base", Path = ".", Ordinal = 1 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "ROM", Path = ".", Ordinal = 2 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Firmware", Path = Path.Combine(".", "Firmware"), Ordinal = 3 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Movies", Path = Path.Combine(".", "Movies"), Ordinal = 4 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Movie backups", Path = Path.Combine(".", "Movies", "backup"), Ordinal = 5 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "A/V Dumps", Path = ".", Ordinal = 6 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Tools", Path = Path.Combine(".", "Tools"), Ordinal = 7 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Lua", Path = Path.Combine(".", "Lua"), Ordinal = 8 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Watch (.wch)", Path = Path.Combine(".", "."), Ordinal = 9 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Debug Logs", Path = Path.Combine(".", ""), Ordinal = 10 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Macros", Path = Path.Combine(".", "Movies", "Macros"), Ordinal = 11 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "TAStudio states", Path = Path.Combine(".", "Movies", "TAStudio states"), Ordinal = 12 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Multi-Disk Bundles", Path = Path.Combine(".", ""), Ordinal = 13 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "External Tools", Path = Path.Combine(".", "ExternalTools"), Ordinal = 14 },
new PathEntry { System = "Global_NULL", SystemDisplayName = "Global", Type = "Temp Files", Path = "", Ordinal = 15 },
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "Base", Path = Path.Combine(".", "Intellivision"), Ordinal = 0 },
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "INTV", SystemDisplayName = "Intellivision", Type = "Palettes", Path = Path.Combine(".", "Palettes"), Ordinal = 6 },
new PathEntry { System = "NES", SystemDisplayName = "NES", Type = "Base", Path = Path.Combine(".", "NES"), Ordinal = 0 },
new PathEntry { System = "NES", SystemDisplayName = "NES", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "NES", SystemDisplayName = "NES", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "NES", SystemDisplayName = "NES", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "NES", SystemDisplayName = "NES", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "NES", SystemDisplayName = "NES", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "NES", SystemDisplayName = "NES", Type = "Palettes", Path = Path.Combine(".", "Palettes"), Ordinal = 6 },
new PathEntry { System = "SNES_SGB", SystemDisplayName = "SNES", Type = "Base", Path = Path.Combine(".", "SNES"), Ordinal = 0 },
new PathEntry { System = "SNES_SGB", SystemDisplayName = "SNES", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "SNES_SGB", SystemDisplayName = "SNES", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "SNES_SGB", SystemDisplayName = "SNES", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "SNES_SGB", SystemDisplayName = "SNES", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "SNES_SGB", SystemDisplayName = "SNES", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "GBA", SystemDisplayName = "GBA", Type = "Base", Path = Path.Combine(".", "GBA"), Ordinal = 0 },
new PathEntry { System = "GBA", SystemDisplayName = "GBA", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "GBA", SystemDisplayName = "GBA", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "GBA", SystemDisplayName = "GBA", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "GBA", SystemDisplayName = "GBA", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "GBA", SystemDisplayName = "GBA", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "SMS", SystemDisplayName = "SMS", Type = "Base", Path = Path.Combine(".", "SMS"), Ordinal = 0 },
new PathEntry { System = "SMS", SystemDisplayName = "SMS", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "SMS", SystemDisplayName = "SMS", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "SMS", SystemDisplayName = "SMS", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "SMS", SystemDisplayName = "SMS", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "SMS", SystemDisplayName = "SMS", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "GG", SystemDisplayName = "GG", Type = "Base", Path = Path.Combine(".", "Game Gear"), Ordinal = 0 },
new PathEntry { System = "GG", SystemDisplayName = "GG", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "GG", SystemDisplayName = "GG", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "GG", SystemDisplayName = "GG", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "GG", SystemDisplayName = "GG", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "GG", SystemDisplayName = "GG", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "SG", SystemDisplayName = "SG", Type = "Base", Path = Path.Combine(".", "SG-1000"), Ordinal = 0 },
new PathEntry { System = "SG", SystemDisplayName = "SG", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "SG", SystemDisplayName = "SG", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "SG", SystemDisplayName = "SG", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "SG", SystemDisplayName = "SG", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "SG", SystemDisplayName = "SG", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "GEN", SystemDisplayName = "Genesis", Type = "Base", Path = Path.Combine(".", "Genesis"), Ordinal = 0 },
new PathEntry { System = "GEN", SystemDisplayName = "Genesis", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "GEN", SystemDisplayName = "Genesis", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "GEN", SystemDisplayName = "Genesis", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "GEN", SystemDisplayName = "Genesis", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "GEN", SystemDisplayName = "Genesis", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "PCE_PCECD_SGX", SystemDisplayName = "PC Engine", Type = "Base", Path = Path.Combine(".", "PC Engine"), Ordinal = 0 },
new PathEntry { System = "PCE_PCECD_SGX", SystemDisplayName = "PC Engine", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "PCE_PCECD_SGX", SystemDisplayName = "PC Engine", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "PCE_PCECD_SGX", SystemDisplayName = "PC Engine", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "PCE_PCECD_SGX", SystemDisplayName = "PC Engine", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "PCE_PCECD_SGX", SystemDisplayName = "PC Engine", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "GB_GBC", SystemDisplayName = "Gameboy", Type = "Base", Path = Path.Combine(".", "Gameboy"), Ordinal = 0 },
new PathEntry { System = "GB_GBC", SystemDisplayName = "Gameboy", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "GB_GBC", SystemDisplayName = "Gameboy", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "GB_GBC", SystemDisplayName = "Gameboy", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "GB_GBC", SystemDisplayName = "Gameboy", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "GB_GBC", SystemDisplayName = "Gameboy", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "GB_GBC", SystemDisplayName = "Gameboy", Type = "Palettes", Path = Path.Combine(".", "Palettes"), Ordinal = 6 },
new PathEntry { System = "DGB", SystemDisplayName = "Dual Gameboy", Type = "Base", Path = Path.Combine(".", "Dual Gameboy"), Ordinal = 0 },
new PathEntry { System = "DGB", SystemDisplayName = "Dual Gameboy", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "DGB", SystemDisplayName = "Dual Gameboy", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "DGB", SystemDisplayName = "Dual Gameboy", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "DGB", SystemDisplayName = "Dual Gameboy", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "DGB", SystemDisplayName = "Dual Gameboy", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "DGB", SystemDisplayName = "Dual Gameboy", Type = "Palettes", Path = Path.Combine(".", "Palettes"), Ordinal = 6 },
new PathEntry { System = "TI83", SystemDisplayName = "TI83", Type = "Base", Path = Path.Combine(".", "TI83"), Ordinal = 0 },
new PathEntry { System = "TI83", SystemDisplayName = "TI83", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "TI83", SystemDisplayName = "TI83", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "TI83", SystemDisplayName = "TI83", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "TI83", SystemDisplayName = "TI83", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "TI83", SystemDisplayName = "TI83", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "A26", SystemDisplayName = "Atari 2600", Type = "Base", Path = Path.Combine(".", "Atari 2600"), Ordinal = 0 },
new PathEntry { System = "A26", SystemDisplayName = "Atari 2600", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "A26", SystemDisplayName = "Atari 2600", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "A26", SystemDisplayName = "Atari 2600", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "A26", SystemDisplayName = "Atari 2600", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "A78", SystemDisplayName = "Atari 7800", Type = "Base", Path = Path.Combine(".", "Atari 7800"), Ordinal = 0 },
new PathEntry { System = "A78", SystemDisplayName = "Atari 7800", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "A78", SystemDisplayName = "Atari 7800", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "A78", SystemDisplayName = "Atari 7800", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "A78", SystemDisplayName = "Atari 7800", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "A78", SystemDisplayName = "Atari 7800", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "C64", SystemDisplayName = "Commodore 64", Type = "Base", Path = Path.Combine(".", "C64"), Ordinal = 0 },
new PathEntry { System = "C64", SystemDisplayName = "Commodore 64", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "C64", SystemDisplayName = "Commodore 64", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "C64", SystemDisplayName = "Commodore 64", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "C64", SystemDisplayName = "Commodore 64", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Base", Path = Path.Combine(".", "ZXSpectrum"), Ordinal = 0 },
new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "AmstradCPC", SystemDisplayName = "Amstrad CPC", Type = "Base", Path = Path.Combine(".", "AmstradCPC"), Ordinal = 0 },
new PathEntry { System = "AmstradCPC", SystemDisplayName = "Amstrad CPC", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "AmstradCPC", SystemDisplayName = "Amstrad CPC", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "AmstradCPC", SystemDisplayName = "Amstrad CPC", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "AmstradCPC", SystemDisplayName = "Amstrad CPC", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Base", Path = Path.Combine(".", "PSX"), Ordinal = 0 },
new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "Coleco", SystemDisplayName = "Coleco", Type = "Base", Path = Path.Combine(".", "Coleco"), Ordinal = 0 },
new PathEntry { System = "Coleco", SystemDisplayName = "Coleco", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "Coleco", SystemDisplayName = "Coleco", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "Coleco", SystemDisplayName = "Coleco", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "Coleco", SystemDisplayName = "Coleco", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "N64", SystemDisplayName = "N64", Type = "Base", Path = Path.Combine(".", "N64"), Ordinal = 0 },
new PathEntry { System = "N64", SystemDisplayName = "N64", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "N64", SystemDisplayName = "N64", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "N64", SystemDisplayName = "N64", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "N64", SystemDisplayName = "N64", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "N64", SystemDisplayName = "N64", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "SAT", SystemDisplayName = "Saturn", Type = "Base", Path = Path.Combine(".", "Saturn"), Ordinal = 0 },
new PathEntry { System = "SAT", SystemDisplayName = "Saturn", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "SAT", SystemDisplayName = "Saturn", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "SAT", SystemDisplayName = "Saturn", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "SAT", SystemDisplayName = "Saturn", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "SAT", SystemDisplayName = "Saturn", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "WSWAN", SystemDisplayName = "WonderSwan", Type = "Base", Path = Path.Combine(".", "WonderSwan"), Ordinal = 0 },
new PathEntry { System = "WSWAN", SystemDisplayName = "WonderSwan", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "WSWAN", SystemDisplayName = "WonderSwan", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "WSWAN", SystemDisplayName = "WonderSwan", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "WSWAN", SystemDisplayName = "WonderSwan", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "WSWAN", SystemDisplayName = "WonderSwan", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "Lynx", SystemDisplayName = "Lynx", Type = "Base", Path = Path.Combine(".", "Lynx"), Ordinal = 0 },
new PathEntry { System = "Lynx", SystemDisplayName = "Lynx", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "Lynx", SystemDisplayName = "Lynx", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "Lynx", SystemDisplayName = "Lynx", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "Lynx", SystemDisplayName = "Lynx", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "Lynx", SystemDisplayName = "Lynx", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "AppleII", SystemDisplayName = "Apple II", Type = "Base", Path = Path.Combine(".", "Apple II"), Ordinal = 0 },
new PathEntry { System = "AppleII", SystemDisplayName = "Apple II", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "AppleII", SystemDisplayName = "Apple II", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "AppleII", SystemDisplayName = "Apple II", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "AppleII", SystemDisplayName = "Apple II", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Base", Path = Path.Combine(".", "Libretro"), Ordinal = 0 },
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Cores", Path = Path.Combine(".", "Cores"), Ordinal = 1 },
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "System", Path = Path.Combine(".", "System"), Ordinal = 2 },
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 3 },
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 4 },
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 5 },
new PathEntry { System = "Libretro", SystemDisplayName = "Libretro", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 6 },
new PathEntry { System = "VB", SystemDisplayName = "VB", Type = "Base", Path = Path.Combine(".", "VB"), Ordinal = 0 },
new PathEntry { System = "VB", SystemDisplayName = "VB", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "VB", SystemDisplayName = "VB", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "VB", SystemDisplayName = "VB", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "VB", SystemDisplayName = "VB", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "VB", SystemDisplayName = "VB", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "NGP", SystemDisplayName = "NGP", Type = "Base", Path = Path.Combine(".", "NGP"), Ordinal = 0 },
new PathEntry { System = "NGP", SystemDisplayName = "NGP", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "NGP", SystemDisplayName = "NGP", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "NGP", SystemDisplayName = "NGP", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "NGP", SystemDisplayName = "NGP", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "NGP", SystemDisplayName = "NGP", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "PCFX", SystemDisplayName = "PCFX", Type = "Base", Path = Path.Combine(".", "PCFX"), Ordinal = 0 },
new PathEntry { System = "PCFX", SystemDisplayName = "PCFX", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "PCFX", SystemDisplayName = "PCFX", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "PCFX", SystemDisplayName = "PCFX", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "PCFX", SystemDisplayName = "PCFX", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "PCFX", SystemDisplayName = "PCFX", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "ChannelF", SystemDisplayName = "Fairchild Channel F", Type = "Base", Path = Path.Combine(".", "ZXSpectrum"), Ordinal = 0 },
new PathEntry { System = "ChannelF", SystemDisplayName = "Fairchild Channel F", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "ChannelF", SystemDisplayName = "Fairchild Channel F", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "ChannelF", SystemDisplayName = "Fairchild Channel F", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "ChannelF", SystemDisplayName = "Fairchild Channel F", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "Base", Path = Path.Combine(".", "GB3x"), Ordinal = 0 },
new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "GB4x", SystemDisplayName = "GB4x", Type = "Base", Path = Path.Combine(".", "GB4x"), Ordinal = 0 },
new PathEntry { System = "GB4x", SystemDisplayName = "GB4x", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "GB4x", SystemDisplayName = "GB4x", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "GB4x", SystemDisplayName = "GB4x", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "GB4x", SystemDisplayName = "GB4x", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "GB4x", SystemDisplayName = "GB4x", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "Base", Path = Path.Combine(".", "VEC"), Ordinal = 0 },
new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "O2", SystemDisplayName = "O2", Type = "Base", Path = Path.Combine(".", "O2"), Ordinal = 0 },
new PathEntry { System = "O2", SystemDisplayName = "O2", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "O2", SystemDisplayName = "O2", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "O2", SystemDisplayName = "O2", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "O2", SystemDisplayName = "O2", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "O2", SystemDisplayName = "O2", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
new PathEntry { System = "MSX", SystemDisplayName = "MSX", Type = "Base", Path = Path.Combine(".", "MSX"), Ordinal = 0 },
new PathEntry { System = "MSX", SystemDisplayName = "MSX", Type = "ROM", Path = ".", Ordinal = 1 },
new PathEntry { System = "MSX", SystemDisplayName = "MSX", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 },
new PathEntry { System = "MSX", SystemDisplayName = "MSX", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 },
new PathEntry { System = "MSX", SystemDisplayName = "MSX", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 },
new PathEntry { System = "MSX", SystemDisplayName = "MSX", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 },
};
}
}

View File

@ -0,0 +1,352 @@
using System;
using System.IO;
using BizHawk.Common;
using BizHawk.Common.PathExtensions;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public static class PathEntryExtensions
{
/// <summary>
/// Returns the base path of the given system.
/// If the system can not be found, an empty string is returned
/// </summary>
public static string BaseFor(this PathEntryCollection collection, string systemId)
{
return string.IsNullOrWhiteSpace(systemId)
? ""
: collection[systemId, "Base"]?.Path ?? "";
}
public static string GlobalBaseAbsolutePath(this PathEntryCollection collection)
{
var globalBase = collection["Global", "Base"].Path;
// if %exe% prefixed then substitute exe path and repeat
if (globalBase.StartsWith("%exe%", StringComparison.InvariantCultureIgnoreCase))
{
globalBase = PathUtils.ExeDirectoryPath + globalBase.Substring(5);
}
// rooted paths get returned without change
// (this is done after keyword substitution to avoid problems though)
if (Path.IsPathRooted(globalBase))
{
return globalBase;
}
// not-rooted things are relative to exe path
globalBase = Path.Combine(PathUtils.ExeDirectoryPath, globalBase);
return globalBase;
}
/// <summary>
/// Returns an entry for the given system and pathType (ROM, screenshot, etc)
/// but falls back to the base system or global system if it fails
/// to find pathType or systemId
/// </summary>
public static PathEntry EntryWithFallback(this PathEntryCollection collection, string pathType, string systemId)
{
return (collection[systemId, pathType]
?? collection[systemId, "Base"])
?? collection["Global", "Base"];
}
public static string AbsolutePathForType(this PathEntryCollection collection, string systemId, string type)
{
var path = collection.EntryWithFallback(type, systemId).Path;
return collection.AbsolutePathFor(path, systemId);
}
/// <summary>
/// Returns an absolute path for the given relative path.
/// If provided, the systemId will be used to generate the path.
/// Wildcards are supported.
/// Logic will fallback until an absolute path is found,
/// using Global Base as a last resort
/// </summary>
public static string AbsolutePathFor(this PathEntryCollection collection, string path, string systemId)
{
// warning: supposedly Path.GetFullPath accesses directories (and needs permissions)
// if this poses a problem, we need to paste code from .net or mono sources and fix them to not pose problems, rather than homebrew stuff
return Path.GetFullPath(collection.AbsolutePathForInner(path, systemId));
}
private static string AbsolutePathForInner(this PathEntryCollection collection, string path, string systemId)
{
// Hack
if (systemId == "Global")
{
systemId = null;
}
// This function translates relative path and special identifiers in absolute paths
if (path.Length < 1)
{
return collection.GlobalBaseAbsolutePath();
}
if (path == "%recent%")
{
return Environment.SpecialFolder.Recent.ToString();
}
if (path.StartsWith("%exe%"))
{
return PathUtils.ExeDirectoryPath + path.Substring(5);
}
if (path.StartsWith("%rom%"))
{
return collection.LastRomPath + path.Substring(5);
}
if (path[0] == '.')
{
if (!string.IsNullOrWhiteSpace(systemId))
{
path = path.Remove(0, 1);
path = path.Insert(0, collection.BaseFor(systemId));
}
if (path.Length == 1)
{
return collection.GlobalBaseAbsolutePath();
}
if (path[0] == '.')
{
path = path.Remove(0, 1);
path = path.Insert(0, collection.GlobalBaseAbsolutePath());
}
return path;
}
if (Path.IsPathRooted(path))
{
return path;
}
//handling of initial .. was removed (Path.GetFullPath can handle it)
//handling of file:// or file:\\ was removed (can Path.GetFullPath handle it? not sure)
// all bad paths default to EXE
return PathUtils.ExeDirectoryPath;
}
public static string MovieAbsolutePath(this PathEntryCollection collection)
{
var path = collection["Global", "Movies"].Path;
return collection.AbsolutePathFor(path, null);
}
public static string MovieBackupsAbsolutePath(this PathEntryCollection collection)
{
var path = collection["Global", "Movie backups"].Path;
return collection.AbsolutePathFor(path, null);
}
public static string AvAbsolutePath(this PathEntryCollection collection)
{
var path = collection["Global", "A/V Dumps"].Path;
return collection.AbsolutePathFor(path, null);
}
public static string LuaAbsolutePath(this PathEntryCollection collection)
{
var path = collection["Global", "Lua"].Path;
return collection.AbsolutePathFor(path, null);
}
public static string FirmwareAbsolutePath(this PathEntryCollection collection)
{
return collection.AbsolutePathFor(collection.FirmwaresPathFragment, null);
}
public static string LogAbsolutePath(this PathEntryCollection collection)
{
var path = collection.ResolveToolsPath(collection["Global", "Debug Logs"].Path);
return collection.AbsolutePathFor(path, null);
}
public static string WatchAbsolutePath(this PathEntryCollection collection)
{
var path = collection.ResolveToolsPath(collection["Global", "Watch (.wch)"].Path);
return collection.AbsolutePathFor(path, null);
}
public static string ToolsAbsolutePath(this PathEntryCollection collection)
{
var path = collection["Global", "Tools"].Path;
return collection.AbsolutePathFor(path, null);
}
public static string TastudioStatesAbsolutePath(this PathEntryCollection collection)
{
var path = collection["Global", "TAStudio states"].Path;
return collection.AbsolutePathFor(path, null);
}
public static string MultiDiskAbsolutePath(this PathEntryCollection collection)
{
var path = collection.ResolveToolsPath(collection["Global", "Multi-Disk Bundles"].Path);
return collection.AbsolutePathFor(path, null);
}
public static string RomAbsolutePath(this PathEntryCollection collection, string systemId = null)
{
if (string.IsNullOrWhiteSpace(systemId))
{
return collection.AbsolutePathFor(collection["Global_NULL", "ROM"].Path, "Global_NULL");
}
if (collection.UseRecentForRoms)
{
return Environment.SpecialFolder.Recent.ToString();
}
var path = collection[systemId, "ROM"];
if (!path.Path.PathIsSet())
{
path = collection["Global", "ROM"];
if (path.Path.PathIsSet())
{
return collection.AbsolutePathFor(path.Path, null);
}
}
return collection.AbsolutePathFor(path.Path, systemId);
}
public static string SaveRamAbsolutePath(this PathEntryCollection collection, GameInfo game, bool movieIsActive)
{
var name = game.FilesystemSafeName();
if (movieIsActive)
{
name += $".{Path.GetFileNameWithoutExtension(Global.MovieSession.Movie.Filename)}";
}
var pathEntry = collection[game.System, "Save RAM"]
?? collection[game.System, "Base"];
return $"{Path.Combine(collection.AbsolutePathFor(pathEntry.Path, game.System), name)}.SaveRAM";
}
// Shenanigans
public static string RetroSaveRamAbsolutePath(this PathEntryCollection collection, GameInfo game, bool movieIsActive, string movieFilename)
{
var name = game.FilesystemSafeName();
name = Path.GetDirectoryName(name);
if (name == "")
{
name = game.FilesystemSafeName();
}
if (movieIsActive)
{
name = Path.Combine(name, $"movie-{Path.GetFileNameWithoutExtension(movieFilename)}");
}
var pathEntry = collection[game.System, "Save RAM"]
?? collection[game.System, "Base"];
return Path.Combine(collection.AbsolutePathFor(pathEntry.Path, game.System), name);
}
// Shenanigans
public static string RetroSystemAbsolutePath(this PathEntryCollection collection, GameInfo game)
{
var name = game.FilesystemSafeName();
name = Path.GetDirectoryName(name);
if (string.IsNullOrEmpty(name))
{
name = game.FilesystemSafeName();
}
var pathEntry = collection[game.System, "System"]
?? collection[game.System, "Base"];
return Path.Combine(collection.AbsolutePathFor(pathEntry.Path, game.System), name);
}
public static string AutoSaveRamAbsolutePath(this PathEntryCollection collection, GameInfo game, bool movieIsActive)
{
var path = collection.SaveRamAbsolutePath(game, movieIsActive);
return path.Insert(path.Length - 8, ".AutoSaveRAM");
}
public static string CheatsAbsolutePath(this PathEntryCollection collection, string systemId)
{
var pathEntry = collection[systemId, "Cheats"]
?? collection[systemId, "Base"];
return collection.AbsolutePathFor(pathEntry.Path,systemId);
}
public static string SaveStateAbsolutePath(this PathEntryCollection collection, string systemId)
{
var pathEntry = collection[systemId, "Savestates"]
?? collection[systemId, "Base"];
return collection.AbsolutePathFor(pathEntry.Path, systemId);
}
public static string ScreenshotAbsolutePathFor(this PathEntryCollection collection, string systemId)
{
var entry = collection[systemId, "Screenshots"]
?? collection[systemId, "Base"];
return collection.AbsolutePathFor(entry.Path, systemId);
}
public static string PalettesAbsolutePathFor(this PathEntryCollection collection, string systemId)
{
return collection.AbsolutePathFor(collection[systemId, "Palettes"].Path, systemId);
}
/// <summary>
/// Takes an absolute path and attempts to convert it to a relative, based on the system,
/// or global base if no system is supplied, if it is not a subfolder of the base, it will return the path unaltered
/// </summary>
public static string TryMakeRelative(this PathEntryCollection collection, string absolutePath, string system = null) => absolutePath.MakeRelativeTo(
string.IsNullOrWhiteSpace(system)
? collection.GlobalBaseAbsolutePath()
: collection.AbsolutePathFor(collection.BaseFor(system), system)
);
/// <summary>
/// Puts the currently configured temp path into the environment for use as actual temp directory
/// </summary>
public static void RefreshTempPath(this PathEntryCollection collection)
{
if (!string.IsNullOrWhiteSpace(collection.TempFilesFragment))
{
// TODO - BUG - needs to route through PathManager.MakeAbsolutePath or something similar, but how?
string target = collection.TempFilesFragment;
TempFileManager.HelperSetTempPath(target);
}
}
private static string ResolveToolsPath(this PathEntryCollection collection, string subPath)
{
if (Path.IsPathRooted(subPath) || subPath.StartsWith("%"))
{
return subPath;
}
var toolsPath = collection["Global", "Tools"].Path;
// Hack for backwards compatibility, prior to 1.11.5, .wch files were in .\Tools, we don't want that to turn into .Tools\Tools
if (subPath == "Tools")
{
return toolsPath;
}
return Path.Combine(toolsPath, subPath);
}
}
}

View File

@ -8,12 +8,12 @@ namespace BizHawk.Client.Common
/// <param name="θ">angle (in degrees) in range <c>0..359</c> (this is not asserted)</param>
/// <returns>rectangular (Cartesian) coordinates <c>(x, y)</c>. <c>x</c> and/or <c>y</c> may be outside the range <c>-128..127</c>.</returns>
/// <seealso cref="RectToPolarLookup"/>
public static (short, short) PolarToRectLookup(ushort r, ushort θ) => (PolarRectConversionData._rθ2x[r, θ], PolarRectConversionData._rθ2y[r, θ]);
public static (short X, short Y) PolarToRectLookup(ushort r, ushort θ) => (PolarRectConversionData._rθ2x[r, θ], PolarRectConversionData._rθ2y[r, θ]);
/// <param name="x">horizontal component of rectangular (Cartesian) coordinates <c>(x, y)</c>, in range <c>-128..127</c> (this is not asserted)</param>
/// <param name="y">vertical component, as <paramref name="x"/></param>
/// <returns>polar coordinates <c>(r, θ)</c> where <c>r</c> is radial displacement in range <c>0..181</c> and <c>θ</c> is angle (in degrees) in range <c>0..359</c> (from <c>+x</c> towards <c>+y</c>)</returns>
/// <seealso cref="PolarToRectLookup"/>
public static (ushort, ushort) RectToPolarLookup(sbyte x, sbyte y) => unchecked((PolarRectConversionData._xy2r[(byte) x, (byte) y], PolarRectConversionData._xy2θ[(byte) x, (byte) y]));
public static (ushort R, ushort Θ) RectToPolarLookup(sbyte x, sbyte y) => unchecked((PolarRectConversionData._xy2r[(byte) x, (byte) y], PolarRectConversionData._xy2θ[(byte) x, (byte) y]));
}
}

View File

@ -18,10 +18,7 @@ namespace BizHawk.Client.Common
// pass floats solely from the original source
// this works in the code because SourceOr is the autofire controller
public float GetFloat(string name)
{
return Source.GetFloat(name);
}
public float GetFloat(string name) => Source.GetFloat(name);
internal IController Source { get; set; }
internal IController SourceAnd { get; set; }
@ -43,16 +40,12 @@ namespace BizHawk.Client.Common
// pass floats solely from the original source
// this works in the code because SourceOr is the autofire controller
public float GetFloat(string name)
{
return Source.GetFloat(name);
}
public float GetFloat(string name) => Source.GetFloat(name);
internal IController Source { get; set; }
internal IController SourceXor { get; set; }
}
public class ORAdapter : IController
{
public ControllerDefinition Definition => Source.Definition;
@ -65,10 +58,7 @@ namespace BizHawk.Client.Common
// pass floats solely from the original source
// this works in the code because SourceOr is the autofire controller
public float GetFloat(string name)
{
return Source.GetFloat(name);
}
public float GetFloat(string name) => Source.GetFloat(name);
internal IController Source { get; set; }
internal IController SourceOr { get; set; }

View File

@ -5,47 +5,91 @@ using BizHawk.Client.Common.InputAdapterExtensions;
namespace BizHawk.Client.Common
{
public static class InputManager
// don't take my word for it, but here is a guide...
// user -> Input -> ActiveController -> UDLR -> StickyXORPlayerInputAdapter -> TurboAdapter(TBD) -> Lua(?TBD?) -> ..
// .. -> MultitrackRewiringControllerAdapter -> MovieInputSourceAdapter -> (MovieSession) -> MovieOutputAdapter -> ControllerOutput(1) -> Game
// (1)->Input Display
public class InputManager
{
public static void RewireInputChain()
// the movie will be spliced in between these if it is present
public CopyControllerAdapter MovieInputSourceAdapter { get; } = new CopyControllerAdapter();
public CopyControllerAdapter MovieOutputHardpoint { get; } = new CopyControllerAdapter();
public MultitrackRewiringControllerAdapter MultitrackRewiringAdapter { get; } = new MultitrackRewiringControllerAdapter();
// the original source controller, bound to the user, sort of the "input" port for the chain, i think
public Controller ActiveController { get; set; } // TODO: private setter, add a method that takes both controllers in
// rapid fire version on the user controller, has its own key bindings and is OR'ed against ActiveController
public AutofireController AutoFireController { get; set; } // TODO: private setter, add a method that takes both controllers in
// the "output" port for the controller chain.
public CopyControllerAdapter ControllerOutput { get; } = new CopyControllerAdapter();
public UdlrControllerAdapter UdLRControllerAdapter { get; } = new UdlrControllerAdapter();
public AutoFireStickyXorAdapter AutofireStickyXorAdapter { get; } = new AutoFireStickyXorAdapter();
/// <summary>
/// provides an opportunity to mutate the player's input in an autohold style
/// </summary>
public StickyXorAdapter StickyXorAdapter { get; } = new StickyXorAdapter();
/// <summary>
/// Used to AND to another controller, used for <see cref="JoypadApi.Set(Dictionary{string, bool}, int?)">JoypadApi.Set</see>
/// </summary>
public OverrideAdapter ButtonOverrideAdapter { get; } = new OverrideAdapter();
/// <summary>
/// fire off one-frame logical button clicks here. useful for things like ti-83 virtual pad and reset buttons
/// </summary>
public ClickyVirtualPadController ClickyVirtualPadController { get; } = new ClickyVirtualPadController();
// Input state for game controller inputs are coalesced here
// This relies on a client specific implementation!
public SimpleController ControllerInputCoalescer { get; set; }
public Controller ClientControls { get; set; }
public void RewireInputChain()
{
Global.ControllerInputCoalescer.Clear();
Global.ControllerInputCoalescer.Definition = Global.ActiveController.Definition;
ControllerInputCoalescer.Clear();
ControllerInputCoalescer.Definition = ActiveController.Definition;
Global.UD_LR_ControllerAdapter.Source = Global.ActiveController.Or(Global.AutoFireController);
UdLRControllerAdapter.Source = ActiveController.Or(AutoFireController);
Global.StickyXORAdapter.Source = Global.UD_LR_ControllerAdapter;
Global.AutofireStickyXORAdapter.Source = Global.StickyXORAdapter;
StickyXorAdapter.Source = UdLRControllerAdapter;
AutofireStickyXorAdapter.Source = StickyXorAdapter;
Global.MultitrackRewiringAdapter.Source = Global.AutofireStickyXORAdapter;
Global.MovieInputSourceAdapter.Source = Global.MultitrackRewiringAdapter;
Global.ControllerOutput.Source = Global.MovieOutputHardpoint;
MultitrackRewiringAdapter.Source = AutofireStickyXorAdapter;
MovieInputSourceAdapter.Source = MultitrackRewiringAdapter;
ControllerOutput.Source = MovieOutputHardpoint;
Global.MovieSession.MovieControllerAdapter.Definition = Global.MovieInputSourceAdapter.Definition;
Global.MovieSession.MovieControllerAdapter.Definition = MovieInputSourceAdapter.Definition;
// connect the movie session before MovieOutputHardpoint if it is doing anything
// otherwise connect the MovieInputSourceAdapter to it, effectively bypassing the movie session
if (Global.MovieSession != null)
{
Global.MovieOutputHardpoint.Source = Global.MovieSession.MovieControllerAdapter;
MovieOutputHardpoint.Source = Global.MovieSession.MovieControllerAdapter;
}
else
{
Global.MovieOutputHardpoint.Source = Global.MovieInputSourceAdapter;
MovieOutputHardpoint.Source = MovieInputSourceAdapter;
}
}
public static void SyncControls()
public void SyncControls(IEmulator emulator, Config config)
{
var def = Global.Emulator.ControllerDefinition;
var def = emulator.ControllerDefinition;
Global.ActiveController = BindToDefinition(def, Global.Config.AllTrollers, Global.Config.AllTrollersAnalog);
Global.AutoFireController = BindToDefinitionAF(def, Global.Emulator, Global.Config.AllTrollersAutoFire);
ActiveController = BindToDefinition(def, config.AllTrollers, config.AllTrollersAnalog);
AutoFireController = BindToDefinitionAF(def, emulator, config.AllTrollersAutoFire);
// allow propagating controls that are in the current controller definition but not in the prebaked one
// these two lines shouldn't be required anymore under the new system?
Global.ActiveController.ForceType(new ControllerDefinition(def));
Global.ClickyVirtualPadController.Definition = new ControllerDefinition(def);
ActiveController.ForceType(new ControllerDefinition(def));
ClickyVirtualPadController.Definition = new ControllerDefinition(def);
RewireInputChain();
}

View File

@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
@ -29,47 +29,16 @@ namespace BizHawk.Client.Common
}
public float GetFloat(string name)
{
if (_floatOverrides.ContainsKey(name))
{
return _floatOverrides[name];
}
=> _floatOverrides.ContainsKey(name)
? _floatOverrides[name]
: 0.0F;
return 0.0F;
}
public IEnumerable<string> Overrides => _overrides.Select(kvp => kvp.Key);
public IEnumerable<string> Overrides
{
get
{
foreach (var kvp in _overrides)
{
yield return kvp.Key;
}
}
}
public IEnumerable<string> FloatOverrides => _floatOverrides.Select(kvp => kvp.Key);
public IEnumerable<string> FloatOverrides
{
get
{
foreach (var kvp in _floatOverrides)
{
yield return kvp.Key;
}
}
}
public IEnumerable<string> InversedButtons
{
get
{
foreach (var name in _inverses)
{
yield return name;
}
}
}
public IEnumerable<string> InversedButtons => _inverses;
public void SetFloat(string name, float value)
{

View File

@ -6,59 +6,14 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{
public interface ISticky : IController
public class StickyXorAdapter : IController
{
bool StickyIsInEffect(string button);
}
/// <summary>
/// Used by input display, to determine if either autofire or regular stickies
/// are "in effect" because we color this scenario differently
/// </summary>
public class StickyOrAdapter : IController
{
public ControllerDefinition Definition => Source.Definition;
public bool IsPressed(string button)
{
return Source.StickyIsInEffect(button)
|| SourceStickyOr.StickyIsInEffect(button);
}
// pass floats solely from the original source
// this works in the code because SourceOr is the autofire controller
public float GetFloat(string name)
{
int i = Source.Definition.FloatControls.IndexOf(name);
return Source.Definition.FloatRanges[i].Mid; // Floats don't make sense in sticky land
}
public ISticky Source { get; set; }
public ISticky SourceStickyOr { get; set; }
}
public class StickyXorAdapter : ISticky, IController
{
/// <summary>
/// Determines if a sticky is current mashing the button itself,
/// If sticky is not set then false, if set, it returns true if the Source is not pressed, else false
/// </summary>
public bool StickyIsInEffect(string button)
{
if (IsSticky(button))
{
return !Source.IsPressed(button);
}
return false;
}
public ControllerDefinition Definition => Source.Definition;
public bool IsPressed(string button)
{
var source = Source.IsPressed(button);
source ^= _stickySet.Contains(button);
source ^= CurrentStickies.Contains(button);
return source;
}
@ -83,8 +38,6 @@ namespace BizHawk.Client.Common
private List<string> _justPressed = new List<string>();
private readonly HashSet<string> _stickySet = new HashSet<string>();
// if SetFloat() is called (typically virtual pads), then that float will entirely override the Source input
// otherwise, the source is passed thru.
private readonly WorkingDictionary<string, float?> _floatSet = new WorkingDictionary<string, float?>();
@ -101,39 +54,33 @@ namespace BizHawk.Client.Common
}
}
public void ClearStickyFloats()
{
_floatSet.Clear();
}
public void ClearStickyFloats() => _floatSet.Clear();
public void SetSticky(string button, bool isSticky)
{
if (isSticky)
{
_stickySet.Add(button);
CurrentStickies.Add(button);
}
else
{
_stickySet.Remove(button);
CurrentStickies.Remove(button);
}
}
public void Unset(string button)
{
_stickySet.Remove(button);
CurrentStickies.Remove(button);
_floatSet.Remove(button);
}
public bool IsSticky(string button)
{
return _stickySet.Contains(button);
}
public bool IsSticky(string button) => CurrentStickies.Contains(button);
public HashSet<string> CurrentStickies => _stickySet;
public HashSet<string> CurrentStickies { get; } = new HashSet<string>();
public void ClearStickies()
{
_stickySet.Clear();
CurrentStickies.Clear();
_floatSet.Clear();
}
@ -141,13 +88,13 @@ namespace BizHawk.Client.Common
{
foreach (var button in buttons.Where(button => !_justPressed.Contains(button)))
{
if (_stickySet.Contains(button))
if (CurrentStickies.Contains(button))
{
_stickySet.Remove(button);
CurrentStickies.Remove(button);
}
else
{
_stickySet.Add(button);
CurrentStickies.Add(button);
}
}
@ -155,22 +102,8 @@ namespace BizHawk.Client.Common
}
}
public class AutoFireStickyXorAdapter : ISticky, IController
public class AutoFireStickyXorAdapter : IController
{
/// <summary>
/// Determines if a sticky is current mashing the button itself,
/// If sticky is not set then false, if set, it returns true if the Source is not pressed, else false
/// </summary>
public bool StickyIsInEffect(string button)
{
if (IsSticky(button))
{
return !Source.IsPressed(button);
}
return false;
}
public ControllerDefinition Definition => Source.Definition;
public bool IsPressed(string button)

View File

@ -56,12 +56,7 @@ namespace BizHawk.Client.Common
[LuaMethod("trim", "returns a string that trims whitespace on the left and right ends of the string")]
public static string Trim(string str)
{
if (string.IsNullOrEmpty(str))
{
return null;
}
return str.Trim();
return string.IsNullOrEmpty(str) ? null : str.Trim();
}
[LuaMethodExample("local stbizrep = bizstring.replace( \"Some string\", \"Some\", \"Replaced\" );")]

View File

@ -220,7 +220,7 @@ __Types and notation__
}
}
private string TypeCleanup(string str)
private static string TypeCleanup(string str)
{
return str
.Replace("System", "")

View File

@ -61,18 +61,18 @@
public void Toggle()
{
if (State == RunState.Paused)
switch (State)
{
State = RunState.Running;
}
else if (State == RunState.Disabled)
{
State = RunState.Running;
FrameWaiting = false;
}
else
{
State = RunState.Disabled;
case RunState.Paused:
State = RunState.Running;
break;
case RunState.Disabled:
State = RunState.Running;
FrameWaiting = false;
break;
default:
State = RunState.Disabled;
break;
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using BizHawk.Common.PathExtensions;
namespace BizHawk.Client.Common
{
@ -138,8 +139,7 @@ namespace BizHawk.Client.Common
sb
.Append(file.Enabled ? "1" : "0")
.Append(' ')
.Append(PathManager.MakeRelativeTo(PathManager.MakeAbsolutePath(file.Path, ""),
Path.GetDirectoryName(path)))
.Append(Global.Config.PathEntries.AbsolutePathFor(file.Path, "").MakeRelativeTo(Path.GetDirectoryName(path)))
.AppendLine();
}
}

View File

@ -60,22 +60,13 @@ namespace BizHawk.Client.Common
protected static Color? ToColor(object o)
{
if (o == null)
return o switch
{
return null;
}
if (o is double d)
{
return Color.FromArgb((int)(long)d);
}
if (o is string s)
{
return Color.FromName(s);
}
return null;
null => null,
double d => Color.FromArgb((int) (long) d),
string s => Color.FromName(s),
_ => (Color?) null
};
}
protected void Log(object message)

View File

@ -4,33 +4,29 @@ namespace BizHawk.Client.Common
{
public static class HeaderKeys
{
public const string EMULATIONVERSION = "emuVersion";
public const string MOVIEVERSION = "MovieVersion";
public const string PLATFORM = "Platform";
public const string GAMENAME = "GameName";
public const string AUTHOR = "Author";
public const string RERECORDS = "rerecordCount";
public const string STARTSFROMSAVESTATE = "StartsFromSavestate";
public const string STARTSFROMSAVERAM = "StartsFromSaveRam";
public const string SAVESTATEBINARYBASE64BLOB = "SavestateBinaryBase64Blob"; // this string will not contain base64: ; it's implicit (this is to avoid another big string op to dice off the base64: substring)
public const string SHA1 = "SHA1";
public const string FIRMWARESHA1 = "FirmwareSHA1";
public const string PAL = "PAL";
public const string BOARDNAME = "BoardName";
public const string SYNCSETTINGS = "SyncSettings";
public const string LOOPOFFSET = "LoopOffset";
public const string VBLANKCOUNT = "VBlankCount";
public const string CYCLECOUNT = "CycleCount";
public const string EmulationVersion = "emuVersion";
public const string MovieVersion = "MovieVersion";
public const string Platform = "Platform";
public const string GameName = "GameName";
public const string Author = "Author";
public const string Rerecords = "rerecordCount";
public const string StartsFromSavestate = "StartsFromSavestate";
public const string StartsFromSaveram = "StartsFromSaveRam";
public const string SavestateBinaryBase64Blob = "SavestateBinaryBase64Blob"; // this string will not contain base64: ; it's implicit (this is to avoid another big string op to dice off the base64: substring)
public const string Sha1 = "SHA1";
public const string FirmwareSha1 = "FirmwareSHA1";
public const string Pal = "PAL";
public const string BoardName = "BoardName";
public const string SyncSettings = "SyncSettings";
public const string LoopOffset = "LoopOffset";
public const string VBlankCount = "VBlankCount";
public const string CycleCount = "CycleCount";
public const string Core = "Core";
// Core Setting
public const string CORE = "Core";
public static bool Contains(string val)
{
return typeof(HeaderKeys)
public static bool Contains(string val) =>
typeof(HeaderKeys)
.GetFields()
.Select(field => field.GetValue(null).ToString())
.Contains(val);
}
}
}

View File

@ -147,7 +147,7 @@ namespace BizHawk.Client.Common
MovieControllerAdapter.LatchFromSource(input);
if (MultiTrack.IsActive)
{
Global.MultitrackRewiringAdapter.Source = MovieControllerAdapter;
Global.InputManager.MultitrackRewiringAdapter.Source = MovieControllerAdapter;
}
}
@ -156,7 +156,7 @@ namespace BizHawk.Client.Common
var gambatteName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(Gameboy), typeof(CoreAttribute))).CoreName;
if (Movie.Core == gambatteName)
{
var movieCycles = Convert.ToUInt64(Movie.HeaderEntries[HeaderKeys.CYCLECOUNT]);
var movieCycles = Convert.ToUInt64(Movie.HeaderEntries[HeaderKeys.CycleCount]);
var coreCycles = (Global.Emulator as Gameboy).CycleCount;
if (movieCycles != (ulong)coreCycles)
{
@ -237,7 +237,7 @@ namespace BizHawk.Client.Common
{
if (!Movie.IsActive())
{
LatchInputFromPlayer(Global.MovieInputSourceAdapter);
LatchInputFromPlayer(Global.InputManager.MovieInputSourceAdapter);
}
else if (Movie.IsFinished())
{
@ -248,7 +248,7 @@ namespace BizHawk.Client.Common
}
else
{
LatchInputFromPlayer(Global.MovieInputSourceAdapter);
LatchInputFromPlayer(Global.InputManager.MovieInputSourceAdapter);
}
}
else if (Movie.IsPlaying())
@ -264,20 +264,20 @@ namespace BizHawk.Client.Common
// Movie may go into finished mode as a result from latching
if (!Movie.IsFinished())
{
if (Global.ClientControls.IsPressed("Scrub Input"))
if (Global.InputManager.ClientControls.IsPressed("Scrub Input"))
{
LatchInputFromPlayer(Global.MovieInputSourceAdapter);
LatchInputFromPlayer(Global.InputManager.MovieInputSourceAdapter);
ClearFrame();
}
else if (Global.Config.MoviePlaybackPokeMode)
{
LatchInputFromPlayer(Global.MovieInputSourceAdapter);
LatchInputFromPlayer(Global.InputManager.MovieInputSourceAdapter);
var lg = Movie.LogGeneratorInstance();
lg.SetSource(Global.MovieOutputHardpoint);
lg.SetSource(Global.InputManager.MovieOutputHardpoint);
if (!lg.IsEmpty)
{
LatchInputFromPlayer(Global.MovieInputSourceAdapter);
Movie.PokeFrame(Global.Emulator.Frame, Global.MovieOutputHardpoint);
LatchInputFromPlayer(Global.InputManager.MovieInputSourceAdapter);
Movie.PokeFrame(Global.Emulator.Frame, Global.InputManager.MovieOutputHardpoint);
}
else
{
@ -305,17 +305,17 @@ namespace BizHawk.Client.Common
{
if (MultiTrack.IsActive)
{
LatchMultitrackPlayerInput(Global.MultitrackRewiringAdapter);
LatchMultitrackPlayerInput(Global.InputManager.MultitrackRewiringAdapter);
}
else
{
LatchInputFromPlayer(Global.MovieInputSourceAdapter);
LatchInputFromPlayer(Global.InputManager.MovieInputSourceAdapter);
}
}
// the movie session makes sure that the correct input has been read and merged to its MovieControllerAdapter;
// this has been wired to Global.MovieOutputHardpoint in RewireInputChain
Movie.RecordFrame(Global.Emulator.Frame, Global.MovieOutputHardpoint);
Movie.RecordFrame(Global.Emulator.Frame, Global.InputManager.MovieOutputHardpoint);
}
public void HandleMovieAfterFrameLoop()
@ -368,7 +368,7 @@ namespace BizHawk.Client.Common
}
else if (Movie.IsFinished())
{
LatchInputFromPlayer(Global.MovieInputSourceAdapter);
LatchInputFromPlayer(Global.InputManager.MovieInputSourceAdapter);
}
}
else
@ -457,12 +457,12 @@ namespace BizHawk.Client.Common
// Note: this populates MovieControllerAdapter's Type with the appropriate controller
// Don't set it to a movie instance of the adapter or you will lose the definition!
InputManager.RewireInputChain();
Global.InputManager.RewireInputChain();
if (!record && emulator.SystemId == "NES") // For NES we need special logic since the movie will drive which core to load
{
var quicknesName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(QuickNES), typeof(CoreAttribute))).CoreName;
var neshawkName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(NES), typeof(CoreAttribute))).CoreName;
var quicknesName = typeof(QuickNES).CoreName();
var neshawkName = typeof(NES).CoreName();
// If either is specified use that, else use whatever is currently set
if (movie.Core == quicknesName)
@ -478,10 +478,10 @@ namespace BizHawk.Client.Common
}
else if (!record && emulator.SystemId == "SNES") // ditto with snes9x vs bsnes
{
var snes9xName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(Snes9x), typeof(CoreAttribute))).CoreName;
var bsnesName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(LibsnesCore), typeof(CoreAttribute))).CoreName;
var snes9XName = typeof(Snes9x).CoreName();
var bsnesName = typeof(LibsnesCore).CoreName();
if (movie.Core == snes9xName)
if (movie.Core == snes9XName)
{
PreviousSnesInSnes9x = Global.Config.SnesInSnes9x;
Global.Config.SnesInSnes9x = true;
@ -494,8 +494,8 @@ namespace BizHawk.Client.Common
}
else if (!record && emulator.SystemId == "GBA") // ditto with GBA, we should probably architect this at some point, this isn't sustainable
{
var mGBAName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(MGBAHawk), typeof(CoreAttribute))).CoreName;
var vbaNextName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(VBANext), typeof(CoreAttribute))).CoreName;
var mGBAName = typeof(MGBAHawk).CoreName();
var vbaNextName = typeof(VBANext).CoreName();
if (movie.Core == mGBAName)
{
@ -510,8 +510,8 @@ namespace BizHawk.Client.Common
}
else if (!record && (emulator.SystemId == "GB" || emulator.SystemId == "GBC"))
{
var gbHawkName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(GBHawk), typeof(CoreAttribute))).CoreName;
var gambatteName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(Gameboy), typeof(CoreAttribute))).CoreName;
var gbHawkName = typeof(GBHawk).CoreName();
var gambatteName = typeof(Gameboy).CoreName();
if (movie.Core == gbHawkName)
{

View File

@ -80,12 +80,7 @@ namespace BizHawk.Client.Common
get
{
var key = systemId + (pal ? "_PAL" : "");
if (Rates.ContainsKey(key))
{
return Rates[key];
}
return 60.0;
return Rates.ContainsKey(key) ? Rates[key] : 60.0;
}
}
@ -102,12 +97,12 @@ namespace BizHawk.Client.Common
private double Fps(IMovie movie)
{
var system = movie.HeaderEntries[HeaderKeys.PLATFORM];
var core = movie.HeaderEntries[HeaderKeys.CORE];
var pal = movie.HeaderEntries.ContainsKey(HeaderKeys.PAL)
&& movie.HeaderEntries[HeaderKeys.PAL] == "1";
var system = movie.HeaderEntries[HeaderKeys.Platform];
var core = movie.HeaderEntries[HeaderKeys.Core];
var pal = movie.HeaderEntries.ContainsKey(HeaderKeys.Pal)
&& movie.HeaderEntries[HeaderKeys.Pal] == "1";
if (movie.HeaderEntries.ContainsKey(HeaderKeys.CYCLECOUNT) && core == "Gambatte")
if (movie.HeaderEntries.ContainsKey(HeaderKeys.CycleCount) && core == "Gambatte")
{
system = "GB_Clock";
}

View File

@ -77,7 +77,7 @@ namespace BizHawk.Client.Common
{
int index = 1;
var sb = new StringBuilder();
List<Subtitle> subs = new List<Subtitle>();
var subs = new List<Subtitle>();
foreach (var subtitle in this)
{
subs.Add(new Subtitle(subtitle));

View File

@ -153,7 +153,7 @@ namespace BizHawk.Client.Common
{
foreach (var button in Definition.BoolButtons)
{
_myBoolButtons[button] = Global.AutofireStickyXORAdapter.IsSticky(button);
_myBoolButtons[button] = Global.InputManager.AutofireStickyXorAdapter.IsSticky(button);
}
// float controls don't have sticky logic, so latch default value

View File

@ -90,7 +90,7 @@ namespace BizHawk.Client.Common
{
int val;
int i = _source.Definition.FloatControls.IndexOf(button);
int mid = (int)_source.Definition.FloatRanges[i].Mid;
var mid = _source.Definition.FloatRanges[i].Mid;
if (createEmpty)
{

View File

@ -419,6 +419,11 @@ namespace BizHawk.Client.Common
["IV"] = '4',
["V"] = '5',
["VI"] = '6',
},
["NDS"] = new Dictionary<string, char>
{
["Lid"] = 'P',
["Touch"] = 'T'
}
};
}

View File

@ -30,75 +30,57 @@ namespace BizHawk.Client.Common
{
get
{
if (!Header.ContainsKey(HeaderKeys.RERECORDS))
if (!Header.ContainsKey(HeaderKeys.Rerecords))
{
Header[HeaderKeys.RERECORDS] = "0";
Header[HeaderKeys.Rerecords] = "0";
}
return ulong.Parse(Header[HeaderKeys.RERECORDS]);
return ulong.Parse(Header[HeaderKeys.Rerecords]);
}
set
{
if (Header[HeaderKeys.RERECORDS] != value.ToString())
if (Header[HeaderKeys.Rerecords] != value.ToString())
{
Changes = true;
Header[HeaderKeys.RERECORDS] = value.ToString();
Header[HeaderKeys.Rerecords] = value.ToString();
}
}
}
public bool StartsFromSavestate
{
get
{
if (Header.ContainsKey(HeaderKeys.STARTSFROMSAVESTATE))
{
return bool.Parse(Header[HeaderKeys.STARTSFROMSAVESTATE]);
}
return false;
}
get => Header.ContainsKey(HeaderKeys.StartsFromSavestate) && bool.Parse(Header[HeaderKeys.StartsFromSavestate]);
set
{
if (value)
{
Header[HeaderKeys.STARTSFROMSAVESTATE] = "True";
Header[HeaderKeys.StartsFromSavestate] = "True";
}
else
{
Header.Remove(HeaderKeys.STARTSFROMSAVESTATE);
Header.Remove(HeaderKeys.StartsFromSavestate);
}
}
}
public bool StartsFromSaveRam
{
get
{
if (Header.ContainsKey(HeaderKeys.STARTSFROMSAVERAM))
{
return bool.Parse(Header[HeaderKeys.STARTSFROMSAVERAM]);
}
return false;
}
get => Header.ContainsKey(HeaderKeys.StartsFromSaveram) && bool.Parse(Header[HeaderKeys.StartsFromSaveram]);
set
{
if (value)
{
if (!Header.ContainsKey(HeaderKeys.STARTSFROMSAVERAM))
if (!Header.ContainsKey(HeaderKeys.StartsFromSaveram))
{
Header.Add(HeaderKeys.STARTSFROMSAVERAM, "True");
Header.Add(HeaderKeys.StartsFromSaveram, "True");
}
}
else
{
if (Header.ContainsKey(HeaderKeys.STARTSFROMSAVERAM))
if (Header.ContainsKey(HeaderKeys.StartsFromSaveram))
{
Header.Remove(HeaderKeys.STARTSFROMSAVERAM);
Header.Remove(HeaderKeys.StartsFromSaveram);
}
}
}
@ -106,122 +88,104 @@ namespace BizHawk.Client.Common
public string GameName
{
get
{
if (Header.ContainsKey(HeaderKeys.GAMENAME))
{
return Header[HeaderKeys.GAMENAME];
}
return "";
}
get => Header.ContainsKey(HeaderKeys.GameName) ? Header[HeaderKeys.GameName] : "";
set
{
if (Header[HeaderKeys.GAMENAME] != value)
if (Header[HeaderKeys.GameName] != value)
{
Changes = true;
Header[HeaderKeys.GAMENAME] = value;
Header[HeaderKeys.GameName] = value;
}
}
}
public string SystemID
{
get
{
if (Header.ContainsKey(HeaderKeys.PLATFORM))
{
return Header[HeaderKeys.PLATFORM];
}
return "";
}
get => Header.ContainsKey(HeaderKeys.Platform) ? Header[HeaderKeys.Platform] : "";
set
{
if (Header[HeaderKeys.PLATFORM] != value)
if (Header[HeaderKeys.Platform] != value)
{
Changes = true;
Header[HeaderKeys.PLATFORM] = value;
Header[HeaderKeys.Platform] = value;
}
}
}
public string Hash
{
get => Header[HeaderKeys.SHA1];
get => Header[HeaderKeys.Sha1];
set
{
if (Header[HeaderKeys.SHA1] != value)
if (Header[HeaderKeys.Sha1] != value)
{
Changes = true;
Header[HeaderKeys.SHA1] = value;
Header[HeaderKeys.Sha1] = value;
}
}
}
public string Author
{
get => Header[HeaderKeys.AUTHOR];
get => Header[HeaderKeys.Author];
set
{
if (Header[HeaderKeys.AUTHOR] != value)
if (Header[HeaderKeys.Author] != value)
{
Changes = true;
Header[HeaderKeys.AUTHOR] = value;
Header[HeaderKeys.Author] = value;
}
}
}
public string Core
{
get => Header[HeaderKeys.CORE];
get => Header[HeaderKeys.Core];
set
{
if (Header[HeaderKeys.CORE] != value)
if (Header[HeaderKeys.Core] != value)
{
Changes = true;
Header[HeaderKeys.CORE] = value;
Header[HeaderKeys.Core] = value;
}
}
}
public string BoardName
{
get => Header[HeaderKeys.BOARDNAME];
get => Header[HeaderKeys.BoardName];
set
{
if (Header[HeaderKeys.BOARDNAME] != value)
if (Header[HeaderKeys.BoardName] != value)
{
Changes = true;
Header[HeaderKeys.BOARDNAME] = value;
Header[HeaderKeys.BoardName] = value;
}
}
}
public string EmulatorVersion
{
get => Header[HeaderKeys.EMULATIONVERSION];
get => Header[HeaderKeys.EmulationVersion];
set
{
if (Header[HeaderKeys.EMULATIONVERSION] != value)
if (Header[HeaderKeys.EmulationVersion] != value)
{
Changes = true;
Header[HeaderKeys.EMULATIONVERSION] = value;
Header[HeaderKeys.EmulationVersion] = value;
}
}
}
public string FirmwareHash
{
get => Header[HeaderKeys.FIRMWARESHA1];
get => Header[HeaderKeys.FirmwareSha1];
set
{
if (Header[HeaderKeys.FIRMWARESHA1] != value)
if (Header[HeaderKeys.FirmwareSha1] != value)
{
Changes = true;
Header[HeaderKeys.FIRMWARESHA1] = value;
Header[HeaderKeys.FirmwareSha1] = value;
}
}
}
@ -230,7 +194,7 @@ namespace BizHawk.Client.Common
{
get
{
var offsetStr = Header[HeaderKeys.LOOPOFFSET];
var offsetStr = Header[HeaderKeys.LoopOffset];
if (!string.IsNullOrWhiteSpace(offsetStr))
{
return int.Parse(offsetStr);
@ -243,11 +207,11 @@ namespace BizHawk.Client.Common
{
if (value.HasValue)
{
Header[HeaderKeys.LOOPOFFSET] = value.ToString();
Header[HeaderKeys.LoopOffset] = value.ToString();
}
else
{
Header.Remove(HeaderKeys.LOOPOFFSET);
Header.Remove(HeaderKeys.LoopOffset);
}
}
}

View File

@ -20,7 +20,7 @@ namespace BizHawk.Client.Common
return;
}
var backupDir = PathManager.MakeAbsolutePath(Global.Config.PathEntries.MoviesBackupsPathFragment, null);
var backupDir = Global.Config.PathEntries.MovieBackupsAbsolutePath();
var backupName = Filename;
backupName = backupName.Insert(Filename.LastIndexOf("."), $".{DateTime.Now:yyyy-MM-dd HH.mm.ss}");
backupName = Path.Combine(backupDir, Path.GetFileName(backupName));
@ -169,17 +169,17 @@ namespace BizHawk.Client.Common
if (Global.Emulator is Emulation.Cores.Nintendo.SubNESHawk.SubNESHawk)
{
var _subnes = (Emulation.Cores.Nintendo.SubNESHawk.SubNESHawk)Global.Emulator;
Header[HeaderKeys.VBLANKCOUNT] = _subnes.VBL_CNT.ToString();
Header[HeaderKeys.VBlankCount] = _subnes.VBL_CNT.ToString();
}
else if (Global.Emulator is Emulation.Cores.Nintendo.Gameboy.Gameboy)
{
var _gameboy = (Emulation.Cores.Nintendo.Gameboy.Gameboy)Global.Emulator;
Header[HeaderKeys.CYCLECOUNT] = _gameboy.CycleCount.ToString();
Header[HeaderKeys.CycleCount] = _gameboy.CycleCount.ToString();
}
else if (Global.Emulator is Emulation.Cores.Nintendo.SubGBHawk.SubGBHawk)
{
var _subgb = (Emulation.Cores.Nintendo.SubGBHawk.SubGBHawk)Global.Emulator;
Header[HeaderKeys.VBLANKCOUNT] = _subgb.VBL_CNT.ToString();
Header[HeaderKeys.VBlankCount] = _subgb.VBL_CNT.ToString();
}
var file = new FileInfo(fn);

View File

@ -289,7 +289,7 @@ namespace BizHawk.Client.Common
protected void WriteRawInputLog(TextWriter writer)
{
var lg = new Bk2LogEntryGenerator(LogKey);
lg.SetSource(Global.MovieOutputHardpoint);
lg.SetSource(Global.InputManager.MovieOutputHardpoint);
writer.WriteLine(lg.GenerateLogKey());

View File

@ -21,7 +21,7 @@ namespace BizHawk.Client.Common
Mode = MovieMode.Inactive;
MakeBackup = true;
Header[HeaderKeys.MOVIEVERSION] = "BizHawk v2.0.0";
Header[HeaderKeys.MovieVersion] = "BizHawk v2.0.0";
Log = StringLogUtil.MakeStringLog();
}
@ -74,16 +74,16 @@ namespace BizHawk.Client.Common
{
get
{
if (Header.ContainsKey(HeaderKeys.VBLANKCOUNT))
if (Header.ContainsKey(HeaderKeys.VBlankCount))
{
return Convert.ToUInt64(Header[HeaderKeys.VBLANKCOUNT]);
return Convert.ToUInt64(Header[HeaderKeys.VBlankCount]);
}
else if (Header.ContainsKey(HeaderKeys.CYCLECOUNT))
else if (Header.ContainsKey(HeaderKeys.CycleCount))
{
var gambatteName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(Gameboy), typeof(CoreAttribute))).CoreName;
if (Header[HeaderKeys.CORE] == gambatteName)
if (Header[HeaderKeys.Core] == gambatteName)
{
return Convert.ToUInt64(Header[HeaderKeys.CYCLECOUNT]);
return Convert.ToUInt64(Header[HeaderKeys.CycleCount]);
}
}
return (ulong)Log.Count;

View File

@ -1,7 +1,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Security.AccessControl;
using BizHawk.Common;
namespace BizHawk.Client.Common
@ -10,9 +11,9 @@ namespace BizHawk.Client.Common
{
public static bool DefaultToDisk { get; set; }
public static bool DefaultToAWE { get; set; }
public static bool DefaultToAwe { get; set; }
/// <exception cref="InvalidOperationException"><see cref="DefaultToAWE"/> is <see langword="true"/> but not running on Windows host</exception>
/// <exception cref="InvalidOperationException"><see cref="DefaultToAwe"/> is <see langword="true"/> but not running on Windows host</exception>
public static IStringLog MakeStringLog()
{
if (DefaultToDisk)
@ -20,7 +21,7 @@ namespace BizHawk.Client.Common
return new StreamStringLog(true);
}
if (DefaultToAWE)
if (DefaultToAwe)
{
return OSTailoredCode.IsUnixHost
? throw new InvalidOperationException("logging to AWE is only available on Windows for now")
@ -51,7 +52,7 @@ namespace BizHawk.Client.Common
{
public IStringLog Clone()
{
ListStringLog ret = new ListStringLog();
var ret = new ListStringLog();
ret.AddRange(this);
return ret;
}
@ -67,29 +68,27 @@ namespace BizHawk.Client.Common
/// </summary>
internal class StreamStringLog : IStringLog
{
private readonly Stream stream;
private readonly Stream _stream;
private readonly List<long> _offsets = new List<long>();
private readonly BinaryWriter _bw;
private readonly BinaryReader _br;
private readonly bool _mDisk;
private long _cursor;
public StreamStringLog(bool disk)
{
_mDisk = disk;
if (disk)
{
var path = TempFileManager.GetTempFilename("movieOnDisk");
stream = new FileStream(path, FileMode.Create, System.Security.AccessControl.FileSystemRights.FullControl, FileShare.None, 4 * 1024, FileOptions.DeleteOnClose);
_stream = new FileStream(path, FileMode.Create, FileSystemRights.FullControl, FileShare.None, 4 * 1024, FileOptions.DeleteOnClose);
}
else
{
stream = new AWEMemoryStream();
_stream = new AWEMemoryStream();
}
_bw = new BinaryWriter(stream);
_br = new BinaryReader(stream);
_bw = new BinaryWriter(_stream);
_br = new BinaryReader(_stream);
}
public IStringLog Clone()
@ -105,22 +104,21 @@ namespace BizHawk.Client.Common
public void Dispose()
{
stream.Dispose();
_stream.Dispose();
}
public int Count => _offsets.Count;
public void Clear()
{
stream.SetLength(0);
_stream.SetLength(0);
_offsets.Clear();
_cursor = 0;
}
public void Add(string str)
{
stream.Position = stream.Length;
_offsets.Add(stream.Position);
_stream.Position = _stream.Length;
_offsets.Add(_stream.Position);
_bw.Write(str);
_bw.Flush();
}
@ -135,14 +133,14 @@ namespace BizHawk.Client.Common
{
get
{
stream.Position = _offsets[index];
_stream.Position = _offsets[index];
return _br.ReadString();
}
set
{
stream.Position = stream.Length;
_offsets[index] = stream.Position;
_stream.Position = _stream.Length;
_offsets[index] = _stream.Position;
_bw.Write(value);
_bw.Flush();
}
@ -150,8 +148,8 @@ namespace BizHawk.Client.Common
public void Insert(int index, string val)
{
stream.Position = stream.Length;
_offsets.Insert(index, stream.Position);
_stream.Position = _stream.Length;
_offsets.Insert(index, _stream.Position);
_bw.Write(val);
_bw.Flush();
}
@ -178,9 +176,9 @@ namespace BizHawk.Client.Common
private int _index = -1;
public string Current => Log[_index];
object System.Collections.IEnumerator.Current => Log[_index];
object IEnumerator.Current => Log[_index];
bool System.Collections.IEnumerator.MoveNext()
bool IEnumerator.MoveNext()
{
_index++;
if (_index >= Log.Count)
@ -192,7 +190,7 @@ namespace BizHawk.Client.Common
return true;
}
void System.Collections.IEnumerator.Reset() { _index = -1; }
void IEnumerator.Reset() { _index = -1; }
public void Dispose() { }
}
@ -202,7 +200,7 @@ namespace BizHawk.Client.Common
return new Enumerator { Log = this };
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
IEnumerator IEnumerable.GetEnumerator()
{
return new Enumerator { Log = this };
}

View File

@ -2,6 +2,7 @@
using System.IO;
using System.Linq;
using BizHawk.Common.PathExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Nintendo.Gameboy;
using BizHawk.Emulation.Cores.Nintendo.GBHawk;
@ -292,7 +293,7 @@ namespace BizHawk.Client.Common.MovieConversionExtensions
if (Global.Game != null)
{
movie.GameName = PathManager.FilesystemSafeName(Global.Game);
movie.GameName = Global.Game.FilesystemSafeName();
movie.Hash = Global.Game.Hash;
if (Global.Game.FirmwareHash != null)
{
@ -314,7 +315,7 @@ namespace BizHawk.Client.Common.MovieConversionExtensions
var region = Global.Emulator.AsRegionable().Region;
if (region == Emulation.Common.DisplayType.PAL)
{
movie.HeaderEntries.Add(HeaderKeys.PAL, "1");
movie.HeaderEntries.Add(HeaderKeys.Pal, "1");
}
}
@ -343,7 +344,7 @@ namespace BizHawk.Client.Common.MovieConversionExtensions
if (Global.Emulator is Gameboy)
{
movie.HeaderEntries.Add(HeaderKeys.CYCLECOUNT, "0");
movie.HeaderEntries.Add(HeaderKeys.CycleCount, "0");
}
if (Global.Emulator is SMS && ((SMS) Global.Emulator).IsSG1000)
@ -366,9 +367,9 @@ namespace BizHawk.Client.Common.MovieConversionExtensions
movie.HeaderEntries.Add("Is32X", "1");
}
if ((Global.Emulator is SubNESHawk) || (Global.Emulator is SubGBHawk))
if (Global.Emulator is SubNESHawk || Global.Emulator is SubGBHawk)
{
movie.HeaderEntries.Add(HeaderKeys.VBLANKCOUNT, "0");
movie.HeaderEntries.Add(HeaderKeys.VBlankCount, "0");
}
movie.Core = ((CoreAttribute)Attribute

View File

@ -18,7 +18,7 @@ namespace BizHawk.Client.Common.movie.import
protected override void RunImport()
{
var neshawkName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(NES), typeof(CoreAttribute))).CoreName;
Result.Movie.HeaderEntries[HeaderKeys.CORE] = neshawkName;
Result.Movie.HeaderEntries[HeaderKeys.Core] = neshawkName;
using var r = new BinaryReader(SourceFile.Open(FileMode.Open, FileAccess.Read));
var signature = new string(r.ReadChars(4));
@ -28,7 +28,7 @@ namespace BizHawk.Client.Common.movie.import
return;
}
Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = "NES";
Result.Movie.HeaderEntries[HeaderKeys.Platform] = "NES";
var syncSettings = new NES.NESSyncSettings();
@ -74,7 +74,7 @@ namespace BizHawk.Client.Common.movie.import
preference. This means that this site cannot calculate movie lengths reliably.
*/
bool pal = ((flags >> 2) & 0x1) != 0;
Result.Movie.HeaderEntries[HeaderKeys.PAL] = pal.ToString();
Result.Movie.HeaderEntries[HeaderKeys.Pal] = pal.ToString();
// other: reserved, set to 0
bool syncHack = ((flags >> 4) & 0x1) != 0;
@ -124,7 +124,7 @@ namespace BizHawk.Client.Common.movie.import
// Advance past null byte.
r.ReadByte();
string gameName = Encoding.UTF8.GetString(gameBytes.ToArray());
Result.Movie.HeaderEntries[HeaderKeys.GAMENAME] = gameName;
Result.Movie.HeaderEntries[HeaderKeys.GameName] = gameName;
/*
After the header comes "metadata", which is UTF8-coded movie title string. The metadata begins after the ROM
@ -140,7 +140,7 @@ namespace BizHawk.Client.Common.movie.import
// Advance past null byte.
r.ReadByte();
string author = Encoding.UTF8.GetString(authorBytes.ToArray());
Result.Movie.HeaderEntries[HeaderKeys.AUTHOR] = author;
Result.Movie.HeaderEntries[HeaderKeys.Author] = author;
// Advance to first byte of input data.
r.BaseStream.Position = firstFrameOffset;
@ -284,7 +284,7 @@ namespace BizHawk.Client.Common.movie.import
if (fds)
{
Result.Movie.HeaderEntries[HeaderKeys.BOARDNAME] = "FDS";
Result.Movie.HeaderEntries[HeaderKeys.BoardName] = "FDS";
}
syncSettings.Controls = controllerSettings;

View File

@ -15,8 +15,8 @@ namespace BizHawk.Client.Common
protected override void RunImport()
{
var neshawkName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(NES), typeof(CoreAttribute))).CoreName;
Result.Movie.HeaderEntries[HeaderKeys.CORE] = neshawkName;
var emulator = "FCEUX";
Result.Movie.HeaderEntries[HeaderKeys.Core] = neshawkName;
const string emulator = "FCEUX";
var platform = "NES"; // TODO: FDS?
var syncSettings = new NES.NESSyncSettings();
@ -30,7 +30,7 @@ namespace BizHawk.Client.Common
_deck = controllerSettings.Instantiate((x, y) => true);
AddDeckControlButtons();
Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = platform;
Result.Movie.HeaderEntries[HeaderKeys.Platform] = platform;
using var sr = SourceFile.OpenText();
string line;
@ -73,11 +73,11 @@ namespace BizHawk.Client.Common
}
else if (line.ToLower().StartsWith("romfilename"))
{
Result.Movie.HeaderEntries[HeaderKeys.GAMENAME] = ParseHeader(line, "romFilename");
Result.Movie.HeaderEntries[HeaderKeys.GameName] = ParseHeader(line, "romFilename");
}
else if (line.ToLower().StartsWith("cdgamename"))
{
Result.Movie.HeaderEntries[HeaderKeys.GAMENAME] = ParseHeader(line, "cdGameName");
Result.Movie.HeaderEntries[HeaderKeys.GameName] = ParseHeader(line, "cdGameName");
}
else if (line.ToLower().StartsWith("romchecksum"))
{
@ -94,7 +94,7 @@ namespace BizHawk.Client.Common
}
else if (line.ToLower().StartsWith("comment author"))
{
Result.Movie.HeaderEntries[HeaderKeys.AUTHOR] = ParseHeader(line, "comment author");
Result.Movie.HeaderEntries[HeaderKeys.Author] = ParseHeader(line, "comment author");
}
else if (line.ToLower().StartsWith("rerecordcount"))
{
@ -116,7 +116,7 @@ namespace BizHawk.Client.Common
}
else if (line.ToLower().StartsWith("palflag"))
{
Result.Movie.HeaderEntries[HeaderKeys.PAL] = ParseHeader(line, "palFlag");
Result.Movie.HeaderEntries[HeaderKeys.Pal] = ParseHeader(line, "palFlag");
}
else if (line.ToLower().StartsWith("port0"))
{

View File

@ -32,7 +32,7 @@ namespace BizHawk.Client.Common.movie.import
return;
}
Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = "NES";
Result.Movie.HeaderEntries[HeaderKeys.Platform] = "NES";
var syncSettings = new NES.NESSyncSettings();
// other bits: unknown, set to 0
@ -44,7 +44,7 @@ namespace BizHawk.Client.Common.movie.import
if (((flags >> 5) & 0x1) != 0)
{
fds = true;
Result.Movie.HeaderEntries[HeaderKeys.BOARDNAME] = "FDS";
Result.Movie.HeaderEntries[HeaderKeys.BoardName] = "FDS";
}
else
{

View File

@ -21,7 +21,7 @@ namespace BizHawk.Client.Common.movie.import
return;
}
Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = "GEN";
Result.Movie.HeaderEntries[HeaderKeys.Platform] = "GEN";
// 00F ASCII-encoded GMV file format version. The most recent is 'A'. (?)
string version = new string(r.ReadChars(1));
@ -48,7 +48,7 @@ namespace BizHawk.Client.Common.movie.import
header.
*/
bool pal = ((flags >> 7) & 0x1) != 0;
Result.Movie.HeaderEntries[HeaderKeys.PAL] = pal.ToString();
Result.Movie.HeaderEntries[HeaderKeys.Pal] = pal.ToString();
// bit 6: if "1", movie requires a savestate.
if (((flags >> 6) & 0x1) != 0)

View File

@ -18,7 +18,7 @@ namespace BizHawk.Client.Common.movie.import
protected override void RunImport()
{
var bsnesName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(LibsnesCore), typeof(CoreAttribute))).CoreName;
Result.Movie.HeaderEntries[HeaderKeys.CORE] = bsnesName;
Result.Movie.HeaderEntries[HeaderKeys.Core] = bsnesName;
var hf = new HawkFile(SourceFile.FullName);
@ -77,7 +77,7 @@ namespace BizHawk.Client.Common.movie.import
authorList += authorLast;
}
Result.Movie.HeaderEntries[HeaderKeys.AUTHOR] = authorList;
Result.Movie.HeaderEntries[HeaderKeys.Author] = authorList;
hf.Unbind();
}
else if (item.Name == "coreversion")
@ -93,7 +93,7 @@ namespace BizHawk.Client.Common.movie.import
hf.BindArchiveMember(item.Index);
var stream = hf.GetStream();
string gameName = Encoding.UTF8.GetString(stream.ReadAllBytes()).Trim();
Result.Movie.HeaderEntries[HeaderKeys.GAMENAME] = gameName;
Result.Movie.HeaderEntries[HeaderKeys.GameName] = gameName;
hf.Unbind();
}
else if (item.Name == "gametype")
@ -120,7 +120,7 @@ namespace BizHawk.Client.Common.movie.import
}
bool pal = gametype == "snes_pal" || gametype == "sgb_pal";
Result.Movie.HeaderEntries[HeaderKeys.PAL] = pal.ToString();
Result.Movie.HeaderEntries[HeaderKeys.Pal] = pal.ToString();
hf.Unbind();
}
else if (item.Name == "input")
@ -281,7 +281,7 @@ namespace BizHawk.Client.Common.movie.import
}
}
Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = platform;
Result.Movie.HeaderEntries[HeaderKeys.Platform] = platform;
Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(ss);
Global.Config.SnesInSnes9x = false; // This could be annoying to a user if they don't notice we set this preference, but the alternative is for the movie import to fail to load the movie
}
@ -342,14 +342,10 @@ namespace BizHawk.Client.Common.movie.import
}
// LSNES frames don't start or end with a |.
int start = 1;
int end = sections.Length;
int playerOffset = 0;
for (int section = start; section < end; section++)
for (int player = 1; player < end; player++)
{
// The player number is one less than the section number for the reasons explained above.
int player = section + playerOffset;
string prefix = $"P{player} ";
// Gameboy doesn't currently have a prefix saying which player the input is for.
@ -360,12 +356,12 @@ namespace BizHawk.Client.Common.movie.import
// Only count lines with that have the right number of buttons and are for valid players.
if (
sections[section].Length == buttons.Length)
sections[player].Length == buttons.Length)
{
for (int button = 0; button < buttons.Length; button++)
{
// Consider the button pressed so long as its spot is not occupied by a ".".
controllers[prefix + buttons[button]] = sections[section][button] != '.';
controllers[prefix + buttons[button]] = sections[player][button] != '.';
}
}
}

View File

@ -27,7 +27,7 @@ namespace BizHawk.Client.Common.movie.import
ss.Port4,
ss.Port5);
Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = "PCE";
Result.Movie.HeaderEntries[HeaderKeys.Platform] = "PCE";
using var sr = SourceFile.OpenText();
string line;
@ -84,7 +84,7 @@ namespace BizHawk.Client.Common.movie.import
}
else if (line.ToLower().StartsWith("pcecd"))
{
Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = "PCECD";
Result.Movie.HeaderEntries[HeaderKeys.Platform] = "PCECD";
}
else if (line.ToLower().StartsWith("emuversion"))
{
@ -97,15 +97,15 @@ namespace BizHawk.Client.Common.movie.import
}
else if (line.ToLower().StartsWith("romfilename"))
{
Result.Movie.HeaderEntries[HeaderKeys.GAMENAME] = ParseHeader(line, "romFilename");
Result.Movie.HeaderEntries[HeaderKeys.GameName] = ParseHeader(line, "romFilename");
}
else if (line.ToLower().StartsWith("cdgamename"))
{
Result.Movie.HeaderEntries[HeaderKeys.GAMENAME] = ParseHeader(line, "cdGameName");
Result.Movie.HeaderEntries[HeaderKeys.GameName] = ParseHeader(line, "cdGameName");
}
else if (line.ToLower().StartsWith("comment author"))
{
Result.Movie.HeaderEntries[HeaderKeys.AUTHOR] = ParseHeader(line, "comment author");
Result.Movie.HeaderEntries[HeaderKeys.Author] = ParseHeader(line, "comment author");
}
else if (line.ToLower().StartsWith("rerecordcount"))
{
@ -155,14 +155,12 @@ namespace BizHawk.Client.Common.movie.import
Skip the first two sections of the split, which consist of everything before the starting | and the command.
Do not use the section after the last |. In other words, get the sections for the players.
*/
int start = 2;
int end = sections.Length - 1;
int playerOffset = -1;
for (int section = start; section < end; section++)
for (int section = 2; section < end; section++)
{
// The player number is one less than the section number for the reasons explained above.
int player = section + playerOffset;
int player = section - 1;
string prefix = $"P{player} ";
// Only count lines with that have the right number of buttons and are for valid players.

View File

@ -54,7 +54,7 @@ namespace BizHawk.Client.Common.movie.import
// 0020-005f: string: author info (UTF-8)
string author = NullTerminated(new string(r.ReadChars(64)));
Result.Movie.HeaderEntries[HeaderKeys.AUTHOR] = author;
Result.Movie.HeaderEntries[HeaderKeys.Author] = author;
// 0060: 4-byte little endian flags
byte flags = r.ReadByte();
@ -62,7 +62,7 @@ namespace BizHawk.Client.Common.movie.import
// bit 0: unused
// bit 1: "PAL"
bool pal = ((flags >> 1) & 0x1) != 0;
Result.Movie.HeaderEntries[HeaderKeys.PAL] = pal.ToString();
Result.Movie.HeaderEntries[HeaderKeys.Pal] = pal.ToString();
// bit 2: Japan
bool japan = ((flags >> 2) & 0x1) != 0;
@ -80,14 +80,14 @@ namespace BizHawk.Client.Common.movie.import
isGameGear = false;
}
Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = "SMS"; // System Id is still SMS even if game gear
Result.Movie.HeaderEntries[HeaderKeys.Platform] = "SMS"; // System Id is still SMS even if game gear
// bits 4-31: unused
r.ReadBytes(3);
// 0064-00e3: string: rom name (ASCII)
string gameName = NullTerminated(new string(r.ReadChars(128)));
Result.Movie.HeaderEntries[HeaderKeys.GAMENAME] = gameName;
Result.Movie.HeaderEntries[HeaderKeys.GameName] = gameName;
// 00e4-00f3: binary: rom MD5 digest
byte[] md5 = r.ReadBytes(16);
@ -135,7 +135,7 @@ namespace BizHawk.Client.Common.movie.import
if (player == 1)
{
controllers["Pause"] =
(((controllerState >> 6) & 0x1) != 0 && (!isGameGear))
(((controllerState >> 6) & 0x1) != 0 && !isGameGear)
|| (((controllerState >> 7) & 0x1) != 0 && isGameGear);
}
}

View File

@ -45,17 +45,14 @@ namespace BizHawk.Client.Common
.GetConstructor(new Type[] { })
?.Invoke(new object[] { });
if (importer == null)
{
return ImportResult.Error($"No importer found for file type {ext}");
}
return importer.Import(path);
return importer == null
? ImportResult.Error($"No importer found for file type {ext}")
: importer.Import(path);
}
private static Type ImporterForExtension(string ext)
{
return Importers.FirstOrDefault(i => string.Equals(i.Value.Extension, ext, StringComparison.OrdinalIgnoreCase)).Key;
return Importers.First(i => string.Equals(i.Value.Extension, ext, StringComparison.OrdinalIgnoreCase)).Key;
}
}
}

View File

@ -9,7 +9,7 @@ namespace BizHawk.Client.Common
{
protected override void RunImport()
{
Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = "PSX";
Result.Movie.HeaderEntries[HeaderKeys.Platform] = "PSX";
using var fs = SourceFile.OpenRead();
using var br = new BinaryReader(fs);
@ -56,7 +56,7 @@ namespace BizHawk.Client.Common
if ((flags & 0x04) != 0)
{
movie.HeaderEntries[HeaderKeys.PAL] = "1";
movie.HeaderEntries[HeaderKeys.Pal] = "1";
}
if ((flags & 0x08) != 0)
@ -145,7 +145,7 @@ namespace BizHawk.Client.Common
info.FrameCount = br.ReadUInt32();
uint rerecordCount = br.ReadUInt32();
movie.HeaderEntries[HeaderKeys.RERECORDS] = rerecordCount.ToString();
movie.HeaderEntries[HeaderKeys.Rerecords] = rerecordCount.ToString();
// 018: UInt32 savestateOffset
// 01C: UInt32 memoryCard1Offset
@ -162,7 +162,7 @@ namespace BizHawk.Client.Common
uint authorNameLength = br.ReadUInt32();
char[] authorName = br.ReadChars((int)authorNameLength);
movie.HeaderEntries[HeaderKeys.AUTHOR] = new string(authorName);
movie.HeaderEntries[HeaderKeys.Author] = new string(authorName);
info.ParseSuccessful = true;
return info;

View File

@ -15,7 +15,7 @@ namespace BizHawk.Client.Common.Movie.Import
protected override void RunImport()
{
var movie = Result.Movie;
movie.HeaderEntries[HeaderKeys.PLATFORM] = "PSX";
movie.HeaderEntries[HeaderKeys.Platform] = "PSX";
using var fs = SourceFile.OpenRead();
using var br = new BinaryReader(fs);

View File

@ -17,7 +17,7 @@ namespace BizHawk.Client.Common.movie.import
protected override void RunImport()
{
var bsnesName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(LibsnesCore), typeof(CoreAttribute))).CoreName;
Result.Movie.HeaderEntries[HeaderKeys.CORE] = bsnesName;
Result.Movie.HeaderEntries[HeaderKeys.Core] = bsnesName;
using var fs = SourceFile.Open(FileMode.Open, FileAccess.Read);
using var r = new BinaryReader(fs);
@ -30,26 +30,17 @@ namespace BizHawk.Client.Common.movie.import
return;
}
Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = "SNES";
Result.Movie.HeaderEntries[HeaderKeys.Platform] = "SNES";
// 004 4-byte little-endian unsigned int: version number
uint versionNumber = r.ReadUInt32();
string version;
switch (versionNumber)
var version = versionNumber switch
{
case 1:
version = "1.43";
break;
case 4:
version = "1.51";
break;
case 5:
version = "1.52";
break;
default:
version = "Unknown";
break;
}
1 => "1.43",
4 => "1.51",
5 => "1.52",
_ => "Unknown"
};
Result.Movie.Comments.Add($"{EmulationOrigin} Snes9x version {version}");
Result.Movie.Comments.Add($"{MovieOrigin} .SMV");
@ -125,7 +116,7 @@ namespace BizHawk.Client.Common.movie.import
// bit 1: if "0", movie is NTSC (60 fps); if "1", movie is PAL (50 fps)
bool pal = ((movieFlags >> 1) & 0x1) != 0;
Result.Movie.HeaderEntries[HeaderKeys.PAL] = pal.ToString();
Result.Movie.HeaderEntries[HeaderKeys.Pal] = pal.ToString();
// other: reserved, set to 0
/*
@ -154,7 +145,7 @@ namespace BizHawk.Client.Common.movie.import
Extra ROM info is always positioned right before the savestate. Its size is 30 bytes if MOVIE_SYNC_HASROMINFO
is used (and MOVIE_SYNC_DATA_EXISTS is set), 0 bytes otherwise.
*/
int extraRomInfo = (((syncFlags >> 6) & 0x1) != 0 && (syncFlags & 0x1) != 0) ? 30 : 0;
int extraRomInfo = ((syncFlags >> 6) & 0x1) != 0 && (syncFlags & 0x1) != 0 ? 30 : 0;
// 018 4-byte little-endian unsigned int: offset to the savestate inside file
uint savestateOffset = r.ReadUInt32();
@ -190,11 +181,11 @@ namespace BizHawk.Client.Common.movie.import
from position 32 (0x20 (0x40 for 1.51 and up)) and ends at <savestate_offset -
length_of_extra_rom_info_in_bytes>.
*/
byte[] metadata = r.ReadBytes((int)(savestateOffset - extraRomInfo - ((version != "1.43") ? 0x40 : 0x20)));
byte[] metadata = r.ReadBytes((int)(savestateOffset - extraRomInfo - (version != "1.43" ? 0x40 : 0x20)));
string author = NullTerminated(Encoding.Unicode.GetString(metadata).Trim());
if (!string.IsNullOrWhiteSpace(author))
{
Result.Movie.HeaderEntries[HeaderKeys.AUTHOR] = author;
Result.Movie.HeaderEntries[HeaderKeys.Author] = author;
}
if (extraRomInfo == 30)
@ -206,7 +197,7 @@ namespace BizHawk.Client.Common.movie.import
// the game name copied from the ROM, truncated to 23 bytes (the game name in the ROM is 21 bytes)
string gameName = NullTerminated(Encoding.UTF8.GetString(r.ReadBytes(23)));
Result.Movie.HeaderEntries[HeaderKeys.GAMENAME] = gameName;
Result.Movie.HeaderEntries[HeaderKeys.GameName] = gameName;
}
SimpleController controllers = new SimpleController

View File

@ -3,6 +3,7 @@ using System.IO;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Nintendo.GBA;
using BizHawk.Emulation.Cores.Nintendo.GBHawk;
using BizHawk.Emulation.Cores.Nintendo.Gameboy;
namespace BizHawk.Client.Common.movie.import
{
@ -15,6 +16,7 @@ namespace BizHawk.Client.Common.movie.import
{
using var fs = SourceFile.Open(FileMode.Open, FileAccess.Read);
using var r = new BinaryReader(fs);
bool is_GBC = false;
// 000 4-byte signature: 56 42 4D 1A "VBM\x1A"
string signature = new string(r.ReadChars(4));
@ -113,12 +115,14 @@ namespace BizHawk.Client.Common.movie.import
{
platform = "GBA";
var mGBAName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(MGBAHawk), typeof(CoreAttribute))).CoreName;
Result.Movie.HeaderEntries[HeaderKeys.CORE] = mGBAName;
Result.Movie.HeaderEntries[HeaderKeys.Core] = mGBAName;
}
if (isGBC)
{
platform = "GBC";
is_GBC = true;
platform = "GB";
Result.Movie.HeaderEntries.Add("IsCGBMode", "1");
}
if (isSGB)
@ -126,7 +130,7 @@ namespace BizHawk.Client.Common.movie.import
Result.Errors.Add("SGB imports are not currently supported");
}
Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = platform;
Result.Movie.HeaderEntries[HeaderKeys.Platform] = platform;
// 017 1-byte flags: (values of some boolean emulator options)
flags = r.ReadByte();
@ -154,7 +158,7 @@ namespace BizHawk.Client.Common.movie.import
null-terminated (ASCII?)
*/
string gameName = NullTerminated(new string(r.ReadChars(12)));
Result.Movie.HeaderEntries[HeaderKeys.GAMENAME] = gameName;
Result.Movie.HeaderEntries[HeaderKeys.GameName] = gameName;
// 030 1-byte unsigned char: minor version/revision number of current VBM version, the latest is "1"
byte minorVersion = r.ReadByte();
@ -192,7 +196,7 @@ namespace BizHawk.Client.Common.movie.import
// After the header is 192 bytes of text. The first 64 of these 192 bytes are for the author's name (or names).
string author = NullTerminated(new string(r.ReadChars(64)));
Result.Movie.HeaderEntries[HeaderKeys.AUTHOR] = author;
Result.Movie.HeaderEntries[HeaderKeys.Author] = author;
// The following 128 bytes are for a description of the movie. Both parts must be null-terminated.
string movieDescription = NullTerminated(new string(r.ReadChars(128)));
@ -276,13 +280,24 @@ namespace BizHawk.Client.Common.movie.import
}
else
{
Global.Config.GbUseGbHawk = true;
Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(new GBHawk.GBSyncSettings());
}
if (Global.Config.GbUseGbHawk || Global.Config.UseSubGBHawk)
{
var temp_sync = new GBHawk.GBSyncSettings();
if (is_GBC) { temp_sync.ConsoleMode = GBHawk.GBSyncSettings.ConsoleModeType.GBC; }
else { temp_sync.ConsoleMode = GBHawk.GBSyncSettings.ConsoleModeType.GB; }
Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(temp_sync);
}
else
{
var temp_sync = new Gameboy.GambatteSyncSettings();
if (is_GBC) { temp_sync.ConsoleMode = Gameboy.GambatteSyncSettings.ConsoleModeType.GBC; }
else { temp_sync.ConsoleMode = Gameboy.GambatteSyncSettings.ConsoleModeType.GB; }
Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(temp_sync);
}
}
}
private SimpleController GbController()
private static SimpleController GbController()
{
return new SimpleController
{
@ -293,9 +308,7 @@ namespace BizHawk.Client.Common.movie.import
};
}
private SimpleController GbaController()
{
return new SimpleController { Definition = GBA.GBAController };
}
private static SimpleController GbaController()
=> new SimpleController { Definition = GBA.GBAController };
}
}

View File

@ -13,7 +13,7 @@ namespace BizHawk.Client.Common.movie.import
{
protected override void RunImport()
{
Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = "SAT";
Result.Movie.HeaderEntries[HeaderKeys.Platform] = "SAT";
var ss = new Saturnus.SyncSettings
{
Port1 = SaturnusControllerDeck.Device.Gamepad,
@ -44,7 +44,7 @@ namespace BizHawk.Client.Common.movie.import
}
else if (line.ToLower().StartsWith("cdGameName"))
{
Result.Movie.HeaderEntries[HeaderKeys.GAMENAME] = ParseHeader(line, "romFilename");
Result.Movie.HeaderEntries[HeaderKeys.GameName] = ParseHeader(line, "romFilename");
}
else if (line.ToLower().StartsWith("rerecordcount"))
{
@ -73,7 +73,7 @@ namespace BizHawk.Client.Common.movie.import
else if (line.ToLower().StartsWith("ispal"))
{
bool pal = ParseHeader(line, "isPal") == "1";
Result.Movie.HeaderEntries[HeaderKeys.PAL] = pal.ToString();
Result.Movie.HeaderEntries[HeaderKeys.Pal] = pal.ToString();
}
else
{

View File

@ -28,83 +28,53 @@ namespace BizHawk.Client.Common
/// </summary>
public void SetControllersAsMnemonic(string mnemonic)
{
if (ControlType == "Null Controller")
switch (ControlType)
{
return;
}
if (ControlType == "Lynx Controller")
{
SetLynxControllersAsMnemonic(mnemonic);
return;
}
if (ControlType == "SNES Controller")
{
SetSNESControllersAsMnemonic(mnemonic);
return;
}
if (ControlType == "Commodore 64 Controller")
{
SetC64ControllersAsMnemonic(mnemonic);
return;
}
if (ControlType == "GBA Controller")
{
SetGBAControllersAsMnemonic(mnemonic);
return;
}
if (ControlType == "Atari 7800 ProLine Joystick Controller")
{
SetAtari7800AsMnemonic(mnemonic);
return;
}
if (ControlType == "Dual Gameboy Controller")
{
SetDualGameBoyControllerAsMnemonic(mnemonic);
return;
}
if (ControlType == "WonderSwan Controller")
{
SetWonderSwanControllerAsMnemonic(mnemonic);
return;
}
if (ControlType == "Nintendo 64 Controller")
{
SetN64ControllersAsMnemonic(mnemonic);
return;
}
if (ControlType == "Saturn Controller")
{
SetSaturnControllersAsMnemonic(mnemonic);
return;
}
if (ControlType == "PSP Controller")
{
// TODO
return;
}
if (ControlType == "GPGX Genesis Controller")
{
if (IsGenesis6Button())
case "Null Controller":
return;
case "Lynx Controller":
SetLynxControllersAsMnemonic(mnemonic);
return;
case "SNES Controller":
SetSNESControllersAsMnemonic(mnemonic);
return;
case "Commodore 64 Controller":
SetC64ControllersAsMnemonic(mnemonic);
return;
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;
case "PSP Controller":
// TODO
return;
case "GPGX Genesis Controller":
{
SetGenesis6ControllersAsMnemonic(mnemonic);
}
else
{
SetGenesis3ControllersAsMnemonic(mnemonic);
}
if (IsGenesis6Button())
{
SetGenesis6ControllersAsMnemonic(mnemonic);
}
else
{
SetGenesis3ControllersAsMnemonic(mnemonic);
}
return;
return;
}
}
var c = new MnemonicChecker(mnemonic);
@ -118,41 +88,42 @@ namespace BizHawk.Client.Common
{
return;
}
else if (mnemonic[1] == 'P')
switch (mnemonic[1])
{
Force("Power", true);
}
else if (mnemonic[1] == 'E')
{
Force("FDS Eject", true);
}
else if (mnemonic[1] == '0')
{
Force("FDS Insert 0", true);
}
else if (mnemonic[1] == '1')
{
Force("FDS Insert 1", true);
}
else if (mnemonic[1] == '2')
{
Force("FDS Insert 2", true);
}
else if (mnemonic[1] == '3')
{
Force("FDS Insert 3", true);
}
else if (mnemonic[1] == 'c')
{
Force("VS Coin 1", true);
}
else if (mnemonic[1] == 'C')
{
Force("VS Coin 2", true);
}
else if (mnemonic[1] != '.')
{
Force("Reset", true);
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;
}
}
}
@ -195,9 +166,9 @@ namespace BizHawk.Client.Common
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcindex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
int ctr = start;
if (mnemonic.Length < srcindex + ctr + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
if (mnemonic.Length < srcIndex + ctr + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
{
return;
}
@ -210,17 +181,17 @@ namespace BizHawk.Client.Common
foreach (string button in BkmMnemonicConstants.Buttons[ControlType].Keys)
{
Force(prefix + button, c[srcindex + ctr++]);
Force(prefix + button, c[srcIndex + ctr++]);
}
}
if (ControlType == "SMS Controller")
{
int srcindex = BkmMnemonicConstants.Players[ControlType] * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
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(command, c[srcIndex + ctr++]);
}
}
}
@ -228,10 +199,7 @@ namespace BizHawk.Client.Common
private readonly WorkingDictionary<string, bool> _myBoolButtons = new WorkingDictionary<string, bool>();
private readonly WorkingDictionary<string, float> _myFloatControls = new WorkingDictionary<string, float>();
private bool IsGenesis6Button()
{
return Definition.BoolButtons.Contains("P1 X");
}
private bool IsGenesis6Button() => Definition.BoolButtons.Contains("P1 X");
private void Force(string button, bool state)
{
@ -247,7 +215,7 @@ namespace BizHawk.Client.Common
private void SetGBAControllersAsMnemonic(string mnemonic)
{
MnemonicChecker c = new MnemonicChecker(mnemonic);
var c = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
if (mnemonic.Length < 2)
{
@ -268,7 +236,7 @@ namespace BizHawk.Client.Common
private void SetGenesis6ControllersAsMnemonic(string mnemonic)
{
MnemonicChecker c = new MnemonicChecker(mnemonic);
var c = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
if (mnemonic.Length < 2)
@ -292,9 +260,9 @@ namespace BizHawk.Client.Common
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcindex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
if (mnemonic.Length < srcindex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
{
return;
}
@ -302,14 +270,14 @@ namespace BizHawk.Client.Common
int start = 3;
foreach (string button in BkmMnemonicConstants.Buttons[ControlType].Keys)
{
Force($"P{player} {button}", c[srcindex + start++]);
Force($"P{player} {button}", c[srcIndex + start++]);
}
}
}
private void SetGenesis3ControllersAsMnemonic(string mnemonic)
{
MnemonicChecker c = new MnemonicChecker(mnemonic);
var c = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
if (mnemonic.Length < 2)
@ -333,9 +301,9 @@ namespace BizHawk.Client.Common
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcindex = (player - 1) * (BkmMnemonicConstants.Buttons["GPGX 3-Button Controller"].Count + 1);
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)
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons["GPGX 3-Button Controller"].Count - 1)
{
return;
}
@ -343,7 +311,7 @@ namespace BizHawk.Client.Common
int start = 3;
foreach (string button in BkmMnemonicConstants.Buttons["GPGX 3-Button Controller"].Keys)
{
Force($"P{player} {button}", c[srcindex + start++]);
Force($"P{player} {button}", c[srcIndex + start++]);
}
}
}
@ -369,9 +337,9 @@ namespace BizHawk.Client.Common
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcindex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
if (mnemonic.Length < srcindex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
{
return;
}
@ -379,7 +347,7 @@ namespace BizHawk.Client.Common
int start = 3;
foreach (var button in BkmMnemonicConstants.Buttons[ControlType].Keys)
{
Force($"P{player} {button}", c[srcindex + start++]);
Force($"P{player} {button}", c[srcIndex + start++]);
}
}
}
@ -401,9 +369,9 @@ namespace BizHawk.Client.Common
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcindex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
if (mnemonic.Length < srcindex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
{
return;
}
@ -411,14 +379,14 @@ namespace BizHawk.Client.Common
int start = 3;
foreach (var button in BkmMnemonicConstants.Buttons[ControlType].Keys)
{
Force(button, c[srcindex + start++]);
Force(button, c[srcIndex + start++]);
}
}
}
private void SetN64ControllersAsMnemonic(string mnemonic)
{
MnemonicChecker c = new MnemonicChecker(mnemonic);
var c = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
if (mnemonic.Length < 2)
@ -437,9 +405,9 @@ namespace BizHawk.Client.Common
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcindex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + (BkmMnemonicConstants.Analogs[ControlType].Count * 4) + 1 + 1);
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)
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
{
return;
}
@ -447,12 +415,12 @@ namespace BizHawk.Client.Common
int start = 3;
foreach (string button in BkmMnemonicConstants.Buttons[ControlType].Keys)
{
Force($"P{player} {button}", c[srcindex + start++]);
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)));
Force($"P{player} {name}", int.Parse(mnemonic.Substring(srcIndex + start, 4)));
start += 5;
}
}
@ -460,7 +428,7 @@ namespace BizHawk.Client.Common
private void SetSaturnControllersAsMnemonic(string mnemonic)
{
MnemonicChecker c = new MnemonicChecker(mnemonic);
var c = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
if (mnemonic.Length < 2)
@ -479,9 +447,9 @@ namespace BizHawk.Client.Common
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcindex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
if (mnemonic.Length < srcindex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
if (mnemonic.Length < srcIndex + 3 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
{
return;
}
@ -489,14 +457,14 @@ namespace BizHawk.Client.Common
int start = 3;
foreach (string button in BkmMnemonicConstants.Buttons[ControlType].Keys)
{
Force($"P{player} {button}", c[srcindex + start++]);
Force($"P{player} {button}", c[srcIndex + start++]);
}
}
}
private void SetAtari7800AsMnemonic(string mnemonic)
{
MnemonicChecker c = new MnemonicChecker(mnemonic);
var c = new MnemonicChecker(mnemonic);
_myBoolButtons.Clear();
if (mnemonic.Length < 5)
@ -526,16 +494,16 @@ namespace BizHawk.Client.Common
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcindex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
int start = 6;
if (mnemonic.Length < srcindex + start + BkmMnemonicConstants.Buttons[ControlType].Count)
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++]);
Force($"P{player} {button}", c[srcIndex + start++]);
}
}
}
@ -575,9 +543,9 @@ namespace BizHawk.Client.Common
for (int player = 1; player <= BkmMnemonicConstants.Players[ControlType]; player++)
{
int srcindex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
int srcIndex = (player - 1) * (BkmMnemonicConstants.Buttons[ControlType].Count + 1);
if (mnemonic.Length < srcindex + 1 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
if (mnemonic.Length < srcIndex + 1 + BkmMnemonicConstants.Buttons[ControlType].Count - 1)
{
return;
}
@ -585,14 +553,14 @@ namespace BizHawk.Client.Common
int start = 1;
foreach (var button in BkmMnemonicConstants.Buttons[ControlType].Keys)
{
Force($"P{player} {button}", c[srcindex + start++]);
Force($"P{player} {button}", c[srcIndex + start++]);
}
}
int startk = 13;
int startKey = 13;
foreach (string button in BkmMnemonicConstants.Buttons["Commodore 64 Keyboard"].Keys)
{
Force(button, c[startk++]);
Force(button, c[startKey++]);
}
}

View File

@ -10,11 +10,11 @@ namespace BizHawk.Client.Common
Comments = new List<string>();
Subtitles = new SubtitleList();
this[HeaderKeys.EMULATIONVERSION] = VersionInfo.GetEmuVersion();
this[HeaderKeys.PLATFORM] = Global.Emulator != null ? Global.Emulator.SystemId : "";
this[HeaderKeys.GAMENAME] = "";
this[HeaderKeys.AUTHOR] = "";
this[HeaderKeys.RERECORDS] = "0";
this[HeaderKeys.EmulationVersion] = VersionInfo.GetEmuVersion();
this[HeaderKeys.Platform] = Global.Emulator != null ? Global.Emulator.SystemId : "";
this[HeaderKeys.GameName] = "";
this[HeaderKeys.Author] = "";
this[HeaderKeys.Rerecords] = "0";
}
public List<string> Comments { get; }
@ -22,25 +22,18 @@ namespace BizHawk.Client.Common
public string SavestateBinaryBase64Blob
{
get
{
if (ContainsKey(HeaderKeys.SAVESTATEBINARYBASE64BLOB))
{
return this[HeaderKeys.SAVESTATEBINARYBASE64BLOB];
}
return null;
}
get => ContainsKey(HeaderKeys.SavestateBinaryBase64Blob)
? this[HeaderKeys.SavestateBinaryBase64Blob]
: null;
set
{
if (value == null)
{
Remove(HeaderKeys.SAVESTATEBINARYBASE64BLOB);
Remove(HeaderKeys.SavestateBinaryBase64Blob);
}
else
{
Add(HeaderKeys.SAVESTATEBINARYBASE64BLOB, value);
Add(HeaderKeys.SavestateBinaryBase64Blob, value);
}
}
}
@ -48,7 +41,6 @@ namespace BizHawk.Client.Common
public new string this[string key]
{
get => ContainsKey(key) ? base[key] : "";
set
{
if (ContainsKey(key))

View File

@ -11,7 +11,7 @@ namespace BizHawk.Client.Common
public BkmMovie()
{
Header = new BkmHeader { [HeaderKeys.MOVIEVERSION] = "BizHawk v0.0.1" };
Header = new BkmHeader { [HeaderKeys.MovieVersion] = "BizHawk v0.0.1" };
}
public string PreferredExtension => "bkm";
@ -31,12 +31,7 @@ namespace BizHawk.Client.Common
return double.PositiveInfinity;
}
if (Loaded)
{
return _log.Count;
}
return 0;
return Loaded ? _log.Count : 0;
}
}
@ -81,8 +76,8 @@ namespace BizHawk.Client.Common
public string SyncSettingsJson
{
get => Header[HeaderKeys.SYNCSETTINGS];
set => Header[HeaderKeys.SYNCSETTINGS] = value;
get => Header[HeaderKeys.SyncSettings];
set => Header[HeaderKeys.SyncSettings] = value;
}
public string TextSavestate { get; set; }

View File

@ -131,7 +131,7 @@ namespace BizHawk.Client.Common
continue;
}
if ((currentFrame % _step > 0) && (currentFrame + 1 != _movie.LastEditedFrame))
if (currentFrame % _step > 0 && currentFrame + 1 != _movie.LastEditedFrame)
{
// ignore the pattern if the state doesn't belong already, drop it blindly and skip everything
if (_tsm.Remove(currentFrame))
@ -154,7 +154,7 @@ namespace BizHawk.Client.Common
if (_align)
{
priority -= ((_base * ((1 << zeroCount) * 2 - 1)) >> zeroCount);
priority -= (_base * ((1 << zeroCount) * 2 - 1)) >> zeroCount;
}
if (priority > backwardPriority)

View File

@ -396,7 +396,7 @@ namespace BizHawk.Client.Common
Global.MovieSession.MovieControllerAdapter.LatchSticky();
var lg = LogGeneratorInstance();
lg.SetSource(Global.MovieOutputHardpoint); // account for autohold. needs autohold pattern to be already recorded in the current frame
lg.SetSource(Global.InputManager.MovieOutputHardpoint); // account for autohold. needs autohold pattern to be already recorded in the current frame
for (int i = 0; i < numFrames; i++)
{
@ -461,7 +461,7 @@ namespace BizHawk.Client.Common
ExtendMovieForEdit(frame + count - Log.Count);
}
ChangeLog.AddGeneralUndo(frame, frame + count - 1, $"Set {buttonName}({(val ? "On" : "Off")}): {frame}-{(frame + count - 1)}");
ChangeLog.AddGeneralUndo(frame, frame + count - 1, $"Set {buttonName}({(val ? "On" : "Off")}): {frame}-{frame + count - 1}");
int changed = -1;
for (int i = 0; i < count; i++)
@ -519,7 +519,7 @@ namespace BizHawk.Client.Common
ExtendMovieForEdit(frame - Log.Count + 1);
}
ChangeLog.AddGeneralUndo(frame, frame + count - 1, $"Set {buttonName}({val}): {frame}-{(frame + count - 1)}");
ChangeLog.AddGeneralUndo(frame, frame + count - 1, $"Set {buttonName}({val}): {frame}-{frame + count - 1}");
int changed = -1;
for (int i = 0; i < count; i++)

View File

@ -56,7 +56,7 @@ namespace BizHawk.Client.Common
ChangeLog = new TasMovieChangeLog(this);
TasStateManager = new TasStateManager(this, Global.Config.DefaultTasStateManagerSettings);
Session = new TasSession();
Header[HeaderKeys.MOVIEVERSION] = "BizHawk v2.0 Tasproj v1.0";
Header[HeaderKeys.MovieVersion] = "BizHawk v2.0 Tasproj v1.0";
Markers = new TasMovieMarkerList(this);
Markers.CollectionChanged += Markers_CollectionChanged;
Markers.Add(0, startsFromSavestate ? "Savestate" : "Power on");
@ -395,12 +395,9 @@ namespace BizHawk.Client.Common
public Guid BranchGuidByIndex(int index)
{
if (index >= Branches.Count)
{
return Guid.Empty;
}
return Branches[index].UniqueIdentifier;
return index >= Branches.Count
? Guid.Empty
: Branches[index].UniqueIdentifier;
}
public int BranchIndexByHash(Guid uuid)

View File

@ -44,17 +44,12 @@ namespace BizHawk.Client.Common
public override bool Equals(object obj)
{
if (obj == null)
return obj switch
{
return false;
}
if (obj is TasMovieMarker marker)
{
return Frame == marker.Frame;
}
return false;
null => false,
TasMovieMarker marker => Frame == marker.Frame,
_ => false
};
}
public static bool operator ==(TasMovieMarker marker, int frame)

View File

@ -87,24 +87,15 @@ namespace BizHawk.Client.Common
public string AddressStr => _watch.AddressString;
public string ValueStr
{
get
{
switch (_watch.Size)
public string ValueStr =>
_watch.Size switch
{
default:
case WatchSize.Separator:
return "";
case WatchSize.Byte:
return (_watch as ByteWatch).FormatValue((byte)_val);
case WatchSize.Word:
return (_watch as WordWatch).FormatValue((ushort)_val);
case WatchSize.DWord:
return (_watch as DWordWatch).FormatValue((uint)_val);
}
}
}
WatchSize.Byte => ((ByteWatch) _watch).FormatValue((byte)_val),
WatchSize.Word => ((WordWatch) _watch).FormatValue((ushort)_val),
WatchSize.DWord => ((DWordWatch) _watch).FormatValue((uint)_val),
WatchSize.Separator => "",
_ => ""
};
public string CompareStr
{
@ -112,25 +103,21 @@ namespace BizHawk.Client.Common
{
if (_compare.HasValue)
{
switch (_watch.Size)
return _watch.Size switch
{
default:
case WatchSize.Separator:
return "";
case WatchSize.Byte:
return (_watch as ByteWatch).FormatValue((byte)_compare.Value);
case WatchSize.Word:
return (_watch as WordWatch).FormatValue((ushort)_compare.Value);
case WatchSize.DWord:
return (_watch as DWordWatch).FormatValue((uint)_compare.Value);
}
WatchSize.Byte => ((ByteWatch) _watch).FormatValue((byte)_compare.Value),
WatchSize.Word => ((WordWatch) _watch).FormatValue((ushort)_compare.Value),
WatchSize.DWord => ((DWordWatch) _watch).FormatValue((uint)_compare.Value),
WatchSize.Separator => "",
_ => ""
};
}
return "";
}
}
public CompareType ComparisonType { get; private set; }
public CompareType ComparisonType { get; }
public void Enable(bool handleChange = true)
{
@ -170,15 +157,10 @@ namespace BizHawk.Client.Common
}
}
private string GetStringForPulse(int val)
{
if (_watch.Type == DisplayType.Hex)
{
return val.ToString("X8");
}
return val.ToString();
}
private string GetStringForPulse(int val) =>
_watch.Type == DisplayType.Hex
? val.ToString("X8")
: val.ToString();
public void Pulse()
{
@ -239,13 +221,13 @@ namespace BizHawk.Client.Common
switch (_watch.Size)
{
case WatchSize.Byte:
_watch.Poke((_watch as ByteWatch).FormatValue((byte)_val));
_watch.Poke(((ByteWatch)_watch).FormatValue((byte)_val));
break;
case WatchSize.Word:
_watch.Poke((_watch as WordWatch).FormatValue((ushort)_val));
_watch.Poke(((WordWatch)_watch).FormatValue((ushort)_val));
break;
case WatchSize.DWord:
_watch.Poke((_watch as DWordWatch).FormatValue((uint)_val));
_watch.Poke(((DWordWatch)_watch).FormatValue((uint)_val));
break;
}
}
@ -262,10 +244,10 @@ namespace BizHawk.Client.Common
case WatchSize.Byte:
return _watch.Address == addr;
case WatchSize.Word:
return (addr == _watch.Address) || (addr == _watch.Address + 1);
return addr == _watch.Address || addr == _watch.Address + 1;
case WatchSize.DWord:
return (addr == _watch.Address) || (addr == _watch.Address + 1) ||
(addr == _watch.Address + 2) || (addr == _watch.Address + 3);
return addr == _watch.Address || addr == _watch.Address + 1 ||
addr == _watch.Address + 2 || addr == _watch.Address + 3;
}
}
@ -381,7 +363,7 @@ namespace BizHawk.Client.Common
public void SetType(DisplayType type)
{
if (_watch.IsDiplayTypeAvailable(type))
if (_watch.IsDisplayTypeAvailable(type))
{
_watch.Type = type;
Changes();
@ -416,7 +398,7 @@ namespace BizHawk.Client.Common
public static bool operator ==(Cheat a, Cheat b)
{
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))
if ((object)a == null || (object)b == null)
{
return false;
}
@ -432,7 +414,7 @@ namespace BizHawk.Client.Common
public static bool operator ==(Cheat a, Watch b)
{
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))
if ((object)a == null || (object)b == null)
{
return false;
}

View File

@ -6,6 +6,7 @@ using System.IO;
using System.Linq;
using System.Text;
using BizHawk.Common.CollectionExtensions;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
@ -59,15 +60,9 @@ namespace BizHawk.Client.Common
_cheatList.FirstOrDefault(cheat => cheat.Domain == domain && cheat.Address == address);
public IEnumerator<Cheat> GetEnumerator()
{
return _cheatList.GetEnumerator();
}
public IEnumerator<Cheat> GetEnumerator() => _cheatList.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public void Pulse()
{
@ -80,13 +75,7 @@ namespace BizHawk.Client.Common
public bool AttemptToLoadCheatFile()
{
var file = new FileInfo(_defaultFileName);
if (file.Exists)
{
return Load(file.FullName, false);
}
return false;
return file.Exists && Load(file.FullName, false);
}
public void NewList(string defaultFileName, bool autosave = false)
@ -297,12 +286,9 @@ namespace BizHawk.Client.Common
case WatchSize.Byte:
return activeCheat.Value;
case WatchSize.Word:
if (size == WatchSize.Byte)
{
return GetByteValue(domain, addr);
}
return activeCheat.Value;
return size == WatchSize.Byte
? GetByteValue(domain, addr)
: activeCheat.Value;
case WatchSize.DWord:
if (size == WatchSize.Byte)
{
@ -514,190 +500,72 @@ namespace BizHawk.Client.Common
switch (column)
{
case NameColumn:
if (reverse)
{
_cheatList = _cheatList
.OrderByDescending(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
else
{
_cheatList = _cheatList
.OrderBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
_cheatList = _cheatList
.OrderBy(c => c.Name, reverse)
.ThenBy(c => c.Address ?? 0)
.ToList();
break;
case AddressColumn:
if (reverse)
{
_cheatList = _cheatList
.OrderByDescending(c => c.Address ?? 0)
.ThenBy(c => c.Name)
.ToList();
}
else
{
_cheatList = _cheatList
.OrderBy(c => c.Address ?? 0)
.ThenBy(c => c.Name)
.ToList();
}
_cheatList = _cheatList
.OrderBy(c => c.Address ?? 0, reverse)
.ThenBy(c => c.Name)
.ToList();
break;
case ValueColumn:
if (reverse)
{
_cheatList = _cheatList
.OrderByDescending(c => c.Value ?? 0)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
else
{
_cheatList = _cheatList
.OrderBy(c => c.Value ?? 0)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
_cheatList = _cheatList
.OrderBy(c => c.Value ?? 0, reverse)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
break;
case CompareColumn:
if (reverse)
{
_cheatList = _cheatList
.OrderByDescending(c => c.Compare ?? 0)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
else
{
_cheatList = _cheatList
.OrderBy(c => c.Compare ?? 0)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
_cheatList = _cheatList
.OrderBy(c => c.Compare ?? 0, reverse)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
break;
case OnColumn:
if (reverse)
{
_cheatList = _cheatList
.OrderByDescending(c => c.Enabled)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
else
{
_cheatList = _cheatList
.OrderBy(c => c.Enabled)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
_cheatList = _cheatList
.OrderBy(c => c.Enabled, reverse)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
break;
case DomainColumn:
if (reverse)
{
_cheatList = _cheatList
.OrderByDescending(c => c.Domain)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
else
{
_cheatList = _cheatList
.OrderBy(c => c.Domain)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
_cheatList = _cheatList
.OrderBy(c => c.Domain, reverse)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
break;
case SizeColumn:
if (reverse)
{
_cheatList = _cheatList
.OrderByDescending(c => ((int)c.Size))
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
else
{
_cheatList = _cheatList
.OrderBy(c => ((int)c.Size))
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
_cheatList = _cheatList
.OrderBy(c => (int)c.Size, reverse)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
break;
case EndianColumn:
if (reverse)
{
_cheatList = _cheatList
.OrderByDescending(c => c.BigEndian)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
else
{
_cheatList = _cheatList
.OrderBy(c => c.BigEndian)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
_cheatList = _cheatList
.OrderBy(c => c.BigEndian, reverse)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
break;
case TypeColumn:
if (reverse)
{
_cheatList = _cheatList
.OrderByDescending(c => c.Type)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
else
{
_cheatList = _cheatList
.OrderBy(c => c.Type)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
_cheatList = _cheatList
.OrderBy(c => c.Type, reverse)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
break;
case ComparisonType:
if (reverse)
{
_cheatList = _cheatList
.OrderByDescending(c => c.ComparisonType)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
else
{
_cheatList = _cheatList
.OrderBy(c => c.ComparisonType)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
}
_cheatList = _cheatList
.OrderBy(c => c.ComparisonType, reverse)
.ThenBy(c => c.Name)
.ThenBy(c => c.Address ?? 0)
.ToList();
break;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
namespace BizHawk.Client.Common.RamSearchEngine
{
public enum ComparisonOperator
{
Equal, GreaterThan, GreaterThanEqual, LessThan, LessThanEqual, NotEqual, DifferentBy
}
public enum Compare
{
Previous, SpecificValue, SpecificAddress, Changes, Difference
}
public enum SearchMode
{
Fast, Detailed
}
}

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common.RamSearchEngine
{
internal static class Extensions
{
public static float ToFloat(this long val)
{
var bytes = BitConverter.GetBytes((int)val);
return BitConverter.ToSingle(bytes, 0);
}
public static IEnumerable<IMiniWatch> ToBytes(this IEnumerable<long> addresses, SearchEngineSettings settings)
=> settings.IsDetailed()
? addresses.ToBytes(settings.Domain)
: addresses.ToDetailedBytes(settings.Domain);
public static IEnumerable<IMiniWatch> ToWords(this IEnumerable<long> addresses, SearchEngineSettings settings)
=> settings.IsDetailed()
? addresses.ToWords(settings.Domain, settings.BigEndian)
: addresses.ToDetailedWords(settings.Domain, settings.BigEndian);
public static IEnumerable<IMiniWatch> ToDWords(this IEnumerable<long> addresses, SearchEngineSettings settings)
=> settings.IsDetailed()
? addresses.ToDWords(settings.Domain, settings.BigEndian)
: addresses.ToDetailedDWords(settings.Domain, settings.BigEndian);
private static IEnumerable<IMiniWatch> ToBytes(this IEnumerable<long> addresses, MemoryDomain domain)
=> addresses.Select(a => new MiniByteWatch(domain, a));
private static IEnumerable<IMiniWatch> ToDetailedBytes(this IEnumerable<long> addresses, MemoryDomain domain)
=> addresses.Select(a => new MiniByteWatchDetailed(domain, a));
private static IEnumerable<IMiniWatch> ToWords(this IEnumerable<long> addresses, MemoryDomain domain, bool bigEndian)
=> addresses.Select(a => new MiniWordWatch(domain, a, bigEndian));
private static IEnumerable<IMiniWatch> ToDetailedWords(this IEnumerable<long> addresses, MemoryDomain domain, bool bigEndian)
=> addresses.Select(a => new MiniWordWatchDetailed(domain, a, bigEndian));
private static IEnumerable<IMiniWatch> ToDWords(this IEnumerable<long> addresses, MemoryDomain domain, bool bigEndian)
=> addresses.Select(a => new MiniDWordWatch(domain, a, bigEndian));
private static IEnumerable<IMiniWatch> ToDetailedDWords(this IEnumerable<long> addresses, MemoryDomain domain, bool bigEndian)
=> addresses.Select(a => new MiniDWordWatchDetailed(domain, a, bigEndian));
}
}

View File

@ -0,0 +1,72 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common.RamSearchEngine
{
/// <summary>
/// Represents a Ram address for watching in the <see cref="RamSearchEngine" />
/// With the minimal details necessary for searching
/// </summary>
internal interface IMiniWatch
{
long Address { get; }
long Previous { get; } // do not store sign extended variables in here.
void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian);
}
internal sealed class MiniByteWatch : IMiniWatch
{
public long Address { get; }
private byte _previous;
public MiniByteWatch(MemoryDomain domain, long addr)
{
Address = addr;
_previous = domain.PeekByte(Address % domain.Size);
}
public long Previous => _previous;
public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
{
_previous = domain.PeekByte(Address % domain.Size);
}
}
internal sealed class MiniWordWatch : IMiniWatch
{
public long Address { get; }
private ushort _previous;
public MiniWordWatch(MemoryDomain domain, long addr, bool bigEndian)
{
Address = addr;
_previous = domain.PeekUshort(Address % domain.Size, bigEndian);
}
public long Previous => _previous;
public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
{
_previous = domain.PeekUshort(Address, bigEndian);
}
}
internal sealed class MiniDWordWatch : IMiniWatch
{
public long Address { get; }
private uint _previous;
public MiniDWordWatch(MemoryDomain domain, long addr, bool bigEndian)
{
Address = addr;
_previous = domain.PeekUint(Address % domain.Size, bigEndian);
}
public long Previous => _previous;
public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
{
_previous = domain.PeekUint(Address, bigEndian);
}
}
}

View File

@ -0,0 +1,177 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common.RamSearchEngine
{
/// <summary>
/// Represents a <see cref="IMiniWatch" /> but with added details
/// to do change tracking. These types add more information but at a cost of
/// having to poll the ram address on every update
/// </summary>
internal interface IMiniWatchDetails : IMiniWatch
{
int ChangeCount { get; }
void ClearChangeCount();
void Update(PreviousType type, MemoryDomain domain, bool bigEndian);
}
internal sealed class MiniByteWatchDetailed : IMiniWatchDetails
{
public long Address { get; }
private byte _previous;
private byte _prevFrame;
public MiniByteWatchDetailed(MemoryDomain domain, long addr)
{
Address = addr;
SetPreviousToCurrent(domain, false);
}
public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
{
_previous = _prevFrame = domain.PeekByte(Address % domain.Size);
}
public long Previous => _previous;
public int ChangeCount { get; private set; }
public void Update(PreviousType type, MemoryDomain domain, bool bigEndian)
{
var value = domain.PeekByte(Address % domain.Size);
if (value != _prevFrame)
{
ChangeCount++;
}
switch (type)
{
case PreviousType.Original:
case PreviousType.LastSearch:
break;
case PreviousType.LastFrame:
_previous = _prevFrame;
break;
case PreviousType.LastChange:
if (_prevFrame != value)
{
_previous = _prevFrame;
}
break;
}
_prevFrame = value;
}
public void ClearChangeCount() => ChangeCount = 0;
}
internal sealed class MiniWordWatchDetailed : IMiniWatchDetails
{
public long Address { get; }
private ushort _previous;
private ushort _prevFrame;
public MiniWordWatchDetailed(MemoryDomain domain, long addr, bool bigEndian)
{
Address = addr;
SetPreviousToCurrent(domain, bigEndian);
}
public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
{
_previous = _prevFrame = domain.PeekUshort(Address % domain.Size, bigEndian);
}
public long Previous => _previous;
public int ChangeCount { get; private set; }
public void Update(PreviousType type, MemoryDomain domain, bool bigEndian)
{
var value = domain.PeekUshort(Address % domain.Size, bigEndian);
if (value != Previous)
{
ChangeCount++;
}
switch (type)
{
case PreviousType.Original:
case PreviousType.LastSearch:
break;
case PreviousType.LastFrame:
_previous = _prevFrame;
break;
case PreviousType.LastChange:
if (_prevFrame != value)
{
_previous = _prevFrame;
}
break;
}
_prevFrame = value;
}
public void ClearChangeCount() => ChangeCount = 0;
}
internal sealed class MiniDWordWatchDetailed : IMiniWatchDetails
{
public long Address { get; }
private uint _previous;
private uint _prevFrame;
public MiniDWordWatchDetailed(MemoryDomain domain, long addr, bool bigEndian)
{
Address = addr;
SetPreviousToCurrent(domain, bigEndian);
}
public void SetPreviousToCurrent(MemoryDomain domain, bool bigEndian)
{
_previous = _prevFrame = domain.PeekUint(Address % domain.Size, bigEndian);
}
public long Previous => (int)_previous;
public int ChangeCount { get; private set; }
public void Update(PreviousType type, MemoryDomain domain, bool bigEndian)
{
var value = domain.PeekUint(Address % domain.Size, bigEndian);
if (value != Previous)
{
ChangeCount++;
}
switch (type)
{
case PreviousType.Original:
case PreviousType.LastSearch:
break;
case PreviousType.LastFrame:
_previous = _prevFrame;
break;
case PreviousType.LastChange:
if (_prevFrame != value)
{
_previous = _prevFrame;
}
break;
}
_prevFrame = value;
}
public void ClearChangeCount() => ChangeCount = 0;
}
}

View File

@ -0,0 +1,642 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Common;
using BizHawk.Common.CollectionExtensions;
using BizHawk.Common.NumberExtensions;
using BizHawk.Emulation.Common;
// ReSharper disable PossibleInvalidCastExceptionInForeachLoop
namespace BizHawk.Client.Common.RamSearchEngine
{
public class RamSearchEngine
{
private Compare _compareTo = Compare.Previous;
private List<IMiniWatch> _watchList = new List<IMiniWatch>();
private readonly SearchEngineSettings _settings;
private readonly UndoHistory<IMiniWatch> _history = new UndoHistory<IMiniWatch>(true);
private bool _isSorted = true; // Tracks whether or not the list is sorted by address, if it is, binary search can be used for finding watches
public RamSearchEngine(SearchEngineSettings settings, IMemoryDomains memoryDomains)
{
_settings = new SearchEngineSettings(memoryDomains)
{
Mode = settings.Mode,
Domain = settings.Domain,
Size = settings.Size,
CheckMisAligned = settings.CheckMisAligned,
Type = settings.Type,
BigEndian = settings.BigEndian,
PreviousType = settings.PreviousType
};
}
public RamSearchEngine(SearchEngineSettings settings, IMemoryDomains memoryDomains, Compare compareTo, long? compareValue, int? differentBy)
: this(settings, memoryDomains)
{
_compareTo = compareTo;
DifferentBy = differentBy;
CompareValue = compareValue;
}
#region API
public IEnumerable<long> OutOfRangeAddress => _watchList
.Where(watch => watch.Address >= Domain.Size)
.Select(watch => watch.Address);
public void Start()
{
_history.Clear();
var domain = _settings.Domain;
var listSize = domain.Size;
if (!_settings.CheckMisAligned)
{
listSize /= (int)_settings.Size;
}
_watchList = new List<IMiniWatch>((int)listSize);
switch (_settings.Size)
{
default:
case WatchSize.Byte:
for (int i = 0; i < domain.Size; i++)
{
if (_settings.IsDetailed())
{
_watchList.Add(new MiniByteWatchDetailed(domain, i));
}
else
{
_watchList.Add(new MiniByteWatch(domain, i));
}
}
break;
case WatchSize.Word:
for (int i = 0; i < domain.Size - 1; i += _settings.CheckMisAligned ? 1 : 2)
{
if (_settings.IsDetailed())
{
_watchList.Add(new MiniWordWatchDetailed(domain, i, _settings.BigEndian));
}
else
{
_watchList.Add(new MiniWordWatch(domain, i, _settings.BigEndian));
}
}
break;
case WatchSize.DWord:
for (int i = 0; i < domain.Size - 3; i += _settings.CheckMisAligned ? 1 : 4)
{
if (_settings.IsDetailed())
{
_watchList.Add(new MiniDWordWatchDetailed(domain, i, _settings.BigEndian));
}
else
{
_watchList.Add(new MiniDWordWatch(domain, i, _settings.BigEndian));
}
}
break;
}
}
/// <summary>
/// Exposes the current watch state based on index
/// </summary>
public Watch this[int index] =>
Watch.GenerateWatch(
_settings.Domain,
_watchList[index].Address,
_settings.Size,
_settings.Type,
_settings.BigEndian,
"",
0,
_watchList[index].Previous,
_settings.IsDetailed() ? ((IMiniWatchDetails)_watchList[index]).ChangeCount : 0);
public int DoSearch()
{
int before = _watchList.Count;
_watchList = _compareTo switch
{
Compare.Previous => ComparePrevious(_watchList).ToList(),
Compare.SpecificValue => CompareSpecificValue(_watchList).ToList(),
Compare.SpecificAddress => CompareSpecificAddress(_watchList).ToList(),
Compare.Changes => CompareChanges(_watchList).ToList(),
Compare.Difference => CompareDifference(_watchList).ToList(),
_ => ComparePrevious(_watchList).ToList()
};
if (_settings.PreviousType == PreviousType.LastSearch)
{
SetPreviousToCurrent();
}
if (UndoEnabled)
{
_history.AddState(_watchList);
}
return before - _watchList.Count;
}
public bool Preview(long address)
{
var listOfOne = Enumerable.Repeat(_isSorted
? _watchList.BinarySearch(w => w.Address, address)
: _watchList.FirstOrDefault(w => w.Address == address), 1);
return _compareTo switch
{
Compare.Previous => !ComparePrevious(listOfOne).Any(),
Compare.SpecificValue => !CompareSpecificValue(listOfOne).Any(),
Compare.SpecificAddress => !CompareSpecificAddress(listOfOne).Any(),
Compare.Changes => !CompareChanges(listOfOne).Any(),
Compare.Difference => !CompareDifference(listOfOne).Any(),
_ => !ComparePrevious(listOfOne).Any()
};
}
public int Count => _watchList.Count;
public SearchMode Mode => _settings.Mode;
public MemoryDomain Domain => _settings.Domain;
/// <exception cref="InvalidOperationException">(from setter) <see cref="Mode"/> is <see cref="SearchMode.Fast"/> and <paramref name="value"/> is not <see cref="Compare.Changes"/></exception>
public Compare CompareTo
{
get => _compareTo;
set
{
if (CanDoCompareType(value))
{
_compareTo = value;
}
else
{
throw new InvalidOperationException();
}
}
}
public long? CompareValue { get; set; }
public ComparisonOperator Operator { get; set; }
// zero 07-sep-2014 - this isn't ideal. but don't bother changing it (to a long, for instance) until it can support floats. maybe store it as a double here.
public int? DifferentBy { get; set; }
public void Update()
{
if (_settings.IsDetailed())
{
foreach (IMiniWatchDetails watch in _watchList)
{
watch.Update(_settings.PreviousType, _settings.Domain, _settings.BigEndian);
}
}
}
public void SetType(DisplayType type) => _settings.Type = type;
public void SetEndian(bool bigEndian) => _settings.BigEndian = bigEndian;
/// <exception cref="InvalidOperationException"><see cref="Mode"/> is <see cref="SearchMode.Fast"/> and <paramref name="type"/> is <see cref="PreviousType.LastFrame"/></exception>
public void SetPreviousType(PreviousType type)
{
if (_settings.IsFastMode() && type == PreviousType.LastFrame)
{
throw new InvalidOperationException();
}
_settings.PreviousType = type;
}
public void SetPreviousToCurrent()
{
_watchList.ForEach(w => w.SetPreviousToCurrent(_settings.Domain, _settings.BigEndian));
}
public void ClearChangeCounts()
{
if (_settings.IsDetailed())
{
foreach (var watch in _watchList.Cast<IMiniWatchDetails>())
{
watch.ClearChangeCount();
}
}
}
/// <summary>
/// Remove a set of watches
/// However, this should not be used with large data sets (100k or more) as it uses a contains logic to perform the task
/// </summary>
public void RemoveSmallWatchRange(IEnumerable<Watch> watches)
{
if (UndoEnabled)
{
_history.AddState(_watchList);
}
var addresses = watches.Select(w => w.Address);
_watchList.RemoveAll(w => addresses.Contains(w.Address));
}
public void RemoveRange(IEnumerable<int> indices)
{
if (UndoEnabled)
{
_history.AddState(_watchList);
}
var removeList = indices.Select(i => _watchList[i]); // This will fail after int.MaxValue but RAM Search fails on domains that large anyway
_watchList = _watchList.Except(removeList).ToList();
}
public void AddRange(IEnumerable<long> addresses, bool append)
{
if (!append)
{
_watchList.Clear();
}
var list = _settings.Size switch
{
WatchSize.Byte => addresses.ToBytes(_settings),
WatchSize.Word => addresses.ToWords(_settings),
WatchSize.DWord => addresses.ToDWords(_settings),
_ => addresses.ToBytes(_settings)
};
_watchList.AddRange(list);
}
public void Sort(string column, bool reverse)
{
_isSorted = column == WatchList.Address && !reverse;
switch (column)
{
case WatchList.Address:
_watchList = _watchList.OrderBy(w => w.Address, reverse).ToList();
break;
case WatchList.Value:
_watchList = _watchList.OrderBy(w => GetValue(w.Address), reverse).ToList();
break;
case WatchList.Prev:
_watchList = _watchList.OrderBy(w => w.Previous, reverse).ToList();
break;
case WatchList.ChangesCol:
if (_settings.IsDetailed())
{
_watchList = _watchList
.Cast<IMiniWatchDetails>()
.OrderBy(w => w.ChangeCount, reverse)
.Cast<IMiniWatch>()
.ToList();
}
break;
case WatchList.Diff:
_watchList = _watchList.OrderBy(w => GetValue(w.Address) - w.Previous, reverse).ToList();
break;
}
}
#endregion
#region Undo API
public bool UndoEnabled { get; set; }
public bool CanUndo => UndoEnabled && _history.CanUndo;
public bool CanRedo => UndoEnabled && _history.CanRedo;
public void ClearHistory() => _history.Clear();
public int Undo()
{
int origCount = _watchList.Count;
if (UndoEnabled)
{
_watchList = _history.Undo().ToList();
return _watchList.Count - origCount;
}
return _watchList.Count;
}
public int Redo()
{
int origCount = _watchList.Count;
if (UndoEnabled)
{
_watchList = _history.Redo().ToList();
return origCount - _watchList.Count;
}
return _watchList.Count;
}
#endregion
#region Comparisons
private IEnumerable<IMiniWatch> ComparePrevious(IEnumerable<IMiniWatch> watchList)
{
switch (Operator)
{
default:
case ComparisonOperator.Equal:
return _settings.Type == DisplayType.Float
? watchList.Where(w => GetValue(w.Address).ToFloat().HawkFloatEquality(w.Previous.ToFloat()))
: watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) == SignExtendAsNeeded(w.Previous));
case ComparisonOperator.NotEqual:
return watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) != SignExtendAsNeeded(w.Previous));
case ComparisonOperator.GreaterThan:
return _settings.Type == DisplayType.Float
? watchList.Where(w => GetValue(w.Address).ToFloat() > w.Previous.ToFloat())
: watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) > SignExtendAsNeeded(w.Previous));
case ComparisonOperator.GreaterThanEqual:
return _settings.Type == DisplayType.Float
? watchList.Where(w => GetValue(w.Address).ToFloat() >= w.Previous.ToFloat())
: watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) >= SignExtendAsNeeded(w.Previous));
case ComparisonOperator.LessThan:
return _settings.Type == DisplayType.Float
? watchList.Where(w => GetValue(w.Address).ToFloat() < w.Previous.ToFloat())
: watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) < SignExtendAsNeeded(w.Previous));
case ComparisonOperator.LessThanEqual:
return _settings.Type == DisplayType.Float
? watchList.Where(w => GetValue(w.Address).ToFloat() <= w.Previous.ToFloat())
: watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) <= SignExtendAsNeeded(w.Previous));
case ComparisonOperator.DifferentBy:
if (DifferentBy.HasValue)
{
var differentBy = DifferentBy.Value;
if (_settings.Type == DisplayType.Float)
{
return watchList.Where(w => (GetValue(w.Address).ToFloat() + differentBy).HawkFloatEquality(w.Previous.ToFloat())
|| (GetValue(w.Address).ToFloat() - differentBy).HawkFloatEquality(w.Previous.ToFloat()));
}
return watchList.Where(w =>
{
long val = SignExtendAsNeeded(GetValue(w.Address));
long prev = SignExtendAsNeeded(w.Previous);
return val + differentBy == prev
|| val - differentBy == prev;
});
}
else
{
throw new InvalidOperationException();
}
}
}
private IEnumerable<IMiniWatch> CompareSpecificValue(IEnumerable<IMiniWatch> watchList)
{
if (CompareValue.HasValue)
{
var compareValue = CompareValue.Value;
switch (Operator)
{
default:
case ComparisonOperator.Equal:
return _settings.Type == DisplayType.Float
? watchList.Where(w => GetValue(w.Address).ToFloat().HawkFloatEquality(compareValue.ToFloat()))
: watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) == SignExtendAsNeeded(compareValue));
case ComparisonOperator.NotEqual:
return _settings.Type == DisplayType.Float
? watchList.Where(w => !GetValue(w.Address).ToFloat().HawkFloatEquality(compareValue.ToFloat()))
: watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) != SignExtendAsNeeded(compareValue));
case ComparisonOperator.GreaterThan:
return _settings.Type == DisplayType.Float
? watchList.Where(w => GetValue(w.Address).ToFloat() > compareValue.ToFloat())
: watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) > SignExtendAsNeeded(compareValue));
case ComparisonOperator.GreaterThanEqual:
return _settings.Type == DisplayType.Float
? watchList.Where(w => GetValue(w.Address).ToFloat() >= compareValue.ToFloat())
: watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) >= SignExtendAsNeeded(compareValue));
case ComparisonOperator.LessThan:
return _settings.Type == DisplayType.Float
? watchList.Where(w => GetValue(w.Address).ToFloat() < compareValue.ToFloat())
: watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) < SignExtendAsNeeded(compareValue));
case ComparisonOperator.LessThanEqual:
return _settings.Type == DisplayType.Float
? watchList.Where(w => GetValue(w.Address).ToFloat() <= compareValue.ToFloat())
: watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) <= SignExtendAsNeeded(compareValue));
case ComparisonOperator.DifferentBy:
if (DifferentBy.HasValue)
{
var differentBy = DifferentBy.Value;
if (_settings.Type == DisplayType.Float)
{
return watchList.Where(w => (GetValue(w.Address).ToFloat() + differentBy).HawkFloatEquality(compareValue)
|| (GetValue(w.Address).ToFloat() - differentBy).HawkFloatEquality(compareValue));
}
return watchList.Where(w
=> SignExtendAsNeeded(GetValue(w.Address)) + differentBy == compareValue
|| SignExtendAsNeeded(GetValue(w.Address)) - differentBy == compareValue);
}
throw new InvalidOperationException();
}
}
throw new InvalidOperationException();
}
private IEnumerable<IMiniWatch> CompareSpecificAddress(IEnumerable<IMiniWatch> watchList)
{
if (CompareValue.HasValue)
{
var compareValue = CompareValue.Value;
switch (Operator)
{
default:
case ComparisonOperator.Equal:
return watchList.Where(w => w.Address == compareValue);
case ComparisonOperator.NotEqual:
return watchList.Where(w => w.Address != compareValue);
case ComparisonOperator.GreaterThan:
return watchList.Where(w => w.Address > compareValue);
case ComparisonOperator.GreaterThanEqual:
return watchList.Where(w => w.Address >= compareValue);
case ComparisonOperator.LessThan:
return watchList.Where(w => w.Address < compareValue);
case ComparisonOperator.LessThanEqual:
return watchList.Where(w => w.Address <= compareValue);
case ComparisonOperator.DifferentBy:
if (DifferentBy.HasValue)
{
return watchList.Where(w => w.Address + DifferentBy.Value == compareValue
|| w.Address - DifferentBy.Value == compareValue);
}
throw new InvalidOperationException();
}
}
throw new InvalidOperationException();
}
private IEnumerable<IMiniWatch> CompareChanges(IEnumerable<IMiniWatch> watchList)
{
if (_settings.IsDetailed() && CompareValue.HasValue)
{
var compareValue = CompareValue.Value;
switch (Operator)
{
default:
case ComparisonOperator.Equal:
return watchList
.Cast<IMiniWatchDetails>()
.Where(w => w.ChangeCount == compareValue);
case ComparisonOperator.NotEqual:
return watchList
.Cast<IMiniWatchDetails>()
.Where(w => w.ChangeCount != compareValue);
case ComparisonOperator.GreaterThan:
return watchList
.Cast<IMiniWatchDetails>()
.Where(w => w.ChangeCount > compareValue);
case ComparisonOperator.GreaterThanEqual:
return watchList
.Cast<IMiniWatchDetails>()
.Where(w => w.ChangeCount >= compareValue);
case ComparisonOperator.LessThan:
return watchList
.Cast<IMiniWatchDetails>()
.Where(w => w.ChangeCount < compareValue);
case ComparisonOperator.LessThanEqual:
return watchList
.Cast<IMiniWatchDetails>()
.Where(w => w.ChangeCount <= compareValue);
case ComparisonOperator.DifferentBy:
if (DifferentBy.HasValue)
{
return watchList
.Cast<IMiniWatchDetails>()
.Where(w => w.ChangeCount + DifferentBy.Value == compareValue
|| w.ChangeCount - DifferentBy.Value == compareValue);
}
throw new InvalidOperationException();
}
}
throw new InvalidCastException();
}
private IEnumerable<IMiniWatch> CompareDifference(IEnumerable<IMiniWatch> watchList)
{
if (CompareValue.HasValue)
{
var compareValue = CompareValue.Value;
switch (Operator)
{
default:
case ComparisonOperator.Equal:
return _settings.Type == DisplayType.Float
? watchList.Where(w => (GetValue(w.Address).ToFloat() - w.Previous.ToFloat()).HawkFloatEquality(compareValue))
: watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) == compareValue);
case ComparisonOperator.NotEqual:
return _settings.Type == DisplayType.Float
? watchList.Where(w => !(GetValue(w.Address).ToFloat() - w.Previous.ToFloat()).HawkFloatEquality(compareValue))
: watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) != compareValue);
case ComparisonOperator.GreaterThan:
return _settings.Type == DisplayType.Float
? watchList.Where(w => GetValue(w.Address).ToFloat() - w.Previous.ToFloat() > compareValue)
: watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) > compareValue);
case ComparisonOperator.GreaterThanEqual:
return _settings.Type == DisplayType.Float
? watchList.Where(w => GetValue(w.Address).ToFloat() - w.Previous.ToFloat() >= compareValue)
: watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) >= compareValue);
case ComparisonOperator.LessThan:
return _settings.Type == DisplayType.Float
? watchList.Where(w => GetValue(w.Address).ToFloat() - w.Previous.ToFloat() < compareValue)
: watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) < compareValue);
case ComparisonOperator.LessThanEqual:
return _settings.Type == DisplayType.Float
? watchList.Where(w => GetValue(w.Address).ToFloat() - w.Previous.ToFloat() <= compareValue)
: watchList.Where(w => SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) <= compareValue);
case ComparisonOperator.DifferentBy:
if (DifferentBy.HasValue)
{
var differentBy = DifferentBy.Value;
if (_settings.Type == DisplayType.Float)
{
return watchList.Where(w => (GetValue(w.Address).ToFloat() - w.Previous.ToFloat() + differentBy).HawkFloatEquality(compareValue)
|| (GetValue(w.Address).ToFloat() - w.Previous.ToFloat() - differentBy).HawkFloatEquality(w.Previous));
}
return watchList.Where(w
=> SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) + differentBy == compareValue
|| SignExtendAsNeeded(GetValue(w.Address)) - SignExtendAsNeeded(w.Previous) - differentBy == compareValue);
}
throw new InvalidOperationException();
}
}
throw new InvalidCastException();
}
#endregion
private long SignExtendAsNeeded(long val)
{
if (_settings.Type != DisplayType.Signed)
{
return val;
}
return _settings.Size switch
{
WatchSize.Byte => (sbyte) val,
WatchSize.Word => (short) val,
WatchSize.DWord => (int) val,
_ => (sbyte) val
};
}
private long GetValue(long addr)
{
// do not return sign extended variables from here.
return _settings.Size switch
{
WatchSize.Byte => _settings.Domain.PeekByte(addr % Domain.Size),
WatchSize.Word => _settings.Domain.PeekUshort(addr % Domain.Size, _settings.BigEndian),
WatchSize.DWord => _settings.Domain.PeekUint(addr % Domain.Size, _settings.BigEndian),
_ => _settings.Domain.PeekByte(addr % Domain.Size)
};
}
private bool CanDoCompareType(Compare compareType)
{
return _settings.Mode switch
{
SearchMode.Detailed => true,
SearchMode.Fast => (compareType != Compare.Changes),
_ => true
};
}
}
}

View File

@ -0,0 +1,41 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common.RamSearchEngine
{
public class SearchEngineSettings
{
public SearchEngineSettings(IMemoryDomains memoryDomains)
{
BigEndian = memoryDomains.MainMemory.EndianType == MemoryDomain.Endian.Big;
Size = (WatchSize)memoryDomains.MainMemory.WordSize;
Type = DisplayType.Unsigned;
Mode = memoryDomains.MainMemory.Size > 1024 * 1024
? SearchMode.Fast
: SearchMode.Detailed;
Domain = memoryDomains.MainMemory;
CheckMisAligned = false;
PreviousType = PreviousType.LastSearch;
}
/*Require restart*/
public SearchMode Mode { get; set; }
public MemoryDomain Domain { get; set; }
public WatchSize Size { get; set; }
public bool CheckMisAligned { get; set; }
/*Can be changed mid-search*/
public DisplayType Type { get; set; }
public bool BigEndian { get; set; }
public PreviousType PreviousType { get; set; }
}
public static class SearchEngineSettingsExtensions
{
public static bool IsFastMode(this SearchEngineSettings settings)
=> settings.Mode == SearchMode.Fast;
public static bool IsDetailed(this SearchEngineSettings settings)
=> settings.Mode == SearchMode.Detailed;
}
}

View File

@ -181,18 +181,14 @@ namespace BizHawk.Client.Common
// TODO: Implements IFormattable
public string FormatValue(byte val)
{
switch (Type)
return Type switch
{
default:
case DisplayType.Unsigned:
return val.ToString();
case DisplayType.Signed:
return ((sbyte)val).ToString();
case DisplayType.Hex:
return $"{val:X2}";
case DisplayType.Binary:
return Convert.ToString(val, 2).PadLeft(8, '0').Insert(4, " ");
}
DisplayType.Unsigned => val.ToString(),
DisplayType.Signed => ((sbyte) val).ToString(),
DisplayType.Hex => $"{val:X2}",
DisplayType.Binary => Convert.ToString(val, 2).PadLeft(8, '0').Insert(4, " "),
_ => val.ToString()
};
}
/// <summary>

View File

@ -35,7 +35,7 @@ namespace BizHawk.Client.Common
/// <exception cref="ArgumentException">Occurs when a <see cref="DisplayType"/> is incompatible with the <see cref="WatchSize"/></exception>
protected Watch(MemoryDomain domain, long address, WatchSize size, DisplayType type, bool bigEndian, string note)
{
if (IsDiplayTypeAvailable(type))
if (IsDisplayTypeAvailable(type))
{
_domain = domain;
Address = address;
@ -136,18 +136,14 @@ namespace BizHawk.Client.Common
/// <returns>New <see cref="Watch"/> instance. True type is depending of size parameter</returns>
public static Watch GenerateWatch(MemoryDomain domain, long address, WatchSize size, DisplayType type, bool bigEndian, string note = "", long value = 0, long prev = 0, int changeCount = 0)
{
switch (size)
return size switch
{
default:
case WatchSize.Separator:
return SeparatorWatch.NewSeparatorWatch(note);
case WatchSize.Byte:
return new ByteWatch(domain, address, type, bigEndian, note, (byte)value, (byte)prev, changeCount);
case WatchSize.Word:
return new WordWatch(domain, address, type, bigEndian, note, (ushort)value, (ushort)prev, changeCount);
case WatchSize.DWord:
return new DWordWatch(domain, address, type, bigEndian, note, (uint)value, (uint)prev, changeCount);
}
WatchSize.Separator => SeparatorWatch.NewSeparatorWatch(note),
WatchSize.Byte => new ByteWatch(domain, address, type, bigEndian, note, (byte) value, (byte) prev, changeCount),
WatchSize.Word => new WordWatch(domain, address, type, bigEndian, note, (ushort) value, (ushort) prev, changeCount),
WatchSize.DWord => new DWordWatch(domain, address, type, bigEndian, note, (uint) value, (uint) prev, changeCount),
_ => SeparatorWatch.NewSeparatorWatch(note)
};
}
#region Operators
@ -314,7 +310,7 @@ namespace BizHawk.Client.Common
return _domain.PeekUshort(Address, BigEndian);
}
return _domain.PeekUshort(Address % _domain.Size, BigEndian); // TODO: % size stil lisn't correct since it could be the last byte of the domain
return _domain.PeekUshort(Address % _domain.Size, BigEndian); // TODO: % size still isn't correct since it could be the last byte of the domain
}
protected uint GetDWord(bool bypassFreeze = false)
@ -455,14 +451,14 @@ namespace BizHawk.Client.Common
/// <returns>True if both object are equals; otherwise, false</returns>
public override bool Equals(object obj)
{
if (obj is Watch)
if (obj is Watch watch)
{
return Equals((Watch)obj);
return Equals(watch);
}
if (obj is Cheat)
if (obj is Cheat cheat)
{
return Equals((Cheat)obj);
return Equals(cheat);
}
return base.Equals(obj);
@ -482,7 +478,7 @@ namespace BizHawk.Client.Common
/// used for the current <see cref="Watch"/>
/// </summary>
/// <param name="type"><see cref="DisplayType"/> you want to check</param>
public bool IsDiplayTypeAvailable(DisplayType type)
public bool IsDisplayTypeAvailable(DisplayType type)
{
return AvailableTypes().Any(d => d == type);
}
@ -501,10 +497,7 @@ namespace BizHawk.Client.Common
/// It's used by the "Display on screen" option in the RamWatch window
/// </summary>
/// <returns>A well formatted string representation</returns>
public virtual string ToDisplayString()
{
return $"{Notes}: {ValueString}";
}
public virtual string ToDisplayString() => $"{Notes}: {ValueString}";
#endregion
@ -565,18 +558,9 @@ namespace BizHawk.Client.Common
/// </summary>
public long Address { get; }
private string AddressFormatStr
{
get
{
if (_domain != null)
{
return $"X{(_domain.Size - 1).NumHexDigits()}";
}
return "";
}
}
private string AddressFormatStr => _domain != null
? $"X{(_domain.Size - 1).NumHexDigits()}"
: "";
/// <summary>
/// Gets the address in the <see cref="MemoryDomain"/> formatted as string
@ -603,7 +587,7 @@ namespace BizHawk.Client.Common
get => _type;
set
{
if (IsDiplayTypeAvailable(value))
if (IsDisplayTypeAvailable(value))
{
_type = value;
}
@ -632,12 +616,6 @@ namespace BizHawk.Client.Common
}
}
/// <summary>
/// Gets a value indicating whether the current address is
/// within in the range of current <see cref="MemoryDomain"/>
/// </summary>
public bool IsOutOfRange => !IsSeparator && _domain.Size != 0 && Address >= _domain.Size;
/// <summary>
/// Gets a value that defined if the current <see cref="Watch"/> is actually a <see cref="SeparatorWatch"/>
/// </summary>
@ -658,122 +636,88 @@ namespace BizHawk.Client.Common
// TODO: Replace all the following stuff by implementing ISerializable
public static string DisplayTypeToString(DisplayType type)
{
switch (type)
return type switch
{
default:
return type.ToString();
case DisplayType.FixedPoint_12_4:
return "Fixed Point 12.4";
case DisplayType.FixedPoint_20_12:
return "Fixed Point 20.12";
case DisplayType.FixedPoint_16_16:
return "Fixed Point 16.16";
}
DisplayType.FixedPoint_12_4 => "Fixed Point 12.4",
DisplayType.FixedPoint_20_12 => "Fixed Point 20.12",
DisplayType.FixedPoint_16_16 => "Fixed Point 16.16",
_ => type.ToString()
};
}
public static DisplayType StringToDisplayType(string name)
{
switch (name)
return name switch
{
default:
return (DisplayType)Enum.Parse(typeof(DisplayType), name);
case "Fixed Point 12.4":
return DisplayType.FixedPoint_12_4;
case "Fixed Point 20.12":
return DisplayType.FixedPoint_20_12;
case "Fixed Point 16.16":
return DisplayType.FixedPoint_16_16;
}
"Fixed Point 12.4" => DisplayType.FixedPoint_12_4,
"Fixed Point 20.12" => DisplayType.FixedPoint_20_12,
"Fixed Point 16.16" => DisplayType.FixedPoint_16_16,
_ => (DisplayType) Enum.Parse(typeof(DisplayType), name)
};
}
public char SizeAsChar
{
get
{
switch (Size)
return Size switch
{
default:
case WatchSize.Separator:
return 'S';
case WatchSize.Byte:
return 'b';
case WatchSize.Word:
return 'w';
case WatchSize.DWord:
return 'd';
}
WatchSize.Separator => 'S',
WatchSize.Byte => 'b',
WatchSize.Word => 'w',
WatchSize.DWord => 'd',
_ => 'S'
};
}
}
public static WatchSize SizeFromChar(char c)
{
switch (c)
return c switch
{
default:
case 'S':
return WatchSize.Separator;
case 'b':
return WatchSize.Byte;
case 'w':
return WatchSize.Word;
case 'd':
return WatchSize.DWord;
}
'S' => WatchSize.Separator,
'b' => WatchSize.Byte,
'w' => WatchSize.Word,
'd' => WatchSize.DWord,
_ => WatchSize.Separator
};
}
public char TypeAsChar
{
get
{
switch (Type)
return Type switch
{
default:
case DisplayType.Separator:
return '_';
case DisplayType.Unsigned:
return 'u';
case DisplayType.Signed:
return 's';
case DisplayType.Hex:
return 'h';
case DisplayType.Binary:
return 'b';
case DisplayType.FixedPoint_12_4:
return '1';
case DisplayType.FixedPoint_20_12:
return '2';
case DisplayType.FixedPoint_16_16:
return '3';
case DisplayType.Float:
return 'f';
}
DisplayType.Separator => '_',
DisplayType.Unsigned => 'u',
DisplayType.Signed => 's',
DisplayType.Hex => 'h',
DisplayType.Binary => 'b',
DisplayType.FixedPoint_12_4 => '1',
DisplayType.FixedPoint_20_12 => '2',
DisplayType.FixedPoint_16_16 => '3',
DisplayType.Float => 'f',
_ => '_'
};
}
}
public static DisplayType DisplayTypeFromChar(char c)
{
switch (c)
return c switch
{
default:
case '_':
return DisplayType.Separator;
case 'u':
return DisplayType.Unsigned;
case 's':
return DisplayType.Signed;
case 'h':
return DisplayType.Hex;
case 'b':
return DisplayType.Binary;
case '1':
return DisplayType.FixedPoint_12_4;
case '2':
return DisplayType.FixedPoint_20_12;
case '3':
return DisplayType.FixedPoint_16_16;
case 'f':
return DisplayType.Float;
}
'_' => DisplayType.Separator,
'u' => DisplayType.Unsigned,
's' => DisplayType.Signed,
'h' => DisplayType.Hex,
'b' => DisplayType.Binary,
'1' => DisplayType.FixedPoint_12_4,
'2' => DisplayType.FixedPoint_20_12,
'3' => DisplayType.FixedPoint_16_16,
'f' => DisplayType.Float,
_ => DisplayType.Separator
};
}
}
}

View File

@ -20,12 +20,7 @@ namespace BizHawk.Client.Common
{
if (ReferenceEquals(x, null))
{
if (ReferenceEquals(y, null))
{
return true;
}
return false;
return ReferenceEquals(y, null);
}
if (ReferenceEquals(y, null))
@ -33,12 +28,7 @@ namespace BizHawk.Client.Common
return false;
}
if (ReferenceEquals(x, y))
{
return true;
}
return false;
return ReferenceEquals(x, y);
}
/// <summary>
@ -46,10 +36,7 @@ namespace BizHawk.Client.Common
/// </summary>
/// <param name="obj">Watch to get hash</param>
/// <returns>int that can serves as a unique representation of current Watch</returns>
public int GetHashCode(Watch obj)
{
return obj.GetHashCode();
}
public int GetHashCode(Watch obj) => obj.GetHashCode();
}
}
}

View File

@ -21,14 +21,14 @@ namespace BizHawk.Client.Common
{
#region Fields
public const string ADDRESS = "AddressColumn";
public const string VALUE = "ValueColumn";
public const string PREV = "PrevColumn";
public const string CHANGES = "ChangesColumn";
public const string DIFF = "DiffColumn";
public const string TYPE = "TypeColumn";
public const string DOMAIN = "DomainColumn";
public const string NOTES = "NotesColumn";
public const string Address = "AddressColumn";
public const string Value = "ValueColumn";
public const string Prev = "PrevColumn";
public const string ChangesCol = "ChangesColumn";
public const string Diff = "DiffColumn";
public const string Type = "TypeColumn";
public const string Domain = "DomainColumn";
public const string Notes = "NotesColumn";
private static readonly Dictionary<string, IComparer<Watch>> WatchComparers;
@ -48,14 +48,14 @@ namespace BizHawk.Client.Common
// Initialize mapping of columns to comparers for sorting.
WatchComparers = new Dictionary<string, IComparer<Watch>>
{
{ ADDRESS, new WatchAddressComparer() },
{ VALUE, new WatchValueComparer() },
{ PREV, new WatchPreviousValueComparer() },
{ CHANGES, new WatchChangeCountComparer() },
{ DIFF, new WatchValueDifferenceComparer() },
{ TYPE, new WatchFullDisplayTypeComparer() },
{ DOMAIN, new WatchDomainComparer() },
{ NOTES, new WatchNoteComparer() }
{ Address, new WatchAddressComparer() },
{ Value, new WatchValueComparer() },
{ Prev, new WatchPreviousValueComparer() },
{ ChangesCol, new WatchChangeCountComparer() },
{ Diff, new WatchValueDifferenceComparer() },
{ Type, new WatchFullDisplayTypeComparer() },
{ Domain, new WatchDomainComparer() },
{ Notes, new WatchNoteComparer() }
};
}

View File

@ -55,10 +55,7 @@ namespace BizHawk.Client.Common
/// Get a list a <see cref="DisplayType"/> that can be used for this <see cref="WordWatch"/>
/// </summary>
/// <returns>An enumeration that contains all valid <see cref="DisplayType"/></returns>
public override IEnumerable<DisplayType> AvailableTypes()
{
return ValidTypes;
}
public override IEnumerable<DisplayType> AvailableTypes() => ValidTypes;
/// <summary>
/// Reset the previous value; set it to the current one
@ -193,20 +190,19 @@ namespace BizHawk.Client.Common
// TODO: Implements IFormattable
public string FormatValue(ushort val)
{
switch (Type)
return Type switch
{
default:
case DisplayType.Unsigned:
return val.ToString();
case DisplayType.Signed:
return ((short)val).ToString();
case DisplayType.Hex:
return $"{val:X4}";
case DisplayType.FixedPoint_12_4:
return $"{val / 16.0:F4}";
case DisplayType.Binary:
return Convert.ToString(val, 2).PadLeft(16, '0').Insert(8, " ").Insert(4, " ").Insert(14, " ");
}
DisplayType.Unsigned => val.ToString(),
DisplayType.Signed => ((short) val).ToString(), DisplayType.Hex => $"{val:X4}",
DisplayType.FixedPoint_12_4 => $"{val / 16.0:F4}",
DisplayType.Binary => Convert
.ToString(val, 2)
.PadLeft(16, '0')
.Insert(8, " ")
.Insert(4, " ")
.Insert(14, " "),
_ => val.ToString()
};
}
/// <summary>

View File

@ -7,6 +7,7 @@ using System.Windows.Forms;
using BizHawk.Client.Common;
using BizHawk.Common;
using BizHawk.Common.PathExtensions;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
@ -86,7 +87,7 @@ namespace BizHawk.Client.EmuHawk
try
{
_ffmpeg = OSTailoredCode.ConstructSubshell(
OSTailoredCode.IsUnixHost ? "ffmpeg" : Path.Combine(PathManager.GetDllDirectory(), "ffmpeg.exe"),
OSTailoredCode.IsUnixHost ? "ffmpeg" : Path.Combine(PathUtils.DllDirectoryPath, "ffmpeg.exe"),
$"-y -f nut -i - {_token.Commandline} \"{_baseName}{(_segment == 0 ? string.Empty : $"_{_segment}")}{_ext}\"",
checkStdout: false,
checkStderr: true // ffmpeg sends informative display to stderr, and nothing to stdout

View File

@ -34,18 +34,12 @@ namespace BizHawk.Client.EmuHawk
private void NumericUpDown2_ValueChanged(object sender, EventArgs e)
{
if (numericUpDown2.Value == -1)
label3.Text = numericUpDown2.Value switch
{
label3.Text = "Auto";
}
else if (numericUpDown2.Value == 0)
{
label3.Text = "Fastest";
}
else
{
label3.Text = $"{(int)((100 + numericUpDown2.Value / 2) / numericUpDown2.Value)} FPS";
}
-1 => "Auto",
0 => "Fastest",
_ => $"{(int) ((100 + numericUpDown2.Value / 2) / numericUpDown2.Value)} FPS"
};
}
}
}

View File

@ -28,8 +28,6 @@ namespace BizHawk.Client.EmuHawk
/// </summary>
private readonly bool _singlePass;
protected int DitherLevel;
/// <summary>
/// Construct the quantizer
/// </summary>
@ -149,21 +147,6 @@ namespace BizHawk.Client.EmuHawk
}
}
private int ClampToByte(int val)
{
if (val < 0)
{
return 0;
}
if (val > 255)
{
return 255;
}
return val;
}
/// <summary>
/// Execute a second pass through the bitmap
/// </summary>
@ -176,7 +159,6 @@ namespace BizHawk.Client.EmuHawk
{
BitmapData outputData = null;
Color[] palettes = output.Palette.Entries;
int weight = DitherLevel;
try
{
@ -186,11 +168,9 @@ namespace BizHawk.Client.EmuHawk
// Define the source data pointers. The source row is a byte to
// keep addition of the stride value easier (as this is in bytes)
byte* pSourceRow = (byte*)sourceData.Scan0.ToPointer();
int* pSourcePixel = (int*)pSourceRow;
// Now define the destination data pointers
byte* pDestinationRow = (byte*)outputData.Scan0.ToPointer();
byte* pDestinationPixel = pDestinationRow;
int[] errorThisRowR = new int[width + 1];
int[] errorThisRowG = new int[width + 1];
@ -204,6 +184,8 @@ namespace BizHawk.Client.EmuHawk
int ptrInc;
int* pSourcePixel;
byte* pDestinationPixel;
if ((row & 1) == 0)
{
pSourcePixel = (int*)pSourceRow;
@ -223,14 +205,11 @@ namespace BizHawk.Client.EmuHawk
// Quantize the pixel
int srcPixel = *pSourcePixel;
int srcR = srcPixel & 0xFF; //not
int srcG = (srcPixel >> 8) & 0xFF; //a
int srcB = (srcPixel >> 16) & 0xFF; //mistake
int srcA = (srcPixel >> 24) & 0xFF;
int targetB = ClampToByte(srcB - ((errorThisRowB[col] * weight) / 8));
int targetG = ClampToByte(srcG - ((errorThisRowG[col] * weight) / 8));
int targetR = ClampToByte(srcR - ((errorThisRowR[col] * weight) / 8));
int targetB = 0;
int targetG = 0;
int targetR = 0;
int targetA = srcA;
int target = (targetA << 24) | (targetB << 16) | (targetG << 8) | targetR;

View File

@ -6,6 +6,7 @@ using System.Windows.Forms;
using BizHawk.Bizware.BizwareGL;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{
@ -32,8 +33,8 @@ namespace BizHawk.Client.EmuHawk
{
var ofd = new OpenFileDialog
{
FileName = $"{PathManager.FilesystemSafeName(Global.Game)}.syncless.txt",
InitialDirectory = PathManager.MakeAbsolutePath(Global.Config.PathEntries.AvPathFragment, null)
FileName = $"{Global.Game.FilesystemSafeName()}.syncless.txt",
InitialDirectory = Global.Config.PathEntries.AvAbsolutePath()
};
if (ofd.ShowDialog() == DialogResult.Cancel)

View File

@ -12,14 +12,20 @@ namespace BizHawk.Client.EmuHawk
/// </summary>
public partial class VideoWriterChooserForm : Form
{
private readonly int _captureWidth, _captureHeight;
private readonly int _captureWidth = 640;
private readonly int _captureHeight = 480;
private VideoWriterChooserForm(MainForm mainForm, IEmulator emulator, Config config)
{
InitializeComponent();
_captureWidth = emulator.CoreComm.NominalWidth;
_captureHeight = emulator.CoreComm.NominalHeight;
// TODO: do we want to use virtual w/h?
if (emulator.HasVideoProvider())
{
var videoProvider = emulator.AsVideoProvider();
_captureWidth = videoProvider.BufferWidth;
_captureHeight = videoProvider.BufferHeight;
}
if (config.AviCaptureOsd)
{

View File

@ -59,15 +59,14 @@ namespace BizHawk.Client.EmuHawk
private Graphics GetGraphics()
{
var g = _GUISurface?.GetGraphics() ?? Graphics.FromImage(_nullGraphicsBitmap);
// we don't like CoreComm, right? Someone should find a different way to do this then.
var tx = Emulator.CoreComm.ScreenLogicalOffsetX;
var ty = Emulator.CoreComm.ScreenLogicalOffsetY;
var (tx, ty) = Emulator.ScreenLogicalOffsets();
if (tx != 0 || ty != 0)
{
var transform = g.Transform;
transform.Translate(-tx, -ty);
g.Transform = transform;
}
return g;
}
@ -488,8 +487,9 @@ namespace BizHawk.Client.EmuHawk
}
else
{
x -= Emulator.CoreComm.ScreenLogicalOffsetX;
y -= Emulator.CoreComm.ScreenLogicalOffsetY;
var (ox, oy) = Emulator.ScreenLogicalOffsets();
x -= ox;
y -= oy;
}
var pos = new MessagePosition{ X = x, Y = y, Anchor = (MessagePosition.AnchorType)a };

View File

@ -11,7 +11,7 @@ namespace BizHawk.Client.EmuHawk
public Dictionary<string, bool> Get()
{
var buttons = new Dictionary<string, bool>();
foreach (var kvp in Global.ControllerInputCoalescer.BoolButtons().Where(kvp => kvp.Value)) buttons[kvp.Key] = true;
foreach (var kvp in Global.InputManager.ControllerInputCoalescer.BoolButtons().Where(kvp => kvp.Value)) buttons[kvp.Key] = true;
return buttons;
}

View File

@ -96,17 +96,14 @@
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed"
HintPath="$(SolutionDir)packages/Newtonsoft.Json.12.0.3/lib/net45/Newtonsoft.Json.dll"
Private="true" />
<!--<Reference Include="OpenTK, Version=3.0.1.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4"
HintPath="../packages/OpenTK.3.0.1/lib/net20/OpenTK.dll"
Private="true" />-->
<Reference Include="OpenTK.GLControl, Version=3.0.1.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4"
HintPath="../packages/OpenTK.GLControl.3.0.1/lib/net20/OpenTK.GLControl.dll"
HintPath="$(SolutionDir)packages/OpenTK.GLControl.3.0.1/lib/net20/OpenTK.GLControl.dll"
Private="true" />
<Reference Include="ICSharpCode.SharpZipLib, Version=1.1.0.145, Culture=neutral, PublicKeyToken=1b03e6acf1164f73"
HintPath="$(SolutionDir)packages/SharpZipLib.1.1.0/lib/net45/ICSharpCode.SharpZipLib.dll"
Private="true" />
<Reference Include="System.Drawing.Common, Version=4.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51"
HintPath="../packages/System.Drawing.Common.4.7.0/lib/net461/System.Drawing.Common.dll"
HintPath="$(SolutionDir)packages/System.Drawing.Common.4.7.0/lib/net461/System.Drawing.Common.dll"
Private="true" />
</ItemGroup>
<ItemGroup>
@ -162,6 +159,9 @@
<ProjectReference Include="$(SolutionDir)BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj"
Name="BizHawk.Emulation.Cores"
Project="{197D4314-8A9F-49BA-977D-54ACEFAEB6BA}" />
<ProjectReference Include="$(SolutionDir)BizHawk.WinForms.Controls/BizHawk.WinForms.Controls.csproj"
Name="BizHawk.WinForms.Controls"
Project="{B5A2214B-3CB0-48C4-8DB1-98B38D48AC4A}" />
<ProjectReference Include="$(SolutionDir)Bizware/BizHawk.Bizware.BizwareGL/BizHawk.Bizware.BizwareGL.csproj"
Name="BizHawk.Bizware.BizwareGL"
Project="{9F84A0B2-861E-4EF4-B89B-5E2A3F38A465}" />
@ -171,7 +171,6 @@
<Compile Include="$(SolutionDir)BizHawk.Common/OSTailoredCode.cs" Link="OSTailoredCode.cs" />
<Compile Include="$(SolutionDir)Version/svnrev.cs" Link="svnrev.cs" />
<Compile Include="$(SolutionDir)Version/VersionInfo.cs" Link="VersionInfo.cs" />
<Compile Remove="Properties/AssemblyInfo.cs" />
<Compile Remove="Properties/Settings.Designer.cs" />
<Content Include="images/logo.ico" />
<EmbeddedResource Include="**/*.resx" />
@ -343,6 +342,9 @@
<Compile Update="config/N64/N64VideoPluginconfig.cs" SubType="Form" />
<Compile Update="config/N64/N64VideoPluginconfig.Designer.cs" DependentUpon="N64VideoPluginconfig.cs" />
<EmbeddedResource Update="config/N64/N64VideoPluginconfig.resx" DependentUpon="N64VideoPluginconfig.cs" />
<Compile Update="config/NDS/NDSSettings.cs" SubType="Form" />
<Compile Update="config/NDS/NDSSettings.Designer.cs" DependentUpon="NDSSettings.cs" />
<EmbeddedResource Update="config/NDS/NDSSettings.resx" DependentUpon="NDSSettings.cs" />
<Compile Update="config/NES/NesControllerSettings.cs" SubType="Form" />
<Compile Update="config/NES/NesControllerSettings.Designer.cs" DependentUpon="NesControllerSettings.cs" />
<EmbeddedResource Update="config/NES/NesControllerSettings.resx" DependentUpon="NesControllerSettings.cs" />
@ -418,11 +420,9 @@
<Compile Update="CoreFeatureAnalysis.cs" SubType="Form" />
<Compile Update="CoreFeatureAnalysis.Designer.cs" DependentUpon="CoreFeatureAnalysis.cs" />
<EmbeddedResource Update="CoreFeatureAnalysis.resx" DependentUpon="CoreFeatureAnalysis.cs" />
<Compile Update="CustomControls/AutosizedLabel.cs" SubType="Component" />
<Compile Update="CustomControls/ExceptionBox.cs" SubType="Form" />
<Compile Update="CustomControls/ExceptionBox.designer.cs" DependentUpon="ExceptionBox.cs" />
<EmbeddedResource Update="CustomControls/ExceptionBox.resx" DependentUpon="ExceptionBox.cs" />
<Compile Update="CustomControls/FLPInGroupBox.cs" SubType="Component" />
<Compile Update="CustomControls/FolderBrowserDialogEx.cs" SubType="Component" />
<Compile Update="CustomControls/HexTextBox.cs" SubType="Component" />
<Compile Update="CustomControls/InputRoll/InputRoll.cs" SubType="Component" />
@ -434,9 +434,6 @@
<Compile Update="CustomControls/MsgBox.designer.cs" DependentUpon="MsgBox.cs" />
<EmbeddedResource Update="CustomControls/MsgBox.resx" DependentUpon="MsgBox.cs" />
<Compile Update="CustomControls/RepeatButton.cs" SubType="Component" />
<Compile Update="CustomControls/SingleColumnFLP.cs" SubType="Component" />
<Compile Update="CustomControls/SingleRowFLP.cs" SubType="Component" />
<Compile Update="CustomControls/TLPInGroupBox.cs" SubType="Component" />
<Compile Update="CustomControls/ToolStripEx.cs" SubType="Component" />
<Compile Update="CustomControls/TransparentTrackbar.cs" SubType="Component" />
<Compile Update="CustomControls/ViewportPanel.cs" SubType="Component" />

View File

@ -1,22 +0,0 @@
using System.ComponentModel;
using System.Windows.Forms;
namespace BizHawk.Client.EmuHawk.CustomControls
{
public class AutosizedLabel : Label
{
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override bool AutoSize => true;
public AutosizedLabel() : base() {
Anchor = AnchorStyles.None;
}
public AutosizedLabel(string labelText) : this()
{
Text = labelText;
}
}
}

View File

@ -1,19 +0,0 @@
using System.Windows.Forms;
namespace BizHawk.Client.EmuHawk.CustomControls
{
/// <seealso cref="TLPInGroupBox"/>
public class FLPInGroupBox : GroupBox
{
public new ControlCollection Controls => InnerFLP.Controls;
public FlowLayoutPanel InnerFLP { get; } = new FlowLayoutPanel
{
Dock = DockStyle.Fill,
FlowDirection = FlowDirection.TopDown,
WrapContents = false
};
public FLPInGroupBox() : base() => base.Controls.Add(InnerFLP);
}
}

View File

@ -37,12 +37,7 @@ namespace BizHawk.Client.EmuHawk
public static bool operator ==(Cell a, Cell b)
{
if (ReferenceEquals(a, null))
{
return ReferenceEquals(b, null);
}
return a.Equals(b);
return a?.Equals(b) ?? ReferenceEquals(b, null);
}
public static bool operator !=(Cell a, Cell b)

View File

@ -31,9 +31,7 @@ namespace BizHawk.Client.EmuHawk
{
if (this.Any(c => c.Name == column.Name))
{
// The designer sucks, doing nothing for now
return;
//throw new InvalidOperationException("A column with this name already exists.");
throw new InvalidOperationException("A column with this name already exists.");
}
base.Add(column);
@ -47,9 +45,6 @@ namespace BizHawk.Client.EmuHawk
{
if (this.Any(c => c.Name == column.Name))
{
// The designer sucks, doing nothing for now
return;
throw new InvalidOperationException("A column with this name already exists.");
}
}
@ -62,9 +57,6 @@ namespace BizHawk.Client.EmuHawk
{
if (this.Any(c => c.Name == column.Name))
{
// The designer sucks, doing nothing for now
return;
throw new InvalidOperationException("A column with this name already exists.");
}

View File

@ -100,8 +100,7 @@ namespace BizHawk.Client.EmuHawk.CustomControls
{
if (_msgIcon != null)
{
Graphics g = e.Graphics;
g.DrawIconUnstretched(_msgIcon, new Rectangle(FormXMargin, FormYMargin, _msgIcon.Width, _msgIcon.Height));
e.Graphics.DrawIconUnstretched(_msgIcon, new Rectangle(FormXMargin, FormYMargin, _msgIcon.Width, _msgIcon.Height));
}
base.OnPaint(e);
@ -110,19 +109,14 @@ namespace BizHawk.Client.EmuHawk.CustomControls
// Get system icon for MessageBoxIcon.
private static Icon GetMessageBoxIcon(MessageBoxIcon icon)
{
switch (icon)
return icon switch
{
case MessageBoxIcon.Asterisk:
return SystemIcons.Asterisk;
case MessageBoxIcon.Error:
return SystemIcons.Error;
case MessageBoxIcon.Exclamation:
return SystemIcons.Exclamation;
case MessageBoxIcon.Question:
return SystemIcons.Question;
default:
return null;
}
MessageBoxIcon.Asterisk => SystemIcons.Asterisk,
MessageBoxIcon.Error => SystemIcons.Error,
MessageBoxIcon.Exclamation => SystemIcons.Exclamation,
MessageBoxIcon.Question => SystemIcons.Question,
_ => null
};
}
// Sets button text and returns the width.

Some files were not shown because too many files have changed in this diff Show More