475 lines
13 KiB
C#
475 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
|
|
using BizHawk.Common;
|
|
using BizHawk.Emulation.Common;
|
|
using BizHawk.Emulation.Common.IEmulatorExtensions;
|
|
|
|
namespace BizHawk.Client.Common
|
|
{
|
|
public partial class TasMovie
|
|
{
|
|
public TasMovieChangeLog ChangeLog;
|
|
|
|
public override void RecordFrame(int frame, IController source)
|
|
{
|
|
if (frame != 0)
|
|
ChangeLog.AddGeneralUndo(frame, frame, "Record Frame: " + frame);
|
|
|
|
base.RecordFrame(frame, source);
|
|
|
|
LagLog.RemoveFrom(frame);
|
|
LagLog[frame] = Global.Emulator.AsInputPollable().IsLagFrame;
|
|
|
|
if (frame != 0)
|
|
ChangeLog.SetGeneralRedo();
|
|
}
|
|
|
|
public override void Truncate(int frame)
|
|
{
|
|
bool endBatch = ChangeLog.BeginNewBatch("Truncate Movie: " + frame, true);
|
|
ChangeLog.AddGeneralUndo(frame, InputLogLength - 1);
|
|
|
|
if (frame < _log.Count - 1)
|
|
{
|
|
Changes = true;
|
|
}
|
|
|
|
base.Truncate(frame);
|
|
|
|
LagLog.RemoveFrom(frame);
|
|
StateManager.Invalidate(frame);
|
|
Markers.TruncateAt(frame);
|
|
|
|
ChangeLog.SetGeneralRedo();
|
|
if (endBatch)
|
|
ChangeLog.EndBatch();
|
|
}
|
|
|
|
public override void PokeFrame(int frame, IController source)
|
|
{
|
|
ChangeLog.AddGeneralUndo(frame, frame, "Set Frame At: " + frame);
|
|
|
|
base.PokeFrame(frame, source);
|
|
InvalidateAfter(frame);
|
|
|
|
ChangeLog.SetGeneralRedo();
|
|
}
|
|
public void SetFrame(int frame, string source)
|
|
{
|
|
ChangeLog.AddGeneralUndo(frame, frame, "Set Frame At: " + frame);
|
|
|
|
base.SetFrameAt(frame, source);
|
|
InvalidateAfter(frame);
|
|
|
|
ChangeLog.SetGeneralRedo();
|
|
}
|
|
|
|
public override void ClearFrame(int frame)
|
|
{
|
|
ChangeLog.AddGeneralUndo(frame, frame, "Clear Frame: " + frame);
|
|
|
|
base.ClearFrame(frame);
|
|
InvalidateAfter(frame);
|
|
|
|
ChangeLog.SetGeneralRedo();
|
|
}
|
|
|
|
public void RemoveFrame(int frame)
|
|
{
|
|
bool endBatch = ChangeLog.BeginNewBatch("Remove Frame: " + frame, true);
|
|
ChangeLog.AddGeneralUndo(frame, InputLogLength - 1);
|
|
|
|
_log.RemoveAt(frame);
|
|
if (BindMarkersToInput)
|
|
{
|
|
bool wasRecording = ChangeLog.IsRecording;
|
|
ChangeLog.IsRecording = false;
|
|
int firstIndex = Markers.FindIndex(m => m.Frame >= frame);
|
|
if (firstIndex != -1)
|
|
{
|
|
for (int i = firstIndex; i < Markers.Count; i++)
|
|
{
|
|
TasMovieMarker m = Markers.ElementAt(i);
|
|
if (m.Frame == frame)
|
|
Markers.Remove(m);
|
|
else
|
|
Markers.Move(m.Frame, m.Frame - 1);
|
|
}
|
|
}
|
|
ChangeLog.IsRecording = wasRecording;
|
|
}
|
|
|
|
Changes = true;
|
|
InvalidateAfter(frame);
|
|
|
|
ChangeLog.SetGeneralRedo();
|
|
if (endBatch)
|
|
ChangeLog.EndBatch();
|
|
}
|
|
public void RemoveFrames(int[] frames)
|
|
{
|
|
if (frames.Any())
|
|
{
|
|
var invalidateAfter = frames.Min();
|
|
|
|
bool endBatch = ChangeLog.BeginNewBatch("Remove Multiple Frames", true);
|
|
ChangeLog.AddGeneralUndo(invalidateAfter, InputLogLength - 1);
|
|
|
|
foreach (var frame in frames.OrderByDescending(x => x)) // Removin them in reverse order allows us to remove by index;
|
|
{
|
|
_log.RemoveAt(frame);
|
|
if (BindMarkersToInput) // TODO: This is slow, is there a better way to do it?
|
|
{
|
|
bool wasRecording = ChangeLog.IsRecording;
|
|
ChangeLog.IsRecording = false;
|
|
int firstIndex = Markers.FindIndex(m => m.Frame >= frame);
|
|
if (firstIndex != -1)
|
|
{
|
|
for (int i = firstIndex; i < Markers.Count; i++)
|
|
{
|
|
TasMovieMarker m = Markers.ElementAt(i);
|
|
if (m.Frame == frame)
|
|
Markers.Remove(m);
|
|
else
|
|
Markers.Move(m.Frame, m.Frame - 1);
|
|
}
|
|
}
|
|
ChangeLog.IsRecording = wasRecording;
|
|
}
|
|
}
|
|
|
|
Changes = true;
|
|
InvalidateAfter(invalidateAfter);
|
|
|
|
ChangeLog.SetGeneralRedo();
|
|
if (endBatch)
|
|
ChangeLog.EndBatch();
|
|
}
|
|
}
|
|
public void RemoveFrames(int removeStart, int removeUpTo)
|
|
{
|
|
bool endBatch = ChangeLog.BeginNewBatch("Remove Frames: " + removeStart + "-" + removeUpTo, true);
|
|
ChangeLog.AddGeneralUndo(removeStart, InputLogLength - 1);
|
|
|
|
for (int i = removeUpTo - 1; i >= removeStart; i--)
|
|
_log.RemoveAt(i);
|
|
|
|
if (BindMarkersToInput)
|
|
{
|
|
bool wasRecording = ChangeLog.IsRecording;
|
|
ChangeLog.IsRecording = false;
|
|
int firstIndex = Markers.FindIndex(m => m.Frame >= removeStart);
|
|
if (firstIndex != -1)
|
|
{
|
|
for (int i = firstIndex; i < Markers.Count; i++)
|
|
{
|
|
TasMovieMarker m = Markers.ElementAt(i);
|
|
if (m.Frame < removeUpTo)
|
|
Markers.Remove(m);
|
|
else
|
|
Markers.Move(m.Frame, m.Frame - (removeUpTo - removeStart));
|
|
}
|
|
}
|
|
ChangeLog.IsRecording = wasRecording;
|
|
}
|
|
|
|
Changes = true;
|
|
InvalidateAfter(removeStart);
|
|
|
|
ChangeLog.SetGeneralRedo();
|
|
if (endBatch)
|
|
ChangeLog.EndBatch();
|
|
}
|
|
|
|
public void InsertInput(int frame, string inputState)
|
|
{
|
|
bool endBatch = ChangeLog.BeginNewBatch("Insert Frame: " + frame, true);
|
|
ChangeLog.AddGeneralUndo(frame, InputLogLength);
|
|
|
|
_log.Insert(frame, inputState);
|
|
Changes = true;
|
|
InvalidateAfter(frame);
|
|
|
|
if (BindMarkersToInput)
|
|
{
|
|
bool wasRecording = ChangeLog.IsRecording;
|
|
ChangeLog.IsRecording = false;
|
|
int firstIndex = Markers.FindIndex(m => m.Frame >= frame);
|
|
if (firstIndex != -1)
|
|
{
|
|
for (int i = firstIndex; i < Markers.Count; i++)
|
|
{
|
|
TasMovieMarker m = Markers.ElementAt(i);
|
|
Markers.Move(m.Frame, m.Frame + 1);
|
|
}
|
|
}
|
|
ChangeLog.IsRecording = wasRecording;
|
|
}
|
|
|
|
ChangeLog.SetGeneralRedo();
|
|
if (endBatch)
|
|
ChangeLog.EndBatch();
|
|
}
|
|
public void InsertInput(int frame, IEnumerable<string> inputLog)
|
|
{
|
|
bool endBatch = ChangeLog.BeginNewBatch("Insert Frame: " + frame, true);
|
|
ChangeLog.AddGeneralUndo(frame, InputLogLength + inputLog.Count() - 1);
|
|
|
|
_log.InsertRange(frame, inputLog);
|
|
Changes = true;
|
|
InvalidateAfter(frame);
|
|
|
|
if (BindMarkersToInput)
|
|
{
|
|
bool wasRecording = ChangeLog.IsRecording;
|
|
ChangeLog.IsRecording = false;
|
|
int firstIndex = Markers.FindIndex(m => m.Frame >= frame);
|
|
if (firstIndex != -1)
|
|
{
|
|
for (int i = firstIndex; i < Markers.Count; i++)
|
|
{
|
|
TasMovieMarker m = Markers.ElementAt(i);
|
|
Markers.Move(m.Frame, m.Frame + inputLog.Count());
|
|
}
|
|
}
|
|
ChangeLog.IsRecording = wasRecording;
|
|
}
|
|
|
|
ChangeLog.SetGeneralRedo();
|
|
if (endBatch)
|
|
ChangeLog.EndBatch();
|
|
}
|
|
public void InsertInput(int frame, IEnumerable<IController> inputStates)
|
|
{
|
|
// ChangeLog is done in the InsertInput call.
|
|
var lg = LogGeneratorInstance();
|
|
|
|
var inputLog = new List<string>();
|
|
|
|
foreach (var input in inputStates)
|
|
{
|
|
lg.SetSource(input);
|
|
inputLog.Add(lg.GenerateLogEntry());
|
|
}
|
|
|
|
InsertInput(frame, inputLog); // Sets the ChangeLog
|
|
}
|
|
|
|
public void CopyOverInput(int frame, IEnumerable<IController> inputStates)
|
|
{
|
|
ChangeLog.AddGeneralUndo(frame, frame + inputStates.Count() - 1, "Copy Over Input: " + frame);
|
|
|
|
var lg = LogGeneratorInstance();
|
|
var states = inputStates.ToList();
|
|
for (int i = 0; i < states.Count; i++)
|
|
{
|
|
lg.SetSource(states[i]);
|
|
_log[frame + i] = lg.GenerateLogEntry();
|
|
}
|
|
|
|
Changes = true;
|
|
InvalidateAfter(frame);
|
|
|
|
ChangeLog.SetGeneralRedo();
|
|
}
|
|
|
|
public void InsertEmptyFrame(int frame, int count = 1)
|
|
{
|
|
bool endBatch = ChangeLog.BeginNewBatch("Insert Empty Frame: " + frame, true);
|
|
ChangeLog.AddGeneralUndo(frame, InputLogLength + count - 1);
|
|
|
|
var lg = LogGeneratorInstance();
|
|
lg.SetSource(Global.MovieSession.MovieControllerInstance());
|
|
|
|
for (int i = 0; i < count; i++)
|
|
_log.Insert(frame, lg.EmptyEntry);
|
|
|
|
if (BindMarkersToInput)
|
|
{
|
|
bool wasRecording = ChangeLog.IsRecording;
|
|
ChangeLog.IsRecording = false;
|
|
int firstIndex = Markers.FindIndex(m => m.Frame >= frame);
|
|
if (firstIndex != -1)
|
|
{
|
|
for (int i = firstIndex; i < Markers.Count; i++)
|
|
{
|
|
TasMovieMarker m = Markers.ElementAt(i);
|
|
Markers.Move(m.Frame, m.Frame + count);
|
|
}
|
|
}
|
|
ChangeLog.IsRecording = wasRecording;
|
|
}
|
|
|
|
|
|
Changes = true;
|
|
InvalidateAfter(frame - 1);
|
|
|
|
ChangeLog.SetGeneralRedo();
|
|
if (endBatch)
|
|
ChangeLog.EndBatch();
|
|
|
|
if (Global.Emulator.Frame < _log.Count) // Don't stay in recording mode? Fixes TAStudio recording after paint inserting.
|
|
this.SwitchToPlay();
|
|
}
|
|
|
|
private void ExtendMovieForEdit(int numFrames)
|
|
{
|
|
bool endBatch = ChangeLog.BeginNewBatch("Auto-Extend Movie", true);
|
|
int oldLength = InputLogLength;
|
|
ChangeLog.AddGeneralUndo(oldLength, oldLength + numFrames - 1);
|
|
|
|
var lg = LogGeneratorInstance();
|
|
lg.SetSource(Global.MovieOutputHardpoint); // account for autohold. needs autohold pattern to be already recorded in the current frame
|
|
|
|
for (int i = 0; i < numFrames; i++)
|
|
_log.Add(lg.GenerateLogEntry());
|
|
|
|
Changes = true;
|
|
|
|
ChangeLog.SetGeneralRedo();
|
|
if (endBatch)
|
|
ChangeLog.EndBatch();
|
|
|
|
if (Global.Emulator.Frame < _log.Count) // Don't stay in recording mode? Fixes TAStudio recording after paint inserting.
|
|
this.SwitchToPlay();
|
|
}
|
|
public void ToggleBoolState(int frame, string buttonName)
|
|
{
|
|
if (frame >= _log.Count) // Insert blank frames up to this point
|
|
ExtendMovieForEdit(frame - _log.Count + 1);
|
|
|
|
var adapter = GetInputState(frame) as Bk2ControllerAdapter;
|
|
adapter[buttonName] = !adapter.IsPressed(buttonName);
|
|
|
|
var lg = LogGeneratorInstance();
|
|
lg.SetSource(adapter);
|
|
_log[frame] = lg.GenerateLogEntry();
|
|
Changes = true;
|
|
InvalidateAfter(frame);
|
|
|
|
ChangeLog.AddBoolToggle(frame, buttonName, !adapter[buttonName], "Toggle " + buttonName + ": " + frame);
|
|
}
|
|
|
|
public void SetBoolState(int frame, string buttonName, bool val)
|
|
{
|
|
if (frame >= _log.Count) // Insert blank frames up to this point
|
|
ExtendMovieForEdit(frame - _log.Count + 1);
|
|
|
|
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)
|
|
{
|
|
InvalidateAfter(frame);
|
|
Changes = true;
|
|
ChangeLog.AddBoolToggle(frame, buttonName, old, "Set " + buttonName + "(" + (val ? "On" : "Off") + "): " + frame);
|
|
}
|
|
}
|
|
public void SetBoolStates(int frame, int count, string buttonName, bool val)
|
|
{
|
|
if (frame + count >= _log.Count) // Insert blank frames up to this point
|
|
ExtendMovieForEdit(frame - _log.Count + 1);
|
|
|
|
ChangeLog.AddGeneralUndo(frame, frame + count - 1, "Set " + buttonName + "(" + (val ? "On" : "Off") + "): " + frame + "-" + (frame + count - 1));
|
|
|
|
int changed = -1;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var adapter = GetInputState(frame + i) as Bk2ControllerAdapter;
|
|
bool old = adapter[buttonName];
|
|
adapter[buttonName] = val;
|
|
|
|
var lg = LogGeneratorInstance();
|
|
lg.SetSource(adapter);
|
|
_log[frame + i] = lg.GenerateLogEntry();
|
|
|
|
if (changed == -1 && old != val)
|
|
changed = frame + i;
|
|
}
|
|
|
|
if (changed != -1)
|
|
{
|
|
InvalidateAfter(changed);
|
|
Changes = true;
|
|
}
|
|
|
|
ChangeLog.SetGeneralRedo();
|
|
}
|
|
|
|
public void SetFloatState(int frame, string buttonName, float val)
|
|
{
|
|
if (frame >= _log.Count) // Insert blank frames up to this point
|
|
ExtendMovieForEdit(frame - _log.Count + 1);
|
|
|
|
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)
|
|
{
|
|
InvalidateAfter(frame);
|
|
Changes = true;
|
|
ChangeLog.AddFloatChange(frame, buttonName, old, val, "Set " + buttonName + "(" + val + "): " + frame);
|
|
}
|
|
}
|
|
public void SetFloatStates(int frame, int count, string buttonName, float val)
|
|
{
|
|
if (frame + count >= _log.Count) // Insert blank frames up to this point
|
|
ExtendMovieForEdit(frame - _log.Count + 1);
|
|
|
|
ChangeLog.AddGeneralUndo(frame, frame + count - 1, "Set " + buttonName + "(" + val + "): " + frame + "-" + (frame + count - 1));
|
|
|
|
int changed = -1;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var adapter = GetInputState(frame + i) as Bk2ControllerAdapter;
|
|
float old = adapter.GetFloat(buttonName);
|
|
adapter.SetFloat(buttonName, val);
|
|
|
|
var lg = LogGeneratorInstance();
|
|
lg.SetSource(adapter);
|
|
_log[frame + i] = lg.GenerateLogEntry();
|
|
|
|
if (changed == -1 && old != val)
|
|
changed = frame + i;
|
|
}
|
|
|
|
if (changed != -1)
|
|
{
|
|
InvalidateAfter(changed);
|
|
Changes = true;
|
|
}
|
|
|
|
ChangeLog.SetGeneralRedo();
|
|
}
|
|
|
|
#region "LagLog"
|
|
public void RemoveLagHistory(int frame)
|
|
{
|
|
LagLog.RemoveHistoryAt(frame);
|
|
}
|
|
public void InsertLagHistory(int frame, bool isLag)
|
|
{
|
|
LagLog.InsertHistoryAt(frame, isLag);
|
|
}
|
|
|
|
public void SetLag(int frame, bool? value)
|
|
{
|
|
LagLog[frame] = value;
|
|
}
|
|
#endregion
|
|
}
|
|
}
|