Skip to content
Draft
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
2 changes: 2 additions & 0 deletions src/BizHawk.Client.Common/movie/interfaces/ITasMovie.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public interface ITasMovie : IMovie, INotifyPropertyChanged, IDisposable
/// </summary>
Action<int> GreenzoneInvalidated { get; set; }

void SetUpPokeListener();

string DisplayValue(int frame, string buttonName, bool defaultAxisAsBlank);
void FlagChanges();
void ClearChanges();
Expand Down
17 changes: 17 additions & 0 deletions src/BizHawk.Client.Common/movie/tasproj/IStateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,25 @@ public interface IStateManager : IDisposable
/// </summary>
void Unreserve(int frame);

/// <returns><see langword="true"/> iff this collection has a state at <paramref name="frame"/></returns>
bool HasState(int frame);

/// <returns>
/// <see langword="true"/> iff this collection has a state at <paramref name="frame"/>
/// and that state was "cheated" i.e. made after a poke
/// </returns>
bool HasCheatedState(int frame);

/// <summary>
/// marks the state at <paramref name="frame"/>, if there is one, and all states following it
/// as being "cheated" i.e. made after a poke
/// </summary>
/// <remarks>
/// the state manager automatically propagates the flag to subsequent states,
/// so there's no need to call this method repeatedly
/// </remarks>
void SetCheatedFlagStarting(int frame);

/// <summary>
/// Clears out all savestates after the given frame number
/// </summary>
Expand Down
2 changes: 2 additions & 0 deletions src/BizHawk.Client.Common/movie/tasproj/TasMovie.IO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ private void LoadTasprojExtras(ZipStateLoader bl)
try
{
TasStateManager = settings.CreateManager(IsReserved);
SetUpPokeListener();
TasStateManager.LoadStateHistory(br);
}
catch
Expand All @@ -181,6 +182,7 @@ private void LoadTasprojExtras(ZipStateLoader bl)
{
TasStateManager = Session.Settings.DefaultTasStateManagerSettings.CreateManager(IsReserved);
}
SetUpPokeListener();
}
}
}
Expand Down
23 changes: 22 additions & 1 deletion src/BizHawk.Client.Common/movie/tasproj/TasMovie.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ internal sealed partial class TasMovie : Bk2Movie, ITasMovie
public new const string Extension = "tasproj";
private IInputPollable _inputPollable;

private Func<int>/*?*/ _getCurrentFrame;

private IMemoryDomains/*?*/ _memDomains;

public const double CurrentVersion = 1.1;

/// <exception cref="InvalidOperationException">loaded core does not implement <see cref="IStatable"/></exception>
Expand All @@ -39,9 +43,12 @@ public override void Attach(IEmulator emulator)
throw new InvalidOperationException($"A core must be able to provide an {nameof(IInputPollable)} service");
}

_getCurrentFrame = () => emulator.Frame; // capture local I guess
_inputPollable = emulator.AsInputPollable();
_memDomains = emulator.HasMemoryDomains() ? emulator.AsMemoryDomains() : null;

TasStateManager ??= Session.Settings.DefaultTasStateManagerSettings.CreateManager(IsReserved);
SetUpPokeListener();
if (StartsFromSavestate)
{
TasStateManager.Engage(BinarySavestate);
Expand All @@ -58,6 +65,18 @@ public override void Attach(IEmulator emulator)
base.Attach(emulator);
}

private void FlagCheated()
=> TasStateManager.SetCheatedFlagStarting(_getCurrentFrame());

public void SetUpPokeListener()
{
if (_memDomains is not null) foreach (var domain in _memDomains)
{
domain.Poked -= FlagCheated;
domain.Poked += FlagCheated;
}
}

public override bool StartsFromSavestate
{
get => base.StartsFromSavestate;
Expand Down Expand Up @@ -99,9 +118,11 @@ public ITasMovieRecord this[int index]
}
}

