Rip out a bunch of stuff from my previous attempt at a mnemonics overhaul and from TasMovie
This commit is contained in:
parent
b7c692e52a
commit
d27df7816d
|
@ -1,103 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
using BizHawk.Common;
|
|
||||||
using BizHawk.Emulation.Common;
|
|
||||||
|
|
||||||
namespace BizHawk.Client.Common
|
|
||||||
{
|
|
||||||
public class BooleanControllerMnemonicGenerator : IMnemonicGenerator
|
|
||||||
{
|
|
||||||
private readonly NamedDictionary<string, char> _controllerMnemonics;
|
|
||||||
|
|
||||||
public BooleanControllerMnemonicGenerator(string name, IEnumerable<KeyValuePair<string, char>> mnemonics)
|
|
||||||
{
|
|
||||||
_controllerMnemonics = new NamedDictionary<string, char>(name);
|
|
||||||
foreach (var kvp in mnemonics)
|
|
||||||
{
|
|
||||||
_controllerMnemonics.Add(kvp.Key, kvp.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsFloat { get { return true; } }
|
|
||||||
|
|
||||||
public void Add(string key, char value)
|
|
||||||
{
|
|
||||||
_controllerMnemonics.Add(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Dictionary<string, char> AvailableMnemonics
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _controllerMnemonics.ToDictionary(kvp => ControllerPrefix + " " + kvp.Key, kvp => kvp.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IController Source { get; set; }
|
|
||||||
public string ControllerPrefix { get; set; }
|
|
||||||
public string Name
|
|
||||||
{
|
|
||||||
get { return _controllerMnemonics.Name; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public char this[string key]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _controllerMnemonics[ControllerPrefix + " " + key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsEmpty
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _controllerMnemonics.All(kvp => !this.Source.IsPressed(kvp.Key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string MnemonicString
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder(_controllerMnemonics.Count);
|
|
||||||
foreach (var kvp in _controllerMnemonics)
|
|
||||||
{
|
|
||||||
sb.Append(Source.IsPressed(kvp.Key) ? kvp.Value : '.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string EmptyMnemonicString
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder(_controllerMnemonics.Count);
|
|
||||||
foreach (var kvp in _controllerMnemonics)
|
|
||||||
{
|
|
||||||
sb.Append('.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IDictionary<string, bool> ParseMnemonicSegment(string mnemonicSegment)
|
|
||||||
{
|
|
||||||
var buttons = new Dictionary<string, bool>();
|
|
||||||
var keys = _controllerMnemonics.Select(kvp => kvp.Key).ToList();
|
|
||||||
|
|
||||||
for (var i = 0; i < mnemonicSegment.Length; i++)
|
|
||||||
{
|
|
||||||
buttons.Add(keys[i], mnemonicSegment[i] != '.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return buttons;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using BizHawk.Emulation.Common;
|
|
||||||
|
|
||||||
namespace BizHawk.Client.Common
|
|
||||||
{
|
|
||||||
public interface IMnemonicGenerator
|
|
||||||
{
|
|
||||||
IController Source { get; set; }
|
|
||||||
|
|
||||||
string Name { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the prefix that will be prepended to all button names
|
|
||||||
/// Example: "P1" would combine with "Up" to make "P1 Up"
|
|
||||||
/// </summary>
|
|
||||||
string ControllerPrefix { get; set; }
|
|
||||||
|
|
||||||
void Add(string key, char value);
|
|
||||||
|
|
||||||
char this[string key] { get; }
|
|
||||||
bool IsEmpty { get; }
|
|
||||||
string MnemonicString { get; }
|
|
||||||
|
|
||||||
bool IsFloat { get; } // Float or Boolean
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a string that represents an empty or default mnemonic
|
|
||||||
/// </summary>
|
|
||||||
string EmptyMnemonicString { get; }
|
|
||||||
|
|
||||||
// Analog TODO: this assumes the Generator is boolean, pass an object structure that contains both the boolean and float dictionaries
|
|
||||||
/// <summary>
|
|
||||||
/// Parses a segment of a full mnemonic string (the content between pipes)
|
|
||||||
/// Note: this assume the pipes are not being passed in!
|
|
||||||
/// </summary>
|
|
||||||
IDictionary<string, bool> ParseMnemonicSegment(string mnemonicSegment);
|
|
||||||
|
|
||||||
Dictionary<string, char> AvailableMnemonics { get; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using BizHawk.Emulation.Common;
|
|
||||||
|
|
||||||
namespace BizHawk.Client.Common
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A console specific collection of Mnemonic generators
|
|
||||||
/// This handles includes all the "business" logic specific to the console
|
|
||||||
/// </summary>
|
|
||||||
public interface IMnemonicPorts
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the total number of available controller ports (this does not include the console controls
|
|
||||||
/// </summary>
|
|
||||||
int Count { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the Source controller to read the input state from
|
|
||||||
/// </summary>
|
|
||||||
IController Source { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the given port with an IMnemonicGenerator implementation
|
|
||||||
/// Ports are zero based
|
|
||||||
/// Set will throw an InvalidOperationException if a particular implementation is not allowed, this is platform specific logic such as NES doesn't allow a zapper in port 0, etc
|
|
||||||
/// Both will throw an ArgumentOutOfRangeException exception if portNum is not less than Count
|
|
||||||
/// </summary>
|
|
||||||
IMnemonicGenerator this[int portNum] { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets an IMnemonicGenerator implementation that represents the buttons and controls on the console itself (Reset, Power, etc)
|
|
||||||
/// </summary>
|
|
||||||
IMnemonicGenerator ConsoleControls { get; }
|
|
||||||
|
|
||||||
Dictionary<string, bool> ParseMnemonicString(string mnemonicStr);
|
|
||||||
|
|
||||||
Dictionary<string, bool> GetBoolButtons();
|
|
||||||
Dictionary<string, float> GetFloatButtons();
|
|
||||||
|
|
||||||
string EmptyMnemonic { get; }
|
|
||||||
|
|
||||||
Dictionary<string, char> AvailableMnemonics { get; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
using BizHawk.Emulation.Common;
|
|
||||||
|
|
||||||
namespace BizHawk.Client.Common
|
|
||||||
{
|
|
||||||
// Lot's of todos here
|
|
||||||
public static class MnemonicGeneratorFactory
|
|
||||||
{
|
|
||||||
public static IMnemonicPorts Generate()
|
|
||||||
{
|
|
||||||
switch (Global.Emulator.SystemId)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
case "NES":
|
|
||||||
return new NesMnemonicGenerator(Global.MovieOutputHardpoint, false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,225 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using BizHawk.Emulation.Common;
|
|
||||||
|
|
||||||
namespace BizHawk.Client.Common
|
|
||||||
{
|
|
||||||
public class NesMnemonicGenerator : IMnemonicPorts
|
|
||||||
{
|
|
||||||
private bool _isFds;
|
|
||||||
private bool _isFourscore;
|
|
||||||
|
|
||||||
public NesMnemonicGenerator(IController source, bool fds = false, bool isFourscore = false)
|
|
||||||
{
|
|
||||||
Source = source;
|
|
||||||
_isFds = fds;
|
|
||||||
_isFourscore = isFourscore;
|
|
||||||
|
|
||||||
_nesConsoleControls.Source = source;
|
|
||||||
_fdsConsoleControls.Source = source;
|
|
||||||
_controllerPorts.ForEach(x => x.Source = source);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool FourScoreEnabled
|
|
||||||
{
|
|
||||||
get { return _isFourscore; }
|
|
||||||
set { _isFourscore = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsFDS
|
|
||||||
{
|
|
||||||
get { return _isFds; }
|
|
||||||
set { _isFds = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
#region IMnemonicPorts Implementation
|
|
||||||
|
|
||||||
public string EmptyMnemonic
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var blah = AvailableGenerators.Select(x => x.EmptyMnemonicString);
|
|
||||||
return "|" + string.Join("|", blah) + "|";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count
|
|
||||||
{
|
|
||||||
get { return _isFourscore ? 4 : 2; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public IController Source { get; set; }
|
|
||||||
|
|
||||||
// This is probably not necessary, but let's see how things go
|
|
||||||
public IEnumerable<IMnemonicGenerator> AvailableGenerators
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
yield return ConsoleControls;
|
|
||||||
|
|
||||||
for (var i = 0; i < Count; i++)
|
|
||||||
{
|
|
||||||
yield return _controllerPorts[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IMnemonicGenerator ConsoleControls
|
|
||||||
{
|
|
||||||
get { return _isFds ? _fdsConsoleControls : _nesConsoleControls; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public IMnemonicGenerator this[int portNum]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (portNum < Count)
|
|
||||||
{
|
|
||||||
return _controllerPorts[portNum];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException("portNum");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (portNum < Count)
|
|
||||||
{
|
|
||||||
// Eventually this will support zappers and FDS controllers, Arkanoid paddle, etc
|
|
||||||
if (value is BooleanControllerMnemonicGenerator)
|
|
||||||
{
|
|
||||||
_controllerPorts[portNum] = value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Invalid Mnemonic Generator for the given port");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException("portNum");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region TODO: nothing specific to this object here, this could be done in a base class
|
|
||||||
|
|
||||||
public Dictionary<string, bool> ParseMnemonicString(string mnemonicStr)
|
|
||||||
{
|
|
||||||
var segments = mnemonicStr.Split('|');
|
|
||||||
var kvps = new List<KeyValuePair<string, bool>>();
|
|
||||||
var generators = AvailableGenerators.ToList();
|
|
||||||
for (var i = 0; i < mnemonicStr.Length; i++)
|
|
||||||
{
|
|
||||||
kvps.AddRange(generators[i].ParseMnemonicSegment(segments[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return kvps.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Dictionary<string, bool> GetBoolButtons()
|
|
||||||
{
|
|
||||||
return AvailableGenerators
|
|
||||||
.Where(g => g.IsFloat)
|
|
||||||
.SelectMany(mc => mc.AvailableMnemonics)
|
|
||||||
.ToDictionary(kvp => kvp.Key, kvp => Source.IsPressed(kvp.Key));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Dictionary<string, float> GetFloatButtons()
|
|
||||||
{
|
|
||||||
return AvailableGenerators
|
|
||||||
.Where(g => !g.IsFloat)
|
|
||||||
.SelectMany(mc => mc.AvailableMnemonics)
|
|
||||||
.ToDictionary(kvp => kvp.Key, kvp => Source.GetFloat(kvp.Key));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: refactor me!
|
|
||||||
public Dictionary<string, char> AvailableMnemonics
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var dictionary = new Dictionary<string, char>();
|
|
||||||
foreach (var generator in AvailableGenerators)
|
|
||||||
{
|
|
||||||
foreach (var mnemonic in generator.AvailableMnemonics)
|
|
||||||
{
|
|
||||||
dictionary.Add(mnemonic.Key, mnemonic.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dictionary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Privates
|
|
||||||
|
|
||||||
private static readonly Dictionary<string, char> _basicController = new Dictionary<string, char>
|
|
||||||
{
|
|
||||||
{ "Up", 'U' },
|
|
||||||
{ "Down", 'D' },
|
|
||||||
{ "Left", 'L' },
|
|
||||||
{ "Right", 'R' },
|
|
||||||
{ "Select", 's' },
|
|
||||||
{ "Start", 'S' },
|
|
||||||
{ "B", 'B' },
|
|
||||||
{ "A", 'A' }
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly BooleanControllerMnemonicGenerator _nesConsoleControls = new BooleanControllerMnemonicGenerator(
|
|
||||||
"Console",
|
|
||||||
new Dictionary<string, char>
|
|
||||||
{
|
|
||||||
{ "Reset", 'r' },
|
|
||||||
{ "Power", 'P' },
|
|
||||||
}
|
|
||||||
)
|
|
||||||
{
|
|
||||||
ControllerPrefix = string.Empty
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly BooleanControllerMnemonicGenerator _fdsConsoleControls = new BooleanControllerMnemonicGenerator(
|
|
||||||
"Console",
|
|
||||||
new Dictionary<string, char>
|
|
||||||
{
|
|
||||||
{ "Reset", 'r' },
|
|
||||||
{ "Power", 'P' },
|
|
||||||
{ "FDS Eject", 'E' },
|
|
||||||
{ "FDS Insert 0", '0' },
|
|
||||||
{ "FDS Insert 1", '1' },
|
|
||||||
}
|
|
||||||
)
|
|
||||||
{
|
|
||||||
ControllerPrefix = string.Empty
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly List<IMnemonicGenerator> _controllerPorts =
|
|
||||||
new List<IMnemonicGenerator>
|
|
||||||
{
|
|
||||||
new BooleanControllerMnemonicGenerator("Player 1", _basicController)
|
|
||||||
{
|
|
||||||
ControllerPrefix = "P1"
|
|
||||||
},
|
|
||||||
new BooleanControllerMnemonicGenerator("Player 2", _basicController)
|
|
||||||
{
|
|
||||||
ControllerPrefix = "P2"
|
|
||||||
},
|
|
||||||
new BooleanControllerMnemonicGenerator("Player 3", _basicController)
|
|
||||||
{
|
|
||||||
ControllerPrefix = "P3"
|
|
||||||
},
|
|
||||||
new BooleanControllerMnemonicGenerator("Player 4", _basicController)
|
|
||||||
{
|
|
||||||
ControllerPrefix = "P4"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,7 +7,7 @@ namespace BizHawk.Client.Common
|
||||||
{
|
{
|
||||||
public partial class Bk2Movie : IMovie
|
public partial class Bk2Movie : IMovie
|
||||||
{
|
{
|
||||||
private readonly Bk2Header Header = new Bk2Header();
|
protected readonly Bk2Header Header = new Bk2Header();
|
||||||
private string _syncSettingsJson = string.Empty;
|
private string _syncSettingsJson = string.Empty;
|
||||||
private string _savestateBlob = string.Empty;
|
private string _savestateBlob = string.Empty;
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace BizHawk.Client.Common
|
||||||
|
|
||||||
public string Filename { get; set; }
|
public string Filename { get; set; }
|
||||||
|
|
||||||
public string PreferredExtension { get { return "bk2"; } }
|
public virtual string PreferredExtension { get { return "bk2"; } }
|
||||||
|
|
||||||
public bool Changes { get; private set; }
|
public bool Changes { get; private set; }
|
||||||
public bool IsCountingRerecords { get; set; }
|
public bool IsCountingRerecords { get; set; }
|
||||||
|
|
|
@ -9,625 +9,55 @@ using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
namespace BizHawk.Client.Common
|
namespace BizHawk.Client.Common
|
||||||
{
|
{
|
||||||
public class TasMovie : IMovie
|
public class TasMovie : Bk2Movie
|
||||||
{
|
{
|
||||||
// TODO: pass source into
|
public TasMovie(string path)
|
||||||
// TODO: preloading, or benchmark and see how much of a performaance gain it really is
|
: base(path)
|
||||||
// TODO: support loop Offset
|
|
||||||
// TODO: consider the fileformat of binary and lagged data
|
|
||||||
private readonly IMnemonicPorts _mg;
|
|
||||||
private readonly IController _source = Global.MovieOutputHardpoint;
|
|
||||||
|
|
||||||
public MovieRecord this[int index]
|
|
||||||
{
|
{
|
||||||
get { return _records[index]; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<string> ActivePlayers { get; set; }
|
|
||||||
|
|
||||||
public Dictionary<string, char> AvailableMnemonics
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _mg.AvailableMnemonics;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string PreferredExtension { get { return Extension; } }
|
|
||||||
|
|
||||||
public const string Extension = "tasproj";
|
|
||||||
|
|
||||||
public void ToggleButton(int frame, string buttonName)
|
|
||||||
{
|
|
||||||
InvalidateGreenzone(frame);
|
|
||||||
/*Serialize todo
|
|
||||||
_records[frame].SetButton(buttonName, !_records[frame].Buttons[buttonName]);
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetButton(int frame, string buttonName, bool value)
|
|
||||||
{
|
|
||||||
InvalidateGreenzone(frame);
|
|
||||||
/*Serialize TODO
|
|
||||||
_records[frame].SetButton(buttonName, value);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsPressed(int frame, string buttonName)
|
|
||||||
{
|
|
||||||
return true; //Serialize TODO - _records[frame].Buttons[buttonName];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes the greenzone content after the given frame
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="frame"></param>
|
|
||||||
private void InvalidateGreenzone(int frame)
|
|
||||||
{
|
|
||||||
for (int i = frame + 1; i < _records.Count; i++)
|
|
||||||
{
|
|
||||||
_records[i].ClearState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region IMovie Implementation
|
|
||||||
|
|
||||||
public TasMovie(string filename)
|
|
||||||
: this()
|
|
||||||
{
|
|
||||||
Filename = filename;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TasMovie()
|
public TasMovie()
|
||||||
|
: base()
|
||||||
{
|
{
|
||||||
_mg = MnemonicGeneratorFactory.Generate();
|
Header[HeaderKeys.MOVIEVERSION] = "BizHawk v2.0 Tasproj v1.0";
|
||||||
Filename = string.Empty;
|
|
||||||
Header = new BkmHeader();
|
|
||||||
Header[HeaderKeys.MOVIEVERSION] = "BizHawk v2.0";
|
|
||||||
_records = new MovieRecordList();
|
|
||||||
_mode = Moviemode.Inactive;
|
|
||||||
IsCountingRerecords = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SubtitleList Subtitles
|
public override string PreferredExtension
|
||||||
{
|
|
||||||
get { return Header.Subtitles; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public IList<string> Comments
|
|
||||||
{
|
|
||||||
get { return Header.Comments; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public string SyncSettingsJson
|
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return Header[HeaderKeys.SYNCSETTINGS];
|
return Extension;
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Header[HeaderKeys.SYNCSETTINGS] = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SavestateBinaryBase64Blob
|
public const string Extension = "tasproj";
|
||||||
|
|
||||||
|
public MovieRecord this[int index]
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return Header.SavestateBinaryBase64Blob;
|
// TODO
|
||||||
|
return new MovieRecord("", false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
public void SetBoolButton(int index, string button, bool value)
|
||||||
{
|
{
|
||||||
Header.SavestateBinaryBase64Blob = value;
|
// TODO
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong Rerecords
|
public Dictionary<string, string> ColumnNames
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return Header.Rerecords;
|
// TODO
|
||||||
}
|
return new Dictionary<string, string>()
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Header.Rerecords = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool StartsFromSavestate
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Header.StartsFromSavestate;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Header.StartsFromSavestate = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GameName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Header.GameName;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Header.GameName = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string SystemID
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Header.SystemID;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Header.SystemID = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Hash
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Header[HeaderKeys.SHA1];
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Header[HeaderKeys.SHA1] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Author
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Header[HeaderKeys.AUTHOR];
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Header[HeaderKeys.AUTHOR] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Core
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Header[HeaderKeys.CORE];
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Header[HeaderKeys.CORE] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Platform
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Header[HeaderKeys.PLATFORM];
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Header[HeaderKeys.PLATFORM] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string FirmwareHash
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Header[HeaderKeys.FIRMWARESHA1];
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Header[HeaderKeys.FIRMWARESHA1] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string EmulatorVersion
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Header[HeaderKeys.EMULATIONVERSION];
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Header[HeaderKeys.EMULATIONVERSION] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string BoardName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Header[HeaderKeys.BOARDNAME];
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Header[HeaderKeys.BOARDNAME] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool PreLoadHeaderAndLength(HawkFile hawkFile)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IDictionary<string, string> HeaderEntries
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Header;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SaveBackup()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Filename { get; set; }
|
|
||||||
|
|
||||||
public BkmHeader Header { get; private set; }
|
|
||||||
|
|
||||||
public bool IsActive
|
|
||||||
{
|
|
||||||
get { return _mode != Moviemode.Inactive; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsPlaying
|
|
||||||
{
|
|
||||||
get { return _mode == Moviemode.Play; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsRecording
|
|
||||||
{
|
|
||||||
get { return _mode == Moviemode.Record; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsFinished
|
|
||||||
{
|
|
||||||
get { return false; } //a TasMovie is never in this mode.
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsCountingRerecords { get; set; }
|
|
||||||
|
|
||||||
public bool Changes { get; set; }
|
|
||||||
|
|
||||||
public TimeSpan Time
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
double dblseconds = GetSeconds(_records.Count);
|
|
||||||
int seconds = (int)(dblseconds % 60);
|
|
||||||
int days = seconds / 86400;
|
|
||||||
int hours = seconds / 3600;
|
|
||||||
int minutes = (seconds / 60) % 60;
|
|
||||||
int milliseconds = (int)((dblseconds - seconds) * 1000);
|
|
||||||
return new TimeSpan(days, hours, minutes, seconds, milliseconds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public double FrameCount
|
|
||||||
{
|
|
||||||
get { return _records.Count; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int InputLogLength
|
|
||||||
{
|
|
||||||
get { return _records.Count; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetInput(int frame)
|
|
||||||
{
|
|
||||||
if (frame < _records.Count)
|
|
||||||
{
|
|
||||||
if (frame >= 0)
|
|
||||||
{
|
|
||||||
if (!_records[frame].HasState)
|
|
||||||
{
|
|
||||||
_records[frame].CaptureSate();
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty; //Serialize TODO _mg.GenerateMnemonicString(_records[frame].Buttons);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_mode = Moviemode.Record;
|
|
||||||
|
|
||||||
/* Serialize TODO
|
|
||||||
var buttons = _mg.ParseMnemonicString(_mg.EmptyMnemonic);
|
|
||||||
_records.Add(new MovieRecord(buttons, true));
|
|
||||||
*/
|
|
||||||
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetInputLog()
|
|
||||||
{
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
foreach (var record in _records)
|
|
||||||
{
|
|
||||||
sb.AppendLine(record.SerializedInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SwitchToRecord()
|
|
||||||
{
|
|
||||||
_mode = Moviemode.Record;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SwitchToPlay()
|
|
||||||
{
|
|
||||||
_mode = Moviemode.Play;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StartNewPlayback()
|
|
||||||
{
|
|
||||||
_mode = Moviemode.Play;
|
|
||||||
Global.Emulator.ClearSaveRam(); // should this exist??
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Stop(bool saveChanges = true)
|
|
||||||
{
|
|
||||||
// adelikat: I think Tastudio should be in charge of saving, and so we should not attempt to manage any logic like that here
|
|
||||||
// EmuHawk client UI assumes someone has already picked a filename ahead of time and that it is in charge of movies
|
|
||||||
_mode = Moviemode.Inactive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Truncate(int frame)
|
|
||||||
{
|
|
||||||
_records.Truncate(frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ClearFrame(int frame)
|
|
||||||
{
|
|
||||||
if (frame < _records.Count)
|
|
||||||
{
|
|
||||||
Changes = true;
|
|
||||||
_records[frame].ClearInput();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AppendFrame(IController source)
|
|
||||||
{
|
|
||||||
Changes = true;
|
|
||||||
/* Serialize TODO
|
|
||||||
_mg.Source = source;
|
|
||||||
var record = new MovieRecord(_mg.GetBoolButtons(), true);
|
|
||||||
_records.Add(record);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecordFrame(int frame, IController source)
|
|
||||||
{
|
|
||||||
if (_mode == Moviemode.Record)
|
|
||||||
{
|
|
||||||
Changes = true;
|
|
||||||
if (Global.Config.VBAStyleMovieLoadState)
|
|
||||||
{
|
|
||||||
if (Global.Emulator.Frame < _records.Count)
|
|
||||||
{
|
|
||||||
_records.Truncate(Global.Emulator.Frame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frame < _records.Count)
|
|
||||||
{
|
|
||||||
PokeFrame(frame, source);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AppendFrame(source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PokeFrame(int frame, IController source)
|
|
||||||
{
|
|
||||||
InvalidateGreenzone(frame);
|
|
||||||
if (frame < _records.Count)
|
|
||||||
{
|
|
||||||
Changes = true;
|
|
||||||
_mg.Source = source;
|
|
||||||
/* Serialize TODO
|
|
||||||
_records[frame].SetInput(_mg.GetBoolButtons());
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public double Fps
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var system = Header[HeaderKeys.PLATFORM];
|
|
||||||
var pal = Header.ContainsKey(HeaderKeys.PAL) &&
|
|
||||||
Header[HeaderKeys.PAL] == "1";
|
|
||||||
|
|
||||||
return _frameRates[system, pal];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StartNewRecording()
|
|
||||||
{
|
|
||||||
SwitchToRecord();
|
|
||||||
|
|
||||||
// TODO: MakeBackup logic - Tastudio logic should be to always make backups before saving!
|
|
||||||
|
|
||||||
if (Changes && !String.IsNullOrWhiteSpace(Filename))
|
|
||||||
{
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
_records.Clear();
|
|
||||||
Header.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Load()
|
|
||||||
{
|
|
||||||
var file = new FileInfo(Filename);
|
|
||||||
if (!file.Exists)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// there's a lot of common code here with SavestateManager. refactor?
|
|
||||||
using (BinaryStateLoader bl = BinaryStateLoader.LoadAndDetect(Filename))
|
|
||||||
{
|
|
||||||
if (bl == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Header.Clear();
|
|
||||||
_records.Clear();
|
|
||||||
|
|
||||||
bl.GetLump(BinaryStateLump.Movieheader, true,
|
|
||||||
delegate(TextReader tr)
|
|
||||||
{
|
|
||||||
string line;
|
|
||||||
while ((line = tr.ReadLine()) != null)
|
|
||||||
if (!Header.ParseLineFromFile(line))
|
|
||||||
Header.Comments.Add(line);
|
|
||||||
});
|
|
||||||
bl.GetLump(BinaryStateLump.Input, true,
|
|
||||||
delegate(TextReader tr)
|
|
||||||
{
|
|
||||||
string line = string.Empty;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
line = tr.ReadLine();
|
|
||||||
if (line == null)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (line.StartsWith("|"))
|
|
||||||
{
|
|
||||||
/* Serialize TODO
|
|
||||||
var parsedButtons = _mg.ParseMnemonicString(line);
|
|
||||||
_records.Add(new MovieRecord(parsedButtons, captureState: false));
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (StartsFromSavestate)
|
|
||||||
{
|
|
||||||
// should we raise some sort of error if there's a savestate in the archive but Header.StartsFromSavestate is false?
|
|
||||||
bl.GetCoreState(
|
|
||||||
delegate(Stream s)
|
|
||||||
{
|
|
||||||
BinaryReader br = new BinaryReader(s);
|
|
||||||
Global.Emulator.LoadStateBinary(br);
|
|
||||||
},
|
|
||||||
delegate(Stream s)
|
|
||||||
{
|
|
||||||
StreamReader sr = new StreamReader(s);
|
|
||||||
Global.Emulator.LoadStateText(sr);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
bl.GetLump(BinaryStateLump.Framebuffer, false,
|
|
||||||
delegate(BinaryReader br)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
var buff = Global.Emulator.VideoProvider.GetVideoBuffer();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for (i = 0; i < buff.Length; i++)
|
|
||||||
{
|
|
||||||
int j = br.ReadInt32();
|
|
||||||
buff[i] = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (EndOfStreamException) { }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_mode = Moviemode.Play;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Save()
|
|
||||||
{
|
|
||||||
// there's a lot of common code here with SavestateManager. refactor?
|
|
||||||
|
|
||||||
using (FileStream fs = new FileStream(Filename, FileMode.Create, FileAccess.Write))
|
|
||||||
using (BinaryStateSaver bs = new BinaryStateSaver(fs))
|
|
||||||
{
|
|
||||||
bs.PutLump(BinaryStateLump.Movieheader, (tw) => tw.WriteLine(Header.ToString()));
|
|
||||||
bs.PutLump(BinaryStateLump.Input, (tw) => tw.WriteLine(GetInputLog()));
|
|
||||||
if (StartsFromSavestate)
|
|
||||||
{
|
|
||||||
#if true
|
|
||||||
bs.PutLump(BinaryStateLump.CorestateText, (tw) => Global.Emulator.SaveStateText(tw));
|
|
||||||
#else
|
|
||||||
bs.PutLump(BinaryStateLump.Corestate, (bw) => Global.Emulator.SaveStateBinary(bw));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Changes = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CheckTimeLines(TextReader reader, out string errorMessage)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ExtractInputLog(TextReader reader, out string errorMessage)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private
|
|
||||||
|
|
||||||
private enum Moviemode { Inactive, Play, Record, Finished }
|
|
||||||
private readonly MovieRecordList _records;
|
|
||||||
private Moviemode _mode;
|
|
||||||
private readonly PlatformFrameRates _frameRates = new PlatformFrameRates();
|
|
||||||
|
|
||||||
private double GetSeconds(int frameCount)
|
|
||||||
{
|
|
||||||
double frames = frameCount;
|
|
||||||
|
|
||||||
if (frames < 1)
|
|
||||||
{
|
{
|
||||||
return 0;
|
{ "A", "A" },
|
||||||
|
{ "B", "B" }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var system = Header[HeaderKeys.PLATFORM];
|
|
||||||
var pal = Header.ContainsKey(HeaderKeys.PAL) && Header[HeaderKeys.PAL] == "1";
|
|
||||||
|
|
||||||
return frames / _frameRates[system, pal];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue