Core/Movie: Make DTM Wii Remote data use SerializedWiimoteState.

This commit is contained in:
Jordan Woyak 2025-02-12 21:22:45 -06:00
parent c770e7c276
commit 83d4249838
5 changed files with 183 additions and 212 deletions

View File

@ -17,13 +17,10 @@
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/MathUtil.h"
#include "Common/MsgHandler.h"
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "Core/HW/Wiimote.h"
#include "Core/Movie.h"
#include "Core/System.h"
#include "Core/HW/WiimoteCommon/WiimoteConstants.h"
#include "Core/HW/WiimoteCommon/WiimoteHid.h"
@ -39,8 +36,6 @@
#include "Core/HW/WiimoteEmu/Extension/Turntable.h"
#include "Core/HW/WiimoteEmu/Extension/UDrawTablet.h"
#include "InputCommon/ControllerEmu/Control/Input.h"
#include "InputCommon/ControllerEmu/Control/Output.h"
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
@ -589,9 +584,6 @@ void Wiimote::Update(const WiimoteEmu::DesiredWiimoteState& target_state)
void Wiimote::SendDataReport(const DesiredWiimoteState& target_state)
{
auto& movie = Core::System::GetInstance().GetMovie();
movie.SetPolledDevice();
if (InputReportID::ReportDisabled == m_reporting_mode)
{
// The wiimote is in this disabled after an extension change.
@ -607,78 +599,66 @@ void Wiimote::SendDataReport(const DesiredWiimoteState& target_state)
DataReportBuilder rpt_builder(m_reporting_mode);
if (movie.IsPlayingInput() && movie.PlayWiimote(m_bt_device_index, rpt_builder,
m_active_extension, GetExtensionEncryptionKey()))
// Core buttons:
if (rpt_builder.HasCore())
{
// Update buttons in status struct from movie:
rpt_builder.GetCoreData(&m_status.buttons);
rpt_builder.SetCoreData(m_status.buttons);
}
else
// Acceleration:
if (rpt_builder.HasAccel())
{
// Core buttons:
if (rpt_builder.HasCore())
rpt_builder.SetAccelData(target_state.acceleration);
}
// IR Camera:
if (rpt_builder.HasIR())
{
// Note: Camera logic currently contains no changing state so we can just update it here.
// If that changes this should be moved to Wiimote::Update();
m_camera_logic.Update(target_state.camera_points);
// The real wiimote reads camera data from the i2c bus starting at offset 0x37:
const u8 camera_data_offset =
CameraLogic::REPORT_DATA_OFFSET + rpt_builder.GetIRDataFormatOffset();
u8* ir_data = rpt_builder.GetIRDataPtr();
const u8 ir_size = rpt_builder.GetIRDataSize();
if (ir_size != m_i2c_bus.BusRead(CameraLogic::I2C_ADDR, camera_data_offset, ir_size, ir_data))
{
rpt_builder.SetCoreData(m_status.buttons);
}
// Acceleration:
if (rpt_builder.HasAccel())
{
rpt_builder.SetAccelData(target_state.acceleration);
}
// IR Camera:
if (rpt_builder.HasIR())
{
// Note: Camera logic currently contains no changing state so we can just update it here.
// If that changes this should be moved to Wiimote::Update();
m_camera_logic.Update(target_state.camera_points);
// The real wiimote reads camera data from the i2c bus starting at offset 0x37:
const u8 camera_data_offset =
CameraLogic::REPORT_DATA_OFFSET + rpt_builder.GetIRDataFormatOffset();
u8* ir_data = rpt_builder.GetIRDataPtr();
const u8 ir_size = rpt_builder.GetIRDataSize();
if (ir_size != m_i2c_bus.BusRead(CameraLogic::I2C_ADDR, camera_data_offset, ir_size, ir_data))
{
// This happens when IR reporting is enabled but the camera hardware is disabled.
// It commonly occurs when changing IR sensitivity.
std::fill_n(ir_data, ir_size, u8(0xff));
}
}
// Extension port:
if (rpt_builder.HasExt())
{
// Prepare extension input first as motion-plus may read from it.
// This currently happens in Wiimote::Update();
// TODO: Separate extension input data preparation from Update.
// GetActiveExtension()->PrepareInput();
if (m_is_motion_plus_attached)
{
// TODO: Make input preparation triggered by bus read.
m_motion_plus.PrepareInput(target_state.motion_plus.has_value() ?
target_state.motion_plus.value() :
MotionPlus::GetDefaultGyroscopeData());
}
u8* ext_data = rpt_builder.GetExtDataPtr();
const u8 ext_size = rpt_builder.GetExtDataSize();
if (ext_size != m_i2c_bus.BusRead(ExtensionPort::REPORT_I2C_SLAVE,
ExtensionPort::REPORT_I2C_ADDR, ext_size, ext_data))
{
// Real wiimote seems to fill with 0xff on failed bus read
std::fill_n(ext_data, ext_size, u8(0xff));
}
// This happens when IR reporting is enabled but the camera hardware is disabled.
// It commonly occurs when changing IR sensitivity.
std::fill_n(ir_data, ir_size, u8(0xff));
}
}
movie.CheckWiimoteStatus(m_bt_device_index, rpt_builder, m_active_extension,
GetExtensionEncryptionKey());
// Extension port:
if (rpt_builder.HasExt())
{
// Prepare extension input first as motion-plus may read from it.
// This currently happens in Wiimote::Update();
// TODO: Separate extension input data preparation from Update.
// GetActiveExtension()->PrepareInput();
if (m_is_motion_plus_attached)
{
// TODO: Make input preparation triggered by bus read.
m_motion_plus.PrepareInput(target_state.motion_plus.has_value() ?
target_state.motion_plus.value() :
MotionPlus::GetDefaultGyroscopeData());
}
u8* ext_data = rpt_builder.GetExtDataPtr();
const u8 ext_size = rpt_builder.GetExtDataSize();
if (ext_size != m_i2c_bus.BusRead(ExtensionPort::REPORT_I2C_SLAVE,
ExtensionPort::REPORT_I2C_ADDR, ext_size, ext_data))
{
// Real wiimote seems to fill with 0xff on failed bus read
std::fill_n(ext_data, ext_size, u8(0xff));
}
}
// Send the report:
InterruptDataInputCallback(rpt_builder.GetDataPtr(), rpt_builder.GetDataSize());
@ -741,10 +721,11 @@ void Wiimote::LoadDefaults(const ControllerInterface& ciface)
// B
m_buttons->SetControlExpression(1, "`Click 1`");
#endif
m_buttons->SetControlExpression(2, "`1`"); // 1
m_buttons->SetControlExpression(3, "`2`"); // 2
m_buttons->SetControlExpression(4, "Q"); // -
m_buttons->SetControlExpression(5, "E"); // +
// 1 2 - +
m_buttons->SetControlExpression(2, "`1`");
m_buttons->SetControlExpression(3, "`2`");
m_buttons->SetControlExpression(4, "Q");
m_buttons->SetControlExpression(5, "E");
#ifdef _WIN32
m_buttons->SetControlExpression(6, "RETURN"); // Home

View File

@ -11,8 +11,6 @@
#include "Common/Assert.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/NandPaths.h"
#include "Common/StringUtil.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/Debugger/Debugger_SymbolMap.h"
@ -22,6 +20,7 @@
#include "Core/HW/WiimoteEmu/DesiredWiimoteState.h"
#include "Core/IOS/Device.h"
#include "Core/IOS/IOS.h"
#include "Core/Movie.h"
#include "Core/NetPlayClient.h"
#include "Core/NetPlayProto.h"
#include "Core/SysConf.h"
@ -390,6 +389,16 @@ void BluetoothEmuDevice::Update()
}
}
auto& movie = Core::System::GetInstance().GetMovie();
for (int i = 0; i != MAX_WIIMOTES; ++i)
{
if (next_call[i] == WiimoteDevice::NextUpdateInputCall::None)
continue;
movie.PlayWiimote(i, &wiimote_states[i]);
movie.CheckWiimoteStatus(i, wiimote_states[i]);
}
for (size_t i = 0; i < m_wiimotes.size(); ++i)
m_wiimotes[i]->UpdateInput(next_call[i], wiimote_states[i]);

