Bug Summary

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