code:
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/ns/language.h"
#include "core/hle/service/set/settings_server.h"

namespace Service::NS {

constexpr ApplicationLanguagePriorityList priority_list_american_english = {{
    ApplicationLanguage::AmericanEnglish,
    ApplicationLanguage::BritishEnglish,
    ApplicationLanguage::LatinAmericanSpanish,
    ApplicationLanguage::CanadianFrench,
    ApplicationLanguage::French,
    ApplicationLanguage::German,
    ApplicationLanguage::Spanish,
    ApplicationLanguage::Italian,
    ApplicationLanguage::Dutch,
    ApplicationLanguage::Portuguese,
    ApplicationLanguage::Russian,
    ApplicationLanguage::Japanese,
    ApplicationLanguage::SimplifiedChinese,
    ApplicationLanguage::TraditionalChinese,
    ApplicationLanguage::Korean,
}};

constexpr ApplicationLanguagePriorityList priority_list_british_english = {{
    ApplicationLanguage::BritishEnglish,
    ApplicationLanguage::AmericanEnglish,
    ApplicationLanguage::French,
    ApplicationLanguage::German,
    ApplicationLanguage::Spanish,
    ApplicationLanguage::Italian,
    ApplicationLanguage::Dutch,
    ApplicationLanguage::Portuguese,
    ApplicationLanguage::Russian,
    ApplicationLanguage::LatinAmericanSpanish,
    ApplicationLanguage::CanadianFrench,
    ApplicationLanguage::Japanese,
    ApplicationLanguage::SimplifiedChinese,
    ApplicationLanguage::TraditionalChinese,
    ApplicationLanguage::Korean,
}};

constexpr ApplicationLanguagePriorityList priority_list_japanese = {{
    ApplicationLanguage::Japanese,
    ApplicationLanguage::AmericanEnglish,
    ApplicationLanguage::BritishEnglish,
    ApplicationLanguage::LatinAmericanSpanish,
    ApplicationLanguage::CanadianFrench,
    ApplicationLanguage::French,
    ApplicationLanguage::German,
    ApplicationLanguage::Spanish,
    ApplicationLanguage::Italian,
    ApplicationLanguage::Dutch,
    ApplicationLanguage::Portuguese,
    ApplicationLanguage::Russian,
    ApplicationLanguage::SimplifiedChinese,
    ApplicationLanguage::TraditionalChinese,
    ApplicationLanguage::Korean,
}};

constexpr ApplicationLanguagePriorityList priority_list_french = {{
    ApplicationLanguage::French,
    ApplicationLanguage::CanadianFrench,
    ApplicationLanguage::BritishEnglish,
    ApplicationLanguage::AmericanEnglish,
    ApplicationLanguage::German,
    ApplicationLanguage::Spanish,
    ApplicationLanguage::Italian,
    ApplicationLanguage::Dutch,
    ApplicationLanguage::Portuguese,
    ApplicationLanguage::Russian,
    ApplicationLanguage::LatinAmericanSpanish,
    ApplicationLanguage::Japanese,
    ApplicationLanguage::SimplifiedChinese,
    ApplicationLanguage::TraditionalChinese,
    ApplicationLanguage::Korean,
}};

constexpr ApplicationLanguagePriorityList priority_list_german = {{
    ApplicationLanguage::German,
    ApplicationLanguage::BritishEnglish,
    ApplicationLanguage::AmericanEnglish,
    ApplicationLanguage::French,
    ApplicationLanguage::Spanish,
    ApplicationLanguage::Italian,
    ApplicationLanguage::Dutch,
    ApplicationLanguage::Portuguese,
    ApplicationLanguage::Russian,
    ApplicationLanguage::LatinAmericanSpanish,
    ApplicationLanguage::CanadianFrench,
    ApplicationLanguage::Japanese,
    ApplicationLanguage::SimplifiedChinese,
    ApplicationLanguage::TraditionalChinese,
    ApplicationLanguage::Korean,
}};

constexpr ApplicationLanguagePriorityList priority_list_latin_american_spanish = {{
    ApplicationLanguage::LatinAmericanSpanish,
    ApplicationLanguage::Spanish,
    ApplicationLanguage::AmericanEnglish,
    ApplicationLanguage::BritishEnglish,
    ApplicationLanguage::Portuguese,
    ApplicationLanguage::CanadianFrench,
    ApplicationLanguage::French,
    ApplicationLanguage::Italian,
    ApplicationLanguage::German,
    ApplicationLanguage::Dutch,
    ApplicationLanguage::Russian,
    ApplicationLanguage::Japanese,
    ApplicationLanguage::SimplifiedChinese,
    ApplicationLanguage::TraditionalChinese,
    ApplicationLanguage::Korean,
}};

constexpr ApplicationLanguagePriorityList priority_list_spanish = {{
    ApplicationLanguage::Spanish,
    ApplicationLanguage::LatinAmericanSpanish,
    ApplicationLanguage::BritishEnglish,
    ApplicationLanguage::AmericanEnglish,
    ApplicationLanguage::French,
    ApplicationLanguage::German,
    ApplicationLanguage::Italian,
    ApplicationLanguage::Dutch,
    ApplicationLanguage::Portuguese,
    ApplicationLanguage::Russian,
    ApplicationLanguage::CanadianFrench,
    ApplicationLanguage::Japanese,
    ApplicationLanguage::SimplifiedChinese,
    ApplicationLanguage::TraditionalChinese,
    ApplicationLanguage::Korean,
}};

constexpr ApplicationLanguagePriorityList priority_list_italian = {{
    ApplicationLanguage::Italian,
    ApplicationLanguage::BritishEnglish,
    ApplicationLanguage::AmericanEnglish,
    ApplicationLanguage::French,
    ApplicationLanguage::German,
    ApplicationLanguage::Spanish,
    ApplicationLanguage::Dutch,
    ApplicationLanguage::Portuguese,
    ApplicationLanguage::Russian,
    ApplicationLanguage::LatinAmericanSpanish,
    ApplicationLanguage::CanadianFrench,
    ApplicationLanguage::Japanese,
    ApplicationLanguage::SimplifiedChinese,
    ApplicationLanguage::TraditionalChinese,
    ApplicationLanguage::Korean,
}};

constexpr ApplicationLanguagePriorityList priority_list_dutch = {{
    ApplicationLanguage::Dutch,
    ApplicationLanguage::BritishEnglish,
    ApplicationLanguage::AmericanEnglish,
    ApplicationLanguage::French,
    ApplicationLanguage::German,
    ApplicationLanguage::Spanish,
    ApplicationLanguage::Italian,
    ApplicationLanguage::Portuguese,
    ApplicationLanguage::Russian,
    ApplicationLanguage::LatinAmericanSpanish,
    ApplicationLanguage::CanadianFrench,
    ApplicationLanguage::Japanese,
    ApplicationLanguage::SimplifiedChinese,
    ApplicationLanguage::TraditionalChinese,
    ApplicationLanguage::Korean,
}};

constexpr ApplicationLanguagePriorityList priority_list_canadian_french = {{
    ApplicationLanguage::CanadianFrench,
    ApplicationLanguage::French,
    ApplicationLanguage::AmericanEnglish,
    ApplicationLanguage::BritishEnglish,
    ApplicationLanguage::LatinAmericanSpanish,
    ApplicationLanguage::Spanish,
    ApplicationLanguage::German,
    ApplicationLanguage::Italian,
    ApplicationLanguage::Dutch,
    ApplicationLanguage::Portuguese,
    ApplicationLanguage::Russian,
    ApplicationLanguage::Japanese,
    ApplicationLanguage::SimplifiedChinese,
    ApplicationLanguage::TraditionalChinese,
    ApplicationLanguage::Korean,
}};

constexpr ApplicationLanguagePriorityList priority_list_portuguese = {{
    ApplicationLanguage::Portuguese,
    ApplicationLanguage::BritishEnglish,
    ApplicationLanguage::AmericanEnglish,
    ApplicationLanguage::French,
    ApplicationLanguage::German,
    ApplicationLanguage::Spanish,
    ApplicationLanguage::Italian,
    ApplicationLanguage::Dutch,
    ApplicationLanguage::Russian,
    ApplicationLanguage::LatinAmericanSpanish,
    ApplicationLanguage::CanadianFrench,
    ApplicationLanguage::Japanese,
    ApplicationLanguage::SimplifiedChinese,
    ApplicationLanguage::TraditionalChinese,
    ApplicationLanguage::Korean,
}};

constexpr ApplicationLanguagePriorityList priority_list_russian = {{
    ApplicationLanguage::Russian,
    ApplicationLanguage::BritishEnglish,
    ApplicationLanguage::AmericanEnglish,
    ApplicationLanguage::French,
    ApplicationLanguage::German,
    ApplicationLanguage::Spanish,
    ApplicationLanguage::Italian,
    ApplicationLanguage::Dutch,
    ApplicationLanguage::Portuguese,
    ApplicationLanguage::LatinAmericanSpanish,
    ApplicationLanguage::CanadianFrench,
    ApplicationLanguage::Japanese,
    ApplicationLanguage::SimplifiedChinese,
    ApplicationLanguage::TraditionalChinese,
    ApplicationLanguage::Korean,
}};

constexpr ApplicationLanguagePriorityList priority_list_korean = {{
    ApplicationLanguage::Korean,
    ApplicationLanguage::AmericanEnglish,
    ApplicationLanguage::BritishEnglish,
    ApplicationLanguage::LatinAmericanSpanish,
    ApplicationLanguage::CanadianFrench,
    ApplicationLanguage::French,
    ApplicationLanguage::German,
    ApplicationLanguage::Spanish,
    ApplicationLanguage::Italian,
    ApplicationLanguage::Dutch,
    ApplicationLanguage::Portuguese,
    ApplicationLanguage::Russian,
    ApplicationLanguage::Japanese,
    ApplicationLanguage::SimplifiedChinese,
    ApplicationLanguage::TraditionalChinese,
}};

constexpr ApplicationLanguagePriorityList priority_list_traditional_chinese = {{
    ApplicationLanguage::TraditionalChinese,
    ApplicationLanguage::SimplifiedChinese,
    ApplicationLanguage::AmericanEnglish,
    ApplicationLanguage::BritishEnglish,
    ApplicationLanguage::Japanese,
    ApplicationLanguage::LatinAmericanSpanish,
    ApplicationLanguage::CanadianFrench,
    ApplicationLanguage::French,
    ApplicationLanguage::German,
    ApplicationLanguage::Spanish,
    ApplicationLanguage::Italian,
    ApplicationLanguage::Dutch,
    ApplicationLanguage::Portuguese,
    ApplicationLanguage::Russian,
    ApplicationLanguage::Korean,
}};

constexpr ApplicationLanguagePriorityList priority_list_simplified_chinese = {{
    ApplicationLanguage::SimplifiedChinese,
    ApplicationLanguage::TraditionalChinese,
    ApplicationLanguage::AmericanEnglish,
    ApplicationLanguage::BritishEnglish,
    ApplicationLanguage::Japanese,
    ApplicationLanguage::LatinAmericanSpanish,
    ApplicationLanguage::CanadianFrench,
    ApplicationLanguage::French,
    ApplicationLanguage::German,
    ApplicationLanguage::Spanish,
    ApplicationLanguage::Italian,
    ApplicationLanguage::Dutch,
    ApplicationLanguage::Portuguese,
    ApplicationLanguage::Russian,
    ApplicationLanguage::Korean,
}};

constexpr ApplicationLanguagePriorityList priority_list_brazilian_portuguese = {{
    ApplicationLanguage::BrazilianPortuguese,
    ApplicationLanguage::Portuguese,
    ApplicationLanguage::LatinAmericanSpanish,
    ApplicationLanguage::AmericanEnglish,
    ApplicationLanguage::BritishEnglish,
    ApplicationLanguage::Japanese,
    ApplicationLanguage::French,
    ApplicationLanguage::German,
    ApplicationLanguage::Spanish,
    ApplicationLanguage::Italian,
    ApplicationLanguage::Dutch,
    ApplicationLanguage::CanadianFrench,
    ApplicationLanguage::Russian,
    ApplicationLanguage::Korean,
    ApplicationLanguage::SimplifiedChinese,
    ApplicationLanguage::TraditionalChinese,
}};

const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(
    const ApplicationLanguage lang) {
    switch (lang) {
    case ApplicationLanguage::AmericanEnglish:
        return &priority_list_american_english;
    case ApplicationLanguage::BritishEnglish:
        return &priority_list_british_english;
    case ApplicationLanguage::Japanese:
        return &priority_list_japanese;
    case ApplicationLanguage::French:
        return &priority_list_french;
    case ApplicationLanguage::German:
        return &priority_list_german;
    case ApplicationLanguage::LatinAmericanSpanish:
        return &priority_list_latin_american_spanish;
    case ApplicationLanguage::Spanish:
        return &priority_list_spanish;
    case ApplicationLanguage::Italian:
        return &priority_list_italian;
    case ApplicationLanguage::Dutch:
        return &priority_list_dutch;
    case ApplicationLanguage::CanadianFrench:
        return &priority_list_canadian_french;
    case ApplicationLanguage::Portuguese:
        return &priority_list_portuguese;
    case ApplicationLanguage::Russian:
        return &priority_list_russian;
    case ApplicationLanguage::Korean:
        return &priority_list_korean;
    case ApplicationLanguage::TraditionalChinese:
        return &priority_list_traditional_chinese;
    case ApplicationLanguage::SimplifiedChinese:
        return &priority_list_simplified_chinese;
    case ApplicationLanguage::BrazilianPortuguese:
        return &priority_list_brazilian_portuguese;
    default:
        return nullptr;
    }
}

std::optional<ApplicationLanguage> ConvertToApplicationLanguage(
    const Set::LanguageCode language_code) {
    switch (language_code) {
    case Set::LanguageCode::EN_US:
        return ApplicationLanguage::AmericanEnglish;
    case Set::LanguageCode::EN_GB:
        return ApplicationLanguage::BritishEnglish;
    case Set::LanguageCode::JA:
        return ApplicationLanguage::Japanese;
    case Set::LanguageCode::FR:
        return ApplicationLanguage::French;
    case Set::LanguageCode::DE:
        return ApplicationLanguage::German;
    case Set::LanguageCode::ES_419:
        return ApplicationLanguage::LatinAmericanSpanish;
    case Set::LanguageCode::ES:
        return ApplicationLanguage::Spanish;
    case Set::LanguageCode::IT:
        return ApplicationLanguage::Italian;
    case Set::LanguageCode::NL:
        return ApplicationLanguage::Dutch;
    case Set::LanguageCode::FR_CA:
        return ApplicationLanguage::CanadianFrench;
    case Set::LanguageCode::PT:
        return ApplicationLanguage::Portuguese;
    case Set::LanguageCode::RU:
        return ApplicationLanguage::Russian;
    case Set::LanguageCode::KO:
        return ApplicationLanguage::Korean;
    case Set::LanguageCode::ZH_TW:
    case Set::LanguageCode::ZH_HANT:
        return ApplicationLanguage::TraditionalChinese;
    case Set::LanguageCode::ZH_CN:
    case Set::LanguageCode::ZH_HANS:
        return ApplicationLanguage::SimplifiedChinese;
    case Set::LanguageCode::PT_BR:
        return ApplicationLanguage::BrazilianPortuguese;
    default:
        return std::nullopt;
    }
}

std::optional<Set::LanguageCode> ConvertToLanguageCode(const ApplicationLanguage lang) {
    switch (lang) {
    case ApplicationLanguage::AmericanEnglish:
        return Set::LanguageCode::EN_US;
    case ApplicationLanguage::BritishEnglish:
        return Set::LanguageCode::EN_GB;
    case ApplicationLanguage::Japanese:
        return Set::LanguageCode::JA;
    case ApplicationLanguage::French:
        return Set::LanguageCode::FR;
    case ApplicationLanguage::German:
        return Set::LanguageCode::DE;
    case ApplicationLanguage::LatinAmericanSpanish:
        return Set::LanguageCode::ES_419;
    case ApplicationLanguage::Spanish:
        return Set::LanguageCode::ES;
    case ApplicationLanguage::Italian:
        return Set::LanguageCode::IT;
    case ApplicationLanguage::Dutch:
        return Set::LanguageCode::NL;
    case ApplicationLanguage::CanadianFrench:
        return Set::LanguageCode::FR_CA;
    case ApplicationLanguage::Portuguese:
        return Set::LanguageCode::PT;
    case ApplicationLanguage::Russian:
        return Set::LanguageCode::RU;
    case ApplicationLanguage::Korean:
        return Set::LanguageCode::KO;
    case ApplicationLanguage::TraditionalChinese:
        return Set::LanguageCode::ZH_HANT;
    case ApplicationLanguage::SimplifiedChinese:
        return Set::LanguageCode::ZH_HANS;
    case ApplicationLanguage::BrazilianPortuguese:
        return Set::LanguageCode::PT_BR;
    default:
        return std::nullopt;
    }
}
} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <optional>
#include "common/common_types.h"
#include "core/hle/service/set/system_settings_server.h"

