Просмотр исходного кода

Merge pull request #2228 from DarkLordZach/applet-manager-p1

applets: Add AppletManager and implement PhotoViewer and Error applets
bunnei 7 лет назад
Родитель
Сommit
53f746fa9a

+ 8 - 2
src/core/CMakeLists.txt

@@ -88,6 +88,10 @@ add_library(core STATIC
     file_sys/vfs_vector.h
     file_sys/xts_archive.cpp
     file_sys/xts_archive.h
+    frontend/applets/error.cpp
+    frontend/applets/error.h
+    frontend/applets/general_frontend.cpp
+    frontend/applets/general_frontend.h
     frontend/applets/profile_select.cpp
     frontend/applets/profile_select.h
     frontend/applets/software_keyboard.cpp
@@ -177,12 +181,14 @@ add_library(core STATIC
     hle/service/am/applet_oe.h
     hle/service/am/applets/applets.cpp
     hle/service/am/applets/applets.h
+    hle/service/am/applets/error.cpp
+    hle/service/am/applets/error.h
+    hle/service/am/applets/general_backend.cpp
+    hle/service/am/applets/general_backend.h
     hle/service/am/applets/profile_select.cpp
     hle/service/am/applets/profile_select.h
     hle/service/am/applets/software_keyboard.cpp
     hle/service/am/applets/software_keyboard.h
-    hle/service/am/applets/stub_applet.cpp
-    hle/service/am/applets/stub_applet.h
     hle/service/am/applets/web_browser.cpp
     hle/service/am/applets/web_browser.h
     hle/service/am/idle.cpp

+ 17 - 33
src/core/core.cpp

@@ -18,13 +18,18 @@
 #include "core/file_sys/registered_cache.h"
 #include "core/file_sys/vfs_concat.h"
 #include "core/file_sys/vfs_real.h"
+#include "core/frontend/applets/error.h"
+#include "core/frontend/applets/general_frontend.h"
+#include "core/frontend/applets/profile_select.h"
+#include "core/frontend/applets/software_keyboard.h"
+#include "core/frontend/applets/web_browser.h"
 #include "core/gdbstub/gdbstub.h"
 #include "core/hle/kernel/client_port.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/process.h"
 #include "core/hle/kernel/scheduler.h"
 #include "core/hle/kernel/thread.h"
-#include "core/hle/service/am/applets/software_keyboard.h"
+#include "core/hle/service/am/applets/applets.h"
 #include "core/hle/service/service.h"
 #include "core/hle/service/sm/sm.h"
 #include "core/loader/loader.h"
@@ -110,12 +115,7 @@ struct System::Impl {
             content_provider = std::make_unique<FileSys::ContentProviderUnion>();
 
         /// Create default implementations of applets if one is not provided.
-        if (profile_selector == nullptr)
-            profile_selector = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
-        if (software_keyboard == nullptr)
-            software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
-        if (web_browser == nullptr)
-            web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
+        applet_manager.SetDefaultAppletsIfMissing();
 
         telemetry_session = std::make_unique<Core::TelemetrySession>();
         service_manager = std::make_shared<Service::SM::ServiceManager>();
@@ -223,9 +223,7 @@ struct System::Impl {
         app_loader.reset();
 
         // Clear all applets
-        profile_selector.reset();
-        software_keyboard.reset();
-        web_browser.reset();
+        applet_manager.ClearAll();
 
         LOG_DEBUG(Core, "Shutdown OK");
     }
@@ -264,9 +262,7 @@ struct System::Impl {
     std::unique_ptr<FileSys::CheatEngine> cheat_engine;
 
     /// Frontend applets
-    std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector;
-    std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
-    std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser;
+    Service::AM::Applets::AppletManager applet_manager;
 
     /// Service manager
     std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -476,20 +472,20 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
     return impl->virtual_filesystem;
 }
 
-void System::SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet) {
-    impl->profile_selector = std::move(applet);
+void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) {
+    impl->applet_manager.SetAppletFrontendSet(std::move(set));
 }
 
-const Frontend::ProfileSelectApplet& System::GetProfileSelector() const {
-    return *impl->profile_selector;
+void System::SetDefaultAppletFrontendSet() {
+    impl->applet_manager.SetDefaultAppletFrontendSet();
 }
 
-void System::SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet) {
-    impl->software_keyboard = std::move(applet);
+Service::AM::Applets::AppletManager& System::GetAppletManager() {
+    return impl->applet_manager;
 }
 
-const Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const {
-    return *impl->software_keyboard;
+const Service::AM::Applets::AppletManager& System::GetAppletManager() const {
+    return impl->applet_manager;
 }
 
 void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
@@ -513,18 +509,6 @@ void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) {
     impl->content_provider->ClearSlot(slot);
 }
 
-void System::SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet) {
-    impl->web_browser = std::move(applet);
-}
-
-Frontend::WebBrowserApplet& System::GetWebBrowser() {
-    return *impl->web_browser;
-}
-
-const Frontend::WebBrowserApplet& System::GetWebBrowser() const {
-    return *impl->web_browser;
-}
-
 System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
     return impl->Init(*this, emu_window);
 }

+ 15 - 14
src/core/core.h

@@ -14,9 +14,6 @@
 
 namespace Core::Frontend {
 class EmuWindow;
-class ProfileSelectApplet;
-class SoftwareKeyboardApplet;
-class WebBrowserApplet;
 } // namespace Core::Frontend
 
 namespace FileSys {
@@ -38,9 +35,18 @@ class AppLoader;
 enum class ResultStatus : u16;
 } // namespace Loader
 
-namespace Service::SM {
+namespace Service {
+
+namespace AM::Applets {
+struct AppletFrontendSet;
+class AppletManager;
+} // namespace AM::Applets
+
+namespace SM {
 class ServiceManager;
-} // namespace Service::SM
+} // namespace SM
+
+} // namespace Service
 
 namespace Tegra {
 class DebugContext;
@@ -260,18 +266,13 @@ public:
     void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id,
                            VAddr code_region_start, VAddr code_region_end);
 
-    void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet);
-
-    const Frontend::ProfileSelectApplet& GetProfileSelector() const;
-
-    void SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet);
+    void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set);
 
