Bug Summary

File:Source/Core/Core/Src/Movie.cpp
Location:line 654, column 2
Description:Null pointer passed as an argument to a 'nonnull' parameter

Annotated Source Code

1// Copyright 2013 Dolphin Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include "Movie.h"
6
7#include "Core.h"
8#include "ConfigManager.h"
9#include "Thread.h"
10#include "FileUtil.h"
11#include "PowerPC/PowerPC.h"
12#include "HW/SI.h"
13#include "HW/Wiimote.h"
14#include "HW/WiimoteEmu/WiimoteEmu.h"
15#include "HW/WiimoteEmu/WiimoteHid.h"
16#include "IPC_HLE/WII_IPC_HLE_Device_usb.h"
17#include "State.h"
18#include "Timer.h"
19#include "VideoConfig.h"
20#include "HW/EXI.h"
21#include "HW/EXI_Device.h"
22#include "HW/EXI_Channel.h"
23#include "HW/DVDInterface.h"
24#include "../../Common/Src/NandPaths.h"
25#include "Crypto/md5.h"
26#include "scmrev.h"
27
28// The chunk to allocate movie data in multiples of.
29#define DTM_BASE_LENGTH(1024) (1024)
30
31std::mutex cs_frameSkip;
32
33namespace Movie {
34
35bool g_bFrameStep = false;
36bool g_bFrameStop = false;
37bool g_bReadOnly = true;
38u32 g_rerecords = 0;
39PlayMode g_playMode = MODE_NONE;
40
41u32 g_framesToSkip = 0, g_frameSkipCounter = 0;
42
43u8 g_numPads = 0;
44ControllerState g_padState;
45DTMHeader tmpHeader;
46u8* tmpInput = NULL__null;
47size_t tmpInputAllocated = 0;
48u64 g_currentByte = 0, g_totalBytes = 0;
49u64 g_currentFrame = 0, g_totalFrames = 0; // VI
50u64 g_currentLagCount = 0, g_totalLagCount = 0; // just stats
51u64 g_currentInputCount = 0, g_totalInputCount = 0; // just stats
52u64 g_recordingStartTime; // seconds since 1970 that recording started
53bool bSaveConfig, bSkipIdle, bDualCore, bProgressive, bDSPHLE, bFastDiscSpeed = false;
54bool bMemcard, g_bClearSave = false;
55std::string videoBackend = "unknown";
56int iCPUCore = 1;
57bool g_bDiscChange = false;
58std::string g_discChange = "";
59std::string author = "";
60u64 g_titleID = 0;
61unsigned char MD5[16];
62u8 bongos;
63u8 revision[20];
64
65bool g_bRecordingFromSaveState = false;
66bool g_bPolled = false;
67int g_currentSaveVersion = 0;
68
69std::string tmpStateFilename = "dtm.sav";
70
71std::string g_InputDisplay[8];
72
73ManipFunction mfunc = NULL__null;
74
75void EnsureTmpInputSize(size_t bound)
76{
77 if (tmpInputAllocated >= bound)
78 return;
79 // The buffer expands in powers of two of DTM_BASE_LENGTH
80 // (standard exponential buffer growth).
81 size_t newAlloc = DTM_BASE_LENGTH(1024);
82 while (newAlloc < bound)
83 newAlloc *= 2;
84
85 u8* newTmpInput = new u8[newAlloc];
86 tmpInputAllocated = newAlloc;
87 if (tmpInput != NULL__null)
88 {
89 if (g_totalBytes > 0)
90 memcpy(newTmpInput, tmpInput, (size_t)g_totalBytes);
91 delete[] tmpInput;
92 }
93 tmpInput = newTmpInput;
94}
95
96std::string GetInputDisplay()
97{
98 if (!IsPlayingInput() && !IsRecordingInput())
99 {
100 g_numPads = 0;
101 for (int i = 0; i < 4; i++)
102 {
103 if (SConfig::GetInstance().m_SIDevice[i] == SIDEVICE_GC_CONTROLLER || SConfig::GetInstance().m_SIDevice[i] == SIDEVICE_GC_TARUKONGA)
104 g_numPads |= (1 << i);
105 if (g_wiimote_sources[i] != WIIMOTE_SRC_NONE)
106 g_numPads |= (1 << (i + 4));
107 }
108 }
109
110 std::string inputDisplay = "";
111 for (int i = 0; i < 8; ++i)
112 if ((g_numPads & (1 << i)) != 0)
113 inputDisplay.append(g_InputDisplay[i]);
114
115 return inputDisplay;
116}
117
118void FrameUpdate()
119{
120 g_currentFrame++;
121 if(!g_bPolled)
122 g_currentLagCount++;
123
124 if (IsRecordingInput())
125 {
126 g_totalFrames = g_currentFrame;
127 g_totalLagCount = g_currentLagCount;
128 }
129 if (g_bFrameStep)
130 {
131 Core::SetState(Core::CORE_PAUSE);
132 g_bFrameStep = false;
133 }
134
135 // ("framestop") the only purpose of this is to cause interpreter/jit Run() to return temporarily.
136 // after that we set it back to CPU_RUNNING and continue as normal.
137 if (g_bFrameStop)
138 *PowerPC::GetStatePtr() = PowerPC::CPU_STEPPING;
139
140 if(g_framesToSkip)
141 FrameSkipping();
142
143 g_bPolled = false;
144}
145
146// called when game is booting up, even if no movie is active,
147// but potentially after BeginRecordingInput or PlayInput has been called.
148void Init()
149{
150 g_bPolled = false;
151 g_bFrameStep = false;
152 g_bFrameStop = false;
153 bSaveConfig = false;
154 iCPUCore = SConfig::GetInstance().m_LocalCoreStartupParameter.iCPUCore;
155 if (IsPlayingInput())
156 {
157 ReadHeader();
158 std::thread md5thread(CheckMD5);
159 if ((strncmp((char *)tmpHeader.gameID, Core::g_CoreStartupParameter.GetUniqueID().c_str(), 6)))
160 {
161 PanicAlert("The recorded game (%s) is not the same as the selected game (%s)", tmpHeader.gameID, Core::g_CoreStartupParameter.GetUniqueID().c_str())MsgAlert(false, WARNING, "The recorded game (%s) is not the same as the selected game (%s)"
, tmpHeader.gameID, Core::g_CoreStartupParameter.GetUniqueID(
).c_str())
;
162 EndPlayInput(false);
163 }
164 }
165
166 if (IsRecordingInput())
167 {
168 GetSettings();
169 std::thread md5thread(GetMD5);
170 }
171
172 g_frameSkipCounter = g_framesToSkip;
173 memset(&g_padState, 0, sizeof(g_padState));
174 if (!tmpHeader.bFromSaveState || !IsPlayingInput())
175 Core::SetStateFileName("");
176
177 for (int i = 0; i < 8; ++i)
178 g_InputDisplay[i].clear();
179
180 if (!IsPlayingInput() && !IsRecordingInput())
181 {
182 g_bRecordingFromSaveState = false;
183 g_rerecords = 0;
184 g_currentByte = 0;
185 g_currentFrame = 0;
186 g_currentLagCount = 0;
187 g_currentInputCount = 0;
188 }
189}
190
191void InputUpdate()
192{
193 g_currentInputCount++;
194 if (IsRecordingInput())
195 g_totalInputCount = g_currentInputCount;
196
197 if (IsPlayingInput() && g_currentInputCount == (g_totalInputCount -1) && SConfig::GetInstance().m_PauseMovie)
198 Core::SetState(Core::CORE_PAUSE);
199}
200
201void SetFrameSkipping(unsigned int framesToSkip)
202{
203 std::lock_guard<std::mutex> lk(cs_frameSkip);
204
205 g_framesToSkip = framesToSkip;
206 g_frameSkipCounter = 0;
207
208 // Don't forget to re-enable rendering in case it wasn't...
209 // as this won't be changed anymore when frameskip is turned off
210 if (framesToSkip == 0)
211 g_video_backend->Video_SetRendering(true);
212}
213
214void SetPolledDevice()
215{
216 g_bPolled = true;
217}
218
219void DoFrameStep()
220{
221 if(Core::GetState() == Core::CORE_PAUSE)
222 {
223 // if already paused, frame advance for 1 frame
224 Core::SetState(Core::CORE_RUN);
225 Core::RequestRefreshInfo();
226 g_bFrameStep = true;
227 }
228 else if(!g_bFrameStep)
229 {
230 // if not paused yet, pause immediately instead
231 Core::SetState(Core::CORE_PAUSE);
232 }
233}
234
235void SetFrameStopping(bool bEnabled)
236{
237 g_bFrameStop = bEnabled;
238}
239
240void SetReadOnly(bool bEnabled)
241{
242 if (g_bReadOnly != bEnabled)
243 Core::DisplayMessage(bEnabled ? "Read-only mode." : "Read+Write mode.", 1000);
244
245 g_bReadOnly = bEnabled;
246}
247
248void FrameSkipping()
249{
250 // Frameskipping will desync movie playback
251 if (!IsPlayingInput() && !IsRecordingInput())
252 {
253 std::lock_guard<std::mutex> lk(cs_frameSkip);
254
255 g_frameSkipCounter++;
256 if (g_frameSkipCounter > g_framesToSkip || Core::ShouldSkipFrame(g_frameSkipCounter) == false)
257 g_frameSkipCounter = 0;
258
259 g_video_backend->Video_SetRendering(!g_frameSkipCounter);
260 }
261}
262
263bool IsRecordingInput()
264{
265 return (g_playMode == MODE_RECORDING);
266}
267
268bool IsRecordingInputFromSaveState()
269{
270 return g_bRecordingFromSaveState;
271}
272
273bool IsJustStartingRecordingInputFromSaveState()
274{
275 return IsRecordingInputFromSaveState() && g_currentFrame == 0;
276}
277
278bool IsJustStartingPlayingInputFromSaveState()
279{
280 return IsRecordingInputFromSaveState() && g_currentFrame == 1 && IsPlayingInput();
281}
282
283bool IsPlayingInput()
284{
285 return (g_playMode == MODE_PLAYING);
286}
287
288bool IsReadOnly()
289{
290 return g_bReadOnly;
291}
292
293u64 GetRecordingStartTime()
294{
295 return g_recordingStartTime;
296}
297
298bool IsUsingPad(int controller)
299{
300 return ((g_numPads & (1 << controller)) != 0);
301}
302
303bool IsUsingBongo(int controller)
304{
305 return ((bongos & (1 << controller)) != 0);
306}
307
308bool IsUsingWiimote(int wiimote)
309{
310 return ((g_numPads & (1 << (wiimote + 4))) != 0);
311}
312
313bool IsConfigSaved()
314{
315 return bSaveConfig;
316}
317bool IsDualCore()
318{
319 return bDualCore;
320}
321
322bool IsProgressive()
323{
324 return bProgressive;
325}
326
327bool IsSkipIdle()
328{
329 return bSkipIdle;
330}
331
332bool IsDSPHLE()
333{
334 return bDSPHLE;
335}
336
337bool IsFastDiscSpeed()
338{
339 return bFastDiscSpeed;
340}
341
342int GetCPUMode()
343{
344 return iCPUCore;
345}
346
347bool IsStartingFromClearSave()
348{
349 return g_bClearSave;
350}
351
352bool IsUsingMemcard()
353{
354 return bMemcard;
355}
356
357void ChangePads(bool instantly)
358{
359 if (Core::GetState() == Core::CORE_UNINITIALIZED)
360 return;
361
362 int controllers = 0;
363
364 for (int i = 0; i < 4; i++)
365 if (SConfig::GetInstance().m_SIDevice[i] == SIDEVICE_GC_CONTROLLER || SConfig::GetInstance().m_SIDevice[i] == SIDEVICE_GC_TARUKONGA)
366 controllers |= (1 << i);
367
368 if (instantly && (g_numPads & 0x0F) == controllers)
369 return;
370
371 for (int i = 0; i < 4; i++)
372 if (instantly) // Changes from savestates need to be instantaneous
373 SerialInterface::AddDevice(IsUsingPad(i) ? (IsUsingBongo(i) ? SIDEVICE_GC_TARUKONGA : SIDEVICE_GC_CONTROLLER) : SIDEVICE_NONE, i);
374 else
375 SerialInterface::ChangeDevice(IsUsingPad(i) ? (IsUsingBongo(i) ? SIDEVICE_GC_TARUKONGA : SIDEVICE_GC_CONTROLLER) : SIDEVICE_NONE, i);
376}
377
378void ChangeWiiPads(bool instantly)
379{
380 int controllers = 0;
381
382 for (int i = 0; i < 4; i++)
383 if (g_wiimote_sources[i] != WIIMOTE_SRC_NONE)
384 controllers |= (1 << i);
385
386 // This is important for Wiimotes, because they can desync easily if they get re-activated
387 if (instantly && (g_numPads >> 4) == controllers)
388 return;
389
390 for (int i = 0; i < MAX_BBMOTES; i++)
391 {
392 g_wiimote_sources[i] = IsUsingWiimote(i) ? WIIMOTE_SRC_EMU : WIIMOTE_SRC_NONE;
393 GetUsbPointer()->AccessWiiMote(i | 0x100)->Activate(IsUsingWiimote(i));
394 }
395}
396
397bool BeginRecordingInput(int controllers)
398{
399 if(g_playMode != MODE_NONE || controllers == 0)
400 return false;
401
402 g_numPads = controllers;
403 g_currentFrame = g_totalFrames = 0;
404 g_currentLagCount = g_totalLagCount = 0;
405 g_currentInputCount = g_totalInputCount = 0;
406 g_recordingStartTime = Common::Timer::GetLocalTimeSinceJan1970();
407 g_rerecords = 0;
408
409 for (int i = 0; i < 4; i++)
410 if (SConfig::GetInstance().m_SIDevice[i] == SIDEVICE_GC_TARUKONGA)
411 bongos |= (1 << i);
412
413 if (Core::IsRunning())
414 {
415 if(File::Exists(tmpStateFilename))
416 File::Delete(tmpStateFilename);
417
418 State::SaveAs(tmpStateFilename.c_str());
419 g_bRecordingFromSaveState = true;
420
421 // 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.
422 // TODO: find a way to GetTitleDataPath() from Movie::Init()
423 if (Core::g_CoreStartupParameter.bWii)
424 {
425 if (File::Exists((Common::GetTitleDataPath(g_titleID) + "banner.bin").c_str()))
426 Movie::g_bClearSave = false;
427 else
428 Movie::g_bClearSave = true;
429 }
430 std::thread md5thread(GetMD5);
431 }
432 g_playMode = MODE_RECORDING;
433 GetSettings();
434 author = SConfig::GetInstance().m_strMovieAuthor;
435 EnsureTmpInputSize(1);
436
437 g_currentByte = g_totalBytes = 0;
438
439 Core::DisplayMessage("Starting movie recording", 2000);
440 return true;
441}
442
443static void Analog2DToString(u8 x, u8 y, const char* prefix, char* str)
444{
445 if((x <= 1 || x == 128 || x >= 255)
446 && (y <= 1 || y == 128 || y >= 255))
447 {
448 if(x != 128 || y != 128)
449 {
450 if(x != 128 && y != 128)
451 {
452 sprintf(str, "%s:%s,%s", prefix, x<128?"LEFT":"RIGHT", y<128?"DOWN":"UP");
453 }
454 else if(x != 128)
455 {
456 sprintf(str, "%s:%s", prefix, x<128?"LEFT":"RIGHT");
457 }
458 else
459 {
460 sprintf(str, "%s:%s", prefix, y<128?"DOWN":"UP");
461 }
462 }
463 else
464 {
465 str[0] = '\0';
466 }
467 }
468 else
469 {
470 sprintf(str, "%s:%d,%d", prefix, x, y);
471 }
472}
473
474static void Analog1DToString(u8 v, const char* prefix, char* str)
475{
476 if(v > 0)
477 {
478 if(v == 255)
479 {
480 strcpy(str, prefix);
481 }
482 else
483 {
484 sprintf(str, "%s:%d", prefix, v);
485 }
486 }
487 else
488 {
489 str[0] = '\0';
490 }
491}
492
493void SetInputDisplayString(ControllerState padState, int controllerID)
494{
495 char inp[70];
496 sprintf(inp, "P%d:", controllerID + 1);
497 g_InputDisplay[controllerID] = inp;
498
499 if(g_padState.A)
500 g_InputDisplay[controllerID].append(" A");
501 if(g_padState.B)
502 g_InputDisplay[controllerID].append(" B");
503 if(g_padState.X)
504 g_InputDisplay[controllerID].append(" X");
505 if(g_padState.Y)
506 g_InputDisplay[controllerID].append(" Y");
507 if(g_padState.Z)
508 g_InputDisplay[controllerID].append(" Z");
509 if(g_padState.Start)
510 g_InputDisplay[controllerID].append(" START");
511
512 if(g_padState.DPadUp)
513 g_InputDisplay[controllerID].append(" UP");
514 if(g_padState.DPadDown)
515 g_InputDisplay[controllerID].append(" DOWN");
516 if(g_padState.DPadLeft)
517 g_InputDisplay[controllerID].append(" LEFT");
518 if(g_padState.DPadRight)
519 g_InputDisplay[controllerID].append(" RIGHT");
520
521 Analog1DToString(g_padState.TriggerL, " L", inp);
522 g_InputDisplay[controllerID].append(inp);
523
524 Analog1DToString(g_padState.TriggerR, " R", inp);
525 g_InputDisplay[controllerID].append(inp);
526
527 Analog2DToString(g_padState.AnalogStickX, g_padState.AnalogStickY, " ANA", inp);
528 g_InputDisplay[controllerID].append(inp);
529
530 Analog2DToString(g_padState.CStickX, g_padState.CStickY, " C", inp);
531 g_InputDisplay[controllerID].append(inp);
532
533 g_InputDisplay[controllerID].append("\n");
534}
535
536void SetWiiInputDisplayString(int remoteID, u8* const coreData, u8* const accelData, u8* const irData)
537{
538 int controllerID = remoteID + 4;
539
540 char inp[70];
541 sprintf(inp, "R%d:", remoteID + 1);
542 g_InputDisplay[controllerID] = inp;
543
544 if(coreData)
545 {
546 wm_core buttons = *(wm_core*)coreData;
547 if(buttons & WiimoteEmu::Wiimote::PAD_LEFT)
548 g_InputDisplay[controllerID].append(" LEFT");
549 if(buttons & WiimoteEmu::Wiimote::PAD_RIGHT)
550 g_InputDisplay[controllerID].append(" RIGHT");
551 if(buttons & WiimoteEmu::Wiimote::PAD_DOWN)
552 g_InputDisplay[controllerID].append(" DOWN");
553 if(buttons & WiimoteEmu::Wiimote::PAD_UP)
554 g_InputDisplay[controllerID].append(" UP");
555 if(buttons & WiimoteEmu::Wiimote::BUTTON_A)
556 g_InputDisplay[controllerID].append(" A");
557 if(buttons & WiimoteEmu::Wiimote::BUTTON_B)
558 g_InputDisplay[controllerID].append(" B");
559 if(buttons & WiimoteEmu::Wiimote::BUTTON_PLUS)
560 g_InputDisplay[controllerID].append(" +");
561 if(buttons & WiimoteEmu::Wiimote::BUTTON_MINUS)
562 g_InputDisplay[controllerID].append(" -");
563 if(buttons & WiimoteEmu::Wiimote::BUTTON_ONE)
564 g_InputDisplay[controllerID].append(" 1");
565 if(buttons & WiimoteEmu::Wiimote::BUTTON_TWO)
566 g_InputDisplay[controllerID].append(" 2");
567 if(buttons & WiimoteEmu::Wiimote::BUTTON_HOME)
568 g_InputDisplay[controllerID].append(" HOME");
569 }
570
571 if(accelData)
572 {
573 wm_accel* dt = (wm_accel*)accelData;
574 sprintf(inp, " ACC:%d,%d,%d", dt->x, dt->y, dt->z);
575 g_InputDisplay[controllerID].append(inp);
576 }
577
578 if(irData) // incomplete
579 {
580 sprintf(inp, " IR:%d,%d", ((u8*)irData)[0], ((u8*)irData)[1]);
581 g_InputDisplay[controllerID].append(inp);
582 }
583
584 g_InputDisplay[controllerID].append("\n");
585}
586
587void CheckPadStatus(SPADStatus *PadStatus, int controllerID)
588{
589 g_padState.A = ((PadStatus->button & PAD_BUTTON_A0x0100) != 0);
590 g_padState.B = ((PadStatus->button & PAD_BUTTON_B0x0200) != 0);
591 g_padState.X = ((PadStatus->button & PAD_BUTTON_X0x0400) != 0);
592 g_padState.Y = ((PadStatus->button & PAD_BUTTON_Y0x0800) != 0);
593 g_padState.Z = ((PadStatus->button & PAD_TRIGGER_Z0x0010) != 0);
594 g_padState.Start = ((PadStatus->button & PAD_BUTTON_START0x1000) != 0);
595
596 g_padState.DPadUp = ((PadStatus->button & PAD_BUTTON_UP0x0008) != 0);
597 g_padState.DPadDown = ((PadStatus->button & PAD_BUTTON_DOWN0x0004) != 0);
598 g_padState.DPadLeft = ((PadStatus->button & PAD_BUTTON_LEFT0x0001) != 0);
599 g_padState.DPadRight = ((PadStatus->button & PAD_BUTTON_RIGHT0x0002) != 0);
600
601 g_padState.L = ((PadStatus->button & PAD_TRIGGER_L0x0040) != 0);
602 g_padState.R = ((PadStatus->button & PAD_TRIGGER_R0x0020) != 0);
603 g_padState.TriggerL = PadStatus->triggerLeft;
604 g_padState.TriggerR = PadStatus->triggerRight;
605
606 g_padState.AnalogStickX = PadStatus->stickX;
607 g_padState.AnalogStickY = PadStatus->stickY;
608
609 g_padState.CStickX = PadStatus->substickX;
610 g_padState.CStickY = PadStatus->substickY;
611
612 SetInputDisplayString(g_padState, controllerID);
613}
614
615void RecordInput(SPADStatus *PadStatus, int controllerID)
616{
617 if (!IsRecordingInput() || !IsUsingPad(controllerID))
618 return;
619
620 CheckPadStatus(PadStatus, controllerID);
621
622 if (g_bDiscChange)
623 {
624 g_padState.disc = g_bDiscChange;
625 g_bDiscChange = false;
626 }
627
628 EnsureTmpInputSize((size_t)(g_currentByte + 8));
629 memcpy(&(tmpInput[g_currentByte]), &g_padState, 8);
630 g_currentByte += 8;
631 g_totalBytes = g_currentByte;
632}
633
634void CheckWiimoteStatus(int wiimote, u8 *data, const WiimoteEmu::ReportFeatures& rptf, int irMode)
635{
636 u8* const coreData = rptf.core?(data+rptf.core):NULL__null;
1
'?' condition is false
637 u8* const accelData = rptf.accel?(data+rptf.accel):NULL__null;
2
'?' condition is false
638 u8* const irData = rptf.ir?(data+rptf.ir):NULL__null;
3
'?' condition is true
639 u8 size = rptf.size;
640 SetWiiInputDisplayString(wiimote, coreData, accelData, irData);
641
642 if (IsRecordingInput())
4
Taking true branch
643 RecordWiimote(wiimote, data, size);
5
Passing null pointer value via 2nd parameter 'data'
6
Calling 'RecordWiimote'
644}
645
646void RecordWiimote(int wiimote, u8 *data, u8 size)
647{
648 if(!IsRecordingInput() || !IsUsingWiimote(wiimote))
7
Taking false branch
649 return;
650
651 InputUpdate();
652 EnsureTmpInputSize((size_t)(g_currentByte + size + 1));
653 tmpInput[g_currentByte++] = size;
654 memcpy(&(tmpInput[g_currentByte]), data, size);
8
Null pointer passed as an argument to a 'nonnull' parameter
655 g_currentByte += size;
656 g_totalBytes = g_currentByte;
657}
658
659void ReadHeader()
660{
661 g_numPads = tmpHeader.numControllers;
662 g_recordingStartTime = tmpHeader.recordingStartTime;
663 if (g_rerecords < tmpHeader.numRerecords)
664 g_rerecords = tmpHeader.numRerecords;
665
666 if (tmpHeader.bSaveConfig)
667 {
668 bSaveConfig = true;
669 bSkipIdle = tmpHeader.bSkipIdle;
670 bDualCore = tmpHeader.bDualCore;
671 bProgressive = tmpHeader.bProgressive;
672 bDSPHLE = tmpHeader.bDSPHLE;
673 bFastDiscSpeed = tmpHeader.bFastDiscSpeed;
674 iCPUCore = tmpHeader.CPUCore;
675 g_bClearSave = tmpHeader.bClearSave;
676 bMemcard = tmpHeader.bMemcard;
677 bongos = tmpHeader.bongos;
678 memcpy(revision, tmpHeader.revision, ARRAYSIZE(revision)(sizeof(revision)/sizeof((revision)[0])));
679 }
680 else
681 {
682 GetSettings();
683 }
684
685 videoBackend.resize(ARRAYSIZE(tmpHeader.videoBackend)(sizeof(tmpHeader.videoBackend)/sizeof((tmpHeader.videoBackend
)[0]))
);
686 for (unsigned int i = 0; i < ARRAYSIZE(tmpHeader.videoBackend)(sizeof(tmpHeader.videoBackend)/sizeof((tmpHeader.videoBackend
)[0]))
;i++)
687 {
688 videoBackend[i] = tmpHeader.videoBackend[i];
689 }
690
691 g_discChange.resize(ARRAYSIZE(tmpHeader.discChange)(sizeof(tmpHeader.discChange)/sizeof((tmpHeader.discChange)[0
]))
);
692 for (unsigned int i = 0; i < ARRAYSIZE(tmpHeader.discChange)(sizeof(tmpHeader.discChange)/sizeof((tmpHeader.discChange)[0
]))
;i++)
693 {
694 g_discChange[i] = tmpHeader.discChange[i];
695 }
696
697 author.resize(ARRAYSIZE(tmpHeader.author)(sizeof(tmpHeader.author)/sizeof((tmpHeader.author)[0])));
698 for (unsigned int i = 0; i < ARRAYSIZE(tmpHeader.author)(sizeof(tmpHeader.author)/sizeof((tmpHeader.author)[0]));i++)
699 {
700 author[i] = tmpHeader.author[i];
701 }
702 memcpy(MD5, tmpHeader.md5, 16);
703}
704
705bool PlayInput(const char *filename)
706{
707 if(!filename || g_playMode != MODE_NONE)
708 return false;
709
710 if(!File::Exists(filename))
711 return false;
712
713 File::IOFile g_recordfd;
714
715 if (!g_recordfd.Open(filename, "rb"))
716 return false;
717
718 g_recordfd.ReadArray(&tmpHeader, 1);
719
720 if(tmpHeader.filetype[0] != 'D' || tmpHeader.filetype[1] != 'T' || tmpHeader.filetype[2] != 'M' || tmpHeader.filetype[3] != 0x1A) {
721 PanicAlertT("Invalid recording file")MsgAlert(false, WARNING, "Invalid recording file");
722 goto cleanup;
723 }
724
725 ReadHeader();
726 g_totalFrames = tmpHeader.frameCount;
727 g_totalLagCount = tmpHeader.lagCount;
728 g_totalInputCount = tmpHeader.inputCount;
729 g_currentFrame = 0;
730 g_currentLagCount = 0;
731 g_currentInputCount = 0;
732
733 g_playMode = MODE_PLAYING;
734
735 g_totalBytes = g_recordfd.GetSize() - 256;
736 EnsureTmpInputSize((size_t)g_totalBytes);
737 g_recordfd.ReadArray(tmpInput, (size_t)g_totalBytes);
738 g_currentByte = 0;
739 g_recordfd.Close();
740
741 // Load savestate (and skip to frame data)
742 if(tmpHeader.bFromSaveState)
743 {
744 const std::string stateFilename = std::string(filename) + ".sav";
745 if(File::Exists(stateFilename))
746 Core::SetStateFileName(stateFilename);
747 g_bRecordingFromSaveState = true;
748 Movie::LoadInput(filename);
749 }
750
751 return true;
752
753cleanup:
754 g_recordfd.Close();
755 return false;
756}
757
758void DoState(PointerWrap &p)
759{
760 static const int MOVIE_STATE_VERSION = 1;
761 g_currentSaveVersion = MOVIE_STATE_VERSION;
762 p.Do(g_currentSaveVersion);
763 // many of these could be useful to save even when no movie is active,
764 // and the data is tiny, so let's just save it regardless of movie state.
765 p.Do(g_currentFrame);
766 p.Do(g_currentByte);
767 p.Do(g_currentLagCount);
768 p.Do(g_currentInputCount);
769 p.Do(g_bPolled);
770 // other variables (such as g_totalBytes and g_totalFrames) are set in LoadInput
771}
772
773void LoadInput(const char *filename)
774{
775 File::IOFile t_record;
776 if (!t_record.Open(filename, "r+b"))
777 {
778 PanicAlertT("Failed to read %s", filename)MsgAlert(false, WARNING, "Failed to read %s", filename);
779 EndPlayInput(false);
780 return;
781 }
782
783 t_record.ReadArray(&tmpHeader, 1);
784
785 if(tmpHeader.filetype[0] != 'D' || tmpHeader.filetype[1] != 'T' || tmpHeader.filetype[2] != 'M' || tmpHeader.filetype[3] != 0x1A)
786 {
787 PanicAlertT("Savestate movie %s is corrupted, movie recording stopping...", filename)MsgAlert(false, WARNING, "Savestate movie %s is corrupted, movie recording stopping..."
, filename)
;
788 EndPlayInput(false);
789 return;
790 }
791 ReadHeader();
792 if (!g_bReadOnly)
793 {
794 g_rerecords++;
795 tmpHeader.numRerecords = g_rerecords;
796 t_record.Seek(0, SEEK_SET0);
797 t_record.WriteArray(&tmpHeader, 1);
798 }
799
800 ChangePads(true);
801 if (Core::g_CoreStartupParameter.bWii)
802 ChangeWiiPads(true);
803
804 u64 totalSavedBytes = t_record.GetSize() - 256;
805
806 bool afterEnd = false;
807 if (g_currentByte > totalSavedBytes)
808 {
809 //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);
810 afterEnd = true;
811 }
812
813 if (!g_bReadOnly || tmpInput == NULL__null)
814 {
815 g_totalFrames = tmpHeader.frameCount;
816 g_totalLagCount = tmpHeader.lagCount;
817 g_totalInputCount = tmpHeader.inputCount;
818
819 EnsureTmpInputSize((size_t)totalSavedBytes);
820 g_totalBytes = totalSavedBytes;
821 t_record.ReadArray(tmpInput, (size_t)g_totalBytes);
822 }
823 else if (g_currentByte > 0)
824 {
825 if (g_currentByte > totalSavedBytes)
826 {
827 }
828 else if (g_currentByte > g_totalBytes)
829 {
830 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)MsgAlert(false, WARNING, "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)
;
831 }
832 else if(g_currentByte > 0 && g_totalBytes > 0)
833 {
834 // verify identical from movie start to the save's current frame
835 u32 len = (u32)g_currentByte;
836 u8* movInput = new u8[len];
837 t_record.ReadArray(movInput, (size_t)len);
838 for (u32 i = 0; i < len; ++i)
839 {
840 if (movInput[i] != tmpInput[i])
841 {
842 // this is a "you did something wrong" alert for the user's benefit.
843 // we'll try to say what's going on in excruciating detail, otherwise the user might not believe us.
844 if(IsUsingWiimote(0))
845 {
846 // TODO: more detail
847 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)MsgAlert(false, WARNING, "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)
;
848 }
849 else
850 {
851 int frame = i/8;
852 ControllerState curPadState;
853 memcpy(&curPadState, &(tmpInput[frame*8]), 8);
854 ControllerState movPadState;
855 memcpy(&movPadState, &(movInput[frame*8]), 8);
856 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"MsgAlert(false, WARNING, "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)
857 "More information: The current movie is %d frames long and the savestate's movie is %d frames long.\n\n"MsgAlert(false, WARNING, "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)
858 "On frame %d, the current movie presses:\n"MsgAlert(false, WARNING, "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)
859 "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"MsgAlert(false, WARNING, "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)
860 "\n\n"MsgAlert(false, WARNING, "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)
861 "On frame %d, the savestate's movie presses:\n"MsgAlert(false, WARNING, "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)
862 "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",MsgAlert(false, WARNING, "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)
863 (int)frame,MsgAlert(false, WARNING, "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)
864 (int)g_totalFrames, (int)tmpHeader.frameCount,MsgAlert(false, WARNING, "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)
865 (int)frame,MsgAlert(false, WARNING, "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)
866 (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,MsgAlert(false, WARNING, "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)
867 (int)frame,MsgAlert(false, WARNING, "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)
868 (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)MsgAlert(false, WARNING, "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)
;
869 }
870 break;
871 }
872 }
873 delete [] movInput;
874 }
875 }
876 t_record.Close();
877
878 bSaveConfig = tmpHeader.bSaveConfig;
879
880 if (!afterEnd)
881 {
882 if (g_bReadOnly)
883 {
884 if(g_playMode != MODE_PLAYING)
885 {
886 g_playMode = MODE_PLAYING;
887 Core::DisplayMessage("Switched to playback", 2000);
888 }
889 }
890 else
891 {
892 if(g_playMode != MODE_RECORDING)
893 {
894 g_playMode = MODE_RECORDING;
895 Core::DisplayMessage("Switched to recording", 2000);
896 }
897 }
898 }
899 else
900 {
901 EndPlayInput(false);
902 }
903}
904
905static void CheckInputEnd()
906{
907 if (g_currentFrame > g_totalFrames || g_currentByte >= g_totalBytes)
908 {
909 EndPlayInput(!g_bReadOnly);
910 }
911}
912
913void PlayController(SPADStatus *PadStatus, int controllerID)
914{
915 // Correct playback is entirely dependent on the emulator polling the controllers
916 // in the same order done during recording
917 if (!IsPlayingInput() || !IsUsingPad(controllerID) || tmpInput == NULL__null)
918 return;
919
920 if (g_currentByte + 8 > g_totalBytes)
921 {
922 PanicAlertT("Premature movie end in PlayController. %u + 8 > %u", (u32)g_currentByte, (u32)g_totalBytes)MsgAlert(false, WARNING, "Premature movie end in PlayController. %u + 8 > %u"
, (u32)g_currentByte, (u32)g_totalBytes)
;
923 EndPlayInput(!g_bReadOnly);
924 return;
925 }
926
927 // dtm files don't save the mic button or error bit. not sure if they're actually used, but better safe than sorry
928 signed char e = PadStatus->err;
929 memset(PadStatus, 0, sizeof(SPADStatus));
930 PadStatus->err = e;
931
932
933 memcpy(&g_padState, &(tmpInput[g_currentByte]), 8);
934 g_currentByte += 8;
935
936 PadStatus->triggerLeft = g_padState.TriggerL;
937 PadStatus->triggerRight = g_padState.TriggerR;
938
939 PadStatus->stickX = g_padState.AnalogStickX;
940 PadStatus->stickY = g_padState.AnalogStickY;
941
942 PadStatus->substickX = g_padState.CStickX;
943 PadStatus->substickY = g_padState.CStickY;
944
945 PadStatus->button |= PAD_USE_ORIGIN0x0080;
946
947 if(g_padState.A)
948 {
949 PadStatus->button |= PAD_BUTTON_A0x0100;
950 PadStatus->analogA = 0xFF;
951 }
952 if(g_padState.B)
953 {
954 PadStatus->button |= PAD_BUTTON_B0x0200;
955 PadStatus->analogB = 0xFF;
956 }
957 if(g_padState.X)
958 PadStatus->button |= PAD_BUTTON_X0x0400;
959 if(g_padState.Y)
960 PadStatus->button |= PAD_BUTTON_Y0x0800;
961 if(g_padState.Z)
962 PadStatus->button |= PAD_TRIGGER_Z0x0010;
963 if(g_padState.Start)
964 PadStatus->button |= PAD_BUTTON_START0x1000;
965
966 if(g_padState.DPadUp)
967 PadStatus->button |= PAD_BUTTON_UP0x0008;
968 if(g_padState.DPadDown)
969 PadStatus->button |= PAD_BUTTON_DOWN0x0004;
970 if(g_padState.DPadLeft)
971 PadStatus->button |= PAD_BUTTON_LEFT0x0001;
972 if(g_padState.DPadRight)
973 PadStatus->button |= PAD_BUTTON_RIGHT0x0002;
974
975 if(g_padState.L)
976 PadStatus->button |= PAD_TRIGGER_L0x0040;
977 if(g_padState.R)
978 PadStatus->button |= PAD_TRIGGER_R0x0020;
979 if (g_padState.disc)
980 {
981 // This implementation assumes the disc change will only happen once. Trying to change more than that will cause
982 // 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.
983 Core::SetState(Core::CORE_PAUSE);
984 int numPaths = (int)SConfig::GetInstance().m_ISOFolder.size();
985 bool found = false;
986 std::string path;
987 for (int i = 0; i < numPaths; i++)
988 {
989 path = SConfig::GetInstance().m_ISOFolder[i];
990 if (File::Exists((path + '/' + g_discChange.c_str()).c_str()))
991 {
992 found = true;
993 break;
994 }
995 }
996 if (found)
997 {
998 DVDInterface::ChangeDisc((path + '/' + g_discChange.c_str()).c_str());
999 Core::SetState(Core::CORE_RUN);
1000 }
1001 else
1002 {
1003 PanicAlert("Change the disc to %s", g_discChange.c_str())MsgAlert(false, WARNING, "Change the disc to %s", g_discChange
.c_str())
;
1004 }
1005 }
1006
1007 SetInputDisplayString(g_padState, controllerID);
1008 CheckInputEnd();
1009}
1010
1011bool PlayWiimote(int wiimote, u8 *data, const WiimoteEmu::ReportFeatures& rptf, int irMode)
1012{
1013 if(!IsPlayingInput() || !IsUsingWiimote(wiimote) || tmpInput == NULL__null)
1014 return false;
1015
1016 if (g_currentByte > g_totalBytes)
1017 {
1018 PanicAlertT("Premature movie end in PlayWiimote. %u > %u", (u32)g_currentByte, (u32)g_totalBytes)MsgAlert(false, WARNING, "Premature movie end in PlayWiimote. %u > %u"
, (u32)g_currentByte, (u32)g_totalBytes)
;
1019 EndPlayInput(!g_bReadOnly);
1020 return false;
1021 }
1022
1023 u8* const coreData = rptf.core?(data+rptf.core):NULL__null;
1024 u8* const accelData = rptf.accel?(data+rptf.accel):NULL__null;
1025 u8* const irData = rptf.ir?(data+rptf.ir):NULL__null;
1026 u8 size = rptf.size;
1027
1028 u8 sizeInMovie = tmpInput[g_currentByte];
1029
1030 if (size != sizeInMovie)
1031 {
1032 PanicAlertT("Fatal desync. Aborting playback. (Error in PlayWiimote: %u != %u, byte %u.)%s", (u32)sizeInMovie, (u32)size, (u32)g_currentByte, (g_numPads & 0xF)?" Try re-creating the recording with all GameCube controllers disabled (in Configure > Gamecube > Device Settings), or restarting Dolphin (Dolphin currently must be restarted every time before playing back a wiimote movie).":"")MsgAlert(false, WARNING, "Fatal desync. Aborting playback. (Error in PlayWiimote: %u != %u, byte %u.)%s"
, (u32)sizeInMovie, (u32)size, (u32)g_currentByte, (g_numPads
& 0xF)?" Try re-creating the recording with all GameCube controllers disabled (in Configure > Gamecube > Device Settings), or restarting Dolphin (Dolphin currently must be restarted every time before playing back a wiimote movie)."
:"")
;
1033 EndPlayInput(!g_bReadOnly);
1034 return false;
1035 }
1036
1037 g_currentByte++;
1038
1039 if (g_currentByte + size > g_totalBytes)
1040 {
1041 PanicAlertT("Premature movie end in PlayWiimote. %u + %d > %u", (u32)g_currentByte, size, (u32)g_totalBytes)MsgAlert(false, WARNING, "Premature movie end in PlayWiimote. %u + %d > %u"
, (u32)g_currentByte, size, (u32)g_totalBytes)
;
1042 EndPlayInput(!g_bReadOnly);
1043 return false;
1044 }
1045
1046 memcpy(data, &(tmpInput[g_currentByte]), size);
1047 g_currentByte += size;
1048
1049 SetWiiInputDisplayString(wiimote, coreData, accelData, irData);
1050
1051 g_currentInputCount++;
1052
1053 CheckInputEnd();
1054 return true;
1055}
1056
1057void EndPlayInput(bool cont)
1058{
1059 if (cont)
1060 {
1061 g_playMode = MODE_RECORDING;
1062 Core::DisplayMessage("Reached movie end. Resuming recording.", 2000);
1063 }
1064 else if(g_playMode != MODE_NONE)
1065 {
1066 g_rerecords = 0;
1067 g_currentByte = 0;
1068 g_playMode = MODE_NONE;
1069 Core::DisplayMessage("Movie End.", 2000);
1070 g_bRecordingFromSaveState = 0;
1071 // we don't clear these things because otherwise we can't resume playback if we load a movie state later
1072 //g_totalFrames = g_totalBytes = 0;
1073 //delete tmpInput;
1074 //tmpInput = NULL;
1075 }
1076}
1077
1078void SaveRecording(const char *filename)
1079{
1080 File::IOFile save_record(filename, "wb");
1081 // Create the real header now and write it
1082 DTMHeader header;
1083 memset(&header, 0, sizeof(DTMHeader));
1084
1085 header.filetype[0] = 'D'; header.filetype[1] = 'T'; header.filetype[2] = 'M'; header.filetype[3] = 0x1A;
1086 strncpy((char *)header.gameID, Core::g_CoreStartupParameter.GetUniqueID().c_str(), 6);
1087 header.bWii = Core::g_CoreStartupParameter.bWii;
1088 header.numControllers = g_numPads & (Core::g_CoreStartupParameter.bWii ? 0xFF : 0x0F);
1089
1090 header.bFromSaveState = g_bRecordingFromSaveState;
1091 header.frameCount = g_totalFrames;
1092 header.lagCount = g_totalLagCount;
1093 header.inputCount = g_totalInputCount;
1094 header.numRerecords = g_rerecords;
1095 header.recordingStartTime = g_recordingStartTime;
1096
1097 header.bSaveConfig = true;
1098 header.bSkipIdle = bSkipIdle;
1099 header.bDualCore = bDualCore;
1100 header.bProgressive = bProgressive;
1101 header.bDSPHLE = bDSPHLE;
1102 header.bFastDiscSpeed = bFastDiscSpeed;
1103 strncpy((char *)header.videoBackend, videoBackend.c_str(),ARRAYSIZE(header.videoBackend)(sizeof(header.videoBackend)/sizeof((header.videoBackend)[0])
)
);
1104 header.CPUCore = iCPUCore;
1105 header.bEFBAccessEnable = g_ActiveConfig.bEFBAccessEnable;
1106 header.bEFBCopyEnable = g_ActiveConfig.bEFBCopyEnable;
1107 header.bCopyEFBToTexture = g_ActiveConfig.bCopyEFBToTexture;
1108 header.bEFBCopyCacheEnable = g_ActiveConfig.bEFBCopyCacheEnable;
1109 header.bEFBEmulateFormatChanges = g_ActiveConfig.bEFBEmulateFormatChanges;
1110 header.bUseXFB = g_ActiveConfig.bUseXFB;
1111 header.bUseRealXFB = g_ActiveConfig.bUseRealXFB;
1112 header.bMemcard = bMemcard;
1113 header.bClearSave = g_bClearSave;
1114 strncpy((char *)header.discChange, g_discChange.c_str(),ARRAYSIZE(header.discChange)(sizeof(header.discChange)/sizeof((header.discChange)[0])));
1115 strncpy((char *)header.author, author.c_str(),ARRAYSIZE(header.author)(sizeof(header.author)/sizeof((header.author)[0])));
1116 memcpy(header.md5,MD5,16);
1117 header.bongos = bongos;
1118 memcpy(header.revision, revision, ARRAYSIZE(header.revision)(sizeof(header.revision)/sizeof((header.revision)[0])));
1119
1120 // TODO
1121 header.uniqueID = 0;
1122 // header.audioEmulator;
1123
1124 save_record.WriteArray(&header, 1);
1125
1126 bool success = save_record.WriteArray(tmpInput, (size_t)g_totalBytes);
1127
1128 if (success && g_bRecordingFromSaveState)
1129 {
1130 std::string stateFilename = filename;
1131 stateFilename.append(".sav");
1132 success = File::Copy(tmpStateFilename, stateFilename);
1133 }
1134
1135 if (success)
1136 Core::DisplayMessage(StringFromFormat("DTM %s saved", filename).c_str(), 2000);
1137 else
1138 Core::DisplayMessage(StringFromFormat("Failed to save %s", filename).c_str(), 2000);
1139}
1140
1141void SetInputManip(ManipFunction func)
1142{
1143 mfunc = func;
1144}
1145
1146void CallInputManip(SPADStatus *PadStatus, int controllerID)
1147{
1148 if (mfunc)
1149 (*mfunc)(PadStatus, controllerID);
1150}
1151
1152void SetGraphicsConfig()
1153{
1154 g_Config.bEFBAccessEnable = tmpHeader.bEFBAccessEnable;
1155 g_Config.bEFBCopyEnable = tmpHeader.bEFBCopyEnable;
1156 g_Config.bCopyEFBToTexture = tmpHeader.bCopyEFBToTexture;
1157 g_Config.bEFBCopyCacheEnable = tmpHeader.bEFBCopyCacheEnable;
1158 g_Config.bEFBEmulateFormatChanges = tmpHeader.bEFBEmulateFormatChanges;
1159 g_Config.bUseXFB = tmpHeader.bUseXFB;
1160 g_Config.bUseRealXFB = tmpHeader.bUseRealXFB;
1161}
1162
1163void GetSettings()
1164{
1165 bSaveConfig = true;
1166 bSkipIdle = SConfig::GetInstance().m_LocalCoreStartupParameter.bSkipIdle;
1167 bDualCore = SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread;
1168 bProgressive = SConfig::GetInstance().m_LocalCoreStartupParameter.bProgressive;
1169 bDSPHLE = SConfig::GetInstance().m_LocalCoreStartupParameter.bDSPHLE;
1170 bFastDiscSpeed = SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed;
1171 videoBackend = SConfig::GetInstance().m_LocalCoreStartupParameter.m_strVideoBackend;
1172 iCPUCore = SConfig::GetInstance().m_LocalCoreStartupParameter.iCPUCore;
1173 if (!Core::g_CoreStartupParameter.bWii)
1174 g_bClearSave = !File::Exists(SConfig::GetInstance().m_strMemoryCardA);
1175 bMemcard = SConfig::GetInstance().m_EXIDevice[0] == EXIDEVICE_MEMORYCARD;
1176
1177 int temp;
1178
1179 for(int i = 0; i < 4; ++i )
1180 {
1181 sscanf(SCM_REV_STR"388ddee68508bcfdaf865182c0903a7f27bc5bbc" + 2 * i, "%2x", &temp );
1182 revision[i] = temp;
1183 }
1184}
1185
1186void CheckMD5()
1187{
1188 for (int i=0, n=0; i<16; i++)
1189 {
1190 if (tmpHeader.md5[i] != 0)
1191 continue;
1192 n++;
1193 if (n == 16)
1194 return;
1195 }
1196 Core::DisplayMessage("Verifying checksum...", 2000);
1197
1198 unsigned char gameMD5[16];
1199 char game[255];
1200 memcpy(game, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strFilename.c_str(), SConfig::GetInstance().m_LocalCoreStartupParameter.m_strFilename.size());
1201 md5_file(game, gameMD5);
1202
1203 if (memcmp(gameMD5,MD5,16) == 0)
1204 Core::DisplayMessage("Checksum of current game matches the recorded game.", 2000);
1205 else
1206 PanicAlert("Checksum of current game does not match the recorded game!")MsgAlert(false, WARNING, "Checksum of current game does not match the recorded game!"
)
;
1207}
1208
1209void GetMD5()
1210{
1211 Core::DisplayMessage("Calculating checksum of game file...", 2000);
1212 for (int i = 0; i < 16; i++)
1213 MD5[i] = 0;
1214 char game[255];
1215 memcpy(game, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strFilename.c_str(),SConfig::GetInstance().m_LocalCoreStartupParameter.m_strFilename.size());
1216 md5_file(game, MD5);
1217 Core::DisplayMessage("Finished calculating checksum.", 2000);
1218}
1219
1220void Shutdown()
1221{
1222 g_currentInputCount = g_totalInputCount = g_totalFrames = g_totalBytes = 0;
1223 delete [] tmpInput;
1224 tmpInput = NULL__null;
1225 tmpInputAllocated = 0;
1226}
1227};