From ad6b140cb020d04ecbbeacbe0ebc3267c9a8f032 Mon Sep 17 00:00:00 2001
From: zhupengfei <zhupengfei321@sina.cn>
Date: Wed, 18 Jul 2018 20:07:00 +0800
Subject: [PATCH] service/apt: Implement soft reset & CloseApplication

---
 src/citra_qt/bootmanager.cpp       |  6 +++
 src/citra_qt/main.cpp              |  7 ++--
 src/core/core.cpp                  | 18 +++++++++
 src/core/core.h                    | 20 ++++++++++
 src/core/hle/service/apt/apt.cpp   | 59 ++++++++++++++++++++++++++++++
 src/core/hle/service/apt/apt.h     | 44 ++++++++++++++++++++++
 src/core/hle/service/apt/apt_a.cpp |  6 +--
 src/core/hle/service/apt/apt_s.cpp |  6 +--
 src/core/hle/service/apt/apt_u.cpp |  6 +--
 9 files changed, 160 insertions(+), 12 deletions(-)

diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index 17868da69..aa828f7fd 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -35,6 +35,12 @@ void EmuThread::run() {
                 emit DebugModeLeft();
 
             Core::System::ResultStatus result = Core::System::GetInstance().RunLoop();
+            if (result == Core::System::ResultStatus::ShutdownRequested) {
+                // Notify frontend we shutdown
+                emit ErrorThrown(result, "");
+                // End emulation execution
+                break;
+            }
             if (result != Core::System::ResultStatus::Success) {
                 this->SetRunning(false);
                 emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails());
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index 3f0388d0a..43cf839ae 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -1400,7 +1400,6 @@ void GMainWindow::UpdateStatusBar() {
 }
 
 void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) {
-    QMessageBox::StandardButton answer;
     QString status_message;
 
     QString title, message;
@@ -1435,9 +1434,11 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
     message_box.setIcon(QMessageBox::Icon::Critical);
     QPushButton* continue_button = message_box.addButton(tr("Continue"), QMessageBox::RejectRole);
     QPushButton* abort_button = message_box.addButton(tr("Abort"), QMessageBox::AcceptRole);
-    message_box.exec();
+    if (result != Core::System::ResultStatus::ShutdownRequested)
+        message_box.exec();
 
-    if (message_box.clickedButton() == abort_button) {
+    if (result == Core::System::ResultStatus::ShutdownRequested ||
+        message_box.clickedButton() == abort_button) {
         if (emu_thread) {
             ShutdownGame();
         }
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 0ce5fcb8b..1e49bd817 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -76,6 +76,12 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
     HW::Update();
     Reschedule();
 
+    if (reset_requested.exchange(false)) {
+        Reset();
+    } else if (shutdown_requested.exchange(false)) {
+        return ResultStatus::ShutdownRequested;
+    }
+
     return status;
 }
 
@@ -131,6 +137,8 @@ System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& file
     }
     Memory::SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table);
     status = ResultStatus::Success;
+    m_emu_window = &emu_window;
+    m_filepath = filepath;
     return status;
 }
 
@@ -238,4 +246,14 @@ void System::Shutdown() {
     LOG_DEBUG(Core, "Shutdown OK");
 }
 
+void System::Reset() {
+    // This is NOT a proper reset, but a temporary workaround by shutting down the system and
+    // reloading.
+    // TODO: Properly implement the reset
+
+    Shutdown();
+    // Reload the system with the same setting
+    Load(*m_emu_window, m_filepath);
+}
+
 } // namespace Core
diff --git a/src/core/core.h b/src/core/core.h
index b4f3408f3..21e50a5a6 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -55,6 +55,7 @@ public:
                                             /// generic drivers installed
         ErrorVideoCore_ErrorBelowGL33,      ///< Error in the video core due to the user not having
                                             /// OpenGL 3.3 or higher
+        ShutdownRequested,                  ///< Emulated program requested a system shutdown
         ErrorUnknown                        ///< Any other error
     };
 
@@ -79,6 +80,19 @@ public:
     /// Shutdown the emulated system.
     void Shutdown();
 