-    const Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const;
+    void SetDefaultAppletFrontendSet();
 
-    void SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet);
+    Service::AM::Applets::AppletManager& GetAppletManager();
 
-    Frontend::WebBrowserApplet& GetWebBrowser();
-    const Frontend::WebBrowserApplet& GetWebBrowser() const;
+    const Service::AM::Applets::AppletManager& GetAppletManager() const;
 
     void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider);
 

+ 34 - 0
src/core/frontend/applets/error.cpp

@@ -0,0 +1,34 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/frontend/applets/error.h"
+
+namespace Core::Frontend {
+
+ErrorApplet::~ErrorApplet() = default;
+
+void DefaultErrorApplet::ShowError(ResultCode error, std::function<void()> finished) const {
+    LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})",
+                 static_cast<u32>(error.module.Value()), error.description.Value(), error.raw);
+}
+
+void DefaultErrorApplet::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
+                                                std::function<void()> finished) const {
+    LOG_CRITICAL(
+        Service_Fatal,
+        "Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}",
+        static_cast<u32>(error.module.Value()), error.description.Value(), error.raw, time.count());
+}
+
+void DefaultErrorApplet::ShowCustomErrorText(ResultCode error, std::string main_text,
+                                             std::string detail_text,
+                                             std::function<void()> finished) const {
+    LOG_CRITICAL(Service_Fatal,
+                 "Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})",
+                 static_cast<u32>(error.module.Value()), error.description.Value(), error.raw);
+    LOG_CRITICAL(Service_Fatal, "    Main Text: {}", main_text);
+    LOG_CRITICAL(Service_Fatal, "    Detail Text: {}", detail_text);
+}
+
+} // namespace Core::Frontend

+ 37 - 0
src/core/frontend/applets/error.h

@@ -0,0 +1,37 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <chrono>
+#include <functional>
+
+#include "core/hle/result.h"
+
+namespace Core::Frontend {
+
+class ErrorApplet {
+public:
+    virtual ~ErrorApplet();
+
+    virtual void ShowError(ResultCode error, std::function<void()> finished) const = 0;
+
+    virtual void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
+                                        std::function<void()> finished) const = 0;
+
+    virtual void ShowCustomErrorText(ResultCode error, std::string dialog_text,
+                                     std::string fullscreen_text,
+                                     std::function<void()> finished) const = 0;
+};
+
+class DefaultErrorApplet final : public ErrorApplet {
+public:
+    void ShowError(ResultCode error, std::function<void()> finished) const override;
+    void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
+                                std::function<void()> finished) const override;
+    void ShowCustomErrorText(ResultCode error, std::string main_text, std::string detail_text,
+                             std::function<void()> finished) const override;
+};
+
+} // namespace Core::Frontend

+ 27 - 0
src/core/frontend/applets/general_frontend.cpp

@@ -0,0 +1,27 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/logging/log.h"
+#include "core/frontend/applets/general_frontend.h"
+
+namespace Core::Frontend {
+
+PhotoViewerApplet::~PhotoViewerApplet() = default;
+
+DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() {}
+
+void DefaultPhotoViewerApplet::ShowPhotosForApplication(u64 title_id,
+                                                        std::function<void()> finished) const {
+    LOG_INFO(Service_AM,
+             "Application requested frontend to display stored photos for title_id={:016X}",
+             title_id);
+    finished();
+}
+
+void DefaultPhotoViewerApplet::ShowAllPhotos(std::function<void()> finished) const {
+    LOG_INFO(Service_AM, "Application requested frontend to display all stored photos.");
+    finished();
+}
+
+} // namespace Core::Frontend

+ 28 - 0
src/core/frontend/applets/general_frontend.h

