From 9d2d349d7b0d30784b13260844deec500b5a50c8 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 5 Jun 2019 12:10:23 -0400
Subject: [PATCH] applets: Implement Auth applet backend

This is responsible for parental controls and supports verifying, changing, and registering PIN codes.
---
 .../service/am/applets/general_backend.cpp    | 116 ++++++++++++++++++
 .../hle/service/am/applets/general_backend.h  |  30 +++++
 2 files changed, 146 insertions(+)

diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp
index 54c155dd8e..e0def8dffd 100644
--- a/src/core/hle/service/am/applets/general_backend.cpp
+++ b/src/core/hle/service/am/applets/general_backend.cpp
@@ -17,6 +17,8 @@
 
 namespace Service::AM::Applets {
 
+constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221};
+
 static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) {
     std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet();
     for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) {
@@ -35,6 +37,120 @@ static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix)
     }
 }
 
+Auth::Auth(Core::Frontend::ParentalControlsApplet& frontend) : frontend(frontend) {}
+
+Auth::~Auth() = default;
+
+void Auth::Initialize() {
+    Applet::Initialize();
+    complete = false;
+
+    const auto storage = broker.PopNormalDataToApplet();
+    ASSERT(storage != nullptr);
+    const auto data = storage->GetData();
+    ASSERT(data.size() >= 0xC);
+
+    struct Arg {
+        INSERT_PADDING_BYTES(4);
+        AuthAppletType type;
+        u8 arg0;
+        u8 arg1;
+        u8 arg2;
+        INSERT_PADDING_BYTES(1);
+    };
+    static_assert(sizeof(Arg) == 0xC, "Arg (AuthApplet) has incorrect size.");
+
+    Arg arg{};
+    std::memcpy(&arg, data.data(), sizeof(Arg));
+
+    type = arg.type;
+    arg0 = arg.arg0;
+    arg1 = arg.arg1;
+    arg2 = arg.arg2;
+}
+
+bool Auth::TransactionComplete() const {
+    return complete;
+}
+
+ResultCode Auth::GetStatus() const {
+    return successful ? RESULT_SUCCESS : ERROR_INVALID_PIN;
+}
+
+void Auth::ExecuteInteractive() {
+    UNREACHABLE_MSG("Unexpected interactive applet data.");
+}
+
+void Auth::Execute() {
+    if (complete) {
+        return;
+    }
+
+    const auto unimplemented_log = [this] {
+        UNIMPLEMENTED_MSG("Unimplemented Auth applet type for type={:08X}, arg0={:02X}, "
+                          "arg1={:02X}, arg2={:02X}",
+                          static_cast<u32>(type), arg0, arg1, arg2);
+    };
+
+    switch (type) {
+    case AuthAppletType::ShowParentalAuthentication: {
+        const auto callback = [this](bool successful) { AuthFinished(successful); };
+
+        if (arg0 == 1 && arg1 == 0 && arg2 == 1) {
+            // ShowAuthenticatorForConfiguration
+            frontend.VerifyPINForSettings(callback);
+        } else if (arg1 == 0 && arg2 == 0) {
+            // ShowParentalAuthentication(bool)
+            frontend.VerifyPIN(callback, static_cast<bool>(arg0));
+        } else {
+            unimplemented_log();
+        }
+        break;
+    }
+    case AuthAppletType::RegisterParentalPasscode: {
+        const auto callback = [this] { AuthFinished(true); };
+
+        if (arg0 == 0 && arg1 == 0 && arg2 == 0) {
+            // RegisterParentalPasscode
+            frontend.RegisterPIN(callback);
+        } else {
+            unimplemented_log();
+        }
+        break;
+    }
+    case AuthAppletType::ChangeParentalPasscode: {
+        const auto callback = [this] { AuthFinished(true); };
+
+        if (arg0 == 0 && arg1 == 0 && arg2 == 0) {
+            // ChangeParentalPasscode
+            frontend.ChangePIN(callback);
+        } else {
+            unimplemented_log();
+        }
+        break;
+    }
+    default:
+        unimplemented_log();
+    }
+}
+
+void Auth::AuthFinished(bool successful) {
+    this->successful = successful;
+
+    struct Return {
+        ResultCode result_code;
+    };
+    static_assert(sizeof(Return) == 0x4, "Return (AuthApplet) has incorrect size.");
+
+    Return return_{GetStatus()};
+
+    std::vector<u8> out(sizeof(Return));
+    std::memcpy(out.data(), &return_, sizeof(Return));
+
+    broker.PushNormalDataFromApplet(IStorage{out});
+    broker.SignalStateChanged();
+}
+
 PhotoViewer::PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend) : frontend(frontend) {}
 
 PhotoViewer::~PhotoViewer() = default;
diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/general_backend.h
index fb68a2543e..0da252044b 100644
--- a/src/core/hle/service/am/applets/general_backend.h
+++ b/src/core/hle/service/am/applets/general_backend.h
@@ -8,6 +8,36 @@
 
 namespace Service::AM::Applets {
 
+enum class AuthAppletType : u32 {
+    ShowParentalAuthentication,
+    RegisterParentalPasscode,
+    ChangeParentalPasscode,
+};
+
+class Auth final : public Applet {
+public:
+    explicit Auth(Core::Frontend::ParentalControlsApplet& frontend);
+    ~Auth() override;
+
+    void Initialize() override;
+    bool TransactionComplete() const override;
+    ResultCode GetStatus() const override;
+    void ExecuteInteractive() override;
+    void Execute() override;
+
+    void AuthFinished(bool successful = true);
+
+private:
+    Core::Frontend::ParentalControlsApplet& frontend;
+    bool complete = false;
+    bool successful = false;
+
+    AuthAppletType type = AuthAppletType::ShowParentalAuthentication;
+    u8 arg0 = 0;
+    u8 arg1 = 0;
+    u8 arg2 = 0;
+};
+
 enum class PhotoViewerAppletMode : u8 {
     CurrentApp = 0,
     AllApps = 1,
-- 
GitLab