diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index 474b42a60..af05827a0 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -241,6 +241,7 @@ int main(int argc, char** argv) { if (!movie_record.empty() && !movie_play.empty()) { LOG_CRITICAL(Frontend, "Cannot both play and record a movie"); + return -1; } log_filter.ParseFilterString(Settings::values.log_filter); diff --git a/src/core/core.cpp b/src/core/core.cpp index 653e33c42..c861e345c 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -161,7 +161,7 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { Service::Init(); AudioCore::Init(); GDBStub::Init(); - Movie::Init(); + Movie::GetInstance().Init(); if (!VideoCore::Init(emu_window)) { return ResultStatus::ErrorVideoCore; @@ -187,7 +187,7 @@ void System::Shutdown() { perf_results.frametime * 1000.0); // Shutdown emulation session - Movie::Shutdown(); + Movie::GetInstance().Shutdown(); GDBStub::Shutdown(); AudioCore::Shutdown(); VideoCore::Shutdown(); diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 85bc54c4d..bb6ffad24 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -138,7 +138,7 @@ static void UpdatePadCallback(u64 userdata, int cycles_late) { s16 circle_pad_x = static_cast(circle_pad_x_f * MAX_CIRCLEPAD_POS); s16 circle_pad_y = static_cast(circle_pad_y_f * MAX_CIRCLEPAD_POS); - Movie::HandlePadAndCircleStatus(state, circle_pad_x, circle_pad_y); + Core::Movie::GetInstance().HandlePadAndCircleStatus(state, circle_pad_x, circle_pad_y); const DirectionState direction = GetStickDirectionState(circle_pad_x, circle_pad_y); state.circle_up.Assign(direction.up); @@ -185,7 +185,7 @@ static void UpdatePadCallback(u64 userdata, int cycles_late) { touch_entry.y = static_cast(y * Core::kScreenBottomHeight); touch_entry.valid.Assign(pressed ? 1 : 0); - Movie::HandleTouchStatus(touch_entry); + Core::Movie::GetInstance().HandleTouchStatus(touch_entry); // TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which // supposedly is "Touch-screen entry, which contains the raw coordinate data prior to being @@ -225,7 +225,7 @@ static void UpdateAccelerometerCallback(u64 userdata, int cycles_late) { accelerometer_entry.y = static_cast(accel.y); accelerometer_entry.z = static_cast(accel.z); - Movie::HandleAccelerometerStatus(accelerometer_entry); + Core::Movie::GetInstance().HandleAccelerometerStatus(accelerometer_entry); // Make up "raw" entry // TODO(wwylele): @@ -265,7 +265,7 @@ static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) { gyroscope_entry.y = static_cast(gyro.y); gyroscope_entry.z = static_cast(gyro.z); - Movie::HandleGyroscopeStatus(gyroscope_entry); + Core::Movie::GetInstance().HandleGyroscopeStatus(gyroscope_entry); // Make up "raw" entry mem->gyroscope.raw_entry.x = gyroscope_entry.x; diff --git a/src/core/hle/service/ir/extra_hid.cpp b/src/core/hle/service/ir/extra_hid.cpp index 1d3df1a4b..9f9325728 100644 --- a/src/core/hle/service/ir/extra_hid.cpp +++ b/src/core/hle/service/ir/extra_hid.cpp @@ -194,7 +194,7 @@ void ExtraHID::SendHIDStatus() { response.buttons.r_not_held.Assign(1); response.unknown = 0; - Movie::HandleExtraHidResponse(response); + Core::Movie::GetInstance().HandleExtraHidResponse(response); std::vector response_buffer(sizeof(response)); memcpy(response_buffer.data(), &response, sizeof(response)); diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp index 4f5af8b02..22ca83655 100644 --- a/src/core/hle/service/ir/ir_rst.cpp +++ b/src/core/hle/service/ir/ir_rst.cpp @@ -65,7 +65,7 @@ void IR_RST::UpdateCallback(u64 userdata, int cycles_late) { s16 c_stick_x = static_cast(c_stick_x_f * MAX_CSTICK_RADIUS); s16 c_stick_y = static_cast(c_stick_y_f * MAX_CSTICK_RADIUS); - Movie::HandleIrRst(state, c_stick_x, c_stick_y); + Core::Movie::GetInstance().HandleIrRst(state, c_stick_x, c_stick_y); if (!raw_c_stick) { const HID::DirectionState direction = HID::GetStickDirectionState(c_stick_x, c_stick_y); diff --git a/src/core/movie.cpp b/src/core/movie.cpp index 0a6e82b63..87271f73f 100644 --- a/src/core/movie.cpp +++ b/src/core/movie.cpp @@ -19,7 +19,9 @@ #include "core/hle/service/ir/ir_rst.h" #include "core/movie.h" -namespace Movie { +namespace Core { + +/*static*/ Movie Movie::s_instance; enum class PlayMode { None, Recording, Playing }; @@ -116,25 +118,21 @@ struct CTMHeader { static_assert(sizeof(CTMHeader) == 256, "CTMHeader should be 256 bytes"); #pragma pack(pop) -static PlayMode play_mode = PlayMode::None; -static std::vector recorded_input; -static size_t current_byte = 0; - -static bool IsPlayingInput() { +bool Movie::IsPlayingInput() { return play_mode == PlayMode::Playing; } -static bool IsRecordingInput() { +bool Movie::IsRecordingInput() { return play_mode == PlayMode::Recording; } -static void CheckInputEnd() { +void Movie::CheckInputEnd() { if (current_byte + sizeof(ControllerState) > recorded_input.size()) { LOG_INFO(Movie, "Playback finished"); play_mode = PlayMode::None; } } -static void Play(Service::HID::PadState& pad_state, s16& circle_pad_x, s16& circle_pad_y) { +void Movie::Play(Service::HID::PadState& pad_state, s16& circle_pad_x, s16& circle_pad_y) { ControllerState s; std::memcpy(&s, &recorded_input[current_byte], sizeof(ControllerState)); current_byte += sizeof(ControllerState); @@ -163,7 +161,7 @@ static void Play(Service::HID::PadState& pad_state, s16& circle_pad_x, s16& circ circle_pad_y = s.pad_and_circle.circle_pad_y; } -static void Play(Service::HID::TouchDataEntry& touch_data) { +void Movie::Play(Service::HID::TouchDataEntry& touch_data) { ControllerState s; std::memcpy(&s, &recorded_input[current_byte], sizeof(ControllerState)); current_byte += sizeof(ControllerState); @@ -180,7 +178,7 @@ static void Play(Service::HID::TouchDataEntry& touch_data) { touch_data.valid.Assign(s.touch.valid); } -static void Play(Service::HID::AccelerometerDataEntry& accelerometer_data) { +void Movie::Play(Service::HID::AccelerometerDataEntry& accelerometer_data) { ControllerState s; std::memcpy(&s, &recorded_input[current_byte], sizeof(ControllerState)); current_byte += sizeof(ControllerState); @@ -197,7 +195,7 @@ static void Play(Service::HID::AccelerometerDataEntry& accelerometer_data) { accelerometer_data.z = s.accelerometer.z; } -static void Play(Service::HID::GyroscopeDataEntry& gyroscope_data) { +void Movie::Play(Service::HID::GyroscopeDataEntry& gyroscope_data) { ControllerState s; std::memcpy(&s, &recorded_input[current_byte], sizeof(ControllerState)); current_byte += sizeof(ControllerState); @@ -214,7 +212,7 @@ static void Play(Service::HID::GyroscopeDataEntry& gyroscope_data) { gyroscope_data.z = s.gyroscope.z; } -static void Play(Service::IR::PadState& pad_state, s16& c_stick_x, s16& c_stick_y) { +void Movie::Play(Service::IR::PadState& pad_state, s16& c_stick_x, s16& c_stick_y) { ControllerState s; std::memcpy(&s, &recorded_input[current_byte], sizeof(ControllerState)); current_byte += sizeof(ControllerState); @@ -232,7 +230,7 @@ static void Play(Service::IR::PadState& pad_state, s16& c_stick_x, s16& c_stick_ pad_state.zr.Assign(s.ir_rst.zr); } -static void Play(Service::IR::ExtraHIDResponse& extra_hid_response) { +void Movie::Play(Service::IR::ExtraHIDResponse& extra_hid_response) { ControllerState s; std::memcpy(&s, &recorded_input[current_byte], sizeof(ControllerState)); current_byte += sizeof(ControllerState); @@ -252,13 +250,13 @@ static void Play(Service::IR::ExtraHIDResponse& extra_hid_response) { extra_hid_response.buttons.zr_not_held.Assign(s.extra_hid_response.zr_not_held); } -static void Record(const ControllerState& controller_state) { +void Movie::Record(const ControllerState& controller_state) { recorded_input.resize(current_byte + sizeof(ControllerState)); std::memcpy(&recorded_input[current_byte], &controller_state, sizeof(ControllerState)); current_byte += sizeof(ControllerState); } -static void Record(const Service::HID::PadState& pad_state, const s16& circle_pad_x, +void Movie::Record(const Service::HID::PadState& pad_state, const s16& circle_pad_x, const s16& circle_pad_y) { ControllerState s; s.type = ControllerStateType::PadAndCircle; @@ -282,7 +280,7 @@ static void Record(const Service::HID::PadState& pad_state, const s16& circle_pa Record(s); } -static void Record(const Service::HID::TouchDataEntry& touch_data) { +void Movie::Record(const Service::HID::TouchDataEntry& touch_data) { ControllerState s; s.type = ControllerStateType::Touch; @@ -293,7 +291,7 @@ static void Record(const Service::HID::TouchDataEntry& touch_data) { Record(s); } -static void Record(const Service::HID::AccelerometerDataEntry& accelerometer_data) { +void Movie::Record(const Service::HID::AccelerometerDataEntry& accelerometer_data) { ControllerState s; s.type = ControllerStateType::Accelerometer; @@ -304,7 +302,7 @@ static void Record(const Service::HID::AccelerometerDataEntry& accelerometer_dat Record(s); } -static void Record(const Service::HID::GyroscopeDataEntry& gyroscope_data) { +void Movie::Record(const Service::HID::GyroscopeDataEntry& gyroscope_data) { ControllerState s; s.type = ControllerStateType::Gyroscope; @@ -315,7 +313,7 @@ static void Record(const Service::HID::GyroscopeDataEntry& gyroscope_data) { Record(s); } -static void Record(const Service::IR::PadState& pad_state, const s16& c_stick_x, +void Movie::Record(const Service::IR::PadState& pad_state, const s16& c_stick_x, const s16& c_stick_y) { ControllerState s; s.type = ControllerStateType::IrRst; @@ -328,7 +326,7 @@ static void Record(const Service::IR::PadState& pad_state, const s16& c_stick_x, Record(s); } -static void Record(const Service::IR::ExtraHIDResponse& extra_hid_response) { +void Movie::Record(const Service::IR::ExtraHIDResponse& extra_hid_response) { ControllerState s; s.type = ControllerStateType::ExtraHidResponse; @@ -342,7 +340,7 @@ static void Record(const Service::IR::ExtraHIDResponse& extra_hid_response) { Record(s); } -static bool ValidateHeader(const CTMHeader& header) { +bool Movie::ValidateHeader(const CTMHeader& header) { if (header_magic_bytes != header.filetype) { LOG_ERROR(Movie, "Playback file does not have valid header"); return false; @@ -366,7 +364,7 @@ static bool ValidateHeader(const CTMHeader& header) { return true; } -static void SaveMovie() { +void Movie::SaveMovie() { LOG_INFO(Movie, "Saving movie"); FileUtil::IOFile save_record(Settings::values.movie_record, "wb"); @@ -393,7 +391,7 @@ static void SaveMovie() { } } -void Init() { +void Movie::Init() { if (!Settings::values.movie_play.empty()) { LOG_INFO(Movie, "Loading Movie for playback"); FileUtil::IOFile save_record(Settings::values.movie_play, "rb"); @@ -420,21 +418,20 @@ void Init() { } } -void Shutdown() { - if (!IsRecordingInput()) { - return; +void Movie::Shutdown() { + if (IsRecordingInput()) { + SaveMovie(); } - SaveMovie(); - play_mode = PlayMode::None; recorded_input.resize(0); current_byte = 0; } template -static void Handle(Targs&... Fargs) { +void Movie::Handle(Targs&... Fargs) { if (IsPlayingInput()) { + ASSERT(current_byte + sizeof(ControllerState) <= recorded_input.size()); Play(Fargs...); CheckInputEnd(); } else if (IsRecordingInput()) { @@ -442,28 +439,28 @@ static void Handle(Targs&... Fargs) { } } -void HandlePadAndCircleStatus(Service::HID::PadState& pad_state, s16& circle_pad_x, - s16& circle_pad_y) { +void Movie::HandlePadAndCircleStatus(Service::HID::PadState& pad_state, s16& circle_pad_x, + s16& circle_pad_y) { Handle(pad_state, circle_pad_x, circle_pad_y); } -void HandleTouchStatus(Service::HID::TouchDataEntry& touch_data) { +void Movie::HandleTouchStatus(Service::HID::TouchDataEntry& touch_data) { Handle(touch_data); } -void HandleAccelerometerStatus(Service::HID::AccelerometerDataEntry& accelerometer_data) { +void Movie::HandleAccelerometerStatus(Service::HID::AccelerometerDataEntry& accelerometer_data) { Handle(accelerometer_data); } -void HandleGyroscopeStatus(Service::HID::GyroscopeDataEntry& gyroscope_data) { +void Movie::HandleGyroscopeStatus(Service::HID::GyroscopeDataEntry& gyroscope_data) { Handle(gyroscope_data); } -void HandleIrRst(Service::IR::PadState& pad_state, s16& c_stick_x, s16& c_stick_y) { +void Movie::HandleIrRst(Service::IR::PadState& pad_state, s16& c_stick_x, s16& c_stick_y) { Handle(pad_state, c_stick_x, c_stick_y); } -void HandleExtraHidResponse(Service::IR::ExtraHIDResponse& extra_hid_response) { +void Movie::HandleExtraHidResponse(Service::IR::ExtraHIDResponse& extra_hid_response) { Handle(extra_hid_response); } -} +}; // namespace Core diff --git a/src/core/movie.h b/src/core/movie.h index 44b1978a2..c406252a4 100644 --- a/src/core/movie.h +++ b/src/core/movie.h @@ -19,46 +19,96 @@ union PadState; } } -namespace Movie { +namespace Core { +struct CTMHeader; +struct ControllerState; +enum class PlayMode; -void Init(); +class Movie { +public: + /** + * Gets the instance of the Movie singleton class. + * @returns Reference to the instance of the Movie singleton class. + */ + static Movie& GetInstance() { + return s_instance; + } -void Shutdown(); + void Init(); -/** - * When recording: Takes a copy of the given input states so they can be used for playback - * When playing: Replaces the given input states with the ones stored in the playback file - */ -void HandlePadAndCircleStatus(Service::HID::PadState& pad_state, s16& circle_pad_x, - s16& circle_pad_y); + void Shutdown(); -/** -* When recording: Takes a copy of the given input states so they can be used for playback -* When playing: Replaces the given input states with the ones stored in the playback file -*/ -void HandleTouchStatus(Service::HID::TouchDataEntry& touch_data); + /** + * When recording: Takes a copy of the given input states so they can be used for playback + * When playing: Replaces the given input states with the ones stored in the playback file + */ + void HandlePadAndCircleStatus(Service::HID::PadState& pad_state, s16& circle_pad_x, + s16& circle_pad_y); -/** -* When recording: Takes a copy of the given input states so they can be used for playback -* When playing: Replaces the given input states with the ones stored in the playback file -*/ -void HandleAccelerometerStatus(Service::HID::AccelerometerDataEntry& accelerometer_data); + /** + * When recording: Takes a copy of the given input states so they can be used for playback + * When playing: Replaces the given input states with the ones stored in the playback file + */ + void HandleTouchStatus(Service::HID::TouchDataEntry& touch_data); -/** -* When recording: Takes a copy of the given input states so they can be used for playback -* When playing: Replaces the given input states with the ones stored in the playback file -*/ -void HandleGyroscopeStatus(Service::HID::GyroscopeDataEntry& gyroscope_data); + /** + * When recording: Takes a copy of the given input states so they can be used for playback + * When playing: Replaces the given input states with the ones stored in the playback file + */ + void HandleAccelerometerStatus(Service::HID::AccelerometerDataEntry& accelerometer_data); -/** -* When recording: Takes a copy of the given input states so they can be used for playback -* When playing: Replaces the given input states with the ones stored in the playback file -*/ -void HandleIrRst(Service::IR::PadState& pad_state, s16& c_stick_x, s16& c_stick_y); + /** + * When recording: Takes a copy of the given input states so they can be used for playback + * When playing: Replaces the given input states with the ones stored in the playback file + */ + void HandleGyroscopeStatus(Service::HID::GyroscopeDataEntry& gyroscope_data); -/** -* When recording: Takes a copy of the given input states so they can be used for playback -* When playing: Replaces the given input states with the ones stored in the playback file -*/ -void HandleExtraHidResponse(Service::IR::ExtraHIDResponse& extra_hid_response); -} + /** + * When recording: Takes a copy of the given input states so they can be used for playback + * When playing: Replaces the given input states with the ones stored in the playback file + */ + void HandleIrRst(Service::IR::PadState& pad_state, s16& c_stick_x, s16& c_stick_y); + + /** + * When recording: Takes a copy of the given input states so they can be used for playback + * When playing: Replaces the given input states with the ones stored in the playback file + */ + void HandleExtraHidResponse(Service::IR::ExtraHIDResponse& extra_hid_response); + +private: + static Movie s_instance; + + bool IsPlayingInput(); + + bool IsRecordingInput(); + + void CheckInputEnd(); + + template + void Handle(Targs&... Fargs); + + void Play(Service::HID::PadState& pad_state, s16& circle_pad_x, s16& circle_pad_y); + void Play(Service::HID::TouchDataEntry& touch_data); + void Play(Service::HID::AccelerometerDataEntry& accelerometer_data); + void Play(Service::HID::GyroscopeDataEntry& gyroscope_data); + void Play(Service::IR::PadState& pad_state, s16& c_stick_x, s16& c_stick_y); + void Play(Service::IR::ExtraHIDResponse& extra_hid_response); + + void Record(const ControllerState& controller_state); + void Record(const Service::HID::PadState& pad_state, const s16& circle_pad_x, + const s16& circle_pad_y); + void Record(const Service::HID::TouchDataEntry& touch_data); + void Record(const Service::HID::AccelerometerDataEntry& accelerometer_data); + void Record(const Service::HID::GyroscopeDataEntry& gyroscope_data); + void Record(const Service::IR::PadState& pad_state, const s16& c_stick_x, const s16& c_stick_y); + void Record(const Service::IR::ExtraHIDResponse& extra_hid_response); + + bool ValidateHeader(const CTMHeader& header); + + void SaveMovie(); + + PlayMode play_mode; + std::vector recorded_input; + size_t current_byte = 0; +}; +} // namespace Core \ No newline at end of file