@@ -0,0 +1,28 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <functional>
+#include "common/common_types.h"
+
+namespace Core::Frontend {
+
+class PhotoViewerApplet {
+public:
+    virtual ~PhotoViewerApplet();
+
+    virtual void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const = 0;
+    virtual void ShowAllPhotos(std::function<void()> finished) const = 0;
+};
+
+class DefaultPhotoViewerApplet final : public PhotoViewerApplet {
+public:
+    ~DefaultPhotoViewerApplet() override;
+
+    void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const override;
+    void ShowAllPhotos(std::function<void()> finished) const override;
+};
+
+} // namespace Core::Frontend

+ 3 - 24
src/core/hle/service/am/am.cpp

@@ -22,7 +22,6 @@
 #include "core/hle/service/am/applets/applets.h"
 #include "core/hle/service/am/applets/profile_select.h"
 #include "core/hle/service/am/applets/software_keyboard.h"
-#include "core/hle/service/am/applets/stub_applet.h"
 #include "core/hle/service/am/applets/web_browser.h"
 #include "core/hle/service/am/idle.h"
 #include "core/hle/service/am/omm.h"
@@ -42,12 +41,6 @@ constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
 constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3};
 constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
 
-enum class AppletId : u32 {
-    ProfileSelect = 0x10,
-    SoftwareKeyboard = 0x11,
-    LibAppletOff = 0x17,
-};
-
 constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
 
 struct LaunchParameters {
@@ -886,30 +879,16 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
 
 ILibraryAppletCreator::~ILibraryAppletCreator() = default;
 
-static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
-    switch (id) {
-    case AppletId::ProfileSelect:
-        return std::make_shared<Applets::ProfileSelect>();
-    case AppletId::SoftwareKeyboard:
-        return std::make_shared<Applets::SoftwareKeyboard>();
-    case AppletId::LibAppletOff:
-        return std::make_shared<Applets::WebBrowser>();
-    default:
-        LOG_ERROR(Service_AM, "Unimplemented AppletId [{:08X}]! -- Falling back to stub!",
-                  static_cast<u32>(id));
-        return std::make_shared<Applets::StubApplet>();
-    }
-}
-
 void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
-    const auto applet_id = rp.PopRaw<AppletId>();
+    const auto applet_id = rp.PopRaw<Applets::AppletId>();
     const auto applet_mode = rp.PopRaw<u32>();
 
     LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}",
               static_cast<u32>(applet_id), applet_mode);
 
-    const auto applet = GetAppletFromId(applet_id);
+    const auto& applet_manager{Core::System::GetInstance().GetAppletManager()};
+    const auto applet = applet_manager.GetApplet(applet_id);
 
     if (applet == nullptr) {
         LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", static_cast<u32>(applet_id));

+ 82 - 0
src/core/hle/service/am/applets/applets.cpp

@@ -5,11 +5,21 @@
 #include <cstring>
 #include "common/assert.h"
 #include "core/core.h"
+#include "core/frontend/applets/error.h"
+#include "core/frontend/applets/general_frontend.h"
+#include "core/frontend/applets/profile_select.h"
+#include "core/frontend/applets/software_keyboard.h"
+#include "core/frontend/applets/web_browser.h"
 #include "core/hle/kernel/readable_event.h"
 #include "core/hle/kernel/server_session.h"
 #include "core/hle/kernel/writable_event.h"
 #include "core/hle/service/am/am.h"
 #include "core/hle/service/am/applets/applets.h"
+#include "core/hle/service/am/applets/error.h"
+#include "core/hle/service/am/applets/general_backend.h"
+#include "core/hle/service/am/applets/profile_select.h"
+#include "core/hle/service/am/applets/software_keyboard.h"
+#include "core/hle/service/am/applets/web_browser.h"
 
 namespace Service::AM::Applets {
 
@@ -111,4 +121,76 @@ void Applet::Initialize() {
     initialized = true;
 }
 
+AppletManager::AppletManager() = default;
+
+AppletManager::~AppletManager() = default;
+
+void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
+    if (set.error != nullptr)
+        frontend.error = std::move(set.error);
+    if (set.photo_viewer != nullptr)
+        frontend.photo_viewer = std::move(set.photo_viewer);
+    if (set.profile_select != nullptr)
+        frontend.profile_select = std::move(set.profile_select);
+    if (set.software_keyboard != nullptr)
+        frontend.software_keyboard = std::move(set.software_keyboard);
+    if (set.web_browser != nullptr)
+        frontend.web_browser = std::move(set.web_browser);
+}
+
+void AppletManager::SetDefaultAppletFrontendSet() {
+    frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
+    frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
+    frontend.profile_select = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
+    frontend.software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
+    frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
+}
+
+void AppletManager::SetDefaultAppletsIfMissing() {
+    if (frontend.error == nullptr) {
+        frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
+    }
+
+    if (frontend.photo_viewer == nullptr) {
+        frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
+    }
+
+    if (frontend.profile_select == nullptr) {
+        frontend.profile_select = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
+    }
+
+    if (frontend.software_keyboard == nullptr) {
+        frontend.software_keyboard =
+            std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
+    }
+
+    if (frontend.web_browser == nullptr) {
+        frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
+    }
+}
+
+void AppletManager::ClearAll() {
+    frontend = {};
+}
+
+std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
+    switch (id) {
+    case AppletId::Error:
+        return std::make_shared<Error>(*frontend.error);
+    case AppletId::ProfileSelect:
+        return std::make_shared<ProfileSelect>(*frontend.profile_select);
+    case AppletId::SoftwareKeyboard:
+        return std::make_shared<SoftwareKeyboard>(*frontend.software_keyboard);
+    case AppletId::PhotoViewer:
+        return std::make_shared<PhotoViewer>(*frontend.photo_viewer);
+    case AppletId::LibAppletOff:
+        return std::make_shared<WebBrowser>(*frontend.web_browser);
+    default:
+        UNIMPLEMENTED_MSG(
+            "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
+            static_cast<u8>(id));
+        return std::make_shared<StubApplet>();
+    }
+}
+
 } // namespace Service::AM::Applets