View File

@ -292,9 +292,8 @@ void WiimoteDevice::SetSource(WiimoteCommon::HIDWiimote* hid_source)
if (m_hid_source)
{
m_hid_source->SetInterruptCallback(std::bind(&WiimoteDevice::InterruptDataInputCallback, this,
std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3));
m_hid_source->SetInterruptCallback(
std::bind_front(&WiimoteDevice::InterruptDataInputCallback, this));
Activate(true);
}
}

View File

@ -12,7 +12,6 @@
#include <mbedtls/config.h>
#include <mbedtls/md.h>
#include <mutex>
#include <sstream>
#include <thread>
#include <utility>
#include <variant>
@ -32,13 +31,13 @@
#include "Common/NandPaths.h"
#include "Common/StringUtil.h"
#include "Common/Timer.h"
#include "Common/VariantUtil.h"
#include "Common/Version.h"
#include "Core/AchievementManager.h"
#include "Core/Boot/Boot.h"
#include "Core/Config/AchievementSettings.h"
#include "Core/Config/MainSettings.h"
#include "Core/Config/SYSCONFSettings.h"
#include "Core/Config/WiimoteSettings.h"
#include "Core/ConfigLoaders/MovieConfigLoader.h"
#include "Core/ConfigManager.h"
@ -54,14 +53,10 @@
#include "Core/HW/SI/SI.h"
#include "Core/HW/SI/SI_Device.h"
#include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteCommon/DataReport.h"
#include "Core/HW/WiimoteCommon/WiimoteReport.h"
#include "Core/HW/WiimoteEmu/Encryption.h"
#include "Core/HW/WiimoteEmu/Extension/Classic.h"
#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
#include "Core/HW/WiimoteEmu/ExtensionPort.h"
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
#include "Core/IOS/USB/Bluetooth/WiimoteDevice.h"
#include "Core/NetPlayProto.h"
@ -69,16 +64,11 @@
#include "Core/System.h"
#include "Core/WiiUtils.h"
#include "DiscIO/Enums.h"
#include "InputCommon/GCPadStatus.h"
#include "VideoCommon/VideoBackendBase.h"
#include "VideoCommon/VideoConfig.h"
// The chunk to allocate movie data in multiples of.
#define DTM_BASE_LENGTH (1024)
namespace Movie
{
using namespace WiimoteCommon;
@ -554,7 +544,7 @@ bool MovieManager::BeginRecordingInput(const ControllerTypeArray& controllers,
if (!Core::IsRunning(m_system))
{
// This will also reset the Wiimotes for GameCube games, but that shouldn't do anything
Wiimote::ResetAllWiimotes();
::Wiimote::ResetAllWiimotes();
}
m_play_mode = PlayMode::Recording;
@ -673,16 +663,13 @@ static std::string GenerateInputDisplayString(ControllerState padState, int cont
}
// NOTE: CPU Thread
static std::string GenerateWiiInputDisplayString(int remoteID, const DataReportBuilder& rpt,
ExtensionNumber ext, const EncryptionKey& key)
static std::string GenerateWiiInputDisplayString(int index, const DesiredWiimoteState& state)
{
std::string display_str = fmt::format("R{}:", remoteID + 1);
std::string display_str = fmt::format("R{}:", index + 1);
if (rpt.HasCore())
const auto& buttons = state.buttons;
if (buttons.hex & WiimoteCommon::ButtonData::BUTTON_MASK)
{
ButtonData buttons;
rpt.GetCoreData(&buttons);
if (buttons.left)
display_str += " LEFT";
if (buttons.right)
@ -707,94 +694,83 @@ static std::string GenerateWiiInputDisplayString(int remoteID, const DataReportB
display_str += " HOME";
}
if (rpt.HasAccel())
if (state.acceleration != state.DEFAULT_ACCELERATION)
{
AccelData accel_data;
rpt.GetAccelData(&accel_data);
// FYI: This will only print partial data for interleaved reports.
const AccelData& accel_data = state.acceleration;
display_str +=
fmt::format(" ACC:{},{},{}", accel_data.value.x, accel_data.value.y, accel_data.value.z);
}
if (rpt.HasIR())
if (state.camera_points != state.DEFAULT_CAMERA)
{
const u8* const ir_data = rpt.GetIRDataPtr();
// TODO: This does not handle the different IR formats.
const u16 x = ir_data[0] | ((ir_data[2] >> 4 & 0x3) << 8);
const u16 y = ir_data[1] | ((ir_data[2] >> 6 & 0x3) << 8);
display_str += fmt::format(" IR:{},{}", x, y);
display_str += " IR:";
for (auto& point : state.camera_points)
{
if (point.size == 0xff)
display_str += "_,";
else
display_str += fmt::format("{},{},", point.position.x, point.position.y);
}
display_str.pop_back();
}
// Nunchuk
if (rpt.HasExt() && ext == ExtensionNumber::NUNCHUK)
if (state.extension.data.index() != ExtensionNumber::NONE)
{
const u8* const extData = rpt.GetExtDataPtr();
const auto ext_visitor = overloaded{
[&](const Nunchuk::DataFormat& nunchuk) {
const auto bt = nunchuk.GetButtons();
if (bt & Nunchuk::BUTTON_C)
display_str += " C";
if (bt & Nunchuk::BUTTON_Z)
display_str += " Z";
display_str += fmt::format(" N-ACC:{},{},{}", nunchuk.GetAccelX(), nunchuk.GetAccelY(),
nunchuk.GetAccelZ());
display_str += Analog2DToString(nunchuk.jx, nunchuk.jy, " ANA");
},
[&](const Classic::DataFormat& cc) {
const auto bt = cc.GetButtons();
constexpr std::pair<u16, const char*> named_buttons[] = {
{Classic::PAD_LEFT, "LEFT"}, {Classic::PAD_RIGHT, "RIGHT"},
{Classic::PAD_DOWN, "DOWN"}, {Classic::PAD_UP, "UP"},
{Classic::BUTTON_A, "A"}, {Classic::BUTTON_B, "B"},
{Classic::BUTTON_X, "X"}, {Classic::BUTTON_Y, "Y"},
{Classic::BUTTON_ZL, "ZL"}, {Classic::BUTTON_ZR, "ZR"},
{Classic::BUTTON_PLUS, "+"}, {Classic::BUTTON_MINUS, "-"},
{Classic::BUTTON_HOME, "HOME"},
};
for (auto& [value, name] : named_buttons)
{
if (bt & value)
{
display_str += ' ';
display_str += name;
}
}
constexpr auto trigger_max = (1 << Classic::TRIGGER_BITS) - 1;
display_str += Analog1DToString(cc.GetLeftTrigger().value, " L", trigger_max);
display_str += Analog1DToString(cc.GetRightTrigger().value, " R", trigger_max);
Nunchuk::DataFormat nunchuk;
memcpy(&nunchuk, extData, sizeof(nunchuk));
key.Decrypt((u8*)&nunchuk, 0, sizeof(nunchuk));
nunchuk.bt.hex = nunchuk.bt.hex ^ 0x3;
constexpr auto lstick_max = (1 << Classic::LEFT_STICK_BITS) - 1;
const auto left_stick = cc.GetLeftStick().value;
display_str += Analog2DToString(left_stick.x, left_stick.y, " ANA", lstick_max);
const std::string accel = fmt::format(" N-ACC:{},{},{}", nunchuk.GetAccelX(),
nunchuk.GetAccelY(), nunchuk.GetAccelZ());
if (nunchuk.bt.c)
display_str += " C";
if (nunchuk.bt.z)
display_str += " Z";
display_str += accel;
display_str += Analog2DToString(nunchuk.jx, nunchuk.jy, " ANA");
}
// Classic controller
if (rpt.HasExt() && ext == ExtensionNumber::CLASSIC)
{
const u8* const extData = rpt.GetExtDataPtr();
Classic::DataFormat cc;
memcpy(&cc, extData, sizeof(cc));
key.Decrypt((u8*)&cc, 0, sizeof(cc));
cc.bt.hex = cc.bt.hex ^ 0xFFFF;
if (cc.bt.dpad_left)
display_str += " LEFT";
if (cc.bt.dpad_right)
display_str += " RIGHT";
if (cc.bt.dpad_down)
display_str += " DOWN";
if (cc.bt.dpad_up)
display_str += " UP";
if (cc.bt.a)
display_str += " A";
if (cc.bt.b)
display_str += " B";
if (cc.bt.x)
display_str += " X";
if (cc.bt.y)
display_str += " Y";
if (cc.bt.zl)
display_str += " ZL";
if (cc.bt.zr)
display_str += " ZR";
if (cc.bt.plus)
display_str += " +";
if (cc.bt.minus)
display_str += " -";
if (cc.bt.home)
display_str += " HOME";
display_str += Analog1DToString(cc.GetLeftTrigger().value, " L", 31);
display_str += Analog1DToString(cc.GetRightTrigger().value, " R", 31);
const auto left_stick = cc.GetLeftStick().value;
display_str += Analog2DToString(left_stick.x, left_stick.y, " ANA", 63);
const auto right_stick = cc.GetRightStick().value;
display_str += Analog2DToString(right_stick.x, right_stick.y, " R-ANA", 31);
constexpr auto rstick_max = (1 << Classic::RIGHT_STICK_BITS) - 1;
const auto right_stick = cc.GetRightStick().value;
display_str += Analog2DToString(right_stick.x, right_stick.y, " R-ANA", rstick_max);
},
[&](const Guitar::DataFormat&) { display_str += " Guitar"; },
[&](const Drums::DesiredState&) { display_str += " Drums"; },
[&](const Turntable::DataFormat&) { display_str += " Turntable"; },
[&](const UDrawTablet::DataFormat&) { display_str += " UDraw"; },
[&](const DrawsomeTablet::DataFormat&) { display_str += " Drawsome"; },
[&](const TaTaCon::DataFormat&) { display_str += " TaTaCon"; },
[&](const Shinkansen::DesiredState&) { display_str += " Shinkansen"; },
[](const auto& arg) {
static_assert(std::is_same_v<std::monostate, std::decay_t<decltype(arg)>>,
"unimplemented extension");
},
};
std::visit(ext_visitor, state.extension.data);
}
return display_str;
@ -857,29 +833,32 @@ void MovieManager::RecordInput(const GCPadStatus* PadStatus, int controllerID)
}
// NOTE: CPU Thread
void MovieManager::CheckWiimoteStatus(int wiimote, const DataReportBuilder& rpt,
ExtensionNumber ext, const EncryptionKey& key)
void MovieManager::CheckWiimoteStatus(int wiimote, const DesiredWiimoteState& desired_state)
{
SetPolledDevice();
{
std::string display_str = GenerateWiiInputDisplayString(wiimote, rpt, ext, key);
std::string display_str = GenerateWiiInputDisplayString(wiimote, desired_state);
std::lock_guard guard(m_input_display_lock);
m_input_display[wiimote + 4] = std::move(display_str);
}
if (IsRecordingInput())
RecordWiimote(wiimote, rpt.GetDataPtr(), rpt.GetDataSize());
RecordWiimote(wiimote, SerializeDesiredState(desired_state));
}
void MovieManager::RecordWiimote(int wiimote, const u8* data, u8 size)
void MovieManager::RecordWiimote(int wiimote, const SerializedWiimoteState& serialized_state)
{
if (!IsRecordingInput() || !IsUsingWiimote(wiimote))
return;
InputUpdate();
const u8 size = serialized_state.length;
m_temp_input.resize(m_current_byte + size + 1);
m_temp_input[m_current_byte++] = size;
memcpy(&m_temp_input[m_current_byte], data, size);
std::copy_n(serialized_state.data.data(), size, m_temp_input.data() + m_current_byte);
m_current_byte += size;
}
@ -955,7 +934,7 @@ bool MovieManager::PlayInput(const std::string& movie_path,
m_play_mode = PlayMode::Playing;
// Wiimotes cause desync issues if they're not reset before launching the game
Wiimote::ResetAllWiimotes();
::Wiimote::ResetAllWiimotes();
Core::UpdateWantDeterminism(m_system);
@ -1275,50 +1254,54 @@ void MovieManager::PlayController(GCPadStatus* PadStatus, int controllerID)
}
// NOTE: CPU Thread
bool MovieManager::PlayWiimote(int wiimote, WiimoteCommon::DataReportBuilder& rpt,
ExtensionNumber ext, const EncryptionKey& key)
bool MovieManager::PlayWiimote(int wiimote, DesiredWiimoteState* desired_state)
{
if (!IsPlayingInput() || !IsUsingWiimote(wiimote) || m_temp_input.empty())
return false;
if (m_current_byte > m_temp_input.size())
if (m_current_byte + sizeof(u8) > m_temp_input.size())
{
PanicAlertFmtT("Premature movie end in PlayWiimote. {0} > {1}", m_current_byte,
PanicAlertFmtT("Premature movie end in PlayWiimote. {0} + 1 > {1}", m_current_byte,
m_temp_input.size());
EndPlayInput(!m_read_only);
return false;
}
const u8 size = rpt.GetDataSize();
const u8 sizeInMovie = m_temp_input[m_current_byte];
SerializedWiimoteState serialized;
serialized.length = m_temp_input[m_current_byte];
if (size != sizeInMovie)
if (serialized.length > serialized.data.size())
{
PanicAlertFmtT(
"Fatal desync. Aborting playback. (Error in PlayWiimote: {0} != {1}, byte {2}.){3}",
sizeInMovie, size, m_current_byte,
(m_controllers == ControllerTypeArray{}) ?
" Try re-creating the recording with all GameCube controllers "
"disabled (in Configure > GameCube > Device Settings)." :
"");
PanicAlertFmtT("Invalid serialized length:{0} in PlayWiimote. byte:{1}", int(serialized.length),
m_current_byte);
EndPlayInput(!m_read_only);
return false;
}
m_current_byte++;
if (m_current_byte + size > m_temp_input.size())
++m_current_byte;
if (m_current_byte + serialized.length > m_temp_input.size())
{
PanicAlertFmtT("Premature movie end in PlayWiimote. {0} + {1} > {2}", m_current_byte, size,
m_temp_input.size());
PanicAlertFmtT("Premature movie end in PlayWiimote. {0} + {1} > {2}", m_current_byte,
int(serialized.length), m_temp_input.size());
EndPlayInput(!m_read_only);
return false;
}
memcpy(rpt.GetDataPtr(), &m_temp_input[m_current_byte], size);
m_current_byte += size;
std::copy_n(m_temp_input.data() + m_current_byte, serialized.length, serialized.data.data());
if (!WiimoteEmu::DeserializeDesiredState(desired_state, serialized))
{
PanicAlertFmtT("Aborting playback. Error in DeserializeDesiredState. byte:{0}{1}",
m_current_byte,
(m_controllers == ControllerTypeArray{}) ?
" Try re-creating the recording with all GameCube controllers "
"disabled (in Configure > GameCube > Device Settings)." :
"");
EndPlayInput(!m_read_only);
return false;
}
m_current_input_count++;
m_current_byte += serialized.length;
++m_current_input_count;
CheckInputEnd();
return true;

View File

@ -12,6 +12,7 @@
#include <vector>
#include "Common/CommonTypes.h"
#include "Core/HW/WiimoteEmu/DesiredWiimoteState.h"
struct BootParameters;
@ -206,21 +207,19 @@ public:
bool BeginRecordingInput(const ControllerTypeArray& controllers,
const WiimoteEnabledArray& wiimotes);
void RecordInput(const GCPadStatus* PadStatus, int controllerID);
void RecordWiimote(int wiimote, const u8* data, u8 size);
void RecordWiimote(int wiimote, const WiimoteEmu::SerializedWiimoteState& serialized_state);
bool PlayInput(const std::string& movie_path, std::optional<std::string>* savestate_path);
void LoadInput(const std::string& movie_path);
void ReadHeader();
void PlayController(GCPadStatus* PadStatus, int controllerID);
bool PlayWiimote(int wiimote, WiimoteCommon::DataReportBuilder& rpt,
WiimoteEmu::ExtensionNumber ext, const WiimoteEmu::EncryptionKey& key);
bool PlayWiimote(int wiimote, WiimoteEmu::DesiredWiimoteState* desired_state);
void EndPlayInput(bool cont);
void SaveRecording(const std::string& filename);
void DoState(PointerWrap& p);
void Shutdown();
void CheckPadStatus(const GCPadStatus* PadStatus, int controllerID);
void CheckWiimoteStatus(int wiimote, const WiimoteCommon::DataReportBuilder& rpt,
WiimoteEmu::ExtensionNumber ext, const WiimoteEmu::EncryptionKey& key);
void CheckWiimoteStatus(int wiimote, const WiimoteEmu::DesiredWiimoteState& desired_state);
std::string GetInputDisplay();
std::string GetRTCDisplay() const;