namespace Service::NS {
/// This is nn::ns::detail::ApplicationLanguage
enum class ApplicationLanguage : u8 {
    AmericanEnglish = 0,
    BritishEnglish,
    Japanese,
    French,
    German,
    LatinAmericanSpanish,
    Spanish,
    Italian,
    Dutch,
    CanadianFrench,
    Portuguese,
    Russian,
    Korean,
    TraditionalChinese,
    SimplifiedChinese,
    BrazilianPortuguese,
    Count
};
using ApplicationLanguagePriorityList =
    const std::array<ApplicationLanguage, static_cast<std::size_t>(ApplicationLanguage::Count)>;

constexpr u32 GetSupportedLanguageFlag(const ApplicationLanguage lang) {
    return 1U << static_cast<u32>(lang);
}

const ApplicationLanguagePriorityList* GetApplicationLanguagePriorityList(ApplicationLanguage lang);
std::optional<ApplicationLanguage> ConvertToApplicationLanguage(Set::LanguageCode language_code);
std::optional<Set::LanguageCode> ConvertToLanguageCode(ApplicationLanguage lang);
} // namespace Service::NS

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/ns/develop_interface.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/ns/platform_service_manager.h"
#include "core/hle/service/ns/query_service.h"
#include "core/hle/service/ns/service_getter_interface.h"
#include "core/hle/service/ns/system_update_interface.h"
#include "core/hle/service/ns/vulnerability_manager_interface.h"
#include "core/hle/service/server_manager.h"

namespace Service::NS {

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);

    server_manager->RegisterNamedService(
        "ns:am2", std::make_shared<IServiceGetterInterface>(system, "ns:am2"));
    server_manager->RegisterNamedService(
        "ns:ec", std::make_shared<IServiceGetterInterface>(system, "ns:ec"));
    server_manager->RegisterNamedService(
        "ns:rid", std::make_shared<IServiceGetterInterface>(system, "ns:rid"));
    server_manager->RegisterNamedService(
        "ns:rt", std::make_shared<IServiceGetterInterface>(system, "ns:rt"));
    server_manager->RegisterNamedService(
        "ns:web", std::make_shared<IServiceGetterInterface>(system, "ns:web"));
    server_manager->RegisterNamedService(
        "ns:ro", std::make_shared<IServiceGetterInterface>(system, "ns:ro"));

    server_manager->RegisterNamedService("ns:dev", std::make_shared<IDevelopInterface>(system));
    server_manager->RegisterNamedService("ns:su", std::make_shared<ISystemUpdateInterface>(system));
    server_manager->RegisterNamedService("ns:vm",
                                         std::make_shared<IVulnerabilityManagerInterface>(system));
    server_manager->RegisterNamedService("pdm:qry", std::make_shared<IQueryService>(system));

    server_manager->RegisterNamedService("pl:s",
                                         std::make_shared<IPlatformServiceManager>(system, "pl:s"));
    server_manager->RegisterNamedService("pl:u",
                                         std::make_shared<IPlatformServiceManager>(system, "pl:u"));
    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

namespace Core {
class System;
}

namespace Service::NS {

void LoopProcess(Core::System& system);

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/result.h"

namespace Service::NS {

constexpr Result ResultApplicationLanguageNotFound{ErrorModule::NS, 300};

}


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/common_funcs.h"
#include "common/uuid.h"
#include "core/file_sys/romfs_factory.h"

namespace Service::NS {

enum class ApplicationRecordType : u8 {
    Installing = 2,
    Installed = 3,
    GameCardNotInserted = 5,
    Archived = 11,
    GameCard = 16,
};

enum class ApplicationControlSource : u8 {
    CacheOnly = 0,
    Storage = 1,
    StorageOnly = 2,
};

enum class BackgroundNetworkUpdateState : u8 {
    None,
    InProgress,
    Ready,
};

struct ApplicationRecord {
    u64 application_id;
    ApplicationRecordType type;
    u8 unknown;
    INSERT_PADDING_BYTES_NOINIT(0x6);
    u8 unknown2;
    INSERT_PADDING_BYTES_NOINIT(0x7);
};
static_assert(sizeof(ApplicationRecord) == 0x18, "ApplicationRecord has incorrect size.");

/// ApplicationView
struct ApplicationView {
    u64 application_id;           ///< ApplicationId.
    u32 unk;                      ///< Unknown.
    u32 flags;                    ///< Flags.
    std::array<u8, 0x10> unk_x10; ///< Unknown.
    u32 unk_x20;                  ///< Unknown.
    u16 unk_x24;                  ///< Unknown.
    std::array<u8, 0x2> unk_x26;  ///< Unknown.
    std::array<u8, 0x8> unk_x28;  ///< Unknown.
    std::array<u8, 0x10> unk_x30; ///< Unknown.
    u32 unk_x40;                  ///< Unknown.
    u8 unk_x44;                   ///< Unknown.
    std::array<u8, 0xb> unk_x45;  ///< Unknown.
};
static_assert(sizeof(ApplicationView) == 0x50, "ApplicationView has incorrect size.");

struct ApplicationRightsOnClient {
    u64 application_id;
    Common::UUID uid;
    u8 flags;
    u8 flags2;
    INSERT_PADDING_BYTES_NOINIT(0x6);
};
static_assert(sizeof(ApplicationRightsOnClient) == 0x20,
              "ApplicationRightsOnClient has incorrect size.");

/// NsPromotionInfo
struct PromotionInfo {
    u64 start_timestamp; ///< POSIX timestamp for the promotion start.
    u64 end_timestamp;   ///< POSIX timestamp for the promotion end.
    s64 remaining_time;  ///< Remaining time until the promotion ends, in nanoseconds
                         ///< ({end_timestamp - current_time} converted to nanoseconds).
    INSERT_PADDING_BYTES_NOINIT(0x4);
    u8 flags; ///< Flags. Bit0: whether the PromotionInfo is valid (including bit1). Bit1 clear:
              ///< remaining_time is set.
    INSERT_PADDING_BYTES_NOINIT(0x3);
};
static_assert(sizeof(PromotionInfo) == 0x20, "PromotionInfo has incorrect size.");

/// NsApplicationViewWithPromotionInfo
struct ApplicationViewWithPromotionInfo {
    ApplicationView view;    ///< \ref NsApplicationView
    PromotionInfo promotion; ///< \ref NsPromotionInfo
};
static_assert(sizeof(ApplicationViewWithPromotionInfo) == 0x70,
              "ApplicationViewWithPromotionInfo has incorrect size.");

struct ApplicationOccupiedSizeEntity {
    FileSys::StorageId storage_id;
    u64 app_size;
    u64 patch_size;
    u64 aoc_size;
};
static_assert(sizeof(ApplicationOccupiedSizeEntity) == 0x20,
              "ApplicationOccupiedSizeEntity has incorrect size.");

struct ApplicationOccupiedSize {
    std::array<ApplicationOccupiedSizeEntity, 4> entities;
};
static_assert(sizeof(ApplicationOccupiedSize) == 0x80,
              "ApplicationOccupiedSize has incorrect size.");

struct ContentPath {
    u8 file_system_proxy_type;
    u64 program_id;
};
static_assert(sizeof(ContentPath) == 0x10, "ContentPath has incorrect size.");

struct Uid {
    alignas(8) Common::UUID uuid;
};
static_assert(sizeof(Uid) == 0x10, "Uid has incorrect size.");

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <algorithm>
#include <cstring>
#include <vector>

#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/system_archive/system_archive.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_memory.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ns/platform_service_manager.h"

namespace Service::NS {

struct FontRegion {
    u32 offset;
    u32 size;
};

// The below data is specific to shared font data dumped from Switch on f/w 2.2
// Virtual address and offsets/sizes likely will vary by dump
[[maybe_unused]] constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
constexpr u32 EXPECTED_RESULT{0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be
constexpr u32 EXPECTED_MAGIC{0x36f81a1e};  // What we expect the encrypted bfttf first 4 bytes to be
constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000};
constexpr FontRegion EMPTY_REGION{0, 0};

static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMemory& output,
                              std::size_t& offset) {
    ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE,
               "Shared fonts exceeds 17mb!");
    ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number");

