2013-04-17 22:43:11 -04:00
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
2010-06-09 01:37:08 +00:00
2014-02-19 13:09:14 -05:00
#include <polarssl/md5.h>
2014-02-17 05:18:15 -05:00
2014-07-27 13:37:09 -04:00
#include "Common/ChunkFile.h"
2014-04-06 09:28:44 -04:00
#include "Common/CommonPaths.h"
2014-02-17 05:18:15 -05:00
#include "Common/FileUtil.h"
2014-04-06 09:28:44 -04:00
#include "Common/Hash.h"
2014-02-17 05:18:15 -05:00
#include "Common/NandPaths.h"
2014-07-02 22:42:36 -04:00
#include "Common/StringUtil.h"
2014-02-17 05:18:15 -05:00
#include "Common/Thread.h"
#include "Common/Timer.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
2014-06-18 16:13:46 -04:00
#include "Core/CoreTiming.h"
2014-02-17 05:18:15 -05:00
#include "Core/Movie.h"
#include "Core/NetPlayProto.h"
#include "Core/State.h"
2014-04-06 09:28:44 -04:00
#include "Core/DSP/DSPCore.h"
2014-02-17 05:18:15 -05:00
#include "Core/HW/DVDInterface.h"
#include "Core/HW/EXI_Device.h"
2014-11-02 01:58:59 -05:00
#include "Core/HW/ProcessorInterface.h"
2014-02-17 05:18:15 -05:00
#include "Core/HW/SI.h"
#include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
#include "Core/HW/WiimoteEmu/WiimoteHid.h"
2014-09-29 23:17:59 -04:00
#include "Core/HW/WiimoteEmu/Attachment/Classic.h"
#include "Core/HW/WiimoteEmu/Attachment/Nunchuk.h"
2014-02-17 05:18:15 -05:00
#include "Core/IPC_HLE/WII_IPC_HLE_Device_usb.h"
2015-05-08 17:28:03 -04:00
#include "Core/IPC_HLE/WII_IPC_HLE_WiiMote.h"
2014-02-17 05:18:15 -05:00
#include "Core/PowerPC/PowerPC.h"
2014-07-27 13:37:09 -04:00
#include "InputCommon/GCPadStatus.h"
2014-02-17 05:18:15 -05:00
#include "VideoCommon/VideoConfig.h"
2011-02-12 02:14:20 +00:00
2012-11-25 22:41:48 -05:00
// The chunk to allocate movie data in multiples of.
#define DTM_BASE_LENGTH (1024)
2011-05-03 00:06:44 +00:00
2014-07-08 15:58:25 +02:00
static std :: mutex cs_frameSkip ;
2010-06-09 01:37:08 +00:00
2011-06-24 06:50:50 +00:00
namespace Movie {
2010-06-09 01:37:08 +00:00
2014-09-08 19:13:49 -04:00
static bool s_bFrameStep = false ;
static bool s_bFrameStop = false ;
static bool s_bReadOnly = true ;
static u32 s_rerecords = 0 ;
static PlayMode s_playMode = MODE_NONE ;
2010-06-09 01:37:08 +00:00
2014-09-08 19:13:49 -04:00
static u32 s_framesToSkip = 0 , s_frameSkipCounter = 0 ;
2010-06-09 01:37:08 +00:00
2014-09-08 19:13:49 -04:00
static u8 s_numPads = 0 ;
static ControllerState s_padState ;
2014-07-08 15:58:25 +02:00
static DTMHeader tmpHeader ;
static u8 * tmpInput = nullptr ;
static size_t tmpInputAllocated = 0 ;
2014-09-08 19:13:49 -04:00
static u64 s_currentByte = 0 , s_totalBytes = 0 ;
2011-12-14 04:03:05 -08:00
u64 g_currentFrame = 0 , g_totalFrames = 0 ; // VI
2014-07-08 22:37:58 +02:00
u64 g_currentLagCount = 0 ;
2014-09-11 01:55:43 -04:00
static u64 s_totalLagCount = 0 ; // just stats
2011-12-14 04:03:05 -08:00
u64 g_currentInputCount = 0 , g_totalInputCount = 0 ; // just stats
2014-09-08 19:13:49 -04:00
static u64 s_totalTickCount = 0 , s_tickCountAtLastInput = 0 ; // just stats
static u64 s_recordingStartTime ; // seconds since 1970 that recording started
static bool s_bSaveConfig = false , s_bSkipIdle = false , s_bDualCore = false , s_bProgressive = false , s_bDSPHLE = false , s_bFastDiscSpeed = false ;
static bool s_bSyncGPU = false , s_bNetPlay = false ;
static std :: string s_videoBackend = "unknown" ;
static int s_iCPUCore = 1 ;
2014-07-08 15:58:25 +02:00
bool g_bClearSave = false ;
2012-10-20 14:18:42 -04:00
bool g_bDiscChange = false ;
2014-11-02 01:58:59 -05:00
bool g_bReset = false ;
2014-09-08 19:13:49 -04:00
static std :: string s_author = "" ;
2012-10-20 14:18:42 -04:00
std :: string g_discChange = "" ;
2012-10-25 02:44:30 -04:00
u64 g_titleID = 0 ;
2014-09-08 19:13:49 -04:00
static u8 s_MD5 [ 16 ];
static u8 s_bongos , s_memcards ;
static u8 s_revision [ 20 ];
static u32 s_DSPiromHash = 0 ;
static u32 s_DSPcoefHash = 0 ;
2010-06-09 01:37:08 +00:00
2014-09-08 19:13:49 -04:00
static bool s_bRecordingFromSaveState = false ;
static bool s_bPolled = false ;
2010-06-09 01:37:08 +00:00
2014-09-08 19:13:49 -04:00
static std :: string s_InputDisplay [ 8 ];
2011-02-17 09:12:36 +00:00
2014-09-14 23:42:32 -04:00
static GCManipFunction gcmfunc = nullptr ;
static WiiManipFunction wiimfunc = nullptr ;
2011-06-24 06:50:50 +00:00
2014-07-08 14:29:26 +02:00
static void EnsureTmpInputSize ( size_t bound )
2012-11-25 22:41:48 -05:00
{
if ( tmpInputAllocated >= bound )
return ;
// The buffer expands in powers of two of DTM_BASE_LENGTH
// (standard exponential buffer growth).
size_t newAlloc = DTM_BASE_LENGTH ;
while ( newAlloc < bound )
newAlloc *= 2 ;
2013-10-29 01:23:17 -04:00
2012-11-25 22:41:48 -05:00
u8 * newTmpInput = new u8 [ newAlloc ];
tmpInputAllocated = newAlloc ;
2014-03-09 21:14:26 +01:00
if ( tmpInput != nullptr )
2012-11-25 22:41:48 -05:00
{
2014-09-08 19:13:49 -04:00
if ( s_totalBytes > 0 )
memcpy ( newTmpInput , tmpInput , ( size_t ) s_totalBytes );
2012-11-25 22:41:48 -05:00
delete [] tmpInput ;
}
tmpInput = newTmpInput ;
}
2011-12-26 05:09:30 -06:00
2014-08-30 18:53:18 -04:00
static bool IsMovieHeader ( u8 magic [ 4 ])
{
return magic [ 0 ] == 'D' &&
magic [ 1 ] == 'T' &&
magic [ 2 ] == 'M' &&
magic [ 3 ] == 0x1A ;
}
2011-02-17 09:12:36 +00:00
std :: string GetInputDisplay ()
{
2014-09-06 23:44:25 -04:00
if ( ! IsMovieActive ())
2012-12-10 06:20:06 -05:00
{
2014-09-08 19:13:49 -04:00
s_numPads = 0 ;
2014-09-08 20:15:47 -04:00
for ( int i = 0 ; i < 4 ; ++ i )
2012-12-10 06:20:06 -05:00
{
2014-09-22 11:45:54 -04:00
if ( SerialInterface :: GetDeviceType ( i ) != SIDEVICE_NONE )
2014-09-08 19:13:49 -04:00
s_numPads |= ( 1 << i );
2012-12-10 06:20:06 -05:00
if ( g_wiimote_sources [ i ] != WIIMOTE_SRC_NONE )
2014-09-08 19:13:49 -04:00
s_numPads |= ( 1 << ( i + 4 ));
2012-12-10 06:20:06 -05:00
}
}
2013-04-16 23:14:36 -04:00
2011-02-17 09:12:36 +00:00
std :: string inputDisplay = "" ;
2011-12-14 04:03:05 -08:00
for ( int i = 0 ; i < 8 ; ++ i )
2014-09-08 19:13:49 -04:00
if (( s_numPads & ( 1 << i )) != 0 )
inputDisplay . append ( s_InputDisplay [ i ]);
2013-10-29 01:23:17 -04:00
return inputDisplay ;
2011-02-17 09:12:36 +00:00
}
2010-06-09 01:37:08 +00:00
void FrameUpdate ()
{
2011-12-14 04:03:05 -08:00
g_currentFrame ++ ;
2014-09-08 19:13:49 -04:00
if ( ! s_bPolled )
2011-12-14 04:03:05 -08:00
g_currentLagCount ++ ;
if ( IsRecordingInput ())
{
g_totalFrames = g_currentFrame ;
2014-09-11 01:55:43 -04:00
s_totalLagCount = g_currentLagCount ;
2011-12-14 04:03:05 -08:00
}
2014-09-08 19:13:49 -04:00
if ( s_bFrameStep )
2011-12-11 21:08:26 -08:00
{
2010-06-09 01:37:08 +00:00
Core :: SetState ( Core :: CORE_PAUSE );
2014-09-08 19:13:49 -04:00
s_bFrameStep = false ;
2011-12-11 21:08:26 -08:00
}
2013-10-29 01:23:17 -04:00
2010-06-09 01:37:08 +00:00
// ("framestop") the only purpose of this is to cause interpreter/jit Run() to return temporarily.
2014-07-07 05:30:06 +02:00
// after that we set it back to CPU_RUNNING and continue as normal.
2014-09-08 19:13:49 -04:00
if ( s_bFrameStop )
2014-07-07 05:30:06 +02:00
* PowerPC :: GetStatePtr () = PowerPC :: CPU_STEPPING ;
2010-06-09 01:37:08 +00:00
2014-09-08 19:13:49 -04:00
if ( s_framesToSkip )
2010-06-09 01:37:08 +00:00
FrameSkipping ();
2013-10-29 01:23:17 -04:00
2014-09-08 19:13:49 -04:00
s_bPolled = false ;
2010-06-09 01:37:08 +00:00
}
2012-01-02 02:20:22 -08:00
// called when game is booting up, even if no movie is active,
// but potentially after BeginRecordingInput or PlayInput has been called.
void Init ()
{
2014-09-08 19:13:49 -04:00
s_bPolled = false ;
s_bFrameStep = false ;
s_bFrameStop = false ;
s_bSaveConfig = false ;
s_iCPUCore = SConfig :: GetInstance (). m_LocalCoreStartupParameter . iCPUCore ;
2012-10-20 19:54:38 -04:00
if ( IsPlayingInput ())
2012-10-18 04:18:40 -04:00
{
2012-10-20 19:54:38 -04:00
ReadHeader ();
2012-11-25 19:26:37 -05:00
std :: thread md5thread ( CheckMD5 );
2013-07-28 17:55:35 +02:00
md5thread . detach ();
2014-09-08 20:15:47 -04:00
if ( strncmp (( char * ) tmpHeader . gameID , SConfig :: GetInstance (). m_LocalCoreStartupParameter . GetUniqueID (). c_str (), 6 ))
2012-10-24 19:37:51 -04:00
{
2014-09-08 20:15:47 -04:00
PanicAlertT ( "The recorded game (%s) is not the same as the selected game (%s)" , tmpHeader . gameID , SConfig :: GetInstance (). m_LocalCoreStartupParameter . GetUniqueID (). c_str ());
2012-10-24 19:37:51 -04:00
EndPlayInput ( false );
}
2012-10-18 04:18:40 -04:00
}
2013-04-16 23:14:36 -04:00
2012-10-24 16:35:52 -04:00
if ( IsRecordingInput ())
{
GetSettings ();
2012-11-26 12:29:36 -05:00
std :: thread md5thread ( GetMD5 );
2013-07-28 17:55:35 +02:00
md5thread . detach ();
2014-09-08 19:13:49 -04:00
s_tickCountAtLastInput = 0 ;
2012-10-24 16:35:52 -04:00
}
2012-11-26 12:29:36 -05:00
2014-09-08 19:13:49 -04:00
s_frameSkipCounter = s_framesToSkip ;
memset ( & s_padState , 0 , sizeof ( s_padState ));
2012-10-21 17:48:45 -04:00
if ( ! tmpHeader . bFromSaveState || ! IsPlayingInput ())
2012-10-18 04:14:25 -04:00
Core :: SetStateFileName ( "" );
2012-10-24 23:21:34 -04:00
2014-09-08 19:13:49 -04:00
for ( auto & disp : s_InputDisplay )
2013-10-29 01:09:01 -04:00
disp . clear ();
2012-01-02 02:20:22 -08:00
2014-09-06 23:44:25 -04:00
if ( ! IsMovieActive ())
2012-01-02 02:20:22 -08:00
{
2014-09-08 19:13:49 -04:00
s_bRecordingFromSaveState = false ;
s_rerecords = 0 ;
s_currentByte = 0 ;
2012-01-02 02:20:22 -08:00
g_currentFrame = 0 ;
g_currentLagCount = 0 ;
g_currentInputCount = 0 ;
}
}
2011-03-17 10:41:56 +00:00
void InputUpdate ()
{
2011-12-14 04:03:05 -08:00
g_currentInputCount ++ ;
if ( IsRecordingInput ())
2014-06-18 16:13:46 -04:00
{
2011-12-14 04:03:05 -08:00
g_totalInputCount = g_currentInputCount ;
2014-09-08 19:13:49 -04:00
s_totalTickCount += CoreTiming :: GetTicks () - s_tickCountAtLastInput ;
s_tickCountAtLastInput = CoreTiming :: GetTicks ();
2014-06-18 16:13:46 -04:00
}
2012-11-18 01:07:48 -05:00
2014-09-08 20:15:47 -04:00
if ( IsPlayingInput () && g_currentInputCount == ( g_totalInputCount - 1 ) && SConfig :: GetInstance (). m_PauseMovie )
2012-11-18 01:07:48 -05:00
Core :: SetState ( Core :: CORE_PAUSE );
2011-03-17 10:41:56 +00:00
}
2010-06-09 01:37:08 +00:00
void SetFrameSkipping ( unsigned int framesToSkip )
{
2011-03-05 06:11:26 +00:00
std :: lock_guard < std :: mutex > lk ( cs_frameSkip );
2013-10-29 01:23:17 -04:00
2014-09-08 19:13:49 -04:00
s_framesToSkip = framesToSkip ;
s_frameSkipCounter = 0 ;
2013-10-29 01:23:17 -04:00
2010-06-09 01:37:08 +00:00
// Don't forget to re-enable rendering in case it wasn't...
// as this won't be changed anymore when frameskip is turned off
if ( framesToSkip == 0 )
2011-01-31 01:28:32 +00:00
g_video_backend -> Video_SetRendering ( true );
2010-06-09 01:37:08 +00:00
}
void SetPolledDevice ()
{
2014-09-08 19:13:49 -04:00
s_bPolled = true ;
2010-06-09 01:37:08 +00:00
}
2011-12-11 21:08:26 -08:00
void DoFrameStep ()
2010-06-09 01:37:08 +00:00
{
2014-03-11 00:30:55 +13:00
if ( Core :: GetState () == Core :: CORE_PAUSE )
2011-12-11 21:08:26 -08:00
{
// if already paused, frame advance for 1 frame
Core :: SetState ( Core :: CORE_RUN );
2011-12-14 04:03:05 -08:00
Core :: RequestRefreshInfo ();
2014-09-08 19:13:49 -04:00
s_bFrameStep = true ;
2011-12-11 21:08:26 -08:00
}
2014-09-08 19:13:49 -04:00
else if ( ! s_bFrameStep )
2011-12-11 21:08:26 -08:00
{
// if not paused yet, pause immediately instead
Core :: SetState ( Core :: CORE_PAUSE );
}
2010-06-09 01:37:08 +00:00
}
2011-01-17 01:47:27 +00:00
void SetFrameStopping ( bool bEnabled )
2010-06-09 01:37:08 +00:00
{
2014-09-08 19:13:49 -04:00
s_bFrameStop = bEnabled ;
2010-06-09 01:37:08 +00:00
}
2011-02-12 02:14:20 +00:00
void SetReadOnly ( bool bEnabled )
{
2014-09-08 19:13:49 -04:00
if ( s_bReadOnly != bEnabled )
2011-12-11 21:08:26 -08:00
Core :: DisplayMessage ( bEnabled ? "Read-only mode." : "Read+Write mode." , 1000 );
2014-09-08 19:13:49 -04:00
s_bReadOnly = bEnabled ;
2011-02-12 02:14:20 +00:00
}
2010-06-09 01:37:08 +00:00
void FrameSkipping ()
{
2011-02-11 12:26:15 +00:00
// Frameskipping will desync movie playback
2014-09-08 20:15:47 -04:00
if ( ! IsMovieActive () || NetPlay :: IsNetPlayRunning ())
2011-02-11 12:26:15 +00:00
{
2011-03-05 06:11:26 +00:00
std :: lock_guard < std :: mutex > lk ( cs_frameSkip );
2010-06-09 01:37:08 +00:00
2014-09-08 19:13:49 -04:00
s_frameSkipCounter ++ ;
if ( s_frameSkipCounter > s_framesToSkip || Core :: ShouldSkipFrame ( s_frameSkipCounter ) == false )
s_frameSkipCounter = 0 ;
2012-10-20 19:54:38 -04:00
2014-09-08 19:13:49 -04:00
g_video_backend -> Video_SetRendering ( ! s_frameSkipCounter );
2011-02-11 12:26:15 +00:00
}
2010-06-09 01:37:08 +00:00
}
bool IsRecordingInput ()
{
2014-09-08 19:13:49 -04:00
return ( s_playMode == MODE_RECORDING );
2010-06-09 01:37:08 +00:00
}
2011-02-15 09:07:55 +00:00
bool IsRecordingInputFromSaveState ()
{
2014-09-08 19:13:49 -04:00
return s_bRecordingFromSaveState ;
2011-02-15 09:07:55 +00:00
}
2011-12-14 04:03:05 -08:00
bool IsJustStartingRecordingInputFromSaveState ()
{
return IsRecordingInputFromSaveState () && g_currentFrame == 0 ;
}
2012-10-18 04:03:12 -04:00
bool IsJustStartingPlayingInputFromSaveState ()
{
2012-10-21 17:48:45 -04:00
return IsRecordingInputFromSaveState () && g_currentFrame == 1 && IsPlayingInput ();
2012-10-18 04:03:12 -04:00
}
2010-06-09 01:37:08 +00:00
bool IsPlayingInput ()
{
2014-09-08 19:13:49 -04:00
return ( s_playMode == MODE_PLAYING );
2010-06-09 01:37:08 +00:00
}
2014-09-06 23:44:25 -04:00
bool IsMovieActive ()
{
2014-09-08 19:13:49 -04:00
return s_playMode != MODE_NONE ;
2014-09-06 23:44:25 -04:00
}
2011-07-11 20:15:05 +00:00
bool IsReadOnly ()
{
2014-09-08 19:13:49 -04:00
return s_bReadOnly ;
2011-07-11 20:15:05 +00:00
}
2011-12-26 05:09:30 -06:00
u64 GetRecordingStartTime ()
{
2014-09-08 19:13:49 -04:00
return s_recordingStartTime ;
2011-12-26 05:09:30 -06:00
}
2010-09-06 21:41:01 +00:00
bool IsUsingPad ( int controller )
{
2014-09-08 19:13:49 -04:00
return (( s_numPads & ( 1 << controller )) != 0 );
2011-02-11 19:09:46 +00:00
}
2012-12-18 23:19:15 -05:00
bool IsUsingBongo ( int controller )
{
2014-09-08 19:13:49 -04:00
return (( s_bongos & ( 1 << controller )) != 0 );
2012-12-18 23:19:15 -05:00
}
2011-02-11 19:09:46 +00:00
bool IsUsingWiimote ( int wiimote )
{
2014-09-08 19:13:49 -04:00
return (( s_numPads & ( 1 << ( wiimote + 4 ))) != 0 );
2010-09-06 21:41:01 +00:00
}
2012-10-18 04:18:40 -04:00
bool IsConfigSaved ()
{
2014-09-08 19:13:49 -04:00
return s_bSaveConfig ;
2012-10-18 04:18:40 -04:00
}
bool IsDualCore ()
{
2014-09-08 19:13:49 -04:00
return s_bDualCore ;
2012-10-18 04:18:40 -04:00
}
bool IsProgressive ()
{
2014-09-08 19:13:49 -04:00
return s_bProgressive ;
2012-10-18 04:18:40 -04:00
}
bool IsSkipIdle ()
{
2014-09-08 19:13:49 -04:00
return s_bSkipIdle ;
2012-10-18 04:18:40 -04:00
}
bool IsDSPHLE ()
{
2014-09-08 19:13:49 -04:00
return s_bDSPHLE ;
2012-10-18 04:18:40 -04:00
}
bool IsFastDiscSpeed ()
{
2014-09-08 19:13:49 -04:00
return s_bFastDiscSpeed ;
2012-10-18 04:18:40 -04:00
}
2014-07-07 05:30:06 +02:00
int GetCPUMode ()
2012-10-18 04:18:40 -04:00
{
2014-09-08 19:13:49 -04:00
return s_iCPUCore ;
2012-10-21 17:48:45 -04:00
}
2012-10-25 02:44:30 -04:00
bool IsStartingFromClearSave ()
2012-10-21 17:48:45 -04:00
{
2012-10-25 02:44:30 -04:00
return g_bClearSave ;
2012-10-21 17:48:45 -04:00
}
2014-07-02 21:45:59 -04:00
bool IsUsingMemcard ( int memcard )
2012-10-21 17:48:45 -04:00
{
2014-09-08 19:13:49 -04:00
return ( s_memcards & ( 1 << memcard )) != 0 ;
2012-10-18 04:18:40 -04:00
}
2013-06-20 06:08:17 -04:00
bool IsSyncGPU ()
{
2014-09-08 19:13:49 -04:00
return s_bSyncGPU ;
2013-06-20 06:08:17 -04:00
}
2012-10-18 04:18:40 -04:00
2013-09-03 15:50:41 -04:00
bool IsNetPlayRecording ()
{
2014-09-08 19:13:49 -04:00
return s_bNetPlay ;
2013-09-03 15:50:41 -04:00
}
2011-02-12 00:06:58 +00:00
void ChangePads ( bool instantly )
2010-09-06 21:41:01 +00:00
{
2014-07-08 00:47:57 +02:00
if ( ! Core :: IsRunning ())
2011-07-11 20:15:05 +00:00
return ;
int controllers = 0 ;
2014-09-08 20:15:47 -04:00
for ( int i = 0 ; i < MAX_SI_CHANNELS ; ++ i )
2012-12-18 23:19:15 -05:00
if ( SConfig :: GetInstance (). m_SIDevice [ i ] == SIDEVICE_GC_CONTROLLER || SConfig :: GetInstance (). m_SIDevice [ i ] == SIDEVICE_GC_TARUKONGA )
2011-07-11 20:15:05 +00:00
controllers |= ( 1 << i );
2014-09-08 19:13:49 -04:00
if ( instantly && ( s_numPads & 0x0F ) == controllers )
2011-07-11 20:15:05 +00:00
return ;
2014-09-08 20:15:47 -04:00
for ( int i = 0 ; i < MAX_SI_CHANNELS ; ++ i )
2011-07-11 20:15:05 +00:00
if ( instantly ) // Changes from savestates need to be instantaneous
2012-12-18 23:19:15 -05:00
SerialInterface :: AddDevice ( IsUsingPad ( i ) ? ( IsUsingBongo ( i ) ? SIDEVICE_GC_TARUKONGA : SIDEVICE_GC_CONTROLLER ) : SIDEVICE_NONE , i );
2011-07-11 20:15:05 +00:00
else
2012-12-18 23:19:15 -05:00
SerialInterface :: ChangeDevice ( IsUsingPad ( i ) ? ( IsUsingBongo ( i ) ? SIDEVICE_GC_TARUKONGA : SIDEVICE_GC_CONTROLLER ) : SIDEVICE_NONE , i );
2011-02-11 19:09:46 +00:00
}
2011-07-11 20:15:05 +00:00
void ChangeWiiPads ( bool instantly )
2011-02-11 19:09:46 +00:00
{
2011-07-11 20:15:05 +00:00
int controllers = 0 ;
2014-09-08 20:15:47 -04:00
for ( int i = 0 ; i < MAX_WIIMOTES ; ++ i )
2011-07-11 20:15:05 +00:00
if ( g_wiimote_sources [ i ] != WIIMOTE_SRC_NONE )
controllers |= ( 1 << i );
// This is important for Wiimotes, because they can desync easily if they get re-activated
2014-09-08 19:13:49 -04:00
if ( instantly && ( s_numPads >> 4 ) == controllers )
2011-07-11 20:15:05 +00:00
return ;
2014-09-08 20:15:47 -04:00
for ( int i = 0 ; i < MAX_WIIMOTES ; ++ i )
2011-02-11 19:09:46 +00:00
{
g_wiimote_sources [ i ] = IsUsingWiimote ( i ) ? WIIMOTE_SRC_EMU : WIIMOTE_SRC_NONE ;
GetUsbPointer () -> AccessWiiMote ( i | 0x100 ) -> Activate ( IsUsingWiimote ( i ));
2010-09-06 21:41:01 +00:00
}
}
2010-08-30 07:05:47 +00:00
bool BeginRecordingInput ( int controllers )
2010-06-09 01:37:08 +00:00
{
2014-09-08 19:13:49 -04:00
if ( s_playMode != MODE_NONE || controllers == 0 )
2010-06-09 01:37:08 +00:00
return false ;
2011-02-15 09:07:55 +00:00
2014-09-06 17:26:40 -04:00
bool was_unpaused = Core :: PauseAndLock ( true );
2014-09-08 19:13:49 -04:00
s_numPads = controllers ;
2012-10-18 04:03:12 -04:00
g_currentFrame = g_totalFrames = 0 ;
2014-09-11 01:55:43 -04:00
g_currentLagCount = s_totalLagCount = 0 ;
2012-10-18 04:03:12 -04:00
g_currentInputCount = g_totalInputCount = 0 ;
2014-09-08 19:13:49 -04:00
s_totalTickCount = s_tickCountAtLastInput = 0 ;
s_bongos = 0 ;
s_memcards = 0 ;
2013-09-03 15:50:41 -04:00
if ( NetPlay :: IsNetPlayRunning ())
{
2014-09-08 19:13:49 -04:00
s_bNetPlay = true ;
2015-02-02 02:08:58 -08:00
s_recordingStartTime = g_netplay_initial_gctime ;
2013-09-03 15:50:41 -04:00
}
else
2014-09-06 07:10:41 -04:00
{
2014-09-08 19:13:49 -04:00
s_recordingStartTime = Common :: Timer :: GetLocalTimeSinceJan1970 ();
2014-09-06 07:10:41 -04:00
}
2013-09-03 15:50:41 -04:00
2014-09-08 19:13:49 -04:00
s_rerecords = 0 ;
2012-10-18 04:03:12 -04:00
2014-09-06 07:10:41 -04:00
for ( int i = 0 ; i < MAX_SI_CHANNELS ; ++ i )
2012-12-18 23:19:15 -05:00
if ( SConfig :: GetInstance (). m_SIDevice [ i ] == SIDEVICE_GC_TARUKONGA )
2014-09-08 19:13:49 -04:00
s_bongos |= ( 1 << i );
2012-12-18 23:19:15 -05:00
2014-07-08 22:32:35 +02:00
if ( Core :: IsRunningAndStarted ())
2011-02-15 09:07:55 +00:00
{
2015-02-14 02:25:58 +01:00
const std :: string save_path = File :: GetUserPath ( D_STATESAVES_IDX ) + "dtm.sav" ;
if ( File :: Exists ( save_path ))
File :: Delete ( save_path );
2011-05-03 00:06:44 +00:00
2015-02-14 02:25:58 +01:00
State :: SaveAs ( save_path );
2014-09-08 19:13:49 -04:00
s_bRecordingFromSaveState = true ;
2012-10-25 02:44:30 -04:00
2012-10-26 12:06:32 -04:00
// This is only done here if starting from save state because otherwise we won't have the titleid. Otherwise it's set in WII_IPC_HLE_Device_es.cpp.
2012-10-25 02:44:30 -04:00
// TODO: find a way to GetTitleDataPath() from Movie::Init()
2014-09-09 00:24:49 -04:00
if ( SConfig :: GetInstance (). m_LocalCoreStartupParameter . bWii )
2012-11-23 22:23:58 -05:00
{
2014-03-12 15:33:41 -04:00
if ( File :: Exists ( Common :: GetTitleDataPath ( g_titleID ) + "banner.bin" ))
2012-11-23 22:23:58 -05:00
Movie :: g_bClearSave = false ;
else
Movie :: g_bClearSave = true ;
}
2012-11-26 12:29:36 -05:00
std :: thread md5thread ( GetMD5 );
2013-09-11 03:34:05 -04:00
md5thread . detach ();
2013-07-01 17:44:42 -04:00
GetSettings ();
2011-02-15 09:07:55 +00:00
}
2014-09-08 19:13:49 -04:00
s_playMode = MODE_RECORDING ;
s_author = SConfig :: GetInstance (). m_strMovieAuthor ;
2012-11-25 22:41:48 -05:00
EnsureTmpInputSize ( 1 );
2014-09-08 19:13:49 -04:00
s_currentByte = s_totalBytes = 0 ;
2012-10-18 04:18:40 -04:00
2014-09-06 17:26:40 -04:00
Core :: UpdateWantDeterminism ();
Core :: PauseAndLock ( false , was_unpaused );
2010-08-30 07:05:47 +00:00
Core :: DisplayMessage ( "Starting movie recording" , 2000 );
2010-06-09 01:37:08 +00:00
return true ;
}
2014-09-29 23:17:59 -04:00
static std :: string Analog2DToString ( u8 x , u8 y , const std :: string & prefix , u8 range = 255 )
2011-12-14 04:03:05 -08:00
{
2014-09-29 23:17:59 -04:00
u8 center = range / 2 + 1 ;
if (( x <= 1 || x == center || x >= range ) &&
( y <= 1 || y == center || y >= range ))
2011-12-14 04:03:05 -08:00
{
2014-09-29 23:17:59 -04:00
if ( x != center || y != center )
2011-12-14 04:03:05 -08:00
{
2014-09-29 23:17:59 -04:00
if ( x != center && y != center )
2011-12-14 04:03:05 -08:00
{
2014-09-29 23:17:59 -04:00
return StringFromFormat ( "%s:%s,%s" , prefix . c_str (), x < center ? "LEFT" : "RIGHT" , y < center ? "DOWN" : "UP" );
2011-12-14 04:03:05 -08:00
}
2014-09-29 23:17:59 -04:00
else if ( x != center )
2011-12-14 04:03:05 -08:00
{
2014-09-29 23:17:59 -04:00
return StringFromFormat ( "%s:%s" , prefix . c_str (), x < center ? "LEFT" : "RIGHT" );
2011-12-14 04:03:05 -08:00
}
else
{
2014-09-29 23:17:59 -04:00
return StringFromFormat ( "%s:%s" , prefix . c_str (), y < center ? "DOWN" : "UP" );
2011-12-14 04:03:05 -08:00
}
}
else
{
2014-07-02 22:42:36 -04:00
return "" ;
2011-12-14 04:03:05 -08:00
}
}
else
{
2014-07-02 22:42:36 -04:00
return StringFromFormat ( "%s:%d,%d" , prefix . c_str (), x , y );
2011-12-14 04:03:05 -08:00
}
}
2014-09-29 23:17:59 -04:00
static std :: string Analog1DToString ( u8 v , const std :: string & prefix , u8 range = 255 )
2011-12-14 04:03:05 -08:00
{
2014-03-11 00:30:55 +13:00
if ( v > 0 )
2011-12-14 04:03:05 -08:00
{
2014-09-29 23:17:59 -04:00
if ( v == range )
2011-12-14 04:03:05 -08:00
{
2014-07-02 22:42:36 -04:00
return prefix ;
2011-12-14 04:03:05 -08:00
}
else
{
2014-07-02 22:42:36 -04:00
return StringFromFormat ( "%s:%d" , prefix . c_str (), v );
2011-12-14 04:03:05 -08:00
}
}
else
{
2014-07-02 22:42:36 -04:00
return "" ;
2011-12-14 04:03:05 -08:00
}
}
2014-07-08 14:29:26 +02:00
static void SetInputDisplayString ( ControllerState padState , int controllerID )
2011-02-17 09:12:36 +00:00
{
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ] = StringFromFormat ( "P%d:" , controllerID + 1 );
2011-02-17 09:12:36 +00:00
2014-07-26 17:14:54 -04:00
if ( padState . A )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " A" );
2014-07-26 17:14:54 -04:00
if ( padState . B )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " B" );
2014-07-26 17:14:54 -04:00
if ( padState . X )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " X" );
2014-07-26 17:14:54 -04:00
if ( padState . Y )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " Y" );
2014-07-26 17:14:54 -04:00
if ( padState . Z )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " Z" );
2014-07-26 17:14:54 -04:00
if ( padState . Start )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " START" );
2011-02-17 09:12:36 +00:00
2014-07-26 17:14:54 -04:00
if ( padState . DPadUp )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " UP" );
2014-07-26 17:14:54 -04:00
if ( padState . DPadDown )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " DOWN" );
2014-07-26 17:14:54 -04:00
if ( padState . DPadLeft )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " LEFT" );
2014-07-26 17:14:54 -04:00
if ( padState . DPadRight )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " RIGHT" );
2014-11-02 01:58:59 -05:00
if ( padState . reset )
s_InputDisplay [ controllerID ]. append ( " RESET" );
2011-12-14 04:03:05 -08:00
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( Analog1DToString ( padState . TriggerL , " L" ));
s_InputDisplay [ controllerID ]. append ( Analog1DToString ( padState . TriggerR , " R" ));
s_InputDisplay [ controllerID ]. append ( Analog2DToString ( padState . AnalogStickX , padState . AnalogStickY , " ANA" ));
s_InputDisplay [ controllerID ]. append ( Analog2DToString ( padState . CStickX , padState . CStickY , " C" ));
s_InputDisplay [ controllerID ]. append ( " \n " );
2011-12-14 04:03:05 -08:00
}
2014-09-29 23:17:59 -04:00
static void SetWiiInputDisplayString ( int remoteID , u8 * const data , const WiimoteEmu :: ReportFeatures & rptf , int ext , const wiimote_key key )
2011-12-14 04:03:05 -08:00
{
int controllerID = remoteID + 4 ;
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ] = StringFromFormat ( "R%d:" , remoteID + 1 );
2011-12-14 04:03:05 -08:00
2014-09-29 23:17:59 -04:00
u8 * const coreData = rptf . core ? ( data + rptf . core ) : nullptr ;
u8 * const accelData = rptf . accel ? ( data + rptf . accel ) : nullptr ;
u8 * const irData = rptf . ir ? ( data + rptf . ir ) : nullptr ;
u8 * const extData = rptf . ext ? ( data + rptf . ext ) : nullptr ;
2014-03-11 00:30:55 +13:00
if ( coreData )
2011-12-14 04:03:05 -08:00
{
2014-10-24 21:59:16 -04:00
wm_buttons buttons = * ( wm_buttons * ) coreData ;
if ( buttons . left )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " LEFT" );
2014-10-24 21:59:16 -04:00
if ( buttons . right )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " RIGHT" );
2014-10-24 21:59:16 -04:00
if ( buttons . down )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " DOWN" );
2014-10-24 21:59:16 -04:00
if ( buttons . up )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " UP" );
2014-10-24 21:59:16 -04:00
if ( buttons . a )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " A" );
2014-10-24 21:59:16 -04:00
if ( buttons . b )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " B" );
2014-10-24 21:59:16 -04:00
if ( buttons . plus )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " +" );
2014-10-24 21:59:16 -04:00
if ( buttons . minus )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " -" );
2014-10-24 21:59:16 -04:00
if ( buttons . one )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " 1" );
2014-10-24 21:59:16 -04:00
if ( buttons . two )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " 2" );
2014-10-24 21:59:16 -04:00
if ( buttons . home )
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " HOME" );
2011-02-17 09:12:36 +00:00
}
2014-03-11 00:30:55 +13:00
if ( accelData )
2011-02-17 09:12:36 +00:00
{
2011-12-14 04:03:05 -08:00
wm_accel * dt = ( wm_accel * ) accelData ;
2014-10-30 15:15:36 -04:00
std :: string accel = StringFromFormat ( " ACC:%d,%d,%d" ,
dt -> x << 2 | (( wm_buttons * ) coreData ) -> acc_x_lsb , dt -> y << 2 | (( wm_buttons * ) coreData ) -> acc_y_lsb << 1 , dt -> z << 2 | (( wm_buttons * ) coreData ) -> acc_z_lsb << 1 );
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( accel );
2011-02-17 09:12:36 +00:00
}
2011-12-14 04:03:05 -08:00
2014-09-30 08:24:18 -04:00
if ( irData )
2011-02-17 09:12:36 +00:00
{
2014-09-30 08:24:18 -04:00
u16 x = irData [ 0 ] | (( irData [ 2 ] >> 4 & 0x3 ) << 8 );
2014-09-14 23:42:32 -04:00
u16 y = irData [ 1 ] | (( irData [ 2 ] >> 6 & 0x3 ) << 8 );
std :: string ir = StringFromFormat ( " IR:%d,%d" , x , y );
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( ir );
2011-02-17 09:12:36 +00:00
}
2014-10-31 04:34:30 -04:00
// Nunchuk
2014-09-29 23:17:59 -04:00
if ( extData && ext == 1 )
{
2014-10-31 04:34:30 -04:00
wm_nc nunchuk ;
memcpy ( & nunchuk , extData , sizeof ( wm_nc ));
WiimoteDecrypt ( & key , ( u8 * ) & nunchuk , 0 , sizeof ( wm_nc ));
nunchuk . bt . hex = nunchuk . bt . hex ^ 0x3 ;
2014-09-29 23:17:59 -04:00
2014-10-31 04:34:30 -04:00
std :: string accel = StringFromFormat ( " N-ACC:%d,%d,%d" ,
( nunchuk . ax << 2 ) | nunchuk . bt . acc_x_lsb , ( nunchuk . ay << 2 ) | nunchuk . bt . acc_y_lsb , ( nunchuk . az << 2 ) | nunchuk . bt . acc_z_lsb );
2014-09-29 23:17:59 -04:00
2014-10-31 04:34:30 -04:00
if ( nunchuk . bt . c )
2014-09-29 23:17:59 -04:00
s_InputDisplay [ controllerID ]. append ( " C" );
2014-10-31 04:34:30 -04:00
if ( nunchuk . bt . z )
2014-09-29 23:17:59 -04:00
s_InputDisplay [ controllerID ]. append ( " Z" );
s_InputDisplay [ controllerID ]. append ( accel );
2014-10-31 04:34:30 -04:00
s_InputDisplay [ controllerID ]. append ( Analog2DToString ( nunchuk . jx , nunchuk . jy , " ANA" ));
2014-09-29 23:17:59 -04:00
}
// Classic controller
if ( extData && ext == 2 )
{
wm_classic_extension cc ;
memcpy ( & cc , extData , sizeof ( wm_classic_extension ));
WiimoteDecrypt ( & key , ( u8 * ) & cc , 0 , sizeof ( wm_classic_extension ));
2014-10-24 21:59:16 -04:00
cc . bt . hex = cc . bt . hex ^ 0xFFFF ;
2014-09-29 23:17:59 -04:00
2014-10-24 21:59:16 -04:00
if ( cc . bt . regular_data . dpad_left )
2014-09-29 23:17:59 -04:00
s_InputDisplay [ controllerID ]. append ( " LEFT" );
2014-10-24 21:59:16 -04:00
if ( cc . bt . dpad_right )
2014-09-29 23:17:59 -04:00
s_InputDisplay [ controllerID ]. append ( " RIGHT" );
2014-10-24 21:59:16 -04:00
if ( cc . bt . dpad_down )
2014-09-29 23:17:59 -04:00
s_InputDisplay [ controllerID ]. append ( " DOWN" );
2014-10-24 21:59:16 -04:00
if ( cc . bt . regular_data . dpad_up )
2014-09-29 23:17:59 -04:00
s_InputDisplay [ controllerID ]. append ( " UP" );
2014-10-24 21:59:16 -04:00
if ( cc . bt . a )
2014-09-29 23:17:59 -04:00
s_InputDisplay [ controllerID ]. append ( " A" );
2014-10-24 21:59:16 -04:00
if ( cc . bt . b )
2014-09-29 23:17:59 -04:00
s_InputDisplay [ controllerID ]. append ( " B" );
2014-10-24 21:59:16 -04:00
if ( cc . bt . x )
2014-09-29 23:17:59 -04:00
s_InputDisplay [ controllerID ]. append ( " X" );
2014-10-24 21:59:16 -04:00
if ( cc . bt . y )
2014-09-29 23:17:59 -04:00
s_InputDisplay [ controllerID ]. append ( " Y" );
2014-10-24 21:59:16 -04:00
if ( cc . bt . zl )
2014-09-29 23:17:59 -04:00
s_InputDisplay [ controllerID ]. append ( " ZL" );
2014-10-24 21:59:16 -04:00
if ( cc . bt . zr )
2014-09-29 23:17:59 -04:00
s_InputDisplay [ controllerID ]. append ( " ZR" );
2014-10-24 21:59:16 -04:00
if ( cc . bt . plus )
2014-09-29 23:17:59 -04:00
s_InputDisplay [ controllerID ]. append ( " +" );
2014-10-24 21:59:16 -04:00
if ( cc . bt . minus )
2014-09-29 23:17:59 -04:00
s_InputDisplay [ controllerID ]. append ( " -" );
2014-10-24 21:59:16 -04:00
if ( cc . bt . home )
2014-09-29 23:17:59 -04:00
s_InputDisplay [ controllerID ]. append ( " HOME" );
s_InputDisplay [ controllerID ]. append ( Analog1DToString ( cc . lt1 | ( cc . lt2 << 3 ), " L" , 31 ));
s_InputDisplay [ controllerID ]. append ( Analog1DToString ( cc . rt , " R" , 31 ));
2014-10-24 21:59:16 -04:00
s_InputDisplay [ controllerID ]. append ( Analog2DToString ( cc . regular_data . lx , cc . regular_data . ly , " ANA" , 63 ));
2014-09-29 23:17:59 -04:00
s_InputDisplay [ controllerID ]. append ( Analog2DToString ( cc . rx1 | ( cc . rx2 << 1 ) | ( cc . rx3 << 3 ), cc . ry , " R-ANA" , 31 ));
}
2014-09-08 19:13:49 -04:00
s_InputDisplay [ controllerID ]. append ( " \n " );
2011-02-17 09:12:36 +00:00
}
2014-07-10 22:02:32 -04:00
void CheckPadStatus ( GCPadStatus * PadStatus , int controllerID )
2010-06-09 01:37:08 +00:00
{
2014-09-08 19:13:49 -04:00
s_padState . A = (( PadStatus -> button & PAD_BUTTON_A ) != 0 );
s_padState . B = (( PadStatus -> button & PAD_BUTTON_B ) != 0 );
s_padState . X = (( PadStatus -> button & PAD_BUTTON_X ) != 0 );
s_padState . Y = (( PadStatus -> button & PAD_BUTTON_Y ) != 0 );
s_padState . Z = (( PadStatus -> button & PAD_TRIGGER_Z ) != 0 );
s_padState . Start = (( PadStatus -> button & PAD_BUTTON_START ) != 0 );
2012-12-10 06:20:06 -05:00
2014-09-08 19:13:49 -04:00
s_padState . DPadUp = (( PadStatus -> button & PAD_BUTTON_UP ) != 0 );
s_padState . DPadDown = (( PadStatus -> button & PAD_BUTTON_DOWN ) != 0 );
s_padState . DPadLeft = (( PadStatus -> button & PAD_BUTTON_LEFT ) != 0 );
s_padState . DPadRight = (( PadStatus -> button & PAD_BUTTON_RIGHT ) != 0 );
2012-12-10 06:20:06 -05:00
2014-09-08 20:15:47 -04:00
s_padState . L = (( PadStatus -> button & PAD_TRIGGER_L ) != 0 );
s_padState . R = (( PadStatus -> button & PAD_TRIGGER_R ) != 0 );
s_padState . TriggerL = PadStatus -> triggerLeft ;
s_padState . TriggerR = PadStatus -> triggerRight ;
2012-10-20 14:18:42 -04:00
2014-09-08 19:13:49 -04:00
s_padState . AnalogStickX = PadStatus -> stickX ;
s_padState . AnalogStickY = PadStatus -> stickY ;
2012-12-10 06:20:06 -05:00
2014-09-08 20:15:47 -04:00
s_padState . CStickX = PadStatus -> substickX ;
s_padState . CStickY = PadStatus -> substickY ;
2011-12-15 09:22:16 -08:00
2014-11-02 01:58:59 -05:00
s_padState . disc = g_bDiscChange ;
g_bDiscChange = false ;
s_padState . reset = g_bReset ;
g_bReset = false ;
2014-09-08 19:13:49 -04:00
SetInputDisplayString ( s_padState , controllerID );
2012-12-10 06:20:06 -05:00
}
2014-07-10 22:02:32 -04:00
void RecordInput ( GCPadStatus * PadStatus , int controllerID )
2012-12-10 06:20:06 -05:00
{
if ( ! IsRecordingInput () || ! IsUsingPad ( controllerID ))
return ;
CheckPadStatus ( PadStatus , controllerID );
2014-09-08 19:13:49 -04:00
EnsureTmpInputSize (( size_t )( s_currentByte + 8 ));
memcpy ( & ( tmpInput [ s_currentByte ]), & s_padState , 8 );
s_currentByte += 8 ;
s_totalBytes = s_currentByte ;
2010-06-09 01:37:08 +00:00
}
2014-09-29 23:17:59 -04:00
void CheckWiimoteStatus ( int wiimote , u8 * data , const WiimoteEmu :: ReportFeatures & rptf , int ext , const wiimote_key key )
2011-02-11 18:53:51 +00:00
{
2014-09-29 23:17:59 -04:00
SetWiiInputDisplayString ( wiimote , data , rptf , ext , key );
2012-12-10 06:20:06 -05:00
if ( IsRecordingInput ())
2014-10-31 04:34:30 -04:00
RecordWiimote ( wiimote , data , rptf . size );
2012-12-10 06:20:06 -05:00
}
void RecordWiimote ( int wiimote , u8 * data , u8 size )
{
2014-03-11 00:30:55 +13:00
if ( ! IsRecordingInput () || ! IsUsingWiimote ( wiimote ))
2012-12-10 06:20:06 -05:00
return ;
2011-12-15 09:22:16 -08:00
2011-12-14 04:03:05 -08:00
InputUpdate ();
2014-09-08 19:13:49 -04:00
EnsureTmpInputSize (( size_t )( s_currentByte + size + 1 ));
tmpInput [ s_currentByte ++ ] = size ;
memcpy ( & ( tmpInput [ s_currentByte ]), data , size );
s_currentByte += size ;
s_totalBytes = s_currentByte ;
2011-02-11 18:53:51 +00:00
}
2012-10-20 14:18:42 -04:00
void ReadHeader ()
{
2014-09-08 19:13:49 -04:00
s_numPads = tmpHeader . numControllers ;
s_recordingStartTime = tmpHeader . recordingStartTime ;
if ( s_rerecords < tmpHeader . numRerecords )
s_rerecords = tmpHeader . numRerecords ;
2012-10-20 14:18:42 -04:00
2012-10-20 19:54:38 -04:00
if ( tmpHeader . bSaveConfig )
{
2014-09-08 19:13:49 -04:00
s_bSaveConfig = true ;
s_bSkipIdle = tmpHeader . bSkipIdle ;
s_bDualCore = tmpHeader . bDualCore ;
s_bProgressive = tmpHeader . bProgressive ;
s_bDSPHLE = tmpHeader . bDSPHLE ;
s_bFastDiscSpeed = tmpHeader . bFastDiscSpeed ;
s_iCPUCore = tmpHeader . CPUCore ;
2012-10-25 02:44:30 -04:00
g_bClearSave = tmpHeader . bClearSave ;
2014-09-08 19:13:49 -04:00
s_memcards = tmpHeader . memcards ;
s_bongos = tmpHeader . bongos ;
s_bSyncGPU = tmpHeader . bSyncGPU ;
s_bNetPlay = tmpHeader . bNetPlay ;
memcpy ( s_revision , tmpHeader . revision , ArraySize ( s_revision ));
2012-10-20 19:54:38 -04:00
}
else
{
2012-10-24 16:35:52 -04:00
GetSettings ();
2012-10-20 19:54:38 -04:00
}
2012-10-20 14:18:42 -04:00
2014-09-08 19:13:49 -04:00
s_videoBackend = ( char * ) tmpHeader . videoBackend ;
2013-07-01 17:44:42 -04:00
g_discChange = ( char * ) tmpHeader . discChange ;
2014-09-08 19:13:49 -04:00
s_author = ( char * ) tmpHeader . author ;
memcpy ( s_MD5 , tmpHeader . md5 , 16 );
s_DSPiromHash = tmpHeader . DSPiromHash ;
s_DSPcoefHash = tmpHeader . DSPcoefHash ;
2012-10-20 14:18:42 -04:00
}
2014-03-12 15:33:41 -04:00
bool PlayInput ( const std :: string & filename )
2010-06-09 01:37:08 +00:00
{
2014-09-08 19:13:49 -04:00
if ( s_playMode != MODE_NONE )
2010-06-09 01:37:08 +00:00
return false ;
2011-05-03 00:06:44 +00:00
2014-03-11 00:30:55 +13:00
if ( ! File :: Exists ( filename ))
2010-06-09 01:37:08 +00:00
return false ;
2012-10-21 17:48:45 -04:00
2011-05-03 00:06:44 +00:00
File :: IOFile g_recordfd ;
2012-10-21 17:48:45 -04:00
2011-05-03 00:06:44 +00:00
if ( ! g_recordfd . Open ( filename , "rb" ))
2010-06-09 01:37:08 +00:00
return false ;
2012-10-21 17:48:45 -04:00
2011-05-03 00:06:44 +00:00
g_recordfd . ReadArray ( & tmpHeader , 1 );
2013-10-29 01:23:17 -04:00
2014-08-30 18:53:18 -04:00
if ( ! IsMovieHeader ( tmpHeader . filetype ))
2014-08-30 17:23:13 -04:00
{
2011-01-13 02:05:58 +00:00
PanicAlertT ( "Invalid recording file" );
2014-09-08 20:15:47 -04:00
g_recordfd . Close ();
return false ;
2010-06-09 01:37:08 +00:00
}
2012-10-21 17:48:45 -04:00
2012-10-20 14:18:42 -04:00
ReadHeader ();
2011-12-14 04:03:05 -08:00
g_totalFrames = tmpHeader . frameCount ;
2014-09-11 01:55:43 -04:00
s_totalLagCount = tmpHeader . lagCount ;
2011-12-14 04:03:05 -08:00
g_totalInputCount = tmpHeader . inputCount ;
2014-09-08 19:13:49 -04:00
s_totalTickCount = tmpHeader . tickCount ;
2011-12-15 09:22:16 -08:00
g_currentFrame = 0 ;
g_currentLagCount = 0 ;
g_currentInputCount = 0 ;
2014-09-08 19:13:49 -04:00
s_playMode = MODE_PLAYING ;
2013-10-29 01:23:17 -04:00
2014-09-06 17:26:40 -04:00
Core :: UpdateWantDeterminism ();
2014-09-08 19:13:49 -04:00
s_totalBytes = g_recordfd . GetSize () - 256 ;
EnsureTmpInputSize (( size_t ) s_totalBytes );
g_recordfd . ReadArray ( tmpInput , ( size_t ) s_totalBytes );
s_currentByte = 0 ;
2011-05-03 00:06:44 +00:00
g_recordfd . Close ();
2012-10-21 17:48:45 -04:00
2013-01-19 15:02:02 -05:00
// Load savestate (and skip to frame data)
2014-03-11 00:30:55 +13:00
if ( tmpHeader . bFromSaveState )
2013-01-19 15:02:02 -05:00
{
2014-03-12 15:33:41 -04:00
const std :: string stateFilename = filename + ".sav" ;
2014-03-11 00:30:55 +13:00
if ( File :: Exists ( stateFilename ))
2013-01-19 15:02:02 -05:00
Core :: SetStateFileName ( stateFilename );
2014-09-08 19:13:49 -04:00
s_bRecordingFromSaveState = true ;
2013-01-19 15:02:02 -05:00
Movie :: LoadInput ( filename );
}
2010-06-09 01:37:08 +00:00
return true ;
}
2011-12-28 02:33:41 -06:00
void DoState ( PointerWrap & p )
2011-12-14 04:03:05 -08:00
{
// many of these could be useful to save even when no movie is active,
// and the data is tiny, so let's just save it regardless of movie state.
p . Do ( g_currentFrame );
2014-09-08 19:13:49 -04:00
p . Do ( s_currentByte );
2011-12-14 04:03:05 -08:00
p . Do ( g_currentLagCount );
p . Do ( g_currentInputCount );
2014-09-08 19:13:49 -04:00
p . Do ( s_bPolled );
p . Do ( s_tickCountAtLastInput );
// other variables (such as s_totalBytes and g_totalFrames) are set in LoadInput
2011-12-14 04:03:05 -08:00
}
2014-03-12 15:33:41 -04:00
void LoadInput ( const std :: string & filename )
2010-08-30 07:05:47 +00:00
{
2013-01-19 15:02:02 -05:00
File :: IOFile t_record ;
if ( ! t_record . Open ( filename , "r+b" ))
{
2014-03-12 15:33:41 -04:00
PanicAlertT ( "Failed to read %s" , filename . c_str ());
2013-01-19 15:02:02 -05:00
EndPlayInput ( false );
return ;
}
2012-10-20 14:18:42 -04:00
2011-05-03 00:06:44 +00:00
t_record . ReadArray ( & tmpHeader , 1 );
2012-10-21 17:48:45 -04:00
2014-08-30 18:53:18 -04:00
if ( ! IsMovieHeader ( tmpHeader . filetype ))
2011-02-15 09:07:55 +00:00
{
2014-03-12 15:33:41 -04:00
PanicAlertT ( "Savestate movie %s is corrupted, movie recording stopping..." , filename . c_str ());
2011-02-15 17:03:20 +00:00
EndPlayInput ( false );
2010-08-30 07:05:47 +00:00
return ;
}
2012-10-20 14:18:42 -04:00
ReadHeader ();
2014-09-08 19:13:49 -04:00
if ( ! s_bReadOnly )
2011-12-17 23:27:11 -08:00
{
2014-09-08 19:13:49 -04:00
s_rerecords ++ ;
tmpHeader . numRerecords = s_rerecords ;
2011-12-17 23:27:11 -08:00
t_record . Seek ( 0 , SEEK_SET );
t_record . WriteArray ( & tmpHeader , 1 );
}
2012-10-20 14:18:42 -04:00
2011-02-12 00:06:58 +00:00
ChangePads ( true );
2014-09-09 00:24:49 -04:00
if ( SConfig :: GetInstance (). m_LocalCoreStartupParameter . bWii )
2011-07-11 20:15:05 +00:00
ChangeWiiPads ( true );
2011-05-03 00:06:44 +00:00
2011-12-17 23:27:11 -08:00
u64 totalSavedBytes = t_record . GetSize () - 256 ;
bool afterEnd = false ;
2014-07-05 19:48:44 -04:00
// This can only happen if the user manually deletes data from the dtm.
2014-09-08 19:13:49 -04:00
if ( s_currentByte > totalSavedBytes )
2011-12-17 23:27:11 -08:00
{
2014-09-08 19:13:49 -04:00
PanicAlertT ( "Warning: You loaded a save whose movie ends before the current frame in the save (byte %u < %u) (frame %u < %u). You should load another save before continuing." , ( u32 ) totalSavedBytes + 256 , ( u32 ) s_currentByte + 256 , ( u32 ) tmpHeader . frameCount , ( u32 ) g_currentFrame );
2011-12-17 23:27:11 -08:00
afterEnd = true ;
}
2014-09-08 19:13:49 -04:00
if ( ! s_bReadOnly || tmpInput == nullptr )
2011-12-14 04:03:05 -08:00
{
g_totalFrames = tmpHeader . frameCount ;
2014-09-11 01:55:43 -04:00
s_totalLagCount = tmpHeader . lagCount ;
2011-12-14 04:03:05 -08:00
g_totalInputCount = tmpHeader . inputCount ;
2014-09-08 19:13:49 -04:00
s_totalTickCount = s_tickCountAtLastInput = tmpHeader . tickCount ;
2011-12-14 04:03:05 -08:00
2012-11-25 22:41:48 -05:00
EnsureTmpInputSize (( size_t ) totalSavedBytes );
2014-09-08 19:13:49 -04:00
s_totalBytes = totalSavedBytes ;
t_record . ReadArray ( tmpInput , ( size_t ) s_totalBytes );
2011-12-14 04:03:05 -08:00
}
2014-09-08 19:13:49 -04:00
else if ( s_currentByte > 0 )
2011-12-14 04:03:05 -08:00
{
2014-09-08 19:13:49 -04:00
if ( s_currentByte > totalSavedBytes )
2011-12-14 04:03:05 -08:00
{
}
2014-09-08 19:13:49 -04:00
else if ( s_currentByte > s_totalBytes )
2011-12-14 04:03:05 -08:00
{
2014-07-05 19:48:44 -04:00
afterEnd = true ;
2014-09-08 19:13:49 -04:00
PanicAlertT ( "Warning: You loaded a save that's after the end of the current movie. (byte %u > %u) (frame %u > %u). You should load another save before continuing, or load this state with read-only mode off." , ( u32 ) s_currentByte + 256 , ( u32 ) s_totalBytes + 256 , ( u32 ) g_currentFrame , ( u32 ) g_totalFrames );
2011-12-17 23:27:11 -08:00
}
2014-09-08 19:13:49 -04:00
else if ( s_currentByte > 0 && s_totalBytes > 0 )
2011-12-17 23:27:11 -08:00
{
2011-12-26 05:09:30 -06:00
// verify identical from movie start to the save's current frame
2014-09-08 19:13:49 -04:00
u32 len = ( u32 ) s_currentByte ;
2011-12-17 23:27:11 -08:00
u8 * movInput = new u8 [ len ];
t_record . ReadArray ( movInput , ( size_t ) len );
2011-12-14 04:03:05 -08:00
for ( u32 i = 0 ; i < len ; ++ i )
{
2011-12-17 23:27:11 -08:00
if ( movInput [ i ] != tmpInput [ i ])
2011-12-14 04:03:05 -08:00
{
2011-12-17 23:27:11 -08:00
// this is a "you did something wrong" alert for the user's benefit.
// we'll try to say what's going on in excruciating detail, otherwise the user might not believe us.
2014-03-11 00:30:55 +13:00
if ( IsUsingWiimote ( 0 ))
2013-10-29 01:23:17 -04:00
{
2011-12-17 23:27:11 -08:00
// TODO: more detail
PanicAlertT ( "Warning: You loaded a save whose movie mismatches on byte %d (0x%X). You should load another save before continuing, or load this state with read-only mode off. Otherwise you'll probably get a desync." , i + 256 , i + 256 );
2014-09-08 19:13:49 -04:00
memcpy ( tmpInput , movInput , s_currentByte );
2011-12-17 23:27:11 -08:00
}
2011-12-14 04:03:05 -08:00
else
2011-12-17 23:27:11 -08:00
{
2014-09-08 20:15:47 -04:00
int frame = i / 8 ;
2011-12-17 23:27:11 -08:00
ControllerState curPadState ;
memcpy ( & curPadState , & ( tmpInput [ frame * 8 ]), 8 );
ControllerState movPadState ;
memcpy ( & movPadState , & ( movInput [ frame * 8 ]), 8 );
PanicAlertT ( "Warning: You loaded a save whose movie mismatches on frame %d. You should load another save before continuing, or load this state with read-only mode off. Otherwise you'll probably get a desync. \n\n "
"More information: The current movie is %d frames long and the savestate's movie is %d frames long. \n\n "
"On frame %d, the current movie presses: \n "
"Start=%d, A=%d, B=%d, X=%d, Y=%d, Z=%d, DUp=%d, DDown=%d, DLeft=%d, DRight=%d, L=%d, R=%d, LT=%d, RT=%d, AnalogX=%d, AnalogY=%d, CX=%d, CY=%d"
" \n\n "
"On frame %d, the savestate's movie presses: \n "
"Start=%d, A=%d, B=%d, X=%d, Y=%d, Z=%d, DUp=%d, DDown=%d, DLeft=%d, DRight=%d, L=%d, R=%d, LT=%d, RT=%d, AnalogX=%d, AnalogY=%d, CX=%d, CY=%d" ,
( int ) frame ,
( int ) g_totalFrames , ( int ) tmpHeader . frameCount ,
( int ) frame ,
( int ) curPadState . Start , ( int ) curPadState . A , ( int ) curPadState . B , ( int ) curPadState . X , ( int ) curPadState . Y , ( int ) curPadState . Z , ( int ) curPadState . DPadUp , ( int ) curPadState . DPadDown , ( int ) curPadState . DPadLeft , ( int ) curPadState . DPadRight , ( int ) curPadState . L , ( int ) curPadState . R , ( int ) curPadState . TriggerL , ( int ) curPadState . TriggerR , ( int ) curPadState . AnalogStickX , ( int ) curPadState . AnalogStickY , ( int ) curPadState . CStickX , ( int ) curPadState . CStickY ,
( int ) frame ,
( int ) movPadState . Start , ( int ) movPadState . A , ( int ) movPadState . B , ( int ) movPadState . X , ( int ) movPadState . Y , ( int ) movPadState . Z , ( int ) movPadState . DPadUp , ( int ) movPadState . DPadDown , ( int ) movPadState . DPadLeft , ( int ) movPadState . DPadRight , ( int ) movPadState . L , ( int ) movPadState . R , ( int ) movPadState . TriggerL , ( int ) movPadState . TriggerR , ( int ) movPadState . AnalogStickX , ( int ) movPadState . AnalogStickY , ( int ) movPadState . CStickX , ( int ) movPadState . CStickY );
2013-06-21 01:53:50 -04:00
2011-12-17 23:27:11 -08:00
}
2011-12-14 04:03:05 -08:00
break ;
}
}
2012-03-24 19:41:13 +02:00
delete [] movInput ;
2011-12-14 04:03:05 -08:00
}
}
2011-05-03 00:06:44 +00:00
t_record . Close ();
2011-12-14 04:03:05 -08:00
2014-09-08 19:13:49 -04:00
s_bSaveConfig = tmpHeader . bSaveConfig ;
2011-07-11 20:15:05 +00:00
2011-12-17 23:27:11 -08:00
if ( ! afterEnd )
2011-07-11 20:15:05 +00:00
{
2014-09-08 19:13:49 -04:00
if ( s_bReadOnly )
2011-12-14 04:03:05 -08:00
{
2014-09-08 19:13:49 -04:00
if ( s_playMode != MODE_PLAYING )
2011-12-17 23:27:11 -08:00
{
2014-09-08 19:13:49 -04:00
s_playMode = MODE_PLAYING ;
2011-12-17 23:27:11 -08:00
Core :: DisplayMessage ( "Switched to playback" , 2000 );
}
}
else
{
2014-09-08 19:13:49 -04:00
if ( s_playMode != MODE_RECORDING )
2011-12-17 23:27:11 -08:00
{
2014-09-08 19:13:49 -04:00
s_playMode = MODE_RECORDING ;
2011-12-17 23:27:11 -08:00
Core :: DisplayMessage ( "Switched to recording" , 2000 );
}
2011-12-14 04:03:05 -08:00
}
2011-07-11 20:15:05 +00:00
}
else
{
2011-12-17 23:27:11 -08:00
EndPlayInput ( false );
2011-12-14 04:03:05 -08:00
}
}
static void CheckInputEnd ()
{
2014-09-08 19:13:49 -04:00
if ( g_currentFrame > g_totalFrames || s_currentByte >= s_totalBytes || ( CoreTiming :: GetTicks () > s_totalTickCount && ! IsRecordingInputFromSaveState ()))
2011-12-14 04:03:05 -08:00
{
2014-09-08 19:13:49 -04:00
EndPlayInput ( ! s_bReadOnly );
2011-07-11 20:15:05 +00:00
}
2010-08-30 07:05:47 +00:00
}
2014-07-10 22:02:32 -04:00
void PlayController ( GCPadStatus * PadStatus , int controllerID )
2010-06-09 01:37:08 +00:00
{
2012-10-21 17:48:45 -04:00
// Correct playback is entirely dependent on the emulator polling the controllers
// in the same order done during recording
2014-03-09 21:14:26 +01:00
if ( ! IsPlayingInput () || ! IsUsingPad ( controllerID ) || tmpInput == nullptr )
2012-10-21 17:48:45 -04:00
return ;
2014-09-08 19:13:49 -04:00
if ( s_currentByte + 8 > s_totalBytes )
2011-03-11 10:21:46 +00:00
{
2014-09-08 19:13:49 -04:00
PanicAlertT ( "Premature movie end in PlayController. %u + 8 > %u" , ( u32 ) s_currentByte , ( u32 ) s_totalBytes );
EndPlayInput ( ! s_bReadOnly );
2011-05-03 00:06:44 +00:00
return ;
2011-03-11 10:21:46 +00:00
}
2011-06-24 06:50:50 +00:00
2012-10-26 12:06:32 -04:00
// dtm files don't save the mic button or error bit. not sure if they're actually used, but better safe than sorry
2011-06-24 06:50:50 +00:00
signed char e = PadStatus -> err ;
2014-07-10 22:02:32 -04:00
memset ( PadStatus , 0 , sizeof ( GCPadStatus ));
2011-06-24 06:50:50 +00:00
PadStatus -> err = e ;
2011-12-15 09:22:16 -08:00
2014-09-08 19:13:49 -04:00
memcpy ( & s_padState , & ( tmpInput [ s_currentByte ]), 8 );
s_currentByte += 8 ;
2013-10-29 01:23:17 -04:00
2014-09-08 19:13:49 -04:00
PadStatus -> triggerLeft = s_padState . TriggerL ;
PadStatus -> triggerRight = s_padState . TriggerR ;
2011-02-17 09:12:36 +00:00
2014-09-08 19:13:49 -04:00
PadStatus -> stickX = s_padState . AnalogStickX ;
PadStatus -> stickY = s_padState . AnalogStickY ;
2011-02-17 09:12:36 +00:00
2014-09-08 19:13:49 -04:00
PadStatus -> substickX = s_padState . CStickX ;
PadStatus -> substickY = s_padState . CStickY ;
2011-02-17 09:12:36 +00:00
2010-06-09 01:37:08 +00:00
PadStatus -> button |= PAD_USE_ORIGIN ;
2013-10-29 01:23:17 -04:00
2014-09-08 19:13:49 -04:00
if ( s_padState . A )
2011-02-17 09:12:36 +00:00
{
2010-06-09 01:37:08 +00:00
PadStatus -> button |= PAD_BUTTON_A ;
PadStatus -> analogA = 0xFF ;
}
2014-09-08 19:13:49 -04:00
if ( s_padState . B )
2011-02-17 09:12:36 +00:00
{
2010-06-09 01:37:08 +00:00
PadStatus -> button |= PAD_BUTTON_B ;
PadStatus -> analogB = 0xFF ;
}
2014-09-08 19:13:49 -04:00
if ( s_padState . X )
2010-06-09 01:37:08 +00:00
PadStatus -> button |= PAD_BUTTON_X ;
2014-09-08 19:13:49 -04:00
if ( s_padState . Y )
2010-06-09 01:37:08 +00:00
PadStatus -> button |= PAD_BUTTON_Y ;
2014-09-08 19:13:49 -04:00
if ( s_padState . Z )
2010-06-09 01:37:08 +00:00
PadStatus -> button |= PAD_TRIGGER_Z ;
2014-09-08 19:13:49 -04:00
if ( s_padState . Start )
2010-06-09 01:37:08 +00:00
PadStatus -> button |= PAD_BUTTON_START ;
2013-10-29 01:23:17 -04:00
2014-09-08 19:13:49 -04:00
if ( s_padState . DPadUp )
2010-06-09 01:37:08 +00:00
PadStatus -> button |= PAD_BUTTON_UP ;
2014-09-08 19:13:49 -04:00
if ( s_padState . DPadDown )
2010-06-09 01:37:08 +00:00
PadStatus -> button |= PAD_BUTTON_DOWN ;
2014-09-08 19:13:49 -04:00
if ( s_padState . DPadLeft )
2010-06-09 01:37:08 +00:00
PadStatus -> button |= PAD_BUTTON_LEFT ;
2014-09-08 19:13:49 -04:00
if ( s_padState . DPadRight )
2010-06-09 01:37:08 +00:00
PadStatus -> button |= PAD_BUTTON_RIGHT ;
2013-10-29 01:23:17 -04:00
2014-09-08 19:13:49 -04:00
if ( s_padState . L )
2010-06-09 01:37:08 +00:00
PadStatus -> button |= PAD_TRIGGER_L ;
2014-09-08 19:13:49 -04:00
if ( s_padState . R )
2010-06-09 01:37:08 +00:00
PadStatus -> button |= PAD_TRIGGER_R ;
2014-09-08 19:13:49 -04:00
if ( s_padState . disc )
2012-10-20 14:18:42 -04:00
{
// This implementation assumes the disc change will only happen once. Trying to change more than that will cause
// it to load the last disc every time. As far as i know though, there are no 3+ disc games, so this should be fine.
Core :: SetState ( Core :: CORE_PAUSE );
bool found = false ;
std :: string path ;
2014-09-11 01:55:43 -04:00
for ( size_t i = 0 ; i < SConfig :: GetInstance (). m_ISOFolder . size (); ++ i )
2012-10-20 14:18:42 -04:00
{
path = SConfig :: GetInstance (). m_ISOFolder [ i ];
2014-03-12 15:33:41 -04:00
if ( File :: Exists ( path + '/' + g_discChange ))
2012-10-20 14:18:42 -04:00
{
found = true ;
break ;
}
}
if ( found )
{
2014-03-12 15:33:41 -04:00
DVDInterface :: ChangeDisc ( path + '/' + g_discChange );
2012-10-20 14:18:42 -04:00
Core :: SetState ( Core :: CORE_RUN );
}
else
{
2014-09-08 20:15:47 -04:00
PanicAlertT ( "Change the disc to %s" , g_discChange . c_str ());
2012-10-20 14:18:42 -04:00
}
}
2011-02-17 09:12:36 +00:00
2014-11-02 01:58:59 -05:00
if ( s_padState . reset )
ProcessorInterface :: ResetButton_Tap ();
2014-09-08 19:13:49 -04:00
SetInputDisplayString ( s_padState , controllerID );
2011-12-14 04:03:05 -08:00
CheckInputEnd ();
2010-06-09 01:37:08 +00:00
}
2014-09-29 23:17:59 -04:00
bool PlayWiimote ( int wiimote , u8 * data , const WiimoteEmu :: ReportFeatures & rptf , int ext , const wiimote_key key )
2011-02-11 18:53:51 +00:00
{
2014-03-11 00:30:55 +13:00
if ( ! IsPlayingInput () || ! IsUsingWiimote ( wiimote ) || tmpInput == nullptr )
2011-02-12 08:25:09 +00:00
return false ;
2011-12-15 09:22:16 -08:00
2014-09-08 19:13:49 -04:00
if ( s_currentByte > s_totalBytes )
2011-05-03 00:06:44 +00:00
{
2014-09-08 19:13:49 -04:00
PanicAlertT ( "Premature movie end in PlayWiimote. %u > %u" , ( u32 ) s_currentByte , ( u32 ) s_totalBytes );
EndPlayInput ( ! s_bReadOnly );
2011-05-03 00:06:44 +00:00
return false ;
}
2011-12-15 09:22:16 -08:00
u8 size = rptf . size ;
2014-09-08 19:13:49 -04:00
u8 sizeInMovie = tmpInput [ s_currentByte ];
2011-12-15 09:22:16 -08:00
if ( size != sizeInMovie )
2011-05-03 00:06:44 +00:00
{
2014-09-08 19:13:49 -04:00
PanicAlertT ( "Fatal desync. Aborting playback. (Error in PlayWiimote: %u != %u, byte %u.)%s" , ( u32 ) sizeInMovie , ( u32 ) size , ( u32 ) s_currentByte ,
( s_numPads & 0xF ) ? " Try re-creating the recording with all GameCube controllers disabled (in Configure > GameCube > Device Settings)." : "" );
EndPlayInput ( ! s_bReadOnly );
2011-12-15 09:22:16 -08:00
return false ;
}
2014-09-08 19:13:49 -04:00
s_currentByte ++ ;
2011-12-15 09:22:16 -08:00
2014-09-08 19:13:49 -04:00
if ( s_currentByte + size > s_totalBytes )
2011-12-15 09:22:16 -08:00
{
2014-09-08 19:13:49 -04:00
PanicAlertT ( "Premature movie end in PlayWiimote. %u + %d > %u" , ( u32 ) s_currentByte , size , ( u32 ) s_totalBytes );
EndPlayInput ( ! s_bReadOnly );
2011-05-03 00:06:44 +00:00
return false ;
}
2013-10-29 01:23:17 -04:00
2014-09-08 19:13:49 -04:00
memcpy ( data , & ( tmpInput [ s_currentByte ]), size );
s_currentByte += size ;
2013-10-29 01:23:17 -04:00
2011-12-14 04:03:05 -08:00
g_currentInputCount ++ ;
2013-10-29 01:23:17 -04:00
2011-12-14 04:03:05 -08:00
CheckInputEnd ();
2011-02-12 08:25:09 +00:00
return true ;
2011-02-11 18:53:51 +00:00
}
2013-10-29 01:23:17 -04:00
void EndPlayInput ( bool cont )
2011-02-15 17:03:20 +00:00
{
2011-05-03 00:06:44 +00:00
if ( cont )
2011-02-12 02:14:20 +00:00
{
2014-09-08 19:13:49 -04:00
s_playMode = MODE_RECORDING ;
2011-12-17 23:27:11 -08:00
Core :: DisplayMessage ( "Reached movie end. Resuming recording." , 2000 );
2011-02-12 02:14:20 +00:00
}
2014-09-08 19:13:49 -04:00
else if ( s_playMode != MODE_NONE )
2011-02-12 02:14:20 +00:00
{
2014-09-08 19:13:49 -04:00
s_rerecords = 0 ;
s_currentByte = 0 ;
s_playMode = MODE_NONE ;
2014-09-06 17:26:40 -04:00
Core :: UpdateWantDeterminism ();
2011-12-17 23:27:11 -08:00
Core :: DisplayMessage ( "Movie End." , 2000 );
2014-09-08 19:13:49 -04:00
s_bRecordingFromSaveState = false ;
2011-12-17 23:27:11 -08:00
// we don't clear these things because otherwise we can't resume playback if we load a movie state later
2014-09-08 19:13:49 -04:00
//g_totalFrames = s_totalBytes = 0;
2011-12-17 23:27:11 -08:00
//delete tmpInput;
2014-03-09 21:14:26 +01:00
//tmpInput = nullptr;
2011-02-12 02:14:20 +00:00
}
2010-06-09 01:37:08 +00:00
}
2014-03-12 15:33:41 -04:00
void SaveRecording ( const std :: string & filename )
2010-08-30 07:05:47 +00:00
{
2011-05-03 00:06:44 +00:00
File :: IOFile save_record ( filename , "wb" );
2011-07-11 20:15:05 +00:00
// Create the real header now and write it
DTMHeader header ;
memset ( & header , 0 , sizeof ( DTMHeader ));
2013-10-29 01:23:17 -04:00
2011-07-11 20:15:05 +00:00
header . filetype [ 0 ] = 'D' ; header . filetype [ 1 ] = 'T' ; header . filetype [ 2 ] = 'M' ; header . filetype [ 3 ] = 0x1A ;
2014-09-09 00:24:49 -04:00
strncpy (( char * ) header . gameID , SConfig :: GetInstance (). m_LocalCoreStartupParameter . GetUniqueID (). c_str (), 6 );
header . bWii = SConfig :: GetInstance (). m_LocalCoreStartupParameter . bWii ;
2014-09-08 19:13:49 -04:00
header . numControllers = s_numPads & ( SConfig :: GetInstance (). m_LocalCoreStartupParameter . bWii ? 0xFF : 0x0F );
2013-10-29 01:23:17 -04:00
2014-09-08 19:13:49 -04:00
header . bFromSaveState = s_bRecordingFromSaveState ;
2011-12-14 04:03:05 -08:00
header . frameCount = g_totalFrames ;
2014-09-11 01:55:43 -04:00
header . lagCount = s_totalLagCount ;
2011-12-14 04:03:05 -08:00
header . inputCount = g_totalInputCount ;
2014-09-08 19:13:49 -04:00
header . numRerecords = s_rerecords ;
header . recordingStartTime = s_recordingStartTime ;
2012-10-18 04:18:40 -04:00
header . bSaveConfig = true ;
2014-09-08 19:13:49 -04:00
header . bSkipIdle = s_bSkipIdle ;
header . bDualCore = s_bDualCore ;
header . bProgressive = s_bProgressive ;
header . bDSPHLE = s_bDSPHLE ;
header . bFastDiscSpeed = s_bFastDiscSpeed ;
strncpy (( char * ) header . videoBackend , s_videoBackend . c_str (), ArraySize ( header . videoBackend ));
header . CPUCore = s_iCPUCore ;
2012-10-18 04:18:40 -04:00
header . bEFBAccessEnable = g_ActiveConfig . bEFBAccessEnable ;
2015-02-21 11:45:48 +01:00
header . bEFBCopyEnable = true ;
2015-02-21 12:08:47 +01:00
header . bSkipEFBCopyToRam = g_ActiveConfig . bSkipEFBCopyToRam ;
2015-01-21 20:37:12 -08:00
header . bEFBCopyCacheEnable = false ;
2012-10-18 04:18:40 -04:00
header . bEFBEmulateFormatChanges = g_ActiveConfig . bEFBEmulateFormatChanges ;
header . bUseXFB = g_ActiveConfig . bUseXFB ;
header . bUseRealXFB = g_ActiveConfig . bUseRealXFB ;
2014-09-08 19:13:49 -04:00
header . memcards = s_memcards ;
2012-10-25 02:44:30 -04:00
header . bClearSave = g_bClearSave ;
2014-09-08 19:13:49 -04:00
header . bSyncGPU = s_bSyncGPU ;
header . bNetPlay = s_bNetPlay ;
2013-09-11 19:19:36 -05:00
strncpy (( char * ) header . discChange , g_discChange . c_str (), ArraySize ( header . discChange ));
2014-09-08 19:13:49 -04:00
strncpy (( char * ) header . author , s_author . c_str (), ArraySize ( header . author ));
memcpy ( header . md5 , s_MD5 , 16 );
header . bongos = s_bongos ;
memcpy ( header . revision , s_revision , ArraySize ( header . revision ));
header . DSPiromHash = s_DSPiromHash ;
header . DSPcoefHash = s_DSPcoefHash ;
header . tickCount = s_totalTickCount ;
2012-10-18 04:18:40 -04:00
2011-07-11 20:15:05 +00:00
// TODO
2013-10-29 01:23:17 -04:00
header . uniqueID = 0 ;
2011-07-11 20:15:05 +00:00
// header.audioEmulator;
2012-10-20 19:54:38 -04:00
2011-07-11 20:15:05 +00:00
save_record . WriteArray ( & header , 1 );
2011-02-12 02:14:20 +00:00
2014-09-08 19:13:49 -04:00
bool success = save_record . WriteArray ( tmpInput , ( size_t ) s_totalBytes );
2011-02-12 02:14:20 +00:00
2014-09-08 19:13:49 -04:00
if ( success && s_bRecordingFromSaveState )
2011-02-15 09:07:55 +00:00
{
2014-03-12 15:33:41 -04:00
std :: string stateFilename = filename + ".sav" ;
2015-02-13 20:11:44 -05:00
success = File :: Copy ( File :: GetUserPath ( D_STATESAVES_IDX ) + "dtm.sav" , stateFilename );
2011-02-15 09:07:55 +00:00
}
2013-10-29 01:23:17 -04:00
2011-02-12 02:14:20 +00:00
if ( success )
2014-03-12 15:33:41 -04:00
Core :: DisplayMessage ( StringFromFormat ( "DTM %s saved" , filename . c_str ()), 2000 );
2010-08-30 07:05:47 +00:00
else
2014-03-12 15:33:41 -04:00
Core :: DisplayMessage ( StringFromFormat ( "Failed to save %s" , filename . c_str ()), 2000 );
2010-08-30 07:05:47 +00:00
}
2011-06-24 06:50:50 +00:00
2014-09-14 23:42:32 -04:00
void SetGCInputManip ( GCManipFunction func )
2011-06-24 06:50:50 +00:00
{
2014-09-14 23:42:32 -04:00
gcmfunc = func ;
}
void SetWiiInputManip ( WiiManipFunction func )
{
wiimfunc = func ;
2011-06-24 06:50:50 +00:00
}
2014-09-14 23:42:32 -04:00
void CallGCInputManip ( GCPadStatus * PadStatus , int controllerID )
2011-06-24 06:50:50 +00:00
{
2014-09-14 23:42:32 -04:00
if ( gcmfunc )
( * gcmfunc )( PadStatus , controllerID );
}
2014-10-31 04:34:30 -04:00
void CallWiiInputManip ( u8 * data , WiimoteEmu :: ReportFeatures rptf , int controllerID , int ext , const wiimote_key key )
2014-09-14 23:42:32 -04:00
{
if ( wiimfunc )
2014-10-31 04:34:30 -04:00
( * wiimfunc )( data , rptf , controllerID , ext , key );
2011-06-24 06:50:50 +00:00
}
2012-10-18 04:18:40 -04:00
void SetGraphicsConfig ()
{
g_Config . bEFBAccessEnable = tmpHeader . bEFBAccessEnable ;
2015-02-21 12:08:47 +01:00
g_Config . bSkipEFBCopyToRam = tmpHeader . bSkipEFBCopyToRam ;
2012-10-18 04:18:40 -04:00
g_Config . bEFBEmulateFormatChanges = tmpHeader . bEFBEmulateFormatChanges ;
g_Config . bUseXFB = tmpHeader . bUseXFB ;
g_Config . bUseRealXFB = tmpHeader . bUseRealXFB ;
}
2012-10-24 16:35:52 -04:00
void GetSettings ()
{
2014-09-08 19:13:49 -04:00
s_bSaveConfig = true ;
s_bSkipIdle = SConfig :: GetInstance (). m_LocalCoreStartupParameter . bSkipIdle ;
s_bDualCore = SConfig :: GetInstance (). m_LocalCoreStartupParameter . bCPUThread ;
s_bProgressive = SConfig :: GetInstance (). m_LocalCoreStartupParameter . bProgressive ;
s_bDSPHLE = SConfig :: GetInstance (). m_LocalCoreStartupParameter . bDSPHLE ;
s_bFastDiscSpeed = SConfig :: GetInstance (). m_LocalCoreStartupParameter . bFastDiscSpeed ;
s_videoBackend = g_video_backend -> GetName ();
s_bSyncGPU = SConfig :: GetInstance (). m_LocalCoreStartupParameter . bSyncGPU ;
s_iCPUCore = SConfig :: GetInstance (). m_LocalCoreStartupParameter . iCPUCore ;
s_bNetPlay = NetPlay :: IsNetPlayRunning ();
2014-09-09 00:24:49 -04:00
if ( ! SConfig :: GetInstance (). m_LocalCoreStartupParameter . bWii )
2012-10-25 02:44:30 -04:00
g_bClearSave = ! File :: Exists ( SConfig :: GetInstance (). m_strMemoryCardA );
2014-09-08 19:13:49 -04:00
s_memcards |= ( SConfig :: GetInstance (). m_EXIDevice [ 0 ] == EXIDEVICE_MEMORYCARD ) << 0 ;
s_memcards |= ( SConfig :: GetInstance (). m_EXIDevice [ 1 ] == EXIDEVICE_MEMORYCARD ) << 1 ;
2014-04-23 14:51:59 -07:00
unsigned int tmp ;
2013-06-24 09:14:22 -04:00
for ( int i = 0 ; i < 20 ; ++ i )
2013-01-19 15:02:02 -05:00
{
2014-04-23 14:51:59 -07:00
sscanf ( & scm_rev_git_str [ 2 * i ], "%02x" , & tmp );
2014-09-08 19:13:49 -04:00
s_revision [ i ] = tmp ;
2013-01-19 15:02:02 -05:00
}
2014-09-08 19:13:49 -04:00
if ( ! s_bDSPHLE )
2014-04-06 09:28:44 -04:00
{
std :: string irom_file = File :: GetUserPath ( D_GCUSER_IDX ) + DSP_IROM ;
std :: string coef_file = File :: GetUserPath ( D_GCUSER_IDX ) + DSP_COEF ;
if ( ! File :: Exists ( irom_file ))
irom_file = File :: GetSysDirectory () + GC_SYS_DIR DIR_SEP DSP_IROM ;
if ( ! File :: Exists ( coef_file ))
coef_file = File :: GetSysDirectory () + GC_SYS_DIR DIR_SEP DSP_COEF ;
std :: vector < u16 > irom ( DSP_IROM_SIZE );
File :: IOFile file_irom ( irom_file , "rb" );
file_irom . ReadArray ( irom . data (), DSP_IROM_SIZE );
file_irom . Close ();
2014-09-08 20:15:47 -04:00
for ( int i = 0 ; i < DSP_IROM_SIZE ; ++ i )
2014-04-06 09:28:44 -04:00
irom [ i ] = Common :: swap16 ( irom [ i ]);
std :: vector < u16 > coef ( DSP_COEF_SIZE );
File :: IOFile file_coef ( coef_file , "rb" );
file_coef . ReadArray ( coef . data (), DSP_COEF_SIZE );
file_coef . Close ();
2014-09-08 20:15:47 -04:00
for ( int i = 0 ; i < DSP_COEF_SIZE ; ++ i )
2014-04-06 09:28:44 -04:00
coef [ i ] = Common :: swap16 ( coef [ i ]);
2014-09-08 19:13:49 -04:00
s_DSPiromHash = HashAdler32 (( u8 * ) irom . data (), DSP_IROM_BYTE_SIZE );
s_DSPcoefHash = HashAdler32 (( u8 * ) coef . data (), DSP_COEF_BYTE_SIZE );
2014-04-06 09:28:44 -04:00
}
else
{
2014-09-08 19:13:49 -04:00
s_DSPiromHash = 0 ;
s_DSPcoefHash = 0 ;
2014-04-06 09:28:44 -04:00
}
2012-10-24 16:35:52 -04:00
}
2012-11-23 22:23:58 -05:00
void CheckMD5 ()
{
2014-09-06 07:10:41 -04:00
for ( int i = 0 , n = 0 ; i < 16 ; ++ i )
2012-11-23 22:23:58 -05:00
{
2012-11-25 19:26:37 -05:00
if ( tmpHeader . md5 [ i ] != 0 )
continue ;
n ++ ;
if ( n == 16 )
return ;
2012-11-23 22:23:58 -05:00
}
2012-11-25 19:26:37 -05:00
Core :: DisplayMessage ( "Verifying checksum..." , 2000 );
2012-11-23 22:23:58 -05:00
unsigned char gameMD5 [ 16 ];
2014-11-05 14:14:24 -05:00
md5_file ( SConfig :: GetInstance (). m_LocalCoreStartupParameter . m_strFilename . c_str (), gameMD5 );
2012-11-23 22:23:58 -05:00
2014-09-08 19:13:49 -04:00
if ( memcmp ( gameMD5 , s_MD5 , 16 ) == 0 )
2012-11-25 19:26:37 -05:00
Core :: DisplayMessage ( "Checksum of current game matches the recorded game." , 2000 );
2012-11-23 22:23:58 -05:00
else
2013-12-23 07:18:28 -05:00
Core :: DisplayMessage ( "Checksum of current game does not match the recorded game!" , 3000 );
2012-11-25 19:26:37 -05:00
}
void GetMD5 ()
{
Core :: DisplayMessage ( "Calculating checksum of game file..." , 2000 );
2014-09-08 19:13:49 -04:00
memset ( s_MD5 , 0 , sizeof ( s_MD5 ));
2014-11-05 14:14:24 -05:00
md5_file ( SConfig :: GetInstance (). m_LocalCoreStartupParameter . m_strFilename . c_str (), s_MD5 );
2012-11-25 19:26:37 -05:00
Core :: DisplayMessage ( "Finished calculating checksum." , 2000 );
2012-11-23 22:23:58 -05:00
}
2012-11-24 18:27:20 -05:00
void Shutdown ()
{
2014-09-08 19:13:49 -04:00
g_currentInputCount = g_totalInputCount = g_totalFrames = s_totalBytes = s_tickCountAtLastInput = 0 ;
2012-11-24 18:27:20 -05:00
delete [] tmpInput ;
2014-03-09 21:14:26 +01:00
tmpInput = nullptr ;
2012-11-25 22:41:48 -05:00
tmpInputAllocated = 0 ;
2012-11-24 18:27:20 -05:00
}
2012-11-25 22:41:48 -05:00
};