+ 55 - 0
src/core/hle/service/am/applets/applets.h

@@ -12,12 +12,43 @@
 
 union ResultCode;
 
+namespace Core::Frontend {
+class ErrorApplet;
+class PhotoViewerApplet;
+class ProfileSelectApplet;
+class SoftwareKeyboardApplet;
+class WebBrowserApplet;
+} // namespace Core::Frontend
+
 namespace Service::AM {
 
 class IStorage;
 
 namespace Applets {
 
+enum class AppletId : u32 {
+    OverlayDisplay = 0x02,
+    QLaunch = 0x03,
+    Starter = 0x04,
+    Auth = 0x0A,
+    Cabinet = 0x0B,
+    Controller = 0x0C,
+    DataErase = 0x0D,
+    Error = 0x0E,
+    NetConnect = 0x0F,
+    ProfileSelect = 0x10,
+    SoftwareKeyboard = 0x11,
+    MiiEdit = 0x12,
+    LibAppletWeb = 0x13,
+    LibAppletShop = 0x14,
+    PhotoViewer = 0x15,
+    Settings = 0x16,
+    LibAppletOff = 0x17,
+    LibAppletWhitelisted = 0x18,
+    LibAppletAuth = 0x19,
+    MyPage = 0x1A,
+};
+
 class AppletDataBroker final {
 public:
     AppletDataBroker();
@@ -105,5 +136,29 @@ protected:
     bool initialized = false;
 };
 
+struct AppletFrontendSet {
+    std::unique_ptr<Core::Frontend::ErrorApplet> error;
+    std::unique_ptr<Core::Frontend::PhotoViewerApplet> photo_viewer;
+    std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_select;
+    std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
+    std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser;
+};
+
+class AppletManager {
+public:
+    AppletManager();
+    ~AppletManager();
+
+    void SetAppletFrontendSet(AppletFrontendSet set);
+    void SetDefaultAppletFrontendSet();
+    void SetDefaultAppletsIfMissing();
+    void ClearAll();
+
+    std::shared_ptr<Applet> GetApplet(AppletId id) const;
+
+private:
+    AppletFrontendSet frontend;
+};
+
 } // namespace Applets
 } // namespace Service::AM

+ 182 - 0
src/core/hle/service/am/applets/error.cpp

