BizHawk/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs

907 lines
26 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using System.ComponentModel;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
using BizHawk.Client.Common;
using BizHawk.Client.Common.MovieConversionExtensions;
using BizHawk.Client.EmuHawk.WinFormExtensions;
using BizHawk.Client.EmuHawk.ToolExtensions;
namespace BizHawk.Client.EmuHawk
{
public partial class TAStudio : Form, IToolFormAutoConfig, IControlMainform
{
// TODO: UI flow that conveniently allows to start from savestate
private const string CursorColumnName = "CursorColumn";
private const string FrameColumnName = "FrameColumn";
private readonly List<TasClipboardEntry> _tasClipboard = new List<TasClipboardEntry>();
private BackgroundWorker _saveBackgroundWorker;
private MovieEndAction _originalEndAction; // The movie end behavior selected by the user (that is overridden by TAStudio)
2014-07-09 21:56:27 +00:00
private Dictionary<string, string> GenerateColumnNames()
{
2014-07-09 21:56:27 +00:00
var lg = Global.MovieSession.LogGeneratorInstance();
lg.SetSource(Global.MovieSession.MovieControllerAdapter);
return (lg as Bk2LogEntryGenerator).Map();
}
private UndoHistoryForm undoForm;
public ScreenshotPopupControl ScreenshotControl = new ScreenshotPopupControl
{
Size = new Size(256, 240),
};
[ConfigPersist]
public TAStudioSettings Settings { get; set; }
public class TAStudioSettings
{
public TAStudioSettings()
{
RecentTas = new RecentFiles(8);
DrawInput = true;
AutoPause = true;
FollowCursor = true;
ScrollSpeed = 1;
FollowCursorAlwaysScroll = false;
FollowCursorScrollMethod = "near";
}
public RecentFiles RecentTas { get; set; }
public bool DrawInput { get; set; }
public bool AutoPause { get; set; }
public bool AutoRestoreLastPosition { get; set; }
public bool FollowCursor { get; set; }
public bool EmptyMarkers { get; set; }
public int ScrollSpeed { get; set; }
2015-07-25 08:33:23 +00:00
public bool FollowCursorAlwaysScroll { get; set; }
public string FollowCursorScrollMethod { get; set; }
public int MainVerticalSplitDistance { get; set; }
public int BranchMarkerSplitDistance { get; set; }
}
public TasMovie CurrentTasMovie
{
get { return Global.MovieSession.Movie as TasMovie; }
}
#region "Initializing"
public TAStudio()
{
InitializeComponent();
if (Global.Emulator != null)
{
// Set the screenshot to "1x" resolution of the core
// TODO: cores like n64 and psx are going to still have sizes too big for the control
// Find a smart way to keep them small
ScreenshotControl.Size = new Size(Global.Emulator.VideoProvider().BufferWidth, Global.Emulator.VideoProvider().BufferHeight);
}
ScreenshotControl.Visible = false;
Controls.Add(ScreenshotControl);
ScreenshotControl.BringToFront();
Settings = new TAStudioSettings();
2015-01-01 20:48:14 +00:00
// TODO: show this at all times or hide it when saving is done?
this.SavingProgressBar.Visible = false;
InitializeSaveWorker();
WantsToControlStopMovie = true;
TasPlaybackBox.Tastudio = this;
MarkerControl.Tastudio = this;
BookMarkControl.Tastudio = this;
MarkerControl.Emulator = this.Emulator;
TasView.QueryItemText += TasView_QueryItemText;
TasView.QueryItemBkColor += TasView_QueryItemBkColor;
TasView.QueryRowBkColor += TasView_QueryRowBkColor;
TasView.QueryItemIcon += TasView_QueryItemIcon;
TasView.QueryFrameLag += TasView_QueryFrameLag;
TasView.PointedCellChanged += TasView_PointedCellChanged;
TasView.MultiSelect = true;
TasView.MaxCharactersInHorizontal = 1;
WantsToControlRestartMovie = true;
}
private void InitializeSaveWorker()
{
if (_saveBackgroundWorker != null)
{
_saveBackgroundWorker.Dispose();
_saveBackgroundWorker = null; // Idk if this line is even useful.
}
_saveBackgroundWorker = new BackgroundWorker();
_saveBackgroundWorker.WorkerReportsProgress = true;
_saveBackgroundWorker.DoWork += (s, e) =>
{
this.Invoke(() => this.MessageStatusLabel.Text = "Saving " + Path.GetFileName(CurrentTasMovie.Filename) + "...");
this.Invoke(() => this.SavingProgressBar.Visible = true);
CurrentTasMovie.Save();
};
_saveBackgroundWorker.ProgressChanged += (s, e) =>
{
SavingProgressBar.Value = e.ProgressPercentage;
};
_saveBackgroundWorker.RunWorkerCompleted += (s, e) =>
{
this.Invoke(() => this.MessageStatusLabel.Text = Path.GetFileName(CurrentTasMovie.Filename) + " saved.");
this.Invoke(() => this.SavingProgressBar.Visible = false);
InitializeSaveWorker(); // Required, or it will error when trying to report progress again.
};
if (CurrentTasMovie != null) // Again required. TasMovie has a separate reference.
CurrentTasMovie.NewBGWorker(_saveBackgroundWorker);
}
private bool _initialized = false;
private void Tastudio_Load(object sender, EventArgs e)
2014-10-17 17:40:11 +00:00
{
if (!InitializeOnLoad())
{
Close();
this.DialogResult = System.Windows.Forms.DialogResult.Cancel;
return;
}
SetColumnsFromCurrentStickies();
if (VersionInfo.DeveloperBuild)
{
RightClickMenu.Items.AddRange(TasView.GenerateContextMenuItems().ToArray());
RightClickMenu.Items
.OfType<ToolStripMenuItem>()
.First(t => t.Name == "RotateMenuItem")
.Click += (o, ov) =>
{
CurrentTasMovie.FlagChanges();
};
}
TasView.InputPaintingMode = Settings.DrawInput;
TasView.ScrollSpeed = Settings.ScrollSpeed;
2015-07-25 08:33:23 +00:00
TasView.AlwaysScroll = Settings.FollowCursorAlwaysScroll;
TasView.ScrollMethod = Settings.FollowCursorScrollMethod;
// Remembering Split container logic
int defaultMainSplitDistance = MainVertialSplit.SplitterDistance;
int defaultBranchMarkerSplitDistance = BranchesMarkersSplit.SplitterDistance;
ToolStripMenuItem restoreDefaults = TASMenu.Items
.OfType<ToolStripMenuItem>()
.Single(t => t.Name == "SettingsSubMenu")
.DropDownItems
.OfType<ToolStripMenuItem>()
.Single(t => t.Text == "Restore &Defaults");
restoreDefaults.Click += (o, ev) =>
{
MainVertialSplit.SplitterDistance = defaultMainSplitDistance;
BranchesMarkersSplit.SplitterDistance = defaultBranchMarkerSplitDistance;
};
if (Settings.MainVerticalSplitDistance > 0)
{
MainVertialSplit.SplitterDistance = Settings.MainVerticalSplitDistance;
}
if (Settings.BranchMarkerSplitDistance > 0)
{
BranchesMarkersSplit.SplitterDistance = Settings.BranchMarkerSplitDistance;
}
////////////////
RefreshDialog();
_initialized = true;
2014-10-17 17:40:11 +00:00
}
private bool InitializeOnLoad()
2014-07-08 15:15:35 +00:00
{
// Start Scenario 1: A regular movie is active
if (Global.MovieSession.Movie.IsActive && !(Global.MovieSession.Movie is TasMovie))
{
var result = MessageBox.Show("In order to use Tastudio, a new project must be created from the current movie\nThe current movie will be saved and closed, and a new project file will be created\nProceed?", "Convert movie", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
if (result == DialogResult.OK)
{
ConvertCurrentMovieToTasproj();
StartNewMovieWrapper(false);
}
else
{
return false;
}
}
// Start Scenario 2: A tasproj is already active
else if (Global.MovieSession.Movie.IsActive && Global.MovieSession.Movie is TasMovie)
{
// Nothing to do
}
// Start Scenario 3: No movie, but user wants to autload their last project
else if (Settings.RecentTas.AutoLoad && !string.IsNullOrEmpty(Settings.RecentTas.MostRecent))
{
2015-03-22 16:55:34 +00:00
bool result = LoadFile(new FileInfo(Settings.RecentTas.MostRecent));
if (!result)
{
TasView.AllColumns.Clear();
2015-03-22 16:55:34 +00:00
StartNewTasMovie();
}
}
// Start Scenario 4: No movie, default behavior of engaging tastudio with a new default project
else
{
2015-03-22 16:55:34 +00:00
StartNewTasMovie();
}
EngageTastudio();
if (!TasView.AllColumns.Any()) // If a project with column settings has already been loaded we don't need to do this
{
SetUpColumns();
}
return true;
}
private void SetTasMovieCallbacks()
{
CurrentTasMovie.ClientSettingsForSave = ClientSettingsForSave;
CurrentTasMovie.GetClientSettingsOnLoad = GetClientSettingsOnLoad;
}
private string ClientSettingsForSave()
{
return TasView.UserSettingsSerialized();
}
private void GetClientSettingsOnLoad(string settingsJson)
{
TasView.LoadSettingsSerialized(settingsJson);
2015-03-23 20:15:35 +00:00
SetUpToolStripColumns();
}
private void SetUpColumns()
{
TasView.AllColumns.Clear();
AddColumn(CursorColumnName, string.Empty, 18);
AddColumn(FrameColumnName, "Frame#", 68);
var columnNames = GenerateColumnNames();
foreach (var kvp in columnNames)
{
// N64 hack for now, for fake analog
if (Emulator.SystemId == "N64")
{
if (kvp.Key.Contains("A Up") || kvp.Key.Contains("A Down") ||
kvp.Key.Contains("A Left") || kvp.Key.Contains("A Right"))
{
continue;
}
}
AddColumn(kvp.Key, kvp.Value, 20 * kvp.Value.Length);
}
// Patterns
int bStart = 0;
int fStart = 0;
if (BoolPatterns == null)
{
BoolPatterns = new AutoPatternBool[controllerType.BoolButtons.Count + 2];
FloatPatterns = new AutoPatternFloat[controllerType.FloatControls.Count + 2];
}
else
{
bStart = BoolPatterns.Length - 2;
fStart = FloatPatterns.Length - 2;
Array.Resize(ref BoolPatterns, controllerType.BoolButtons.Count + 2);
Array.Resize(ref FloatPatterns, controllerType.FloatControls.Count + 2);
}
for (int i = bStart; i < BoolPatterns.Length - 2; i++)
BoolPatterns[i] = new AutoPatternBool(1, 1);
BoolPatterns[BoolPatterns.Length - 2] = new AutoPatternBool(1, 0);
BoolPatterns[BoolPatterns.Length - 1] = new AutoPatternBool(
Global.Config.AutofireOn, Global.Config.AutofireOff);
for (int i = fStart; i < FloatPatterns.Length - 2; i++)
FloatPatterns[i] = new AutoPatternFloat(new float[] { 1f });
FloatPatterns[FloatPatterns.Length - 2] = new AutoPatternFloat(new float[] { 1f });
FloatPatterns[FloatPatterns.Length - 1] = new AutoPatternFloat(
1f, Global.Config.AutofireOn, 0f, Global.Config.AutofireOff);
SetUpToolStripColumns();
}
public void AddColumn(string columnName, string columnText, int columnWidth)
{
if (TasView.AllColumns[columnName] == null)
{
var column = new InputRoll.RollColumn
{
Name = columnName,
Text = columnText,
Width = columnWidth,
};
TasView.AllColumns.Add(column);
}
2014-07-08 15:15:35 +00:00
}
private void EngageTastudio()
{
GlobalWin.MainForm.PauseOnFrame = null;
GlobalWin.OSD.AddMessage("TAStudio engaged");
SetTasMovieCallbacks();
2014-09-16 23:26:17 +00:00
SetTextProperty();
2014-07-12 01:32:21 +00:00
GlobalWin.MainForm.PauseEmulator();
GlobalWin.MainForm.RelinquishControl(this);
_originalEndAction = Global.Config.MovieEndAction;
GlobalWin.MainForm.ClearRewindData();
Global.Config.MovieEndAction = MovieEndAction.Record;
GlobalWin.MainForm.SetMainformMovieInfo();
Global.MovieSession.ReadOnly = true;
2014-07-08 15:15:35 +00:00
}
#endregion
2015-03-22 16:55:34 +00:00
#region "Loading"
private void ConvertCurrentMovieToTasproj()
2014-07-08 15:15:35 +00:00
{
Global.MovieSession.Movie.Save();
Global.MovieSession.Movie = Global.MovieSession.Movie.ToTasMovie();
Global.MovieSession.Movie.Save();
Global.MovieSession.Movie.SwitchToRecord();
Settings.RecentTas.Add(Global.MovieSession.Movie.Filename);
2014-07-08 15:15:35 +00:00
}
2015-03-22 16:55:34 +00:00
private bool LoadFile(FileInfo file)
2014-07-08 15:15:35 +00:00
{
2015-03-22 16:55:34 +00:00
if (!file.Exists)
{
Settings.RecentTas.HandleLoadError(file.FullName);
return false;
}
2015-06-27 11:48:05 +00:00
if (CurrentTasMovie == null)
{
Global.MovieSession.Movie = new TasMovie(false, _saveBackgroundWorker);
(Global.MovieSession.Movie as TasMovie).TasStateManager.InvalidateCallback = GreenzoneInvalidated;
2015-06-27 11:48:05 +00:00
}
2015-03-22 16:55:34 +00:00
CurrentTasMovie.Filename = file.FullName;
try
{
CurrentTasMovie.Load();
}
catch
{
MessageBox.Show(
"Tastudio could not open the file. Due to the loading process, the emulator/Tastudio may be in a unspecified state depending on the error.",
"Tastudio",
MessageBoxButtons.OK);
return false;
}
Settings.RecentTas.Add(CurrentTasMovie.Filename);
if (!HandleMovieLoadStuff())
return false;
BookMarkControl.UpdateValues();
2015-03-22 16:55:34 +00:00
RefreshDialog();
return true;
2014-07-08 15:15:35 +00:00
}
private void StartNewTasMovie()
{
if (AskSaveChanges())
{
2015-03-22 16:55:34 +00:00
Global.MovieSession.Movie = new TasMovie(false, _saveBackgroundWorker);
(Global.MovieSession.Movie as TasMovie).TasStateManager.InvalidateCallback = GreenzoneInvalidated;
2015-03-22 16:55:34 +00:00
CurrentTasMovie.PropertyChanged += new PropertyChangedEventHandler(this.TasMovie_OnPropertyChanged);
CurrentTasMovie.Filename = DefaultTasProjName(); // TODO don't do this, take over any mainform actions that can crash without a filename
CurrentTasMovie.PopulateWithDefaultHeaderValues();
2015-03-23 20:15:35 +00:00
SetTasMovieCallbacks();
2015-03-22 16:55:34 +00:00
CurrentTasMovie.ClearChanges(); // Don't ask to save changes here.
HandleMovieLoadStuff();
CurrentTasMovie.TasStateManager.Capture(); // Capture frame 0 always.
2015-03-22 16:55:34 +00:00
BookMarkControl.UpdateValues();
2014-07-09 22:44:20 +00:00
RefreshDialog();
}
}
2015-03-22 16:55:34 +00:00
private bool HandleMovieLoadStuff(TasMovie movie = null)
{
2015-03-22 16:55:34 +00:00
if (movie == null)
movie = CurrentTasMovie;
2015-03-22 16:55:34 +00:00
WantsToControlStopMovie = false;
bool result = StartNewMovieWrapper(movie.InputLogLength == 0, movie);
if (!result)
return false;
WantsToControlStopMovie = true;
2015-03-23 20:15:35 +00:00
CurrentTasMovie.ChangeLog.ClearLog();
2015-03-22 16:55:34 +00:00
CurrentTasMovie.ClearChanges();
2015-03-22 16:55:34 +00:00
SetTextProperty();
MessageStatusLabel.Text = Path.GetFileName(CurrentTasMovie.Filename) + " loaded.";
2015-03-22 16:55:34 +00:00
return true;
}
private bool StartNewMovieWrapper(bool record, IMovie movie = null)
{
_initializing = true;
if (movie == null)
movie = CurrentTasMovie;
bool result = GlobalWin.MainForm.StartNewMovie(movie, record);
_initializing = false;
2015-03-22 16:55:34 +00:00
return result;
}
private void DummyLoadProject(string path)
{
2015-03-22 16:55:34 +00:00
if (AskSaveChanges())
LoadFile(new FileInfo(path));
}
private void DummyLoadMacro(string path)
{
if (!TasView.AnyRowsSelected)
return;
MovieZone loadZone = new MovieZone(path);
if (loadZone != null)
{
loadZone.Start = TasView.FirstSelectedIndex.Value;
loadZone.PlaceZone(CurrentTasMovie);
}
}
private void SetColumnsFromCurrentStickies()
{
foreach (var column in TasView.VisibleColumns)
{
if (Global.StickyXORAdapter.IsSticky(column.Name))
{
column.Emphasis = true;
}
}
}
#endregion
private void TastudioToStopMovie()
{
Global.MovieSession.StopMovie(false);
GlobalWin.MainForm.SetMainformMovieInfo();
}
private void DisengageTastudio()
{
GlobalWin.MainForm.PauseOnFrame = null;
GlobalWin.OSD.AddMessage("TAStudio disengaged");
Global.MovieSession.Movie = MovieService.DefaultInstance;
GlobalWin.MainForm.TakeBackControl();
Global.Config.MovieEndAction = _originalEndAction;
GlobalWin.MainForm.SetMainformMovieInfo();
// Do not keep TAStudio's disk save states.
if (Directory.Exists(PathManager.MakeAbsolutePath(Global.Config.PathEntries["Global", "TAStudio states"].Path, null)))
Directory.Delete(PathManager.MakeAbsolutePath(Global.Config.PathEntries["Global", "TAStudio states"].Path, null), true);
}
/// <summary>
/// Used when starting a new project
/// </summary>
private static string DefaultTasProjName()
{
return Path.Combine(
PathManager.MakeAbsolutePath(Global.Config.PathEntries.MoviesPathFragment, null),
TasMovie.DefaultProjectName + "." + TasMovie.Extension);
}
/// <summary>
/// Used for things like SaveFile dialogs to suggest a name to the user
/// </summary>
/// <returns></returns>
private static string SuggestedTasProjName()
{
return Path.Combine(
PathManager.MakeAbsolutePath(Global.Config.PathEntries.MoviesPathFragment, null),
PathManager.FilesystemSafeName(Global.Game) + "." + TasMovie.Extension);
}
2014-09-16 23:26:17 +00:00
private void SetTextProperty()
{
var text = "TAStudio";
if (CurrentTasMovie != null)
{
text += " - " + CurrentTasMovie.Name + (CurrentTasMovie.Changes ? "*" : "");
}
if (this.InvokeRequired)
{
this.Invoke(() => Text = text);
}
else
{
Text = text;
}
}
public void RefreshDialog(bool refreshTasView = true)
2014-07-08 15:15:35 +00:00
{
if (refreshTasView)
RefreshTasView();
if (MarkerControl != null)
MarkerControl.UpdateValues();
if (undoForm != null && !undoForm.IsDisposed)
undoForm.UpdateValues();
}
private void RefreshTasView()
{
CurrentTasMovie.UseInputCache = true;
if (TasView.RowCount != CurrentTasMovie.InputLogLength + 1)
TasView.RowCount = CurrentTasMovie.InputLogLength + 1;
TasView.Refresh();
2014-08-23 16:00:56 +00:00
CurrentTasMovie.FlushInputCache();
CurrentTasMovie.UseInputCache = false;
2015-03-23 20:15:35 +00:00
lastRefresh = Global.Emulator.Frame;
2014-07-08 15:15:35 +00:00
}
private void DoAutoRestore()
{
if (Settings.AutoRestoreLastPosition && _autoRestoreFrame.HasValue)
{
if (_autoRestoreFrame > Emulator.Frame) // Don't unpause if we are already on the desired frame, else runaway seek
{
GlobalWin.MainForm.PauseOnFrame = _autoRestoreFrame;
2015-03-23 20:15:35 +00:00
GlobalWin.MainForm.UnpauseEmulator();
}
2014-07-13 15:26:50 +00:00
}
else
{
if (_autoRestorePaused.HasValue && !_autoRestorePaused.Value)
GlobalWin.MainForm.UnpauseEmulator();
_autoRestorePaused = null;
GlobalWin.MainForm.PauseOnFrame = null; // Cancel seek to autorestore point
}
_autoRestoreFrame = null;
2014-07-13 15:26:50 +00:00
}
2014-08-31 16:51:19 +00:00
private void StartAtNearestFrameAndEmulate(int frame)
{
if (frame == Emulator.Frame)
return;
CurrentTasMovie.SwitchToPlay();
KeyValuePair<int, byte[]> closestState = CurrentTasMovie.TasStateManager.GetStateClosestToFrame(frame);
if (closestState.Value != null && (frame < Emulator.Frame || closestState.Key > Emulator.Frame))
2014-08-31 16:51:19 +00:00
{
LoadState(closestState);
2014-08-31 16:51:19 +00:00
}
// frame == Emualtor.Frame when frame == 0
if (frame > Emulator.Frame)
{
if (GlobalWin.MainForm.EmulatorPaused || GlobalWin.MainForm.IsSeeking) // make seek frame keep up with emulation on fast scrolls
{
GlobalWin.MainForm.PauseOnFrame = frame;
GlobalWin.MainForm.UnpauseEmulator();
}
}
2014-08-31 16:51:19 +00:00
}
public void LoadState(KeyValuePair<int, byte[]> state)
{
StatableEmulator.LoadStateBinary(new BinaryReader(new MemoryStream(state.Value.ToArray())));
if (state.Key == 0 && CurrentTasMovie.StartsFromSavestate)
{
Emulator.ResetCounters();
}
_hackyDontUpdate = true;
GlobalWin.Tools.UpdateBefore();
GlobalWin.Tools.UpdateAfter();
_hackyDontUpdate = false;
}
private void UpdateOtherTools() // a hack probably, surely there is a better way to do this
{
_hackyDontUpdate = true;
GlobalWin.Tools.UpdateBefore();
GlobalWin.Tools.UpdateAfter();
_hackyDontUpdate = false;
}
2014-07-17 19:00:28 +00:00
public void TogglePause()
{
GlobalWin.MainForm.TogglePause();
2014-07-17 19:00:28 +00:00
}
private void SetSplicer()
{
// TODO: columns selected
// TODO: clipboard
var list = TasView.SelectedRows;
string message = "Selected: ";
if (list.Any())
{
message += list.Count() + " rows 0 col, Clipboard: ";
}
else
{
message += list.Count() + " none, Clipboard: ";
}
message += _tasClipboard.Any() ? _tasClipboard.Count + " rows 0 col" : "empty";
SplicerStatusLabel.Text = message;
}
2014-07-16 02:17:19 +00:00
public void CallAddMarkerPopUp(int? frame = null)
{
var markerFrame = frame ?? TasView.LastSelectedIndex ?? Emulator.Frame;
InputPrompt i = new InputPrompt
{
Text = "Marker for frame " + markerFrame,
TextInputType = InputPrompt.InputType.Text,
Message = "Enter a message",
InitialValue = CurrentTasMovie.Markers.IsMarker(markerFrame) ? CurrentTasMovie.Markers.PreviousOrCurrent(markerFrame).Message : ""
};
var result = i.ShowHawkDialog();
if (result == DialogResult.OK)
{
CurrentTasMovie.Markers.Add(new TasMovieMarker(markerFrame, i.PromptText));
MarkerControl.UpdateValues();
}
}
public void CallEditMarkerPopUp(TasMovieMarker marker)
{
var markerFrame = marker.Frame;
InputPrompt i = new InputPrompt
{
Text = "Marker for frame " + markerFrame,
TextInputType = InputPrompt.InputType.Text,
Message = "Enter a message",
InitialValue = CurrentTasMovie.Markers.IsMarker(markerFrame) ? CurrentTasMovie.Markers.PreviousOrCurrent(markerFrame).Message : ""
};
var result = i.ShowHawkDialog();
if (result == DialogResult.OK)
{
marker.Message = i.PromptText;
MarkerControl.UpdateValues();
}
}
2014-07-11 02:31:43 +00:00
private void UpdateChangesIndicator()
{
// TODO
}
private void DoTriggeredAutoRestoreIfNeeded()
{
if (_triggerAutoRestore)
{
DoAutoRestore();
_triggerAutoRestore = false;
_autoRestorePaused = null;
}
}
2014-10-17 17:40:11 +00:00
#region Dialog Events
private void Tastudio_Closing(object sender, FormClosingEventArgs e)
{
if (!_initialized)
return;
_exiting = true;
if (AskSaveChanges())
{
WantsToControlStopMovie = false;
GlobalWin.MainForm.StopMovie(saveChanges: false);
DisengageTastudio();
}
else
{
e.Cancel = true;
_exiting = false;
}
if (undoForm != null)
undoForm.Close();
}
2014-10-17 17:40:11 +00:00
/// <summary>
/// This method is called everytime the Changes property is toggled on a TasMovie instance.
/// </summary>
private void TasMovie_OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
2014-09-16 23:26:17 +00:00
SetTextProperty();
}
private void TAStudio_DragEnter(object sender, DragEventArgs e)
{
e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Copy : DragDropEffects.None;
}
private void TAStudio_DragDrop(object sender, DragEventArgs e)
{
if (!AskSaveChanges())
return;
var filePaths = (string[])e.Data.GetData(DataFormats.FileDrop);
if (Path.GetExtension(filePaths[0]) == "." + TasMovie.Extension)
{
2015-03-22 16:55:34 +00:00
FileInfo file = new FileInfo(filePaths[0]);
if (file.Exists)
{
2015-03-22 16:55:34 +00:00
LoadFile(file);
}
}
}
private void TAStudio_MouseLeave(object sender, EventArgs e)
{
DoTriggeredAutoRestoreIfNeeded();
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Tab ||
keyData == (Keys.Shift | Keys.Tab) ||
keyData == Keys.Space)
{
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
#endregion
#region "Marker Control right-click menu"
private void MarkerContextMenu_Opening(object sender, CancelEventArgs e)
{
EditMarkerContextMenuItem.Enabled =
RemoveMarkerContextMenuItem.Enabled =
ScrollToMarkerToolStripMenuItem.Enabled =
MarkerControl.MarkerInputRoll.AnyRowsSelected;
}
private void ScrollToMarkerToolStripMenuItem_Click(object sender, EventArgs e)
{
SetVisibleIndex(MarkerControl.SelectedMarkerFrame());
RefreshTasView();
}
private void EditMarkerContextMenuItem_Click(object sender, EventArgs e)
{
MarkerControl.EditMarker();
}
private void AddMarkerContextMenuItem_Click(object sender, EventArgs e)
{
MarkerControl.AddMarker();
}
private void RemoveMarkerContextMenuItem_Click(object sender, EventArgs e)
{
MarkerControl.RemoveMarker();
}
#endregion
private bool AutoAdjustInput()
{
TasMovieRecord lagLog = CurrentTasMovie[Emulator.Frame - 1]; // Minus one because get frame is +1;
bool isLag = Emulator.AsInputPollable().IsLagFrame;
if (lagLog.WasLagged.HasValue)
{
if (lagLog.WasLagged.Value && !isLag)
{ // Deleting this frame requires rewinding a frame.
2015-03-23 20:15:35 +00:00
CurrentTasMovie.ChangeLog.AddInputBind(Global.Emulator.Frame - 1, true, "Bind Input; Delete " + (Global.Emulator.Frame - 1));
bool wasRecording = CurrentTasMovie.ChangeLog.IsRecording;
CurrentTasMovie.ChangeLog.IsRecording = false;
CurrentTasMovie.RemoveFrame(Global.Emulator.Frame - 1);
2015-03-23 20:15:35 +00:00
CurrentTasMovie.RemoveLagHistory(Global.Emulator.Frame); // Removes from WasLag
CurrentTasMovie.ChangeLog.IsRecording = wasRecording;
GoToFrame(Emulator.Frame - 1);
return true;
}
else if (!lagLog.WasLagged.Value && isLag)
{ // (it shouldn't need to rewind, since the inserted input wasn't polled)
2015-03-23 20:15:35 +00:00
CurrentTasMovie.ChangeLog.AddInputBind(Global.Emulator.Frame - 1, false, "Bind Input; Insert " + (Global.Emulator.Frame - 1));
bool wasRecording = CurrentTasMovie.ChangeLog.IsRecording;
CurrentTasMovie.ChangeLog.IsRecording = false;
CurrentTasMovie.InsertInput(Global.Emulator.Frame - 1, CurrentTasMovie.GetInputLogEntry(Emulator.Frame - 2));
2015-03-23 20:15:35 +00:00
CurrentTasMovie.InsertLagHistory(Global.Emulator.Frame, true);
CurrentTasMovie.ChangeLog.IsRecording = wasRecording;
return true;
}
}
return false;
}
private void TAStudio_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F)
TasPlaybackBox.FollowCursor ^= true;
}
private void MainVertialSplit_SplitterMoved(object sender, SplitterEventArgs e)
{
Settings.MainVerticalSplitDistance = MainVertialSplit.SplitterDistance;
}
private void BranchesMarkersSplit_SplitterMoved(object sender, SplitterEventArgs e)
{
Settings.BranchMarkerSplitDistance = BranchesMarkersSplit.SplitterDistance;
}
private void StartANewProjectFromSaveRamMenuItem_Click(object sender, EventArgs e)
{
if (TasView.SelectedRows.Count() == 1 &&
!CurrentTasMovie.StartsFromSavestate &&
!CurrentTasMovie.StartsFromSaveRam &&
SaveRamEmulator != null)
{
if (AskSaveChanges())
{
int index = TasView.SelectedRows.First();
GoToFrame(index);
TasMovie newProject = CurrentTasMovie.ConvertToSaveRamAnchoredMovie(
(byte[])SaveRamEmulator.CloneSaveRam());
GlobalWin.MainForm.PauseEmulator();
LoadFile(new FileInfo(newProject.Filename));
}
}
}
}
}