2015-03-09 22:30:54 +00:00
using System ;
using System.Linq ;
using System.IO ;
using BizHawk.Emulation.Common ;
2016-12-06 17:09:17 +00:00
using BizHawk.Client.Common ;
2015-03-09 22:30:54 +00:00
namespace BizHawk.Client.EmuHawk
{
2015-03-11 16:14:02 +00:00
public class MovieZone
2015-03-09 22:30:54 +00:00
{
2016-12-06 17:09:17 +00:00
public string Name { get ; set ; }
public int Start { get ; set ; }
public int Length { get ; set ; }
2015-03-09 22:30:54 +00:00
2015-03-11 16:14:02 +00:00
private string _inputKey ;
public string InputKey
{
get { return _inputKey ; }
set { _inputKey = value ; ReSetLog ( ) ; }
}
2016-12-06 17:09:17 +00:00
2015-03-09 22:30:54 +00:00
private string [ ] _log ;
public bool Replace = true ;
public bool Overlay = false ;
private Bk2ControllerAdapter controller ;
private Bk2ControllerAdapter targetController ;
2015-03-20 16:53:42 +00:00
public MovieZone ( IMovie movie , int start , int length , string key = "" )
2015-03-09 22:30:54 +00:00
{
2016-12-06 17:09:17 +00:00
var lg = Global . MovieSession . LogGeneratorInstance ( ) as Bk2LogEntryGenerator ;
2015-03-09 22:30:54 +00:00
lg . SetSource ( Global . MovieSession . MovieControllerAdapter ) ;
targetController = new Bk2ControllerAdapter ( ) ;
2016-12-12 18:30:32 +00:00
targetController . Definition = Global . Emulator . ControllerDefinition ;
2015-03-11 16:14:02 +00:00
targetController . LatchFromSource ( targetController ) ; // Reference and create all buttons
2015-03-09 22:30:54 +00:00
if ( key = = "" )
2016-12-06 17:09:17 +00:00
{
2015-03-09 22:30:54 +00:00
key = lg . GenerateLogKey ( ) ;
2016-12-06 17:09:17 +00:00
}
2015-03-09 22:30:54 +00:00
key = key . Replace ( "LogKey:" , "" ) . Replace ( "#" , "" ) ;
key = key . Substring ( 0 , key . Length - 1 ) ;
2015-03-11 16:14:02 +00:00
_inputKey = key ;
2015-03-09 22:30:54 +00:00
Length = length ;
_log = new string [ length ] ;
// Get a IController that only contains buttons in key.
string [ ] keys = key . Split ( '|' ) ;
2016-12-06 17:10:50 +00:00
var d = new ControllerDefinition ( ) ;
2015-03-09 22:30:54 +00:00
for ( int i = 0 ; i < keys . Length ; i + + )
{
if ( Global . Emulator . ControllerDefinition . BoolButtons . Contains ( keys [ i ] ) )
2016-12-06 17:09:17 +00:00
{
2015-03-09 22:30:54 +00:00
d . BoolButtons . Add ( keys [ i ] ) ;
2016-12-06 17:09:17 +00:00
}
2015-03-09 22:30:54 +00:00
else
2016-12-06 17:09:17 +00:00
{
2015-03-09 22:30:54 +00:00
d . FloatControls . Add ( keys [ i ] ) ;
2016-12-06 17:09:17 +00:00
}
2015-03-09 22:30:54 +00:00
}
2016-12-12 18:30:32 +00:00
controller = new Bk2ControllerAdapter { Definition = d } ;
2016-12-06 17:09:17 +00:00
var logGenerator = new Bk2LogEntryGenerator ( "" ) ;
2015-03-11 16:14:02 +00:00
logGenerator . SetSource ( controller ) ;
2015-03-10 20:06:34 +00:00
logGenerator . GenerateLogEntry ( ) ; // Reference and create all buttons.
2015-03-22 16:55:34 +00:00
string movieKey = logGenerator . GenerateLogKey ( ) . Replace ( "LogKey:" , "" ) . Replace ( "#" , "" ) ;
movieKey = movieKey . Substring ( 0 , movieKey . Length - 1 ) ;
if ( key = = movieKey )
{
for ( int i = 0 ; i < length ; i + + )
2016-12-06 17:09:17 +00:00
{
2015-03-22 16:55:34 +00:00
_log [ i ] = movie . GetInputLogEntry ( i + start ) ;
2016-12-06 17:09:17 +00:00
}
2015-03-22 16:55:34 +00:00
}
else
2015-03-09 22:30:54 +00:00
{
2015-03-22 16:55:34 +00:00
for ( int i = 0 ; i < length ; i + + )
{
controller . LatchFromSource ( movie . GetInputState ( i + start ) ) ;
_log [ i ] = logGenerator . GenerateLogEntry ( ) ;
}
2015-03-11 16:14:02 +00:00
}
}
2016-12-06 17:09:17 +00:00
2015-03-11 16:14:02 +00:00
private void ReSetLog ( )
{
// Get a IController that only contains buttons in key.
string [ ] keys = _inputKey . Split ( '|' ) ;
2016-12-06 17:09:17 +00:00
var d = new ControllerDefinition ( ) ;
2015-03-11 16:14:02 +00:00
for ( int i = 0 ; i < keys . Length ; i + + )
{
if ( Global . Emulator . ControllerDefinition . BoolButtons . Contains ( keys [ i ] ) )
2016-12-06 17:09:17 +00:00
{
2015-03-11 16:14:02 +00:00
d . BoolButtons . Add ( keys [ i ] ) ;
2016-12-06 17:09:17 +00:00
}
2015-03-11 16:14:02 +00:00
else
2016-12-06 17:09:17 +00:00
{
2015-03-11 16:14:02 +00:00
d . FloatControls . Add ( keys [ i ] ) ;
2016-12-06 17:09:17 +00:00
}
2015-03-11 16:14:02 +00:00
}
2016-12-12 18:30:32 +00:00
var newController = new Bk2ControllerAdapter { Definition = d } ;
2016-12-06 17:09:17 +00:00
var logGenerator = new Bk2LogEntryGenerator ( "" ) ;
2015-03-11 16:14:02 +00:00
logGenerator . SetSource ( newController ) ;
logGenerator . GenerateLogEntry ( ) ; // Reference and create all buttons.
2016-12-06 17:09:17 +00:00
2015-03-11 16:14:02 +00:00
// Reset all buttons in targetController (it may still have buttons that aren't being set here set true)
2016-12-06 17:09:17 +00:00
var tC = new Bk2LogEntryGenerator ( "" ) ;
2015-03-11 16:14:02 +00:00
tC . SetSource ( targetController ) ;
targetController . SetControllersAsMnemonic ( tC . EmptyEntry ) ;
for ( int i = 0 ; i < Length ; i + + )
{
controller . SetControllersAsMnemonic ( _log [ i ] ) ;
2015-03-10 20:06:34 +00:00
LatchFromSourceButtons ( targetController , controller ) ;
2015-03-11 16:14:02 +00:00
newController . LatchFromSource ( targetController ) ;
2015-03-09 22:30:54 +00:00
_log [ i ] = logGenerator . GenerateLogEntry ( ) ;
}
2015-03-11 16:14:02 +00:00
controller = newController ;
2015-03-09 22:30:54 +00:00
}
2015-03-20 16:53:42 +00:00
public void PlaceZone ( IMovie movie )
2015-03-09 22:30:54 +00:00
{
2015-07-16 19:39:33 +00:00
if ( movie is TasMovie )
2016-12-06 17:09:17 +00:00
{
2019-03-18 14:06:37 +00:00
( movie as TasMovie ) . ChangeLog . BeginNewBatch ( $"Place Macro at {Start}" ) ;
2016-12-06 17:09:17 +00:00
}
2015-03-20 16:53:42 +00:00
2015-03-10 20:06:34 +00:00
if ( Start > movie . InputLogLength )
{ // Cannot place a frame here. Find a nice way around this.
return ;
}
2015-03-20 16:53:42 +00:00
if ( ! Replace & & movie is TasMovie )
{ // Can't be done with a regular movie.
( movie as TasMovie ) . InsertEmptyFrame ( Start , Length ) ;
}
2015-03-09 22:30:54 +00:00
if ( Overlay )
{
for ( int i = 0 ; i < Length ; i + + )
2015-03-10 20:06:34 +00:00
{ // Overlay the frames.
2015-03-11 16:14:02 +00:00
controller . SetControllersAsMnemonic ( _log [ i ] ) ;
LatchFromSourceButtons ( targetController , controller ) ;
2015-03-10 20:06:34 +00:00
ORLatchFromSource ( targetController , movie . GetInputState ( i + Start ) ) ;
2015-03-20 16:53:42 +00:00
movie . PokeFrame ( i + Start , targetController ) ;
2015-03-09 22:30:54 +00:00
}
}
else
{
for ( int i = 0 ; i < Length ; i + + )
2015-03-10 20:06:34 +00:00
{ // Copy over the frame.
2015-03-11 16:14:02 +00:00
controller . SetControllersAsMnemonic ( _log [ i ] ) ;
LatchFromSourceButtons ( targetController , controller ) ;
2015-03-20 16:53:42 +00:00
movie . PokeFrame ( i + Start , targetController ) ;
2015-03-09 22:30:54 +00:00
}
}
if ( movie is TasMovie ) // Assume TAStudio is open?
{
2015-07-16 19:39:33 +00:00
( movie as TasMovie ) . ChangeLog . EndBatch ( ) ;
2015-03-10 20:06:34 +00:00
if ( Global . Emulator . Frame > Start )
2015-03-09 22:30:54 +00:00
{
// TODO: Go to start of macro? Ask TAStudio to do that?
// TasMovie.InvalidateAfter(Start) [this is private]
// Load last state, Emulate to Start
2015-03-11 16:14:02 +00:00
// Or do this, if TAStudio has to be open.
if ( GlobalWin . Tools . IsLoaded < TAStudio > ( ) )
2016-12-06 17:10:50 +00:00
{
2015-03-11 16:14:02 +00:00
( GlobalWin . Tools . Get < TAStudio > ( ) as TAStudio ) . GoToFrame ( Start ) ;
2016-12-06 17:10:50 +00:00
}
2015-03-09 22:30:54 +00:00
GlobalWin . Tools . UpdateBefore ( ) ;
GlobalWin . Tools . UpdateAfter ( ) ;
}
else if ( GlobalWin . Tools . IsLoaded < TAStudio > ( ) )
{
GlobalWin . Tools . Get < TAStudio > ( ) . UpdateValues ( ) ;
}
}
2015-03-20 16:53:42 +00:00
if ( movie . InputLogLength > = Global . Emulator . Frame )
{
movie . SwitchToPlay ( ) ;
Global . Config . MovieEndAction = MovieEndAction . Record ;
}
2015-03-09 22:30:54 +00:00
}
public void Save ( string fileName )
{
// Save the controller definition/LogKey
// Save the controller name and player count. (Only for the user.)
// Save whether or not the macro should use overlay input, and/or replace
2015-03-10 20:06:34 +00:00
string [ ] header = new string [ 4 ] ;
header [ 0 ] = InputKey ;
header [ 1 ] = Global . Emulator . ControllerDefinition . Name ;
header [ 2 ] = Global . Emulator . ControllerDefinition . PlayerCount . ToString ( ) ;
2019-03-18 14:06:37 +00:00
header [ 3 ] = $"{Overlay},{Replace}" ;
2015-03-09 22:30:54 +00:00
2015-03-10 20:06:34 +00:00
File . WriteAllLines ( fileName , header ) ;
2015-03-09 22:30:54 +00:00
File . AppendAllLines ( fileName , _log ) ;
}
2016-12-06 17:09:17 +00:00
2015-03-09 22:30:54 +00:00
public MovieZone ( string fileName )
{
if ( ! File . Exists ( fileName ) )
2016-12-06 17:09:17 +00:00
{
2015-03-09 22:30:54 +00:00
return ;
2016-12-06 17:09:17 +00:00
}
2015-03-09 22:30:54 +00:00
2015-03-10 20:06:34 +00:00
string [ ] readText = File . ReadAllLines ( fileName ) ;
2016-12-06 17:09:17 +00:00
2015-03-10 20:06:34 +00:00
// If the LogKey contains buttons/controls not accepted by the emulator,
// tell the user and display the macro's controller name and player count
2015-03-11 16:14:02 +00:00
_inputKey = readText [ 0 ] ;
2015-03-10 20:06:34 +00:00
Bk2LogEntryGenerator lg = Global . MovieSession . LogGeneratorInstance ( ) as Bk2LogEntryGenerator ;
lg . SetSource ( Global . MovieSession . MovieControllerAdapter ) ;
string key = lg . GenerateLogKey ( ) ;
key = key . Replace ( "LogKey:" , "" ) . Replace ( "#" , "" ) ;
key = key . Substring ( 0 , key . Length - 1 ) ;
string [ ] emuKeys = key . Split ( '|' ) ;
2015-03-11 16:14:02 +00:00
string [ ] macroKeys = _inputKey . Split ( '|' ) ;
2015-03-10 20:06:34 +00:00
for ( int i = 0 ; i < macroKeys . Length ; i + + )
{
if ( ! emuKeys . Contains ( macroKeys [ i ] ) )
{
2019-03-18 14:06:37 +00:00
System . Windows . Forms . MessageBox . Show ( $"The selected macro is not compatible with the current emulator core.\nMacro controller: {readText[1]}\nMacro player count: {readText[2]}" , "Error" ) ;
2015-03-10 20:06:34 +00:00
return ;
}
}
// Settings
string [ ] settings = readText [ 3 ] . Split ( ',' ) ;
Overlay = Convert . ToBoolean ( settings [ 0 ] ) ;
Replace = Convert . ToBoolean ( settings [ 1 ] ) ;
_log = new string [ readText . Length - 4 ] ;
readText . ToList ( ) . CopyTo ( 4 , _log , 0 , _log . Length ) ;
2015-03-09 22:30:54 +00:00
Length = _log . Length ;
Start = 0 ;
Name = Path . GetFileNameWithoutExtension ( fileName ) ;
2015-03-10 20:06:34 +00:00
2015-03-11 16:14:02 +00:00
// Adapters
2015-03-10 20:06:34 +00:00
targetController = new Bk2ControllerAdapter ( ) ;
2016-12-12 18:30:32 +00:00
targetController . Definition = Global . Emulator . ControllerDefinition ;
2015-03-10 20:06:34 +00:00
targetController . LatchFromSource ( targetController ) ; // Reference and create all buttons
2015-03-11 16:14:02 +00:00
string [ ] keys = _inputKey . Split ( '|' ) ;
ControllerDefinition d = new ControllerDefinition ( ) ;
for ( int i = 0 ; i < keys . Length ; i + + )
{
if ( Global . Emulator . ControllerDefinition . BoolButtons . Contains ( keys [ i ] ) )
2016-12-06 17:09:17 +00:00
{
2015-03-11 16:14:02 +00:00
d . BoolButtons . Add ( keys [ i ] ) ;
2016-12-06 17:09:17 +00:00
}
2015-03-11 16:14:02 +00:00
else
2016-12-06 17:09:17 +00:00
{
2015-03-11 16:14:02 +00:00
d . FloatControls . Add ( keys [ i ] ) ;
2016-12-06 17:09:17 +00:00
}
2015-03-11 16:14:02 +00:00
}
2016-12-06 17:09:17 +00:00
2016-12-12 18:30:32 +00:00
controller = new Bk2ControllerAdapter { Definition = d } ;
2015-03-09 22:30:54 +00:00
}
2015-03-10 20:06:34 +00:00
2016-12-06 17:09:17 +00:00
#region Custom Latch
2015-03-10 20:06:34 +00:00
2016-12-06 17:09:17 +00:00
private void LatchFromSourceButtons ( Bk2ControllerAdapter latching , IController source )
2015-03-10 20:06:34 +00:00
{
2016-12-12 18:30:32 +00:00
foreach ( string button in source . Definition . BoolButtons )
2015-03-10 20:06:34 +00:00
{
latching [ button ] = source . IsPressed ( button ) ;
}
2016-12-12 18:30:32 +00:00
foreach ( string name in source . Definition . FloatControls )
2015-03-10 20:06:34 +00:00
{
latching . SetFloat ( name , source . GetFloat ( name ) ) ;
}
}
private void ORLatchFromSource ( Bk2ControllerAdapter latching , IController source )
{
2016-12-12 18:30:32 +00:00
foreach ( string button in latching . Definition . BoolButtons )
2015-03-10 20:06:34 +00:00
{
latching [ button ] | = source . IsPressed ( button ) ;
}
2016-12-12 18:30:32 +00:00
foreach ( string name in latching . Definition . FloatControls )
2015-03-10 20:06:34 +00:00
{
float sFloat = source . GetFloat ( name ) ;
2016-12-12 18:30:32 +00:00
int indexRange = source . Definition . FloatControls . IndexOf ( name ) ;
if ( sFloat = = source . Definition . FloatRanges [ indexRange ] . Mid )
2016-12-06 17:09:17 +00:00
{
2015-03-10 20:06:34 +00:00
latching . SetFloat ( name , sFloat ) ;
2016-12-06 17:09:17 +00:00
}
2015-03-10 20:06:34 +00:00
}
}
#endregion
2015-03-09 22:30:54 +00:00
}
}