@@ -0,0 +1,182 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include <cstring>
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "common/string_util.h"
+#include "core/core.h"
+#include "core/frontend/applets/error.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applets/error.h"
+
+namespace Service::AM::Applets {
+
+#pragma pack(push, 4)
+struct ShowError {
+    u8 mode;
+    bool jump;
+    INSERT_PADDING_BYTES(4);
+    bool use_64bit_error_code;
+    INSERT_PADDING_BYTES(1);
+    u64 error_code_64;
+    u32 error_code_32;
+};
+static_assert(sizeof(ShowError) == 0x14, "ShowError has incorrect size.");
+#pragma pack(pop)
+
+struct ShowErrorRecord {
+    u8 mode;
+    bool jump;
+    INSERT_PADDING_BYTES(6);
+    u64 error_code_64;
+    u64 posix_time;
+};
+static_assert(sizeof(ShowErrorRecord) == 0x18, "ShowErrorRecord has incorrect size.");
+
+struct SystemErrorArg {
+    u8 mode;
+    bool jump;
+    INSERT_PADDING_BYTES(6);
+    u64 error_code_64;
+    std::array<char, 8> language_code;
+    std::array<char, 0x800> main_text;
+    std::array<char, 0x800> detail_text;
+};
+static_assert(sizeof(SystemErrorArg) == 0x1018, "SystemErrorArg has incorrect size.");
+
+struct ApplicationErrorArg {
+    u8 mode;
+    bool jump;
+    INSERT_PADDING_BYTES(6);
+    u32 error_code;
+    std::array<char, 8> language_code;
+    std::array<char, 0x800> main_text;
+    std::array<char, 0x800> detail_text;
+};
+static_assert(sizeof(ApplicationErrorArg) == 0x1014, "ApplicationErrorArg has incorrect size.");
+
+union Error::ErrorArguments {
+    ShowError error;
+    ShowErrorRecord error_record;
+    SystemErrorArg system_error;
+    ApplicationErrorArg application_error;
+};
+
+namespace {
+template <typename T>
+void CopyArgumentData(const std::vector<u8>& data, T& variable) {
+    ASSERT(data.size() >= sizeof(T));
+    std::memcpy(&variable, data.data(), sizeof(T));
+}
+
+ResultCode Decode64BitError(u64 error) {
+    const auto description = (error >> 32) & 0x1FFF;
+    auto module = error & 0x3FF;
+    if (module >= 2000)
+        module -= 2000;
+    module &= 0x1FF;
+    return {static_cast<ErrorModule>(module), static_cast<u32>(description)};
+}
+
+} // Anonymous namespace
+
+Error::Error(const Core::Frontend::ErrorApplet& frontend) : frontend(frontend) {}
+
+Error::~Error() = default;
+
+void Error::Initialize() {
+    Applet::Initialize();
+    args = std::make_unique<ErrorArguments>();
+    complete = false;
+
+    const auto storage = broker.PopNormalDataToApplet();
+    ASSERT(storage != nullptr);
+    const auto data = storage->GetData();
+
+    ASSERT(!data.empty());
+    std::memcpy(&mode, data.data(), sizeof(ErrorAppletMode));
+
+    switch (mode) {
+    case ErrorAppletMode::ShowError:
+        CopyArgumentData(data, args->error);
+        if (args->error.use_64bit_error_code) {
+            error_code = Decode64BitError(args->error.error_code_64);
+        } else {
+            error_code = ResultCode(args->error.error_code_32);
+        }
+        break;
+    case ErrorAppletMode::ShowSystemError:
+        CopyArgumentData(data, args->system_error);
+        error_code = ResultCode(Decode64BitError(args->system_error.error_code_64));
+        break;
+    case ErrorAppletMode::ShowApplicationError:
+        CopyArgumentData(data, args->application_error);
+        error_code = ResultCode(args->application_error.error_code);
+        break;
+    case ErrorAppletMode::ShowErrorRecord:
+        CopyArgumentData(data, args->error_record);
+        error_code = Decode64BitError(args->error_record.error_code_64);
+        break;
+    default:
+        UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", static_cast<u8>(mode));
+    }
+}
+
+bool Error::TransactionComplete() const {
+    return complete;
+}
+
+ResultCode Error::GetStatus() const {
+    return RESULT_SUCCESS;
+}
+
+void Error::ExecuteInteractive() {
+    UNREACHABLE_MSG("Unexpected interactive applet data!");
+}
+
+void Error::Execute() {
+    if (complete) {
+        return;
+    }
+
+    const auto callback = [this] { DisplayCompleted(); };
+
+    switch (mode) {
+    case ErrorAppletMode::ShowError:
+        frontend.ShowError(error_code, callback);
+        break;
+    case ErrorAppletMode::ShowSystemError:
+    case ErrorAppletMode::ShowApplicationError: {
+        const auto system = mode == ErrorAppletMode::ShowSystemError;
+        const auto& main_text =
+            system ? args->system_error.main_text : args->application_error.main_text;
+        const auto& detail_text =
+            system ? args->system_error.detail_text : args->application_error.detail_text;
+
+        frontend.ShowCustomErrorText(
+            error_code,
+            Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()),
+            Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()),
+            callback);
+        break;
+    }
+    case ErrorAppletMode::ShowErrorRecord:
+        frontend.ShowErrorWithTimestamp(
+            error_code, std::chrono::seconds{args->error_record.posix_time}, callback);
+        break;
+    default:
+        UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", static_cast<u8>(mode));
+        DisplayCompleted();
+    }
+}
+
+void Error::DisplayCompleted() {
+    complete = true;
+    broker.PushNormalDataFromApplet(IStorage{{}});
+    broker.SignalStateChanged();
+}
+
+} // namespace Service::AM::Applets

+ 47 - 0
src/core/hle/service/am/applets/error.h

@@ -0,0 +1,47 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/result.h"
+#include "core/hle/service/am/applets/applets.h"
+
+namespace Service::AM::Applets {
+
+enum class ErrorAppletMode : u8 {
+    ShowError = 0,
+    ShowSystemError = 1,
+    ShowApplicationError = 2,
+    ShowEula = 3,
+    ShowErrorPctl = 4,
+    ShowErrorRecord = 5,
+    ShowUpdateEula = 8,
+};
+
+class Error final : public Applet {
+public:
+    explicit Error(const Core::Frontend::ErrorApplet& frontend);
+    ~Error() override;
+
+    void Initialize() override;
+
+    bool TransactionComplete() const override;
+    ResultCode GetStatus() const override;
+    void ExecuteInteractive() override;
+    void Execute() override;
+
+    void DisplayCompleted();
+
+private:
+    union ErrorArguments;
+
+    const Core::Frontend::ErrorApplet& frontend;
+    ResultCode error_code = RESULT_SUCCESS;
+    ErrorAppletMode mode = ErrorAppletMode::ShowError;
+    std::unique_ptr<ErrorArguments> args;
+
+    bool complete = false;
+};
+
+} // namespace Service::AM::Applets

