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
|
||||
{
|
||||
private readonly Bk2Header Header = new Bk2Header();
|
||||
protected readonly Bk2Header Header = new Bk2Header();
|
||||
private string _syncSettingsJson = string.Empty;
|
||||
private string _savestateBlob = string.Empty;
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace BizHawk.Client.Common
|
|||
|
||||
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 IsCountingRerecords { get; set; }
|
||||
|
|
|
@ -9,625 +9,55 @@ using BizHawk.Emulation.Common;
|
|||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public class TasMovie : IMovie
|
||||
public class TasMovie : Bk2Movie
|
||||
{
|
||||
// TODO: pass source into
|
||||
// TODO: preloading, or benchmark and see how much of a performaance gain it really is
|
||||
// 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]
|
||||
public TasMovie(string path)
|
||||
: base(path)
|
||||
{
|
||||
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()
|
||||
: base()
|
||||
{
|
||||
_mg = MnemonicGeneratorFactory.Generate();
|
||||
Filename = string.Empty;
|
||||
Header = new BkmHeader();
|
||||
Header[HeaderKeys.MOVIEVERSION] = "BizHawk v2.0";
|
||||
_records = new MovieRecordList();
|
||||
_mode = Moviemode.Inactive;
|
||||
IsCountingRerecords = true;
|
||||
Header[HeaderKeys.MOVIEVERSION] = "BizHawk v2.0 Tasproj v1.0";
|
||||
}
|
||||
|
||||
public SubtitleList Subtitles
|
||||
{
|
||||
get { return Header.Subtitles; }
|
||||
}
|
||||
|
||||
public IList<string> Comments
|
||||
{
|
||||
get { return Header.Comments; }
|
||||
}
|
||||
|
||||
public string SyncSettingsJson
|
||||
public override string PreferredExtension
|
||||
{
|
||||
get
|
||||
{
|
||||
return Header[HeaderKeys.SYNCSETTINGS];
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Header[HeaderKeys.SYNCSETTINGS] = value;
|
||||
return Extension;
|
||||
}
|
||||
}
|
||||
|
||||
public string SavestateBinaryBase64Blob
|
||||
public const string Extension = "tasproj";
|
||||
|
||||
public MovieRecord this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return Header.SavestateBinaryBase64Blob;
|
||||
// TODO
|
||||
return new MovieRecord("", false);
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Header.SavestateBinaryBase64Blob = value;
|
||||
}
|
||||
}
|
||||
public void SetBoolButton(int index, string button, bool value)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
public ulong Rerecords
|
||||
public Dictionary<string, string> ColumnNames
|
||||
{
|
||||
get
|
||||
{
|
||||
return Header.Rerecords;
|
||||
}
|
||||
|
||||
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)
|
||||
// TODO
|
||||
return new Dictionary<string, string>()
|
||||
{
|
||||
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;
|
||||
{ "A", "A" },
|
||||
{ "B", "B" }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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