    const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor
    std::vector<u32> transformed_font(input.size());
    // TODO(ogniK): Figure out a better way to do this
    std::transform(input.begin(), input.end(), transformed_font.begin(),
                   [&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); });
    transformed_font[1] = Common::swap32(transformed_font[1]) ^ KEY; // "re-encrypt" the size
    std::memcpy(output.data() + offset, transformed_font.data(),
                transformed_font.size() * sizeof(u32));
    offset += transformed_font.size() * sizeof(u32);
}

void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output) {
    ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number");

    if (input.size() < 2) {
        LOG_ERROR(Service_NS, "Input font is empty");
        return;
    }

    const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor
    std::vector<u32> transformed_font(input.size());
    // TODO(ogniK): Figure out a better way to do this
    std::transform(input.begin(), input.end(), transformed_font.begin(),
                   [&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); });
    std::memcpy(output.data(), transformed_font.data() + 2,
                (transformed_font.size() - 2) * sizeof(u32));
}

void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output,
                       std::size_t& offset) {
    ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE,
               "Shared fonts exceeds 17mb!");

    const auto key = Common::swap32(EXPECTED_RESULT ^ EXPECTED_MAGIC);
    std::vector<u32> transformed_font(input.size() + 2);
    transformed_font[0] = Common::swap32(EXPECTED_MAGIC);
    transformed_font[1] = Common::swap32(static_cast<u32>(input.size() * sizeof(u32))) ^ key;
    std::transform(input.begin(), input.end(), transformed_font.begin() + 2,
                   [key](u32 in) { return in ^ key; });
    std::memcpy(output.data() + offset, transformed_font.data(),
                transformed_font.size() * sizeof(u32));
    offset += transformed_font.size() * sizeof(u32);
}

// Helper function to make BuildSharedFontsRawRegions a bit nicer
static u32 GetU32Swapped(const u8* data) {
    u32 value;
    std::memcpy(&value, data, sizeof(value));
    return Common::swap32(value);
}

struct IPlatformServiceManager::Impl {
    const FontRegion& GetSharedFontRegion(std::size_t index) const {
        if (index >= shared_font_regions.size() || shared_font_regions.empty()) {
            // No font fallback
            return EMPTY_REGION;
        }
        return shared_font_regions.at(index);
    }

    void BuildSharedFontsRawRegions(const Kernel::PhysicalMemory& input) {
        // As we can derive the xor key we can just populate the offsets
        // based on the shared memory dump
        unsigned cur_offset = 0;

        for (std::size_t i = 0; i < SHARED_FONTS.size(); i++) {
            // Out of shared fonts/invalid font
            if (GetU32Swapped(input.data() + cur_offset) != EXPECTED_RESULT) {
                break;
            }

            // Derive key within inverse xor
            const u32 KEY = GetU32Swapped(input.data() + cur_offset) ^ EXPECTED_MAGIC;
            const u32 SIZE = GetU32Swapped(input.data() + cur_offset + 4) ^ KEY;
            shared_font_regions.push_back(FontRegion{cur_offset + 8, SIZE});
            cur_offset += SIZE + 8;
        }
    }

    /// Backing memory for the shared font data
    std::shared_ptr<Kernel::PhysicalMemory> shared_font;

    // Automatically populated based on shared_fonts dump or system archives.
    std::vector<FontRegion> shared_font_regions;
};

IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const char* service_name_)
    : ServiceFramework{system_, service_name_}, impl{std::make_unique<Impl>()} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, D<&IPlatformServiceManager::RequestLoad>, "RequestLoad"},
        {1, D<&IPlatformServiceManager::GetLoadState>, "GetLoadState"},
        {2, D<&IPlatformServiceManager::GetSize>, "GetSize"},
        {3, D<&IPlatformServiceManager::GetSharedMemoryAddressOffset>, "GetSharedMemoryAddressOffset"},
        {4, D<&IPlatformServiceManager::GetSharedMemoryNativeHandle>, "GetSharedMemoryNativeHandle"},
        {5, D<&IPlatformServiceManager::GetSharedFontInOrderOfPriority>, "GetSharedFontInOrderOfPriority"},
        {6, D<&IPlatformServiceManager::GetSharedFontInOrderOfPriority>, "GetSharedFontInOrderOfPriorityForSystem"},
        {100, nullptr, "RequestApplicationFunctionAuthorization"},
        {101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"},
        {102, nullptr, "RequestApplicationFunctionAuthorizationByApplicationId"},
        {103, nullptr, "RefreshApplicationFunctionBlackListDebugRecord"},
        {104, nullptr, "RequestApplicationFunctionAuthorizationByProgramId"},
        {105, nullptr, "GetFunctionBlackListSystemVersionToAuthorize"},
        {106, nullptr, "GetFunctionBlackListVersion"},
        {1000, nullptr, "LoadNgWordDataForPlatformRegionChina"},
        {1001, nullptr, "GetNgWordDataSizeForPlatformRegionChina"},
    };
    // clang-format on
    RegisterHandlers(functions);

    auto& fsc = system.GetFileSystemController();

    // Attempt to load shared font data from disk
    const auto* nand = fsc.GetSystemNANDContents();
    std::size_t offset = 0;
    // Rebuild shared fonts from data ncas or synthesize

    impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(SHARED_FONT_MEM_SIZE);
    for (auto font : SHARED_FONTS) {
        FileSys::VirtualFile romfs;
        const auto nca =
            nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data);
        if (nca) {
            romfs = nca->GetRomFS();
        }

        if (!romfs) {
            romfs = FileSys::SystemArchive::SynthesizeSystemArchive(static_cast<u64>(font.first));
        }

        if (!romfs) {
            LOG_ERROR(Service_NS, "Failed to find or synthesize {:016X}! Skipping", font.first);
            continue;
        }

        const auto extracted_romfs = FileSys::ExtractRomFS(romfs);
        if (!extracted_romfs) {
            LOG_ERROR(Service_NS, "Failed to extract RomFS for {:016X}! Skipping", font.first);
            continue;
        }
        const auto font_fp = extracted_romfs->GetFile(font.second);
        if (!font_fp) {
            LOG_ERROR(Service_NS, "{:016X} has no file \"{}\"! Skipping", font.first, font.second);
            continue;
        }
        std::vector<u32> font_data_u32(font_fp->GetSize() / sizeof(u32));
        font_fp->ReadBytes<u32>(font_data_u32.data(), font_fp->GetSize());
        // We need to be BigEndian as u32s for the xor encryption
        std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(),
                       Common::swap32);
        // Font offset and size do not account for the header
        const FontRegion region{static_cast<u32>(offset + 8),
                                static_cast<u32>((font_data_u32.size() * sizeof(u32)) - 8)};
        DecryptSharedFont(font_data_u32, *impl->shared_font, offset);
        impl->shared_font_regions.push_back(region);
    }
}

IPlatformServiceManager::~IPlatformServiceManager() = default;

Result IPlatformServiceManager::RequestLoad(SharedFontType type) {
    // Games don't call this so all fonts should be loaded
    LOG_DEBUG(Service_NS, "called, shared_font_type={}", type);
    R_SUCCEED();
}

Result IPlatformServiceManager::GetLoadState(Out<LoadState> out_load_state, SharedFontType type) {
    LOG_DEBUG(Service_NS, "called, shared_font_type={}", type);
    *out_load_state = LoadState::Loaded;
    R_SUCCEED();
}

Result IPlatformServiceManager::GetSize(Out<u32> out_size, SharedFontType type) {
    LOG_DEBUG(Service_NS, "called, shared_font_type={}", type);
    *out_size = impl->GetSharedFontRegion(static_cast<size_t>(type)).size;
    R_SUCCEED();
}

Result IPlatformServiceManager::GetSharedMemoryAddressOffset(Out<u32> out_shared_memory_offset,
                                                             SharedFontType type) {
    LOG_DEBUG(Service_NS, "called, shared_font_type={}", type);
    *out_shared_memory_offset = impl->GetSharedFontRegion(static_cast<size_t>(type)).offset;
    R_SUCCEED();
}

Result IPlatformServiceManager::GetSharedMemoryNativeHandle(
    OutCopyHandle<Kernel::KSharedMemory> out_shared_memory_native_handle) {
    // Map backing memory for the font data
    LOG_DEBUG(Service_NS, "called");

    // Create shared font memory object
    std::memcpy(kernel.GetFontSharedMem().GetPointer(), impl->shared_font->data(),
                impl->shared_font->size());

    // FIXME: this shouldn't belong to the kernel
    *out_shared_memory_native_handle = &kernel.GetFontSharedMem();
    R_SUCCEED();
}

Result IPlatformServiceManager::GetSharedFontInOrderOfPriority(
    OutArray<u32, BufferAttr_HipcMapAlias> out_font_codes,
    OutArray<u32, BufferAttr_HipcMapAlias> out_font_offsets,
    OutArray<u32, BufferAttr_HipcMapAlias> out_font_sizes, Out<bool> out_fonts_are_loaded,
    Out<u32> out_font_count, Set::LanguageCode language_code) {
    LOG_DEBUG(Service_NS, "called, language_code={:#x}", language_code);

    // The maximum number of elements that can be returned is 6. Regardless of the available fonts
    // or buffer size.
    constexpr size_t MaxElementCount = 6;

    // TODO(ogniK): Have actual priority order
    const auto max_size = std::min({MaxElementCount, out_font_codes.size(), out_font_offsets.size(),
                                    out_font_sizes.size(), impl->shared_font_regions.size()});

    for (size_t i = 0; i < max_size; i++) {
        auto region = impl->GetSharedFontRegion(i);

        out_font_codes[i] = static_cast<u32>(i);
        out_font_offsets[i] = region.offset;
        out_font_sizes[i] = region.size;
    }

    *out_fonts_are_loaded = true;
    *out_font_count = static_cast<u32>(max_size);
    R_SUCCEED();
}

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <memory>
#include <vector>
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/set/settings_types.h"

