Merge branch 'master' of https://github.com/TASVideos/BizHawk
This commit is contained in:
commit
10d3d764b3
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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")
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 },
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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]));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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\" );")]
|
||||
|
|
|
@ -220,7 +220,7 @@ __Types and notation__
|
|||
}
|
||||
}
|
||||
|
||||
private string TypeCleanup(string str)
|
||||
private static string TypeCleanup(string str)
|
||||
{
|
||||
return str
|
||||
.Replace("System", "")
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -419,6 +419,11 @@ namespace BizHawk.Client.Common
|
|||
["IV"] = '4',
|
||||
["V"] = '5',
|
||||
["VI"] = '6',
|
||||
},
|
||||
["NDS"] = new Dictionary<string, char>
|
||||
{
|
||||
["Lid"] = 'P',
|
||||
["Touch"] = 'T'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"))
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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] != '.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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++]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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++)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue