Rip out a bunch of stuff from my previous attempt at a mnemonics overhaul and from TasMovie

This commit is contained in:
adelikat 2014-06-14 21:09:14 +00:00
parent b7c692e52a
commit d27df7816d
8 changed files with 25 additions and 1030 deletions

View File

@ -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;
}
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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);
}
}
}
}

View File

@ -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
}
}

View File

@ -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;

View File

@ -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; }

View File

@ -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
}
}