namespace Service {

namespace FileSystem {
class FileSystemController;
} // namespace FileSystem

namespace NS {

enum class FontArchives : u64 {
    Extension = 0x0100000000000810,
    Standard = 0x0100000000000811,
    Korean = 0x0100000000000812,
    ChineseTraditional = 0x0100000000000813,
    ChineseSimple = 0x0100000000000814,
};

enum class SharedFontType : u32 {
    JapanUSEuropeStandard = 0,
    ChineseSimplified = 1,
    ExtendedChineseSimplified = 2,
    ChineseTraditional = 3,
    KoreanHangul = 4,
    NintendoExtended = 5,
};

enum class LoadState : u32 {
    Loading = 0,
    Loaded = 1,
};

constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{
    std::make_pair(FontArchives::Standard, "nintendo_udsg-r_std_003.bfttf"),
    std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_org_zh-cn_003.bfttf"),
    std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_ext_zh-cn_003.bfttf"),
    std::make_pair(FontArchives::ChineseTraditional, "nintendo_udjxh-db_zh-tw_003.bfttf"),
    std::make_pair(FontArchives::Korean, "nintendo_udsg-r_ko_003.bfttf"),
    std::make_pair(FontArchives::Extension, "nintendo_ext_003.bfttf"),
    std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf"),
};

void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output);
void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, std::size_t& offset);

class IPlatformServiceManager final : public ServiceFramework<IPlatformServiceManager> {
public:
    explicit IPlatformServiceManager(Core::System& system_, const char* service_name_);
    ~IPlatformServiceManager() override;

private:
    Result RequestLoad(SharedFontType type);
    Result GetLoadState(Out<LoadState> out_load_state, SharedFontType type);
    Result GetSize(Out<u32> out_size, SharedFontType type);
    Result GetSharedMemoryAddressOffset(Out<u32> out_shared_memory_offset, SharedFontType type);
    Result GetSharedMemoryNativeHandle(
        OutCopyHandle<Kernel::KSharedMemory> out_shared_memory_native_handle);
    Result GetSharedFontInOrderOfPriority(OutArray<u32, BufferAttr_HipcMapAlias> out_font_codes,
                                          OutArray<u32, BufferAttr_HipcMapAlias> out_font_offsets,
                                          OutArray<u32, BufferAttr_HipcMapAlias> out_font_sizes,
                                          Out<bool> out_fonts_are_loaded, Out<u32> out_font_count,
                                          Set::LanguageCode language_code);

    struct Impl;
    std::unique_ptr<Impl> impl;
};

} // namespace NS

} // namespace Service


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/logging/log.h"
#include "common/uuid.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ns/query_service.h"
#include "core/hle/service/service.h"