+    /// Shutdown and then load again
+    void Reset();
+
+    /// Request reset of the system
+    void RequestReset() {
+        reset_requested = true;
+    }
+
+    /// Request shutdown of the system
+    void RequestShutdown() {
+        shutdown_requested = true;
+    }
+
     /**
      * Load an executable application.
      * @param emu_window Reference to the host-system window used for video output and keyboard
@@ -209,6 +223,12 @@ private:
 
     ResultStatus status = ResultStatus::Success;
     std::string status_details = "";
+    /// Saved variables for reset
+    EmuWindow* m_emu_window;
+    std::string m_filepath;
+
+    std::atomic<bool> reset_requested;
+    std::atomic<bool> shutdown_requested;
 };
 
 inline ARM_Interface& CPU() {
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index 656e2fd77..776fba72f 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -543,6 +543,65 @@ void Module::Interface::StartLibraryApplet(Kernel::HLERequestContext& ctx) {
     rb.Push(apt->applet_manager->StartLibraryApplet(applet_id, object, buffer));
 }
 
+void Module::Interface::CloseApplication(Kernel::HLERequestContext& ctx) {
+    IPC::RequestParser rp(ctx, 0x27, 1, 4);
+    u32 parameters_size = rp.Pop<u32>();
+    Kernel::SharedPtr<Kernel::Object> object = rp.PopGenericObject();
+    std::vector<u8> buffer = rp.PopStaticBuffer();
+
+    LOG_DEBUG(Service_APT, "called");
+
+    Core::System::GetInstance().RequestShutdown();
+
+    IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+    rb.Push(RESULT_SUCCESS);
+}
+
+void Module::Interface::PrepareToDoApplicationJump(Kernel::HLERequestContext& ctx) {
+    IPC::RequestParser rp(ctx, 0x31, 4, 0);
+    u32 flags = rp.Pop<u8>();
+    u32 program_id_low = rp.Pop<u32>();
+    u32 program_id_high = rp.Pop<u32>();
+    Service::FS::MediaType media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
+
+    LOG_WARNING(Service_APT,
+                "(STUBBED) called, flags={:08X}, program_id_low={:08X}, program_id_high={:08X}, "
+                "media_type={:08X}",
+                flags, program_id_low, program_id_high, static_cast<u8>(media_type));
+
+    if (flags == 0x2) {
+        // It seems that flags 0x2 means jumping to the same application,
+        // and ignore the parameters. This is used in Pokemon main series
+        // to soft reset.
+        application_reset_prepared = true;
+    }
+
+    IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+    rb.Push(RESULT_SUCCESS);
+}
+
+void Module::Interface::DoApplicationJump(Kernel::HLERequestContext& ctx) {
+    IPC::RequestParser rp(ctx, 0x32, 2, 4);
+    u32 parameter_size = rp.Pop<u32>();
+    u32 hmac_size = rp.Pop<u32>();
+    std::vector<u8> parameter = rp.PopStaticBuffer();
+    std::vector<u8> hmac = rp.PopStaticBuffer();
+
+    LOG_WARNING(Service_APT, "(STUBBED) called");
+
+    if (application_reset_prepared) {
+        // Reset system
+        Core::System::GetInstance().RequestReset();
+    } else {
+        // After the jump, the application should shutdown
+        // TODO: Actually implement the jump
+        Core::System::GetInstance().RequestShutdown();
+    }
+
+    IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+    rb.Push(RESULT_SUCCESS);
+}
+
 void Module::Interface::CancelLibraryApplet(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp(ctx, 0x3B, 1, 0); // 0x003B0040
     bool exiting = rp.Pop<bool>();
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index 488c429f6..54149f4e9 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -409,6 +409,49 @@ public:
          */
         void StartLibraryApplet(Kernel::HLERequestContext& ctx);
 
