diff --git a/src/BizHawk.Client.Common/movie/tasproj/TasBranch.cs b/src/BizHawk.Client.Common/movie/tasproj/TasBranch.cs
index 9a5da7ccb0c..7cd4d243510 100644
--- a/src/BizHawk.Client.Common/movie/tasproj/TasBranch.cs
+++ b/src/BizHawk.Client.Common/movie/tasproj/TasBranch.cs
@@ -252,17 +252,7 @@ public void Load(ZipStateLoader bl, ITasMovie movie)
});
b.Markers = new TasMovieMarkerList(movie);
- bl.GetLump(nmarkers, abort: false, tr =>
- {
- string line;
- while ((line = tr.ReadLine()) != null)
- {
- if (!string.IsNullOrWhiteSpace(line))
- {
- b.Markers.Add(new TasMovieMarker(line));
- }
- }
- });
+ bl.GetLump(nmarkers, abort: false, b.Markers.LoadFromFile);
bl.GetLump(nusertext, abort: false, tr =>
{
diff --git a/src/BizHawk.Client.Common/movie/tasproj/TasMovie.IO.cs b/src/BizHawk.Client.Common/movie/tasproj/TasMovie.IO.cs
index 0eb657e96bb..aafac0c2937 100644
--- a/src/BizHawk.Client.Common/movie/tasproj/TasMovie.IO.cs
+++ b/src/BizHawk.Client.Common/movie/tasproj/TasMovie.IO.cs
@@ -84,17 +84,7 @@ private void LoadTasprojExtras(ZipStateLoader bl)
{
bl.GetLump(BinaryStateLump.LagLog, abort: false, tr => LagLog.Load(tr));
- bl.GetLump(BinaryStateLump.Markers, abort: false, tr =>
- {
- string line;
- while ((line = tr.ReadLine()) != null)
- {
- if (!string.IsNullOrWhiteSpace(line))
- {
- Markers.Add(new TasMovieMarker(line));
- }
- }
- });
+ bl.GetLump(BinaryStateLump.Markers, abort: false, Markers.LoadFromFile);
bl.GetLump(BinaryStateLump.ClientSettings, abort: false, tr =>
{
diff --git a/src/BizHawk.Client.Common/movie/tasproj/TasMovie.cs b/src/BizHawk.Client.Common/movie/tasproj/TasMovie.cs
index 4efd1bacf81..a20e6eae643 100644
--- a/src/BizHawk.Client.Common/movie/tasproj/TasMovie.cs
+++ b/src/BizHawk.Client.Common/movie/tasproj/TasMovie.cs
@@ -326,7 +326,7 @@ public bool IsReserved(int frame)
// Why the frame before?
// because we always navigate to the frame before and emulate 1 frame so that we ensure a proper frame buffer on the screen
// users want instant navigation to markers, so to do this, we need to reserve the frame before the marker, not the marker itself
- return Markers.Exists(m => m.Frame - 1 == frame)
+ return Markers.Exists(m => m.WantsState && m.Frame - 1 == frame)
|| Branches.Any(b => b.Frame == frame); // Branches should already be in the reserved list, but it doesn't hurt to check
}
diff --git a/src/BizHawk.Client.Common/movie/tasproj/TasMovieMarker.cs b/src/BizHawk.Client.Common/movie/tasproj/TasMovieMarker.cs
index b878250e2f1..cb84fe41019 100644
--- a/src/BizHawk.Client.Common/movie/tasproj/TasMovieMarker.cs
+++ b/src/BizHawk.Client.Common/movie/tasproj/TasMovieMarker.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
+using System.IO;
using System.Linq;
using System.Text;
using BizHawk.Common.CollectionExtensions;
@@ -22,18 +23,32 @@ public TasMovieMarker(int frame, string message = "")
///
/// Initializes a new instance of the class from a line of text
///
- public TasMovieMarker(string line)
+ public TasMovieMarker(string line, int version)
{
var split = line.Split('\t');
Frame = int.Parse(split[0]);
- Message = split[1];
+ if (version == 1)
+ {
+ Message = split[1];
+ }
+ else if (version == 2)
+ {
+ WantsState = bool.Parse(split[1]);
+ Message = split[2];
+ }
+ else
+ {
+ throw new Exception("Invalid version.");
+ }
}
public int Frame { get; private set; }
public string Message { get; set; }
- public override string ToString() => Frame.ToString() + '\t' + Message;
+ public bool WantsState { get; set; } = true;
+
+ public override string ToString() => $"{Frame}\t{WantsState}\t{Message}";
public override int GetHashCode() => Frame.GetHashCode();
@@ -111,6 +126,7 @@ private void OnListChanged(NotifyCollectionChangedAction action)
public override string ToString()
{
var sb = new StringBuilder();
+ sb.AppendLine("2"); // version
foreach (var marker in this)
{
sb.AppendLine(marker.ToString());
@@ -119,6 +135,29 @@ public override string ToString()
return sb.ToString();
}
+ public void LoadFromFile(TextReader tr)
+ {
+ string line;
+ int version = -1;
+ while ((line = tr.ReadLine()) != null)
+ {
+ if (string.IsNullOrWhiteSpace(line)) continue;
+ if (version == -1)
+ {
+ if (line.Contains('\t'))
+ {
+ version = 1;
+ }
+ else
+ {
+ version = int.Parse(line);
+ continue;
+ }
+ }
+ Add(new TasMovieMarker(line, version));
+ }
+ }
+
// the inherited one
public new void Add(TasMovieMarker item)
{
diff --git a/src/BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.Drawing.cs b/src/BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.Drawing.cs
index ceccfb2bbd7..59e05616c4d 100644
--- a/src/BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.Drawing.cs
+++ b/src/BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.Drawing.cs
@@ -186,7 +186,6 @@ private void DrawData(List visibleColumns, int firstVisibleRow, int
int startRow = firstVisibleRow;
int range = Math.Min(lastVisibleRow, RowCount - 1) - startRow + 1;
- _renderer.PrepDrawString(Font, _foreColor);
Cell currentCell = new();
Cell mouseCell = null;
@@ -226,28 +225,21 @@ private void DrawData(List visibleColumns, int firstVisibleRow, int
int strOffsetY = 0;
QueryItemText(this, f + startRow, col, out var text, ref strOffsetX, ref strOffsetY);
- bool rePrep = false;
- Color foreColor = _foreColor;
+ Color? foreColor = QueryItemForeColor?.Invoke(this, f + startRow, col);
Font font = Font;
currentCell.Column = col;
currentCell.RowIndex = f + startRow;
- if (_selectedItems.Contains(currentCell))
+ if (foreColor == null && _selectedItems.Contains(currentCell))
{
foreColor = SystemColors.HighlightText;
- rePrep = true;
}
if (string.IsNullOrEmpty(text) && mouseCell == currentCell)
{
font = new Font(Font, FontStyle.Regular);
foreColor = SystemColors.GrayText;
text = col.Text;
- rePrep = true;
- }
- if (col.Rotatable || rePrep)
- {
- _renderer.PrepDrawString(font, foreColor, rotate: col.Rotatable);
- rePrep = true;
}
+ _renderer.PrepDrawString(font, foreColor ?? _foreColor, rotate: col.Rotatable);
int textWidth = (int)_renderer.MeasureString(text, font).Width;
if (col.Rotatable)
@@ -266,11 +258,6 @@ private void DrawData(List visibleColumns, int firstVisibleRow, int
DrawString(text, new Rectangle(baseX + textX, baseY + textY, MaxColumnWidth, CellHeight));
}
-
- if (rePrep)
- {
- _renderer.PrepDrawString(Font, _foreColor);
- }
}
}
}
@@ -299,34 +286,23 @@ private void DrawData(List visibleColumns, int firstVisibleRow, int
QueryItemText(this, f + startRow, column, out var text, ref strOffsetX, ref strOffsetY);
- bool rePrep = false;
- Color foreColor = _foreColor;
+ Color? foreColor = QueryItemForeColor?.Invoke(this, f + startRow, column);
Font font = Font;
currentCell.Column = column;
currentCell.RowIndex = f + startRow;
- if (_selectedItems.Contains(currentCell))
+ if (foreColor == null && _selectedItems.Contains(currentCell))
{
foreColor = SystemColors.HighlightText;
- rePrep = true;
}
if (string.IsNullOrEmpty(text) && mouseCell == currentCell)
{
font = new Font(Font, FontStyle.Regular);
foreColor = SystemColors.GrayText;
text = column.Text;
- rePrep = true;
- }
- if (rePrep)
- {
- _renderer.PrepDrawString(font, foreColor);
}
+ _renderer.PrepDrawString(font, foreColor ?? _foreColor);
DrawString(text, new Rectangle(point.X + strOffsetX, point.Y + strOffsetY, column.Width, ColumnHeight));
-
- if (rePrep)
- {
- _renderer.PrepDrawString(Font, _foreColor);
- }
}
}
}
diff --git a/src/BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.cs b/src/BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.cs
index c67f70f2d1e..5e4a096c07c 100644
--- a/src/BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.cs
+++ b/src/BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.cs
@@ -449,6 +449,12 @@ public int HoverInterval
[Category("Virtual")]
public event QueryItemBkColorHandler QueryItemBkColor;
+ ///
+ /// Fire the event which requests the color of text for the passed cell
+ ///
+ [Category("Virtual")]
+ public event QueryItemForeColorHandler QueryItemForeColor;
+
[Category("Virtual")]
public event QueryRowBkColorHandler QueryRowBkColor;
@@ -531,6 +537,11 @@ public int HoverInterval
///
public delegate void QueryItemTextHandler(InputRoll sender, int index, RollColumn column, out string text, ref int offsetX, ref int offsetY);
+ ///
+ /// Retrieve the foreground color for a cell. Return null to use the default.
+ ///
+ public delegate Color? QueryItemForeColorHandler(InputRoll sender, int index, RollColumn column);
+
///
/// Retrieve the background color for a cell
///
diff --git a/src/BizHawk.Client.EmuHawk/tools/TAStudio/MarkerControl.Designer.cs b/src/BizHawk.Client.EmuHawk/tools/TAStudio/MarkerControl.Designer.cs
index 8e1160a9d94..72bbcb895b3 100644
--- a/src/BizHawk.Client.EmuHawk/tools/TAStudio/MarkerControl.Designer.cs
+++ b/src/BizHawk.Client.EmuHawk/tools/TAStudio/MarkerControl.Designer.cs
@@ -199,6 +199,7 @@ private void InitializeComponent()
this.MarkerView.TabStop = false;
this.MarkerView.SelectedIndexChanged += new System.EventHandler(this.MarkerView_SelectedIndexChanged);
this.MarkerView.DoubleClick += new System.EventHandler(this.MarkerView_MouseDoubleClick);
+ this.MarkerView.MouseDown += new System.Windows.Forms.MouseEventHandler(this.MarkerView_MouseDown);
//
// MarkersGroupBox
//
diff --git a/src/BizHawk.Client.EmuHawk/tools/TAStudio/MarkerControl.cs b/src/BizHawk.Client.EmuHawk/tools/TAStudio/MarkerControl.cs
index b4fc528d9fd..6fe0d03c332 100644
--- a/src/BizHawk.Client.EmuHawk/tools/TAStudio/MarkerControl.cs
+++ b/src/BizHawk.Client.EmuHawk/tools/TAStudio/MarkerControl.cs
@@ -1,15 +1,18 @@
-using System.ComponentModel;
+using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using BizHawk.Client.Common;
using BizHawk.Client.EmuHawk.Properties;
+using BizHawk.Emulation.Common;
namespace BizHawk.Client.EmuHawk
{
public partial class MarkerControl : UserControl, IDialogParent
{
+ private static string SAVESTATE_COL_NAME = "Savestate";
+
public TAStudio Tastudio { get; set; }
public TasMovieMarkerList Markers => Tastudio.CurrentTasMovie.Markers;
@@ -37,6 +40,7 @@ public MarkerControl()
SetupColumns();
MarkerView.QueryItemBkColor += MarkerView_QueryItemBkColor;
MarkerView.QueryItemText += MarkerView_QueryItemText;
+ MarkerView.QueryItemForeColor += MarkerView_QueryItemForeColor;
}
public void UpdateHotkeyTooltips(Config config)
@@ -50,6 +54,7 @@ private void SetupColumns()
{
MarkerView.AllColumns.Clear();
MarkerView.AllColumns.Add(new(name: "FrameColumn", widthUnscaled: 52, text: "Frame"));
+ MarkerView.AllColumns.Add(new(name: SAVESTATE_COL_NAME, widthUnscaled: 24, text: "State"));
MarkerView.AllColumns.Add(new(name: "LabelColumn", widthUnscaled: 125, text: string.Empty));
}
@@ -72,16 +77,16 @@ private void MarkerView_QueryItemBkColor(InputRoll sender, int index, RollColumn
}
else if (Tastudio.CurrentTasMovie.LagLog[marker.Frame + 1] is bool lagged)
{
- if (lagged)
+ if (column.Name == "FrameColumn")
{
- color = column.Name == "FrameColumn"
+ color = lagged
? Tastudio.Palette.LagZone_FrameCol
- : Tastudio.Palette.LagZone_InputLog;
+ : Tastudio.Palette.GreenZone_FrameCol;
}
else
{
- color = column.Name == "LabelColumn"
- ? Tastudio.Palette.GreenZone_FrameCol
+ color = lagged
+ ? Tastudio.Palette.LagZone_InputLog
: Tastudio.Palette.GreenZone_InputLog;
}
}
@@ -97,14 +102,41 @@ private void MarkerView_QueryItemText(InputRoll sender, int index, RollColumn co
return;
}
+ TasMovieMarker marker = Markers[index];
if (column.Name == "FrameColumn")
{
- text = Markers[index].Frame.ToString();
+ text = marker.Frame.ToString();
}
else if (column.Name == "LabelColumn")
{
- text = Markers[index].Message;
+ text = marker.Message;
+ }
+ else if (column.Name == SAVESTATE_COL_NAME)
+ {
+ if (marker.WantsState)
+ {
+ bool hasState = marker.Frame == 0 || Tastudio.CurrentTasMovie.TasStateManager.HasState(marker.Frame - 1);
+ text = hasState ? "✔" : "-";
+ }
+ else
+ {
+ text = "x";
+ }
+ }
+ }
+
+ private Color? MarkerView_QueryItemForeColor(InputRoll sender, int index, RollColumn column)
+ {
+ if (column.Name == SAVESTATE_COL_NAME)
+ {
+ TasMovieMarker marker = Markers[index];
+ if (!marker.WantsState)
+ {
+ return Color.OrangeRed;
+ }
}
+
+ return null;
}
private void MarkerContextMenu_Opening(object sender, CancelEventArgs e)
@@ -188,6 +220,7 @@ public void AddMarker(int frame, bool editText = false)
{
marker = new TasMovieMarker(frame);
}
+ marker.WantsState = Tastudio.Settings.StatesForMarkers;
UpdateValues();
Markers.Add(marker);
@@ -285,5 +318,24 @@ private void MarkerView_MouseDoubleClick(object sender, EventArgs e)
{
if (MarkerView.AnyRowsSelected) Tastudio.GoToFrame(FirstSelectedMarker.Frame);
}
+
+ private void MarkerView_MouseDown(object sender, MouseEventArgs e)
+ {
+ Cell cell = MarkerView.CurrentCell;
+ if (cell == null || cell.RowIndex < 0 || cell.RowIndex >= Markers.Count) return;
+ if (cell.Column?.Name != SAVESTATE_COL_NAME) return;
+
+ TasMovieMarker marker = Markers[cell.RowIndex.Value];
+ marker.WantsState = !marker.WantsState;
+ if (!marker.WantsState)
+ {
+ Tastudio.CurrentTasMovie.TasStateManager.Unreserve(marker.Frame - 1);
+ }
+ else if (Tastudio.Emulator.Frame == marker.Frame - 1)
+ {
+ Tastudio.CurrentTasMovie.TasStateManager.Capture(marker.Frame - 1, Tastudio.Emulator.AsStatable());
+ }
+ Tastudio.RefreshDialog();
+ }
}
}
diff --git a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs
index 14a72d843bb..e7c37c45caa 100644
--- a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs
+++ b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs
@@ -143,6 +143,7 @@ public TAStudioSettings()
public int RewindStep { get; set; } = 1;
public int RewindStepFast { get; set; } = 4;
public bool ScrollSync { get; set; } = true;
+ public bool StatesForMarkers { get; set; } = true;
public PatternPaintModeEnum PatternPaintMode { get; set; } = TAStudioSettings.PatternPaintModeEnum.Never;
public PatternSelectionEnum PatternSelection { get; set; } = TAStudioSettings.PatternSelectionEnum.Hold;
public Font TasViewFont { get; set; } = new Font("Arial", 8.25F, FontStyle.Bold, GraphicsUnit.Point, 0);
diff --git a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudioSettingsForm.Designer.cs b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudioSettingsForm.Designer.cs
index ba167d958c7..76293a442e0 100644
--- a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudioSettingsForm.Designer.cs
+++ b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudioSettingsForm.Designer.cs
@@ -106,6 +106,7 @@ private void InitializeComponent()
this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
this.EditInvisibleColumnsCheckbox = new System.Windows.Forms.CheckBox();
this.ScrollSyncCheckbox = new System.Windows.Forms.CheckBox();
+ this.StatesForMarkersCheckbox = new System.Windows.Forms.CheckBox();
this.tabControl1.SuspendLayout();
this.tabPage2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.HideLagNum)).BeginInit();
@@ -380,6 +381,7 @@ private void InitializeComponent()
//
// tabPage4
//
+ this.tabPage4.Controls.Add(this.StatesForMarkersCheckbox);
this.tabPage4.Controls.Add(this.label12);
this.tabPage4.Controls.Add(this.DefaultManagerSettingsAppliedLabel);
this.tabPage4.Controls.Add(this.SetDefaultStateSettingsButton);
@@ -731,6 +733,7 @@ private void InitializeComponent()
//
// tabPage3
//
+ this.tabPage3.Controls.Add(this.StatesForMarkersCheckbox);
this.tabPage3.Controls.Add(this.OldBranchesCheckbox);
this.tabPage3.Controls.Add(this.BranchDoubleClickCheckbox);
this.tabPage3.Controls.Add(this.FastRewindNum);
@@ -912,6 +915,17 @@ private void InitializeComponent()
this.ApplyButton.UseVisualStyleBackColor = true;
this.ApplyButton.Click += new System.EventHandler(this.ApplyButton_Click);
//
+ // StatesForMarkersCheckbox
+ //
+ this.StatesForMarkersCheckbox.AutoSize = true;
+ this.StatesForMarkersCheckbox.Location = new System.Drawing.Point(12, 200);
+ this.StatesForMarkersCheckbox.Name = "StatesForMarkersCheckbox";
+ this.StatesForMarkersCheckbox.Size = new System.Drawing.Size(171, 17);
+ this.StatesForMarkersCheckbox.TabIndex = 518;
+ this.StatesForMarkersCheckbox.Text = "Markers keep states by default";
+ this.toolTip1.SetToolTip(this.StatesForMarkersCheckbox, resources.GetString("StatesForMarkersCheckbox.ToolTip"));
+ this.StatesForMarkersCheckbox.UseVisualStyleBackColor = true;
+ //
// tabPage6
//
this.tabPage6.Controls.Add(this.ScrollSyncCheckbox);
@@ -1170,5 +1184,6 @@ private void InitializeComponent()
private System.Windows.Forms.Label label3;
private System.Windows.Forms.NumericUpDown ScrollSpeedNum;
private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.CheckBox StatesForMarkersCheckbox;
}
}
\ No newline at end of file
diff --git a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudioSettingsForm.cs b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudioSettingsForm.cs
index a7e34900e67..5ba583c9192 100644
--- a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudioSettingsForm.cs
+++ b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudioSettingsForm.cs
@@ -101,6 +101,7 @@ private void TAStudioSettingsForm_Load(object sender, EventArgs e)
RewindNum.Value = _settings.GeneralClientSettings.RewindStep;
FastRewindNum.Value = _settings.GeneralClientSettings.RewindStepFast;
ScrollSpeedNum.Value = _settings.GeneralClientSettings.ScrollSpeed;
+ StatesForMarkersCheckbox.Checked = _settings.GeneralClientSettings.StatesForMarkers;
// patterns
foreach (var button in _controllerDef.BoolButtons)
@@ -502,6 +503,7 @@ private void ApplyButton_Click(object sender, EventArgs e)
_settings.GeneralClientSettings.MaxUndoSteps = (int)UndoCountNum.Value;
_settings.GeneralClientSettings.RewindStep = (int)RewindNum.Value;
_settings.GeneralClientSettings.RewindStepFast = (int)FastRewindNum.Value;
+ _settings.GeneralClientSettings.StatesForMarkers = StatesForMarkersCheckbox.Checked;
if (ScrollToViewRadio.Checked) _settings.GeneralClientSettings.FollowCursorScrollMethod = "near";
else if (ScrollToTopRadio.Checked) _settings.GeneralClientSettings.FollowCursorScrollMethod = "top";
diff --git a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudioSettingsForm.resx b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudioSettingsForm.resx
index 477a02dadd3..bdf8ac85214 100644
--- a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudioSettingsForm.resx
+++ b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudioSettingsForm.resx
@@ -124,6 +124,14 @@ And when "Recording mode" is on, the movie will be truncated at the branch frame
17, 17
+
+ TAStudio can automatically keep savestates for each marker.
+If this is enabled, new markers will have savestates enabled by default.
+Note: The savestate needs to be on the frame before the marker. Thus, making
+a marker on the current frame will not automatically create a state.
+(The reason for the state being on the frame before is so that TAStudio
+does not need to store a screenshot with the savestate.)
+
When disabled, inputs that are not visible on the active input roll cannot be edited.
This allows you to, for example, insert and delete frames on a roll with player 1's inputs