2013-04-18 02:43:11 +00: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 18:09:14 +00:00
# include <polarssl/md5.h>
2014-02-17 10:18:15 +00:00
2014-07-27 17:37:09 +00:00
# include "Common/ChunkFile.h"
2014-04-06 13:28:44 +00:00
# include "Common/CommonPaths.h"
2014-02-17 10:18:15 +00:00
# include "Common/FileUtil.h"
2014-04-06 13:28:44 +00:00
# include "Common/Hash.h"
2014-02-17 10:18:15 +00:00
# include "Common/NandPaths.h"
2014-07-03 02:42:36 +00:00
# include "Common/StringUtil.h"
2014-02-17 10:18:15 +00:00
# include "Common/Thread.h"
# include "Common/Timer.h"
# include "Core/ConfigManager.h"
# include "Core/Core.h"
2014-06-18 20:13:46 +00:00
# include "Core/CoreTiming.h"
2014-02-17 10:18:15 +00:00
# include "Core/Movie.h"
# include "Core/NetPlayProto.h"
# include "Core/State.h"
2014-04-06 13:28:44 +00:00
# include "Core/DSP/DSPCore.h"
2014-02-17 10:18:15 +00:00
# include "Core/HW/DVDInterface.h"
# include "Core/HW/EXI_Device.h"
# include "Core/HW/SI.h"
# include "Core/HW/Wiimote.h"
# include "Core/HW/WiimoteEmu/WiimoteEmu.h"
# include "Core/HW/WiimoteEmu/WiimoteHid.h"
# include "Core/IPC_HLE/WII_IPC_HLE_Device_usb.h"
# include "Core/PowerPC/PowerPC.h"
2014-07-27 17:37:09 +00:00
# include "InputCommon/GCPadStatus.h"
2014-02-17 10:18:15 +00:00
# include "VideoCommon/VideoConfig.h"
2011-02-12 02:14:20 +00:00
2012-11-26 03:41:48 +00: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 13:58:25 +00: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-07-08 20:37:58 +00:00
static bool g_bFrameStep = false ;
2014-07-08 13:58:25 +00:00
static bool g_bFrameStop = false ;
2014-07-08 20:37:58 +00:00
static bool g_bReadOnly = true ;
static u32 g_rerecords = 0 ;
static PlayMode g_playMode = MODE_NONE ;
2010-06-09 01:37:08 +00:00
2014-07-08 20:37:58 +00:00
static u32 g_framesToSkip = 0 , g_frameSkipCounter = 0 ;
2010-06-09 01:37:08 +00:00
2014-07-08 20:37:58 +00:00
static u8 g_numPads = 0 ;
2014-07-08 13:58:25 +00:00
static ControllerState g_padState ;
static DTMHeader tmpHeader ;
static u8 * tmpInput = nullptr ;
static size_t tmpInputAllocated = 0 ;
2014-07-08 20:37:58 +00:00
static u64 g_currentByte = 0 , g_totalBytes = 0 ;
2011-12-14 12:03:05 +00:00
u64 g_currentFrame = 0 , g_totalFrames = 0 ; // VI
2014-07-08 20:37:58 +00:00
u64 g_currentLagCount = 0 ;
static u64 g_totalLagCount = 0 ; // just stats
2011-12-14 12:03:05 +00:00
u64 g_currentInputCount = 0 , g_totalInputCount = 0 ; // just stats
2014-07-08 20:37:58 +00:00
static u64 g_totalTickCount = 0 , g_tickCountAtLastInput = 0 ; // just stats
2014-07-08 13:58:25 +00:00
static u64 g_recordingStartTime ; // seconds since 1970 that recording started
static bool bSaveConfig = false , bSkipIdle = false , bDualCore = false , bProgressive = false , bDSPHLE = false , bFastDiscSpeed = false ;
bool g_bClearSave = false ;
static bool bSyncGPU = false , bNetPlay = false ;
static std : : string videoBackend = " unknown " ;
static int iCPUCore = 1 ;
2012-10-20 18:18:42 +00:00
bool g_bDiscChange = false ;
std : : string g_discChange = " " ;
2014-07-08 13:58:25 +00:00
static std : : string author = " " ;
2012-10-25 06:44:30 +00:00
u64 g_titleID = 0 ;
2014-07-08 13:58:25 +00:00
static unsigned char MD5 [ 16 ] ;
static u8 bongos , memcards ;
static u8 revision [ 20 ] ;
static u32 DSPiromHash = 0 ;
static u32 DSPcoefHash = 0 ;
2010-06-09 01:37:08 +00:00
2014-07-08 13:58:25 +00:00
static bool g_bRecordingFromSaveState = false ;
2014-07-08 20:37:58 +00:00
static bool g_bPolled = false ;
2014-07-08 13:58:25 +00:00
static int g_currentSaveVersion = 0 ;
2010-06-09 01:37:08 +00:00
2014-07-08 13:58:25 +00:00
static std : : string tmpStateFilename = File : : GetUserPath ( D_STATESAVES_IDX ) + " dtm.sav " ;
2010-06-09 01:37:08 +00:00
2014-07-08 13:58:25 +00:00
static std : : string g_InputDisplay [ 8 ] ;
2011-02-17 09:12:36 +00:00
2014-07-08 13:58:25 +00:00
static ManipFunction mfunc = nullptr ;
2011-06-24 06:50:50 +00:00
2014-07-08 12:29:26 +00:00
static void EnsureTmpInputSize ( size_t bound )
2012-11-26 03:41:48 +00: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 05:23:17 +00:00
2012-11-26 03:41:48 +00:00
u8 * newTmpInput = new u8 [ newAlloc ] ;
tmpInputAllocated = newAlloc ;
2014-03-09 20:14:26 +00:00
if ( tmpInput ! = nullptr )
2012-11-26 03:41:48 +00:00
{
if ( g_totalBytes > 0 )
2012-12-23 18:37:50 +00:00
memcpy ( newTmpInput , tmpInput , ( size_t ) g_totalBytes ) ;
2012-11-26 03:41:48 +00:00
delete [ ] tmpInput ;
}
tmpInput = newTmpInput ;
}
2011-12-26 11:09:30 +00:00
2011-02-17 09:12:36 +00:00
std : : string GetInputDisplay ( )
{
2012-12-10 11:20:06 +00:00
if ( ! IsPlayingInput ( ) & & ! IsRecordingInput ( ) )
{
g_numPads = 0 ;
for ( int i = 0 ; i < 4 ; i + + )
{
2012-12-19 04:19:15 +00:00
if ( SConfig : : GetInstance ( ) . m_SIDevice [ i ] = = SIDEVICE_GC_CONTROLLER | | SConfig : : GetInstance ( ) . m_SIDevice [ i ] = = SIDEVICE_GC_TARUKONGA )
2012-12-10 11:20:06 +00:00
g_numPads | = ( 1 < < i ) ;
if ( g_wiimote_sources [ i ] ! = WIIMOTE_SRC_NONE )
g_numPads | = ( 1 < < ( i + 4 ) ) ;
}
}
2013-04-17 03:14:36 +00:00
2011-02-17 09:12:36 +00:00
std : : string inputDisplay = " " ;
2011-12-14 12:03:05 +00:00
for ( int i = 0 ; i < 8 ; + + i )
if ( ( g_numPads & ( 1 < < i ) ) ! = 0 )
inputDisplay . append ( g_InputDisplay [ i ] ) ;
2013-10-29 05:23:17 +00:00
return inputDisplay ;
2011-02-17 09:12:36 +00:00
}
2010-06-09 01:37:08 +00:00
void FrameUpdate ( )
{
2011-12-14 12:03:05 +00:00
g_currentFrame + + ;
2014-03-10 11:30:55 +00:00
if ( ! g_bPolled )
2011-12-14 12:03:05 +00:00
g_currentLagCount + + ;
2011-02-15 17:03:20 +00:00
if ( IsRecordingInput ( ) )
2011-12-14 12:03:05 +00:00
{
g_totalFrames = g_currentFrame ;
g_totalLagCount = g_currentLagCount ;
}
2010-06-09 01:37:08 +00:00
if ( g_bFrameStep )
2011-12-12 05:08:26 +00:00
{
2010-06-09 01:37:08 +00:00
Core : : SetState ( Core : : CORE_PAUSE ) ;
2011-12-12 05:08:26 +00:00
g_bFrameStep = false ;
}
2013-10-29 05:23:17 +00: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 03:30:06 +00:00
// after that we set it back to CPU_RUNNING and continue as normal.
2010-06-09 01:37:08 +00:00
if ( g_bFrameStop )
2014-07-07 03:30:06 +00:00
* PowerPC : : GetStatePtr ( ) = PowerPC : : CPU_STEPPING ;
2010-06-09 01:37:08 +00:00
2014-03-10 11:30:55 +00:00
if ( g_framesToSkip )
2010-06-09 01:37:08 +00:00
FrameSkipping ( ) ;
2013-10-29 05:23:17 +00:00
2010-06-09 01:37:08 +00:00
g_bPolled = false ;
}
2012-01-02 10:20:22 +00:00
// called when game is booting up, even if no movie is active,
// but potentially after BeginRecordingInput or PlayInput has been called.
void Init ( )
{
g_bPolled = false ;
g_bFrameStep = false ;
g_bFrameStop = false ;
2012-10-20 23:54:38 +00:00
bSaveConfig = false ;
2012-11-12 16:39:33 +00:00
iCPUCore = SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . iCPUCore ;
2012-10-20 23:54:38 +00:00
if ( IsPlayingInput ( ) )
2012-10-18 08:18:40 +00:00
{
2012-10-20 23:54:38 +00:00
ReadHeader ( ) ;
2012-11-26 00:26:37 +00:00
std : : thread md5thread ( CheckMD5 ) ;
2013-07-28 15:55:35 +00:00
md5thread . detach ( ) ;
2012-10-24 23:37:51 +00:00
if ( ( strncmp ( ( char * ) tmpHeader . gameID , Core : : g_CoreStartupParameter . GetUniqueID ( ) . c_str ( ) , 6 ) ) )
{
PanicAlert ( " The recorded game (%s) is not the same as the selected game (%s) " , tmpHeader . gameID , Core : : g_CoreStartupParameter . GetUniqueID ( ) . c_str ( ) ) ;
EndPlayInput ( false ) ;
}
2012-10-18 08:18:40 +00:00
}
2013-04-17 03:14:36 +00:00
2012-10-24 20:35:52 +00:00
if ( IsRecordingInput ( ) )
{
GetSettings ( ) ;
2012-11-26 17:29:36 +00:00
std : : thread md5thread ( GetMD5 ) ;
2013-07-28 15:55:35 +00:00
md5thread . detach ( ) ;
2014-06-18 20:13:46 +00:00
g_tickCountAtLastInput = 0 ;
2012-10-24 20:35:52 +00:00
}
2012-11-26 17:29:36 +00:00
2012-01-02 10:20:22 +00:00
g_frameSkipCounter = g_framesToSkip ;
memset ( & g_padState , 0 , sizeof ( g_padState ) ) ;
2012-10-21 21:48:45 +00:00
if ( ! tmpHeader . bFromSaveState | | ! IsPlayingInput ( ) )
2012-10-18 08:14:25 +00:00
Core : : SetStateFileName ( " " ) ;
2012-10-25 03:21:34 +00:00
2013-10-29 05:09:01 +00:00
for ( auto & disp : g_InputDisplay )
disp . clear ( ) ;
2012-01-02 10:20:22 +00:00
if ( ! IsPlayingInput ( ) & & ! IsRecordingInput ( ) )
{
g_bRecordingFromSaveState = false ;
g_rerecords = 0 ;
g_currentByte = 0 ;
g_currentFrame = 0 ;
g_currentLagCount = 0 ;
g_currentInputCount = 0 ;
}
}
2011-03-17 10:41:56 +00:00
void InputUpdate ( )
{
2011-12-14 12:03:05 +00:00
g_currentInputCount + + ;
if ( IsRecordingInput ( ) )
2014-06-18 20:13:46 +00:00
{
2011-12-14 12:03:05 +00:00
g_totalInputCount = g_currentInputCount ;
2014-06-18 20:13:46 +00:00
g_totalTickCount + = CoreTiming : : GetTicks ( ) - g_tickCountAtLastInput ;
g_tickCountAtLastInput = CoreTiming : : GetTicks ( ) ;
}
2012-11-18 06:07:48 +00:00
if ( IsPlayingInput ( ) & & g_currentInputCount = = ( g_totalInputCount - 1 ) & & SConfig : : GetInstance ( ) . m_PauseMovie )
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 05:23:17 +00:00
2010-06-09 01:37:08 +00:00
g_framesToSkip = framesToSkip ;
g_frameSkipCounter = 0 ;
2013-10-29 05:23:17 +00: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 ( )
{
g_bPolled = true ;
}
2011-12-12 05:08:26 +00:00
void DoFrameStep ( )
2010-06-09 01:37:08 +00:00
{
2014-03-10 11:30:55 +00:00
if ( Core : : GetState ( ) = = Core : : CORE_PAUSE )
2011-12-12 05:08:26 +00:00
{
// if already paused, frame advance for 1 frame
Core : : SetState ( Core : : CORE_RUN ) ;
2011-12-14 12:03:05 +00:00
Core : : RequestRefreshInfo ( ) ;
2011-12-12 05:08:26 +00:00
g_bFrameStep = true ;
}
2014-03-10 11:30:55 +00:00
else if ( ! g_bFrameStep )
2011-12-12 05:08:26 +00: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
{
2011-01-17 01:47:27 +00:00
g_bFrameStop = bEnabled ;
2010-06-09 01:37:08 +00:00
}
2011-02-12 02:14:20 +00:00
void SetReadOnly ( bool bEnabled )
{
2011-12-12 05:08:26 +00:00
if ( g_bReadOnly ! = bEnabled )
Core : : DisplayMessage ( bEnabled ? " Read-only mode. " : " Read+Write mode. " , 1000 ) ;
2011-02-12 02:14:20 +00:00
g_bReadOnly = bEnabled ;
}
2010-06-09 01:37:08 +00:00
void FrameSkipping ( )
{
2011-02-11 12:26:15 +00:00
// Frameskipping will desync movie playback
if ( ! IsPlayingInput ( ) & & ! IsRecordingInput ( ) )
{
2011-03-05 06:11:26 +00:00
std : : lock_guard < std : : mutex > lk ( cs_frameSkip ) ;
2011-02-11 12:26:15 +00:00
g_frameSkipCounter + + ;
2011-03-15 23:09:12 +00:00
if ( g_frameSkipCounter > g_framesToSkip | | Core : : ShouldSkipFrame ( g_frameSkipCounter ) = = false )
2011-02-11 12:26:15 +00:00
g_frameSkipCounter = 0 ;
2012-10-20 23:54:38 +00:00
2011-02-11 12:26:15 +00:00
g_video_backend - > Video_SetRendering ( ! g_frameSkipCounter ) ;
}
2010-06-09 01:37:08 +00:00
}
bool IsRecordingInput ( )
{
return ( g_playMode = = MODE_RECORDING ) ;
}
2011-02-15 09:07:55 +00:00
bool IsRecordingInputFromSaveState ( )
{
return g_bRecordingFromSaveState ;
}
2011-12-14 12:03:05 +00:00
bool IsJustStartingRecordingInputFromSaveState ( )
{
return IsRecordingInputFromSaveState ( ) & & g_currentFrame = = 0 ;
}
2012-10-18 08:03:12 +00:00
bool IsJustStartingPlayingInputFromSaveState ( )
{
2012-10-21 21:48:45 +00:00
return IsRecordingInputFromSaveState ( ) & & g_currentFrame = = 1 & & IsPlayingInput ( ) ;
2012-10-18 08:03:12 +00:00
}
2010-06-09 01:37:08 +00:00
bool IsPlayingInput ( )
{
return ( g_playMode = = MODE_PLAYING ) ;
}
2011-07-11 20:15:05 +00:00
bool IsReadOnly ( )
{
return g_bReadOnly ;
}
2011-12-26 11:09:30 +00:00
u64 GetRecordingStartTime ( )
{
return g_recordingStartTime ;
}
2010-09-06 21:41:01 +00:00
bool IsUsingPad ( int controller )
{
2011-02-12 00:06:58 +00:00
return ( ( g_numPads & ( 1 < < controller ) ) ! = 0 ) ;
2011-02-11 19:09:46 +00:00
}
2012-12-19 04:19:15 +00:00
bool IsUsingBongo ( int controller )
{
return ( ( bongos & ( 1 < < controller ) ) ! = 0 ) ;
}
2011-02-11 19:09:46 +00:00
bool IsUsingWiimote ( int wiimote )
{
2011-02-12 00:06:58 +00:00
return ( ( g_numPads & ( 1 < < ( wiimote + 4 ) ) ) ! = 0 ) ;
2010-09-06 21:41:01 +00:00
}
2012-10-18 08:18:40 +00:00
bool IsConfigSaved ( )
{
2012-10-20 23:54:38 +00:00
return bSaveConfig ;
2012-10-18 08:18:40 +00:00
}
bool IsDualCore ( )
{
2012-10-20 23:54:38 +00:00
return bDualCore ;
2012-10-18 08:18:40 +00:00
}
bool IsProgressive ( )
{
2012-10-20 23:54:38 +00:00
return bProgressive ;
2012-10-18 08:18:40 +00:00
}
bool IsSkipIdle ( )
{
2012-10-20 23:54:38 +00:00
return bSkipIdle ;
2012-10-18 08:18:40 +00:00
}
bool IsDSPHLE ( )
{
2012-10-20 23:54:38 +00:00
return bDSPHLE ;
2012-10-18 08:18:40 +00:00
}
bool IsFastDiscSpeed ( )
{
2012-10-20 23:54:38 +00:00
return bFastDiscSpeed ;
2012-10-18 08:18:40 +00:00
}
2014-07-07 03:30:06 +00:00
int GetCPUMode ( )
2012-10-18 08:18:40 +00:00
{
2012-10-21 21:48:45 +00:00
return iCPUCore ;
}
2012-10-25 06:44:30 +00:00
bool IsStartingFromClearSave ( )
2012-10-21 21:48:45 +00:00
{
2012-10-25 06:44:30 +00:00
return g_bClearSave ;
2012-10-21 21:48:45 +00:00
}
2014-07-03 01:45:59 +00:00
bool IsUsingMemcard ( int memcard )
2012-10-21 21:48:45 +00:00
{
2014-07-03 06:07:26 +00:00
return ( memcards & ( 1 < < memcard ) ) ! = 0 ;
2012-10-18 08:18:40 +00:00
}
2013-06-20 10:08:17 +00:00
bool IsSyncGPU ( )
{
return bSyncGPU ;
}
2012-10-18 08:18:40 +00:00
2013-09-03 19:50:41 +00:00
bool IsNetPlayRecording ( )
{
return bNetPlay ;
}
2011-02-12 00:06:58 +00:00
void ChangePads ( bool instantly )
2010-09-06 21:41:01 +00:00
{
2014-07-07 22:47:57 +00:00
if ( ! Core : : IsRunning ( ) )
2011-07-11 20:15:05 +00:00
return ;
int controllers = 0 ;
2014-01-09 01:34:14 +00:00
for ( int i = 0 ; i < MAX_SI_CHANNELS ; i + + )
2012-12-19 04:19:15 +00: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 ) ;
if ( instantly & & ( g_numPads & 0x0F ) = = controllers )
return ;
2014-01-09 01:34:14 +00: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-19 04:19:15 +00: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-19 04:19:15 +00: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-01-09 01:34:14 +00: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
if ( instantly & & ( g_numPads > > 4 ) = = controllers )
return ;
2014-01-09 01:34:14 +00: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-03-10 11:30:55 +00:00
if ( g_playMode ! = MODE_NONE | | controllers = = 0 )
2010-06-09 01:37:08 +00:00
return false ;
2011-02-15 09:07:55 +00:00
2012-10-18 08:03:12 +00:00
g_numPads = controllers ;
g_currentFrame = g_totalFrames = 0 ;
g_currentLagCount = g_totalLagCount = 0 ;
g_currentInputCount = g_totalInputCount = 0 ;
2014-06-18 20:13:46 +00:00
g_totalTickCount = g_tickCountAtLastInput = 0 ;
2013-09-03 19:50:41 +00:00
if ( NetPlay : : IsNetPlayRunning ( ) )
{
bNetPlay = true ;
g_recordingStartTime = NETPLAY_INITIAL_GCTIME ;
}
else
g_recordingStartTime = Common : : Timer : : GetLocalTimeSinceJan1970 ( ) ;
2012-10-18 08:03:12 +00:00
g_rerecords = 0 ;
2014-01-09 01:34:14 +00:00
for ( int i = 0 ; i < MAX_SI_CHANNELS ; i + + )
2012-12-19 04:19:15 +00:00
if ( SConfig : : GetInstance ( ) . m_SIDevice [ i ] = = SIDEVICE_GC_TARUKONGA )
bongos | = ( 1 < < i ) ;
2014-07-08 20:32:35 +00:00
if ( Core : : IsRunningAndStarted ( ) )
2011-02-15 09:07:55 +00:00
{
2014-03-10 11:30:55 +00:00
if ( File : : Exists ( tmpStateFilename ) )
2011-05-03 00:06:44 +00:00
File : : Delete ( tmpStateFilename ) ;
2011-02-15 09:07:55 +00:00
2014-03-12 19:33:41 +00:00
State : : SaveAs ( tmpStateFilename ) ;
2011-05-03 00:06:44 +00:00
g_bRecordingFromSaveState = true ;
2012-10-25 06:44:30 +00:00
2012-10-26 16:06:32 +00: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 06:44:30 +00:00
// TODO: find a way to GetTitleDataPath() from Movie::Init()
2012-11-24 03:23:58 +00:00
if ( Core : : g_CoreStartupParameter . bWii )
{
2014-03-12 19:33:41 +00:00
if ( File : : Exists ( Common : : GetTitleDataPath ( g_titleID ) + " banner.bin " ) )
2012-11-24 03:23:58 +00:00
Movie : : g_bClearSave = false ;
else
Movie : : g_bClearSave = true ;
}
2012-11-26 17:29:36 +00:00
std : : thread md5thread ( GetMD5 ) ;
2013-09-11 07:34:05 +00:00
md5thread . detach ( ) ;
2013-07-01 21:44:42 +00:00
GetSettings ( ) ;
2010-06-09 01:37:08 +00:00
}
g_playMode = MODE_RECORDING ;
2012-11-14 21:21:54 +00:00
author = SConfig : : GetInstance ( ) . m_strMovieAuthor ;
2012-11-26 03:41:48 +00:00
EnsureTmpInputSize ( 1 ) ;
2011-12-14 12:03:05 +00:00
g_currentByte = g_totalBytes = 0 ;
2012-10-18 08:18:40 +00:00
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-07-03 02:42:36 +00:00
static std : : string Analog2DToString ( u8 x , u8 y , const std : : string & prefix )
2011-12-14 12:03:05 +00:00
{
2014-03-10 11:30:55 +00:00
if ( ( x < = 1 | | x = = 128 | | x > = 255 ) & &
( y < = 1 | | y = = 128 | | y > = 255 ) )
2011-12-14 12:03:05 +00:00
{
2014-03-10 11:30:55 +00:00
if ( x ! = 128 | | y ! = 128 )
2011-12-14 12:03:05 +00:00
{
2014-03-10 11:30:55 +00:00
if ( x ! = 128 & & y ! = 128 )
2011-12-14 12:03:05 +00:00
{
2014-07-03 02:42:36 +00:00
return StringFromFormat ( " %s:%s,%s " , prefix . c_str ( ) , x < 128 ? " LEFT " : " RIGHT " , y < 128 ? " DOWN " : " UP " ) ;
2011-12-14 12:03:05 +00:00
}
2014-03-10 11:30:55 +00:00
else if ( x ! = 128 )
2011-12-14 12:03:05 +00:00
{
2014-07-03 02:42:36 +00:00
return StringFromFormat ( " %s:%s " , prefix . c_str ( ) , x < 128 ? " LEFT " : " RIGHT " ) ;
2011-12-14 12:03:05 +00:00
}
else
{
2014-07-03 02:42:36 +00:00
return StringFromFormat ( " %s:%s " , prefix . c_str ( ) , y < 128 ? " DOWN " : " UP " ) ;
2011-12-14 12:03:05 +00:00
}
}
else
{
2014-07-03 02:42:36 +00:00
return " " ;
2011-12-14 12:03:05 +00:00
}
}
else
{
2014-07-03 02:42:36 +00:00
return StringFromFormat ( " %s:%d,%d " , prefix . c_str ( ) , x , y ) ;
2011-12-14 12:03:05 +00:00
}
}
2014-07-03 02:42:36 +00:00
static std : : string Analog1DToString ( u8 v , const std : : string & prefix )
2011-12-14 12:03:05 +00:00
{
2014-03-10 11:30:55 +00:00
if ( v > 0 )
2011-12-14 12:03:05 +00:00
{
2014-03-10 11:30:55 +00:00
if ( v = = 255 )
2011-12-14 12:03:05 +00:00
{
2014-07-03 02:42:36 +00:00
return prefix ;
2011-12-14 12:03:05 +00:00
}
else
{
2014-07-03 02:42:36 +00:00
return StringFromFormat ( " %s:%d " , prefix . c_str ( ) , v ) ;
2011-12-14 12:03:05 +00:00
}
}
else
{
2014-07-03 02:42:36 +00:00
return " " ;
2011-12-14 12:03:05 +00:00
}
}
2014-07-08 12:29:26 +00:00
static void SetInputDisplayString ( ControllerState padState , int controllerID )
2011-02-17 09:12:36 +00:00
{
2014-07-03 02:42:36 +00:00
g_InputDisplay [ controllerID ] = StringFromFormat ( " P%d: " , controllerID + 1 ) ;
2011-02-17 09:12:36 +00:00
2014-07-26 21:14:54 +00:00
if ( padState . A )
2011-02-17 09:12:36 +00:00
g_InputDisplay [ controllerID ] . append ( " A " ) ;
2014-07-26 21:14:54 +00:00
if ( padState . B )
2011-02-17 09:12:36 +00:00
g_InputDisplay [ controllerID ] . append ( " B " ) ;
2014-07-26 21:14:54 +00:00
if ( padState . X )
2011-02-17 09:12:36 +00:00
g_InputDisplay [ controllerID ] . append ( " X " ) ;
2014-07-26 21:14:54 +00:00
if ( padState . Y )
2011-02-17 09:12:36 +00:00
g_InputDisplay [ controllerID ] . append ( " Y " ) ;
2014-07-26 21:14:54 +00:00
if ( padState . Z )
2011-02-17 09:12:36 +00:00
g_InputDisplay [ controllerID ] . append ( " Z " ) ;
2014-07-26 21:14:54 +00:00
if ( padState . Start )
2011-02-17 09:12:36 +00:00
g_InputDisplay [ controllerID ] . append ( " START " ) ;
2014-07-26 21:14:54 +00:00
if ( padState . DPadUp )
2011-02-17 09:12:36 +00:00
g_InputDisplay [ controllerID ] . append ( " UP " ) ;
2014-07-26 21:14:54 +00:00
if ( padState . DPadDown )
2011-02-17 09:12:36 +00:00
g_InputDisplay [ controllerID ] . append ( " DOWN " ) ;
2014-07-26 21:14:54 +00:00
if ( padState . DPadLeft )
2011-02-17 09:12:36 +00:00
g_InputDisplay [ controllerID ] . append ( " LEFT " ) ;
2014-07-26 21:14:54 +00:00
if ( padState . DPadRight )
2011-02-17 09:12:36 +00:00
g_InputDisplay [ controllerID ] . append ( " RIGHT " ) ;
2011-12-14 12:03:05 +00:00
2014-07-26 21:14:54 +00:00
g_InputDisplay [ controllerID ] . append ( Analog1DToString ( padState . TriggerL , " L " ) ) ;
g_InputDisplay [ controllerID ] . append ( Analog1DToString ( padState . TriggerR , " R " ) ) ;
g_InputDisplay [ controllerID ] . append ( Analog2DToString ( padState . AnalogStickX , padState . AnalogStickY , " ANA " ) ) ;
g_InputDisplay [ controllerID ] . append ( Analog2DToString ( padState . CStickX , padState . CStickY , " C " ) ) ;
2011-12-14 12:03:05 +00:00
g_InputDisplay [ controllerID ] . append ( " \n " ) ;
}
2014-07-08 12:29:26 +00:00
static void SetWiiInputDisplayString ( int remoteID , u8 * const coreData , u8 * const accelData , u8 * const irData )
2011-12-14 12:03:05 +00:00
{
int controllerID = remoteID + 4 ;
2014-07-03 02:42:36 +00:00
g_InputDisplay [ controllerID ] = StringFromFormat ( " R%d: " , remoteID + 1 ) ;
2011-12-14 12:03:05 +00:00
2014-03-10 11:30:55 +00:00
if ( coreData )
2011-12-14 12:03:05 +00:00
{
wm_core buttons = * ( wm_core * ) coreData ;
2014-03-10 11:30:55 +00:00
if ( buttons & WiimoteEmu : : Wiimote : : PAD_LEFT )
2011-12-14 12:03:05 +00:00
g_InputDisplay [ controllerID ] . append ( " LEFT " ) ;
2014-03-10 11:30:55 +00:00
if ( buttons & WiimoteEmu : : Wiimote : : PAD_RIGHT )
2011-12-14 12:03:05 +00:00
g_InputDisplay [ controllerID ] . append ( " RIGHT " ) ;
2014-03-10 11:30:55 +00:00
if ( buttons & WiimoteEmu : : Wiimote : : PAD_DOWN )
2011-12-14 12:03:05 +00:00
g_InputDisplay [ controllerID ] . append ( " DOWN " ) ;
2014-03-10 11:30:55 +00:00
if ( buttons & WiimoteEmu : : Wiimote : : PAD_UP )
2011-12-14 12:03:05 +00:00
g_InputDisplay [ controllerID ] . append ( " UP " ) ;
2014-03-10 11:30:55 +00:00
if ( buttons & WiimoteEmu : : Wiimote : : BUTTON_A )
2011-12-14 12:03:05 +00:00
g_InputDisplay [ controllerID ] . append ( " A " ) ;
2014-03-10 11:30:55 +00:00
if ( buttons & WiimoteEmu : : Wiimote : : BUTTON_B )
2011-12-14 12:03:05 +00:00
g_InputDisplay [ controllerID ] . append ( " B " ) ;
2014-03-10 11:30:55 +00:00
if ( buttons & WiimoteEmu : : Wiimote : : BUTTON_PLUS )
2011-12-14 12:03:05 +00:00
g_InputDisplay [ controllerID ] . append ( " + " ) ;
2014-03-10 11:30:55 +00:00
if ( buttons & WiimoteEmu : : Wiimote : : BUTTON_MINUS )
2011-12-14 12:03:05 +00:00
g_InputDisplay [ controllerID ] . append ( " - " ) ;
2014-03-10 11:30:55 +00:00
if ( buttons & WiimoteEmu : : Wiimote : : BUTTON_ONE )
2011-12-14 12:03:05 +00:00
g_InputDisplay [ controllerID ] . append ( " 1 " ) ;
2014-03-10 11:30:55 +00:00
if ( buttons & WiimoteEmu : : Wiimote : : BUTTON_TWO )
2011-12-14 12:03:05 +00:00
g_InputDisplay [ controllerID ] . append ( " 2 " ) ;
2014-03-10 11:30:55 +00:00
if ( buttons & WiimoteEmu : : Wiimote : : BUTTON_HOME )
2011-12-14 12:03:05 +00:00
g_InputDisplay [ controllerID ] . append ( " HOME " ) ;
2011-02-17 09:12:36 +00:00
}
2014-03-10 11:30:55 +00:00
if ( accelData )
2011-02-17 09:12:36 +00:00
{
2011-12-14 12:03:05 +00:00
wm_accel * dt = ( wm_accel * ) accelData ;
2014-07-03 02:42:36 +00:00
std : : string accel = StringFromFormat ( " ACC:%d,%d,%d " , dt - > x , dt - > y , dt - > z ) ;
g_InputDisplay [ controllerID ] . append ( accel ) ;
2011-02-17 09:12:36 +00:00
}
2011-12-14 12:03:05 +00:00
2014-03-10 11:30:55 +00:00
if ( irData ) // incomplete
2011-02-17 09:12:36 +00:00
{
2014-07-03 02:42:36 +00:00
std : : string ir = StringFromFormat ( " IR:%d,%d " , ( ( u8 * ) irData ) [ 0 ] , ( ( u8 * ) irData ) [ 1 ] ) ;
g_InputDisplay [ controllerID ] . append ( ir ) ;
2011-02-17 09:12:36 +00:00
}
g_InputDisplay [ controllerID ] . append ( " \n " ) ;
}
2014-07-11 02:02:32 +00:00
void CheckPadStatus ( GCPadStatus * PadStatus , int controllerID )
2010-06-09 01:37:08 +00:00
{
2014-02-16 20:30:18 +00:00
g_padState . A = ( ( PadStatus - > button & PAD_BUTTON_A ) ! = 0 ) ;
g_padState . B = ( ( PadStatus - > button & PAD_BUTTON_B ) ! = 0 ) ;
g_padState . X = ( ( PadStatus - > button & PAD_BUTTON_X ) ! = 0 ) ;
g_padState . Y = ( ( PadStatus - > button & PAD_BUTTON_Y ) ! = 0 ) ;
g_padState . Z = ( ( PadStatus - > button & PAD_TRIGGER_Z ) ! = 0 ) ;
2010-08-30 07:05:47 +00:00
g_padState . Start = ( ( PadStatus - > button & PAD_BUTTON_START ) ! = 0 ) ;
2012-12-10 11:20:06 +00:00
2014-02-16 20:30:18 +00:00
g_padState . DPadUp = ( ( PadStatus - > button & PAD_BUTTON_UP ) ! = 0 ) ;
2010-08-30 07:05:47 +00:00
g_padState . DPadDown = ( ( PadStatus - > button & PAD_BUTTON_DOWN ) ! = 0 ) ;
g_padState . DPadLeft = ( ( PadStatus - > button & PAD_BUTTON_LEFT ) ! = 0 ) ;
g_padState . DPadRight = ( ( PadStatus - > button & PAD_BUTTON_RIGHT ) ! = 0 ) ;
2012-12-10 11:20:06 +00:00
2011-02-15 23:38:44 +00:00
g_padState . L = ( ( PadStatus - > button & PAD_TRIGGER_L ) ! = 0 ) ;
g_padState . R = ( ( PadStatus - > button & PAD_TRIGGER_R ) ! = 0 ) ;
g_padState . TriggerL = PadStatus - > triggerLeft ;
g_padState . TriggerR = PadStatus - > triggerRight ;
2012-10-20 18:18:42 +00:00
2010-08-30 07:05:47 +00:00
g_padState . AnalogStickX = PadStatus - > stickX ;
g_padState . AnalogStickY = PadStatus - > stickY ;
2012-12-10 11:20:06 +00:00
2010-08-30 07:05:47 +00:00
g_padState . CStickX = PadStatus - > substickX ;
g_padState . CStickY = PadStatus - > substickY ;
2011-12-15 17:22:16 +00:00
2011-02-17 09:12:36 +00:00
SetInputDisplayString ( g_padState , controllerID ) ;
2012-12-10 11:20:06 +00:00
}
2014-07-11 02:02:32 +00:00
void RecordInput ( GCPadStatus * PadStatus , int controllerID )
2012-12-10 11:20:06 +00:00
{
if ( ! IsRecordingInput ( ) | | ! IsUsingPad ( controllerID ) )
return ;
CheckPadStatus ( PadStatus , controllerID ) ;
2012-10-20 18:18:42 +00:00
if ( g_bDiscChange )
{
2012-12-10 11:20:06 +00:00
g_padState . disc = g_bDiscChange ;
2012-10-20 18:18:42 +00:00
g_bDiscChange = false ;
}
2012-12-10 11:20:06 +00:00
2012-12-23 18:37:50 +00:00
EnsureTmpInputSize ( ( size_t ) ( g_currentByte + 8 ) ) ;
2012-12-10 11:20:06 +00:00
memcpy ( & ( tmpInput [ g_currentByte ] ) , & g_padState , 8 ) ;
g_currentByte + = 8 ;
g_totalBytes = g_currentByte ;
2010-06-09 01:37:08 +00:00
}
2014-08-02 19:03:34 +00:00
void CheckWiimoteStatus ( int wiimote , u8 * data , const WiimoteEmu : : ReportFeatures & rptf )
2011-02-11 18:53:51 +00:00
{
2014-03-09 20:14:26 +00: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 ;
2011-12-15 17:22:16 +00:00
u8 size = rptf . size ;
2012-12-10 11:20:06 +00:00
SetWiiInputDisplayString ( wiimote , coreData , accelData , irData ) ;
if ( IsRecordingInput ( ) )
RecordWiimote ( wiimote , data , size ) ;
}
void RecordWiimote ( int wiimote , u8 * data , u8 size )
{
2014-03-10 11:30:55 +00:00
if ( ! IsRecordingInput ( ) | | ! IsUsingWiimote ( wiimote ) )
2012-12-10 11:20:06 +00:00
return ;
2011-12-15 17:22:16 +00:00
2011-12-14 12:03:05 +00:00
InputUpdate ( ) ;
2012-12-23 18:37:50 +00:00
EnsureTmpInputSize ( ( size_t ) ( g_currentByte + size + 1 ) ) ;
2011-12-15 17:22:16 +00:00
tmpInput [ g_currentByte + + ] = size ;
2011-12-14 12:03:05 +00:00
memcpy ( & ( tmpInput [ g_currentByte ] ) , data , size ) ;
g_currentByte + = size ;
g_totalBytes = g_currentByte ;
2011-02-11 18:53:51 +00:00
}
2012-10-20 18:18:42 +00:00
void ReadHeader ( )
{
g_numPads = tmpHeader . numControllers ;
g_recordingStartTime = tmpHeader . recordingStartTime ;
2012-10-21 21:48:45 +00:00
if ( g_rerecords < tmpHeader . numRerecords )
2012-10-20 23:54:38 +00:00
g_rerecords = tmpHeader . numRerecords ;
2012-10-20 18:18:42 +00:00
2012-10-20 23:54:38 +00:00
if ( tmpHeader . bSaveConfig )
{
bSaveConfig = true ;
bSkipIdle = tmpHeader . bSkipIdle ;
bDualCore = tmpHeader . bDualCore ;
bProgressive = tmpHeader . bProgressive ;
bDSPHLE = tmpHeader . bDSPHLE ;
bFastDiscSpeed = tmpHeader . bFastDiscSpeed ;
2012-10-21 21:48:45 +00:00
iCPUCore = tmpHeader . CPUCore ;
2012-10-25 06:44:30 +00:00
g_bClearSave = tmpHeader . bClearSave ;
2014-07-03 01:45:59 +00:00
memcards = tmpHeader . memcards ;
2012-12-19 04:19:15 +00:00
bongos = tmpHeader . bongos ;
2013-06-20 10:08:17 +00:00
bSyncGPU = tmpHeader . bSyncGPU ;
2013-09-03 19:50:41 +00:00
bNetPlay = tmpHeader . bNetPlay ;
2013-09-12 00:19:36 +00:00
memcpy ( revision , tmpHeader . revision , ArraySize ( revision ) ) ;
2012-10-20 23:54:38 +00:00
}
else
{
2012-10-24 20:35:52 +00:00
GetSettings ( ) ;
2012-10-20 23:54:38 +00:00
}
2012-10-20 18:18:42 +00:00
2013-07-01 21:44:42 +00:00
videoBackend = ( char * ) tmpHeader . videoBackend ;
g_discChange = ( char * ) tmpHeader . discChange ;
author = ( char * ) tmpHeader . author ;
2012-11-26 00:26:37 +00:00
memcpy ( MD5 , tmpHeader . md5 , 16 ) ;
2014-04-06 13:28:44 +00:00
DSPiromHash = tmpHeader . DSPiromHash ;
DSPcoefHash = tmpHeader . DSPcoefHash ;
2012-10-20 18:18:42 +00:00
}
2014-03-12 19:33:41 +00:00
bool PlayInput ( const std : : string & filename )
2010-06-09 01:37:08 +00:00
{
2014-03-12 19:33:41 +00:00
if ( g_playMode ! = MODE_NONE )
2010-06-09 01:37:08 +00:00
return false ;
2011-05-03 00:06:44 +00:00
2014-03-10 11:30:55 +00:00
if ( ! File : : Exists ( filename ) )
2010-06-09 01:37:08 +00:00
return false ;
2012-10-21 21:48:45 +00:00
2011-05-03 00:06:44 +00:00
File : : IOFile g_recordfd ;
2012-10-21 21:48:45 +00: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 21:48:45 +00:00
2011-05-03 00:06:44 +00:00
g_recordfd . ReadArray ( & tmpHeader , 1 ) ;
2013-10-29 05:23:17 +00:00
2014-03-10 11:30:55 +00:00
if ( tmpHeader . filetype [ 0 ] ! = ' D ' | |
tmpHeader . filetype [ 1 ] ! = ' T ' | |
tmpHeader . filetype [ 2 ] ! = ' M ' | |
tmpHeader . filetype [ 3 ] ! = 0x1A ) {
2011-01-13 02:05:58 +00:00
PanicAlertT ( " Invalid recording file " ) ;
2010-06-09 01:37:08 +00:00
goto cleanup ;
}
2012-10-21 21:48:45 +00:00
2012-10-20 18:18:42 +00:00
ReadHeader ( ) ;
2011-12-14 12:03:05 +00:00
g_totalFrames = tmpHeader . frameCount ;
g_totalLagCount = tmpHeader . lagCount ;
g_totalInputCount = tmpHeader . inputCount ;
2014-06-18 20:13:46 +00:00
g_totalTickCount = tmpHeader . tickCount ;
2011-12-15 17:22:16 +00:00
g_currentFrame = 0 ;
g_currentLagCount = 0 ;
g_currentInputCount = 0 ;
2010-06-09 01:37:08 +00:00
g_playMode = MODE_PLAYING ;
2013-10-29 05:23:17 +00:00
2011-12-14 12:03:05 +00:00
g_totalBytes = g_recordfd . GetSize ( ) - 256 ;
2012-11-26 03:41:48 +00:00
EnsureTmpInputSize ( ( size_t ) g_totalBytes ) ;
2011-12-14 12:03:05 +00:00
g_recordfd . ReadArray ( tmpInput , ( size_t ) g_totalBytes ) ;
g_currentByte = 0 ;
2011-05-03 00:06:44 +00:00
g_recordfd . Close ( ) ;
2012-10-21 21:48:45 +00:00
2013-01-19 20:02:02 +00:00
// Load savestate (and skip to frame data)
2014-03-10 11:30:55 +00:00
if ( tmpHeader . bFromSaveState )
2013-01-19 20:02:02 +00:00
{
2014-03-12 19:33:41 +00:00
const std : : string stateFilename = filename + " .sav " ;
2014-03-10 11:30:55 +00:00
if ( File : : Exists ( stateFilename ) )
2013-01-19 20:02:02 +00:00
Core : : SetStateFileName ( stateFilename ) ;
g_bRecordingFromSaveState = true ;
Movie : : LoadInput ( filename ) ;
}
2010-06-09 01:37:08 +00:00
return true ;
2012-10-21 21:48:45 +00:00
2010-06-09 01:37:08 +00:00
cleanup :
2011-03-11 10:21:46 +00:00
g_recordfd . Close ( ) ;
2010-06-09 01:37:08 +00:00
return false ;
}
2011-12-28 08:33:41 +00:00
void DoState ( PointerWrap & p )
2011-12-14 12:03:05 +00:00
{
static const int MOVIE_STATE_VERSION = 1 ;
g_currentSaveVersion = MOVIE_STATE_VERSION ;
p . Do ( g_currentSaveVersion ) ;
// 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 ) ;
p . Do ( g_currentByte ) ;
p . Do ( g_currentLagCount ) ;
p . Do ( g_currentInputCount ) ;
p . Do ( g_bPolled ) ;
2014-06-18 20:13:46 +00:00
p . Do ( g_tickCountAtLastInput ) ;
2011-12-14 12:03:05 +00:00
// other variables (such as g_totalBytes and g_totalFrames) are set in LoadInput
}
2014-03-12 19:33:41 +00:00
void LoadInput ( const std : : string & filename )
2010-08-30 07:05:47 +00:00
{
2013-01-19 20:02:02 +00:00
File : : IOFile t_record ;
if ( ! t_record . Open ( filename , " r+b " ) )
{
2014-03-12 19:33:41 +00:00
PanicAlertT ( " Failed to read %s " , filename . c_str ( ) ) ;
2013-01-19 20:02:02 +00:00
EndPlayInput ( false ) ;
return ;
}
2012-10-20 18:18:42 +00:00
2011-05-03 00:06:44 +00:00
t_record . ReadArray ( & tmpHeader , 1 ) ;
2012-10-21 21:48:45 +00:00
2014-03-10 11:30:55 +00:00
if ( tmpHeader . filetype [ 0 ] ! = ' D ' | | tmpHeader . filetype [ 1 ] ! = ' T ' | | tmpHeader . filetype [ 2 ] ! = ' M ' | | tmpHeader . filetype [ 3 ] ! = 0x1A )
2011-02-15 09:07:55 +00:00
{
2014-03-12 19:33:41 +00: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 18:18:42 +00:00
ReadHeader ( ) ;
2011-07-11 20:15:05 +00:00
if ( ! g_bReadOnly )
2011-12-18 07:27:11 +00:00
{
2012-11-12 16:39:33 +00:00
g_rerecords + + ;
tmpHeader . numRerecords = g_rerecords ;
2011-12-18 07:27:11 +00:00
t_record . Seek ( 0 , SEEK_SET ) ;
t_record . WriteArray ( & tmpHeader , 1 ) ;
}
2012-10-20 18:18:42 +00:00
2011-02-12 00:06:58 +00:00
ChangePads ( true ) ;
if ( Core : : g_CoreStartupParameter . bWii )
2011-07-11 20:15:05 +00:00
ChangeWiiPads ( true ) ;
2011-05-03 00:06:44 +00:00
2011-12-18 07:27:11 +00:00
u64 totalSavedBytes = t_record . GetSize ( ) - 256 ;
bool afterEnd = false ;
2014-07-05 23:48:44 +00:00
// This can only happen if the user manually deletes data from the dtm.
2011-12-18 07:27:11 +00:00
if ( g_currentByte > totalSavedBytes )
{
2014-07-05 23:48:44 +00: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 ) g_currentByte + 256 , ( u32 ) tmpHeader . frameCount , ( u32 ) g_currentFrame ) ;
2011-12-18 07:27:11 +00:00
afterEnd = true ;
}
2014-03-09 20:14:26 +00:00
if ( ! g_bReadOnly | | tmpInput = = nullptr )
2011-12-14 12:03:05 +00:00
{
g_totalFrames = tmpHeader . frameCount ;
g_totalLagCount = tmpHeader . lagCount ;
g_totalInputCount = tmpHeader . inputCount ;
2014-07-25 00:49:43 +00:00
g_totalTickCount = g_tickCountAtLastInput = tmpHeader . tickCount ;
2011-12-14 12:03:05 +00:00
2012-11-26 03:41:48 +00:00
EnsureTmpInputSize ( ( size_t ) totalSavedBytes ) ;
2011-12-18 07:27:11 +00:00
g_totalBytes = totalSavedBytes ;
2011-12-14 12:03:05 +00:00
t_record . ReadArray ( tmpInput , ( size_t ) g_totalBytes ) ;
}
else if ( g_currentByte > 0 )
{
2011-12-18 07:27:11 +00:00
if ( g_currentByte > totalSavedBytes )
2011-12-14 12:03:05 +00:00
{
}
2011-12-18 07:27:11 +00:00
else if ( g_currentByte > g_totalBytes )
2011-12-14 12:03:05 +00:00
{
2014-07-05 23:48:44 +00:00
afterEnd = true ;
2011-12-18 07:27:11 +00: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 ) g_currentByte + 256 , ( u32 ) g_totalBytes + 256 , ( u32 ) g_currentFrame , ( u32 ) g_totalFrames ) ;
}
2014-03-10 11:30:55 +00:00
else if ( g_currentByte > 0 & & g_totalBytes > 0 )
2011-12-18 07:27:11 +00:00
{
2011-12-26 11:09:30 +00:00
// verify identical from movie start to the save's current frame
2011-12-14 12:03:05 +00:00
u32 len = ( u32 ) g_currentByte ;
2011-12-18 07:27:11 +00:00
u8 * movInput = new u8 [ len ] ;
t_record . ReadArray ( movInput , ( size_t ) len ) ;
2011-12-14 12:03:05 +00:00
for ( u32 i = 0 ; i < len ; + + i )
{
2011-12-18 07:27:11 +00:00
if ( movInput [ i ] ! = tmpInput [ i ] )
2011-12-14 12:03:05 +00:00
{
2011-12-18 07:27:11 +00: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-10 11:30:55 +00:00
if ( IsUsingWiimote ( 0 ) )
2013-10-29 05:23:17 +00:00
{
2011-12-18 07:27:11 +00: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 ) ;
2013-06-21 05:53:50 +00:00
memcpy ( tmpInput , movInput , g_currentByte ) ;
2011-12-18 07:27:11 +00:00
}
2011-12-14 12:03:05 +00:00
else
2011-12-18 07:27:11 +00:00
{
int frame = i / 8 ;
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 05:53:50 +00:00
memcpy ( tmpInput , movInput , g_currentByte ) ;
2011-12-18 07:27:11 +00:00
}
2011-12-14 12:03:05 +00:00
break ;
}
}
2012-03-24 17:41:13 +00:00
delete [ ] movInput ;
2011-12-14 12:03:05 +00:00
}
}
2011-05-03 00:06:44 +00:00
t_record . Close ( ) ;
2011-12-14 12:03:05 +00:00
2012-10-20 23:54:38 +00:00
bSaveConfig = tmpHeader . bSaveConfig ;
2011-07-11 20:15:05 +00:00
2011-12-18 07:27:11 +00:00
if ( ! afterEnd )
2011-07-11 20:15:05 +00:00
{
2011-12-18 07:27:11 +00:00
if ( g_bReadOnly )
2011-12-14 12:03:05 +00:00
{
2014-03-10 11:30:55 +00:00
if ( g_playMode ! = MODE_PLAYING )
2011-12-18 07:27:11 +00:00
{
g_playMode = MODE_PLAYING ;
Core : : DisplayMessage ( " Switched to playback " , 2000 ) ;
}
}
else
{
2014-03-10 11:30:55 +00:00
if ( g_playMode ! = MODE_RECORDING )
2011-12-18 07:27:11 +00:00
{
g_playMode = MODE_RECORDING ;
Core : : DisplayMessage ( " Switched to recording " , 2000 ) ;
}
2011-12-14 12:03:05 +00:00
}
2011-07-11 20:15:05 +00:00
}
else
{
2011-12-18 07:27:11 +00:00
EndPlayInput ( false ) ;
2011-12-14 12:03:05 +00:00
}
}
static void CheckInputEnd ( )
{
2014-06-20 20:56:00 +00:00
if ( g_currentFrame > g_totalFrames | | g_currentByte > = g_totalBytes | | ( CoreTiming : : GetTicks ( ) > g_totalTickCount & & ! IsRecordingInputFromSaveState ( ) ) )
2011-12-14 12:03:05 +00:00
{
EndPlayInput ( ! g_bReadOnly ) ;
2011-07-11 20:15:05 +00:00
}
2010-08-30 07:05:47 +00:00
}
2014-07-11 02:02:32 +00:00
void PlayController ( GCPadStatus * PadStatus , int controllerID )
2010-06-09 01:37:08 +00:00
{
2012-10-21 21:48:45 +00:00
// Correct playback is entirely dependent on the emulator polling the controllers
// in the same order done during recording
2014-03-09 20:14:26 +00:00
if ( ! IsPlayingInput ( ) | | ! IsUsingPad ( controllerID ) | | tmpInput = = nullptr )
2012-10-21 21:48:45 +00:00
return ;
2011-12-14 12:03:05 +00:00
if ( g_currentByte + 8 > g_totalBytes )
2011-03-11 10:21:46 +00:00
{
2011-12-14 12:03:05 +00:00
PanicAlertT ( " Premature movie end in PlayController. %u + 8 > %u " , ( u32 ) g_currentByte , ( u32 ) g_totalBytes ) ;
2011-03-11 10:21:46 +00:00
EndPlayInput ( ! g_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 16:06:32 +00: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-11 02:02:32 +00:00
memset ( PadStatus , 0 , sizeof ( GCPadStatus ) ) ;
2011-06-24 06:50:50 +00:00
PadStatus - > err = e ;
2011-12-15 17:22:16 +00:00
2011-12-14 12:03:05 +00:00
memcpy ( & g_padState , & ( tmpInput [ g_currentByte ] ) , 8 ) ;
g_currentByte + = 8 ;
2013-10-29 05:23:17 +00:00
2011-02-17 09:12:36 +00:00
PadStatus - > triggerLeft = g_padState . TriggerL ;
PadStatus - > triggerRight = g_padState . TriggerR ;
PadStatus - > stickX = g_padState . AnalogStickX ;
PadStatus - > stickY = g_padState . AnalogStickY ;
PadStatus - > substickX = g_padState . CStickX ;
PadStatus - > substickY = g_padState . CStickY ;
2010-06-09 01:37:08 +00:00
PadStatus - > button | = PAD_USE_ORIGIN ;
2013-10-29 05:23:17 +00:00
2014-03-10 11:30:55 +00:00
if ( g_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-03-10 11:30:55 +00:00
if ( g_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-03-10 11:30:55 +00:00
if ( g_padState . X )
2010-06-09 01:37:08 +00:00
PadStatus - > button | = PAD_BUTTON_X ;
2014-03-10 11:30:55 +00:00
if ( g_padState . Y )
2010-06-09 01:37:08 +00:00
PadStatus - > button | = PAD_BUTTON_Y ;
2014-03-10 11:30:55 +00:00
if ( g_padState . Z )
2010-06-09 01:37:08 +00:00
PadStatus - > button | = PAD_TRIGGER_Z ;
2014-03-10 11:30:55 +00:00
if ( g_padState . Start )
2010-06-09 01:37:08 +00:00
PadStatus - > button | = PAD_BUTTON_START ;
2013-10-29 05:23:17 +00:00
2014-03-10 11:30:55 +00:00
if ( g_padState . DPadUp )
2010-06-09 01:37:08 +00:00
PadStatus - > button | = PAD_BUTTON_UP ;
2014-03-10 11:30:55 +00:00
if ( g_padState . DPadDown )
2010-06-09 01:37:08 +00:00
PadStatus - > button | = PAD_BUTTON_DOWN ;
2014-03-10 11:30:55 +00:00
if ( g_padState . DPadLeft )
2010-06-09 01:37:08 +00:00
PadStatus - > button | = PAD_BUTTON_LEFT ;
2014-03-10 11:30:55 +00:00
if ( g_padState . DPadRight )
2010-06-09 01:37:08 +00:00
PadStatus - > button | = PAD_BUTTON_RIGHT ;
2013-10-29 05:23:17 +00:00
2014-03-10 11:30:55 +00:00
if ( g_padState . L )
2010-06-09 01:37:08 +00:00
PadStatus - > button | = PAD_TRIGGER_L ;
2014-03-10 11:30:55 +00:00
if ( g_padState . R )
2010-06-09 01:37:08 +00:00
PadStatus - > button | = PAD_TRIGGER_R ;
2012-10-20 18:18:42 +00:00
if ( g_padState . disc )
{
// 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 ) ;
int numPaths = ( int ) SConfig : : GetInstance ( ) . m_ISOFolder . size ( ) ;
bool found = false ;
std : : string path ;
for ( int i = 0 ; i < numPaths ; i + + )
{
path = SConfig : : GetInstance ( ) . m_ISOFolder [ i ] ;
2014-03-12 19:33:41 +00:00
if ( File : : Exists ( path + ' / ' + g_discChange ) )
2012-10-20 18:18:42 +00:00
{
found = true ;
break ;
}
}
if ( found )
{
2014-03-12 19:33:41 +00:00
DVDInterface : : ChangeDisc ( path + ' / ' + g_discChange ) ;
2012-10-20 18:18:42 +00:00
Core : : SetState ( Core : : CORE_RUN ) ;
}
else
{
PanicAlert ( " Change the disc to %s " , g_discChange . c_str ( ) ) ;
}
}
2011-02-17 09:12:36 +00:00
SetInputDisplayString ( g_padState , controllerID ) ;
2011-12-14 12:03:05 +00:00
CheckInputEnd ( ) ;
2010-06-09 01:37:08 +00:00
}
2014-08-02 19:03:34 +00:00
bool PlayWiimote ( int wiimote , u8 * data , const WiimoteEmu : : ReportFeatures & rptf )
2011-02-11 18:53:51 +00:00
{
2014-03-10 11:30:55 +00:00
if ( ! IsPlayingInput ( ) | | ! IsUsingWiimote ( wiimote ) | | tmpInput = = nullptr )
2011-02-12 08:25:09 +00:00
return false ;
2011-12-15 17:22:16 +00:00
2011-12-14 12:03:05 +00:00
if ( g_currentByte > g_totalBytes )
2011-05-03 00:06:44 +00:00
{
2011-12-14 12:03:05 +00:00
PanicAlertT ( " Premature movie end in PlayWiimote. %u > %u " , ( u32 ) g_currentByte , ( u32 ) g_totalBytes ) ;
2011-05-03 00:06:44 +00:00
EndPlayInput ( ! g_bReadOnly ) ;
return false ;
}
2014-03-09 20:14:26 +00: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 ;
2011-12-15 17:22:16 +00:00
u8 size = rptf . size ;
u8 sizeInMovie = tmpInput [ g_currentByte ] ;
if ( size ! = sizeInMovie )
2011-05-03 00:06:44 +00:00
{
2013-09-19 01:51:37 +00:00
PanicAlertT ( " Fatal desync. Aborting playback. (Error in PlayWiimote: %u != %u, byte %u.)%s " , ( u32 ) sizeInMovie , ( u32 ) size , ( u32 ) g_currentByte ,
2014-06-07 02:30:39 +00:00
( g_numPads & 0xF ) ? " Try re-creating the recording with all GameCube controllers disabled (in Configure > GameCube > Device Settings). " : " " ) ;
2011-12-15 17:22:16 +00:00
EndPlayInput ( ! g_bReadOnly ) ;
return false ;
}
g_currentByte + + ;
if ( g_currentByte + size > g_totalBytes )
{
PanicAlertT ( " Premature movie end in PlayWiimote. %u + %d > %u " , ( u32 ) g_currentByte , size , ( u32 ) g_totalBytes ) ;
2011-05-03 00:06:44 +00:00
EndPlayInput ( ! g_bReadOnly ) ;
return false ;
}
2013-10-29 05:23:17 +00:00
2011-12-15 17:22:16 +00:00
memcpy ( data , & ( tmpInput [ g_currentByte ] ) , size ) ;
g_currentByte + = size ;
2013-10-29 05:23:17 +00:00
2011-12-14 12:03:05 +00:00
SetWiiInputDisplayString ( wiimote , coreData , accelData , irData ) ;
g_currentInputCount + + ;
2013-10-29 05:23:17 +00:00
2011-12-14 12:03:05 +00:00
CheckInputEnd ( ) ;
2011-02-12 08:25:09 +00:00
return true ;
2011-02-11 18:53:51 +00:00
}
2013-10-29 05:23:17 +00: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
g_playMode = MODE_RECORDING ;
2011-12-18 07:27:11 +00:00
Core : : DisplayMessage ( " Reached movie end. Resuming recording. " , 2000 ) ;
2011-02-12 02:14:20 +00:00
}
2014-03-10 11:30:55 +00:00
else if ( g_playMode ! = MODE_NONE )
2011-02-12 02:14:20 +00:00
{
2012-12-10 11:20:06 +00:00
g_rerecords = 0 ;
2011-12-18 07:27:11 +00:00
g_currentByte = 0 ;
2011-02-12 02:14:20 +00:00
g_playMode = MODE_NONE ;
2011-12-18 07:27:11 +00:00
Core : : DisplayMessage ( " Movie End. " , 2000 ) ;
2013-07-01 21:44:42 +00:00
g_bRecordingFromSaveState = false ;
2011-12-18 07:27:11 +00:00
// we don't clear these things because otherwise we can't resume playback if we load a movie state later
//g_totalFrames = g_totalBytes = 0;
//delete tmpInput;
2014-03-09 20:14:26 +00:00
//tmpInput = nullptr;
2011-02-12 02:14:20 +00:00
}
2010-06-09 01:37:08 +00:00
}
2014-03-12 19:33:41 +00: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 05:23:17 +00: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 ;
strncpy ( ( char * ) header . gameID , Core : : g_CoreStartupParameter . GetUniqueID ( ) . c_str ( ) , 6 ) ;
header . bWii = Core : : g_CoreStartupParameter . bWii ;
header . numControllers = g_numPads & ( Core : : g_CoreStartupParameter . bWii ? 0xFF : 0x0F ) ;
2013-10-29 05:23:17 +00:00
2011-07-11 20:15:05 +00:00
header . bFromSaveState = g_bRecordingFromSaveState ;
2011-12-14 12:03:05 +00:00
header . frameCount = g_totalFrames ;
header . lagCount = g_totalLagCount ;
header . inputCount = g_totalInputCount ;
2011-07-11 20:15:05 +00:00
header . numRerecords = g_rerecords ;
2011-12-26 11:09:30 +00:00
header . recordingStartTime = g_recordingStartTime ;
2012-10-18 08:18:40 +00:00
header . bSaveConfig = true ;
2012-10-20 23:54:38 +00:00
header . bSkipIdle = bSkipIdle ;
header . bDualCore = bDualCore ;
header . bProgressive = bProgressive ;
header . bDSPHLE = bDSPHLE ;
header . bFastDiscSpeed = bFastDiscSpeed ;
2013-09-12 00:19:36 +00:00
strncpy ( ( char * ) header . videoBackend , videoBackend . c_str ( ) , ArraySize ( header . videoBackend ) ) ;
2012-10-21 21:48:45 +00:00
header . CPUCore = iCPUCore ;
2012-10-18 08:18:40 +00:00
header . bEFBAccessEnable = g_ActiveConfig . bEFBAccessEnable ;
header . bEFBCopyEnable = g_ActiveConfig . bEFBCopyEnable ;
header . bCopyEFBToTexture = g_ActiveConfig . bCopyEFBToTexture ;
header . bEFBCopyCacheEnable = g_ActiveConfig . bEFBCopyCacheEnable ;
header . bEFBEmulateFormatChanges = g_ActiveConfig . bEFBEmulateFormatChanges ;
header . bUseXFB = g_ActiveConfig . bUseXFB ;
header . bUseRealXFB = g_ActiveConfig . bUseRealXFB ;
2014-07-03 01:45:59 +00:00
header . memcards = memcards ;
2012-10-25 06:44:30 +00:00
header . bClearSave = g_bClearSave ;
2013-06-20 10:08:17 +00:00
header . bSyncGPU = bSyncGPU ;
2013-09-03 19:50:41 +00:00
header . bNetPlay = bNetPlay ;
2013-09-12 00:19:36 +00:00
strncpy ( ( char * ) header . discChange , g_discChange . c_str ( ) , ArraySize ( header . discChange ) ) ;
strncpy ( ( char * ) header . author , author . c_str ( ) , ArraySize ( header . author ) ) ;
2012-11-26 00:26:37 +00:00
memcpy ( header . md5 , MD5 , 16 ) ;
2012-12-19 04:19:15 +00:00
header . bongos = bongos ;
2013-09-12 00:19:36 +00:00
memcpy ( header . revision , revision , ArraySize ( header . revision ) ) ;
2014-04-06 13:28:44 +00:00
header . DSPiromHash = DSPiromHash ;
header . DSPcoefHash = DSPcoefHash ;
2014-06-18 20:13:46 +00:00
header . tickCount = g_totalTickCount ;
2012-10-18 08:18:40 +00:00
2011-07-11 20:15:05 +00:00
// TODO
2013-10-29 05:23:17 +00:00
header . uniqueID = 0 ;
2011-07-11 20:15:05 +00:00
// header.audioEmulator;
2012-10-20 23:54:38 +00:00
2011-07-11 20:15:05 +00:00
save_record . WriteArray ( & header , 1 ) ;
2011-02-12 02:14:20 +00:00
2011-12-14 12:03:05 +00:00
bool success = save_record . WriteArray ( tmpInput , ( size_t ) g_totalBytes ) ;
2011-02-12 02:14:20 +00:00
2011-02-15 09:07:55 +00:00
if ( success & & g_bRecordingFromSaveState )
{
2014-03-12 19:33:41 +00:00
std : : string stateFilename = filename + " .sav " ;
2011-03-01 03:06:14 +00:00
success = File : : Copy ( tmpStateFilename , stateFilename ) ;
2011-02-15 09:07:55 +00:00
}
2013-10-29 05:23:17 +00:00
2011-02-12 02:14:20 +00:00
if ( success )
2014-03-12 19:33:41 +00:00
Core : : DisplayMessage ( StringFromFormat ( " DTM %s saved " , filename . c_str ( ) ) , 2000 ) ;
2010-08-30 07:05:47 +00:00
else
2014-03-12 19:33:41 +00: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
void SetInputManip ( ManipFunction func )
{
mfunc = func ;
}
2014-07-11 02:02:32 +00:00
void CallInputManip ( GCPadStatus * PadStatus , int controllerID )
2011-06-24 06:50:50 +00:00
{
if ( mfunc )
( * mfunc ) ( PadStatus , controllerID ) ;
}
2012-10-18 08:18:40 +00:00
void SetGraphicsConfig ( )
{
g_Config . bEFBAccessEnable = tmpHeader . bEFBAccessEnable ;
g_Config . bEFBCopyEnable = tmpHeader . bEFBCopyEnable ;
g_Config . bCopyEFBToTexture = tmpHeader . bCopyEFBToTexture ;
g_Config . bEFBCopyCacheEnable = tmpHeader . bEFBCopyCacheEnable ;
g_Config . bEFBEmulateFormatChanges = tmpHeader . bEFBEmulateFormatChanges ;
g_Config . bUseXFB = tmpHeader . bUseXFB ;
g_Config . bUseRealXFB = tmpHeader . bUseRealXFB ;
}
2012-10-24 20:35:52 +00:00
void GetSettings ( )
{
2012-10-24 23:37:51 +00:00
bSaveConfig = true ;
2012-10-24 20:35:52 +00:00
bSkipIdle = SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . bSkipIdle ;
bDualCore = SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . bCPUThread ;
bProgressive = SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . bProgressive ;
bDSPHLE = SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . bDSPHLE ;
bFastDiscSpeed = SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . bFastDiscSpeed ;
2013-06-11 01:04:55 +00:00
videoBackend = g_video_backend - > GetName ( ) ;
2013-06-20 10:08:17 +00:00
bSyncGPU = SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . bSyncGPU ;
2012-10-24 20:35:52 +00:00
iCPUCore = SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . iCPUCore ;
2013-09-03 19:50:41 +00:00
bNetPlay = NetPlay : : IsNetPlayRunning ( ) ;
2012-10-25 06:44:30 +00:00
if ( ! Core : : g_CoreStartupParameter . bWii )
g_bClearSave = ! File : : Exists ( SConfig : : GetInstance ( ) . m_strMemoryCardA ) ;
2014-07-03 01:45:59 +00:00
memcards | = ( SConfig : : GetInstance ( ) . m_EXIDevice [ 0 ] = = EXIDEVICE_MEMORYCARD ) < < 0 ;
memcards | = ( SConfig : : GetInstance ( ) . m_EXIDevice [ 1 ] = = EXIDEVICE_MEMORYCARD ) < < 1 ;
2014-04-23 21:51:59 +00:00
unsigned int tmp ;
2013-06-24 13:14:22 +00:00
for ( int i = 0 ; i < 20 ; + + i )
2013-01-19 20:02:02 +00:00
{
2014-04-23 21:51:59 +00:00
sscanf ( & scm_rev_git_str [ 2 * i ] , " %02x " , & tmp ) ;
revision [ i ] = tmp ;
2013-01-19 20:02:02 +00:00
}
2014-04-06 13:28:44 +00:00
if ( ! bDSPHLE )
{
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 ( ) ;
for ( int i = 0 ; i < DSP_IROM_SIZE ; i + + )
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 ( ) ;
for ( int i = 0 ; i < DSP_COEF_SIZE ; i + + )
coef [ i ] = Common : : swap16 ( coef [ i ] ) ;
DSPiromHash = HashAdler32 ( ( u8 * ) irom . data ( ) , DSP_IROM_BYTE_SIZE ) ;
DSPcoefHash = HashAdler32 ( ( u8 * ) coef . data ( ) , DSP_COEF_BYTE_SIZE ) ;
}
else
{
DSPiromHash = 0 ;
DSPcoefHash = 0 ;
}
2012-10-24 20:35:52 +00:00
}
2012-11-24 03:23:58 +00:00
void CheckMD5 ( )
{
2012-11-26 00:26:37 +00:00
for ( int i = 0 , n = 0 ; i < 16 ; i + + )
2012-11-24 03:23:58 +00:00
{
2012-11-26 00:26:37 +00:00
if ( tmpHeader . md5 [ i ] ! = 0 )
continue ;
n + + ;
if ( n = = 16 )
return ;
2012-11-24 03:23:58 +00:00
}
2012-11-26 00:26:37 +00:00
Core : : DisplayMessage ( " Verifying checksum... " , 2000 ) ;
2012-11-24 03:23:58 +00:00
unsigned char gameMD5 [ 16 ] ;
char game [ 255 ] ;
2012-11-26 17:29:36 +00:00
memcpy ( game , SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . m_strFilename . c_str ( ) , SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . m_strFilename . size ( ) ) ;
2012-11-24 03:23:58 +00:00
md5_file ( game , gameMD5 ) ;
2012-11-26 00:26:37 +00:00
if ( memcmp ( gameMD5 , MD5 , 16 ) = = 0 )
Core : : DisplayMessage ( " Checksum of current game matches the recorded game. " , 2000 ) ;
2012-11-24 03:23:58 +00:00
else
2013-12-23 12:18:28 +00:00
Core : : DisplayMessage ( " Checksum of current game does not match the recorded game! " , 3000 ) ;
2012-11-26 00:26:37 +00:00
}
void GetMD5 ( )
{
Core : : DisplayMessage ( " Calculating checksum of game file... " , 2000 ) ;
2013-07-01 21:44:42 +00:00
memset ( MD5 , 0 , sizeof ( MD5 ) ) ;
2012-11-26 00:26:37 +00:00
char game [ 255 ] ;
2012-11-26 17:29:36 +00:00
memcpy ( game , SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . m_strFilename . c_str ( ) , SConfig : : GetInstance ( ) . m_LocalCoreStartupParameter . m_strFilename . size ( ) ) ;
2012-11-26 00:26:37 +00:00
md5_file ( game , MD5 ) ;
Core : : DisplayMessage ( " Finished calculating checksum. " , 2000 ) ;
2012-11-24 03:23:58 +00:00
}
2012-11-24 23:27:20 +00:00
void Shutdown ( )
{
2014-06-18 20:13:46 +00:00
g_currentInputCount = g_totalInputCount = g_totalFrames = g_totalBytes = g_tickCountAtLastInput = 0 ;
2012-11-24 23:27:20 +00:00
delete [ ] tmpInput ;
2014-03-09 20:14:26 +00:00
tmpInput = nullptr ;
2012-11-26 03:41:48 +00:00
tmpInputAllocated = 0 ;
2012-11-24 23:27:20 +00:00
}
2012-11-26 03:41:48 +00:00
} ;