2013-12-01 04:00:02 +00:00
using System ;
using System.Collections.Generic ;
2015-07-25 15:08:23 +00:00
using System.Drawing ;
2013-12-01 04:00:02 +00:00
using System.IO ;
2013-12-05 19:18:20 +00:00
using System.Linq ;
2013-12-01 04:00:02 +00:00
using System.Windows.Forms ;
2014-08-23 18:02:02 +00:00
using System.ComponentModel ;
2013-12-01 20:55:52 +00:00
2014-11-30 16:42:58 +00:00
using BizHawk.Emulation.Common ;
2014-12-05 00:52:16 +00:00
using BizHawk.Emulation.Common.IEmulatorExtensions ;
2014-11-30 16:42:58 +00:00
2013-12-01 04:00:02 +00:00
using BizHawk.Client.Common ;
2014-07-06 21:20:43 +00:00
using BizHawk.Client.Common.MovieConversionExtensions ;
2014-07-27 15:22:30 +00:00
using BizHawk.Client.EmuHawk.WinFormExtensions ;
2014-07-28 01:51:11 +00:00
using BizHawk.Client.EmuHawk.ToolExtensions ;
2013-12-01 04:00:02 +00:00
namespace BizHawk.Client.EmuHawk
{
2015-01-01 18:10:07 +00:00
public partial class TAStudio : Form , IToolFormAutoConfig , IControlMainform
2013-12-01 04:00:02 +00:00
{
2013-12-14 14:40:33 +00:00
// TODO: UI flow that conveniently allows to start from savestate
2015-07-13 19:02:21 +00:00
private const string CursorColumnName = "CursorColumn" ;
2013-12-07 17:29:47 +00:00
private const string FrameColumnName = "FrameColumn" ;
2013-12-06 15:47:23 +00:00
2014-02-01 15:44:51 +00:00
private readonly List < TasClipboardEntry > _tasClipboard = new List < TasClipboardEntry > ( ) ;
2015-02-24 06:47:32 +00:00
private BackgroundWorker _saveBackgroundWorker ;
2015-01-03 02:29:55 +00:00
2014-07-14 00:35:33 +00:00
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-06 22:13:12 +00:00
{
2014-07-09 21:56:27 +00:00
var lg = Global . MovieSession . LogGeneratorInstance ( ) ;
lg . SetSource ( Global . MovieSession . MovieControllerAdapter ) ;
return ( lg as Bk2LogEntryGenerator ) . Map ( ) ;
2014-07-06 22:13:12 +00:00
}
2013-12-01 04:00:02 +00:00
2015-03-04 19:03:00 +00:00
private UndoHistoryForm undoForm ;
2015-07-25 12:40:17 +00:00
public ScreenshotPopupControl ScreenshotControl = new ScreenshotPopupControl
2015-07-25 15:08:23 +00:00
{
2015-07-25 16:01:26 +00:00
Size = new Size ( 256 , 240 ) ,
2015-07-25 15:08:23 +00:00
} ;
2015-07-25 12:40:17 +00:00
2015-07-30 20:14:14 +00:00
public string statesPath
{
get { return PathManager . MakeAbsolutePath ( Global . Config . PathEntries [ "Global" , "TAStudio states" ] . Path , null ) ; }
}
2015-01-01 18:24:35 +00:00
[ConfigPersist]
public TAStudioSettings Settings { get ; set ; }
public class TAStudioSettings
{
public TAStudioSettings ( )
{
RecentTas = new RecentFiles ( 8 ) ;
DrawInput = true ;
AutoPause = true ;
FollowCursor = true ;
2015-10-24 09:42:22 +00:00
ScrollSpeed = 3 ;
2015-07-26 01:01:02 +00:00
FollowCursorAlwaysScroll = false ;
FollowCursorScrollMethod = "near" ;
2015-08-30 16:45:14 +00:00
// default to taseditor fashion
denoteStatesWithIcons = false ;
denoteStatesWithBGColor = true ;
denoteMarkersWithIcons = false ;
denoteMarkersWithBGColor = true ;
2015-01-01 18:24:35 +00:00
}
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 ; }
2015-07-25 08:20:16 +00:00
public int ScrollSpeed { get ; set ; }
2015-07-25 08:33:23 +00:00
public bool FollowCursorAlwaysScroll { get ; set ; }
2015-08-30 16:45:14 +00:00
public string FollowCursorScrollMethod { get ; set ; }
public bool denoteStatesWithIcons { get ; set ; }
public bool denoteStatesWithBGColor { get ; set ; }
public bool denoteMarkersWithIcons { get ; set ; }
public bool denoteMarkersWithBGColor { get ; set ; }
2015-07-26 00:19:47 +00:00
public int MainVerticalSplitDistance { get ; set ; }
public int BranchMarkerSplitDistance { get ; set ; }
2015-01-01 18:24:35 +00:00
}
2014-10-20 19:04:59 +00:00
public TasMovie CurrentTasMovie
{
get { return Global . MovieSession . Movie as TasMovie ; }
}
2015-03-16 20:42:14 +00:00
#region "Initializing"
2013-12-01 04:00:02 +00:00
public TAStudio ( )
{
InitializeComponent ( ) ;
2015-07-26 01:31:06 +00:00
if ( Global . Emulator ! = null )
{
// Set the screenshot to "1x" resolution of the core
2015-07-29 00:46:11 +00:00
// cores like n64 and psx are going to still have sizes too big for the control, so cap them
int width = Global . Emulator . VideoProvider ( ) . BufferWidth ;
int height = Global . Emulator . VideoProvider ( ) . BufferHeight ;
if ( width > 320 )
{
double ratio = 320.0 / ( double ) width ;
width = 320 ;
height = ( int ) ( ( double ) ( height ) * ratio ) ;
}
ScreenshotControl . Size = new Size ( width , height ) ;
2015-07-26 01:31:06 +00:00
}
2015-07-25 12:40:17 +00:00
ScreenshotControl . Visible = false ;
Controls . Add ( ScreenshotControl ) ;
ScreenshotControl . BringToFront ( ) ;
Settings = new TAStudioSettings ( ) ;
2015-01-01 20:48:14 +00:00
2015-02-24 06:47:32 +00:00
// TODO: show this at all times or hide it when saving is done?
this . SavingProgressBar . Visible = false ;
2015-03-02 23:43:52 +00:00
InitializeSaveWorker ( ) ;
WantsToControlStopMovie = true ;
TasPlaybackBox . Tastudio = this ;
MarkerControl . Tastudio = this ;
2015-07-18 20:13:38 +00:00
BookMarkControl . Tastudio = this ;
2015-03-02 23:43:52 +00:00
MarkerControl . Emulator = this . Emulator ;
TasView . QueryItemText + = TasView_QueryItemText ;
TasView . QueryItemBkColor + = TasView_QueryItemBkColor ;
2015-03-19 19:55:38 +00:00
TasView . QueryRowBkColor + = TasView_QueryRowBkColor ;
2015-03-02 23:43:52 +00:00
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.
}
2015-02-24 06:47:32 +00:00
_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 ) ;
2015-03-02 23:43:52 +00:00
InitializeSaveWorker ( ) ; // Required, or it will error when trying to report progress again.
2015-02-24 06:47:32 +00:00
} ;
2015-01-03 02:29:55 +00:00
2015-03-02 23:43:52 +00:00
if ( CurrentTasMovie ! = null ) // Again required. TasMovie has a separate reference.
CurrentTasMovie . NewBGWorker ( _saveBackgroundWorker ) ;
2013-12-01 04:00:02 +00:00
}
2015-03-20 16:53:42 +00:00
private bool _initialized = false ;
2015-03-16 20:42:14 +00:00
private void Tastudio_Load ( object sender , EventArgs e )
2014-10-17 17:40:11 +00:00
{
2015-03-16 20:42:14 +00:00
if ( ! InitializeOnLoad ( ) )
{
Close ( ) ;
2015-09-05 21:05:14 +00:00
DialogResult = DialogResult . Cancel ;
2015-03-16 20:42:14 +00:00
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 ( ) ;
} ;
}
2015-07-25 08:20:16 +00:00
TasView . InputPaintingMode = Settings . DrawInput ;
TasView . ScrollSpeed = Settings . ScrollSpeed ;
2015-07-25 08:33:23 +00:00
TasView . AlwaysScroll = Settings . FollowCursorAlwaysScroll ;
2015-08-30 16:45:14 +00:00
TasView . ScrollMethod = Settings . FollowCursorScrollMethod ;
TasView . denoteStatesWithIcons = Settings . denoteStatesWithIcons ;
TasView . denoteStatesWithBGColor = Settings . denoteStatesWithBGColor ;
TasView . denoteMarkersWithIcons = Settings . denoteMarkersWithIcons ;
TasView . denoteMarkersWithBGColor = Settings . denoteMarkersWithBGColor ;
2015-07-25 08:20:16 +00:00
2015-07-26 00:19:47 +00:00
// 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 ;
}
2015-10-25 14:52:32 +00:00
GoToFrame ( CurrentTasMovie . Session . CurrentFrame ) ;
CurrentTasMovie . CurrentBranch = CurrentTasMovie . Session . CurrentBranch ;
2015-07-26 00:19:47 +00:00
////////////////
2015-03-16 20:42:14 +00:00
RefreshDialog ( ) ;
2015-03-20 16:53:42 +00:00
_initialized = true ;
2014-10-17 17:40:11 +00:00
}
2015-03-16 20:42:14 +00:00
private bool InitializeOnLoad ( )
2014-07-08 15:15:35 +00:00
{
2015-03-16 20:42:14 +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 ) ) ;
2015-03-16 20:42:14 +00:00
if ( ! result )
{
TasView . AllColumns . Clear ( ) ;
2015-03-22 16:55:34 +00:00
StartNewTasMovie ( ) ;
2015-03-16 20:42:14 +00:00
}
}
// 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 ( ) ;
2015-03-16 20:42:14 +00:00
}
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 ;
}
2015-07-12 16:45:30 +00:00
2015-03-16 20:42:14 +00:00
private string ClientSettingsForSave ( )
{
return TasView . UserSettingsSerialized ( ) ;
}
2015-07-12 16:45:30 +00:00
2015-03-16 20:42:14 +00:00
private void GetClientSettingsOnLoad ( string settingsJson )
{
TasView . LoadSettingsSerialized ( settingsJson ) ;
2015-03-23 20:15:35 +00:00
SetUpToolStripColumns ( ) ;
2015-03-16 20:42:14 +00:00
}
private void SetUpColumns ( )
{
TasView . AllColumns . Clear ( ) ;
2015-07-13 17:37:12 +00:00
AddColumn ( CursorColumnName , string . Empty , 18 ) ;
2015-03-16 20:42:14 +00:00
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 ) ;
}
2015-09-02 21:53:02 +00:00
var columnsToHide = TasView . AllColumns
. Where ( c = > c . Name = = "Power" | | c . Name = = "Reset" ) ;
foreach ( var column in columnsToHide )
{
column . Visible = false ;
}
TasView . AllColumns . ColumnsChanged ( ) ;
2015-03-16 20:42:14 +00:00
// 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 ,
2015-09-02 21:53:02 +00:00
Width = columnWidth
2015-03-16 20:42:14 +00:00
} ;
TasView . AllColumns . Add ( column ) ;
}
2014-07-08 15:15:35 +00:00
}
private void EngageTastudio ( )
2013-12-10 16:37:41 +00:00
{
2014-07-27 02:38:21 +00:00
GlobalWin . MainForm . PauseOnFrame = null ;
2013-12-10 16:37:41 +00:00
GlobalWin . OSD . AddMessage ( "TAStudio engaged" ) ;
2014-10-14 18:09:30 +00:00
SetTasMovieCallbacks ( ) ;
2014-09-16 23:26:17 +00:00
SetTextProperty ( ) ;
2014-07-12 01:32:21 +00:00
GlobalWin . MainForm . PauseEmulator ( ) ;
2014-07-08 16:08:52 +00:00
GlobalWin . MainForm . RelinquishControl ( this ) ;
2014-07-14 00:35:33 +00:00
_originalEndAction = Global . Config . MovieEndAction ;
2014-09-27 12:19:50 +00:00
GlobalWin . MainForm . ClearRewindData ( ) ;
2014-07-14 00:35:33 +00:00
Global . Config . MovieEndAction = MovieEndAction . Record ;
2014-07-19 16:03:12 +00:00
GlobalWin . MainForm . SetMainformMovieInfo ( ) ;
2015-03-19 19:55:38 +00:00
Global . MovieSession . ReadOnly = true ;
2014-07-08 15:15:35 +00:00
}
2014-07-08 13:46:59 +00:00
2015-03-16 20:42:14 +00:00
#endregion
2015-03-22 16:55:34 +00:00
#region "Loading"
2015-03-16 20:42:14 +00:00
private void ConvertCurrentMovieToTasproj ( )
2014-07-08 15:15:35 +00:00
{
2015-03-16 20:42:14 +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
}
2014-07-08 13:46:59 +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-08-17 14:13:44 +00:00
TasMovie newMovie = new TasMovie ( false , _saveBackgroundWorker ) ;
newMovie . TasStateManager . InvalidateCallback = GreenzoneInvalidated ;
newMovie . Filename = file . FullName ;
2015-06-27 11:48:05 +00:00
2015-08-17 14:13:44 +00:00
Settings . RecentTas . Add ( newMovie . Filename ) ;
2015-03-22 16:55:34 +00:00
2015-08-17 14:13:44 +00:00
if ( ! HandleMovieLoadStuff ( newMovie ) )
2015-03-22 16:55:34 +00:00
return false ;
2015-10-05 16:08:21 +00:00
// clear all selections
TasView . DeselectAll ( ) ;
BookMarkControl . Restart ( ) ;
MarkerControl . Restart ( ) ;
2015-03-22 16:55:34 +00:00
RefreshDialog ( ) ;
return true ;
2014-07-08 15:15:35 +00:00
}
2014-07-10 19:51:36 +00:00
private void StartNewTasMovie ( )
2013-12-10 16:37:41 +00:00
{
2014-08-19 19:24:17 +00:00
if ( AskSaveChanges ( ) )
2013-12-10 16:37:41 +00:00
{
2015-03-22 16:55:34 +00:00
Global . MovieSession . Movie = new TasMovie ( false , _saveBackgroundWorker ) ;
2015-08-05 21:36:44 +00:00
var stateManager = ( Global . MovieSession . Movie as TasMovie ) . TasStateManager ;
stateManager . MountWriteAccess ( ) ;
stateManager . 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 ( ) ;
2015-07-14 03:05:50 +00:00
CurrentTasMovie . TasStateManager . Capture ( ) ; // Capture frame 0 always.
2015-09-13 08:44:15 +00:00
// clear all selections
TasView . DeselectAll ( ) ;
2015-09-06 18:56:12 +00:00
BookMarkControl . Restart ( ) ;
MarkerControl . Restart ( ) ;
2015-09-13 08:44:15 +00:00
2014-07-09 22:44:20 +00:00
RefreshDialog ( ) ;
2013-12-10 16:37:41 +00:00
}
}
2015-03-22 16:55:34 +00:00
private bool HandleMovieLoadStuff ( TasMovie movie = null )
2015-03-16 20:42:14 +00:00
{
2015-03-22 16:55:34 +00:00
WantsToControlStopMovie = false ;
2015-08-17 14:13:44 +00:00
bool result ;
if ( movie = = null )
{
movie = CurrentTasMovie ;
result = StartNewMovieWrapper ( movie . InputLogLength = = 0 , movie ) ;
}
else
result = StartNewMovieWrapper ( false , movie ) ;
2015-03-22 16:55:34 +00:00
if ( ! result )
return false ;
WantsToControlStopMovie = true ;
2015-03-16 20:42:14 +00:00
2015-03-23 20:15:35 +00:00
CurrentTasMovie . ChangeLog . ClearLog ( ) ;
2015-03-22 16:55:34 +00:00
CurrentTasMovie . ClearChanges ( ) ;
2015-03-16 20:42:14 +00:00
2015-03-22 16:55:34 +00:00
SetTextProperty ( ) ;
MessageStatusLabel . Text = Path . GetFileName ( CurrentTasMovie . Filename ) + " loaded." ;
2015-03-16 20:42:14 +00:00
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-16 20:42:14 +00:00
2015-03-22 16:55:34 +00:00
return result ;
2015-03-16 20:42:14 +00:00
}
2014-10-18 15:50:12 +00:00
private void DummyLoadProject ( string path )
{
2015-03-22 16:55:34 +00:00
if ( AskSaveChanges ( ) )
LoadFile ( new FileInfo ( path ) ) ;
2014-10-18 15:50:12 +00:00
}
2015-03-11 16:14:02 +00:00
private void DummyLoadMacro ( string path )
{
2015-07-26 03:42:50 +00:00
if ( ! TasView . AnyRowsSelected )
2015-03-11 16:14:02 +00:00
return ;
MovieZone loadZone = new MovieZone ( path ) ;
if ( loadZone ! = null )
{
loadZone . Start = TasView . FirstSelectedIndex . Value ;
loadZone . PlaceZone ( CurrentTasMovie ) ;
}
}
2015-03-16 20:42:14 +00:00
private void SetColumnsFromCurrentStickies ( )
2014-10-14 18:09:30 +00:00
{
2015-03-16 20:42:14 +00:00
foreach ( var column in TasView . VisibleColumns )
{
if ( Global . StickyXORAdapter . IsSticky ( column . Name ) )
{
column . Emphasis = true ;
}
}
2014-10-14 18:09:30 +00:00
}
2015-03-16 20:42:14 +00:00
#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.
2015-08-14 02:51:51 +00:00
//if (Directory.Exists(statesPath)) Directory.Delete(statesPath, true);
//TODO - do we need to dispose something here instead?
2015-03-16 20:42:14 +00:00
}
/// <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-10-14 18:09:30 +00:00
}
2014-09-16 23:26:17 +00:00
private void SetTextProperty ( )
2014-09-16 23:25:08 +00:00
{
var text = "TAStudio" ;
2014-10-20 19:04:59 +00:00
if ( CurrentTasMovie ! = null )
2014-09-16 23:25:08 +00:00
{
2014-10-20 19:04:59 +00:00
text + = " - " + CurrentTasMovie . Name + ( CurrentTasMovie . Changes ? "*" : "" ) ;
2014-09-16 23:25:08 +00:00
}
2015-01-06 16:35:22 +00:00
if ( this . InvokeRequired )
{
this . Invoke ( ( ) = > Text = text ) ;
}
else
{
Text = text ;
}
2014-09-16 23:25:08 +00:00
}
2015-07-26 19:54:11 +00:00
public void RefreshDialog ( bool refreshTasView = true )
2014-07-08 15:15:35 +00:00
{
2015-07-26 19:54:11 +00:00
if ( refreshTasView )
RefreshTasView ( ) ;
2015-03-03 06:56:45 +00:00
if ( MarkerControl ! = null )
MarkerControl . UpdateValues ( ) ;
2015-03-04 19:03:00 +00:00
2015-10-04 10:39:14 +00:00
if ( BookMarkControl ! = null )
BookMarkControl . UpdateValues ( ) ;
2015-03-19 19:55:38 +00:00
if ( undoForm ! = null & & ! undoForm . IsDisposed )
2015-03-04 19:03:00 +00:00
undoForm . UpdateValues ( ) ;
2015-03-03 06:56:45 +00:00
}
private void RefreshTasView ( )
{
2014-10-20 19:04:59 +00:00
CurrentTasMovie . UseInputCache = true ;
2015-03-16 20:42:14 +00:00
if ( TasView . RowCount ! = CurrentTasMovie . InputLogLength + 1 )
TasView . RowCount = CurrentTasMovie . InputLogLength + 1 ;
2014-08-23 15:19:48 +00:00
TasView . Refresh ( ) ;
2014-08-23 16:00:56 +00:00
2015-03-03 18:22:54 +00:00
CurrentTasMovie . FlushInputCache ( ) ;
2014-10-20 19:04:59 +00:00
CurrentTasMovie . UseInputCache = false ;
2014-08-29 02:58:52 +00:00
2015-03-23 20:15:35 +00:00
lastRefresh = Global . Emulator . Frame ;
2014-07-08 15:15:35 +00:00
}
2014-10-17 20:10:21 +00:00
private void DoAutoRestore ( )
2014-09-27 14:37:02 +00:00
{
2015-01-01 18:24:35 +00:00
if ( Settings . AutoRestoreLastPosition & & _autoRestoreFrame . HasValue )
2014-09-27 14:37:02 +00:00
{
2014-12-14 01:20:19 +00:00
if ( _autoRestoreFrame > Emulator . Frame ) // Don't unpause if we are already on the desired frame, else runaway seek
2014-09-22 23:24:34 +00:00
{
2014-10-17 20:10:21 +00:00
GlobalWin . MainForm . PauseOnFrame = _autoRestoreFrame ;
2015-03-23 20:15:35 +00:00
GlobalWin . MainForm . UnpauseEmulator ( ) ;
2014-07-16 23:04:56 +00:00
}
2014-07-13 15:26:50 +00:00
}
2015-07-15 19:26:56 +00:00
else
{
if ( _autoRestorePaused . HasValue & & ! _autoRestorePaused . Value )
GlobalWin . MainForm . UnpauseEmulator ( ) ;
_autoRestorePaused = null ;
2015-07-15 20:01:08 +00:00
GlobalWin . MainForm . PauseOnFrame = null ; // Cancel seek to autorestore point
2015-07-15 19:26:56 +00:00
}
2014-10-17 20:10:21 +00:00
_autoRestoreFrame = null ;
2014-07-13 15:26:50 +00:00
}
2014-08-31 16:51:19 +00:00
private void StartAtNearestFrameAndEmulate ( int frame )
{
2015-07-13 19:02:21 +00:00
if ( frame = = Emulator . Frame )
return ;
2014-10-20 19:04:59 +00:00
CurrentTasMovie . SwitchToPlay ( ) ;
2014-10-26 23:26:43 +00:00
KeyValuePair < int , byte [ ] > closestState = CurrentTasMovie . TasStateManager . GetStateClosestToFrame ( frame ) ;
2015-07-13 19:02:21 +00:00
if ( closestState . Value ! = null & & ( frame < Emulator . Frame | | closestState . Key > Emulator . Frame ) )
2014-08-31 16:51:19 +00:00
{
2014-10-26 23:37:42 +00:00
LoadState ( closestState ) ;
2014-08-31 16:51:19 +00:00
}
2015-07-14 03:05:50 +00:00
// frame == Emualtor.Frame when frame == 0
if ( frame > Emulator . Frame )
2015-07-13 19:02:21 +00:00
{
2015-07-16 18:57:51 +00:00
if ( GlobalWin . MainForm . EmulatorPaused | | GlobalWin . MainForm . IsSeeking ) // make seek frame keep up with emulation on fast scrolls
2015-07-14 03:05:50 +00:00
{
GlobalWin . MainForm . PauseOnFrame = frame ;
GlobalWin . MainForm . UnpauseEmulator ( ) ;
}
2015-07-13 19:02:21 +00:00
}
2014-08-31 16:51:19 +00:00
}
2015-07-19 14:37:53 +00:00
public void LoadState ( KeyValuePair < int , byte [ ] > state )
2014-09-27 15:19:07 +00:00
{
2014-12-14 01:20:19 +00:00
StatableEmulator . LoadStateBinary ( new BinaryReader ( new MemoryStream ( state . Value . ToArray ( ) ) ) ) ;
2014-10-26 23:37:42 +00:00
if ( state . Key = = 0 & & CurrentTasMovie . StartsFromSavestate )
{
2014-12-14 01:20:19 +00:00
Emulator . ResetCounters ( ) ;
2014-10-26 23:37:42 +00:00
}
2014-09-27 15:19:07 +00:00
_hackyDontUpdate = true ;
GlobalWin . Tools . UpdateBefore ( ) ;
GlobalWin . Tools . UpdateAfter ( ) ;
_hackyDontUpdate = false ;
}
2014-10-11 17:43:24 +00:00
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 ;
2013-12-15 04:45:46 +00:00
}
2014-07-17 19:00:28 +00:00
public void TogglePause ( )
{
2014-07-17 20:35:12 +00:00
GlobalWin . MainForm . TogglePause ( ) ;
2014-07-17 19:00:28 +00:00
}
2013-12-19 03:45:11 +00:00
private void SetSplicer ( )
{
// TODO: columns selected
// TODO: clipboard
2014-08-30 18:42:14 +00:00
var list = TasView . SelectedRows ;
2014-08-23 18:02:02 +00:00
string message = "Selected: " ;
2013-12-19 03:45:11 +00:00
2014-10-13 18:28:29 +00:00
if ( list . Any ( ) )
2013-12-19 03:45:11 +00:00
{
2014-10-13 18:28:29 +00:00
message + = list . Count ( ) + " rows 0 col, Clipboard: " ;
2013-12-19 03:45:11 +00:00
}
else
{
2014-10-13 18:28:29 +00:00
message + = list . Count ( ) + " none, Clipboard: " ;
2013-12-19 03:45:11 +00:00
}
2015-02-24 06:47:32 +00:00
message + = _tasClipboard . Any ( ) ? _tasClipboard . Count + " rows 0 col" : "empty" ;
2013-12-19 03:45:11 +00:00
SplicerStatusLabel . Text = message ;
}
2014-07-16 02:17:19 +00:00
public void CallAddMarkerPopUp ( int? frame = null )
2014-07-15 23:43:17 +00:00
{
2014-12-14 01:20:19 +00:00
var markerFrame = frame ? ? TasView . LastSelectedIndex ? ? Emulator . Frame ;
2014-07-15 23:43:17 +00:00
InputPrompt i = new InputPrompt
{
2014-07-16 01:37:50 +00:00
Text = "Marker for frame " + markerFrame ,
2014-07-28 02:40:30 +00:00
TextInputType = InputPrompt . InputType . Text ,
2014-08-22 03:59:12 +00:00
Message = "Enter a message" ,
2014-10-20 19:04:59 +00:00
InitialValue = CurrentTasMovie . Markers . IsMarker ( markerFrame ) ? CurrentTasMovie . Markers . PreviousOrCurrent ( markerFrame ) . Message : ""
2014-07-15 23:43:17 +00:00
} ;
var result = i . ShowHawkDialog ( ) ;
if ( result = = DialogResult . OK )
{
2015-03-10 17:11:29 +00:00
CurrentTasMovie . Markers . Add ( new TasMovieMarker ( markerFrame , i . PromptText ) ) ;
2014-08-29 00:04:42 +00:00
MarkerControl . UpdateValues ( ) ;
2014-07-15 23:43:17 +00:00
}
}
2014-10-20 23:03:39 +00:00
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 )
{
2015-03-10 17:11:29 +00:00
marker . Message = i . PromptText ;
2014-10-20 23:03:39 +00:00
MarkerControl . UpdateValues ( ) ;
}
}
2014-07-11 02:31:43 +00:00
private void UpdateChangesIndicator ( )
{
// TODO
}
2014-10-21 01:31:41 +00:00
private void DoTriggeredAutoRestoreIfNeeded ( )
{
if ( _triggerAutoRestore )
{
DoAutoRestore ( ) ;
_triggerAutoRestore = false ;
2015-07-15 19:26:56 +00:00
_autoRestorePaused = null ;
2014-10-21 01:31:41 +00:00
}
}
2014-10-17 17:40:11 +00:00
#region Dialog Events
2014-08-21 22:53:03 +00:00
private void Tastudio_Closing ( object sender , FormClosingEventArgs e )
{
2015-03-20 16:53:42 +00:00
if ( ! _initialized )
return ;
2015-01-06 16:35:22 +00:00
_exiting = true ;
2014-08-21 22:53:03 +00:00
if ( AskSaveChanges ( ) )
{
2014-10-18 20:40:20 +00:00
WantsToControlStopMovie = false ;
2014-08-21 22:53:03 +00:00
GlobalWin . MainForm . StopMovie ( saveChanges : false ) ;
DisengageTastudio ( ) ;
}
else
{
e . Cancel = true ;
2015-01-06 16:35:22 +00:00
_exiting = false ;
2014-08-21 22:53:03 +00:00
}
2015-03-04 19:03:00 +00:00
if ( undoForm ! = null )
undoForm . Close ( ) ;
2014-08-21 22:53:03 +00:00
}
2014-08-19 19:24:17 +00:00
2014-10-17 17:40:11 +00:00
/// <summary>
/// This method is called everytime the Changes property is toggled on a TasMovie instance.
/// </summary>
2014-08-23 18:02:02 +00:00
private void TasMovie_OnPropertyChanged ( object sender , PropertyChangedEventArgs e )
{
2014-09-16 23:26:17 +00:00
SetTextProperty ( ) ;
2014-08-23 18:02:02 +00:00
}
2014-10-21 13:12:12 +00:00
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 )
{
2015-03-23 21:16:13 +00:00
if ( ! AskSaveChanges ( ) )
return ;
2014-10-21 13:12:12 +00:00
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 ] ) ;
2014-11-22 15:19:32 +00:00
if ( file . Exists )
2014-10-21 13:12:12 +00:00
{
2015-03-22 16:55:34 +00:00
LoadFile ( file ) ;
2014-10-21 13:12:12 +00:00
}
}
}
2014-10-22 22:43:42 +00:00
private void TAStudio_MouseLeave ( object sender , EventArgs e )
{
DoTriggeredAutoRestoreIfNeeded ( ) ;
}
2014-10-23 00:47:30 +00:00
protected override bool ProcessCmdKey ( ref Message msg , Keys keyData )
{
if ( keyData = = Keys . Tab | |
keyData = = ( Keys . Shift | Keys . Tab ) | |
keyData = = Keys . Space )
{
return true ;
}
2015-02-24 06:47:32 +00:00
2014-10-23 00:47:30 +00:00
return base . ProcessCmdKey ( ref msg , keyData ) ;
}
2014-02-01 15:44:51 +00:00
#endregion
2014-10-25 13:41:23 +00:00
2015-03-16 20:42:14 +00:00
#region "Marker Control right-click menu"
2014-10-25 13:41:23 +00:00
private void MarkerContextMenu_Opening ( object sender , CancelEventArgs e )
{
EditMarkerContextMenuItem . Enabled =
RemoveMarkerContextMenuItem . Enabled =
2015-03-16 20:42:14 +00:00
ScrollToMarkerToolStripMenuItem . Enabled =
2015-07-26 03:42:50 +00:00
MarkerControl . MarkerInputRoll . AnyRowsSelected ;
2014-10-25 13:41:23 +00:00
}
2015-03-16 20:42:14 +00:00
private void ScrollToMarkerToolStripMenuItem_Click ( object sender , EventArgs e )
{
SetVisibleIndex ( MarkerControl . SelectedMarkerFrame ( ) ) ;
RefreshTasView ( ) ;
}
2014-10-25 13:41:23 +00:00
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 ( ) ;
}
2015-03-16 20:42:14 +00:00
#endregion
2015-02-25 21:17:50 +00:00
2015-03-19 19:55:38 +00:00
private bool AutoAdjustInput ( )
2015-03-01 05:47:32 +00:00
{
2015-03-10 17:11:29 +00:00
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 ;
2015-03-10 17:11:29 +00:00
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 ;
2015-03-10 17:11:29 +00:00
GoToFrame ( Emulator . Frame - 1 ) ;
2015-03-19 19:55:38 +00:00
return true ;
2015-03-10 17:11:29 +00:00
}
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 ;
2015-03-10 17:11:29 +00:00
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 ;
2015-03-19 19:55:38 +00:00
return true ;
2015-03-10 17:11:29 +00:00
}
}
2015-03-19 19:55:38 +00:00
return false ;
2015-03-01 05:47:32 +00:00
}
2015-03-10 17:11:29 +00:00
2015-03-14 16:38:07 +00:00
private void TAStudio_KeyDown ( object sender , KeyEventArgs e )
{
if ( e . KeyCode = = Keys . F )
TasPlaybackBox . FollowCursor ^ = true ;
}
2015-07-26 00:19:47 +00:00
private void MainVertialSplit_SplitterMoved ( object sender , SplitterEventArgs e )
{
Settings . MainVerticalSplitDistance = MainVertialSplit . SplitterDistance ;
}
private void BranchesMarkersSplit_SplitterMoved ( object sender , SplitterEventArgs e )
{
Settings . BranchMarkerSplitDistance = BranchesMarkersSplit . SplitterDistance ;
}
2015-07-27 23:25:15 +00:00
2015-07-29 00:03:03 +00:00
private void TasView_CellDropped ( object sender , InputRoll . CellEventArgs e )
{
if ( e . NewCell ! = null & & e . NewCell . RowIndex . HasValue & &
! CurrentTasMovie . Markers . IsMarker ( e . NewCell . RowIndex . Value ) )
{
var currentMarker = CurrentTasMovie . Markers . Single ( m = > m . Frame = = e . OldCell . RowIndex . Value ) ;
int newFrame = e . NewCell . RowIndex . Value ;
var newMarker = new TasMovieMarker ( newFrame , currentMarker . Message ) ;
CurrentTasMovie . Markers . Remove ( currentMarker ) ;
CurrentTasMovie . Markers . Add ( newMarker ) ;
RefreshDialog ( ) ;
}
}
2015-09-05 23:25:27 +00:00
private void NewFromSubMenu_DropDownOpened ( object sender , EventArgs e )
{
NewFromNowMenuItem . Enabled =
CurrentTasMovie . InputLogLength > 0
& & ! CurrentTasMovie . StartsFromSaveRam ;
NewFromCurrentSaveRamMenuItem . Enabled =
CurrentTasMovie . InputLogLength > 0
& & SaveRamEmulator ! = null ;
}
2013-12-01 04:00:02 +00:00
}
}