From 0238e0c5e7071a7c450d258a47106610b3b3fbf3 Mon Sep 17 00:00:00 2001
From: danzel <danzel@localhost.geek.nz>
Date: Sun, 17 Dec 2017 17:55:56 +1300
Subject: [PATCH] Convert Movie to a class with a static instance, and other
 fixes based on B3n30 feedback.

---
 src/citra/citra.cpp                   |   1 +
 src/core/core.cpp                     |   4 +-
 src/core/hle/service/hid/hid.cpp      |   8 +-
 src/core/hle/service/ir/extra_hid.cpp |   2 +-
 src/core/hle/service/ir/ir_rst.cpp    |   2 +-
 src/core/movie.cpp                    |  73 ++++++++--------
 src/core/movie.h                      | 120 ++++++++++++++++++--------
 7 files changed, 129 insertions(+), 81 deletions(-)

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<s16>(circle_pad_x_f * MAX_CIRCLEPAD_POS);
     s16 circle_pad_y = static_cast<s16>(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<u16>(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<s16>(accel.y);
     accelerometer_entry.z = static_cast<s16>(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<s16>(gyro.y);
     gyroscope_entry.z = static_cast<s16>(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<u8> 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<s16>(c_stick_x_f * MAX_CSTICK_RADIUS);
     s16 c_stick_y = static_cast<s16>(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<u8> 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 <typename... Targs>
-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 <typename... Targs>
+    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<u8> recorded_input;
+    size_t current_byte = 0;
+};
+} // namespace Core
\ No newline at end of file