2013-12-01 20:55:52 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
2013-12-10 01:45:45 +00:00
|
|
|
|
using System.Text;
|
2014-06-11 21:14:13 +00:00
|
|
|
|
|
|
|
|
|
using BizHawk.Common;
|
2013-12-07 16:31:04 +00:00
|
|
|
|
using BizHawk.Emulation.Common;
|
2014-08-23 18:02:02 +00:00
|
|
|
|
using System.ComponentModel;
|
2013-12-07 16:31:04 +00:00
|
|
|
|
|
2013-12-01 20:55:52 +00:00
|
|
|
|
namespace BizHawk.Client.Common
|
|
|
|
|
{
|
2014-08-23 18:02:02 +00:00
|
|
|
|
public sealed partial class TasMovie : Bk2Movie, INotifyPropertyChanged
|
2013-12-01 20:55:52 +00:00
|
|
|
|
{
|
2014-08-29 02:40:45 +00:00
|
|
|
|
private readonly Bk2MnemonicConstants Mnemonics = new Bk2MnemonicConstants();
|
2014-07-07 19:32:37 +00:00
|
|
|
|
private List<bool> LagLog = new List<bool>();
|
2014-07-17 18:21:12 +00:00
|
|
|
|
private readonly TasStateManager StateManager;
|
2014-08-24 14:41:56 +00:00
|
|
|
|
public TasMovieMarkerList Markers { get; set; }
|
2013-12-08 18:44:41 +00:00
|
|
|
|
|
2014-08-24 15:07:27 +00:00
|
|
|
|
public TasMovie(string path) : base(path)
|
|
|
|
|
{
|
|
|
|
|
// TODO: how to call the default constructor AND the base(path) constructor? And is base(path) calling base() ?
|
|
|
|
|
StateManager = new TasStateManager(this);
|
|
|
|
|
Header[HeaderKeys.MOVIEVERSION] = "BizHawk v2.0 Tasproj v1.0";
|
|
|
|
|
Markers = new TasMovieMarkerList(this);
|
|
|
|
|
Markers.CollectionChanged += Markers_CollectionChanged;
|
|
|
|
|
Markers.Add(0, StartsFromSavestate ? "Savestate" : "Power on");
|
|
|
|
|
}
|
2013-12-01 22:29:38 +00:00
|
|
|
|
|
2014-06-14 20:17:07 +00:00
|
|
|
|
public TasMovie()
|
2014-06-14 21:09:14 +00:00
|
|
|
|
: base()
|
2013-12-01 22:29:38 +00:00
|
|
|
|
{
|
2014-07-17 18:21:12 +00:00
|
|
|
|
StateManager = new TasStateManager(this);
|
2014-07-13 14:26:40 +00:00
|
|
|
|
Header[HeaderKeys.MOVIEVERSION] = "BizHawk v2.0 Tasproj v1.0";
|
2014-07-17 18:38:30 +00:00
|
|
|
|
Markers = new TasMovieMarkerList(this);
|
2014-08-24 14:41:56 +00:00
|
|
|
|
Markers.CollectionChanged += Markers_CollectionChanged;
|
2014-07-15 23:43:17 +00:00
|
|
|
|
Markers.Add(0, StartsFromSavestate ? "Savestate" : "Power on");
|
2014-06-11 21:20:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-06-14 21:09:14 +00:00
|
|
|
|
public override string PreferredExtension
|
2014-06-11 21:20:23 +00:00
|
|
|
|
{
|
2014-07-07 18:00:25 +00:00
|
|
|
|
get { return Extension; }
|
2014-06-11 21:20:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-24 14:41:56 +00:00
|
|
|
|
#region Events and Handlers
|
2014-08-23 18:02:02 +00:00
|
|
|
|
|
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
|
|
|
|
|
|
private bool _changes;
|
|
|
|
|
public override bool Changes
|
|
|
|
|
{
|
|
|
|
|
get { return _changes; }
|
|
|
|
|
protected set
|
|
|
|
|
{
|
|
|
|
|
if (_changes != value)
|
|
|
|
|
{
|
|
|
|
|
_changes = value;
|
|
|
|
|
OnPropertyChanged("Changes");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//This event is Raised ony when Changes is TOGGLED.
|
|
|
|
|
private void OnPropertyChanged(string propertyName)
|
|
|
|
|
{
|
|
|
|
|
if (PropertyChanged != null)
|
|
|
|
|
{
|
|
|
|
|
//Raising the event when FirstName or LastName property value changed
|
|
|
|
|
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-24 14:41:56 +00:00
|
|
|
|
void Markers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
Changes = true;
|
|
|
|
|
}
|
2013-12-01 20:55:52 +00:00
|
|
|
|
|
2014-08-24 14:41:56 +00:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
public new const string Extension = "tasproj";
|
2014-07-13 14:13:20 +00:00
|
|
|
|
|
2014-07-07 18:03:02 +00:00
|
|
|
|
public TasMovieRecord this[int index]
|
2013-12-02 21:57:48 +00:00
|
|
|
|
{
|
2013-12-03 01:43:02 +00:00
|
|
|
|
get
|
|
|
|
|
{
|
2014-07-07 18:40:42 +00:00
|
|
|
|
return new TasMovieRecord
|
2014-07-07 18:00:25 +00:00
|
|
|
|
{
|
2014-07-07 18:40:42 +00:00
|
|
|
|
State = StateManager[index],
|
2014-07-13 22:17:31 +00:00
|
|
|
|
LogEntry = GetInputLogEntry(index),
|
2014-07-10 19:24:21 +00:00
|
|
|
|
Lagged = (index < LagLog.Count) ? LagLog[index] : (bool?)null
|
2014-07-07 18:00:25 +00:00
|
|
|
|
};
|
2013-12-03 01:43:02 +00:00
|
|
|
|
}
|
2013-12-02 21:57:48 +00:00
|
|
|
|
}
|
2014-07-08 13:33:01 +00:00
|
|
|
|
|
2014-07-11 18:06:18 +00:00
|
|
|
|
public void ClearChanges()
|
|
|
|
|
{
|
|
|
|
|
Changes = false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-08 13:33:01 +00:00
|
|
|
|
public override void StartNewRecording()
|
|
|
|
|
{
|
|
|
|
|
LagLog.Clear();
|
|
|
|
|
StateManager.Clear();
|
2014-07-09 16:35:39 +00:00
|
|
|
|
Markers.Clear();
|
2014-07-08 13:33:01 +00:00
|
|
|
|
base.StartNewRecording();
|
2014-07-15 23:43:17 +00:00
|
|
|
|
|
|
|
|
|
Markers.Add(0, StartsFromSavestate ? "Savestate" : "Power on");
|
2014-07-08 13:33:01 +00:00
|
|
|
|
}
|
2014-07-09 16:35:39 +00:00
|
|
|
|
|
2014-08-23 18:02:02 +00:00
|
|
|
|
public override void SwitchToPlay()
|
|
|
|
|
{
|
|
|
|
|
_mode = Moviemode.Play;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-11 19:58:24 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Removes lag log and greenzone after this frame
|
|
|
|
|
/// </summary>
|
2014-08-23 02:06:56 +00:00
|
|
|
|
/// <param name="frame">The last frame that can be valid.</param>
|
2014-07-11 19:58:24 +00:00
|
|
|
|
private void InvalidateAfter(int frame)
|
|
|
|
|
{
|
|
|
|
|
if (frame < LagLog.Count)
|
|
|
|
|
{
|
|
|
|
|
LagLog.RemoveRange(frame + 1, LagLog.Count - frame - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StateManager.Invalidate(frame + 1);
|
2014-08-22 17:04:31 +00:00
|
|
|
|
Changes = true; // TODO check if this actually removed anything before flagging changes
|
2014-07-11 19:58:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-07-10 02:45:56 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns the mnemonic value for boolean buttons, and actual value for floats,
|
2014-08-22 17:04:31 +00:00
|
|
|
|
/// for a given frame and button.
|
2014-07-10 02:45:56 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
public string DisplayValue(int frame, string buttonName)
|
|
|
|
|
{
|
|
|
|
|
var adapter = GetInputState(frame);
|
2014-07-16 23:04:56 +00:00
|
|
|
|
return CreateDisplayValueForButton(adapter, buttonName);
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-29 01:59:08 +00:00
|
|
|
|
public string CreateDisplayValueForButton(IController adapter, string buttonName)
|
2014-07-16 23:04:56 +00:00
|
|
|
|
{
|
2014-07-10 02:45:56 +00:00
|
|
|
|
if (adapter.Type.BoolButtons.Contains(buttonName))
|
|
|
|
|
{
|
|
|
|
|
return adapter.IsPressed(buttonName) ?
|
2014-08-29 02:40:45 +00:00
|
|
|
|
Mnemonics[buttonName].ToString() :
|
2014-07-10 02:45:56 +00:00
|
|
|
|
string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (adapter.Type.FloatControls.Contains(buttonName))
|
|
|
|
|
{
|
2014-07-11 16:26:19 +00:00
|
|
|
|
return adapter.GetFloat(buttonName).ToString();
|
2014-07-10 02:45:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "!";
|
|
|
|
|
}
|
2014-07-10 20:40:50 +00:00
|
|
|
|
|
|
|
|
|
public void ToggleBoolState(int frame, string buttonName)
|
|
|
|
|
{
|
|
|
|
|
if (frame < _log.Count)
|
|
|
|
|
{
|
|
|
|
|
var adapter = GetInputState(frame) as Bk2ControllerAdapter;
|
|
|
|
|
adapter[buttonName] = !adapter.IsPressed(buttonName);
|
|
|
|
|
|
|
|
|
|
var lg = LogGeneratorInstance();
|
|
|
|
|
lg.SetSource(adapter);
|
|
|
|
|
_log[frame] = lg.GenerateLogEntry();
|
|
|
|
|
Changes = true;
|
2014-07-11 19:58:24 +00:00
|
|
|
|
InvalidateAfter(frame);
|
2014-07-10 20:40:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetBoolState(int frame, string buttonName, bool val)
|
|
|
|
|
{
|
|
|
|
|
if (frame < _log.Count)
|
|
|
|
|
{
|
|
|
|
|
var adapter = GetInputState(frame) as Bk2ControllerAdapter;
|
|
|
|
|
var old = adapter[buttonName];
|
|
|
|
|
adapter[buttonName] = val;
|
|
|
|
|
|
|
|
|
|
var lg = LogGeneratorInstance();
|
|
|
|
|
lg.SetSource(adapter);
|
|
|
|
|
_log[frame] = lg.GenerateLogEntry();
|
|
|
|
|
|
|
|
|
|
if (old != val)
|
|
|
|
|
{
|
2014-07-11 19:58:24 +00:00
|
|
|
|
InvalidateAfter(frame);
|
2014-07-10 20:40:50 +00:00
|
|
|
|
Changes = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-11 16:26:19 +00:00
|
|
|
|
public void SetFloatState(int frame, string buttonName, float val)
|
|
|
|
|
{
|
|
|
|
|
if (frame < _log.Count)
|
|
|
|
|
{
|
|
|
|
|
var adapter = GetInputState(frame) as Bk2ControllerAdapter;
|
|
|
|
|
var old = adapter.GetFloat(buttonName);
|
|
|
|
|
adapter.SetFloat(buttonName, val);
|
|
|
|
|
|
|
|
|
|
var lg = LogGeneratorInstance();
|
|
|
|
|
lg.SetSource(adapter);
|
|
|
|
|
_log[frame] = lg.GenerateLogEntry();
|
|
|
|
|
|
|
|
|
|
if (old != val)
|
|
|
|
|
{
|
2014-07-11 19:58:24 +00:00
|
|
|
|
InvalidateAfter(frame);
|
2014-07-11 16:26:19 +00:00
|
|
|
|
Changes = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-10 20:40:50 +00:00
|
|
|
|
public bool BoolIsPressed(int frame, string buttonName)
|
|
|
|
|
{
|
|
|
|
|
var adapter = GetInputState(frame) as Bk2ControllerAdapter;
|
|
|
|
|
return adapter.IsPressed(buttonName);
|
|
|
|
|
}
|
2014-07-10 20:48:43 +00:00
|
|
|
|
|
2014-07-11 16:26:19 +00:00
|
|
|
|
public float GetFloatValue(int frame, string buttonName)
|
|
|
|
|
{
|
|
|
|
|
var adapter = GetInputState(frame) as Bk2ControllerAdapter;
|
|
|
|
|
return adapter.GetFloat(buttonName);
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-13 22:17:31 +00:00
|
|
|
|
// TODO: try not to need this, or at least use GetInputState and then a log entry generator
|
|
|
|
|
public string GetInputLogEntry(int frame)
|
2014-07-10 20:48:43 +00:00
|
|
|
|
{
|
|
|
|
|
if (Global.Emulator.Frame == frame && !StateManager.HasState(frame))
|
|
|
|
|
{
|
|
|
|
|
StateManager.Capture();
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-13 20:51:19 +00:00
|
|
|
|
if (Global.Emulator.Frame == frame && frame >= LagLog.Count)
|
|
|
|
|
{
|
|
|
|
|
LagLog.Add(Global.Emulator.IsLagFrame);
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-13 22:17:31 +00:00
|
|
|
|
if (frame < FrameCount && frame >= 0)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
int getframe;
|
|
|
|
|
|
|
|
|
|
if (LoopOffset.HasValue)
|
|
|
|
|
{
|
|
|
|
|
if (frame < _log.Count)
|
|
|
|
|
{
|
|
|
|
|
getframe = frame;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
getframe = ((frame - LoopOffset.Value) % (_log.Count - LoopOffset.Value)) + LoopOffset.Value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
getframe = frame;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return _log[getframe];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return string.Empty;
|
2014-07-10 20:48:43 +00:00
|
|
|
|
}
|
2014-07-11 15:43:47 +00:00
|
|
|
|
|
|
|
|
|
public TasStateManager.ManagerSettings GreenzoneSettings
|
|
|
|
|
{
|
|
|
|
|
get { return StateManager.Settings; }
|
|
|
|
|
}
|
2014-07-13 15:26:50 +00:00
|
|
|
|
|
|
|
|
|
public int LastEmulatedFrame
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2014-07-16 23:04:56 +00:00
|
|
|
|
if (StateManager.StateCount > 0)
|
|
|
|
|
{
|
2014-08-24 21:29:51 +00:00
|
|
|
|
return StateManager.LastKey;
|
2014-07-16 23:04:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
2014-07-13 15:26:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-07-17 18:21:12 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Captures the current frame into the greenzone
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void CaptureCurrentState()
|
|
|
|
|
{
|
|
|
|
|
StateManager.Capture();
|
|
|
|
|
}
|
2013-12-01 20:55:52 +00:00
|
|
|
|
}
|
|
|
|
|
}
|