+ 55 - 1
src/core/hle/service/am/applets/stub_applet.cpp → src/core/hle/service/am/applets/general_backend.cpp

@@ -4,11 +4,15 @@
 
 #include <string>
 
+#include "common/assert.h"
 #include "common/hex_util.h"
 #include "common/logging/log.h"
+#include "core/core.h"
+#include "core/frontend/applets/general_frontend.h"
+#include "core/hle/kernel/process.h"
 #include "core/hle/result.h"
 #include "core/hle/service/am/am.h"
-#include "core/hle/service/am/applets/stub_applet.h"
+#include "core/hle/service/am/applets/general_backend.h"
 
 namespace Service::AM::Applets {
 
@@ -30,6 +34,55 @@ static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) {
     }
 }
 
+PhotoViewer::PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend) : frontend(frontend) {}
+
+PhotoViewer::~PhotoViewer() = default;
+
+void PhotoViewer::Initialize() {
+    Applet::Initialize();
+    complete = false;
+
+    const auto storage = broker.PopNormalDataToApplet();
+    ASSERT(storage != nullptr);
+    const auto data = storage->GetData();
+    ASSERT(!data.empty());
+    mode = static_cast<PhotoViewerAppletMode>(data[0]);
+}
+
+bool PhotoViewer::TransactionComplete() const {
+    return complete;
+}
+
+ResultCode PhotoViewer::GetStatus() const {
+    return RESULT_SUCCESS;
+}
+
+void PhotoViewer::ExecuteInteractive() {
+    UNREACHABLE_MSG("Unexpected interactive applet data.");
+}
+
+void PhotoViewer::Execute() {
+    if (complete)
+        return;
+
+    const auto callback = [this] { ViewFinished(); };
+    switch (mode) {
+    case PhotoViewerAppletMode::CurrentApp:
+        frontend.ShowPhotosForApplication(Core::CurrentProcess()->GetTitleID(), callback);
+        break;
+    case PhotoViewerAppletMode::AllApps:
+        frontend.ShowAllPhotos(callback);
+        break;
+    default:
+        UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", static_cast<u8>(mode));
+    }
+}
+
+void PhotoViewer::ViewFinished() {
+    broker.PushNormalDataFromApplet(IStorage{{}});
+    broker.SignalStateChanged();
+}
+
 StubApplet::StubApplet() = default;
 
 StubApplet::~StubApplet() = default;
@@ -67,4 +120,5 @@ void StubApplet::Execute() {
     broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
     broker.SignalStateChanged();
 }
+
 } // namespace Service::AM::Applets

+ 48 - 0
src/core/hle/service/am/applets/general_backend.h

@@ -0,0 +1,48 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/am/applets/applets.h"
+
+namespace Service::AM::Applets {
+
+enum class PhotoViewerAppletMode : u8 {
+    CurrentApp = 0,
+    AllApps = 1,
+};
+
+class PhotoViewer final : public Applet {
+public:
+    explicit PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend);
+    ~PhotoViewer() override;
+
+    void Initialize() override;
+    bool TransactionComplete() const override;
+    ResultCode GetStatus() const override;
+    void ExecuteInteractive() override;
+    void Execute() override;
+
+    void ViewFinished();
+
+private:
+    const Core::Frontend::PhotoViewerApplet& frontend;
+    bool complete = false;
+    PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp;
+};
+
+class StubApplet final : public Applet {
+public:
+    StubApplet();
+    ~StubApplet() override;
+
+    void Initialize() override;
+
+    bool TransactionComplete() const override;
+    ResultCode GetStatus() const override;
+    void ExecuteInteractive() override;
+    void Execute() override;
+};
+
+} // namespace Service::AM::Applets

+ 3 - 3
src/core/hle/service/am/applets/profile_select.cpp

@@ -15,7 +15,9 @@ namespace Service::AM::Applets {
 
 constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1};
 
-ProfileSelect::ProfileSelect() = default;
+ProfileSelect::ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend)
+    : frontend(frontend) {}
+
 ProfileSelect::~ProfileSelect() = default;
 
 void ProfileSelect::Initialize() {
@@ -51,8 +53,6 @@ void ProfileSelect::Execute() {
         return;
     }
 
-    const auto& frontend{Core::System::GetInstance().GetProfileSelector()};
-
     frontend.SelectProfile([this](std::optional<Account::UUID> uuid) { SelectionComplete(uuid); });
 }
 

+ 3 - 1
src/core/hle/service/am/applets/profile_select.h

@@ -28,7 +28,7 @@ static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has inco
 
 class ProfileSelect final : public Applet {
 public:
-    ProfileSelect();
+    explicit ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend);
     ~ProfileSelect() override;
 
     void Initialize() override;
