From 44e7e85f231b611130f71405b5258f5250e8785b Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Sun, 11 Feb 2024 19:31:01 -0500
Subject: [PATCH] am: rewrite IApplicationFunctions

---
 src/core/CMakeLists.txt                       |   4 +-
 src/core/hle/service/am/am_types.h            |  38 +-
 src/core/hle/service/am/applet.h              |   4 +-
 .../hle/service/am/application_functions.cpp  | 594 ------------------
 .../hle/service/am/application_functions.h    |  58 --
 .../am/service/application_functions.cpp      | 465 ++++++++++++++
 .../am/service/application_functions.h        |  83 +++
 .../service/am/service/application_proxy.cpp  |   2 +-
 8 files changed, 590 insertions(+), 658 deletions(-)
 delete mode 100644 src/core/hle/service/am/application_functions.cpp
 delete mode 100644 src/core/hle/service/am/application_functions.h
 create mode 100644 src/core/hle/service/am/service/application_functions.cpp
 create mode 100644 src/core/hle/service/am/service/application_functions.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 236051515..5d3f4cf1d 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -425,8 +425,6 @@ add_library(core STATIC
     hle/service/am/applet_message_queue.h
     hle/service/am/application_creator.cpp
     hle/service/am/application_creator.h
-    hle/service/am/application_functions.cpp
-    hle/service/am/application_functions.h
     hle/service/am/common_state_getter.cpp
     hle/service/am/common_state_getter.h
     hle/service/am/debug_functions.cpp
@@ -465,6 +463,8 @@ add_library(core STATIC
     hle/service/am/service/all_system_applet_proxies_service.h
     hle/service/am/service/applet_common_functions.cpp
     hle/service/am/service/applet_common_functions.h
+    hle/service/am/service/application_functions.cpp
+    hle/service/am/service/application_functions.h
     hle/service/am/service/application_proxy_service.cpp
     hle/service/am/service/application_proxy_service.h
     hle/service/am/service/application_proxy.cpp
diff --git a/src/core/hle/service/am/am_types.h b/src/core/hle/service/am/am_types.h
index 749081e3d..bf2272c0e 100644
--- a/src/core/hle/service/am/am_types.h
+++ b/src/core/hle/service/am/am_types.h
@@ -18,7 +18,7 @@ enum class AppletType {
     SystemApplet,
 };
 
-enum class GameplayRecordingState : u32 {
+enum class GamePlayRecordingState : u32 {
     Disabled,
     Enabled,
 };
@@ -136,6 +136,11 @@ enum class LibraryAppletMode : u32 {
     AllForegroundInitiallyHidden = 4,
 };
 
+enum class LaunchParameterKind : u32 {
+    UserChannel = 1,
+    AccountPreselectedUser = 2,
+};
+
 enum class CommonArgumentVersion : u32 {
     Version0,
     Version1,
@@ -152,6 +157,22 @@ enum class ThemeColor : u32 {
     BasicBlack = 3,
 };
 
+enum class InputDetectionPolicy : u32 {
+    Unknown0 = 0,
+    Unknown1 = 1,
+};
+
+enum class WindowOriginMode : u32 {
+    LowerLeft = 0,
+    UpperLeft = 1,
+};
+
+enum class ProgramSpecifyKind : u32 {
+    ExecuteProgram = 0,
+    JumpToSubApplicationProgramForDevelopment = 1,
+    RestartProgram = 2,
+};
+
 struct CommonArguments {
     CommonArgumentVersion arguments_version;
     CommonArgumentSize size;
@@ -175,6 +196,21 @@ struct AppletAttribute {
 };
 static_assert(sizeof(AppletAttribute) == 0x80, "AppletAttribute has incorrect size.");
 
+// This is nn::oe::DisplayVersion
+struct DisplayVersion {
+    std::array<char, 0x10> string;
+};
+static_assert(sizeof(DisplayVersion) == 0x10, "DisplayVersion has incorrect size.");
+
+// This is nn::pdm::ApplicationPlayStatistics
+struct ApplicationPlayStatistics {
+    u64 application_id;
+    u64 play_time_ns;
+    u64 launch_count;
+};
+static_assert(sizeof(ApplicationPlayStatistics) == 0x18,
+              "ApplicationPlayStatistics has incorrect size.");
+
 using AppletResourceUserId = u64;
 using ProgramId = u64;
 
diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h
index b29ecdfed..6c593ab1a 100644
--- a/src/core/hle/service/am/applet.h
+++ b/src/core/hle/service/am/applet.h
@@ -76,8 +76,8 @@ struct Applet {
     u32 application_core_usage_mode{};
 
     // Application functions
-    bool gameplay_recording_supported{};
-    GameplayRecordingState gameplay_recording_state{GameplayRecordingState::Disabled};
+    bool game_play_recording_supported{};
+    GamePlayRecordingState game_play_recording_state{GamePlayRecordingState::Disabled};
     bool jit_service_launched{};
     bool is_running{};
     bool application_crash_report_enabled{};
diff --git a/src/core/hle/service/am/application_functions.cpp b/src/core/hle/service/am/application_functions.cpp
deleted file mode 100644
index 51c5be2d1..000000000
--- a/src/core/hle/service/am/application_functions.cpp
+++ /dev/null
@@ -1,594 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "common/settings.h"
-#include "common/uuid.h"
-#include "core/file_sys/control_metadata.h"
-#include "core/file_sys/patch_manager.h"
-#include "core/file_sys/registered_cache.h"
-#include "core/file_sys/savedata_factory.h"
-#include "core/hle/service/acc/profile_manager.h"
-#include "core/hle/service/am/am_results.h"
-#include "core/hle/service/am/applet.h"
-#include "core/hle/service/am/application_functions.h"
-#include "core/hle/service/am/storage.h"
-#include "core/hle/service/filesystem/filesystem.h"
-#include "core/hle/service/filesystem/save_data_controller.h"
-#include "core/hle/service/ipc_helpers.h"
-#include "core/hle/service/ns/ns.h"
-#include "core/hle/service/sm/sm.h"
-
-namespace Service::AM {
-
-enum class LaunchParameterKind : u32 {
-    UserChannel = 1,
-    AccountPreselectedUser = 2,
-};
-
-IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet_)
-    : ServiceFramework{system_, "IApplicationFunctions"}, applet{std::move(applet_)} {
-    // clang-format off
-    static const FunctionInfo functions[] = {
-        {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
-        {10, nullptr, "CreateApplicationAndPushAndRequestToStart"},
-        {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
-        {12, nullptr, "CreateApplicationAndRequestToStart"},
-        {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"},
-        {14, nullptr, "CreateApplicationWithAttributeAndPushAndRequestToStartForQuest"},
-        {15, nullptr, "CreateApplicationWithAttributeAndRequestToStartForQuest"},
-        {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"},
-        {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
-        {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
-        {23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"},
-        {24, nullptr, "GetLaunchStorageInfoForDebug"},
-        {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"},
-        {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"},
-        {27, &IApplicationFunctions::CreateCacheStorage, "CreateCacheStorage"},
-        {28, &IApplicationFunctions::GetSaveDataSizeMax, "GetSaveDataSizeMax"},
-        {29, nullptr, "GetCacheStorageMax"},
-        {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
-        {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
-        {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
-        {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
-        {34, nullptr, "SelectApplicationLicense"},
-        {35, nullptr, "GetDeviceSaveDataSizeMax"},
-        {36, nullptr, "GetLimitedApplicationLicense"},
-        {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"},
-        {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
-        {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
-        {60, nullptr, "SetMediaPlaybackStateForApplication"},
-        {65, &IApplicationFunctions::IsGamePlayRecordingSupported, "IsGamePlayRecordingSupported"},
-        {66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"},
-        {67, &IApplicationFunctions::SetGamePlayRecordingState, "SetGamePlayRecordingState"},
-        {68, nullptr, "RequestFlushGamePlayingMovieForDebug"},
-        {70, nullptr, "RequestToShutdown"},
-        {71, nullptr, "RequestToReboot"},
-        {72, nullptr, "RequestToSleep"},
-        {80, nullptr, "ExitAndRequestToShowThanksMessage"},
-        {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
-        {100, &IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer, "InitializeApplicationCopyrightFrameBuffer"},
-        {101, &IApplicationFunctions::SetApplicationCopyrightImage, "SetApplicationCopyrightImage"},
-        {102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"},
-        {110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"},
-        {111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"},
-        {120, &IApplicationFunctions::ExecuteProgram, "ExecuteProgram"},
-        {121, &IApplicationFunctions::ClearUserChannel, "ClearUserChannel"},
-        {122, &IApplicationFunctions::UnpopToUserChannel, "UnpopToUserChannel"},
-        {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"},
-        {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
-        {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
-        {131, nullptr, "SetDelayTimeToAbortOnGpuError"},
-        {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"},
-        {141, &IApplicationFunctions::TryPopFromFriendInvitationStorageChannel, "TryPopFromFriendInvitationStorageChannel"},
-        {150, &IApplicationFunctions::GetNotificationStorageChannelEvent, "GetNotificationStorageChannelEvent"},
-        {151, nullptr, "TryPopFromNotificationStorageChannel"},
-        {160, &IApplicationFunctions::GetHealthWarningDisappearedSystemEvent, "GetHealthWarningDisappearedSystemEvent"},
-        {170, nullptr, "SetHdcpAuthenticationActivated"},
-        {180, nullptr, "GetLaunchRequiredVersion"},
-        {181, nullptr, "UpgradeLaunchRequiredVersion"},
-        {190, nullptr, "SendServerMaintenanceOverlayNotification"},
-        {200, nullptr, "GetLastApplicationExitReason"},
-        {500, nullptr, "StartContinuousRecordingFlushForDebug"},
-        {1000, nullptr, "CreateMovieMaker"},
-        {1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"},
-    };
-    // clang-format on
-
-    RegisterHandlers(functions);
-}
-
-IApplicationFunctions::~IApplicationFunctions() = default;
-
-void IApplicationFunctions::EnableApplicationCrashReport(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    std::scoped_lock lk{applet->lock};
-    applet->application_crash_report_enabled = true;
-
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::SetApplicationCopyrightImage(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::SetApplicationCopyrightVisibility(HLERequestContext& ctx) {
-    IPC::RequestParser rp{ctx};
-    const auto is_visible = rp.Pop<bool>();
-
-    LOG_WARNING(Service_AM, "(STUBBED) called, is_visible={}", is_visible);
-
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    std::scoped_lock lk{applet->lock};
-    applet->home_button_long_pressed_blocked = true;
-    applet->home_button_short_pressed_blocked = true;
-
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    std::scoped_lock lk{applet->lock};
-    applet->home_button_long_pressed_blocked = false;
-    applet->home_button_short_pressed_blocked = false;
-
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::BeginBlockingHomeButton(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    std::scoped_lock lk{applet->lock};
-    applet->home_button_long_pressed_blocked = true;
-    applet->home_button_short_pressed_blocked = true;
-    applet->home_button_double_click_enabled = true;
-
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::EndBlockingHomeButton(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    std::scoped_lock lk{applet->lock};
-    applet->home_button_long_pressed_blocked = false;
-    applet->home_button_short_pressed_blocked = false;
-    applet->home_button_double_click_enabled = false;
-
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) {
-    IPC::RequestParser rp{ctx};
-    const auto kind = rp.PopEnum<LaunchParameterKind>();
-
-    LOG_INFO(Service_AM, "called, kind={:08X}", kind);
-
-    std::scoped_lock lk{applet->lock};
-
-    auto& channel = kind == LaunchParameterKind::UserChannel
-                        ? applet->user_channel_launch_parameter
-                        : applet->preselected_user_launch_parameter;
-
-    if (channel.empty()) {
-        LOG_WARNING(Service_AM, "Attempted to pop parameter {} but none was found!", kind);
-        IPC::ResponseBuilder rb{ctx, 2};
-        rb.Push(AM::ResultNoDataInChannel);
-        return;
-    }
-
-    auto data = channel.back();
-    channel.pop_back();
-
-    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
-    rb.Push(ResultSuccess);
-    rb.PushIpcInterface<IStorage>(system, std::move(data));
-}
-
-void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) {
-    IPC::RequestParser rp{ctx};
-    u128 user_id = rp.PopRaw<u128>();
-
-    LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
-
-    FileSys::SaveDataAttribute attribute{};
-    attribute.title_id = applet->program_id;
-    attribute.user_id = user_id;
-    attribute.type = FileSys::SaveDataType::SaveData;
-
-    FileSys::VirtualDir save_data{};
-    const auto res = system.GetFileSystemController().OpenSaveDataController()->CreateSaveData(
-        &save_data, FileSys::SaveDataSpaceId::NandUser, attribute);
-
-    IPC::ResponseBuilder rb{ctx, 4};
-    rb.Push(res);
-    rb.Push<u64>(0);
-}
-
-void IApplicationFunctions::SetTerminateResult(HLERequestContext& ctx) {
-    // Takes an input u32 Result, no output.
-    // For example, in some cases official apps use this with error 0x2A2 then
-    // uses svcBreak.
-
-    IPC::RequestParser rp{ctx};
-    u32 result = rp.Pop<u32>();
-    LOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result);
-
-    std::scoped_lock lk{applet->lock};
-    applet->terminate_result = Result(result);
-
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::GetDisplayVersion(HLERequestContext& ctx) {
-    LOG_DEBUG(Service_AM, "called");
-
-    std::array<u8, 0x10> version_string{};
-
-    const auto res = [this] {
-        const FileSys::PatchManager pm{applet->program_id, system.GetFileSystemController(),
-                                       system.GetContentProvider()};
-        auto metadata = pm.GetControlMetadata();
-        if (metadata.first != nullptr) {
-            return metadata;
-        }
-
-        const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(applet->program_id),
-                                              system.GetFileSystemController(),
-                                              system.GetContentProvider()};
-        return pm_update.GetControlMetadata();
-    }();
-
-    if (res.first != nullptr) {
-        const auto& version = res.first->GetVersionString();
-        std::copy(version.begin(), version.end(), version_string.begin());
-    } else {
-        static constexpr char default_version[]{"1.0.0"};
-        std::memcpy(version_string.data(), default_version, sizeof(default_version));
-    }
-
-    IPC::ResponseBuilder rb{ctx, 6};
-    rb.Push(ResultSuccess);
-    rb.PushRaw(version_string);
-}
-
-void IApplicationFunctions::GetDesiredLanguage(HLERequestContext& ctx) {
-    // TODO(bunnei): This should be configurable
-    LOG_DEBUG(Service_AM, "called");
-
-    // Get supported languages from NACP, if possible
-    // Default to 0 (all languages supported)
-    u32 supported_languages = 0;
-
-    const auto res = [this] {
-        const FileSys::PatchManager pm{applet->program_id, system.GetFileSystemController(),
-                                       system.GetContentProvider()};
-        auto metadata = pm.GetControlMetadata();
-        if (metadata.first != nullptr) {
-            return metadata;
-        }
-
-        const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(applet->program_id),
-                                              system.GetFileSystemController(),
-                                              system.GetContentProvider()};
-        return pm_update.GetControlMetadata();
-    }();
-
-    if (res.first != nullptr) {
-        supported_languages = res.first->GetSupportedLanguages();
-    }
-
-    // Call IApplicationManagerInterface implementation.
-    auto& service_manager = system.ServiceManager();
-    auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2");
-    auto app_man = ns_am2->GetApplicationManagerInterface();
-
-    // Get desired application language
-    u8 desired_language{};
-    const auto res_lang =
-        app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages);
-    if (res_lang != ResultSuccess) {
-        IPC::ResponseBuilder rb{ctx, 2};
-        rb.Push(res_lang);
-        return;
-    }
-
-    // Convert to settings language code.
-    u64 language_code{};
-    const auto res_code =
-        app_man->ConvertApplicationLanguageToLanguageCode(&language_code, desired_language);
-    if (res_code != ResultSuccess) {
-        IPC::ResponseBuilder rb{ctx, 2};
-        rb.Push(res_code);
-        return;
-    }
-
-    LOG_DEBUG(Service_AM, "got desired_language={:016X}", language_code);
-
-    IPC::ResponseBuilder rb{ctx, 4};
-    rb.Push(ResultSuccess);
-    rb.Push(language_code);
-}
-
-void IApplicationFunctions::IsGamePlayRecordingSupported(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    IPC::ResponseBuilder rb{ctx, 3};
-    rb.Push(ResultSuccess);
-    rb.Push(applet->gameplay_recording_supported);
-}
-
-void IApplicationFunctions::InitializeGamePlayRecording(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::SetGamePlayRecordingState(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    IPC::RequestParser rp{ctx};
-
-    std::scoped_lock lk{applet->lock};
-    applet->gameplay_recording_state = rp.PopRaw<GameplayRecordingState>();
-
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::NotifyRunning(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    std::scoped_lock lk{applet->lock};
-    applet->is_running = true;
-
-    IPC::ResponseBuilder rb{ctx, 3};
-    rb.Push(ResultSuccess);
-    rb.Push<u8>(0); // Unknown, seems to be ignored by official processes
-}
-
-void IApplicationFunctions::GetPseudoDeviceId(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    IPC::ResponseBuilder rb{ctx, 6};
-    rb.Push(ResultSuccess);
-
-    // Returns a 128-bit UUID
-    rb.Push<u64>(0);
-    rb.Push<u64>(0);
-}
-
-void IApplicationFunctions::ExtendSaveData(HLERequestContext& ctx) {
-    struct Parameters {
-        FileSys::SaveDataType type;
-        u128 user_id;
-        u64 new_normal_size;
-        u64 new_journal_size;
-    };
-    static_assert(sizeof(Parameters) == 40);
-
-    IPC::RequestParser rp{ctx};
-    const auto [type, user_id, new_normal_size, new_journal_size] = rp.PopRaw<Parameters>();
-
-    LOG_DEBUG(Service_AM,
-              "called with type={:02X}, user_id={:016X}{:016X}, new_normal={:016X}, "
-              "new_journal={:016X}",
-              static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
-
-    system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize(
-        type, applet->program_id, user_id, {new_normal_size, new_journal_size});
-
-    IPC::ResponseBuilder rb{ctx, 4};
-    rb.Push(ResultSuccess);
-
-    // The following value is used upon failure to help the system recover.
-    // Since we always succeed, this should be 0.
-    rb.Push<u64>(0);
-}
-
-void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) {
-    struct Parameters {
-        FileSys::SaveDataType type;
-        u128 user_id;
-    };
-    static_assert(sizeof(Parameters) == 24);
-
-    IPC::RequestParser rp{ctx};
-    const auto [type, user_id] = rp.PopRaw<Parameters>();
-
-    LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1],
-              user_id[0]);
-
-    const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize(
-        type, applet->program_id, user_id);
-
-    IPC::ResponseBuilder rb{ctx, 6};
-    rb.Push(ResultSuccess);
-    rb.Push(size.normal);
-    rb.Push(size.journal);
-}
-
-void IApplicationFunctions::CreateCacheStorage(HLERequestContext& ctx) {
-    struct InputParameters {
-        u16 index;
-        s64 size;
-        s64 journal_size;
-    };
-    static_assert(sizeof(InputParameters) == 24);
-
-    struct OutputParameters {
-        u32 storage_target;
-        u64 required_size;
-    };
-    static_assert(sizeof(OutputParameters) == 16);
-
-    IPC::RequestParser rp{ctx};
-    const auto params = rp.PopRaw<InputParameters>();
-
-    LOG_WARNING(Service_AM, "(STUBBED) called with index={}, size={:#x}, journal_size={:#x}",
-                params.index, params.size, params.journal_size);
-
-    const OutputParameters resp{
-        .storage_target = 1,
-        .required_size = 0,
-    };
-
-    IPC::ResponseBuilder rb{ctx, 6};
-    rb.Push(ResultSuccess);
-    rb.PushRaw(resp);
-}
-
-void IApplicationFunctions::GetSaveDataSizeMax(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    constexpr u64 size_max_normal = 0xFFFFFFF;
-    constexpr u64 size_max_journal = 0xFFFFFFF;
-
-    IPC::ResponseBuilder rb{ctx, 6};
-    rb.Push(ResultSuccess);
-    rb.Push(size_max_normal);
-    rb.Push(size_max_journal);
-}
-
-void IApplicationFunctions::QueryApplicationPlayStatistics(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    IPC::ResponseBuilder rb{ctx, 3};
-    rb.Push(ResultSuccess);
-    rb.Push<u32>(0);
-}
-
-void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    IPC::ResponseBuilder rb{ctx, 3};
-    rb.Push(ResultSuccess);
-    rb.Push<u32>(0);
-}
-
-void IApplicationFunctions::ExecuteProgram(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    IPC::RequestParser rp{ctx};
-    [[maybe_unused]] const auto unk_1 = rp.Pop<u32>();
-    [[maybe_unused]] const auto unk_2 = rp.Pop<u32>();
-    const auto program_index = rp.Pop<u64>();
-
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(ResultSuccess);
-
-    // Swap user channel ownership into the system so that it will be preserved
-    system.GetUserChannel().swap(applet->user_channel_launch_parameter);
-    system.ExecuteProgram(program_index);
-}
-
-void IApplicationFunctions::ClearUserChannel(HLERequestContext& ctx) {
-    LOG_DEBUG(Service_AM, "called");
-
-    applet->user_channel_launch_parameter.clear();
-
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::UnpopToUserChannel(HLERequestContext& ctx) {
-    LOG_DEBUG(Service_AM, "called");
-
-    IPC::RequestParser rp{ctx};
-    const auto storage = rp.PopIpcInterface<IStorage>().lock();
-    if (storage) {
-        applet->user_channel_launch_parameter.push_back(storage->GetData());
-    }
-
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(ResultSuccess);
-}
-
-void IApplicationFunctions::GetPreviousProgramIndex(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    IPC::ResponseBuilder rb{ctx, 3};
-    rb.Push(ResultSuccess);
-    rb.Push<s32>(applet->previous_program_index);
-}
-
-void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    IPC::ResponseBuilder rb{ctx, 2, 1};
-    rb.Push(ResultSuccess);
-    rb.PushCopyObjects(applet->gpu_error_detected_event.GetHandle());
-}
-
-void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx) {
-    LOG_DEBUG(Service_AM, "called");
-
-    IPC::ResponseBuilder rb{ctx, 2, 1};
-    rb.Push(ResultSuccess);
-    rb.PushCopyObjects(applet->friend_invitation_storage_channel_event.GetHandle());
-}
-
-void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx) {
-    LOG_DEBUG(Service_AM, "(STUBBED) called");
-
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(AM::ResultNoDataInChannel);
-}
-
-void IApplicationFunctions::GetNotificationStorageChannelEvent(HLERequestContext& ctx) {
-    LOG_DEBUG(Service_AM, "called");
-
-    IPC::ResponseBuilder rb{ctx, 2, 1};
-    rb.Push(ResultSuccess);
-    rb.PushCopyObjects(applet->notification_storage_channel_event.GetHandle());
-}
-
-void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx) {
-    LOG_DEBUG(Service_AM, "called");
-
-    IPC::ResponseBuilder rb{ctx, 2, 1};
-    rb.Push(ResultSuccess);
-    rb.PushCopyObjects(applet->health_warning_disappeared_system_event.GetHandle());
-}
-
-void IApplicationFunctions::PrepareForJit(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
-
-    std::scoped_lock lk{applet->lock};
-    applet->jit_service_launched = true;
-
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(ResultSuccess);
-}
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/application_functions.h b/src/core/hle/service/am/application_functions.h
deleted file mode 100644
index 55eb21d39..000000000
--- a/src/core/hle/service/am/application_functions.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/service/kernel_helpers.h"
-#include "core/hle/service/service.h"
-
-namespace Service::AM {
-
-struct Applet;
-
-class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
-public:
-    explicit IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet_);
-    ~IApplicationFunctions() override;
-
-private:
-    void PopLaunchParameter(HLERequestContext& ctx);
-    void CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx);
-    void EnsureSaveData(HLERequestContext& ctx);
-    void SetTerminateResult(HLERequestContext& ctx);
-    void GetDisplayVersion(HLERequestContext& ctx);
-    void GetDesiredLanguage(HLERequestContext& ctx);
-    void IsGamePlayRecordingSupported(HLERequestContext& ctx);
-    void InitializeGamePlayRecording(HLERequestContext& ctx);
-    void SetGamePlayRecordingState(HLERequestContext& ctx);
-    void NotifyRunning(HLERequestContext& ctx);
-    void GetPseudoDeviceId(HLERequestContext& ctx);
-    void ExtendSaveData(HLERequestContext& ctx);
-    void GetSaveDataSize(HLERequestContext& ctx);
-    void CreateCacheStorage(HLERequestContext& ctx);
-    void GetSaveDataSizeMax(HLERequestContext& ctx);
-    void BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx);
-    void EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx);
-    void BeginBlockingHomeButton(HLERequestContext& ctx);
-    void EndBlockingHomeButton(HLERequestContext& ctx);
-    void EnableApplicationCrashReport(HLERequestContext& ctx);
-    void InitializeApplicationCopyrightFrameBuffer(HLERequestContext& ctx);
-    void SetApplicationCopyrightImage(HLERequestContext& ctx);
-    void SetApplicationCopyrightVisibility(HLERequestContext& ctx);
-    void QueryApplicationPlayStatistics(HLERequestContext& ctx);
-    void QueryApplicationPlayStatisticsByUid(HLERequestContext& ctx);
-    void ExecuteProgram(HLERequestContext& ctx);
-    void ClearUserChannel(HLERequestContext& ctx);
-    void UnpopToUserChannel(HLERequestContext& ctx);
-    void GetPreviousProgramIndex(HLERequestContext& ctx);
-    void GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx);
-    void GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx);
-    void TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx);
-    void GetNotificationStorageChannelEvent(HLERequestContext& ctx);
-    void GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx);
-    void PrepareForJit(HLERequestContext& ctx);
-
-    const std::shared_ptr<Applet> applet;
-};
-
-} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/application_functions.cpp b/src/core/hle/service/am/service/application_functions.cpp
new file mode 100644
index 000000000..346ab42ab
--- /dev/null
+++ b/src/core/hle/service/am/service/application_functions.cpp
@@ -0,0 +1,465 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/settings.h"
+#include "common/uuid.h"
+#include "core/file_sys/control_metadata.h"
+#include "core/file_sys/patch_manager.h"
+#include "core/file_sys/registered_cache.h"
+#include "core/file_sys/savedata_factory.h"
+#include "core/hle/kernel/k_transfer_memory.h"
+#include "core/hle/service/am/am_results.h"
+#include "core/hle/service/am/applet.h"
+#include "core/hle/service/am/service/application_functions.h"
+#include "core/hle/service/am/storage.h"
+#include "core/hle/service/cmif_serialization.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/filesystem/save_data_controller.h"
+#include "core/hle/service/ns/ns.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Service::AM {
+
+IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet)
+    : ServiceFramework{system_, "IApplicationFunctions"}, m_applet{std::move(applet)} {
+    // clang-format off
+    static const FunctionInfo functions[] = {
+        {1, D<&IApplicationFunctions::PopLaunchParameter>, "PopLaunchParameter"},
+        {10, nullptr, "CreateApplicationAndPushAndRequestToStart"},
+        {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
+        {12, nullptr, "CreateApplicationAndRequestToStart"},
+        {13, nullptr, "CreateApplicationAndRequestToStartForQuest"},
+        {14, nullptr, "CreateApplicationWithAttributeAndPushAndRequestToStartForQuest"},
+        {15, nullptr, "CreateApplicationWithAttributeAndRequestToStartForQuest"},
+        {20, D<&IApplicationFunctions::EnsureSaveData>, "EnsureSaveData"},
+        {21, D<&IApplicationFunctions::GetDesiredLanguage>, "GetDesiredLanguage"},
+        {22, D<&IApplicationFunctions::SetTerminateResult>, "SetTerminateResult"},
+        {23, D<&IApplicationFunctions::GetDisplayVersion>, "GetDisplayVersion"},
+        {24, nullptr, "GetLaunchStorageInfoForDebug"},
+        {25, D<&IApplicationFunctions::ExtendSaveData>, "ExtendSaveData"},
+        {26, D<&IApplicationFunctions::GetSaveDataSize>, "GetSaveDataSize"},
+        {27, D<&IApplicationFunctions::CreateCacheStorage>, "CreateCacheStorage"},
+        {28, D<&IApplicationFunctions::GetSaveDataSizeMax>, "GetSaveDataSizeMax"},
+        {29, nullptr, "GetCacheStorageMax"},
+        {30, D<&IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed>, "BeginBlockingHomeButtonShortAndLongPressed"},
+        {31, D<&IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed>, "EndBlockingHomeButtonShortAndLongPressed"},
+        {32, D<&IApplicationFunctions::BeginBlockingHomeButton>, "BeginBlockingHomeButton"},
+        {33, D<&IApplicationFunctions::EndBlockingHomeButton>, "EndBlockingHomeButton"},
+        {34, nullptr, "SelectApplicationLicense"},
+        {35, nullptr, "GetDeviceSaveDataSizeMax"},
+        {36, nullptr, "GetLimitedApplicationLicense"},
+        {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"},
+        {40, D<&IApplicationFunctions::NotifyRunning>, "NotifyRunning"},
+        {50, D<&IApplicationFunctions::GetPseudoDeviceId>, "GetPseudoDeviceId"},
+        {60, nullptr, "SetMediaPlaybackStateForApplication"},
+        {65, D<&IApplicationFunctions::IsGamePlayRecordingSupported>, "IsGamePlayRecordingSupported"},
+        {66, D<&IApplicationFunctions::InitializeGamePlayRecording>, "InitializeGamePlayRecording"},
+        {67, D<&IApplicationFunctions::SetGamePlayRecordingState>, "SetGamePlayRecordingState"},
+        {68, nullptr, "RequestFlushGamePlayingMovieForDebug"},
+        {70, nullptr, "RequestToShutdown"},
+        {71, nullptr, "RequestToReboot"},
+        {72, nullptr, "RequestToSleep"},
+        {80, nullptr, "ExitAndRequestToShowThanksMessage"},
+        {90, D<&IApplicationFunctions::EnableApplicationCrashReport>, "EnableApplicationCrashReport"},
+        {100, D<&IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer>, "InitializeApplicationCopyrightFrameBuffer"},
+        {101, D<&IApplicationFunctions::SetApplicationCopyrightImage>, "SetApplicationCopyrightImage"},
+        {102, D<&IApplicationFunctions::SetApplicationCopyrightVisibility>, "SetApplicationCopyrightVisibility"},
+        {110, D<&IApplicationFunctions::QueryApplicationPlayStatistics>, "QueryApplicationPlayStatistics"},
+        {111, D<&IApplicationFunctions::QueryApplicationPlayStatisticsByUid>, "QueryApplicationPlayStatisticsByUid"},
+        {120, D<&IApplicationFunctions::ExecuteProgram>, "ExecuteProgram"},
+        {121, D<&IApplicationFunctions::ClearUserChannel>, "ClearUserChannel"},
+        {122, D<&IApplicationFunctions::UnpopToUserChannel>, "UnpopToUserChannel"},
+        {123, D<&IApplicationFunctions::GetPreviousProgramIndex>, "GetPreviousProgramIndex"},
+        {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
+        {130, D<&IApplicationFunctions::GetGpuErrorDetectedSystemEvent>, "GetGpuErrorDetectedSystemEvent"},
+        {131, nullptr, "SetDelayTimeToAbortOnGpuError"},
+        {140, D<&IApplicationFunctions::GetFriendInvitationStorageChannelEvent>, "GetFriendInvitationStorageChannelEvent"},
+        {141, D<&IApplicationFunctions::TryPopFromFriendInvitationStorageChannel>, "TryPopFromFriendInvitationStorageChannel"},
+        {150, D<&IApplicationFunctions::GetNotificationStorageChannelEvent>, "GetNotificationStorageChannelEvent"},
+        {151, nullptr, "TryPopFromNotificationStorageChannel"},
+        {160, D<&IApplicationFunctions::GetHealthWarningDisappearedSystemEvent>, "GetHealthWarningDisappearedSystemEvent"},
+        {170, nullptr, "SetHdcpAuthenticationActivated"},
+        {180, nullptr, "GetLaunchRequiredVersion"},
+        {181, nullptr, "UpgradeLaunchRequiredVersion"},
+        {190, nullptr, "SendServerMaintenanceOverlayNotification"},
+        {200, nullptr, "GetLastApplicationExitReason"},
+        {500, nullptr, "StartContinuousRecordingFlushForDebug"},
+        {1000, nullptr, "CreateMovieMaker"},
+        {1001, D<&IApplicationFunctions::PrepareForJit>, "PrepareForJit"},
+    };
+    // clang-format on
+
+    RegisterHandlers(functions);
+}
+
+IApplicationFunctions::~IApplicationFunctions() = default;
+
+Result IApplicationFunctions::PopLaunchParameter(Out<SharedPointer<IStorage>> out_storage,
+                                                 LaunchParameterKind launch_parameter_kind) {
+    LOG_INFO(Service_AM, "called, kind={}", launch_parameter_kind);
+
+    std::scoped_lock lk{m_applet->lock};
+
+    auto& channel = launch_parameter_kind == LaunchParameterKind::UserChannel
+                        ? m_applet->user_channel_launch_parameter
+                        : m_applet->preselected_user_launch_parameter;
+
+    if (channel.empty()) {
+        LOG_WARNING(Service_AM, "Attempted to pop parameter {} but none was found!",
+                    launch_parameter_kind);
+        R_THROW(AM::ResultNoDataInChannel);
+    }
+
+    auto data = channel.back();
+    channel.pop_back();
+
+    *out_storage = std::make_shared<IStorage>(system, std::move(data));
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::EnsureSaveData(Out<u64> out_size, Common::UUID user_id) {
+    LOG_INFO(Service_AM, "called, uid={}", user_id.FormattedString());
+
+    FileSys::SaveDataAttribute attribute{};
+    attribute.title_id = m_applet->program_id;
+    attribute.user_id = user_id.AsU128();
+    attribute.type = FileSys::SaveDataType::SaveData;
+
+    FileSys::VirtualDir save_data{};
+    R_TRY(system.GetFileSystemController().OpenSaveDataController()->CreateSaveData(
+        &save_data, FileSys::SaveDataSpaceId::NandUser, attribute));
+
+    *out_size = 0;
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetDesiredLanguage(Out<u64> out_language_code) {
+    // FIXME: all of this stuff belongs to ns
+    // TODO(bunnei): This should be configurable
+    LOG_DEBUG(Service_AM, "called");
+
+    // Get supported languages from NACP, if possible
+    // Default to 0 (all languages supported)
+    u32 supported_languages = 0;
+
+    const auto res = [this] {
+        const FileSys::PatchManager pm{m_applet->program_id, system.GetFileSystemController(),
+                                       system.GetContentProvider()};
+        auto metadata = pm.GetControlMetadata();
+        if (metadata.first != nullptr) {
+            return metadata;
+        }
+
+        const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(m_applet->program_id),
+                                              system.GetFileSystemController(),
+                                              system.GetContentProvider()};
+        return pm_update.GetControlMetadata();
+    }();
+
+    if (res.first != nullptr) {
+        supported_languages = res.first->GetSupportedLanguages();
+    }
+
+    // Call IApplicationManagerInterface implementation.
+    auto& service_manager = system.ServiceManager();
+    auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2");
+    auto app_man = ns_am2->GetApplicationManagerInterface();
+
+    // Get desired application language
+    u8 desired_language{};
+    R_TRY(app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages));
+
+    // Convert to settings language code.
+    R_TRY(app_man->ConvertApplicationLanguageToLanguageCode(out_language_code, desired_language));
+
+    LOG_DEBUG(Service_AM, "got desired_language={:016X}", *out_language_code);
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::SetTerminateResult(Result terminate_result) {
+    LOG_INFO(Service_AM, "(STUBBED) called, result={:#x} ({}-{})", terminate_result.GetInnerValue(),
+             static_cast<u32>(terminate_result.GetModule()) + 2000,
+             terminate_result.GetDescription());
+
+    std::scoped_lock lk{m_applet->lock};
+    m_applet->terminate_result = terminate_result;
+
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetDisplayVersion(Out<DisplayVersion> out_display_version) {
+    LOG_DEBUG(Service_AM, "called");
+
+    const auto res = [this] {
+        const FileSys::PatchManager pm{m_applet->program_id, system.GetFileSystemController(),
+                                       system.GetContentProvider()};
+        auto metadata = pm.GetControlMetadata();
+        if (metadata.first != nullptr) {
+            return metadata;
+        }
+
+        const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(m_applet->program_id),
+                                              system.GetFileSystemController(),
+                                              system.GetContentProvider()};
+        return pm_update.GetControlMetadata();
+    }();
+
+    if (res.first != nullptr) {
+        const auto& version = res.first->GetVersionString();
+        std::memcpy(out_display_version->string.data(), version.data(),
+                    std::min(version.size(), out_display_version->string.size()));
+    } else {
+        static constexpr char default_version[]{"1.0.0"};
+        std::memcpy(out_display_version->string.data(), default_version, sizeof(default_version));
+    }
+
+    out_display_version->string[out_display_version->string.size() - 1] = '\0';
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::ExtendSaveData(Out<u64> out_required_size, FileSys::SaveDataType type,
+                                             Common::UUID user_id, u64 normal_size,
+                                             u64 journal_size) {
+    LOG_DEBUG(Service_AM, "called with type={} user_id={} normal={:#x} journal={:#x}",
+              static_cast<u8>(type), user_id.FormattedString(), normal_size, journal_size);
+
+    system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize(
+        type, m_applet->program_id, user_id.AsU128(), {normal_size, journal_size});
+
+    // The following value is used to indicate the amount of space remaining on failure
+    // due to running out of space. Since we always succeed, this should be 0.
+    *out_required_size = 0;
+
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetSaveDataSize(Out<u64> out_normal_size, Out<u64> out_journal_size,
+                                              FileSys::SaveDataType type, Common::UUID user_id) {
+    LOG_DEBUG(Service_AM, "called with type={} user_id={}", type, user_id.FormattedString());
+
+    const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize(
+        type, m_applet->program_id, user_id.AsU128());
+
+    *out_normal_size = size.normal;
+    *out_journal_size = size.journal;
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::CreateCacheStorage(Out<u32> out_target_media,
+                                                 Out<u64> out_required_size, u16 index,
+                                                 u64 normal_size, u64 journal_size) {
+    LOG_WARNING(Service_AM, "(STUBBED) called with index={} size={:#x} journal_size={:#x}", index,
+                normal_size, journal_size);
+
+    *out_target_media = 1; // Nand
+    *out_required_size = 0;
+
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetSaveDataSizeMax(Out<u64> out_max_normal_size,
+                                                 Out<u64> out_max_journal_size) {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+
+    *out_max_normal_size = 0xFFFFFFF;
+    *out_max_journal_size = 0xFFFFFFF;
+
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(s64 unused) {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+
+    std::scoped_lock lk{m_applet->lock};
+    m_applet->home_button_long_pressed_blocked = true;
+    m_applet->home_button_short_pressed_blocked = true;
+
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed() {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+
+    std::scoped_lock lk{m_applet->lock};
+    m_applet->home_button_long_pressed_blocked = false;
+    m_applet->home_button_short_pressed_blocked = false;
+
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::BeginBlockingHomeButton(s64 timeout_ns) {
+    LOG_WARNING(Service_AM, "(STUBBED) called, timeout_ns={}", timeout_ns);
+
+    std::scoped_lock lk{m_applet->lock};
+    m_applet->home_button_long_pressed_blocked = true;
+    m_applet->home_button_short_pressed_blocked = true;
+    m_applet->home_button_double_click_enabled = true;
+
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::EndBlockingHomeButton() {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+
+    std::scoped_lock lk{m_applet->lock};
+    m_applet->home_button_long_pressed_blocked = false;
+    m_applet->home_button_short_pressed_blocked = false;
+    m_applet->home_button_double_click_enabled = false;
+
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+    *out_became_running = true;
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+    *out_pseudo_device_id = {};
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::IsGamePlayRecordingSupported(
+    Out<bool> out_is_game_play_recording_supported) {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+    *out_is_game_play_recording_supported = m_applet->game_play_recording_supported;
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::InitializeGamePlayRecording(
+    u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle) {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::SetGamePlayRecordingState(
+    GamePlayRecordingState game_play_recording_state) {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+
+    std::scoped_lock lk{m_applet->lock};
+    m_applet->game_play_recording_state = game_play_recording_state;
+
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::EnableApplicationCrashReport(bool enabled) {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+
+    std::scoped_lock lk{m_applet->lock};
+    m_applet->application_crash_report_enabled = enabled;
+
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer(
+    s32 width, s32 height, u64 transfer_memory_size,
+    InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle) {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::SetApplicationCopyrightImage(
+    s32 x, s32 y, s32 width, s32 height, WindowOriginMode window_origin_mode,
+    InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> image_data) {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::SetApplicationCopyrightVisibility(bool visible) {
+    LOG_WARNING(Service_AM, "(STUBBED) called, is_visible={}", visible);
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::QueryApplicationPlayStatistics(
+    Out<s32> out_entries,
+    OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_play_statistics,
+    InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+    *out_entries = 0;
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::QueryApplicationPlayStatisticsByUid(
+    Out<s32> out_entries,
+    OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_play_statistics,
+    Common::UUID user_id, InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+    *out_entries = 0;
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::ExecuteProgram(ProgramSpecifyKind kind, u64 value) {
+    LOG_WARNING(Service_AM, "(STUBBED) called, kind={}, value={}", kind, value);
+    ASSERT(kind == ProgramSpecifyKind::ExecuteProgram ||
+           kind == ProgramSpecifyKind::RestartProgram);
+
+    // Copy user channel ownership into the system so that it will be preserved
+    system.GetUserChannel() = m_applet->user_channel_launch_parameter;
+    system.ExecuteProgram(value);
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::ClearUserChannel() {
+    LOG_DEBUG(Service_AM, "called");
+    m_applet->user_channel_launch_parameter.clear();
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::UnpopToUserChannel(SharedPointer<IStorage> storage) {
+    LOG_DEBUG(Service_AM, "called");
+    m_applet->user_channel_launch_parameter.push_back(storage->GetData());
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetPreviousProgramIndex(Out<s32> out_previous_program_index) {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+    *out_previous_program_index = m_applet->previous_program_index;
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetGpuErrorDetectedSystemEvent(
+    OutCopyHandle<Kernel::KReadableEvent> out_event) {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+    *out_event = m_applet->gpu_error_detected_event.GetHandle();
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetFriendInvitationStorageChannelEvent(
+    OutCopyHandle<Kernel::KReadableEvent> out_event) {
+    LOG_DEBUG(Service_AM, "called");
+    *out_event = m_applet->friend_invitation_storage_channel_event.GetHandle();
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::TryPopFromFriendInvitationStorageChannel(
+    Out<SharedPointer<IStorage>> out_storage) {
+    LOG_INFO(Service_AM, "(STUBBED) called");
+    R_THROW(AM::ResultNoDataInChannel);
+}
+
+Result IApplicationFunctions::GetNotificationStorageChannelEvent(
+    OutCopyHandle<Kernel::KReadableEvent> out_event) {
+    LOG_DEBUG(Service_AM, "called");
+    *out_event = m_applet->notification_storage_channel_event.GetHandle();
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(
+    OutCopyHandle<Kernel::KReadableEvent> out_event) {
+    LOG_DEBUG(Service_AM, "called");
+    *out_event = m_applet->health_warning_disappeared_system_event.GetHandle();
+    R_SUCCEED();
+}
+
+Result IApplicationFunctions::PrepareForJit() {
+    LOG_WARNING(Service_AM, "(STUBBED) called");
+
+    std::scoped_lock lk{m_applet->lock};
+    m_applet->jit_service_launched = true;
+
+    R_SUCCEED();
+}
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/application_functions.h b/src/core/hle/service/am/service/application_functions.h
new file mode 100644
index 000000000..3548202f8
--- /dev/null
+++ b/src/core/hle/service/am/service/application_functions.h
@@ -0,0 +1,83 @@
+// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/uuid.h"
+#include "core/hle/service/am/am_types.h"
+#include "core/hle/service/cmif_types.h"
+#include "core/hle/service/service.h"
+
+namespace FileSys {
+enum class SaveDataType : u8;
+}
+
+namespace Kernel {
+class KReadableEvent;
+}
+
+namespace Service::AM {
+
+struct Applet;
+class IStorage;
+
+class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
+public:
+    explicit IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet);
+    ~IApplicationFunctions() override;
+
+private:
+    Result PopLaunchParameter(Out<SharedPointer<IStorage>> out_storage,
+                              LaunchParameterKind launch_parameter_kind);
+    Result EnsureSaveData(Out<u64> out_size, Common::UUID user_id);
+    Result GetDesiredLanguage(Out<u64> out_language_code);
+    Result SetTerminateResult(Result terminate_result);
+    Result GetDisplayVersion(Out<DisplayVersion> out_display_version);
+    Result ExtendSaveData(Out<u64> out_required_size, FileSys::SaveDataType type,
+                          Common::UUID user_id, u64 normal_size, u64 journal_size);
+    Result GetSaveDataSize(Out<u64> out_normal_size, Out<u64> out_journal_size,
+                           FileSys::SaveDataType type, Common::UUID user_id);
+    Result CreateCacheStorage(Out<u32> out_target_media, Out<u64> out_required_size, u16 index,
+                              u64 normal_size, u64 journal_size);
+    Result GetSaveDataSizeMax(Out<u64> out_max_normal_size, Out<u64> out_max_journal_size);
+    Result BeginBlockingHomeButtonShortAndLongPressed(s64 unused);
+    Result EndBlockingHomeButtonShortAndLongPressed();
+    Result BeginBlockingHomeButton(s64 timeout_ns);
+    Result EndBlockingHomeButton();
+    Result NotifyRunning(Out<bool> out_became_running);
+    Result GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id);
+    Result IsGamePlayRecordingSupported(Out<bool> out_is_game_play_recording_supported);
+    Result InitializeGamePlayRecording(
+        u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle);
+    Result SetGamePlayRecordingState(GamePlayRecordingState game_play_recording_state);
+    Result EnableApplicationCrashReport(bool enabled);
+    Result InitializeApplicationCopyrightFrameBuffer(
+        s32 width, s32 height, u64 transfer_memory_size,
+        InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle);
+    Result SetApplicationCopyrightImage(
+        s32 x, s32 y, s32 width, s32 height, WindowOriginMode window_origin_mode,
+        InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> image_data);
+    Result SetApplicationCopyrightVisibility(bool visible);
+    Result QueryApplicationPlayStatistics(
+        Out<s32> out_entries,
+        OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_play_statistics,
+        InArray<u64, BufferAttr_HipcMapAlias> application_ids);
+    Result QueryApplicationPlayStatisticsByUid(
+        Out<s32> out_entries,
+        OutArray<ApplicationPlayStatistics, BufferAttr_HipcMapAlias> out_play_statistics,
+        Common::UUID user_id, InArray<u64, BufferAttr_HipcMapAlias> application_ids);
+    Result ExecuteProgram(ProgramSpecifyKind kind, u64 value);
+    Result ClearUserChannel();
+    Result UnpopToUserChannel(SharedPointer<IStorage> storage);
+    Result GetPreviousProgramIndex(Out<s32> out_previous_program_index);
+    Result GetGpuErrorDetectedSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+    Result GetFriendInvitationStorageChannelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+    Result TryPopFromFriendInvitationStorageChannel(Out<SharedPointer<IStorage>> out_storage);
+    Result GetNotificationStorageChannelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+    Result GetHealthWarningDisappearedSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
+    Result PrepareForJit();
+
+    const std::shared_ptr<Applet> m_applet;
+};
+
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/service/application_proxy.cpp b/src/core/hle/service/am/service/application_proxy.cpp
index b22960610..55a4ea72f 100644
--- a/src/core/hle/service/am/service/application_proxy.cpp
+++ b/src/core/hle/service/am/service/application_proxy.cpp
@@ -1,7 +1,6 @@
 // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
-#include "core/hle/service/am/application_functions.h"
 #include "core/hle/service/am/common_state_getter.h"
 #include "core/hle/service/am/debug_functions.h"
 #include "core/hle/service/am/display_controller.h"
@@ -10,6 +9,7 @@
 #include "core/hle/service/am/process_winding_controller.h"
 #include "core/hle/service/am/self_controller.h"
 #include "core/hle/service/am/service/applet_common_functions.h"
+#include "core/hle/service/am/service/application_functions.h"
 #include "core/hle/service/am/service/application_proxy.h"
 #include "core/hle/service/am/service/audio_controller.h"
 #include "core/hle/service/am/window_controller.h"