+        /**
+         * APT::CloseApplication service function
+         *  Inputs:
+         *      0 : Command header [0x00270044]
+         *      1 : Parameters Size
+         *      2 : 0x0
+         *      3 : Handle Parameter
+         *      4 : (Parameters Size << 14) | 2
+         *      5 : void*, Parameters
+         *  Outputs:
+         *      1 : Result of function, 0 on success, otherwise error code
+         */
+        void CloseApplication(Kernel::HLERequestContext& ctx);
+
+        /**
+         * APT::PrepareToDoApplicationJump service function
+         *  Inputs:
+         *      0 : Command header [0x00310100]
+         *      1 : Flags
+         *      2 : Program ID low
+         *      3 : Program ID high
+         *      4 : Media type
+         *  Outputs:
+         *      1 : Result of function, 0 on success, otherwise error code
+         * @param ctx
+         */
+        void PrepareToDoApplicationJump(Kernel::HLERequestContext& ctx);
+
+        /**
+         * APT::DoApplicationJump service function
+         *  Inputs:
+         *      0 : Command header [0x00320084]
+         *      1 : Parameter Size (capped to 0x300)
+         *      2 : HMAC Size (capped to 0x20)
+         *      3 : (Parameter Size << 14) | 2
+         *      4 : void*, Parameter
+         *      5 : (HMAC Size << 14) | 0x802
+         *      6 : void*, HMAC
+         *  Outputs:
+         *      1 : Result of function, 0 on success, otherwise error code
+         */
+        void DoApplicationJump(Kernel::HLERequestContext& ctx);
+
         /**
          * APT::CancelLibraryApplet service function
          *  Inputs:
@@ -533,6 +576,7 @@ public:
 
     private:
         std::shared_ptr<Module> apt;
+        bool application_reset_prepared{};
     };
 
 private:
diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp
index b69ffcc11..8ad09025d 100644
--- a/src/core/hle/service/apt/apt_a.cpp
+++ b/src/core/hle/service/apt/apt_a.cpp
@@ -48,7 +48,7 @@ APT_A::APT_A(std::shared_ptr<Module> apt)
         {0x00240044, nullptr, "JumpToApplication"},
         {0x002500C0, &APT_A::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"},
         {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
-        {0x00270044, nullptr, "CloseApplication"},
+        {0x00270044, &APT_A::CloseApplication, "CloseApplication"},
         {0x00280044, &APT_A::CloseLibraryApplet, "CloseLibraryApplet"},
         {0x00290044, nullptr, "CloseSystemApplet"},
         {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
@@ -58,8 +58,8 @@ APT_A::APT_A(std::shared_ptr<Module> apt)
         {0x002E0044, nullptr, "LeaveHomeMenu"},
         {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
         {0x00300044, nullptr, "LeaveResidentApplet"},
-        {0x00310100, nullptr, "PrepareToDoApplicationJump"},
-        {0x00320084, nullptr, "DoApplicationJump"},
+        {0x00310100, &APT_A::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"},
+        {0x00320084, &APT_A::DoApplicationJump, "DoApplicationJump"},
         {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
         {0x00340084, nullptr, "SendDeliverArg"},
         {0x00350080, nullptr, "ReceiveDeliverArg"},
diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp
index 67ed7b9d9..341c384ca 100644
--- a/src/core/hle/service/apt/apt_s.cpp
+++ b/src/core/hle/service/apt/apt_s.cpp
@@ -48,7 +48,7 @@ APT_S::APT_S(std::shared_ptr<Module> apt)
         {0x00240044, nullptr, "JumpToApplication"},
         {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
         {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
-        {0x00270044, nullptr, "CloseApplication"},
+        {0x00270044, &APT_S::CloseApplication, "CloseApplication"},
         {0x00280044, nullptr, "CloseLibraryApplet"},
         {0x00290044, nullptr, "CloseSystemApplet"},
         {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
@@ -58,8 +58,8 @@ APT_S::APT_S(std::shared_ptr<Module> apt)
         {0x002E0044, nullptr, "LeaveHomeMenu"},
         {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
         {0x00300044, nullptr, "LeaveResidentApplet"},
-        {0x00310100, nullptr, "PrepareToDoApplicationJump"},
-        {0x00320084, nullptr, "DoApplicationJump"},
+        {0x00310100, &APT_S::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"},
+        {0x00320084, &APT_S::DoApplicationJump, "DoApplicationJump"},
         {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
         {0x00340084, nullptr, "SendDeliverArg"},
         {0x00350080, nullptr, "ReceiveDeliverArg"},
diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp
index 44c9ac8e5..9fcb38005 100644
--- a/src/core/hle/service/apt/apt_u.cpp
+++ b/src/core/hle/service/apt/apt_u.cpp
@@ -48,7 +48,7 @@ APT_U::APT_U(std::shared_ptr<Module> apt)
         {0x00240044, nullptr, "JumpToApplication"},
         {0x002500C0, &APT_U::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"},
         {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
-        {0x00270044, nullptr, "CloseApplication"},
+        {0x00270044, &APT_U::CloseApplication, "CloseApplication"},
         {0x00280044, &APT_U::CloseLibraryApplet, "CloseLibraryApplet"},
         {0x00290044, nullptr, "CloseSystemApplet"},
         {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
@@ -58,8 +58,8 @@ APT_U::APT_U(std::shared_ptr<Module> apt)
         {0x002E0044, nullptr, "LeaveHomeMenu"},
         {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
         {0x00300044, nullptr, "LeaveResidentApplet"},
-        {0x00310100, nullptr, "PrepareToDoApplicationJump"},
-        {0x00320084, nullptr, "DoApplicationJump"},
+        {0x00310100, &APT_U::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"},
+        {0x00320084, &APT_U::DoApplicationJump, "DoApplicationJump"},
         {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
         {0x00340084, nullptr, "SendDeliverArg"},
         {0x00350080, nullptr, "ReceiveDeliverArg"},