@@ -41,6 +41,8 @@ public:
     void SelectionComplete(std::optional<Account::UUID> uuid);
 
 private:
+    const Core::Frontend::ProfileSelectApplet& frontend;
+
     UserSelectionConfig config;
     bool complete = false;
     ResultCode status = RESULT_SUCCESS;

+ 2 - 5
src/core/hle/service/am/applets/software_keyboard.cpp

@@ -39,7 +39,8 @@ static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
     return params;
 }
 
-SoftwareKeyboard::SoftwareKeyboard() = default;
+SoftwareKeyboard::SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend)
+    : frontend(frontend) {}
 
 SoftwareKeyboard::~SoftwareKeyboard() = default;
 
@@ -90,8 +91,6 @@ void SoftwareKeyboard::ExecuteInteractive() {
     if (status == INTERACTIVE_STATUS_OK) {
         complete = true;
     } else {
-        const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
-
         std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
         std::memcpy(string.data(), data.data() + 4, string.size() * 2);
         frontend.SendTextCheckDialog(
@@ -106,8 +105,6 @@ void SoftwareKeyboard::Execute() {
         return;
     }
 
-    const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
-
     const auto parameters = ConvertToFrontendParameters(config, initial_text);
 
     frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); },

+ 3 - 1
src/core/hle/service/am/applets/software_keyboard.h

@@ -55,7 +55,7 @@ static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect siz
 
 class SoftwareKeyboard final : public Applet {
 public:
-    SoftwareKeyboard();
+    explicit SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend);
     ~SoftwareKeyboard() override;
 
     void Initialize() override;
@@ -68,6 +68,8 @@ public:
     void WriteText(std::optional<std::u16string> text);
 
 private:
+    const Core::Frontend::SoftwareKeyboardApplet& frontend;
+
     KeyboardConfig config;
     std::u16string initial_text;
     bool complete = false;

+ 0 - 24
src/core/hle/service/am/applets/stub_applet.h

@@ -1,24 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "core/hle/service/am/applets/applets.h"
-
-namespace Service::AM::Applets {
-
-class StubApplet final : public Applet {
-public:
-    StubApplet();
-    ~StubApplet() override;
-
-    void Initialize() override;
-
-    bool TransactionComplete() const override;
-    ResultCode GetStatus() const override;
-    void ExecuteInteractive() override;
-    void Execute() override;
-};
-
-} // namespace Service::AM::Applets

+ 1 - 3
src/core/hle/service/am/applets/web_browser.cpp

@@ -95,7 +95,7 @@ static FileSys::VirtualFile GetManualRomFS() {
     return nullptr;
 }
 
-WebBrowser::WebBrowser() = default;
+WebBrowser::WebBrowser(Core::Frontend::WebBrowserApplet& frontend) : frontend(frontend) {}
 
 WebBrowser::~WebBrowser() = default;
 
@@ -152,8 +152,6 @@ void WebBrowser::Execute() {
         return;
     }
 
-    auto& frontend{Core::System::GetInstance().GetWebBrowser()};
-
     frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
 }
 

+ 3 - 1
src/core/hle/service/am/applets/web_browser.h