var cheated = TasStateManager.HasCheatedState(index);
return new TasMovieRecord
{
HasState = TasStateManager.HasState(index),
Cheated = cheated,
HasState = cheated || TasStateManager.HasState(index),
LogEntry = GetInputLogEntry(index),
Lagged = lagged,
WasLagged = LagLog.History(lagIndex),
Expand Down
2 changes: 2 additions & 0 deletions src/BizHawk.Client.Common/movie/tasproj/TasMovieRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{
public interface ITasMovieRecord
{
bool Cheated { get; }
bool? Lagged { get; }
bool? WasLagged { get; }
string LogEntry { get; }
Expand All @@ -10,6 +11,7 @@ public interface ITasMovieRecord

public class TasMovieRecord : ITasMovieRecord
{
public bool Cheated { get; internal set; }
public bool? Lagged { get; internal set; }
public bool? WasLagged { get; internal set; }
public string LogEntry { get; internal set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,9 @@ public KeyValuePair<int, Stream> GetStateClosestToFrame(int frame)
return new KeyValuePair<int, Stream>(closestState!.Frame, closestState.Read());
}

public bool HasCheatedState(int frame)
=> throw new NotImplementedException();

public bool HasState(int frame)
{
return StateCache.Contains(frame);
Expand Down Expand Up @@ -560,6 +563,9 @@ public void SaveStateHistory(BinaryWriter bw)
}
}

public void SetCheatedFlagStarting(int frame)
=> throw new NotImplementedException();

public void Dispose()
{
_current?.Dispose();
Expand Down
51 changes: 49 additions & 2 deletions src/BizHawk.Client.Common/tools/TAStudio/PagedStateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Runtime.InteropServices;

using BizHawk.Common;
using BizHawk.Common.CollectionExtensions;
using BizHawk.Common.IOExtensions;
using BizHawk.Emulation.Common;

Expand Down Expand Up @@ -128,11 +129,37 @@ private enum StateGroup

private struct StateInfo: IComparable<StateInfo>
{
private const int SIZE_MASK = 0x7FFFFFFF;

public int FirstPage;
public int LastPage;
public int Size;

/// <summary>most-significant (sign) bit: <see cref="IsCheated"/>; remaining bits: <see cref="Size"/></summary>
private int _size;

public int Frame = -1;

public bool IsCheated
{
readonly get => (_size >> 31) is not 0;
set
{
if (value) _size |= ~SIZE_MASK;
else _size &= SIZE_MASK;
}
}

public int Size
{
readonly get => _size & SIZE_MASK;
set
{
Debug.Assert(value >= 0, "size cannot be negative");
_size &= ~SIZE_MASK;
_size |= value;
}
}

public StateInfo() { }

// Use only to aid searching the list!
Expand Down Expand Up @@ -169,6 +196,8 @@ public Stream MakeReadStream(PagedStateManager manager)

private int _lastForceCapture = -1;

private int _cheatedFrom = int.MaxValue;

private class PagedStream : Stream
{
private readonly StateInfo _info;
Expand Down Expand Up @@ -441,6 +470,7 @@ private void InternalCapture(int frame, IStatable source, StateGroup destination
{
Frame = frame,
FirstPage = _firstFree,
IsCheated = _cheatedFrom <= frame,
};

using PagedStream stream = new(newState, this, false);
Expand Down Expand Up @@ -567,6 +597,9 @@ public KeyValuePair<int, Stream> GetStateClosestToFrame(int frame)

public bool HasState(int frame) => _states.Contains(new(frame));

public bool HasCheatedState(int frame)
=> _states.GetViewBetween(new(frame), new(frame)).Min is { Frame: not 0, IsCheated: true };

private void RemoveState(StateInfo state)
{
Debug.Assert(_states.Contains(state), "Do not attempt to remove a non-existent state.");
Expand All @@ -582,6 +615,18 @@ private void RemoveState(StateInfo state)

_bufferIsFull = false;
}

public void SetCheatedFlagStarting(int frame)
{
_cheatedFrom = Math.Min(frame, _cheatedFrom);
if (_states.GetViewBetween(new(frame), new(frame)).Min.Frame is 0) return;
// is there a simpler way to do this?
var copies = _states.GetViewBetween(new(frame), _states.Max).ToArray();
for (var i = 0; i < copies.Length; i++) copies[i].IsCheated = true;
_states.RemoveWhere(state => state.Frame >= frame);
_states.AddRange(copies);
}

public bool InvalidateAfter(int frame)
{
// must keep state on frame 0
Expand All @@ -596,7 +641,9 @@ public bool InvalidateAfter(int frame)
RemoveState(newestState);
newestState = _states.Max;
}
return Count < oldStateCount;
var anyRemoved = Count < oldStateCount;
if (anyRemoved && newestState.Frame < _cheatedFrom) _cheatedFrom = int.MaxValue;
return anyRemoved;
}

public void Unreserve(int frame)
Expand Down
10 changes: 7 additions & 3 deletions src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.ListView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -477,9 +477,13 @@ private void TasView_QueryRowBkColor(InputRoll sender, int index, ref Color colo
}
else
{
color = record.Lagged.Value
? Palette.LagZone_InputLog_Stated
: Palette.GreenZone_InputLog_Stated;
color = record.Cheated
? record.Lagged.Value
? Palette.LagZone_InputLog_Cheated
: Palette.GreenZone_InputLog_Cheated
: record.Lagged.Value
? Palette.LagZone_InputLog_Stated
: Palette.GreenZone_InputLog_Stated;
}
}
else if (record.WasLagged.HasValue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,7 @@ private void TAStudioSettingsToolStripMenuItem_Click(object sender, EventArgs e)
{
bool keep = DialogController.ShowMessageBox2("Attempt to keep old states?", "Keep old states?");
CurrentTasMovie.TasStateManager = CurrentTasMovie.TasStateManager.UpdateSettings(s.CurrentStateManagerSettings, keep);
CurrentTasMovie.SetUpPokeListener();
}
Config.Movies.DefaultTasStateManagerSettings = s.DefaultStateManagerSettings;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ void Init(TAStudioPalette fromPalette)
colours["currentFrame_InputLog"] = fromPalette.CurrentFrame_InputLog;
colours["greenZone_FrameCol"] = fromPalette.GreenZone_FrameCol;
colours["greenZone_InputLog"] = fromPalette.GreenZone_InputLog;
colours["greenZone_InputLog_Cheated"] = fromPalette.GreenZone_InputLog_Cheated;
colours["greenZone_InputLog_Stated"] = fromPalette.GreenZone_InputLog_Stated;
colours["greenZone_InputLog_Invalidated"] = fromPalette.GreenZone_InputLog_Invalidated;
colours["lagZone_FrameCol"] = fromPalette.LagZone_FrameCol;
colours["lagZone_InputLog"] = fromPalette.LagZone_InputLog;
colours["lagZone_InputLog_Cheated"] = fromPalette.LagZone_InputLog_Cheated;
colours["lagZone_InputLog_Stated"] = fromPalette.LagZone_InputLog_Stated;
colours["lagZone_InputLog_Invalidated"] = fromPalette.LagZone_InputLog_Invalidated;
colours["marker_FrameCol"] = fromPalette.Marker_FrameCol;
Expand Down Expand Up @@ -49,10 +51,12 @@ SingleRowFLP Row(string key, string labelText) // can't use ref here because tho
Row("currentFrame_InputLog", "Emulated Frame Cursor"),
Row("greenZone_FrameCol", "Frame# Column"),
Row("greenZone_InputLog", "Input Log"),
Row("greenZone_InputLog_Cheated", "Savestate (After Poked/cheated)"),
Row("greenZone_InputLog_Stated", "Savestate"),
Row("greenZone_InputLog_Invalidated", "Invalidated"),
Row("lagZone_FrameCol", "Frame# Column (Lag)"),
Row("lagZone_InputLog", "Input Log (Lag)"),
Row("lagZone_InputLog_Cheated", "Savestate (Lag, After Poked/cheated)"),
Row("lagZone_InputLog_Stated", "Savestate (Lag)"),
Row("lagZone_InputLog_Invalidated", "Invalidated (Lag)"),
Row("marker_FrameCol", "Marker"),
Expand All @@ -69,10 +73,12 @@ SingleRowFLP Row(string key, string labelText) // can't use ref here because tho
currentFrame_InputLog: colours["currentFrame_InputLog"],
greenZone_FrameCol: colours["greenZone_FrameCol"],
greenZone_InputLog: colours["greenZone_InputLog"],
greenZone_InputLog_Cheated: colours["greenZone_InputLog_Cheated"],
greenZone_InputLog_Stated: colours["greenZone_InputLog_Stated"],
greenZone_InputLog_Invalidated: colours["greenZone_InputLog_Invalidated"],
lagZone_FrameCol: colours["lagZone_FrameCol"],
lagZone_InputLog: colours["lagZone_InputLog"],
lagZone_InputLog_Cheated: colours["lagZone_InputLog_Cheated"],
lagZone_InputLog_Stated: colours["lagZone_InputLog_Stated"],
lagZone_InputLog_Invalidated: colours["lagZone_InputLog_Invalidated"],
marker_FrameCol: colours["marker_FrameCol"],
Expand Down
10 changes: 10 additions & 0 deletions src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudioPalette.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ public readonly struct TAStudioPalette
currentFrame_InputLog: Color.FromArgb(0xB5, 0xE7, 0xF7),
greenZone_FrameCol: Color.FromArgb(0xDD, 0xFF, 0xDD),
greenZone_InputLog: Color.FromArgb(0xD2, 0xF9, 0xD3),
greenZone_InputLog_Cheated: Color.FromArgb(0xD2, 0xF9, 0xFF),
greenZone_InputLog_Stated: Color.FromArgb(0xC4, 0xF7, 0xC8),
greenZone_InputLog_Invalidated: Color.FromArgb(0xE0, 0xFB, 0xE0),
lagZone_FrameCol: Color.FromArgb(0xFF, 0xDC, 0xDD),
lagZone_InputLog: Color.FromArgb(0xF4, 0xDA, 0xDA),
lagZone_InputLog_Cheated: Color.FromArgb(0xF4, 0xDA, 0xFF),
lagZone_InputLog_Stated: Color.FromArgb(0xF0, 0xD0, 0xD2),
lagZone_InputLog_Invalidated: Color.FromArgb(0xF7, 0xE5, 0xE5),
marker_FrameCol: Color.FromArgb(0xF7, 0xFF, 0xC9),
Expand All @@ -33,6 +35,8 @@ public readonly struct TAStudioPalette

public readonly Color GreenZone_InputLog;

public readonly Color GreenZone_InputLog_Cheated;

public readonly Color GreenZone_InputLog_Stated;

public readonly Color GreenZone_InputLog_Invalidated;
Expand All @@ -41,6 +45,8 @@ public readonly struct TAStudioPalette

public readonly Color LagZone_InputLog;

public readonly Color LagZone_InputLog_Cheated;

public readonly Color LagZone_InputLog_Stated;

public readonly Color LagZone_InputLog_Invalidated;
Expand All @@ -54,10 +60,12 @@ public TAStudioPalette(
Color currentFrame_InputLog,
Color greenZone_FrameCol,
Color greenZone_InputLog,
Color greenZone_InputLog_Cheated,
Color greenZone_InputLog_Stated,
Color greenZone_InputLog_Invalidated,
Color lagZone_FrameCol,
Color lagZone_InputLog,
Color lagZone_InputLog_Cheated,
Color lagZone_InputLog_Stated,
Color lagZone_InputLog_Invalidated,
Color marker_FrameCol,
Expand All @@ -68,10 +76,12 @@ public TAStudioPalette(
// SeekFrame_InputLog = Color.FromArgb(0x70, currentFrame_InputLog);
GreenZone_FrameCol = greenZone_FrameCol;
GreenZone_InputLog = greenZone_InputLog;
GreenZone_InputLog_Cheated = greenZone_InputLog_Cheated;
GreenZone_InputLog_Stated = greenZone_InputLog_Stated;
GreenZone_InputLog_Invalidated = greenZone_InputLog_Invalidated;
LagZone_FrameCol = lagZone_FrameCol;
LagZone_InputLog = lagZone_InputLog;
LagZone_InputLog_Cheated = lagZone_InputLog_Cheated;
LagZone_InputLog_Stated = lagZone_InputLog_Stated;
LagZone_InputLog_Invalidated = lagZone_InputLog_Invalidated;
Marker_FrameCol = marker_FrameCol;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ public enum Endian

public bool Writable { get; protected set; }

public event Action Poked;

protected void OnPoked()
=> Poked?.Invoke();

public abstract byte PeekByte(long addr);

public abstract void PokeByte(long addr, byte val);
Expand Down
Loading
Loading