Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions src/BizHawk.Client.Common/inputAdapters/MultitrackAdapter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#nullable enable

using System.Collections.Generic;
using BizHawk.Emulation.Common;

namespace BizHawk.Client.Common
{
/// <summary>
/// Used to enable recording a subset of a controller's buttons, while keeping the existing inputs for the other buttons.
/// Also used to allow TAStudio to clear (or otherwise edit) a subset of input columns.
/// </summary>
internal class MultitrackAdapter : IController
{
/// <summary>
/// Input states in this definition will come from <see cref="ActiveSource"/>. All others will come from <see cref="BackingSource"/>.
/// </summary>
public ControllerDefinition ActiveDefinition { get; set; }

public IController ActiveSource { get; set; }

public IController BackingSource { get; set; }

public ControllerDefinition Definition => BackingSource.Definition;

public MultitrackAdapter(IController active, IController backing, ControllerDefinition activeDefinition)
{
ActiveSource = active;
BackingSource = backing;
ActiveDefinition = activeDefinition;
}

public bool IsPressed(string button)
{
if (ActiveDefinition.BoolButtons.Contains(button))
{
return ActiveSource.IsPressed(button);
}
else
{
return BackingSource.IsPressed(button);
}
}
public int AxisValue(string name)
{
if (ActiveDefinition.Axes.ContainsKey(name))
{
return ActiveSource.AxisValue(name);
}
else
{
return BackingSource.AxisValue(name);
}
}

public IReadOnlyCollection<(string Name, int Strength)> GetHapticsSnapshot() => throw new NotImplementedException(); // don't use this
public void SetHapticChannelStrength(string name, int strength) => throw new NotImplementedException(); // don't use this
}
}
56 changes: 49 additions & 7 deletions src/BizHawk.Client.Common/movie/bk2/Bk2Movie.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,16 @@ namespace BizHawk.Client.Common
{
public partial class Bk2Movie : BasicMovieInfo, IMovie
{
private Bk2Controller _adapter;
private IController _defaultValueController;
protected IController DefaultValueController
{
get
{
// LogKey isn't available at construction time, so we have to create this instance when it is accessed.
_defaultValueController ??= new Bk2Controller(Session.MovieController.Definition, LogKey);
return _defaultValueController;
}
}

public Bk2Movie(IMovieSession session, string filename) : base(filename)
{
Expand All @@ -18,6 +27,17 @@ public virtual void Attach(IEmulator emulator)
Emulator = emulator;
}

private ControllerDefinition/*?*/ _activeControllerInputs = null;
public virtual ControllerDefinition/*?*/ ActiveControllerInputs
{
get => _activeControllerInputs;
set
{
value?.AssertImmutable();
_activeControllerInputs = value;
}
}

protected bool IsAttached() => Emulator != null;

public IEmulator Emulator { get; private set; }
Expand Down Expand Up @@ -48,6 +68,11 @@ public void CopyLog(IEnumerable<string> log)

public void AppendFrame(IController source)
{
if (ActiveControllerInputs != null)
{
source = new MultitrackAdapter(source, new Bk2Controller(Session.MovieController.Definition, LogKey), ActiveControllerInputs);
}

Log.Add(Bk2LogEntryGenerator.GenerateLogEntry(source));
Changes = true;
}
Expand All @@ -62,15 +87,24 @@ public virtual void RecordFrame(int frame, IController source)
}
}

SetFrameAt(frame, Bk2LogEntryGenerator.GenerateLogEntry(source));

Changes = true;
PokeFrame(frame, source);
}

public virtual void Truncate(int frame)
{
if (frame < Log.Count)
{
if (ActiveControllerInputs != null)
{
for (int i = frame; i < Log.Count; i++)
PokeFrame(i, DefaultValueController);
string defaultEntry = Bk2LogEntryGenerator.EmptyEntry(DefaultValueController);
int firstDefault = Log.Count;
while (firstDefault > frame && Log[firstDefault - 1] == defaultEntry)
firstDefault--;
frame = firstDefault;
}

Log.RemoveRange(frame, Log.Count - frame);
Changes = true;
}
Expand All @@ -80,20 +114,28 @@ public IMovieController GetInputState(int frame)
{
if (frame < FrameCount && frame >= -1)
{
_adapter ??= new Bk2Controller(Session.MovieController.Definition, LogKey);
_adapter.SetFromMnemonic(frame >= 0 ? Log[frame] : Bk2LogEntryGenerator.EmptyEntry(_adapter));
return _adapter;
Bk2Controller controller = new(Session.MovieController.Definition, LogKey);
controller.SetFromMnemonic(frame >= 0 ? Log[frame] : Bk2LogEntryGenerator.EmptyEntry(controller));
return controller;
}

return null;
}

public virtual void PokeFrame(int frame, IController source)
{
if (ActiveControllerInputs != null)
{
source = new MultitrackAdapter(source, GetInputState(frame) ?? DefaultValueController, ActiveControllerInputs);
}

SetFrameAt(frame, Bk2LogEntryGenerator.GenerateLogEntry(source));
Changes = true;
}

/// <summary>
/// Does not use <see cref="ActiveControllerInputs"/>.
/// </summary>
protected void SetFrameAt(int frameNum, string frame)
{
if (Log.Count > frameNum)
Expand Down
8 changes: 8 additions & 0 deletions src/BizHawk.Client.Common/movie/interfaces/IMovie.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ public enum MovieMode
// TODO: consider other event handlers, switching modes?
public interface IMovie : IBasicMovieInfo
{
/// <summary>
/// When not null, methods that edit the movie will only affect buttons or axis values present in this definition,
/// with the exceoption of adding or removing default-value frames at the end of the movie.
/// The instance must be made immutable before assignment.
/// </summary>
ControllerDefinition/*?*/ ActiveControllerInputs { get; set; }

/// <summary>
/// Gets the current movie mode
/// </summary>
Expand Down Expand Up @@ -161,6 +168,7 @@ public interface IMovie : IBasicMovieInfo

/// <summary>
/// Instructs the movie to remove all input from its input log starting with the input at frame.
/// If <see cref="ActiveControllerInputs"/> is not null, this might not actually change the length of the movie.
/// </summary>
/// <param name="frame">The frame at which to truncate</param>
void Truncate(int frame);
Expand Down
3 changes: 2 additions & 1 deletion src/BizHawk.Client.Common/movie/interfaces/ITasMovie.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ public interface ITasMovie : IMovie, INotifyPropertyChanged, IDisposable

/// <summary>
/// Remove all frames between removeStart and removeUpTo (excluding removeUpTo).
/// If <see cref="IMovie.ActiveControllerInputs"/> is not null, this will not actually change the length of the movie.
/// </summary>
/// <param name="removeStart">The first frame to remove.</param>
/// <param name="removeUpTo">The frame after the last frame to remove.</param>
void RemoveFrames(int removeStart, int removeUpTo);
void SetFrame(int frame, string source);
void PokeFrame(int frame, string source);

void LoadBranch(TasBranch branch);

Expand Down
Loading
Loading