@@ -12,7 +12,7 @@ namespace Service::AM::Applets {
 
 class WebBrowser final : public Applet {
 public:
-    WebBrowser();
+    WebBrowser(Core::Frontend::WebBrowserApplet& frontend);
     ~WebBrowser() override;
 
     void Initialize() override;
@@ -32,6 +32,8 @@ public:
     void Finalize();
 
 private:
+    Core::Frontend::WebBrowserApplet& frontend;
+
     bool complete = false;
     bool unpacked = false;
     ResultCode status = RESULT_SUCCESS;

+ 2 - 0
src/yuzu/CMakeLists.txt

@@ -7,6 +7,8 @@ add_executable(yuzu
     Info.plist
     about_dialog.cpp
     about_dialog.h
+    applets/error.cpp
+    applets/error.h
     applets/profile_select.cpp
     applets/profile_select.h
     applets/software_keyboard.cpp

+ 59 - 0
src/yuzu/applets/error.cpp

@@ -0,0 +1,59 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QDateTime>
+#include "core/hle/lock.h"
+#include "yuzu/applets/error.h"
+#include "yuzu/main.h"
+
+QtErrorDisplay::QtErrorDisplay(GMainWindow& parent) {
+    connect(this, &QtErrorDisplay::MainWindowDisplayError, &parent,
+            &GMainWindow::ErrorDisplayDisplayError, Qt::QueuedConnection);
+    connect(&parent, &GMainWindow::ErrorDisplayFinished, this,
+            &QtErrorDisplay::MainWindowFinishedError, Qt::DirectConnection);
+}
+
+QtErrorDisplay::~QtErrorDisplay() = default;
+
+void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const {
+    this->callback = std::move(finished);
+    emit MainWindowDisplayError(
+        tr("An error has occured.\nPlease try again or contact the developer of the "
+           "software.\n\nError Code: %1-%2 (0x%3)")
+            .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
+            .arg(error.description, 4, 10, QChar::fromLatin1('0'))
+            .arg(error.raw, 8, 16, QChar::fromLatin1('0')));
+}
+
+void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
+                                            std::function<void()> finished) const {
+    this->callback = std::move(finished);
+    emit MainWindowDisplayError(
+        tr("An error occured on %1 at %2.\nPlease try again or contact the "
+           "developer of the software.\n\nError Code: %3-%4 (0x%5)")
+            .arg(QDateTime::fromSecsSinceEpoch(time.count()).toString("dddd, MMMM d, yyyy"))
+            .arg(QDateTime::fromSecsSinceEpoch(time.count()).toString("h:mm:ss A"))
+            .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
+            .arg(error.description, 4, 10, QChar::fromLatin1('0'))
+            .arg(error.raw, 8, 16, QChar::fromLatin1('0')));
+}
+
+void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_text,
+                                         std::string fullscreen_text,
+                                         std::function<void()> finished) const {
+    this->callback = std::move(finished);
+    emit MainWindowDisplayError(
+        tr("An error has occured.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5")
+            .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
+            .arg(error.description, 4, 10, QChar::fromLatin1('0'))
+            .arg(error.raw, 8, 16, QChar::fromLatin1('0'))
+            .arg(QString::fromStdString(dialog_text))
+            .arg(QString::fromStdString(fullscreen_text)));
+}
+
+void QtErrorDisplay::MainWindowFinishedError() {
+    // Acquire the HLE mutex
+    std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
+    callback();
+}

+ 33 - 0
src/yuzu/applets/error.h

@@ -0,0 +1,33 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <QObject>
+
+#include "core/frontend/applets/error.h"
+
+class GMainWindow;
+
+class QtErrorDisplay final : public QObject, public Core::Frontend::ErrorApplet {
+    Q_OBJECT
+
+public:
+    explicit QtErrorDisplay(GMainWindow& parent);
+    ~QtErrorDisplay() override;
+
+    void ShowError(ResultCode error, std::function<void()> finished) const override;
+    void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
+                                std::function<void()> finished) const override;
+    void ShowCustomErrorText(ResultCode error, std::string dialog_text, std::string fullscreen_text,
+                             std::function<void()> finished) const override;
+
+signals:
+    void MainWindowDisplayError(QString error) const;
+
+private:
+    void MainWindowFinishedError();
+
+    mutable std::function<void()> callback;
+};

+ 14 - 3
src/yuzu/main.cpp

@@ -8,6 +8,7 @@
 #include <thread>
 
 // VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
+#include "applets/error.h"
 #include "applets/profile_select.h"
 #include "applets/software_keyboard.h"
 #include "applets/web_browser.h"
@@ -15,6 +16,7 @@
 #include "configuration/configure_per_general.h"
 #include "core/file_sys/vfs.h"
 #include "core/file_sys/vfs_real.h"
+#include "core/frontend/applets/general_frontend.h"
 #include "core/frontend/scope_acquire_window_context.h"
 #include "core/hle/service/acc/profile_manager.h"
 #include "core/hle/service/am/applets/applets.h"
@@ -795,9 +797,13 @@ bool GMainWindow::LoadROM(const QString& filename) {
 
     system.SetGPUDebugContext(debug_context);
 
-    system.SetProfileSelector(std::make_unique<QtProfileSelector>(*this));
-    system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this));
-    system.SetWebBrowser(std::make_unique<QtWebBrowser>(*this));
+    system.SetAppletFrontendSet({
+        std::make_unique<QtErrorDisplay>(*this),
+        nullptr,
+        std::make_unique<QtProfileSelector>(*this),
+        std::make_unique<QtSoftwareKeyboard>(*this),
+        std::make_unique<QtWebBrowser>(*this),
+    });
 
     const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
 
@@ -1583,6 +1589,11 @@ void GMainWindow::OnLoadComplete() {
     loading_screen->OnLoadComplete();
 }
 
+void GMainWindow::ErrorDisplayDisplayError(QString body) {
+    QMessageBox::critical(this, tr("Error Display"), body);
+    emit ErrorDisplayFinished();
+}
+
 void GMainWindow::OnMenuReportCompatibility() {
     if (!Settings::values.yuzu_token.empty() && !Settings::values.yuzu_username.empty()) {
         CompatDB compatdb{this};

+ 3 - 0
src/yuzu/main.h

@@ -102,6 +102,8 @@ signals:
     // Signal that tells widgets to update icons to use the current theme
     void UpdateThemedIcons();
 
+    void ErrorDisplayFinished();
+
     void ProfileSelectorFinishedSelection(std::optional<Service::Account::UUID> uuid);
     void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
     void SoftwareKeyboardFinishedCheckDialog();
@@ -111,6 +113,7 @@ signals:
 
 public slots:
     void OnLoadComplete();
+    void ErrorDisplayDisplayError(QString body);
     void ProfileSelectorSelectProfile();
     void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
     void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message);