namespace Service::NS {

IQueryService::IQueryService(Core::System& system_) : ServiceFramework{system_, "pdm:qry"} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, nullptr, "QueryAppletEvent"},
        {1, nullptr, "QueryPlayStatistics"},
        {2, nullptr, "QueryPlayStatisticsByUserAccountId"},
        {3, nullptr, "QueryPlayStatisticsByNetworkServiceAccountId"},
        {4, nullptr, "QueryPlayStatisticsByApplicationId"},
        {5, D<&IQueryService::QueryPlayStatisticsByApplicationIdAndUserAccountId>, "QueryPlayStatisticsByApplicationIdAndUserAccountId"},
        {6, nullptr, "QueryPlayStatisticsByApplicationIdAndNetworkServiceAccountId"},
        {7, nullptr, "QueryLastPlayTimeV0"},
        {8, nullptr, "QueryPlayEvent"},
        {9, nullptr, "GetAvailablePlayEventRange"},
        {10, nullptr, "QueryAccountEvent"},
        {11, nullptr, "QueryAccountPlayEvent"},
        {12, nullptr, "GetAvailableAccountPlayEventRange"},
        {13, nullptr, "QueryApplicationPlayStatisticsForSystemV0"},
        {14, nullptr, "QueryRecentlyPlayedApplication"},
        {15, nullptr, "GetRecentlyPlayedApplicationUpdateEvent"},
        {16, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystemV0"},
        {17, nullptr, "QueryLastPlayTime"},
        {18, nullptr, "QueryApplicationPlayStatisticsForSystem"},
        {19, nullptr, "QueryApplicationPlayStatisticsByUserAccountIdForSystem"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

IQueryService::~IQueryService() = default;

Result IQueryService::QueryPlayStatisticsByApplicationIdAndUserAccountId(
    Out<PlayStatistics> out_play_statistics, bool unknown, u64 application_id, Uid account_id) {
    // TODO(German77): Read statistics of the game
    *out_play_statistics = {
        .application_id = application_id,
        .total_launches = 1,
    };

    LOG_WARNING(Service_NS, "(STUBBED) called. unknown={}. application_id={:016X}, account_id={}",
                unknown, application_id, account_id.uuid.FormattedString());
    R_SUCCEED();
}

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/uuid.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/ns/ns_types.h"
#include "core/hle/service/service.h"

namespace Service::NS {

struct PlayStatistics {
    u64 application_id{};
    u32 first_entry_index{};
    u32 first_timestamp_user{};
    u32 first_timestamp_network{};
    u32 last_entry_index{};
    u32 last_timestamp_user{};
    u32 last_timestamp_network{};
    u32 play_time_in_minutes{};
    u32 total_launches{};
};
static_assert(sizeof(PlayStatistics) == 0x28, "PlayStatistics is an invalid size");

class IQueryService final : public ServiceFramework<IQueryService> {
public:
    explicit IQueryService(Core::System& system_);
    ~IQueryService() override;

private:
    Result QueryPlayStatisticsByApplicationIdAndUserAccountId(
        Out<PlayStatistics> out_play_statistics, bool unknown, u64 application_id, Uid account_id);
};

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/settings.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ns/language.h"
#include "core/hle/service/ns/ns_results.h"
#include "core/hle/service/ns/read_only_application_control_data_interface.h"
#include "core/hle/service/set/settings_server.h"

namespace Service::NS {

IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface(
    Core::System& system_)
    : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, D<&IReadOnlyApplicationControlDataInterface::GetApplicationControlData>, "GetApplicationControlData"},
        {1, D<&IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage>, "GetApplicationDesiredLanguage"},
        {2, D<&IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode>, "ConvertApplicationLanguageToLanguageCode"},
        {3, nullptr, "ConvertLanguageCodeToApplicationLanguage"},
        {4, nullptr, "SelectApplicationDesiredLanguage"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

IReadOnlyApplicationControlDataInterface::~IReadOnlyApplicationControlDataInterface() = default;

Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData(
    OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_actual_size,
    ApplicationControlSource application_control_source, u64 application_id) {
    LOG_INFO(Service_NS, "called with control_source={}, application_id={:016X}",
             application_control_source, application_id);

    const FileSys::PatchManager pm{application_id, system.GetFileSystemController(),
                                   system.GetContentProvider()};
    const auto control = pm.GetControlMetadata();
    const auto size = out_buffer.size();

    const auto icon_size = control.second ? control.second->GetSize() : 0;
    const auto total_size = sizeof(FileSys::RawNACP) + icon_size;

    if (size < total_size) {
        LOG_ERROR(Service_NS, "output buffer is too small! (actual={:016X}, expected_min=0x4000)",
                  size);
        R_THROW(ResultUnknown);
    }

    if (control.first != nullptr) {
        const auto bytes = control.first->GetRawBytes();
        std::memcpy(out_buffer.data(), bytes.data(), bytes.size());
    } else {
        LOG_WARNING(Service_NS, "missing NACP data for application_id={:016X}, defaulting to zero",
                    application_id);
        std::memset(out_buffer.data(), 0, sizeof(FileSys::RawNACP));
    }

    if (control.second != nullptr) {
        control.second->Read(out_buffer.data() + sizeof(FileSys::RawNACP), icon_size);
    } else {
        LOG_WARNING(Service_NS, "missing icon data for application_id={:016X}", application_id);
    }

    *out_actual_size = static_cast<u32>(total_size);
    R_SUCCEED();
}

Result IReadOnlyApplicationControlDataInterface::GetApplicationDesiredLanguage(
    Out<ApplicationLanguage> out_desired_language, u32 supported_languages) {
    LOG_INFO(Service_NS, "called with supported_languages={:08X}", supported_languages);

    // Get language code from settings
    const auto language_code =
        Set::GetLanguageCodeFromIndex(static_cast<s32>(Settings::values.language_index.GetValue()));

    // Convert to application language, get priority list
    const auto application_language = ConvertToApplicationLanguage(language_code);
    if (application_language == std::nullopt) {
        LOG_ERROR(Service_NS, "Could not convert application language! language_code={}",
                  language_code);
        R_THROW(Service::NS::ResultApplicationLanguageNotFound);
    }
    const auto priority_list = GetApplicationLanguagePriorityList(*application_language);
    if (!priority_list) {
        LOG_ERROR(Service_NS,
                  "Could not find application language priorities! application_language={}",
                  *application_language);
        R_THROW(Service::NS::ResultApplicationLanguageNotFound);
    }

    // Try to find a valid language.
    for (const auto lang : *priority_list) {
        const auto supported_flag = GetSupportedLanguageFlag(lang);
        if (supported_languages == 0 || (supported_languages & supported_flag) == supported_flag) {
            *out_desired_language = lang;
            R_SUCCEED();
        }
    }

    LOG_ERROR(Service_NS, "Could not find a valid language! supported_languages={:08X}",
              supported_languages);
    R_THROW(Service::NS::ResultApplicationLanguageNotFound);
}

Result IReadOnlyApplicationControlDataInterface::ConvertApplicationLanguageToLanguageCode(
    Out<u64> out_language_code, ApplicationLanguage application_language) {
    const auto language_code = ConvertToLanguageCode(application_language);
    if (language_code == std::nullopt) {
        LOG_ERROR(Service_NS, "Language not found! application_language={}", application_language);
        R_THROW(Service::NS::ResultApplicationLanguageNotFound);
    }

    *out_language_code = static_cast<u64>(*language_code);
    R_SUCCEED();
}

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/ns/language.h"
#include "core/hle/service/ns/ns_types.h"
#include "core/hle/service/service.h"

namespace Service::NS {

class IReadOnlyApplicationControlDataInterface final
    : public ServiceFramework<IReadOnlyApplicationControlDataInterface> {
public:
    explicit IReadOnlyApplicationControlDataInterface(Core::System& system_);
    ~IReadOnlyApplicationControlDataInterface() override;

public:
    Result GetApplicationControlData(OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
                                     Out<u32> out_actual_size,
                                     ApplicationControlSource application_control_source,
                                     u64 application_id);
    Result GetApplicationDesiredLanguage(Out<ApplicationLanguage> out_desired_language,
                                         u32 supported_languages);
    Result ConvertApplicationLanguageToLanguageCode(Out<u64> out_language_code,
                                                    ApplicationLanguage application_language);
};

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ns/read_only_application_record_interface.h"

namespace Service::NS {

IReadOnlyApplicationRecordInterface::IReadOnlyApplicationRecordInterface(Core::System& system_)
    : ServiceFramework{system_, "IReadOnlyApplicationRecordInterface"} {
    static const FunctionInfo functions[] = {
        {0, D<&IReadOnlyApplicationRecordInterface::HasApplicationRecord>, "HasApplicationRecord"},
        {1, nullptr, "NotifyApplicationFailure"},
        {2, D<&IReadOnlyApplicationRecordInterface::IsDataCorruptedResult>,
         "IsDataCorruptedResult"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

IReadOnlyApplicationRecordInterface::~IReadOnlyApplicationRecordInterface() = default;

Result IReadOnlyApplicationRecordInterface::HasApplicationRecord(
    Out<bool> out_has_application_record, u64 program_id) {
    LOG_WARNING(Service_NS, "(STUBBED) called, program_id={:016X}", program_id);
    *out_has_application_record = true;
    R_SUCCEED();
}

Result IReadOnlyApplicationRecordInterface::IsDataCorruptedResult(
    Out<bool> out_is_data_corrupted_result, Result result) {
    LOG_WARNING(Service_NS, "(STUBBED) called, result={:#x}", result.GetInnerValue());
    *out_is_data_corrupted_result = false;
    R_SUCCEED();
}

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Service::NS {

class IReadOnlyApplicationRecordInterface final
    : public ServiceFramework<IReadOnlyApplicationRecordInterface> {
public:
    explicit IReadOnlyApplicationRecordInterface(Core::System& system_);
    ~IReadOnlyApplicationRecordInterface() override;

private:
    Result HasApplicationRecord(Out<bool> out_has_application_record, u64 program_id);
    Result IsDataCorruptedResult(Out<bool> out_is_data_corrupted_result, Result result);
};

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ns/account_proxy_interface.h"
#include "core/hle/service/ns/application_manager_interface.h"
#include "core/hle/service/ns/application_version_interface.h"
#include "core/hle/service/ns/content_management_interface.h"
#include "core/hle/service/ns/document_interface.h"
#include "core/hle/service/ns/download_task_interface.h"
#include "core/hle/service/ns/dynamic_rights_interface.h"
#include "core/hle/service/ns/ecommerce_interface.h"
#include "core/hle/service/ns/factory_reset_interface.h"
#include "core/hle/service/ns/read_only_application_control_data_interface.h"
#include "core/hle/service/ns/read_only_application_record_interface.h"
#include "core/hle/service/ns/service_getter_interface.h"

namespace Service::NS {

IServiceGetterInterface::IServiceGetterInterface(Core::System& system_, const char* name)
    : ServiceFramework{system_, name} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {7988, D<&IServiceGetterInterface::GetDynamicRightsInterface>, "GetDynamicRightsInterface"},
        {7989, D<&IServiceGetterInterface::GetReadOnlyApplicationControlDataInterface>, "GetReadOnlyApplicationControlDataInterface"},
        {7991, D<&IServiceGetterInterface::GetReadOnlyApplicationRecordInterface>, "GetReadOnlyApplicationRecordInterface"},
        {7992, D<&IServiceGetterInterface::GetECommerceInterface>, "GetECommerceInterface"},
        {7993, D<&IServiceGetterInterface::GetApplicationVersionInterface>, "GetApplicationVersionInterface"},
        {7994, D<&IServiceGetterInterface::GetFactoryResetInterface>, "GetFactoryResetInterface"},
        {7995, D<&IServiceGetterInterface::GetAccountProxyInterface>, "GetAccountProxyInterface"},
        {7996, D<&IServiceGetterInterface::GetApplicationManagerInterface>, "GetApplicationManagerInterface"},
        {7997, D<&IServiceGetterInterface::GetDownloadTaskInterface>, "GetDownloadTaskInterface"},
        {7998, D<&IServiceGetterInterface::GetContentManagementInterface>, "GetContentManagementInterface"},
        {7999, D<&IServiceGetterInterface::GetDocumentInterface>, "GetDocumentInterface"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

IServiceGetterInterface::~IServiceGetterInterface() = default;

Result IServiceGetterInterface::GetDynamicRightsInterface(
    Out<SharedPointer<IDynamicRightsInterface>> out_interface) {
    LOG_DEBUG(Service_NS, "called");
    *out_interface = std::make_shared<IDynamicRightsInterface>(system);
    R_SUCCEED();
}

Result IServiceGetterInterface::GetReadOnlyApplicationControlDataInterface(
    Out<SharedPointer<IReadOnlyApplicationControlDataInterface>> out_interface) {
    LOG_DEBUG(Service_NS, "called");
    *out_interface = std::make_shared<IReadOnlyApplicationControlDataInterface>(system);
    R_SUCCEED();
}

Result IServiceGetterInterface::GetReadOnlyApplicationRecordInterface(
    Out<SharedPointer<IReadOnlyApplicationRecordInterface>> out_interface) {
    LOG_DEBUG(Service_NS, "called");
    *out_interface = std::make_shared<IReadOnlyApplicationRecordInterface>(system);
    R_SUCCEED();
}

Result IServiceGetterInterface::GetECommerceInterface(
    Out<SharedPointer<IECommerceInterface>> out_interface) {
    LOG_DEBUG(Service_NS, "called");
    *out_interface = std::make_shared<IECommerceInterface>(system);
    R_SUCCEED();
}

Result IServiceGetterInterface::GetApplicationVersionInterface(
    Out<SharedPointer<IApplicationVersionInterface>> out_interface) {
    LOG_DEBUG(Service_NS, "called");
    *out_interface = std::make_shared<IApplicationVersionInterface>(system);
    R_SUCCEED();
}

Result IServiceGetterInterface::GetFactoryResetInterface(
    Out<SharedPointer<IFactoryResetInterface>> out_interface) {
    LOG_DEBUG(Service_NS, "called");
    *out_interface = std::make_shared<IFactoryResetInterface>(system);
    R_SUCCEED();
}

Result IServiceGetterInterface::GetAccountProxyInterface(
    Out<SharedPointer<IAccountProxyInterface>> out_interface) {
    LOG_DEBUG(Service_NS, "called");
    *out_interface = std::make_shared<IAccountProxyInterface>(system);
    R_SUCCEED();
}

Result IServiceGetterInterface::GetApplicationManagerInterface(
    Out<SharedPointer<IApplicationManagerInterface>> out_interface) {
    LOG_DEBUG(Service_NS, "called");
    *out_interface = std::make_shared<IApplicationManagerInterface>(system);
    R_SUCCEED();
}

Result IServiceGetterInterface::GetDownloadTaskInterface(
    Out<SharedPointer<IDownloadTaskInterface>> out_interface) {
    LOG_DEBUG(Service_NS, "called");
    *out_interface = std::make_shared<IDownloadTaskInterface>(system);
    R_SUCCEED();
}

Result IServiceGetterInterface::GetContentManagementInterface(
    Out<SharedPointer<IContentManagementInterface>> out_interface) {
    LOG_DEBUG(Service_NS, "called");
    *out_interface = std::make_shared<IContentManagementInterface>(system);
    R_SUCCEED();
}

Result IServiceGetterInterface::GetDocumentInterface(
    Out<SharedPointer<IDocumentInterface>> out_interface) {
    LOG_DEBUG(Service_NS, "called");
    *out_interface = std::make_shared<IDocumentInterface>(system);
    R_SUCCEED();
}

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Service::NS {

class IDynamicRightsInterface;
class IReadOnlyApplicationControlDataInterface;
class IReadOnlyApplicationRecordInterface;
class IECommerceInterface;
class IApplicationVersionInterface;
class IFactoryResetInterface;
class IAccountProxyInterface;
class IApplicationManagerInterface;
class IDownloadTaskInterface;
class IContentManagementInterface;
class IDocumentInterface;

class IServiceGetterInterface : public ServiceFramework<IServiceGetterInterface> {
public:
    explicit IServiceGetterInterface(Core::System& system_, const char* name);
    ~IServiceGetterInterface() override;

public:
    Result GetDynamicRightsInterface(Out<SharedPointer<IDynamicRightsInterface>> out_interface);
    Result GetReadOnlyApplicationControlDataInterface(
        Out<SharedPointer<IReadOnlyApplicationControlDataInterface>> out_interface);
    Result GetReadOnlyApplicationRecordInterface(
        Out<SharedPointer<IReadOnlyApplicationRecordInterface>> out_interface);
    Result GetECommerceInterface(Out<SharedPointer<IECommerceInterface>> out_interface);
    Result GetApplicationVersionInterface(
        Out<SharedPointer<IApplicationVersionInterface>> out_interface);
    Result GetFactoryResetInterface(Out<SharedPointer<IFactoryResetInterface>> out_interface);
    Result GetAccountProxyInterface(Out<SharedPointer<IAccountProxyInterface>> out_interface);
    Result GetApplicationManagerInterface(
        Out<SharedPointer<IApplicationManagerInterface>> out_interface);
    Result GetDownloadTaskInterface(Out<SharedPointer<IDownloadTaskInterface>> out_interface);
    Result GetContentManagementInterface(
        Out<SharedPointer<IContentManagementInterface>> out_interface);
    Result GetDocumentInterface(Out<SharedPointer<IDocumentInterface>> out_interface);
};

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ns/system_update_control.h"

namespace Service::NS {

ISystemUpdateControl::ISystemUpdateControl(Core::System& system_)
    : ServiceFramework{system_, "ISystemUpdateControl"} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, nullptr, "HasDownloaded"},
        {1, nullptr, "RequestCheckLatestUpdate"},
        {2, nullptr, "RequestDownloadLatestUpdate"},
        {3, nullptr, "GetDownloadProgress"},
        {4, nullptr, "ApplyDownloadedUpdate"},
        {5, nullptr, "RequestPrepareCardUpdate"},
        {6, nullptr, "GetPrepareCardUpdateProgress"},
        {7, nullptr, "HasPreparedCardUpdate"},
        {8, nullptr, "ApplyCardUpdate"},
        {9, nullptr, "GetDownloadedEulaDataSize"},
        {10, nullptr, "GetDownloadedEulaData"},
        {11, nullptr, "SetupCardUpdate"},
        {12, nullptr, "GetPreparedCardUpdateEulaDataSize"},
        {13, nullptr, "GetPreparedCardUpdateEulaData"},
        {14, nullptr, "SetupCardUpdateViaSystemUpdater"},
        {15, nullptr, "HasReceived"},
        {16, nullptr, "RequestReceiveSystemUpdate"},
        {17, nullptr, "GetReceiveProgress"},
        {18, nullptr, "ApplyReceivedUpdate"},
        {19, nullptr, "GetReceivedEulaDataSize"},
        {20, nullptr, "GetReceivedEulaData"},
        {21, nullptr, "SetupToReceiveSystemUpdate"},
        {22, nullptr, "RequestCheckLatestUpdateIncludesRebootlessUpdate"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

ISystemUpdateControl::~ISystemUpdateControl() = default;

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/service.h"

namespace Service::NS {

class ISystemUpdateControl final : public ServiceFramework<ISystemUpdateControl> {
public:
    explicit ISystemUpdateControl(Core::System& system_);
    ~ISystemUpdateControl() override;
};

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ns/system_update_control.h"
#include "core/hle/service/ns/system_update_interface.h"

namespace Service::NS {

ISystemUpdateInterface::ISystemUpdateInterface(Core::System& system_)
    : ServiceFramework{system_, "ns:su"}, service_context{system_, "ns:su"},
      update_notification_event{service_context} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, D<&ISystemUpdateInterface::GetBackgroundNetworkUpdateState>, "GetBackgroundNetworkUpdateState"},
        {1, D<&ISystemUpdateInterface::OpenSystemUpdateControl>, "OpenSystemUpdateControl"},
        {2, nullptr, "NotifyExFatDriverRequired"},
        {3, nullptr, "ClearExFatDriverStatusForDebug"},
        {4, nullptr, "RequestBackgroundNetworkUpdate"},
        {5, nullptr, "NotifyBackgroundNetworkUpdate"},
        {6, nullptr, "NotifyExFatDriverDownloadedForDebug"},
        {9, D<&ISystemUpdateInterface::GetSystemUpdateNotificationEventForContentDelivery>, "GetSystemUpdateNotificationEventForContentDelivery"},
        {10, nullptr, "NotifySystemUpdateForContentDelivery"},
        {11, nullptr, "PrepareShutdown"},
        {12, nullptr, "Unknown12"},
        {13, nullptr, "Unknown13"},
        {14, nullptr, "Unknown14"},
        {15, nullptr, "Unknown15"},
        {16, nullptr, "DestroySystemUpdateTask"},
        {17, nullptr, "RequestSendSystemUpdate"},
        {18, nullptr, "GetSendSystemUpdateProgress"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

ISystemUpdateInterface::~ISystemUpdateInterface() = default;

Result ISystemUpdateInterface::GetBackgroundNetworkUpdateState(
    Out<BackgroundNetworkUpdateState> out_background_network_update_state) {
    LOG_WARNING(Service_AM, "(STUBBED) called");
    *out_background_network_update_state = BackgroundNetworkUpdateState::None;
    R_SUCCEED();
}

Result ISystemUpdateInterface::OpenSystemUpdateControl(
    Out<SharedPointer<ISystemUpdateControl>> out_system_update_control) {
    LOG_WARNING(Service_NS, "(STUBBED) called");
    *out_system_update_control = std::make_shared<ISystemUpdateControl>(system);
    R_SUCCEED();
}

Result ISystemUpdateInterface::GetSystemUpdateNotificationEventForContentDelivery(
    OutCopyHandle<Kernel::KReadableEvent> out_event) {
    LOG_WARNING(Service_NS, "(STUBBED) called");
    *out_event = update_notification_event.GetHandle();
    R_SUCCEED();
}

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/ns/ns_types.h"
#include "core/hle/service/os/event.h"
#include "core/hle/service/service.h"

namespace Kernel {
class KReadableEvent;
}

namespace Service::NS {

class ISystemUpdateControl;

class ISystemUpdateInterface final : public ServiceFramework<ISystemUpdateInterface> {
public:
    explicit ISystemUpdateInterface(Core::System& system_);
    ~ISystemUpdateInterface() override;

private:
    Result GetBackgroundNetworkUpdateState(
        Out<BackgroundNetworkUpdateState> out_background_network_update_state);
    Result OpenSystemUpdateControl(
        Out<SharedPointer<ISystemUpdateControl>> out_system_update_control);
    Result GetSystemUpdateNotificationEventForContentDelivery(
        OutCopyHandle<Kernel::KReadableEvent> out_event);

private:
    KernelHelpers::ServiceContext service_context;
    Event update_notification_event;
};

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ns/vulnerability_manager_interface.h"

namespace Service::NS {

IVulnerabilityManagerInterface::IVulnerabilityManagerInterface(Core::System& system_)
    : ServiceFramework{system_, "ns:vm"} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {1200, D<&IVulnerabilityManagerInterface::NeedsUpdateVulnerability>, "NeedsUpdateVulnerability"},
        {1201, nullptr, "UpdateSafeSystemVersionForDebug"},
        {1202, nullptr, "GetSafeSystemVersion"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

IVulnerabilityManagerInterface::~IVulnerabilityManagerInterface() = default;

Result IVulnerabilityManagerInterface::NeedsUpdateVulnerability(
    Out<bool> out_needs_update_vulnerability) {
    LOG_WARNING(Service_NS, "(STUBBED) called");
    *out_needs_update_vulnerability = false;
    R_SUCCEED();
}

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Service::NS {

class IVulnerabilityManagerInterface final
    : public ServiceFramework<IVulnerabilityManagerInterface> {
public:
    explicit IVulnerabilityManagerInterface(Core::System& system_);
    ~IVulnerabilityManagerInterface() override;

private:
    Result NeedsUpdateVulnerability(Out<bool> out_needs_update_vulnerability);
};

} // namespace Service::NS


code:
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
// SPDX-License-Identifier: GPL-3.0-or-later

#include <atomic>
#include <deque>
#include <mutex>

#include "core/hle/kernel/k_process.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/core/heap_mapper.h"
#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
#include "core/memory.h"
#include "video_core/host1x/host1x.h"

namespace Service::Nvidia::NvCore {

Session::Session(SessionId id_, Kernel::KProcess* process_, Core::Asid asid_)
    : id{id_}, process{process_}, asid{asid_}, has_preallocated_area{}, mapper{}, is_active{} {}

Session::~Session() = default;

struct ContainerImpl {
    explicit ContainerImpl(Container& core, Tegra::Host1x::Host1x& host1x_)
        : host1x{host1x_}, file{core, host1x_}, manager{host1x_}, device_file_data{} {}
    Tegra::Host1x::Host1x& host1x;
    NvMap file;
    SyncpointManager manager;
    Container::Host1xDeviceFileData device_file_data;
    std::deque<Session> sessions;
    size_t new_ids{};
    std::deque<size_t> id_pool;
    std::mutex session_guard;
};

Container::Container(Tegra::Host1x::Host1x& host1x_) {
    impl = std::make_unique<ContainerImpl>(*this, host1x_);
}

Container::~Container() = default;

SessionId Container::OpenSession(Kernel::KProcess* process) {
    using namespace Common::Literals;

    std::scoped_lock lk(impl->session_guard);
    for (auto& session : impl->sessions) {
        if (!session.is_active) {
            continue;
        }
        if (session.process == process) {
            session.ref_count++;
            return session.id;
        }
    }
    size_t new_id{};
    auto* memory_interface = &process->GetMemory();
    auto& smmu = impl->host1x.MemoryManager();
    auto asid = smmu.RegisterProcess(memory_interface);
    if (!impl->id_pool.empty()) {
        new_id = impl->id_pool.front();
        impl->id_pool.pop_front();
        impl->sessions[new_id] = Session{SessionId{new_id}, process, asid};
    } else {
        new_id = impl->new_ids++;
        impl->sessions.emplace_back(SessionId{new_id}, process, asid);
    }
    auto& session = impl->sessions[new_id];
    session.is_active = true;
    session.ref_count = 1;
    // Optimization
    if (process->IsApplication()) {
        auto& page_table = process->GetPageTable().GetBasePageTable();
        auto heap_start = page_table.GetHeapRegionStart();

        Kernel::KProcessAddress cur_addr = heap_start;
        size_t region_size = 0;
        VAddr region_start = 0;
        while (true) {
            Kernel::KMemoryInfo mem_info{};
            Kernel::Svc::PageInfo page_info{};
            R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info),
                                          cur_addr));
            auto svc_mem_info = mem_info.GetSvcMemoryInfo();

            // Check if this memory block is heap.
            if (svc_mem_info.state == Kernel::Svc::MemoryState::Normal) {
                if (region_start + region_size == svc_mem_info.base_address) {
                    region_size += svc_mem_info.size;
                } else if (svc_mem_info.size > region_size) {
                    region_size = svc_mem_info.size;
                    region_start = svc_mem_info.base_address;
                }
            }

            // Check if we're done.
            const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size;
            if (next_address <= GetInteger(cur_addr)) {
                break;
            }

            cur_addr = next_address;
        }
        session.has_preallocated_area = false;
        auto start_region = region_size >= 32_MiB ? smmu.Allocate(region_size) : 0;
        if (start_region != 0) {
            session.mapper = std::make_unique<HeapMapper>(region_start, start_region, region_size,
                                                          asid, impl->host1x);
            smmu.TrackContinuity(start_region, region_start, region_size, asid);
            session.has_preallocated_area = true;
            LOG_DEBUG(Debug, "Preallocation created!");
        }
    }
    return SessionId{new_id};
}

void Container::CloseSession(SessionId session_id) {
    std::scoped_lock lk(impl->session_guard);
    auto& session = impl->sessions[session_id.id];
    if (--session.ref_count > 0) {
        return;
    }
    impl->file.UnmapAllHandles(session_id);
    auto& smmu = impl->host1x.MemoryManager();
    if (session.has_preallocated_area) {
        const DAddr region_start = session.mapper->GetRegionStart();
        const size_t region_size = session.mapper->GetRegionSize();
        session.mapper.reset();
        smmu.Free(region_start, region_size);
        session.has_preallocated_area = false;
    }
    session.is_active = false;
    smmu.UnregisterProcess(impl->sessions[session_id.id].asid);
    impl->id_pool.emplace_front(session_id.id);
}

Session* Container::GetSession(SessionId session_id) {
    std::atomic_thread_fence(std::memory_order_acquire);
    return &impl->sessions[session_id.id];
}

NvMap& Container::GetNvMapFile() {
    return impl->file;
}

const NvMap& Container::GetNvMapFile() const {
    return impl->file;
}

Container::Host1xDeviceFileData& Container::Host1xDeviceFile() {
    return impl->device_file_data;
}

const Container::Host1xDeviceFileData& Container::Host1xDeviceFile() const {
    return impl->device_file_data;
}

SyncpointManager& Container::GetSyncpointManager() {
    return impl->manager;
}

const SyncpointManager& Container::GetSyncpointManager() const {
    return impl->manager;
}

} // namespace Service::Nvidia::NvCore


code:
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <deque>
#include <memory>
#include <unordered_map>

#include "core/device_memory_manager.h"
#include "core/hle/service/nvdrv/nvdata.h"

namespace Kernel {
class KProcess;
}

namespace Tegra::Host1x {
class Host1x;
} // namespace Tegra::Host1x

namespace Service::Nvidia::NvCore {

class HeapMapper;
class NvMap;
class SyncpointManager;

struct ContainerImpl;

struct SessionId {
    size_t id;
};

struct Session {
    Session(SessionId id_, Kernel::KProcess* process_, Core::Asid asid_);
    ~Session();

    Session(const Session&) = delete;
    Session& operator=(const Session&) = delete;
    Session(Session&&) = default;
    Session& operator=(Session&&) = default;

    SessionId id;
    Kernel::KProcess* process;
    Core::Asid asid;
    bool has_preallocated_area{};
    std::unique_ptr<HeapMapper> mapper{};
    bool is_active{};
    s32 ref_count{};
};

class Container {
public:
    explicit Container(Tegra::Host1x::Host1x& host1x);
    ~Container();

    SessionId OpenSession(Kernel::KProcess* process);
    void CloseSession(SessionId id);

    Session* GetSession(SessionId id);

    NvMap& GetNvMapFile();

    const NvMap& GetNvMapFile() const;

    SyncpointManager& GetSyncpointManager();

    const SyncpointManager& GetSyncpointManager() const;

    struct Host1xDeviceFileData {
        std::unordered_map<DeviceFD, u32> fd_to_id{};
        std::deque<u32> syncpts_accumulated{};
        u32 nvdec_next_id{};
        u32 vic_next_id{};
    };

    Host1xDeviceFileData& Host1xDeviceFile();

    const Host1xDeviceFileData& Host1xDeviceFile() const;

private:
    std::unique_ptr<ContainerImpl> impl;
};

} // namespace Service::Nvidia::NvCore


code:
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include <mutex>

#include "common/range_sets.h"
#include "common/range_sets.inc"
#include "core/hle/service/nvdrv/core/heap_mapper.h"
#include "video_core/host1x/host1x.h"

namespace Service::Nvidia::NvCore {

struct HeapMapper::HeapMapperInternal {
    HeapMapperInternal(Tegra::Host1x::Host1x& host1x) : m_device_memory{host1x.MemoryManager()} {}
    ~HeapMapperInternal() = default;

    Common::RangeSet<VAddr> m_temporary_set;
    Common::OverlapRangeSet<VAddr> m_mapped_ranges;
    Tegra::MaxwellDeviceMemoryManager& m_device_memory;
    std::mutex m_guard;
};

HeapMapper::HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid,
                       Tegra::Host1x::Host1x& host1x)
    : m_vaddress{start_vaddress}, m_daddress{start_daddress}, m_size{size}, m_asid{asid} {
    m_internal = std::make_unique<HeapMapperInternal>(host1x);
}

HeapMapper::~HeapMapper() {
    // Unmap whatever has been mapped.
    m_internal->m_mapped_ranges.ForEach([this](VAddr start_addr, VAddr end_addr, s32 count) {
        const size_t sub_size = end_addr - start_addr;
        const size_t offset = start_addr - m_vaddress;
        m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size);
    });
}

DAddr HeapMapper::Map(VAddr start, size_t size) {
    std::scoped_lock lk(m_internal->m_guard);
    // Add the mapping range to a temporary range set.
    m_internal->m_temporary_set.Clear();
    m_internal->m_temporary_set.Add(start, size);

    // Remove anything that's already mapped from the temporary range set.
    m_internal->m_mapped_ranges.ForEachInRange(
        start, size, [this](VAddr start_addr, VAddr end_addr, s32) {
            m_internal->m_temporary_set.Subtract(start_addr, end_addr - start_addr);
        });

    // Map anything that has not been mapped yet.
    m_internal->m_temporary_set.ForEach([this](VAddr start_addr, VAddr end_addr) {
        const size_t sub_size = end_addr - start_addr;
        const size_t offset = start_addr - m_vaddress;
        m_internal->m_device_memory.Map(m_daddress + offset, m_vaddress + offset, sub_size, m_asid);
    });

    // Add the mapping range to the split map, to register the map and overlaps.
    m_internal->m_mapped_ranges.Add(start, size);
    m_internal->m_temporary_set.Clear();
    return m_daddress + static_cast<DAddr>(start - m_vaddress);
}

void HeapMapper::Unmap(VAddr start, size_t size) {
    std::scoped_lock lk(m_internal->m_guard);

    // Just subtract the range and whatever is deleted, unmap it.
    m_internal->m_mapped_ranges.Subtract(start, size, [this](VAddr start_addr, VAddr end_addr) {
        const size_t sub_size = end_addr - start_addr;
        const size_t offset = start_addr - m_vaddress;
        m_internal->m_device_memory.Unmap(m_daddress + offset, sub_size);
    });
}

} // namespace Service::Nvidia::NvCore


code:
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <memory>

#include "common/common_types.h"
#include "core/device_memory_manager.h"

namespace Tegra::Host1x {
class Host1x;
} // namespace Tegra::Host1x

namespace Service::Nvidia::NvCore {

class HeapMapper {
public:
    HeapMapper(VAddr start_vaddress, DAddr start_daddress, size_t size, Core::Asid asid,
               Tegra::Host1x::Host1x& host1x);
    ~HeapMapper();

    bool IsInBounds(VAddr start, size_t size) const {
        VAddr end = start + size;
        return start >= m_vaddress && end <= (m_vaddress + m_size);
    }

    DAddr Map(VAddr start, size_t size);

    void Unmap(VAddr start, size_t size);

    DAddr GetRegionStart() const {
        return m_daddress;
    }

    size_t GetRegionSize() const {
        return m_size;
    }

private:
    struct HeapMapperInternal;
    VAddr m_vaddress;
    DAddr m_daddress;
    size_t m_size;
    Core::Asid m_asid;
    std::unique_ptr<HeapMapperInternal> m_internal;
};

} // namespace Service::Nvidia::NvCore


code:
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
// SPDX-License-Identifier: GPL-3.0-or-later

#include <functional>

#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/core/heap_mapper.h"
#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/memory.h"
#include "video_core/host1x/host1x.h"

using Core::Memory::YUZU_PAGESIZE;
constexpr size_t BIG_PAGE_SIZE = YUZU_PAGESIZE * 16;

namespace Service::Nvidia::NvCore {
NvMap::Handle::Handle(u64 size_, Id id_)
    : size(size_), aligned_size(size), orig_size(size), id(id_) {
    flags.raw = 0;
}

NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress,
                              NvCore::SessionId pSessionId) {
    std::scoped_lock lock(mutex);
    // Handles cannot be allocated twice
    if (allocated) {
        return NvResult::AccessDenied;
    }

    flags = pFlags;
    kind = pKind;
    align = pAlign < YUZU_PAGESIZE ? YUZU_PAGESIZE : pAlign;
    session_id = pSessionId;

    // This flag is only applicable for handles with an address passed
    if (pAddress) {
        flags.keep_uncached_after_free.Assign(0);
    } else {
        LOG_CRITICAL(Service_NVDRV,
                     "Mapping nvmap handles without a CPU side address is unimplemented!");
    }

    size = Common::AlignUp(size, YUZU_PAGESIZE);
    aligned_size = Common::AlignUp(size, align);
    address = pAddress;
    allocated = true;

    return NvResult::Success;
}

NvResult NvMap::Handle::Duplicate(bool internal_session) {
    std::scoped_lock lock(mutex);
    // Unallocated handles cannot be duplicated as duplication requires memory accounting (in HOS)
    if (!allocated) [[unlikely]] {
        return NvResult::BadValue;
    }

    // If we internally use FromId the duplication tracking of handles won't work accurately due to
    // us not implementing per-process handle refs.
    if (internal_session) {
        internal_dupes++;
    } else {
        dupes++;
    }

    return NvResult::Success;
}

NvMap::NvMap(Container& core_, Tegra::Host1x::Host1x& host1x_) : host1x{host1x_}, core{core_} {}

void NvMap::AddHandle(std::shared_ptr<Handle> handle_description) {
    std::scoped_lock lock(handles_lock);

    handles.emplace(handle_description->id, std::move(handle_description));
}

void NvMap::UnmapHandle(Handle& handle_description) {
    // Remove pending unmap queue entry if needed
    if (handle_description.unmap_queue_entry) {
        unmap_queue.erase(*handle_description.unmap_queue_entry);
        handle_description.unmap_queue_entry.reset();
    }

    // Free and unmap the handle from Host1x GMMU
    if (handle_description.pin_virt_address) {
        host1x.GMMU().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address),
                            handle_description.aligned_size);
        host1x.Allocator().Free(handle_description.pin_virt_address,
                                static_cast<u32>(handle_description.aligned_size));
        handle_description.pin_virt_address = 0;
    }

    // Free and unmap the handle from the SMMU
    const size_t map_size = handle_description.aligned_size;
    if (!handle_description.in_heap) {
        auto& smmu = host1x.MemoryManager();
        size_t aligned_up = Common::AlignUp(map_size, BIG_PAGE_SIZE);
        smmu.Unmap(handle_description.d_address, map_size);
        smmu.Free(handle_description.d_address, static_cast<size_t>(aligned_up));
        handle_description.d_address = 0;
        return;
    }
    const VAddr vaddress = handle_description.address;
    auto* session = core.GetSession(handle_description.session_id);
    session->mapper->Unmap(vaddress, map_size);
    handle_description.d_address = 0;
    handle_description.in_heap = false;
}

bool NvMap::TryRemoveHandle(const Handle& handle_description) {
    // No dupes left, we can remove from handle map
    if (handle_description.dupes == 0 && handle_description.internal_dupes == 0) {
        std::scoped_lock lock(handles_lock);

        auto it{handles.find(handle_description.id)};
        if (it != handles.end()) {
            handles.erase(it);
        }

        return true;
    } else {
        return false;
    }
}

NvResult NvMap::CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out) {
    if (!size) [[unlikely]] {
        return NvResult::BadValue;
    }

    u32 id{next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed)};
    auto handle_description{std::make_shared<Handle>(size, id)};
    AddHandle(handle_description);

    result_out = handle_description;
    return NvResult::Success;
}

std::shared_ptr<NvMap::Handle> NvMap::GetHandle(Handle::Id handle) {
    std::scoped_lock lock(handles_lock);
    try {
        return handles.at(handle);
    } catch (std::out_of_range&) {
        return nullptr;
    }
}

DAddr NvMap::GetHandleAddress(Handle::Id handle) {
    std::scoped_lock lock(handles_lock);
    try {
        return handles.at(handle)->d_address;
    } catch (std::out_of_range&) {
        return 0;
    }
}

DAddr NvMap::PinHandle(NvMap::Handle::Id handle, bool low_area_pin) {
    auto handle_description{GetHandle(handle)};
    if (!handle_description) [[unlikely]] {
        return 0;
    }

    std::scoped_lock lock(handle_description->mutex);
    const auto map_low_area = [&] {
        if (handle_description->pin_virt_address == 0) {
            auto& gmmu_allocator = host1x.Allocator();
            auto& gmmu = host1x.GMMU();
            u32 address =
                gmmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size));
            gmmu.Map(static_cast<GPUVAddr>(address), handle_description->d_address,
                     handle_description->aligned_size);
            handle_description->pin_virt_address = address;
        }
    };
    if (!handle_description->pins) {
        // If we're in the unmap queue we can just remove ourselves and return since we're already
        // mapped
        {
            // Lock now to prevent our queue entry from being removed for allocation in-between the
            // following check and erase
            std::scoped_lock queueLock(unmap_queue_lock);
            if (handle_description->unmap_queue_entry) {
                unmap_queue.erase(*handle_description->unmap_queue_entry);
                handle_description->unmap_queue_entry.reset();

                if (low_area_pin) {
                    map_low_area();
                    handle_description->pins++;
                    return static_cast<DAddr>(handle_description->pin_virt_address);
                }

                handle_description->pins++;
                return handle_description->d_address;
            }
        }

        using namespace std::placeholders;
        // If not then allocate some space and map it
        DAddr address{};
        auto& smmu = host1x.MemoryManager();
        auto* session = core.GetSession(handle_description->session_id);
        const VAddr vaddress = handle_description->address;
        const size_t map_size = handle_description->aligned_size;
        if (session->has_preallocated_area && session->mapper->IsInBounds(vaddress, map_size)) {
            handle_description->d_address = session->mapper->Map(vaddress, map_size);
            handle_description->in_heap = true;
        } else {
            size_t aligned_up = Common::AlignUp(map_size, BIG_PAGE_SIZE);
            while ((address = smmu.Allocate(aligned_up)) == 0) {
                // Free handles until the allocation succeeds
                std::scoped_lock queueLock(unmap_queue_lock);
                if (auto freeHandleDesc{unmap_queue.front()}) {
                    // Handles in the unmap queue are guaranteed not to be pinned so don't bother
                    // checking if they are before unmapping
                    std::scoped_lock freeLock(freeHandleDesc->mutex);
                    if (handle_description->d_address)
                        UnmapHandle(*freeHandleDesc);
                } else {
                    LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!");
                }
            }

            handle_description->d_address = address;
            smmu.Map(address, vaddress, map_size, session->asid, true);
            handle_description->in_heap = false;
        }
    }

    if (low_area_pin) {
        map_low_area();
    }

    handle_description->pins++;
    if (low_area_pin) {
        return static_cast<DAddr>(handle_description->pin_virt_address);
    }
    return handle_description->d_address;
}

void NvMap::UnpinHandle(Handle::Id handle) {
    auto handle_description{GetHandle(handle)};
    if (!handle_description) {
        return;
    }

    std::scoped_lock lock(handle_description->mutex);
    if (--handle_description->pins < 0) {
        LOG_WARNING(Service_NVDRV, "Pin count imbalance detected!");
    } else if (!handle_description->pins) {
        std::scoped_lock queueLock(unmap_queue_lock);

        // Add to the unmap queue allowing this handle's memory to be freed if needed
        unmap_queue.push_back(handle_description);
        handle_description->unmap_queue_entry = std::prev(unmap_queue.end());
    }
}

void NvMap::DuplicateHandle(Handle::Id handle, bool internal_session) {
    auto handle_description{GetHandle(handle)};
    if (!handle_description) {
        LOG_CRITICAL(Service_NVDRV, "Unregistered handle!");
        return;
    }

    auto result = handle_description->Duplicate(internal_session);
    if (result != NvResult::Success) {
        LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!");
    }
}

std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool internal_session) {
    std::weak_ptr<Handle> hWeak{GetHandle(handle)};
    FreeInfo freeInfo;

    // We use a weak ptr here so we can tell when the handle has been freed and report that back to
    // guest
    if (auto handle_description = hWeak.lock()) {
        std::scoped_lock lock(handle_description->mutex);

        if (internal_session) {
            if (--handle_description->internal_dupes < 0)
                LOG_WARNING(Service_NVDRV, "Internal duplicate count imbalance detected!");
        } else {
            if (--handle_description->dupes < 0) {
                LOG_WARNING(Service_NVDRV, "User duplicate count imbalance detected!");
            } else if (handle_description->dupes == 0) {
                // Force unmap the handle
                if (handle_description->d_address) {
                    std::scoped_lock queueLock(unmap_queue_lock);
                    UnmapHandle(*handle_description);
                }

                handle_description->pins = 0;
            }
        }

        // Try to remove the shared ptr to the handle from the map, if nothing else is using the
        // handle then it will now be freed when `handle_description` goes out of scope
        if (TryRemoveHandle(*handle_description)) {
            LOG_DEBUG(Service_NVDRV, "Removed nvmap handle: {}", handle);
        } else {
            LOG_DEBUG(Service_NVDRV,
                      "Tried to free nvmap handle: {} but didn't as it still has duplicates",
                      handle);
        }

        freeInfo = {
            .address = handle_description->address,
            .size = handle_description->size,
            .was_uncached = handle_description->flags.map_uncached.Value() != 0,
            .can_unlock = true,
        };
    } else {
        return std::nullopt;
    }

    // If the handle hasn't been freed from memory, mark that
    if (!hWeak.expired()) {
        LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle);
        freeInfo.can_unlock = false;
    }

    return freeInfo;
}

void NvMap::UnmapAllHandles(NvCore::SessionId session_id) {
    auto handles_copy = [&] {
        std::scoped_lock lk{handles_lock};
        return handles;
    }();

    for (auto& [id, handle] : handles_copy) {
        {
            std::scoped_lock lk{handle->mutex};
            if (handle->session_id.id != session_id.id || handle->dupes <= 0) {
                continue;
            }
        }
        FreeHandle(id, false);
    }
}

} // namespace Service::Nvidia::NvCore


code:
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <atomic>
#include <list>
#include <memory>
#include <mutex>
#include <optional>
#include <unordered_map>
#include <assert.h>

#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/nvdata.h"

namespace Tegra {

namespace Host1x {
class Host1x;
} // namespace Host1x

} // namespace Tegra

namespace Service::Nvidia::NvCore {

class Container;
/**
 * @brief The nvmap core class holds the global state for nvmap and provides methods to manage
 * handles
 */
class NvMap {
public:
    /**
     * @brief A handle to a contiguous block of memory in an application's address space
     */
    struct Handle {
        std::mutex mutex;

        u64 align{};      //!< The alignment to use when pinning the handle onto the SMMU
        u64 size;         //!< Page-aligned size of the memory the handle refers to
        u64 aligned_size; //!< `align`-aligned size of the memory the handle refers to
        u64 orig_size;    //!< Original unaligned size of the memory this handle refers to

        s32 dupes{1};          //!< How many guest references there are to this handle
        s32 internal_dupes{0}; //!< How many emulator-internal references there are to this handle

        using Id = u32;
        Id id; //!< A globally unique identifier for this handle

        s64 pins{};
        u32 pin_virt_address{};
        std::optional<typename std::list<std::shared_ptr<Handle>>::iterator> unmap_queue_entry{};

        union Flags {
            u32 raw;
            BitField<0, 1, u32> map_uncached; //!< If the handle should be mapped as uncached
            BitField<2, 1, u32> keep_uncached_after_free; //!< Only applicable when the handle was
                                                          //!< allocated with a fixed address
            BitField<4, 1, u32> _unk0_;                   //!< Passed to IOVMM for pins
        } flags{};
        static_assert(sizeof(Flags) == sizeof(u32));

        VAddr address{}; //!< The memory location in the guest's AS that this handle corresponds to,
                         //!< this can also be in the nvdrv tmem
        bool is_shared_mem_mapped{}; //!< If this nvmap has been mapped with the MapSharedMem IPC
                                     //!< call

        u8 kind{};        //!< Used for memory compression
        bool allocated{}; //!< If the handle has been allocated with `Alloc`
        bool in_heap{};
        NvCore::SessionId session_id{};

        DAddr d_address{}; //!< The memory location in the device's AS that this handle corresponds
                           //!< to, this can also be in the nvdrv tmem

        Handle(u64 size, Id id);

        /**
         * @brief Sets up the handle with the given memory config, can allocate memory from the tmem
         * if a 0 address is passed
         */
        [[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress,
                                     NvCore::SessionId pSessionId);

        /**
         * @brief Increases the dupe counter of the handle for the given session
         */
        [[nodiscard]] NvResult Duplicate(bool internal_session);

        /**
         * @brief Obtains a pointer to the handle's memory and marks the handle it as having been
         * mapped
         */
        u8* GetPointer() {
            if (!address) {
                return nullptr;
            }

            is_shared_mem_mapped = true;
            return reinterpret_cast<u8*>(address);
        }
    };

    /**
     * @brief Encapsulates the result of a FreeHandle operation
     */
    struct FreeInfo {
        u64 address;       //!< Address the handle referred to before deletion
        u64 size;          //!< Page-aligned handle size
        bool was_uncached; //!< If the handle was allocated as uncached
        bool can_unlock;   //!< If the address region is ready to be unlocked
    };

    explicit NvMap(Container& core, Tegra::Host1x::Host1x& host1x);

    /**
     * @brief Creates an unallocated handle of the given size
     */
    [[nodiscard]] NvResult CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out);

    std::shared_ptr<Handle> GetHandle(Handle::Id handle);

    DAddr GetHandleAddress(Handle::Id handle);

    /**
     * @brief Maps a handle into the SMMU address space
     * @note This operation is refcounted, the number of calls to this must eventually match the
     * number of calls to `UnpinHandle`
     * @return The SMMU virtual address that the handle has been mapped to
     */
    DAddr PinHandle(Handle::Id handle, bool low_area_pin);

    /**
     * @brief When this has been called an equal number of times to `PinHandle` for the supplied
     * handle it will be added to a list of handles to be freed when necessary
     */
    void UnpinHandle(Handle::Id handle);

    /**
     * @brief Tries to duplicate a handle
     */
    void DuplicateHandle(Handle::Id handle, bool internal_session = false);

    /**
     * @brief Tries to free a handle and remove a single dupe
     * @note If a handle has no dupes left and has no other users a FreeInfo struct will be returned
     * describing the prior state of the handle
     */
    std::optional<FreeInfo> FreeHandle(Handle::Id handle, bool internal_session);

    void UnmapAllHandles(NvCore::SessionId session_id);

private:
    std::list<std::shared_ptr<Handle>> unmap_queue{};
    std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue`

    std::unordered_map<Handle::Id, std::shared_ptr<Handle>>
        handles{};           //!< Main owning map of handles
    std::mutex handles_lock; //!< Protects access to `handles`

    static constexpr u32 HandleIdIncrement{
        4}; //!< Each new handle ID is an increment of 4 from the previous
    std::atomic<u32> next_handle_id{HandleIdIncrement};
    Tegra::Host1x::Host1x& host1x;

    void AddHandle(std::shared_ptr<Handle> handle);

    /**
     * @brief Unmaps and frees the SMMU memory region a handle is mapped to
     * @note Both `unmap_queue_lock` and `handle_description.mutex` MUST be locked when calling this
     */
    void UnmapHandle(Handle& handle_description);

    /**
     * @brief Removes a handle from the map taking its dupes into account
     * @note handle_description.mutex MUST be locked when calling this
     * @return If the handle was removed from the map
     */
    bool TryRemoveHandle(const Handle& handle_description);

    Container& core;
};
} // namespace Service::Nvidia::NvCore


code:
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
// SPDX-License-Identifier: GPL-3.0-or-later

#include "common/assert.h"
#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
#include "video_core/host1x/host1x.h"

namespace Service::Nvidia::NvCore {

SyncpointManager::SyncpointManager(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {
    constexpr u32 VBlank0SyncpointId{26};
    constexpr u32 VBlank1SyncpointId{27};

    // Reserve both vblank syncpoints as client managed as they use Continuous Mode
    // Refer to section 14.3.5.3 of the TRM for more information on Continuous Mode
    // https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/drm/dc.c#L660
    ReserveSyncpoint(VBlank0SyncpointId, true);
    ReserveSyncpoint(VBlank1SyncpointId, true);

    for (u32 syncpoint_id : channel_syncpoints) {
        if (syncpoint_id) {
            ReserveSyncpoint(syncpoint_id, false);
        }
    }
}

SyncpointManager::~SyncpointManager() = default;

u32 SyncpointManager::ReserveSyncpoint(u32 id, bool client_managed) {
    auto& syncpoint = syncpoints.at(id);

    if (syncpoint.reserved) {
        ASSERT_MSG(false, "Requested syncpoint is in use");
        return 0;
    }

    syncpoint.reserved = true;
    syncpoint.interface_managed = client_managed;

    return id;
}

u32 SyncpointManager::FindFreeSyncpoint() {
    for (u32 i{1}; i < syncpoints.size(); i++) {
        if (!syncpoints[i].reserved) {
            return i;
        }
    }
    ASSERT_MSG(false, "Failed to find a free syncpoint!");
    return 0;
}

u32 SyncpointManager::AllocateSyncpoint(bool client_managed) {
    std::lock_guard lock(reservation_lock);
    return ReserveSyncpoint(FindFreeSyncpoint(), client_managed);
}

void SyncpointManager::FreeSyncpoint(u32 id) {
    std::lock_guard lock(reservation_lock);
    auto& syncpoint = syncpoints.at(id);
    ASSERT(syncpoint.reserved);
    syncpoint.reserved = false;
}

bool SyncpointManager::IsSyncpointAllocated(u32 id) const {
    return (id < SyncpointCount) && syncpoints[id].reserved;
}

bool SyncpointManager::HasSyncpointExpired(u32 id, u32 threshold) const {
    const SyncpointInfo& syncpoint{syncpoints.at(id)};

    if (!syncpoint.reserved) {
        ASSERT(false);
        return false;
    }

    // If the interface manages counters then we don't keep track of the maximum value as it handles
    // sanity checking the values then
    if (syncpoint.interface_managed) {
        return static_cast<s32>(syncpoint.counter_min - threshold) >= 0;
    } else {
        return (syncpoint.counter_max - threshold) >= (syncpoint.counter_min - threshold);
    }
}

u32 SyncpointManager::IncrementSyncpointMaxExt(u32 id, u32 amount) {
    auto& syncpoint = syncpoints.at(id);

    if (!syncpoint.reserved) {
        ASSERT(false);
        return 0;
    }

    return syncpoint.counter_max += amount;
}

u32 SyncpointManager::ReadSyncpointMinValue(u32 id) {
    auto& syncpoint = syncpoints.at(id);

    if (!syncpoint.reserved) {
        ASSERT(false);
        return 0;
    }

    return syncpoint.counter_min;
}

u32 SyncpointManager::UpdateMin(u32 id) {
    auto& syncpoint = syncpoints.at(id);

    if (!syncpoint.reserved) {
        ASSERT(false);
        return 0;
    }

    syncpoint.counter_min = host1x.GetSyncpointManager().GetHostSyncpointValue(id);
    return syncpoint.counter_min;
}

NvFence SyncpointManager::GetSyncpointFence(u32 id) {
    auto& syncpoint = syncpoints.at(id);

    if (!syncpoint.reserved) {
        ASSERT(false);
        return NvFence{};
    }

    return {
        .id = static_cast<s32>(id),
        .value = syncpoint.counter_max,
    };
}

} // namespace Service::Nvidia::NvCore